mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-14 05:14:19 +00:00
Add TA and test coverage for the Forex context (#1532)
* Add technical analysis to forex menu * Remove "stock" and "$" from the common ta charts * Add tests for forex context * Update forex custom reset functions * Remove -t arg (ticker) insertion into other_args in forex controller * Remove excluded command from forex ta help string * Update test data Co-authored-by: didierlopes.eth <dro.lopes@campus.fct.unl.pt>
This commit is contained in:
committed by
GitHub
parent
f761db8f98
commit
ede6ec8b1a
@@ -77,7 +77,7 @@ def display_adx(
|
||||
ax1.plot(plot_data.index, plot_data["Close"].values)
|
||||
ax1.set_title(f"Average Directional Movement Index (ADX) on {s_ticker}")
|
||||
ax1.set_xlim(plot_data.index[0], plot_data.index[-1])
|
||||
ax1.set_ylabel("Share Price ($)")
|
||||
ax1.set_ylabel("Price")
|
||||
theme.style_primary_axis(
|
||||
ax1,
|
||||
data_index=plot_data.index.to_list(),
|
||||
@@ -167,7 +167,7 @@ def display_aroon(
|
||||
ax1.plot(plot_data.index, plot_data["Adj Close"].values)
|
||||
ax1.set_title(f"Aroon on {s_ticker}")
|
||||
ax1.set_xlim(plot_data.index[0], plot_data.index[-1])
|
||||
ax1.set_ylabel("Share Price ($)")
|
||||
ax1.set_ylabel("Price")
|
||||
theme.style_primary_axis(
|
||||
ax1,
|
||||
data_index=plot_data.index.to_list(),
|
||||
|
||||
@@ -8,6 +8,7 @@ from gamestonk_terminal.helper_classes import ModelsNamespace as _models
|
||||
# Context menus
|
||||
from .av_view import display_candle as candle
|
||||
from .av_view import display_quote as quote
|
||||
from .technical_analysis import ta_api as ta
|
||||
from .oanda import oanda_api as oanda
|
||||
|
||||
# Models
|
||||
|
||||
@@ -27,7 +27,7 @@ class ForexController(BaseController):
|
||||
"""Forex Controller class."""
|
||||
|
||||
CHOICES_COMMANDS = ["to", "from", "load", "quote", "candle", "resources"]
|
||||
CHOICES_MENUS = ["oanda"]
|
||||
CHOICES_MENUS = ["ta", "oanda"]
|
||||
PATH = "/forex/"
|
||||
FILE_PATH = os.path.join(os.path.dirname(__file__), "README.md")
|
||||
|
||||
@@ -60,12 +60,23 @@ class ForexController(BaseController):
|
||||
quote get last quote
|
||||
load get historical data
|
||||
candle show candle plot for loaded data[/cmds]
|
||||
[menu]
|
||||
> ta technical analysis for loaded coin, e.g.: ema, macd, rsi, adx, bbands, obv
|
||||
[/menu]
|
||||
{has_symbols_end}
|
||||
[info]Forex brokerages:[/info][menu]
|
||||
> oanda Oanda menu[/menu][/cmds]
|
||||
"""
|
||||
console.print(text=help_text, menu="Forex")
|
||||
|
||||
def custom_reset(self):
|
||||
"""Class specific component of reset command"""
|
||||
set_from_symbol = f"from {self.from_symbol}" if self.from_symbol else ""
|
||||
set_to_symbol = f"to {self.to_symbol}" if self.to_symbol else ""
|
||||
if set_from_symbol and set_to_symbol:
|
||||
return ["forex", set_from_symbol, set_to_symbol]
|
||||
return []
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_to(self, other_args: List[str]):
|
||||
"""Process 'to' command."""
|
||||
@@ -164,8 +175,6 @@ class ForexController(BaseController):
|
||||
help="Start date of data.",
|
||||
dest="start_date",
|
||||
)
|
||||
if other_args and "-t" not in other_args and "-h" not in other_args:
|
||||
other_args.insert(0, "-t")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
if self.to_symbol and self.from_symbol:
|
||||
@@ -176,6 +185,7 @@ class ForexController(BaseController):
|
||||
interval=ns_parser.interval,
|
||||
start_date=ns_parser.start_date.strftime("%Y-%m-%d"),
|
||||
)
|
||||
self.data.index.name = "date"
|
||||
console.print(
|
||||
f"Loaded historic data from {self.from_symbol} to {self.to_symbol}"
|
||||
)
|
||||
@@ -237,6 +247,27 @@ class ForexController(BaseController):
|
||||
|
||||
self.queue = self.load_class(OandaController, self.queue)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_ta(self, _):
|
||||
"""Process ta command"""
|
||||
from gamestonk_terminal.forex.technical_analysis.ta_controller import (
|
||||
TechnicalAnalysisController,
|
||||
)
|
||||
|
||||
# TODO: Play with this to get correct usage
|
||||
if self.to_symbol and self.from_symbol and not self.data.empty:
|
||||
self.queue = self.load_class(
|
||||
TechnicalAnalysisController,
|
||||
ticker=f"{self.from_symbol}/{self.to_symbol}",
|
||||
data=self.data,
|
||||
start=self.data.index[0],
|
||||
interval="",
|
||||
queue=self.queue,
|
||||
)
|
||||
|
||||
else:
|
||||
console.print("No currency pair data is loaded. Use 'load' to load data.\n")
|
||||
|
||||
# HELP WANTED!
|
||||
# TODO: Add news and reddit commands back
|
||||
# behavioural analysis and exploratory data analysis would be useful in the
|
||||
|
||||
43
gamestonk_terminal/forex/technical_analysis/ta_api.py
Normal file
43
gamestonk_terminal/forex/technical_analysis/ta_api.py
Normal file
@@ -0,0 +1,43 @@
|
||||
"""TA context API."""
|
||||
import os
|
||||
from gamestonk_terminal.helper_classes import ModelsNamespace as _models
|
||||
|
||||
# flake8: noqa
|
||||
# pylint: disable=unused-import
|
||||
|
||||
# Menu commands
|
||||
from gamestonk_terminal.common.technical_analysis.overlap_view import view_ma as ma
|
||||
from gamestonk_terminal.common.technical_analysis.momentum_view import (
|
||||
display_cci as cci,
|
||||
)
|
||||
from gamestonk_terminal.common.technical_analysis.momentum_view import (
|
||||
display_macd as macd,
|
||||
)
|
||||
from gamestonk_terminal.common.technical_analysis.momentum_view import (
|
||||
display_rsi as rsi,
|
||||
)
|
||||
from gamestonk_terminal.common.technical_analysis.momentum_view import (
|
||||
display_stoch as stoch,
|
||||
)
|
||||
from gamestonk_terminal.common.technical_analysis.momentum_view import (
|
||||
display_fisher as fisher,
|
||||
)
|
||||
from gamestonk_terminal.common.technical_analysis.momentum_view import display_cg as cg
|
||||
from gamestonk_terminal.common.technical_analysis.trend_indicators_view import (
|
||||
display_adx as adx,
|
||||
)
|
||||
from gamestonk_terminal.common.technical_analysis.trend_indicators_view import (
|
||||
display_aroon as aroon,
|
||||
)
|
||||
from gamestonk_terminal.common.technical_analysis.volatility_view import (
|
||||
display_bbands as bbands,
|
||||
)
|
||||
from gamestonk_terminal.common.technical_analysis.volatility_view import (
|
||||
display_donchian as donchian,
|
||||
)
|
||||
from gamestonk_terminal.common.technical_analysis.custom_indicators_view import (
|
||||
fibonacci_retracement as fib,
|
||||
)
|
||||
|
||||
# Models
|
||||
models = _models(os.path.abspath(os.path.dirname(__file__)))
|
||||
927
gamestonk_terminal/forex/technical_analysis/ta_controller.py
Normal file
927
gamestonk_terminal/forex/technical_analysis/ta_controller.py
Normal file
@@ -0,0 +1,927 @@
|
||||
"""Crypto Technical Analysis Controller Module"""
|
||||
__docformat__ = "numpy"
|
||||
# pylint:disable=too-many-lines
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
|
||||
import pandas as pd
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.common.technical_analysis import (
|
||||
custom_indicators_view,
|
||||
momentum_view,
|
||||
overlap_view,
|
||||
trend_indicators_view,
|
||||
volatility_view,
|
||||
volume_view,
|
||||
)
|
||||
from gamestonk_terminal.decorators import log_start_end
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
EXPORT_BOTH_RAW_DATA_AND_FIGURES,
|
||||
check_positive,
|
||||
check_positive_list,
|
||||
parse_known_args_and_warn,
|
||||
valid_date,
|
||||
)
|
||||
from gamestonk_terminal.menu import session
|
||||
from gamestonk_terminal.parent_classes import StockBaseController
|
||||
from gamestonk_terminal.rich_config import console
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TechnicalAnalysisController(StockBaseController):
|
||||
"""Technical Analysis Controller class"""
|
||||
|
||||
CHOICES_COMMANDS = [
|
||||
"ema",
|
||||
"sma",
|
||||
"zlma",
|
||||
"cci",
|
||||
"macd",
|
||||
"rsi",
|
||||
"stoch",
|
||||
"fisher",
|
||||
"cg",
|
||||
"adx",
|
||||
"aroon",
|
||||
"bbands",
|
||||
"donchian",
|
||||
"fib",
|
||||
]
|
||||
|
||||
PATH = "/forex/ta/"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ticker: str,
|
||||
start: datetime,
|
||||
interval: str,
|
||||
data: pd.DataFrame,
|
||||
queue: List[str] = None,
|
||||
):
|
||||
"""Constructor"""
|
||||
super().__init__(queue)
|
||||
|
||||
self.ticker = ticker
|
||||
self.start = start
|
||||
self.interval = interval
|
||||
self.data = data
|
||||
self.data["Adj Close"] = data["Close"]
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.controller_choices}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
currency_str = f" {self.ticker} (from {self.start.strftime('%Y-%m-%d')})"
|
||||
help_text = f"""[cmds]
|
||||
[param]Currency Pair Loaded: [/param]{currency_str}
|
||||
|
||||
[info]Overlap:[/info]
|
||||
ema exponential moving average
|
||||
sma simple moving average
|
||||
zlma zero lag moving average
|
||||
[info]Momentum:[/info]
|
||||
cci commodity channel index
|
||||
macd moving average convergence/divergence
|
||||
rsi relative strength index
|
||||
stoch stochastic oscillator
|
||||
fisher fisher transform
|
||||
cg centre of gravity
|
||||
[info]Trend:[/info]
|
||||
adx average directional movement index
|
||||
aroon aroon indicator
|
||||
[info]Volatility:[/info]
|
||||
bbands bollinger bands
|
||||
donchian donchian channels
|
||||
[info]Custom:[/info]
|
||||
fib fibonacci retracement[/cmds]
|
||||
"""
|
||||
console.print(text=help_text, menu="Forex - Technical Analysis")
|
||||
|
||||
def custom_reset(self):
|
||||
"""Class specific component of reset command"""
|
||||
if self.ticker:
|
||||
from_symbol, to_symbol = self.ticker.split("/")
|
||||
return ["forex", f"from {from_symbol}", f"to {to_symbol}", "load", "ta"]
|
||||
return []
|
||||
|
||||
# TODO: Go through all models and make sure all needed columns are in dfs.
|
||||
# This todo is a duplicated in crypto/ta_controller
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_ema(self, other_args: List[str]):
|
||||
"""Process ema command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="ema",
|
||||
description="""
|
||||
The Exponential Moving Average is a staple of technical
|
||||
analysis and is used in countless technical indicators. In a Simple Moving
|
||||
Average, each value in the time period carries equal weight, and values outside
|
||||
of the time period are not included in the average. However, the Exponential
|
||||
Moving Average is a cumulative calculation, including all data. Past values have
|
||||
a diminishing contribution to the average, while more recent values have a greater
|
||||
contribution. This method allows the moving average to be more responsive to changes
|
||||
in the data.
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--length",
|
||||
action="store",
|
||||
dest="n_length",
|
||||
type=check_positive_list,
|
||||
default=[20, 50],
|
||||
help="Window lengths. Multiple values indicated as comma separated values.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--offset",
|
||||
action="store",
|
||||
dest="n_offset",
|
||||
type=int,
|
||||
default=0,
|
||||
help="offset",
|
||||
)
|
||||
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
overlap_view.view_ma(
|
||||
ma_type="EMA",
|
||||
s_ticker=self.ticker,
|
||||
series=self.data["Close"],
|
||||
length=ns_parser.n_length,
|
||||
offset=ns_parser.n_offset,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_sma(self, other_args: List[str]):
|
||||
"""Process sma command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="sma",
|
||||
description="""
|
||||
Moving Averages are used to smooth the data in an array to
|
||||
help eliminate noise and identify trends. The Simple Moving Average is literally
|
||||
the simplest form of a moving average. Each output value is the average of the
|
||||
previous n values. In a Simple Moving Average, each value in the time period carries
|
||||
equal weight, and values outside of the time period are not included in the average.
|
||||
This makes it less responsive to recent changes in the data, which can be useful for
|
||||
filtering out those changes.
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--length",
|
||||
action="store",
|
||||
dest="n_length",
|
||||
type=check_positive_list,
|
||||
default=[20, 50],
|
||||
help="Window lengths. Multiple values indicated as comma separated values. ",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--offset",
|
||||
action="store",
|
||||
dest="n_offset",
|
||||
type=int,
|
||||
default=0,
|
||||
help="offset",
|
||||
)
|
||||
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
overlap_view.view_ma(
|
||||
ma_type="SMA",
|
||||
s_ticker=self.ticker,
|
||||
series=self.data["Close"],
|
||||
length=ns_parser.n_length,
|
||||
offset=ns_parser.n_offset,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_zlma(self, other_args: List[str]):
|
||||
"""Process zlma command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="zlma",
|
||||
description="""
|
||||
The zero lag exponential moving average (ZLEMA) indicator
|
||||
was created by John Ehlers and Ric Way. The idea is do a
|
||||
regular exponential moving average (EMA) calculation but
|
||||
on a de-lagged data instead of doing it on the regular data.
|
||||
Data is de-lagged by removing the data from "lag" days ago
|
||||
thus removing (or attempting to) the cumulative effect of
|
||||
the moving average.
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--length",
|
||||
action="store",
|
||||
dest="n_length",
|
||||
type=check_positive_list,
|
||||
default=[20],
|
||||
help="Window lengths. Multiple values indicated as comma separated values.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--offset",
|
||||
action="store",
|
||||
dest="n_offset",
|
||||
type=int,
|
||||
default=0,
|
||||
help="offset",
|
||||
)
|
||||
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
overlap_view.view_ma(
|
||||
ma_type="ZLMA",
|
||||
s_ticker=self.ticker,
|
||||
series=self.data["Close"],
|
||||
length=ns_parser.n_length,
|
||||
offset=ns_parser.n_offset,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_cci(self, other_args: List[str]):
|
||||
"""Process cci command"""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="cci",
|
||||
description="""
|
||||
The CCI is designed to detect beginning and ending market trends.
|
||||
The range of 100 to -100 is the normal trading range. CCI values outside of this
|
||||
range indicate overbought or oversold conditions. You can also look for price
|
||||
divergence in the CCI. If the price is making new highs, and the CCI is not,
|
||||
then a price correction is likely.
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--length",
|
||||
action="store",
|
||||
dest="n_length",
|
||||
type=check_positive,
|
||||
default=14,
|
||||
help="length",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--scalar",
|
||||
action="store",
|
||||
dest="n_scalar",
|
||||
type=check_positive,
|
||||
default=0.015,
|
||||
help="scalar",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
momentum_view.display_cci(
|
||||
s_ticker=self.ticker,
|
||||
ohlc=self.data,
|
||||
length=ns_parser.n_length,
|
||||
scalar=ns_parser.n_scalar,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_macd(self, other_args: List[str]):
|
||||
"""Process macd command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="macd",
|
||||
description="""
|
||||
The Moving Average Convergence Divergence (MACD) is the difference
|
||||
between two Exponential Moving Averages. The Signal line is an Exponential Moving
|
||||
Average of the MACD. \n \n The MACD signals trend changes and indicates the start
|
||||
of new trend direction. High values indicate overbought conditions, low values
|
||||
indicate oversold conditions. Divergence with the price indicates an end to the
|
||||
current trend, especially if the MACD is at extreme high or low values. When the MACD
|
||||
line crosses above the signal line a buy signal is generated. When the MACD crosses
|
||||
below the signal line a sell signal is generated. To confirm the signal, the MACD
|
||||
should be above zero for a buy, and below zero for a sell.
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--fast",
|
||||
action="store",
|
||||
dest="n_fast",
|
||||
type=check_positive,
|
||||
default=12,
|
||||
help="The short period.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--slow",
|
||||
action="store",
|
||||
dest="n_slow",
|
||||
type=check_positive,
|
||||
default=26,
|
||||
help="The long period.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--signal",
|
||||
action="store",
|
||||
dest="n_signal",
|
||||
type=check_positive,
|
||||
default=9,
|
||||
help="The signal period.",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
momentum_view.display_macd(
|
||||
s_ticker=self.ticker,
|
||||
series=self.data["Adj Close"],
|
||||
n_fast=ns_parser.n_fast,
|
||||
n_slow=ns_parser.n_slow,
|
||||
n_signal=ns_parser.n_signal,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_rsi(self, other_args: List[str]):
|
||||
"""Process rsi command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="rsi",
|
||||
description="""
|
||||
The Relative Strength Index (RSI) calculates a ratio of the
|
||||
recent upward price movements to the absolute price movement. The RSI ranges
|
||||
from 0 to 100. The RSI is interpreted as an overbought/oversold indicator when
|
||||
the value is over 70/below 30. You can also look for divergence with price. If
|
||||
the price is making new highs/lows, and the RSI is not, it indicates a reversal.
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--length",
|
||||
action="store",
|
||||
dest="n_length",
|
||||
type=check_positive,
|
||||
default=14,
|
||||
help="length",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--scalar",
|
||||
action="store",
|
||||
dest="n_scalar",
|
||||
type=check_positive,
|
||||
default=100,
|
||||
help="scalar",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--drift",
|
||||
action="store",
|
||||
dest="n_drift",
|
||||
type=check_positive,
|
||||
default=1,
|
||||
help="drift",
|
||||
)
|
||||
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
momentum_view.display_rsi(
|
||||
s_ticker=self.ticker,
|
||||
series=self.data["Adj Close"],
|
||||
length=ns_parser.n_length,
|
||||
scalar=ns_parser.n_scalar,
|
||||
drift=ns_parser.n_drift,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_stoch(self, other_args: List[str]):
|
||||
"""Process stoch command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="stoch",
|
||||
description="""
|
||||
The Stochastic Oscillator measures where the close is in relation
|
||||
to the recent trading range. The values range from zero to 100. %D values over 75
|
||||
indicate an overbought condition; values under 25 indicate an oversold condition.
|
||||
When the Fast %D crosses above the Slow %D, it is a buy signal; when it crosses
|
||||
below, it is a sell signal. The Raw %K is generally considered too erratic to use
|
||||
for crossover signals.
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-k",
|
||||
"--fastkperiod",
|
||||
action="store",
|
||||
dest="n_fastkperiod",
|
||||
type=check_positive,
|
||||
default=14,
|
||||
help="The time period of the fastk moving average",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--slowdperiod",
|
||||
action="store",
|
||||
dest="n_slowdperiod",
|
||||
type=check_positive,
|
||||
default=3,
|
||||
help="The time period of the slowd moving average",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--slowkperiod",
|
||||
action="store",
|
||||
dest="n_slowkperiod",
|
||||
type=check_positive,
|
||||
default=3,
|
||||
help="The time period of the slowk moving average",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
momentum_view.display_stoch(
|
||||
s_ticker=self.ticker,
|
||||
ohlc=self.data,
|
||||
fastkperiod=ns_parser.n_fastkperiod,
|
||||
slowdperiod=ns_parser.n_slowdperiod,
|
||||
slowkperiod=ns_parser.n_slowkperiod,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_fisher(self, other_args: List[str]):
|
||||
"""Process fisher command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="fisher",
|
||||
description="""
|
||||
The Fisher Transform is a technical indicator created by John F. Ehlers
|
||||
that converts prices into a Gaussian normal distribution.1 The indicator
|
||||
highlights when prices have moved to an extreme, based on recent prices.
|
||||
This may help in spotting turning points in the price of an asset. It also
|
||||
helps show the trend and isolate the price waves within a trend.
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--length",
|
||||
action="store",
|
||||
dest="n_length",
|
||||
type=check_positive,
|
||||
default=14,
|
||||
help="length",
|
||||
)
|
||||
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
momentum_view.display_fisher(
|
||||
s_ticker=self.ticker,
|
||||
ohlc=self.data,
|
||||
length=ns_parser.n_length,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_cg(self, other_args: List[str]):
|
||||
"""Process cg command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="cg",
|
||||
description="""
|
||||
The Center of Gravity indicator, in short, is used to anticipate future price movements
|
||||
and to trade on price reversals as soon as they happen. However, just like other oscillators,
|
||||
the COG indicator returns the best results in range-bound markets and should be avoided when
|
||||
the price is trending. Traders who use it will be able to closely speculate the upcoming
|
||||
price change of the asset.
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--length",
|
||||
action="store",
|
||||
dest="n_length",
|
||||
type=check_positive,
|
||||
default=14,
|
||||
help="length",
|
||||
)
|
||||
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
momentum_view.display_cg(
|
||||
s_ticker=self.ticker,
|
||||
series=self.data["Adj Close"],
|
||||
length=ns_parser.n_length,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_adx(self, other_args: List[str]):
|
||||
"""Process adx command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="adx",
|
||||
description="""
|
||||
The ADX is a Welles Wilder style moving average of the Directional Movement Index (DX).
|
||||
The values range from 0 to 100, but rarely get above 60. To interpret the ADX, consider
|
||||
a high number to be a strong trend, and a low number, a weak trend.
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--length",
|
||||
action="store",
|
||||
dest="n_length",
|
||||
type=check_positive,
|
||||
default=14,
|
||||
help="length",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--scalar",
|
||||
action="store",
|
||||
dest="n_scalar",
|
||||
type=check_positive,
|
||||
default=100,
|
||||
help="scalar",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--drift",
|
||||
action="store",
|
||||
dest="n_drift",
|
||||
type=check_positive,
|
||||
default=1,
|
||||
help="drift",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
trend_indicators_view.display_adx(
|
||||
s_ticker=self.ticker,
|
||||
ohlc=self.data,
|
||||
length=ns_parser.n_length,
|
||||
scalar=ns_parser.n_scalar,
|
||||
drift=ns_parser.n_drift,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_aroon(self, other_args: List[str]):
|
||||
"""Process aroon command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="aroon",
|
||||
description="""
|
||||
The word aroon is Sanskrit for "dawn's early light." The Aroon
|
||||
indicator attempts to show when a new trend is dawning. The indicator consists
|
||||
of two lines (Up and Down) that measure how long it has been since the highest
|
||||
high/lowest low has occurred within an n period range. \n \n When the Aroon Up is
|
||||
staying between 70 and 100 then it indicates an upward trend. When the Aroon Down
|
||||
is staying between 70 and 100 then it indicates an downward trend. A strong upward
|
||||
trend is indicated when the Aroon Up is above 70 while the Aroon Down is below 30.
|
||||
Likewise, a strong downward trend is indicated when the Aroon Down is above 70 while
|
||||
the Aroon Up is below 30. Also look for crossovers. When the Aroon Down crosses above
|
||||
the Aroon Up, it indicates a weakening of the upward trend (and vice versa).
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--length",
|
||||
action="store",
|
||||
dest="n_length",
|
||||
type=check_positive,
|
||||
default=25,
|
||||
help="length",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--scalar",
|
||||
action="store",
|
||||
dest="n_scalar",
|
||||
type=check_positive,
|
||||
default=100,
|
||||
help="scalar",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--offset",
|
||||
action="store",
|
||||
dest="n_offset",
|
||||
type=check_positive,
|
||||
default=0,
|
||||
help="offset",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
trend_indicators_view.display_aroon(
|
||||
s_ticker=self.ticker,
|
||||
ohlc=self.data,
|
||||
length=ns_parser.n_length,
|
||||
scalar=ns_parser.n_scalar,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_bbands(self, other_args: List[str]):
|
||||
"""Process bbands command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="bbands",
|
||||
description="""
|
||||
Bollinger Bands consist of three lines. The middle band is a simple
|
||||
moving average (generally 20 periods) of the typical price (TP). The upper and lower
|
||||
bands are F standard deviations (generally 2) above and below the middle band.
|
||||
The bands widen and narrow when the volatility of the price is higher or lower,
|
||||
respectively. \n \nBollinger Bands do not, in themselves, generate buy or sell signals;
|
||||
they are an indicator of overbought or oversold conditions. When the price is near the
|
||||
upper or lower band it indicates that a reversal may be imminent. The middle band
|
||||
becomes a support or resistance level. The upper and lower bands can also be
|
||||
interpreted as price targets. When the price bounces off of the lower band and crosses
|
||||
the middle band, then the upper band becomes the price target.
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--length",
|
||||
action="store",
|
||||
dest="n_length",
|
||||
type=check_positive,
|
||||
default=5,
|
||||
help="length",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--std",
|
||||
action="store",
|
||||
dest="n_std",
|
||||
type=check_positive,
|
||||
default=2,
|
||||
help="std",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--mamode",
|
||||
action="store",
|
||||
dest="s_mamode",
|
||||
default="sma",
|
||||
help="mamode",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
volatility_view.display_bbands(
|
||||
ticker=self.ticker,
|
||||
ohlc=self.data,
|
||||
length=ns_parser.n_length,
|
||||
n_std=ns_parser.n_std,
|
||||
mamode=ns_parser.s_mamode,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_donchian(self, other_args: List[str]):
|
||||
"""Process donchian command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="donchian",
|
||||
description="""
|
||||
Donchian Channels are three lines generated by moving average
|
||||
calculations that comprise an indicator formed by upper and lower
|
||||
bands around a midrange or median band. The upper band marks the
|
||||
highest price of a security over N periods while the lower band
|
||||
marks the lowest price of a security over N periods. The area
|
||||
between the upper and lower bands represents the Donchian Channel.
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-u",
|
||||
"--length_upper",
|
||||
action="store",
|
||||
dest="n_length_upper",
|
||||
type=check_positive,
|
||||
default=20,
|
||||
help="length",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--length_lower",
|
||||
action="store",
|
||||
dest="n_length_lower",
|
||||
type=check_positive,
|
||||
default=20,
|
||||
help="length",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
volatility_view.display_donchian(
|
||||
ticker=self.ticker,
|
||||
ohlc=self.data,
|
||||
upper_length=ns_parser.n_length_upper,
|
||||
lower_length=ns_parser.n_length_lower,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_ad(self, other_args: List[str]):
|
||||
"""Process ad command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="ad",
|
||||
description="""
|
||||
The Accumulation/Distribution Line is similar to the On Balance
|
||||
Volume (OBV), which sums the volume times +1/-1 based on whether the close is
|
||||
higher than the previous close. The Accumulation/Distribution indicator, however
|
||||
multiplies the volume by the close location value (CLV). The CLV is based on the
|
||||
movement of the issue within a single bar and can be +1, -1 or zero. \n \n
|
||||
The Accumulation/Distribution Line is interpreted by looking for a divergence in
|
||||
the direction of the indicator relative to price. If the Accumulation/Distribution
|
||||
Line is trending upward it indicates that the price may follow. Also, if the
|
||||
Accumulation/Distribution Line becomes flat while the price is still rising (or falling)
|
||||
then it signals an impending flattening of the price.
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--open",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="b_use_open",
|
||||
help="uses open value of data",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
volume_view.display_ad(
|
||||
s_ticker=self.ticker,
|
||||
ohlc=self.data,
|
||||
use_open=ns_parser.b_use_open,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_obv(self, other_args: List[str]):
|
||||
"""Process obv command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="obv",
|
||||
description="""
|
||||
The On Balance Volume (OBV) is a cumulative total of the up and
|
||||
down volume. When the close is higher than the previous close, the volume is added
|
||||
to the running total, and when the close is lower than the previous close, the volume
|
||||
is subtracted from the running total. \n \n To interpret the OBV, look for the OBV
|
||||
to move with the price or precede price moves. If the price moves before the OBV,
|
||||
then it is a non-confirmed move. A series of rising peaks, or falling troughs, in the
|
||||
OBV indicates a strong trend. If the OBV is flat, then the market is not trending.
|
||||
""",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
volume_view.display_obv(
|
||||
s_ticker=self.ticker,
|
||||
ohlc=self.data,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_fib(self, other_args: List[str]):
|
||||
"""Process fib command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="fib",
|
||||
description="Calculates the fibonacci retracement levels",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--period",
|
||||
dest="period",
|
||||
type=int,
|
||||
help="Days to lookback for retracement",
|
||||
default=120,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--start",
|
||||
dest="start",
|
||||
type=valid_date,
|
||||
help="Starting date to select",
|
||||
required="--end" in other_args,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--end",
|
||||
dest="end",
|
||||
type=valid_date,
|
||||
help="Ending date to select",
|
||||
required="--start" in other_args,
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
custom_indicators_view.fibonacci_retracement(
|
||||
s_ticker=self.ticker,
|
||||
ohlc=self.data,
|
||||
period=ns_parser.period,
|
||||
start_date=ns_parser.start,
|
||||
end_date=ns_parser.end,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
20
scripts/test_forex_ta.gst
vendored
Normal file
20
scripts/test_forex_ta.gst
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
forex
|
||||
from EUR
|
||||
to USD
|
||||
load
|
||||
ta
|
||||
ema
|
||||
sma
|
||||
zlma
|
||||
cci
|
||||
macd
|
||||
rsi
|
||||
stoch
|
||||
fisher
|
||||
cg
|
||||
adx
|
||||
aroon
|
||||
bbands
|
||||
donchian
|
||||
fib
|
||||
exit
|
||||
0
tests/gamestonk_terminal/forex/__init__.py
Normal file
0
tests/gamestonk_terminal/forex/__init__.py
Normal file
525
tests/gamestonk_terminal/forex/cassettes/test_av_model/test_get_historical[CHF-USD].yaml
vendored
Normal file
525
tests/gamestonk_terminal/forex/cassettes/test_av_model/test_get_historical[CHF-USD].yaml
vendored
Normal file
@@ -0,0 +1,525 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
User-Agent:
|
||||
- python-requests/2.27.1
|
||||
method: GET
|
||||
uri: https://www.alphavantage.co/query?apikey=MOCK_API_KEY&from_symbol=CHF&function=FX_DAILY&outputsize=full&to_symbol=USD
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAAAwAAAP//rJpNb1pXEIb3/RVHrBLJRudzPryNZbVSq1a1I1Xd0ZTUSNhEgNWmUf57
|
||||
Ba4TDPQ8tzLLwM31w5w7M+87cz99E0IIox+m60m4nKwno4vwafvR9uM0Dt/dv18s7ybr2eJ+dBFG
|
||||
V4vl9K9wOZnNP4aflrN301V4tfgwvT8Lt7M/bs/CfPHnWXg3X6ymr0dnX2+Ux+FqubgL1x/vflvM
|
||||
Nzd68+3V7gVlHG4WO1+/vb7c/bqOw48P6w8P63A9+3u6BXmYz8Nq84+dy9o4fD9ZrcPP0/fL6ep2
|
||||
+vvmyhxzPo/lPKeQ0kVuFzHu/h8Zh5vZ3TT8urjf3vjtzZvR9tvPjxeNtt9eT5ez6Spc/RJebX/8
|
||||
6+eB+vo3nn3+FMRNhDb3TuOoyXznzz9FZxO9pytak/0ryngT2X8vEH/2C54CtI360z2KxtGXKz6f
|
||||
HZImI1KRTKSllj5pq0iajUgVSbMokEazPmnEmIoIkQqSptr6pFI1dkmbKJJmPP2GpC1BTEUOo/6/
|
||||
Y5oakVbMqJgSnH6uqU9aOaYtEinnfj1Mhz1SSZBR5kjqiUgjkpoBqcWcu6TbEgS5XymjoiOpFCQ1
|
||||
IK1cpYwyKmI9VeeYeoGY4ulrw5hiPTVPtU/qMQKpYe5bpOc0VibVSKTe76bWCpFuf22ftCCpRDp9
|
||||
937lN35Ozc2ANCOpZYyptD5pYlLsphHrqUckzZLh9DGjzFI3pvk8c+5bw5gqVCmJGZ/TUoC0naBK
|
||||
lVKBVLBKbZ71Pinn/pFWeFClIpBy5XcjUs79lpC0Vcj9lPE5bUSKue+pYEZV75MWrKfbQtYnxdy3
|
||||
Kpz7UKVKrpRRKQEp+ygTqlKmSqefC8aUTp99lBXqpmYRumkWJJVIpOijDCv/sX77PPc9IWmh3Gcf
|
||||
ZZhRVlrfm6oaa6lMpOyj9FAn75HmAqpPxZGUKj/7KIv4nOaUTlD5G5DGk5x+30Ur61OLlPvsoyyh
|
||||
Pq2pT2rs9y0ZKBT2UZZYoZCW8sQxVSJVVn1OGVXJRR+5xT7pps/1SVlLScMe1RxiKhxTqvwDfBS6
|
||||
E/MKmj83dieFSNlHtYqkGfo++31T0lLso9SwSkmmHsWOr/W1VDovA6bnh25+f4YiMEFznqFYAdIB
|
||||
jq9EnvX1tZSyi9aiBqSs+jJWfuz7sTGpJyBl1YfPqUeYnx5rc4c9ik4fVZ8Xyn2vMEE7ZhsOHJ8Q
|
||||
KVZ+b2pAqrCP8oSV30sjUlZ9jjOUI/LzeUy57/tm69UnjSdw0cXBm5ryVDJBRiVWfR6RNFNMMaM8
|
||||
KcSUXbRXJK2w4xtAau4KpMq5L6D6XCJlVOOMoirFjs8V51Je+s/psUJ3QOoFSMsJ6qkLxJRVn2uh
|
||||
55RVX8Yq1SL4qJQ5pkoxHaD6UElnA82vrKSzUJViFy0J51Kg+VV1gOoD0gGbs4ZayqzRBI37fhMg
|
||||
lRPsTnAmXfj0W8lAyloqGvYo1Zd30wj1dICLVtxGHhnk7qs+w25KVYpdtONM2oViKnz6Fv+bdFNM
|
||||
Bzi+AX3fQKE4Z5RLx5s+krLqK4UVCpy+cY/aTOG6pHmA6kPSVkD1CW/NC50+u+hjO/E90kRVSl+W
|
||||
+4+kAyo/vdeH70wcW2kd6FMRIB2wN0UtFUnzN9ycWcsNSAe8M4FzfheKaRzgook0n2CGog5TyZgM
|
||||
Z32dbvpIOmBv6khaYHfivI/KSlWKVV/EWZ9W2khwRrUIpz9gb6o4PXfqpok3Eome0wF708S7aHhf
|
||||
asjpaytA2l7uoywbbHibckwbVH520cc49jU/TiUHnL7R6XM9zTTrM8mU+7yRKA49asjeFKuUwOlb
|
||||
jFhPG8V0wN604XMq8Ea38ju9lguRspaK3KMK7U0L9n3pTCUfSfUEpCXVYe9M/AMAAP//rJ3bsS05
|
||||
CENzmQjAxjwm/8Cm+t6/c3Z5aap3BircxiAJ+najLABpCq7Ohf0+5NMOwd0Br6mgm55mByLwp8FO
|
||||
hNmQ+QXdlNmegQpF0U0zoJZi3bQbe1ODG6V4etOvfZQLvWkvZCYcTr9mF1fSfUfKvaniP4Wqr5pf
|
||||
030/fUWNzEKkAc6uLMEpe7/7ihpZRjeqFjgRkrvoBLZHUSMLub5yePdTcB/bodPf7ESgd78C/PyK
|
||||
E8EXxRTzaZ3agLQB6adj+YV0Q0xduPvJszwHan6OaQbcKFYjK3mWp5Lmo4Lf/QNI2YOGikRVgcrj
|
||||
SZV0lVNMBU8vd3ybYsqOmegApOcbKs/K133UMxFyR7q+gDSJQ1kmqDzw7gue3mauj7STLbA9uQGp
|
||||
fUE1r4RKugVvT9sdKfems1GL7iDVnN0dRbUU96azziY9CqaOxpnnD0TKauRBpAW+vmYGbU7R6bO7
|
||||
44O/8CdS8PXN5tM/RkgFdwfOm7Y5fKesR6VB5ufedNiDNqDxzWLHzDqQpZTZSL9zfW6oRwnf6dQV
|
||||
qQkd32Q4nT6pkSPEdBkgZQatUeNL8qCV4EDcFFNm0JI1viF93wXveQNSgUET2HNAypsGOhfFVPCh
|
||||
DH2nC5hexdObvQCpMsFN+XSRas4VynjQjXJBOcNNA05adAl+KULKDNr2oe90w9034fTPuSMVPL1r
|
||||
H0J6gD91QeG991GmTMZ+6Od+cn0DvalQSa+7ImGSGkkVSocTf8p91Br4ThU10g+qPLS9gXn+def6
|
||||
TFAjK9h/SjNnQub3ohvFDFoxL2XAS20rZCaoQvFvTHIZ6VH8Rv2JxxWpsA0HHYg1xKCxt6eK3ihh
|
||||
ilM4/QPzUSeFvT1QS7EaWeiXKqqkazPT+/S3d6RH8PZQTH2amN6DTG9B5ucuugLz6aZdSAIv5Xf+
|
||||
1JQpzjXI9NIbJczxxb7dKBtBjcyeps2CoJtmMs9/f/dtFDUSt4t9Gnz9sbEtGpWzax/1IP3KdjHy
|
||||
TISQpYpiyr2pYz71pA1D9mpvz1+k8QW3XNCeiWGXvNONEtTISqxP02l3R7ADkZAKTlmsUEg3rfpN
|
||||
Wv9WzQkp59MK3jMB+TQFF9IuQMrbGhcrvDQfxXPRlQNIBTWyNld9cPouZH66UYIaWeyWCzp9RloN
|
||||
mV/oTXF3xzgxvYe/0zQ6fc6n3PE1VH29ud8/RTEVtoywB82B62M9qp9J4TtSViPRKzkL1UjB12dw
|
||||
owSnbBCDNr7jLdM7dggp81LF3ynFVNgxs4eQ8qw5+qQnCtge9kt1rQCkPG+618HZSND4mD8dG7hR
|
||||
ihqJN4q858MM2gRVKIIa6TwZS0i545tne/YdKauRi3bJz6bMz17JPxaAC9IW5k2FicOceK9GOiHl
|
||||
zJ+FqjmqPELmvzpmrJWObxZtxKjZb7PU1IaYshqpbBkBx0wLOxG6HZAKPhSc4D7nfGHLSAJSZcMQ
|
||||
OmZAORvh3T9XBu1BypnfonBzy3q9Yeh55+5IWY2sJC3aod9XvlPgUFpQIwe9558qg//fndzf/VY2
|
||||
DAmbW+B/J58Kx9/5dABpCR409PUV1VJT+Jo2ZH7uoseJl5qC/adjgltuByAVZiN5l/yhKc7hCgVv
|
||||
FDNogU7ZQxsweWNbY4UieHo375Xc75HGgjdK2TDEc9GbNl8LqjnVp4Knd/FuuS84ZtIhptjxuS3b
|
||||
4EBcXa83C3JMDyKljs8t7rWUm9Hdd/MDmR/VSDfK/P4c3R0p9qZu61BMNyIlrs+t7wyaG+6XcjtU
|
||||
82MX7bYyASlsv3X78IOpn0ih3y/u+J6AFCE1QIq75dwiAKng6e1D7uOAicPBKU63HQNIW+Cl3tdS
|
||||
3Ec1nf4SuD7848UBBq15Mnb2XTUvydOLHMqiLXjsPh5fDUj3P//+BwAA//+sndttLDEORCNaQHxK
|
||||
zD+xRXu+1h7o1KJvAuOyWnyJxSLVUYnz+8SWS+ZJd9PX56xvkqpo2nmgMLq7yKJMmDkjReFFZ+o8
|
||||
HTdOSJegfkv80yHVJudc6j51tLbC6d2Lvr4ZbbsRNl3dK76t9E2HO2f0fiqw5Df5U+6bjhvd0yae
|
||||
tKB6fxL8qdA3ZZ2JIB00VmscG/r68Q/eUKLAolo400pA6v8g7icpDAnzUd5wplLFF3im1I86b+uo
|
||||
rW24pHd+x7no4zzJ1YBU4KAtmuagrUzKtpu7ruSDlP1pLmJ27aJNV8KsOeX8Aqc3cdsNqYsp0TQS
|
||||
sj5BYYhmIx/NLmLL8ZlW0Zkqm64CZyTW6wmZdc/5W1EYKmEjG7A6R0CafUfqSi6FEzKUoQj3tK6K
|
||||
bQ/S8z6XmgX39BuZ8v/snbTQjTxnCmMUvJ8KOmhzj1Etqd8WeakhHTQpQyGkAgcNeydOOb+CFM/U
|
||||
329iPUP9feGeniCkQkeClVppa6jwzj8GFiXMRg7d01mUSQvKLbHgTLEbaWvB/L4t4vMfIZqOEVLs
|
||||
SNiXV6f/RWpBb33D7/wLkWJHwgzeUMzqrjJiC/fxmTnZPtamZgExygwyabMvw5O/ka6BDAW7kWYb
|
||||
LMoMFIbMFp9p4NfHd35rmDY2g50HZmj7ZptsH2tTswQO2oMDzhTfT81qDiA9fE9Bu8OsT74+09wD
|
||||
SDfb/iakOWT7zve06etzN7KL7mksQJp8poZIg79+speCvmly56zRopyRDn39WoAUtY8fL0X3lP3p
|
||||
l37DL6Q7wUtx19zizpUs5vR+/TO/kAIT4fkFjqbr3JG64KVg54FZNiBtPtN1j/vF3cjHXiia1iYv
|
||||
VexPg5By1tdOSM+iaOobvz7dU+esL4Pu6dxz/sdchGiagFTI+mJTNL2zOu1x/Wz7A0jZ9pOqE+si
|
||||
pHxPA+8pszt6bbT9hBg1gkX1HamN4KUo7ud5jXT1dTPLg/TwmWIuVZv8qZBLJXx9oYo+Tfd0tQNS
|
||||
ZnY1eX6u+FZgvT/Il9qIFCYOS+lGDrOQ4GViOJNe0eBPzYVMms70yzzyL6R/aZ9/1BqbbB/96dNL
|
||||
pHu67xXfQjWcz1W+I2V/Oljx+X1ftK3DX/8c8FJc8S0Dbo89l+ztPbVFFiVMcSZ0eG0ZcXpRU9bW
|
||||
XQ3nQcrsjnRid9TY68mDvHMmStHp3dSLnk0bLoV509iQnwqTsYnc8wFNhGFm1+x7NzKV2cjJontK
|
||||
isIh7OK8d3lSYsqyRYG23Ai7OOeq1/cgFZiyiYrCpIkg7OE9V13JB6mwgZ00u6ZIpZnZHRMJ91Sa
|
||||
N0VF4QUWxTsOf67yHSl3I+fQbGTRVqZhpLkIKTO7VtBsZGS9nTX/+WfuSHkqHllI8+Xh4VeH97yd
|
||||
4kyBKTteFE0zaY6PGd2r9x2pwJTtJrZcGe052y8V2h+kzOwK0pM+s4gvJWwPc/j6gsIQd82L1BsE
|
||||
Tdm4KrU+SFPYx0dsuUMW5VncNaevz/40DrGQSGdiT75VbUpl3nSR6v23ddC/OGjsT8Mh7gsKQ1Z0
|
||||
pg6ef29hEytZlMCUraQzdVLBS0VZ8ABSVmwLUhrYBzddGetKJiEV9vG100a2DVvuWKl1h4OX4tq0
|
||||
hnbI9IF90cVq4n3X6X2Qou23kT9t6PCu+jIK+BvpolyKmbI12Yj0nvNXYYxqu7M7QlAY6kXqtx2w
|
||||
Qag466u5Vyeh6PRm0Neve+dsFeuh9GpAylV0o7Zcb8ilmjm9ne6AlL1U7E1nCqr3bclIA+4pc3r7
|
||||
UIzqgVfJ5v1RHZuQCltDKe5/W2D5S0/68H7Te84fShXth/j85E97hP2mTUhd0D3fpNMLLxPKdgY/
|
||||
YFHC1tA9NMW5SFOWbf9Hwv+OlDm99IL2bAymrM9f7jx4kAob2ClGnTJQFC6h4kOkLag009e3TWri
|
||||
jHTdp41DqKJ3+aZM+uTr/aa76ExdqKNYoZ0sSsj5KUYJ+04Kp42967WXskVIWaMbt4buTfdUOVPK
|
||||
T7mK7o2brTd0zVvZIUP3VKmiaX7/26KA/zvubzxTwfZxr/nA5kAlRsWdJx1CN5LVxA/t4typ7OCm
|
||||
MxVmzddb9VtlPqoPIfV/oNmV0OE9W+jwkj8VdHonhjS7YAuz0ov2OwPRBVbnykP6UhvqqBHYHev+
|
||||
1ucCq3N5I9IEZpcxZyKHkArMLuJJr0pkIb1VF3Ouo8xQB822A/s4mIOWiJT5/LQ/6kEKX/8sZB9X
|
||||
AlKB1VmN7GPiSQvTHEUWxaxOc/z6bTBzxjpoBhWfC6xOp+0M5uvej3rGgXhChmyfZyMdvZTvBdxz
|
||||
gc8/eKbMkh94QTOPe73//ATsOvqcxx2p85lSjPIomDljFTyH6sQFVqfw9fO+OdCc76k/M1Z3pIst
|
||||
KhAp8KTtDCNdfkcqzEY6Il13BuJn9A2QLjpTYTbScPIgSFeScykD3R5XlFoL5/ic4n4Jk1xWgJQn
|
||||
ZIxnzgZmJIpzqcoNSIXZSPJSlhWvZ3n8PnHoymxkctZHE4esfmuP4tIdKftT0j03D1C/VXKprKtF
|
||||
GXfOzEm3x7zh67vgpUBN3ITZSM/BDAWyPkdG9+ev3JFy1rfxTIGFZM75qdcipJz1oT910D81D/76
|
||||
G79+8ZkG8KR9BpCyRTlUJyZUfE5qOBZ210Ezx90c5nOfOjJhNtIDMxQ80y/P63+QFp0pZ3342uMb
|
||||
Mmln7Q6PA0i5inZH2993TdlPQk/39B5NTaii3Y2rk4YzZaR2Z3eYUPEJXopeexR/al2ANBkpxqgz
|
||||
8f7r3/dGGvNPzfdG24c5Pk/M+rwLvJRSRe/XMUo4001xX6iiD8coX2BRXEX7QNbHFV8s8lIBHLSv
|
||||
RvnH83sAUsylYmM0nYCsj2tTH4r7vO+EkabdVZotcLvtI4cCts9VdGDFF3Of5bGIv0XBH6QBMYqr
|
||||
6DDy/HEMvNTUENIc8PxCxYdz0T4Jtv/lJ/7c08s7//qPuaCGE9jliQaNGT+C7RchXeylqN6PpKyP
|
||||
3/riNnP2g1SoohPvqRtkfYLt33R7PkiFrA9fz5uiqRCjEpFy1mfOb9IYTR2zPrIo7ps6vkv5DrAo
|
||||
VsJyu0xwf5By1hcCUqhNjZEW2b5SRVPnLFaA7Tt7qbxodn2QClkf6J4/drtfv0vddnP8IDUh6zP0
|
||||
Uge6PBsz6XDyp0LfdIzzU/L8UehPyfaFvukRzhQsKuNVfvpBqrygYcVHcZ+VW/yQ7QtVdApI4Z4y
|
||||
u8ObYpRS8WF/f0D91llV1APvqfCCxkiTMhRGetPo/kG65h98fWIiuKXQiyakQi6FZ1r0hsL9KE/6
|
||||
+oKm7FCM8iZlQeFN+sZD+SBl2zfMUMqgaz6Ob9LrEFLumza+9SV1I1lbzh0tivumhhrdm5Sw5p2e
|
||||
9AepwD+lt75HsA8YiEE5v8HXN2UXZxYpDB3Q52fNrs95XJFyN3IhD2XVwCZW7u//8HHvSJnTi9ye
|
||||
Fenv+adJSFldjKY5njW7oNoUwiYBRMrqYsX7TQ/wpHnD5YDtmzAbOR24R6LgTF3YFV+ElNVwNm5i
|
||||
HdojUS3ooAFSno2c3vj1YTZygs90k5fi2UgB6TRoINb8gzNldbFCL7UMZiSK1cWa7qkJ6mJOG4SG
|
||||
VEWdY1QFIRVmeRJ3G7e93hVfFYBU2MN7cJaHdhvPOjghkwVI7V9MHZFe32YlrBUHkLI/LZzkapiO
|
||||
O6yF9HOVr0i5Nn1Gl3CTAETTYIuKIaScSxXv5bnrSj7r5gMz6Ut//4O0hLhP/rSDNGUPTcismxLW
|
||||
B+m/yKWGNltvVhVtA9tfwl5zjKbd/na38fSlF/1Bquw1pxi1BmYjiye490X5ev3HlqIpa6gsmKCG
|
||||
c0bxp+uOVNGUpf1Ra5GyoID0tpfng/QI9X4Q0qJM+pdR/hcAAP//rJ1Zjhw5DESvRHHn/S82yPLP
|
||||
oLtGj4P0v9EOKEsUg0vE/89PZcOijyA3JcbXnElPNiBdRH5lvg+eBzwrKVBDkUU3UtDpSmAS4RF6
|
||||
5h0JJaSsz++Z5CTQsHfCPT45AVGKu5GitG8qBZ0z4Q6vQE1aNiz6y+TOzzMlHlXseKFid6QLFq2B
|
||||
PIqyPuF4WnSjFozPSLFtgvT5F6r3GoR0oc9fqNOLrvaz0ERQQMpZn8Ldf6pfUJk4stCSP4B04XdC
|
||||
im0zA1+fPWNHk5CyIkYVRSlRevcXr6nA73ShKcu65zB/utI9b7pRrIYzQr7mc9DZel7pnv9Byupi
|
||||
XaTQTpXe7oWa+CGkHE9VyYXZk3zNWae3C24Us+gu9DwwIzWcfJ/zLxhfNWnLNVXQnHV7gl5T7kZ2
|
||||
4O9UIJ4utOQ/ZgP/jVRmwU07SQNxzgGkh3+nkXFHyty0pUn1vhpulCyQXt/9Bymri5ELs/SZ9xqI
|
||||
cq1JP0hZXayHdM+FNLoZaaXB75S7kU1b8dJOOmjF2nIjDki5gpb07neBPv/mRj1uWXekC3WxQm+O
|
||||
E68raElRauF3kqjZJaSDNpz13bQ7PkgXvpHiVEE7Ra5Mi/qpw5kuupFz2OkKWDTvRYs4nWkuKmgH
|
||||
+X685PsynXCjeFJWJIuQBpzpsLqYGET+BTcdvFGWpIIXjPRalXyQ8t2nrO9x/QMWza5MMC/1IJW/
|
||||
MIfidKab6Y5rzi+z4qaY8yfqSnJd6nmR70gX3JR0z+dMvGUnPQI3irnpOPb4En6nPVyXEk9A6q8j
|
||||
/xMd3tf6nLK+hVJroWOwOHXNuW/aA5F/odRawtNyMIHI3BSmkB6kXOdP1JUUYCcc+f9UFC9Ie7Fv
|
||||
KjHoGAwqI+KLLs898veqG0k+vFLkGbt4TcMJ6aIbGZShpNMMWi2+vgLSTdaHisICUSoXjsFBSDdz
|
||||
KJhJk2to8Nc/aoDUFzwKcymclVxMICrc/UU3ElXwxAyiFHtyTSrdKFm426IP75ALM8+enzl3pKww
|
||||
JCWOmwek0V31xjP2D9JeKF/TjRpQav22vPDzTEsCkC5YdGMvGhwvhpUG5Nm9vyPdZH2JNZR83zmj
|
||||
N2rRjUylGyWUn27eKIEotehGir+dkl/48E4avFGbmV7yOZuG17Rn48MLb9Rqppc6EklIm6s9941D
|
||||
6Q3jG6Ee3xyaRKiXeycP0r9RQVNwvNhMy81VE+FBupjuMMpQnDZkFvr8Qjn/wu9kcLYnBOr8NQsW
|
||||
DXd/waJ96O7DHt/X9suvGsq9H1ULh8vPFCN8fdw7Wbja379+bWZ6U3H+lGrSiw2Zudeka9M3Ha5L
|
||||
kat9c84fA2fKLLoLt45UYPacdyT6USi7I+U5lCCPwxGa7mCHy67rlPyDlKOU0CRCJ8yeb9zC7zMT
|
||||
D1Jjpyvajezjr/2jWgqi1MLhEl3uKmEKKdmFuSIJKTsGe5K/qd5VRiR5i7NOOyBlx+BwQJpJ7rYc
|
||||
T9MTkDLjizJCGjDTG7lwDKY3imd6o5N8eAVqKME8KpIiP8/0JulKSuY0nCne/WgnpM6+5sFfH/yi
|
||||
ZeHCfAaQGp+pklu4e7724H7qyXekfPeNeicZRkiHkdIbtXC4fFwzwC0cMunUhbO1QeTnvmkKvaZZ
|
||||
5GrPm1yfqH1HWnyj8Osr7PIEbxx+zuOOlOMp+pvy73SB9CjcKGZ8QT4SEg0sOrAjsYin3DeNEnyj
|
||||
YH8/sMvzHDvEU57pDdKSl2jI+sIWSM/17ueCmwapNkkksBNvfvcj546UuWl4D53pXfVenNlJHCtA
|
||||
ynffaUMmUuDrc34ad0XhB2kwUto6ihDIpbgqGTDdkQtuGq10pgOvaTA7CVdCyvE0irK+Y/Ca6iI/
|
||||
xa/P8VRgN1IiYT/KOeuLSEDKu5EhlPOHlRJScGMUH0SKuVQYVCWf29CAFKuSIfcOb264qZB6Q4Cy
|
||||
4OrrW0I8XXBTw9c0ESnfqCPwmjI39eEzBUUM582DsEpAynef/KMkBjZjPTHr+xz7HSnXpcSIR6US
|
||||
3+cbNeaAdJFLkRZSCsz2hHBdSgy+PnNTJ4X2J0rB71T53T8FZ8rc1IaQOn1942qPWysg5bufiLRg
|
||||
M9ZYW8664UZxN9KFuuZeMIdinPN7HkJq/PWpfuoCfN+Y8dngmSqfKbFod8j6jFWbDCZmcsFNnfRP
|
||||
xU3p63Pkv+9FSywYnyW9+077pquvP4SUo5QW3Cjrux6KKN8oy1JAilmfRQCPsoC9aOUNGdMYQIpZ
|
||||
nzbFU6NetPLv1CLoTIOR0syE0bSc8h6fjhxAilFKMT81uWvJi/IMmsIbFYtupCXNoBm9+8rsxOSe
|
||||
88eiG6lN3NRK6XfKN+ph4lekzKK//Tc/vz7e/cXv9MDdZxatTW+Uga6kKM+gXZ0D/yDlu4+KGCZQ
|
||||
l1LO+sDx4kHq77++jsIb5fg7/bhj35Fy1neUztQhQ1HeONS+dyNjwaK1YIdXzOmNCo5S585NY8Gi
|
||||
tUm7wwT2TTUXXz/gd8qMTwtzKYVanzLjA4/DBynmUhoOSLVBAVO5eq6VEKVkkUtR31TbC+4+/k4/
|
||||
53FHGov8lG4UeMjsMhSBu88sGp1YxZpYNG9xGlTPY8H4zAtmz13zHvnN6i2P8kU3UtPo7hdMyrLP
|
||||
mZjfa9K+4Kb6Rd3052vq8EbxtrHCNocvuKk6acwoecioLO7+vSPhC26qSWqNSip4apyf+tXj8EHK
|
||||
uZTTbI9W3pEe1pjRUrhR3I3U5t8paCIodyPVESnmUiak2GYGWkha/Dt9ZgTuSDmealN+alDnX+Sn
|
||||
JhWAFOOpGVV6LaBzZqyD9jmPK1LuRtrBykSAsqDy5oHBvqkvupEWjZUJcBJQ1ui2U/A7XXQjD2jM
|
||||
CHnFi8kiQ2k6U86lDGvSB7Tlvv2JnzVpmO3xDTd1zPoKtuOM96Ps7sX5IF3cfdJAtATtDuMenzl9
|
||||
fVnUpWArflHr45zfjLK+RTfyHIqnB2bPV9UeilILbnqEXlNViFKyiFLHASlzU5zuUFd4TTlKKSPl
|
||||
XCpaKeuj/FQwSilsHPqiG2lOWp0WMNmlrN2hgUg5lyKvo6fHdyCeLro8d75vi31TV+qcMYv+IvH/
|
||||
i0Xf9/hsM9MroIT1mFXckTrPofhdoV1s040cpQnEA5oIxpqynxnnO9JFlDqYS4E+/4JHWQ+dKWZ9
|
||||
Z+ZQPAUedXjnTI1+p8yiT1JdShtmzw9//TPjgBQj/zkD28Z6oC512J3h5J3x2YLxHeRRGqB8LTx/
|
||||
eizhRnE30gy7kQVR6nCGco4S0mYehXMoIfK2G2l3P74HaS32Tmj6OEC1yfIwj7pvc9hmptdxi9OD
|
||||
JrqjcPacIv+xv7B1dOg19V5MHxNS5Q1u8uUpBZ8z/yLx/2uHtyFKLWZ6kzKUUsj60n4njr82YxUi
|
||||
P7PoGsr6vv2Ln7vm9DvNpCjFLLrQnaGrQL2Bd85qCu7+ajeSzlSbds15hxfYiW1mep10e6JBZ8K5
|
||||
x0db8baZ6Y0idtJ15/t+MEq545kaz54nnKmTz5kz4/NQyKQXLLpoBs2LXtNY8Kj719fV/Cloyj5T
|
||||
vzApK8xO7h7cD9JaVHv+lSf/AwAA//+s3WGS3DoIBOAT5RVgy7Lvf7HUzNs/mXL0kfJcYLdHMtBA
|
||||
g25Z36YaSiOPqjqB9Gj0+MSkJzx/VaPas57fr47+NPV+VA1saE9z/tp5pu6bBjO+DdlJno1a3zru
|
||||
V6PHl0PRtLbEmXo//1v+s0bqjO/QBHee2NuTo5GbrvefViPjuysrfCCdmDnr5PuvDctrpKdrKJrm
|
||||
qMD8fpqh5MwEUvrTPJWd5M3uwT+RTt/+a2f5GmnD9rVhqG76lR9IXZmYsihnfFU609qllL0Zrf60
|
||||
/TgPILX+dJdWso4NMSodo7ANpzr60zqFdGA+qtKK7vWLwS+k0aj0SoW0YydCpT1/HUDa0Z9qQqYO
|
||||
+NNyL/pNYtZIzaUOas+n1HK71XJD32lDf5rFyYNUrc+3f/D2rZnYx1PFzBZWzCSRWjPB9062UxOH
|
||||
29VQdS6RZqPHt3EnwnZKhdSYOoKuL1tKWSLVluaGnn9D5yw7SlnulyopZlpK2UtIz4YC0VNHOtN8
|
||||
qurMThZ9UYVUU2q5vaFAFNK9oZgR0jyEdDRUnUJqL1XsR2mfdMuf7kLqulTl4xjVmeHl7TeUsjvP
|
||||
9IrH6o6iRcUXbn9jNG2cqWJUQyk7aPuH5k4atb6SRTWUsoz7e8iivFnwPeK/RmrWV9djhuJd8o77
|
||||
jSzaE9y7mPTZ0J/yTJ1FT87wprhUZypeXqqR8U1WejXJVeanb5azRNpQyjqP0tamskVpijM7U5zX
|
||||
8fg7bXApsj5n0VvVY9bXmYrn7R9fOFNtbulsGmA0jc7UEfkpFTPnF27fXGrLp6yvN3UkpOZS6exk
|
||||
PM9Nt79Xe/L6ldXJorkH7W6zzz/PnVQKaTyvoPlMGwxlUUP5H2k16vzD3Uj0Ts7GFOcQUnspvnNW
|
||||
gWiafusoF/ukf5A2lLInkc543I0MWVRDKTuNVLdvTW8uuNQPUitlJxlKZDzuR01alG3fStnadKad
|
||||
biSQNvqmFy1KuWl6u1gtXrn7QWrWF/HYoub5/Dtt9E2PeuylRsP2S0gPI93Z35dFecfM+7+skdr2
|
||||
B5UIl87Uld4cRFr2UkWkYNJpbU8ymmZDh+Lbl0X5nbOcRNqYPFCMugtBHzMSjTOV7TuLDvrTLPDT
|
||||
8KuhSX7qjC+Zm6b4adifxinP775pxkWksihvGXnfyxqpuVRRhbTrTP2OxGru5Acp/Wlwa1NqD1p4
|
||||
E1YWb3/tT+O/62JlIpZe6vUnzFDiXHOpTjcy+IJQLPtRL6RzbfvvH3MJKblUsBcdqTP1jplInim5
|
||||
VBTPtIaQMu6/zAFIDyOVlwr6U8+cRfFMh7/TQaTnc6RbCSm5VLAmHRP8NNzje39ja6TpuE9+ql2d
|
||||
4Zp0HPxOw56fXEqdsw7SawfSRm7Kvec+U+9Ciku339D0Gqm65h3WFzxTc6mURd1x7Q+ktihk0Z1u
|
||||
ZLCCluqdhDsSmYqmzk2DrzOkdCjh904y5Pmz4U+T/lQxamt4qUNI7U/LSHX7njl7H/sSqbuRQa1k
|
||||
DJ2pNb3B79TdyLtY+S9M+i907BPprhjl3PQutfhEiqpkWCn7mkkF0vF8J0KWPL+nOWKKSzVy090Z
|
||||
n2ooMZ/7U3cjs+j5pexK70LKfR33o9GNTPajUh3e9DacBJOORjcyXOtL+dPGJBfyqOhk0Ttz04Mx
|
||||
imca4xBSe/5RU54fL1uHlV0xUkid8VGFdJe//Il0jy8g3b+RR4mfunMWk9+pOxJ86yilQYuznuZR
|
||||
0ciic5tPvVT4jcOMKaT2p9yAeTf6+NE7qeeev5HxsS5lpNHoR8mi3I2M4dxU3+nuuI+6VDS6kY2q
|
||||
5FAe9TrT3wAAAP//gigAAAAA//+03UuS5DgOBNAr8SeSuv/FxiJsajHZ2XpeRs2+LNKLFEAADjj+
|
||||
3UtdQnq9kJ241md/Wvmd2p9SX6pokqtYXexbg31G6oyvs9JLL+XOrtJoUY6lyPCWLY7PXqp0ef6A
|
||||
jQzyqHoeSy3FUkkWvRmh6Ez7PM2jSpJF84367fv4gTSIpYg0sH16qXmLOXMNZcjzB2wkZ3nKEHNm
|
||||
NrJUWlRQQeu8fTFnK/BSiqWcRZc6j2p9X6T3Ya2v3Eluepdj2/d8FDx/uRM28jJ3Uo9jqfLcf/pB
|
||||
Os/5/XIz6vOZTp6pK2iDGd9Q1OcuefD7H6TjBZaH8an7pOsg0qC745yN9K74bwLzjNS2b90e1qW8
|
||||
h9e3bzbytz6DnxalM60Bd7KEdJ+faVmM+YP4tAjpegGppuNKc4SyiDTolqvH3XIJI0Gk/fw1tT9N
|
||||
GF5aVBBLjWMu2jXpAKljqd2OmTOrNQa2H+Sm89yfjvMIxblpwpyV4zcKld4P0vlG/bQfd8pWxqcB
|
||||
G3mfd8m3cVg//SANOmU5c1aGOrqN9OZ3GsRSvP0u25+B6j1v33X+4lhK2Ym1O8r9WJkoO8j4anfM
|
||||
X897z+8bSM1G1u3pOE5zOObvl5Du8ynOqvg0UmssQroCtUZOx+3z6bi9hdTzptzD29SHUj0X3SrP
|
||||
tL8wczZ1+/VoF+d/kQbzUffx1JGn4+qkRQW6kpXfqd6oYDIWuelOdHoZn9qiggnu5wmZsoMsunFG
|
||||
oqmr8zch37+b4P4gDbYyMUJhb89dzm3fbGSggic+qjUrt/QppMFuY6o3qH7azEe1JdsPdnFaq1M9
|
||||
aAnSwTO1Tu+yWqMUhkag1qgzTRSG6E9vKQu6eu7bDxSGLn2nTd3HiU7vlJcKdHq5SeA3kbsfetL+
|
||||
Ti99p4HCEPOoXqTZ5f7TNhT1BVm0/SknuB1JN0bSARvpWh/rp3egiEGLqueTB85N2+nUUVlBT+9v
|
||||
vNhPpGIjvZcHkwcfpEGt7z7ulVzulbw3kAY9vdP9py/MnCGSXhFvuo9ZnhGw5kVIrxcUMchGBvw+
|
||||
qj0r4U0Ha9LS7DpXxPggDRSGyEhIT7p2V3rHLaTtXAvpNwWqH1m0c9O1gDTZzGI9FNVQrkBlZApp
|
||||
0NPrM5USlvtQKj1/oDBk7oRs5D5Vb/ggNXdy2fav81meytsP/CnffWXRv9FA//BSPNP2wmTspMbM
|
||||
OvengcJQdceMmDPXT789V89IywudXeaig+9U/jTo6b3O+vrS+FTvftDTu2lRVBoIYilaVHkllpJF
|
||||
eYL7O4X1jNSxFDdbV00b/yZS9Hc6aB+kjqVY66tznkd9nbffXnj3/UYFUR9tvwY7DlWZqGTN++m7
|
||||
PwM28rfi508+SlVJd3Y1dCLMhI3k7Tepidcd7OKsQuoK2kXuZNbzqqRv34wE+aheyUgYKbzUTLaG
|
||||
ciq+qWPmN7HXf2h0E6ljKdZP2yBrXk5Znhn19Fqndx+/pupEmMpNv9JgnOZ43M2RzkjIouobim2P
|
||||
U5zfWZ5GbTn0Ss6kp9dTR49aSF8YnpGo8qfBbCRjfk9yBVOc6JKfSU/v6OdzfMfTHDPJTa0xc1/n
|
||||
SIe8VNDT6yy6rPPJg8UzDeaj3NfXj/v66rOedJnJbKQzPvb1eRdnrXpNA4Uh66Gw+7gH6rdLSAPt
|
||||
jn7czz+CqfgtpOMNlREiDbQ7ZPvITV/SlA1URdF7fomN/PyZ+sjx/ctj+b9I0df3DQ2GkBYj7ULa
|
||||
MG28oSn7/Y0OpM1R33Nf3+dfDM1FFyNFre9SFv35jeda33fseehMl8+USBeRLiLdivrw7n//t0VI
|
||||
B5E+c9F/FJYfkV5WaabtN3upwjNdvH3b/pTtN2p0702kHXnUXv5Oyy2kgT9tQirl671o+9/zeEZK
|
||||
f7qfe3u+SLeQDnupC0jrMtKLSPv5maIT4RIbGb5RlyyqGKm+U7CRX6R+o9q57Vd9p9X+dNCihnQm
|
||||
SjFSeanaX/BSQxYV1FAGLcq2P4R030Lab3upCqSFsdSi7e+Fd39t2/6QRRXGUrvo3d96o5Ztf916
|
||||
94v9aetEighl2fPvQqT0p7vzTBVJ78JYajM+dca3GUv5TPcyUmUn3neyG5Eqj1o3I+ldFUmX6jM9
|
||||
tv2NvTzfe3lGOoLcdBdaVJs6U98+4tMR5KbBmWqHzIJ6w/evLCGd9vybSBFJr+k3qkwhvYx0CWnZ
|
||||
8qfNSJuQjheQ8o3ajk+LLMq56R60qKHbX28gtT+9FEnvayqWsu0P3b7ZyCDqm4r6ht/9SxZV9wu5
|
||||
aeGZHkd9I8hNg/qp2Mi9/ZqCNx1JbhrU+qSBWF2XgsrISHLTVY8raMO56cXb9+44bmYpRVXJ6dx0
|
||||
yUtV16UWs+i9z8/0eX/UB6nr/ORNSxk6U2fRmDkbSadssI9PZ+oNQqXIopybBnzUIxv5/QlrIRXd
|
||||
frHtk4suRdWebS9180zHG5sD51nHzJ8GkGek/QWlVm4N9YxEgXrDSOZNy3V8pgFvWpWdBPOm7piZ
|
||||
/f9uUf2dfSfsPQ8mY6Eq2pN9J1a+5qarQPf8KkK6XpiRoPptCyYPmpDOF3omdjnvmUBnV092yFzW
|
||||
6lRXZ6AojBmJnmwNXe7uoEXNF870DV1JdssFCpibSN2D5k1XjPoCVVHwpj3p6bXydWWnbDn3/M6i
|
||||
77XOs5Pj3p6eZHzXZnaiLqTRT2P+nrCRk1w0dx64hnLT9s1GBl1Iqp7ftZ1mfD3Iovft7g6x5ttn
|
||||
2vidBt0dRNr2cR+K+P2esJF7HHfLrYDfJ1JX0IbrUuzt6acdMz1gI+9BLzXppVztGTxTx1LcJFBq
|
||||
OUd6yUslO2SYR1VuEblOZyR6kvEFO7n47q/Tak9Lujqp1Po8d/LnUT/bd9KSrk5Xe7ryfbM8N7rk
|
||||
W8BG3t4hQ4sKuuXQ3dECNvK3ssLfWlTwmkIJqwXMWfCd+kzraa2vJV2d1D0vRbbvNyr4Tl093/an
|
||||
88yfBnN8LcmjhicPhLQbKd79luRR47hP+h5GimpPS/Kowajv0ndaHPUN2X6QR5V2ijSZ5hjyp0Ee
|
||||
1X2m13mnLCKUlrCRl5Fy7sReqstLBbORxf50nuf7l2w/YM68i7MolpqePNi6/SCPWrx99fTe0+/+
|
||||
ku0HeZTZSL7707a/9J0GeVQ9502DuRPk+y3Jozq5k6FpY+v0fvnsZ6S2faqMPG+4DHfG9mekNVFq
|
||||
pWKbVUUD7gTvfk2UWr3dds7z24c/rQkb6Zr0TaWB+5Tjqwkb6U2s3BqazO9fQhpsC7fOxDxneGcX
|
||||
UnspKrUWKgtWT8V3WVSw4bLx3b+k0xvsiketryZsZFCXkj893sD+QRr0THhXvGzfOmjqQ6mJGs6v
|
||||
/P5/AAAA//+CuRQAAAD//7zdW67gKBAD0C0BRR7sf2OjvvmbvsrxiGh2YIVQ1MNlZ/G03OsrnX6g
|
||||
hsNcqss/KlFrPHT6gVIrc6nfhAP/pSoaeHLpjQqUWm+6MklXMtKUVYZihaFuxwu6hwWqojf/0/6B
|
||||
Ti9dmaaVWg8ibV+4iOj0p9UajwtIA4dLuzJJXypxZeK7H/idsN4fUhjqdg3tt6KUFYYC30grYNrn
|
||||
rOmNShwu7RqqeBp4cwzdqECp1b6RZCBasS1A+oF33GjkSwX6Ul1IA+Vru9xt9qV+vsfL3f9jHhVU
|
||||
0YHuuSq+xDP2jYP2gzRwuKSa+JCHTO+Bk8AQUmd903rSqk4CH4k3nvSDNPA2Jq9PqqK/jdX/qqOa
|
||||
kB4feB4c93YPpb8xZh6kga6kveL3ddB+9OnekX4Rperer6PetOUepIE+P50ETvb6nJ8OxdMedNDM
|
||||
6VXFV8dWV/JBen/hyqQodawtRveDNNCUNfe87/dQTiKdH7Dk2UGzh8xrn/9BWh/0T+kd5xv12pN+
|
||||
kI5977h2U1O2bfVPH6SBpqwjv3J+uzIF/2mgKct4WrpRK/DjO4A00JQ9+e63fWeWduvdDzRl2ent
|
||||
crhsLTh93ahkbspppHjSawU9ab1RCf+UHLSTc9M9ruSDNNCU5TctsZDW2o380TSSOX+bVL/d2438
|
||||
QRpMI63Qznd/+D+tS0gDfX6e/sV90777RvWkNvVW/PhgM7Z3IQ3iqf/T+QFnYghp7Vcn79vGPzCC
|
||||
KNWEdPwf7I7gRh1TSL+YRl7tg9qUSAO/E37Te+3np/ME0sTvxN5x3OL0jO9YQnpubsdFDMSAKbv4
|
||||
Tc3pHdfuftTqgZq4XtOA02ukVMJa5vMPftNAqXVftSlgIJbiacDpLWvKiidtTdl7CamVWn8j4/9X
|
||||
7nn36U/lUgmnl0iXWJ3DfP7Sfxpwek8rt5AnHXB6DyEN1MXI57+4zbGnL/UgdTw91/Y2xzCn91Am
|
||||
baXWZa+j4hankZ7KpFuwF23uub5poC2HTm9L/E6sJi7Hi9W8FY/JWUv8Tux4wSgV3ChkfS2pos2V
|
||||
HMf+Ht/dhPT8QFuOPZSr79ZRLeL0ujalc6Az6TaEdH5Q8R3X/kRi3EJaH/SlOOMbweSshNRVdPmb
|
||||
qidtH171pVoyjSzOozjf7wFTVlEqmEZORqnr3mfJT939hNNrj0MqYQX1/iGk5wccNM6jAk2E1YX0
|
||||
+MI5UNPIO+BMKEoF00gzuunGeDqe8jUNOL2NSEt9qctdybWE1B008vp6V+Q/ze1pQhpoyjrnv6/9
|
||||
/ahbb5Q5vckml3goR9/aj3qQBnff3/SDeHopSgXTyPva3uY4g1k0kZrTy8lZ59w0QLoUpQJOr5GS
|
||||
JV/mn57KpANOL/VQutQa+xybSNsKppGD+enQ6fdrj8//IGUuVTz9OjHhHQGfv0pITyPV3S9F/jIT
|
||||
od67PX+QMp4W51F14T8tT87qfcrzBynj6Rx696eYsr/9QH990yak3UiVn07NTacnvPM9P/2DlLlU
|
||||
lV7TOZD11biN9AJS16Z1+T+9daP4RhXvvmvTYnUytXVUx/B/qhvl2rSKSJWhlJWv6+bpT8fTydNX
|
||||
PPXUvGoKKXOp4nZcaZtjmNlVh+6+a9PBnL86Tn8E/2k7hZTxdBzc4xP7eDiXGheROp5yL7qGvql1
|
||||
zwdf0xbkUkQqxkxZE6G6MhQzZQc3uJ31iYfyDwAAAP//rJ1bkighCEO3JCJo739jt3rmzs88PKmC
|
||||
HaRaW5GQ5H/FdkcaHWrjQ6uPSOfB1efzFJ1a3eA8nfyOmkk1/xDOU1z9GXSeCvv0vvpH0EY66k48
|
||||
sOa3BqSsikenVj6lhD/qzpyNI7CR0+nkd9LwTmZ5fCBSQRWPdxTd+8ZT8nMhUlbFJ+qijToTrJG4
|
||||
quI/kbIqHlffaFbyN2HKDw0vflPuoOHr5Dc1+jddNCu4/RBSoYOGWh5ywlL6UhtXX8g7wV4f8qZH
|
||||
8O4IQjoa8qOwey5kyNyZs3EUNpInETLqDO+ahDQ71BxeZ3gn3aYCGxk1h3aR37+rOV6kzEgkq42p
|
||||
z7+EmQk6pRS9aZ3fV5KukpCOBncxVB4okwj0RwlsJGp476lM6uo/hFSY7hgl5YE425O0TwW9KfZP
|
||||
R+IcyinOobxIsyOPj2YmBFW8I1LvWP1TV8gEnadCwiVOIvAd5cLqUy0l+PQ+9ZOfZyaAix5bcRhC
|
||||
DS9rzYvOgp9IR91R2OYsr77dtTxjSwmXXPXtetUHlfRWVJyHXUaoPhVyI6HXt6WES04NpX3Kvb6P
|
||||
rXxHKlR9XJ+id8es3vtbmekVppDQEYPvfXjvbynhkv1QKI9PSAtP+qbCTC/O9Q3K4/stiuLH7Dmd
|
||||
p0IyS0PWkXBHDfymwis6SjW/oN//Cr++I81ijoS0+skTiBO/KTtiJOpNDykOZy1H4hOpMM+PnYnA
|
||||
xAtWHG6qpQSHIcFdzOuV9EKko2OqE1Xxqzh/Orbi0zt49nw2THQvQpp1pwGjORRlpvehP0qY6d11
|
||||
T4QleCHRvS/M9O4o/1FKheKEVHAZ4bdpQyXNf5SgkcDVf7J+SsGkbCraSHbAREdhIfHiPif9Ij0N
|
||||
fX706lScsPCb7o4pedScPUXF4Yu0Qx+FJ7+gkDlJSKPBqZW8kIxVnAbcSSraSHzv26R/fzNSc0LK
|
||||
tZRz9xzzTp7qvZ+S+y2+TbOBj3Lap8LblDu97NQqOAwh0tOQHYeeXYIuetHqC2ykoDb2+j71IKQ8
|
||||
3YGMhDHLI2TH0b/PbKRw8mdDhkzQbcpvU3NUcRIbaaw8+OjA35Faw+rTKaUkCTjd+8xGGmp4J538
|
||||
xn+UBVV9Qt4J6vc5mYX9+e2h85TfpoaMhNEcihlPdjlVKCMa+Cj8o4QkgUCkfJ4OPk+tjvSeefAi
|
||||
FVJEoj7ZJSSz3PdpKGwkp91srKQZKUzMhMJGHk5ko1oq+Y+C7nkor2juS2HWkQlcNCIVMg94Bu3U
|
||||
+X2Y7AqFjTzcl0Lfc676HkS6Glhz7EoqvCmuvsBG4h2FDu3K6yQIqZAa+pTfUYKXPHAnobyi+R3l
|
||||
6Cft1Zo/FDby4Dtqkev9zmoHLYiN/ECKq2/ERSf79R3apwIbeZjh9do3/dogd6TRwUWTt1wwx3eo
|
||||
QhF8etlfahEbyZ6yxEUHvfiY4f1jEPbbNxVW3wgpuzWym/ig1RfcGmEOJRSfXsH9FpHyN4W+VCg+
|
||||
vZllpDGKvpIvUj5PHy//+y2rvxqSrjCZZfMp9VAtJbCRqI5jB8xHSAvHbyrUUqfUPf86LEt5Jy/S
|
||||
Bn0Uc9HClDz0+pbwNjXjSdldZnmIN12KNhL3qU16R20h3Za+qcBG4tvUDnYmODEY9ukS3qZzNOhN
|
||||
GSnMS/0DAAD//6zdW5LsOAgE0C1ZEiB7/xub6K6vqXb4cKNqB1gSmEeSGY3adHJuOsUwdLeQ+r4Z
|
||||
+6wh82Mp4+kMbxtrf9/90zlpqXfN2Zea0o2cYaaB1O03GIaoIrKCPBPe4GaUajAMsc+/itwd5piZ
|
||||
IUsZT2Mok44ldjHvR63gmZqxbZMLSYiZtdmVDLA3RIdhiP/9UDy9o3b5w9rEM2UuFWRpDnV6lxHd
|
||||
66Lvm7Et5FGh+X4MZigRiqdGygYnZyEupFh+p6nbN1I2+DcNRakwH0qkPMrTyMW+VCy+U3pUzJSl
|
||||
zKWCudTdM3yz1PH0VOQ/GmyN9ii9U+enwVzKtWlQlyc05WmxNfKdOp76HyXujjCiO7B1tBoVXyNK
|
||||
aZMrnPMH+qerMY2M4O1rah5GHwemPKsxjUzu76d2zWM1/lGnLGXWl5ycpW4/jUDM4pmWz3TQUpxp
|
||||
+kwTk7PVqPiSyiwpPH/6neazwuWPpYxSSWWWLFR8aaxkYn9/dXh6ucOboQzFDJi5ePuMp8m+VGrX
|
||||
PLwhEynfdxUdnEfl0pl6PyoPWmrfP/hOVe+H66i49Dc1pjf3+WmUCnPKNs6UWV9yblqHfN/dngTP
|
||||
xGpU0ckOWon7ON2Z+D2PZ0sdTw/evhCIaXRHht6pK76kWniqjgrj+n4/5tlS51IXb38Mnan/pkO3
|
||||
b0xvkb2h1OdPqzLlxTNlPC1uyJRQSOUeSpUyFFfRtW0polQZe15LGYorvuKEdx+ooutonKnqKE8j
|
||||
kz2UuyN7y099pkAfz0YdVaz4KhVPPY3UP2o26qhiFV3il+p41LNq6I+l9n32ULbQx+X/fqHXNxt1
|
||||
1Cae35aWLd201L5Pnok9D0UpVtF78PbH5/F0H7LUOOkKehTrqM16f2vvpMyHUmC/nY06apMRY6vX
|
||||
t43u2Kl46jrqJEvznWj9/y21etimR7mO2lRlOlNnmsylziWPch1V1JDZYmyratx+ytLlKMUMRf/9
|
||||
cgetTnrU/EJ+Kh608uSssHE4G3VU0fdLvt/KT+VRnTqKWZ+mkWmPqnPKUudS5Orc2uZIszfkpXfq
|
||||
OmpzcralxNr5m2K+Pxt11OaEd5feqXfO9qRH0ff35b8pznS7h7IXb3/49s/PLfWZYkdiNHYj72LQ
|
||||
++0r6zunz3TI0sN11PmxR5kBs7AjMRrTyLpoqbSNK65P66jRqfiOg/991abuoNWl2zf+NImU3cJK
|
||||
pncjfzvwz5YaM8HJWYlfqoHtyT1lqTFojKd3QIM3DJpn0YvvdNpSIhE0OVuu+BbfaWNyNo3skm6k
|
||||
c/61FaU6+FOikMSEtaxrLsTMaOFPD1qqMw1rcoU8qoM/Japz0dIG/pS3n1/QjNXsZE3jT7fiaUPh
|
||||
khwzK5ZU7hqqoSVLrXDJvei5xdxyWpMLM77RwJ+OUpSacX6uHjZkqavoyUx6LlpqbrmSR7mKnkQf
|
||||
3ymgvm1zMPJPaHOMjsIlETNTqM6OpaV4avzpZBU9hZbraMUzkzb+dJ5WDdXtm7encfvz89tf4uqc
|
||||
xp/Ok5aOLyixUjN2NdQYH27/h0q8o3DJDGUKfzrH8VHkf1lqHjRuxc8lxjZ3eudBSxtbnGYYEk+v
|
||||
WUbG01b8y1JvcQ690yE8/x21y58tzkOWmmGImrFDisGHFS+Gb39+YdvYlppZ8Akt97LU7GLr83fa
|
||||
ULh8QiG9LHXWF+ZBk++bs2ssWeoqeizzSmqH16xNg/G0wSlr7mMpBt8JkP2xVL7f4JS9eKZkaXaf
|
||||
/5cJ/NnS+AL/qXrSd1Jp/xxPGwqXnEcNzaLvRLv+hQHzZekXVO6GML2Ht+OOk75vRgyikP5ydP8H
|
||||
AAD//7zdS47rMAxE0S1JpGzJ+9/YQzqzdODDhow3z6DgWDTFz61fSk0Xmzr7BYdLM7rJPi6w5e7u
|
||||
pm+l8wG/ky6a+Nij4L2VFvj85/7X1CfqzunqrdRM2UF2h7aNK0Th0NkvMGUL7OOpnN/sjjt6w1tp
|
||||
gSmbvPHtO7DHuFda6UYWHNgX2cfOT48Bpb6bdpNaNX/ao0C+Tin13XTZ1V61Ps+f9jOkdJqGw/dU
|
||||
81LdnIm+Dikd+880dI/6Bpz+dd/ne2o+P3snjlLe5OrrlNLY9znrdLpaBRbSlNL+hFc8PbkKfidN
|
||||
Sk2+vuwho6zv2KM0/yjtD/jw9qB7WO5mKJVu5DcQ/F/rUkfBg1vv6SN3U/qdFFjyTfHUd1N7xbsu
|
||||
VXC8aPpGFe6m9pCxg1DBK16Rvz/hdTTJQJz7SgukVkapb+DAD1bnntfRW2nBh9c8aSld5p9eOvu+
|
||||
m367An/yT8f+M23K+ZvJ19w3/Ybi/GB0F2ji+ppWSK37POlpnvRaUhoP8KTH3H+mzE8LpNZglLqt
|
||||
n/6wjwvexvf/fivM9DZWz9tBJwF7c6Ay0Sp+J80uzMpPW8HbWM80HolSojR74/CHSX2v9HzA7Yb5
|
||||
qac7FKVaye+ESkUZaYXZHvT4WuUWXaj1KZfyZNct+/it1Ldo0hof8Y47QkrNlKVvZKjS+60U/Evp
|
||||
CaWFbiR9eTiF1H07iZFS6hm07mk5/fv+mvZ1SakraNzfD32j+uWJmdB76lt0Ya5varIrc3e2pxVu
|
||||
fMHKRGqDuzIvdSieFmZ6uc0R2o2MLMz16WtamOl1R0K1vvA2R8wupY6nre/2TqIV/n3F08pMLyO/
|
||||
+Pzd/X3VpFuhGxnkTEQqSnmbI5oy6cpMr79RoX/fSrvOfmGmt3D25384+4WZ3oNOApN8/rF/9j3T
|
||||
W6Dea7YnTBaM++p5uyqTsqxLpeb5w/umGV1K1/5EdzZ6HnjrqPGZMpdKRqnsPFGFiW4q9X4UeX1J
|
||||
xwvX+vI+Sr2UOp5edmdQ1mcmQt5PH7+UxgNK6XhxWWk/pJS5VJJ/mqpMfEtxf52oJqXNLHlyz0Vt
|
||||
Cs/0ZtN7WvA7oc8ZlaanOvPSM/XddHBK3i4iBer9dUqpdyNZ5/8GDP6r48XoiqfuRianOlP9/YqH
|
||||
zH2X56XUfP5udwY9U2coeT9/+lJqPj/npYam5L+tq/9lh/et1LTGg5TmQ+4Mw/v7p5QWCEMkYJrU
|
||||
2s0/XfqauhtZoImTfG0C5hGKp76bDu+aX6KJVzxkLim13wmZXUOzkmny9eDZL/idsIYyRGxL75yN
|
||||
VDx1N3LwRA3V+fMwZ+J+tqetQjfS3yj78ni6Y+BErYrfyWTkX8yl/ExXh1Lfog/On56NngdUOuaQ
|
||||
Umd9pz0P5HYzzOi+n5Z7KXXWxwraUJcn3Y8a9zsSL6XO+kjAzIvkFmfSiPyrQhgK3vgYpZqVnjr7
|
||||
BcIQ56RT+/vpDCVjSWl/4D3tqXjqCtrSe1ogDLGCNlTnr7ynp6KUu5HJrG+IiZCtcDuhUnckputS
|
||||
pOGc+2fft+jwidIeXwz3TZfOfuEWTXfbb6S0j6pkQSlPlM8+JxFGktjm/LTxPW37TqypDm+6Jp1L
|
||||
8bTQjaTP2WDXvODEOpWhFAhDhc7ZuV/nn3ymnu7wLg9pOM5P0Tl7KfXZ576p+1HOT2OGlOYTnTP1
|
||||
Tq4CB03xtNCNpMtdshdtB/ZsfKaOp2R1Jm983ubI+93INiu3aFKaU7vmcTmeojIxCze+JFsu2TX/
|
||||
/ME/AAAA///C1pLGjH0AAAAA//+s3dttxTAMA9CVoviVu/9iRX+LIocX9gaEbCQmKVF/kK4DO7mc
|
||||
1mikkzW11se5k3ErU9ZJA6NY03Fgw6XuaQ8UtA9r2g/sN+UuTmcijPc+lF+kdWDHIZVeO7xjfoTU
|
||||
ChqnOcZN/bSs9jxAGuT0el80a5ooaCWkz4G95uqXSlKaefoBi/ZLusSiPW8qZWIljI+ORNO2m3YF
|
||||
6jnvadtXJhpPP9Cl2hTSe5/x9aLD23eViZWwaE5ztOfZd82X/lEJi+ZLWtsYExY9LiANfFNOHVmV
|
||||
DPwofqWuuV9TIw1c84tI7UgwvYEv6VbBPRU7CVj02lbQope03lJm0f12Z5c6Zpwu1t7Txa6ZuJE8
|
||||
/a4MxP8u8pe+6QzcyM7c8wDp2O1DmYkbGXQhTZ2+veiaQnpAQevslQz2mhdr6u9pufdc6nkwI/Gq
|
||||
Sv4AAAD//7TdSW7dQAwE0BsFbPbwW/e/WCDt4th6DKTsDaN+D1QXh6oLaUGjm5mJzryU735w9/ON
|
||||
/KmyPXa7yc01bc9nzlLqYjkKSFNI44WJww8nuebD+ahYBW6aH3739ZLO/nk4cXgiXc+9Ob47yf9c
|
||||
48MLZVWqkQdf0tGf107uNRBPpH5L8WvKnol0n3QeXFNzU04b96H3aZjxNZ7TfGFN5R3XrYWkqaNV
|
||||
qEYWOmaUk/5uHPmvNSXSQp6fORT2STdz03uVkROp81K++6xFWwGzL0X+AjctdHbpnFoRQ9Mcq8JN
|
||||
PXmw++NuuWs05R5pYT6KUergLI+j1Ecv6UI1kgpDjFIFpHncv/pmoRqZVBkpVM0f3/1ZYNHZrCgs
|
||||
VdHCrPkQ0sJk7LTOhCK/e9AS3HQWGJ87Znprj3c/F5H61cd6VJembK7CBDd331Px9uWRr3nm01nz
|
||||
E6lZ9GLkj/nf2ckssWjG07Ye90qqt2dWGF+YRUu35yjs/hDSgsIQVUao2lTolQzdKFcjm7mpatHN
|
||||
9f1EBm0WWHR0vfmtftsLSlg8p5NqjfsFXcmCUqvufrOuJPulIqTXB3ZyrQdvVCPS+6n48y9ufXjj
|
||||
17HR3XH+j+TuB5HmIaRTux9e09Dum0UH/fgiiXQRadeamkUX1BqbPGSioCtJpPY76UQqnYmKHx/P
|
||||
qauR1pNuQ7qS7umFJ9eJtL/gJKC5k3DdFArtJ9J8QVN2S0veKnix9TUt6PSyAzGUP41e0JS9z5+O
|
||||
QjUy7CET4/E5jZFCavVbTnGG8vzhWXPonp9I1wuOF6ryVHYftehRUb9lPDXSXC8gdTxlNTL2C4rC
|
||||
iKej4ncSn8fv02O9gDRe0JOe+4XvfgNSc9PgjEQMaR+HI38SqT0PHPlHx+4XvqaYkRjiptfLkS/p
|
||||
LdX7bncG9J4PVSOvBQkhvVUVvZBOI9WNQjXyB8OCf13T9JoOrqm56b1a4/Vb5CMRBScBRf4CN22D
|
||||
3LQJqX0kkkjJTffBGyUXkV3wO4Gy4FA18gc2//VGKTMBnd4Cix4Vv5P73vPrjGn30+eU79NwXurj
|
||||
eCpvjvSaTq6p4+n9/H4JacFFZHNNC/GUealDb6npG4X5/a5q5A9GRV9zffzuGylq0b3id1J4S9GV
|
||||
yRm0I4HU1cj4kPFpOi7ce663VK+4htrxYr3gbI2O7l5g0Y1dSG2rItEKeaklpOM5i/4OyBcWbR4F
|
||||
ReFeqEZWuCk9Ds2jFtfUGbRlD27m+uxzthWlKiza2R59ozxvWlhTx1NOc0QnN3VmAvOmveIayonD
|
||||
kFZnuLsjQlGq+dVHNZwY2v3tu9+nkE6/+f+8Dr8BAAD//7zda47kNgwE4CuJkm1S979YsN0YIAg2
|
||||
/hjYm/+DnoJkvool8nc7Dtk5cy61laE0quhwdbJVnRjpkO27iq77edKfcr6E1Jl08Dt1xed6P1BF
|
||||
F7Q9n/+i79RbQ+tgzj+Q9dXKF5CSQatL9X7J8xf0/J8/0e27ii5WJ7VRm5Zr02KMalTRQQZt8js1
|
||||
0lSMcsXn299zCWnD9pXzN7aGDvrTQG1aZ7xw+65NF/3pwTNt8FL3Fd9UHdXToYg/7ehQMGlgqo76
|
||||
YeGgQ3kh7kODNlVH/Qv99U8GbYrrW0a6hLQR9+mlDvKnZnpXCOk0UkbTSdtnR6I2LSoe7jb+2fZ7
|
||||
/53aS+0tpFZ25fGclTQvdRWQNuoo6/qUSe/L3+kl22/UUe7vL/Gn1kmrbzo7qk7HqFO5VDpGLZ6p
|
||||
/eliR+LK56pO1Kaz04283ONTl2c2oiktarzRO6ln3+lPp+gWaUPVyU2s44pn6uNGR2I2VJ0NL8Ue
|
||||
X0eHotv320jv4B7s8njTlb2U30aOpPZcM2UbKvlRPNP1BlKx5zOeKrtmR9Vpnl/TcMbp27fth3XS
|
||||
g+y5+NNjPj3TaLyNHJwzEYtaSd5+gOuLTjcy7E/VN230TsD2RKfi45a7MZWfXu5Goo6Kjv6Um1mc
|
||||
9aU5FGy5C+lPfzQCONPzGdIfkeI90uOFrvnKF+L+FNKGZqL+NNJG1zw6FZ9z/lrPmYmURcU2h8La
|
||||
9BQnnfW0OomO/jTJ9tR+rkKailHWnxYnYY0Rz7s8tChX0b8jQP5r7+Qyh1LyUvG/MGiN7zR4pmbQ
|
||||
zucvDg93JDZtnwxarWCXB7afDa7vpO27G0mtZJE9n/5Ol77TRjdyipUsZX1lTW9h9nE0OmcNpnfI
|
||||
osK3H8pPrT/NoueXEiEbuj5sto6G/jSXvFTSooJxP0sxyt3IJIOW6vBeye80l7yUu5HFzlmpI5En
|
||||
89O823L3awiaKr5fv2HNxEDOn41e9F1t+kE66aWSWV9uIQ0izTvNxBdpvYBUb86uog4l795Ff5Gm
|
||||
bV/faS7Zvl/Ff87jHik9fx5Eeur2p5Eunqm91EEvdW4hDSNNIZ2+fWUoWYpRy/70IFJmfRmK+6n3
|
||||
UTnyhTO1P2V+mscp22eGknevjj5IXUU3kFbIn+5H0fSL1LY/5lOLurbP9E6H8kXqrG8T6UbOf1mB
|
||||
mEP+1H3Tcn9fWslsxKitaNqooi/WUVv609MKxOSZOusLZn3yUuV3fHXJSzUqPnJ9JUV3lvWnUxZl
|
||||
/emeZCau+UIVzTPNh9rzn+lA9wxaY2qTPH9Df+qpTQcV3Q0VEs/Uen6/NSfSyEcatC/S+QLXZ+25
|
||||
Laq2kDYYtEF/qpcH0yr5fV+bRqM2LepQqOjOvW37AaSuTeui55dipnOmpTN1bVqnddL7z6rkv0ht
|
||||
+9O3fyiT9u2fS0iPF/jTU6ykpzfUIlLnUsVoOtQ5M4dSiFHRqE13+NWRlLLX+dSfRqM2Leb8fiPR
|
||||
YHpDt9/oRs7r8UuubMR9+dPGNJx8/n5/NqaM6PYbml4jjUOTBtw3TSL1dLFJdQfn9pg9v51/+kV6
|
||||
WNm1qeySqnNb1zeJ1Bo0vjYOzZIf3sD+mfd5j5TKruDW0NBsuRhUywU9/2hMvuY+Pk1vCO+PijOF
|
||||
1JOvT+7kEtLpt+YzlPVZ0zs5V3KmJrR7zsS8ppB66r03XGpGd2N/1PKZeuq9N7F654H3SFynkHrq
|
||||
/Wmk2h7mKXi3O7m+SBua3sff6fg7wfoXAAAA//+03dmNHTsMBNCURFGtJf/EHmYAf9gY38MHtTOo
|
||||
llpcikv9BSli/lbo6Q12yob+07ZtpdoDpJWdsrT83NJsddtoOtPKTllua9Q2nALShqp5q/T00p4W
|
||||
Jg/WC0gd9T3c3DLn/dzJw//0hR3d3imbnjx4jpC6n596J026PJW5E3jTVtiGU4ikBzdheUZiCWlh
|
||||
p+yxPdWZWpUpgki9XYx6J8ENQ9tnCva8VSZjD5H2fe+jkJ20SsaX9lG6/WUrdWRPrXDZzrj2ppuW
|
||||
P1CLbgWFy8Juuf5cIy38p46llrfgSeuoFXQkQkitHUc9vp8kYX9HOj3JteSjnPHFZm6qmbPwDsSe
|
||||
W0itHTfjNouObQ7lyPJb76SHsxNlfG3fZnytlEVbOXDq9q3BHbJShSx6U92WKsyeN/3O1f+OtJ3C
|
||||
bGQ6i54v6Jp/Zs/bqehGHuvw6j/d1uL8nEV/ITWDdqjCHOKlpvX4zhRSKwYHFS6HlANXQYn1CKkV
|
||||
g5s1uMVKnoJe9BLSfEGDO4S0oMV5iNQM2iTShwrsVgsP3r51eHn7fXd5U3PSk0idm3JXZx9Eei7Z
|
||||
83ZKWpz0+8EIxeq2TZbfupGduuZduuaxCrHUENJXdHipGVt4+11IHUsl41MpsHer2jtCiRdiqQJS
|
||||
v/2U349XKhK8fUd9W1aqUI3cvn35fW+9/xa//ox02+/zRc24j6W23n6hGnmoF50vRCiNtz/vfVRK
|
||||
izO8Cylp+QtanIykzUwU/L6Rmpfi7fcpv1/RiyZS81LTfp/qDNdId6UaSTXG2PpPh3mpz8zEF1Iz
|
||||
aFS5swa3NwzF3EBayKJZ4TWDZnXb3nX7hYyPEUqKP+1WEui9CeljpPRRUmLNsD3NIaSM+nIRqVRE
|
||||
srsPZfBMnUVzX9/osPxpZiI3kdLyD27CGuqYyUIWfYTUGd+YslJD/Gk64xs9hdRR35HlH6qap7Po
|
||||
76/9jNQM2vHtL92+WUmwPbuSRa/BF6WY3+x5HnnTQhb9ODvJ+x60RaT9DaTq6+vO+B6+/XihHsUz
|
||||
tdJVf+SjCj29m9XI+dzzUlMRirPoWNwqqv2nlahvy0o5iw72ofRIIXW3HPjTXalGOjthR7etVNCb
|
||||
FqqRtKddyoE/NTD/v7rpF1LHUu4+nvO+IrGUnTiLzu4a31YkbR/lM4036lGsnPH28/OseVuFjM8c
|
||||
CuumhYwv2xZSWylqx/1UB/odabpb7hCpe9D49kOVszYe90stITXXl+yVlDdthY3Ci0jN9QV1Ix/9
|
||||
p81sTx4hNdc3fPva0X22O7v49guTXOxBe9QreXymg0gd9eW+7uk9eTnJ1VYhiw5OyIQ4lOi2Uqid
|
||||
rEpP7xz38anP9POOmS+kfvuu8XUhHQX+lEjd08vJg64qTzx+UZv/abpLni9KsVRlNvIh0n6fR/U2
|
||||
7ztlgy+qUJE4129/uBMBzMSqTHGyn/+nRoM/YilnfJ+n476Qum5KtZufko8/eH4zE6G3X+jppTcl
|
||||
0jjjlu1ZlSz6eN5UtZPuM+2KpQpZ9ONOWd3+KHTM8EWZQct+24XUu3vPEUnPSjWykZnQvGksd3fk
|
||||
A6SV3JRn2sVLWUfi2zx8RlqwpzzToQhlFnJTnqnt6T63/VKx/aJC/2mhGrldkdCZnkJvzxZSxlKD
|
||||
OmdDZ5rm+tJIzaDxRY2Z/7zGNwu5aXKOb6hTNrd5KfRKzkJumuw/HYO16GmkA0h/VSP/AwAA//+s
|
||||
3duN7DgMBNCUaJGy3fkntpj5HPT2qQt3BoRsPYr14IdK6eAe6VDabo5ZrPQ0a+6vv593erfOU2PT
|
||||
vshFy8PbViFNcU2D85T6U+H9dxLFv2t66ZQ6AkaCPWkpu3q50ptr6r1PDVpLKdvGUb8eq4+Vmo0c
|
||||
pjaNlF3tbk+f+vqBpnfz3m/t/WH/dLpUaaDpJcuj7vm6g1NKb6kAm5I76UV0sp9qes9kaigVM4ud
|
||||
Xt+m0p6fiab3YKd3mIkQ+PeFTirI7nB6w/2cj/qcM1E7mRpqV/zB3B7zUej07mRqqKcwS4lQ7vXV
|
||||
Z1d87QDxOV/qkLqj/EIpsDw7SRi6nd2hLCSz5siX+ql0f2FicGnvW9VZNysNciW5prWfV3qyUp/8
|
||||
t/e+KrWm98AdtQPEV8yZOOTfr2Bm7EvnacBG0nlwtLjoy6lNL61pwEb63teavgt/+bc8lJ9K/epb
|
||||
5E62vDwBa/7SeRpoeuk4XFv608AbiW7PTlycfknTGRsoZUe3aaDpZbcnUHb5zV/c+2YkqEPpen2B
|
||||
O+HXry+saek/DZyxn2dy1U6csZxs3WIk1nk9X1Mjvl5Ob5AzdgeqTp2nZiNn3D9V99w5vQ28vwM2
|
||||
cohORvz+2B81I3RiFG0vz5b6ePzqm5tran8UHYdeU/v4Bv/pBGxkU4M2yusLen3zecrdT6VBwhBP
|
||||
/t3PvTzDNb2+kDHDO8p6qfXimp7PvTxWIjj9VhkzE2DTdVCDdt569QU6lK1K1zcS2vWflistfv3j
|
||||
OWve5xfSxL2mZiRYKf2mK0gaADadAJs2k69nkTW/nzK8k/hN3T03H+VKT1ZqNnKO5/+pT/51qVKz
|
||||
kUYn2vsJw4u31ATY9J0i4h9nc7RTm3q4pn5LXfr6W+m3yfv01CllbDp0cW7598eMxFyq1Gzk8N4f
|
||||
MbzT5vfvVqWXK9WO2lWq1F9/a0cZm451KLd2lJWys7WjAmzK+VFzLaETK2Z47wfYlN7IGZ2nVsvN
|
||||
YqWBsos46pQOxRndgzuqAzayqT62Cik4pcBIdJLTSx/fuz/5zx3lFwrcHJ0kDPE2dV/K6Q09S5UG
|
||||
ml7q+ZUysoIkLNymnSA+vqUW1zRAfEhq7YCNXOPpYUzEMI4armkw6Yp8lPj9BO9v7f0ARQd5fa/n
|
||||
PWmk4XSi6XVaY/djbNpVqDRBfHTIbPFR1qD1oVMqYCOpPe/9erz3u7WjzEYe6/WUN63AebD49e03
|
||||
5auPbuPIa669H/hNnTTAKSIJa86vb28k9VLvckf/rKmTW4CjOmAji/z+oQnsL3clj9FbymxkkZGo
|
||||
jdnGr5PnaS2tqVF08X1aSsIq3/s12vtG0cFENk4M9ilVfJ8a8SVaya1KPZNr694PNL1djzVoRzCR
|
||||
TYjPbGQwL/qjpvd3Tf2fNneUlbJ0HFZrxmGgPoaXZwWIL1nTC5W6z1/gTVeiP6UOxbPjnDRQUCKs
|
||||
JA3H6Q3C+zXXU13fSiZcXp7GyPmmj9MblhDf/4z8/HtKaaq9u5K/YxA/V+pTium39WKlnmqPuTwr
|
||||
mXB5+o5SEpaZM3kkVoD4irk9Jf9+UimcXCtAfMHelwKxrOzCfNOfSgPEx0qplA1w1KHzNMiUXc7t
|
||||
YbrY/RTxrUR/enneibI7gqn26J6vKFPW08Puxz0U5UquRH/qyYGcIHQHnV69pYJMWas6NdU+SBeT
|
||||
CmkliI//6aG+VNlxqMS2FXB8wax48aYJOnnplALi+7mR+/2a/gcAAP//rN1bjiQ7CATQLeG3vf+N
|
||||
jSpLI4369s2DlPnfqg7hBBsCgn9nzvTmr57kGvxO+Zba7EGL23v/8xPo6rzsoSiFjO/6N7fZyecv
|
||||
Gqo9C/Omlz1o0yKk6545+/wX5aZr8NW3wUYWZXyf3xhLSCe+04UK2mWPENJtpCGkG0g3POr6L0dI
|
||||
F5HWTpui1reLT39UIZ1Eet97ftm0yPdtU+ifFmXRl2fz9BfyqN9+4ifSxe+UUerc16Svv9DpxyDS
|
||||
RqSMUocedehRg0hPZZQKIuVUfIRuU/RM/LXHLdLCeHq6fP8cVXoL4+lBblqURX9+474mfZ2+GAl0
|
||||
IF72GEI6bFNWJdt4fvrQmCni+K5/02jT9YyPuuyheFpcQWPkP7c9vR/fP+FXH0/f8ZRVydCr77dA
|
||||
9xPpUpQqCe0OMrxFp+/36dny/UTGx/7TmOJOEjVp+n7wLXWKtTsYT9dT5qyoU/Zy3COP6sqjVn9+
|
||||
m4bj6eIL5RCp7/3C77Q/r/OHotRp9qhFpOWF2/TQ9336d6qin5H4zC7OBB+1nt+mdz1oX6TxvM5v
|
||||
1aa5H/FRF9KE+i37T63aNMajOv8XqbU7an9sUzNn5Y7h/SI1b8oKWlFHd5jfL10eleBNyfGZ5TG/
|
||||
XxqRepMAb9MiTYQY8ah++kVa3/B9qYuNhO8fIX3j1Rcv8KZ3d9SFNMGbdt6mW7fpSWwQ6kKa2MrE
|
||||
V5+mOCMcT+/m+L5IEzpojzdbx9qPOhG+SB1Pu3dzUAnL3MlW5M9sZuE0B7fa26Mc+ROdssX9py/s
|
||||
j5p6SyV2cXKWp0xuZfK+kyKkiU7ZxGYWRv7y3KbhChp1z0PV80z/6dJtmuiUpbZcNHUhzUR2osif
|
||||
6JQdjvzK92eiq1O+n+FN3YGILHq5ZyKG4ql509VUmdgV3+k827yp7qgEb1pUlVxDDG+QOVv9/i1V
|
||||
lJteSMWdrI7vdG7WUFYNIDUbuaYYibWQ789Ofn9FEVJW0NZW9XyJkVhQxLjsMYU0wUaqgrbHEFKz
|
||||
5ptIu6MUuegxX2Aju5A6nga56NrU3bEcpZaQhllzdnfIo7aj1C6KUmYjEx0zSx6ViPxTNi2J7o5J
|
||||
pIr8u7ljZgipuzsO6/zBPhTbFPd+UW76P6XvH0iHeiaOGd7gd2o2cu/H1XP3oVxl63ukZiMH2cho
|
||||
z/n9U4XUjEST7x9Ox9VEJwKRJro7yJqzV7IkuGjdUXFe8KgjmzZ71FSUsm5PUKk1Br/TY5vqhZLo
|
||||
6Z3POmWv9DbBRfP0+wu1PtWlErs5greppzgTLA/3mlu94eI97pHWF7aFT2rJuy5V75FGgo38be71
|
||||
p011+t4gVGoD0sQUJ9UaE9+pK2ixhXS/MHXEzSzt2STXF6knuZYrvdrEmpiRaEVI5wv7ojXFWWZi
|
||||
s3UX0oRuz+M9vMV8VC08fas1Vuqfas9ZtXpDBb8fCTayWwftIJ7+Jjr7H8U2Ig0jpa5kkwZiQrFt
|
||||
KPJ7MtYKmF16KD2hJj6GkNL3B1WbhrYyde827usIKV99g5F/SLenu84/Sgip1RrbJFLpSnrD5WDk
|
||||
T+zi5E6uLkXhNhM7D2jTxG7jx/ui23q2NfRCajayjefK180bL45O3z29CX1+bWeo03sjhyK/2ciE
|
||||
YhuVrxO36dT7NLGL08rX6kGrzTZtivzOois3sLei07fyde30KE/FU2GoTSFNbBIAdxIJ3Z7KqaOE
|
||||
qqgnuNutR8VJ5KbVG9kOtTq9PWx2IE1oyg5PxXOLiDURzhRSx1Pm+zz9jNLAOEI63thyp4zPvZL1
|
||||
Pt//ILWmbFABU12d5SR0JZuQthf28alf6jY3/QMAAP//rN0LysM4DATgK8UPSc79L7b0h4WlW/IJ
|
||||
2gsEEVuyHqOZ/4SHZ0sdT4+raMXTBndH8J+aU5Yo+UW1m2xE/oClRsruK7+totdl5mtG/vEDXXOy
|
||||
iU/jT//+x7OlDVUm+v4iS3NDjXHIUiuzMJ42LD3fR/6GMosZhbcYhoxAXFPvfoNhiL2+Jb2TebGH
|
||||
soaiVINhKKzFKfWwq8GFpCjVULjk6U9VfMOKwTN0+o3aNKwaKkbhMu/5VDx1bTrZQ5n3L7jkaen+
|
||||
ngmL/3S6M+HX1NPIxdfUvt+wtBT5OwqX7PSO8/27fynyN5RZuMtjhUtzde5nPP91OsosRMm7gzYa
|
||||
eicXLHXFt6x1JAX2T8/t/7qSU5ZaP4p70SGuzuUotZ4nEi9LmfUl5/spfdPtruS+b1nKrK+W7mkt
|
||||
eFT4nubm6TPyBzcOS7Po8OSsnvFSL0vH95pcUZrx+Z9G8vStymTtOLGKLnd691Q89TRyUj1spV5T
|
||||
Kweu0OmPhioTu5LilO10ep+xPS9L6/ue9OREwuy3k6/paKgyWYeXisHpPv+Spb/ooNX+uuKbW2+U
|
||||
p5GD93QpQ2lg0JTzn86+aRqFJJ6Ju8HQTo9q7JvGt5YOn/5IRSlXfIMqzPNS99xTnpHKTxvTSDK3
|
||||
mPnas5Nxy/evX0wk1Jca9XX3/DSq6GXf37/g6OY9dRXNTJrv/jInwsIbVZ2Kz1W0sr5lNcaFyF8t
|
||||
LU72+pbmpqfxTwuWem66btamQ6c/GhMJWuoq+nAicREx49N/3pB5WWocCjET1jZ2VxLYnpel+wd9
|
||||
KSoGX9eXeKmXpVZgH56cSYPbUWozSnUwvbyn0o1sYND2M8PQy1LHU2LPt+b7n4Ql3y1dvKfG9F6O
|
||||
UupKGnu+nrmQrmpUfNsqzFS2NmZi3fKoDqZXdVQoP93unu+teNrA9JKpNY6Qsp5HxUVL3UFjXypV
|
||||
8W0ztsWhpYynyU2ulKp9uN6PyXtK3//UUnzrSsr3w/P93LLUc9PgLDrlUeHaNLZyKc9NI9g91+Qs
|
||||
nPVFKZN2FZ1Ey6WmkeFeX6TeKFfRedn35VHW5Pq7Qc+W2vcZ+VPzqPDGYS69pp6bxlaGkmPpnpb/
|
||||
KS1lLpVkFqxrKEot+/5zlMpGbVq3LD3i68vwP60DS12bJiP/kdJVut4/QHZlZxpZqqNKW0fpzYMC
|
||||
Wi4700giZWsO/VP7ftHSbUs5NxWqM83SXOj1ZaM2Pb6nqvjSzIJ/ckjPljKeHvZQbnnU8TbHAWYi
|
||||
G9PIQ7TcPXBPj3Op+5m352Xp8enrnt7KpY5zqfO8b/qylLlUUeHymQvp7xO2lG+Ua9NP0fLN91Xx
|
||||
lbGSNfVGuTYt8kqW5qbprO+PnfDZ0vl9PPVruulRlfSo4XtKHIpUQ9OdCcdT16bFjcMjtfCatnTK
|
||||
912bFvPTo0y6HKVq6fRdm37qkLyjkHT6xkzUkUd5Gpmc8DLrC2tx5lKUuhq51GSU2sqlnPPfyqVc
|
||||
mxbZb0ubsTkavk+Pcm1KnHRJmyPMieD8tFGbutcnRowwpjeAk45OxUdEd2o/KrxzlkDLRWMaGcyl
|
||||
UnPTuBqdiVuWNjC95EPRRGKXO2jIpaJRRUcRfSzWpt3YOXvWOnpZGj/ooKnPHw2WkeI/ZZRKsjdU
|
||||
qi9VfKMC86hoTCMzDyM/+1LhzsQlS68f/FNtcqV1JPKZtemKRhVdRHcw68vgG5Wpe+oq+iYS4VZf
|
||||
6hOJ9/trSt93xXdzm+PWBvdxfnoPWsqs71bFNy52e6zNcQctddbHedQt3p6y74NR+GWp4yk3ZI72
|
||||
TvI460vlUv9W0f8AAAD//6zd6aozNwwG4CtqkWyPl/u/sXJyKJR8YR6Vyf9AhLyMJb3LTU4b36fS
|
||||
lZzGoK3QfeqKb1MJawslv7LQ6dUt5WnkUfc8Q/t0uzOxu159nkZ+6jr9z1tq2ZXppE5UoYr2PEo5
|
||||
ne1xvX+Vqmh+9/v1vNfnE+WzH+5KKlJ7HMJFJEZhGjmJlHUVXfju33txxijUpoPfqE9dljddSb+l
|
||||
7pWvfyK1ysgkO05s40+g3/c6amr1C7WpKz5qdRYwaPc6vT+Rjuerf2keNczhHYuRFljxRB+f59od
|
||||
Y/JEtW/sU2ki+Ox39PpGSf3WvGgxuH2fDrCORgUpe4jqbENn36uPN/+oIGW/oNNrxIwY3KMwjexc
|
||||
/aG+VDuFnOrmd23aOOXpi2qNBQ4vc2q+aTNDhro91kPZ+kYV+KZEzDQpYqRzKrbxqCgMdfOipSzo
|
||||
iYT0+kZF/ZZs4y4t+WY18ca3VHxDu4M3f0Gpdejmd206WO+beWBsj/j7o1CbXsT0Fr5Rvvm73lJR
|
||||
YB0JM2GN7nCkW2ff08hOReFx5WN23Ejm1IptdLW3Tu9Mn6j7s98rfFP2pbpwKM16KG0fRWq+6SHr
|
||||
SCj57ldfBxKhV7iRfKEMOQhV+KbMqaeRg6z4S7o93bOTvhjp9VwNZ8jrqJ/C2U9FalY80XJjk29q
|
||||
xiFYR72i09usJS/tjr2evqR7oeLLsHJLl3aHX31gxffCNNLaHU1I2SgoYoRW366hxzf/lFt4K6iM
|
||||
6D61a+hpcgw+8jY+YTfGrn1acA1t9Dhc8opPe3FCq7NXXEMncyrP2L3tcQgtpF5xDWWvL1JOrJPe
|
||||
xmfyRPEtFZydRFvPc4peX68oDHUqDHV5xh67MYZOlKvoZBWd6vakFduyK6cFhSHf/Js9FDsH3jsI
|
||||
/URqje6lSPtW97zgIzG5+p5IhPVP5cpUUBVd+kYVquj2XBPBL5QRuk8LOr2cmo/9he45FDFaCdU5
|
||||
nmMlPY+C0kCroDo7veM28ad2DjxLkVpnwt2eSU0E5vTCW6pVlFqpg3apiv5kLfiHyx1X31xz9qSn
|
||||
8PyfhH3+QB9fitQ6E43Y86mzb7zUxNS8FeqomVQakP7pZdaREN2tgOqci2yOTvQxV39yn1a4kWRw
|
||||
X2Rwj6fo41bhRhrXJ170snLLQg+lVVCdVBZc+xKLs4CX0tkvcCOJ6N4h/GkUlK95oozsoo/E7sJK
|
||||
DiO7JiPlfbr5Nd2Xcmreydrap66jtpmx0pX8tIHeUZ2YnbRCHbXJi95ytl6zkFO9UFxHfdIQeV/9
|
||||
pX1qpOyl+9R11OZ835Far2+HvlEFbiT1+jZZ8a5NC/vUOhPsnm9VfJ9gwe/7FGi5LEzONrs9R7o9
|
||||
yxqIG33+NP40Qz3pjE7eibU7unJK/GnGQm2aqT7/4Zs/A32pdBWdIY/DTDldHX73M7hPWUUn5/s5
|
||||
wDnLIPb8Nx/3keqWyqEXSl5gHWWnr3nCL/onUt1SOcU1z9XuVz8H9VB+83EfaTKn0pRNsTizE4GY
|
||||
MxlpcJ/qJZ3CSafn+7/5uI2UVXTO1ImaUBTOPrxPMTtJV3w55SKSuz2P9MVYvo/00jxq60T1cduZ
|
||||
iL/P2uFImdPBSO8Z3K/BGGZ8a3IaucHizMI0ct/f/D+/uJ3vv3LKCe8OfaM8jdxLE969NTdF//SV
|
||||
D519VHyvv0muvvbp8erzGxXbq38Y6RdWP3WigkiEvZnTAyTCxte0ltMCEmFpvn9pFo16/5UPnai4
|
||||
jEPRLXWEQ9lgyPz7L/eRsuILujCH9ulZzOlZevMbfxpEdIf6/GGNmZg3+3T8la2AP01iJlLM2Eqk
|
||||
FyNlFR3En6b6/GHHi7zDTLwiNYszOTlLseLD2sd59zX9jXQaLacOWv5nn/4DAAD//6zdQa7DIAyE
|
||||
4f07TY1JAofr3Z/UrKpGfJaSTdcjVDDBM/4vlYZd8h8DyFrpZh8KiWyv/b67YxtS2k1kG1xT8aPc
|
||||
31/6UE6lfOsLOmVDCe4COy4alZpyxxxfiMwS7kgsOWcfpQXCJd/5m3LR4fM0VpNaT6UmXJLEar+U
|
||||
Jwq3lVP2VHo8QBBq9EuZar/vUlpIcdqFpOSBX8+XBKFTqT1o7PHlGPeTXN5RTh3ZJa9+VPpNOg8q
|
||||
jQcod9pRzRmJbKpRhRQnuebORroflV11333TxrqfG4nBTh5MralTnIUMr/Z+DJ+nXedpgXDJ/P4V
|
||||
bPFb6SwQLqm032dxpiYLNk8ZaZP/U2d5Bu+nk0pdoxrX1Ocp7/wpJ8JV2Ocnccg1fSIbOcWOM0ci
|
||||
Ufejwo3cuaOmfNJZSHKFlB4PpDiZNS+w4/aUUt+lOPu4a3JL9oKjm2u6PVD3NZ8/TRLQDSUK36Y+
|
||||
pQp130q/eXx/5+/7HwAA//8DAAAft5FK9gQA
|
||||
headers:
|
||||
Allow:
|
||||
- GET, HEAD, OPTIONS
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 6ef64fc5bd56d90d-AMS
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Mon, 21 Mar 2022 11:22:47 GMT
|
||||
Expect-CT:
|
||||
- max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Cookie
|
||||
Via:
|
||||
- 1.1 vegur
|
||||
X-Frame-Options:
|
||||
- SAMEORIGIN
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
1355
tests/gamestonk_terminal/forex/cassettes/test_av_model/test_get_historical[EUR-USD].yaml
vendored
Normal file
1355
tests/gamestonk_terminal/forex/cassettes/test_av_model/test_get_historical[EUR-USD].yaml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1278
tests/gamestonk_terminal/forex/cassettes/test_av_model/test_get_historical[GBP-EUR].yaml
vendored
Normal file
1278
tests/gamestonk_terminal/forex/cassettes/test_av_model/test_get_historical[GBP-EUR].yaml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1392
tests/gamestonk_terminal/forex/cassettes/test_av_model/test_get_historical[USD-JPY].yaml
vendored
Normal file
1392
tests/gamestonk_terminal/forex/cassettes/test_av_model/test_get_historical[USD-JPY].yaml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
53
tests/gamestonk_terminal/forex/cassettes/test_av_view/test_display_quote[CHF-USD].yaml
vendored
Normal file
53
tests/gamestonk_terminal/forex/cassettes/test_av_view/test_display_quote[CHF-USD].yaml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
User-Agent:
|
||||
- python-requests/2.27.1
|
||||
method: GET
|
||||
uri: https://www.alphavantage.co/query?apikey=MOCK_API_KEY&from_currency=CHF&function=CURRENCY_EXCHANGE_RATE&to_currency=USD
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAAA3zQT0vDMBgG8Ps+xUPOLjRpt9netLN4EJG2u+wioX23FdsEkojK2HeXVtToxPP7
|
||||
e95/xxkAsJJU77uBkD9bS7p5w81rc1B6TyiVJ5bhOMEJC47CmuHxy+amHQnLbwt28e3kb3evhslV
|
||||
L51zKKzSTehjjtqcd91U61AlP9Vnz43uPLWovPLksDZ9r2wYW/Czk5jg0SoR8TKNQrnkuFPOo6Sd
|
||||
JXegdqQyknIexXMpIEQm0yxZhJkVRz2+b2v0xzZ1HpYvOa67Fg+2a/4ZnHJcuac/1YROs9M7AAAA
|
||||
//8DAJHb3+OvAQAA
|
||||
headers:
|
||||
Allow:
|
||||
- GET, HEAD, OPTIONS
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 6ef65a0cef9b0ba5-AMS
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Mon, 21 Mar 2022 11:29:48 GMT
|
||||
Expect-CT:
|
||||
- max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Cookie
|
||||
Via:
|
||||
- 1.1 vegur
|
||||
X-Frame-Options:
|
||||
- SAMEORIGIN
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
53
tests/gamestonk_terminal/forex/cassettes/test_av_view/test_display_quote[EUR-USD].yaml
vendored
Normal file
53
tests/gamestonk_terminal/forex/cassettes/test_av_view/test_display_quote[EUR-USD].yaml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
User-Agent:
|
||||
- python-requests/2.27.1
|
||||
method: GET
|
||||
uri: https://www.alphavantage.co/query?apikey=MOCK_API_KEY&from_currency=EUR&function=CURRENCY_EXCHANGE_RATE&to_currency=USD
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAAA3zQy07CQBQG4D1P8WfWMpk5RYXutODKGFPaDRsyaY/S2HaS6ZBoCO9uWgIOl7g+
|
||||
339uuxEAiJRN7auGkWyd47b4weK72Jj2k5EazyLGboAD1hIvzjbrk01s2ROxyFNx9+fo0r2Z5uC2
|
||||
zoYwksjsdbt8OQ/V5Fwdm+Vt5bnE0hvPHea2ro0LY/fy6hahpVaRIopUKB8kXk3nkfKH427DZU9J
|
||||
EY1VNCYNrWOaxRMKM48SWf+3lW0P22RJWJ5KPFcl3l1V/DN4JvHUfd1UA9qP9r8AAAD//wMAvdGY
|
||||
OKgBAAA=
|
||||
headers:
|
||||
Allow:
|
||||
- GET, HEAD, OPTIONS
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 6ef659f85b93fa44-AMS
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Mon, 21 Mar 2022 11:29:44 GMT
|
||||
Expect-CT:
|
||||
- max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Cookie
|
||||
Via:
|
||||
- 1.1 vegur
|
||||
X-Frame-Options:
|
||||
- SAMEORIGIN
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
53
tests/gamestonk_terminal/forex/cassettes/test_av_view/test_display_quote[GBP-EUR].yaml
vendored
Normal file
53
tests/gamestonk_terminal/forex/cassettes/test_av_view/test_display_quote[GBP-EUR].yaml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
User-Agent:
|
||||
- python-requests/2.27.1
|
||||
method: GET
|
||||
uri: https://www.alphavantage.co/query?apikey=MOCK_API_KEY&from_currency=GBP&function=CURRENCY_EXCHANGE_RATE&to_currency=EUR
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAAA3zQwUvDMBQG8Pv+io+cXUjSTdfebKm7iJTaXbxIaOMaXBNIU1DG/vfRgJo58fx+
|
||||
7/t477gAAFIrefB6UCgm55RpP1F+tL00e4VaekUyHAMMmFM8ODu8ftvCdjMh27wiNz9O/HZPcggu
|
||||
d9rrsUdlJ9Ph2St30GYfryYUjb0uKHd1rFaX6iu+nJyN2ZpeXUM45SnbMMZYLG8pHuXoUas3p8Ze
|
||||
dTMVTIglS5aCg/NMpFlysXNH0cyfe7EmJO+aIh5vKHLdoXK6/ac4pbgf3/9UAZ0WpzMAAAD//wMA
|
||||
g84xEqoBAAA=
|
||||
headers:
|
||||
Allow:
|
||||
- GET, HEAD, OPTIONS
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 6ef65a05cefafa18-AMS
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Mon, 21 Mar 2022 11:29:46 GMT
|
||||
Expect-CT:
|
||||
- max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Cookie
|
||||
Via:
|
||||
- 1.1 vegur
|
||||
X-Frame-Options:
|
||||
- SAMEORIGIN
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
53
tests/gamestonk_terminal/forex/cassettes/test_av_view/test_display_quote[USD-JPY].yaml
vendored
Normal file
53
tests/gamestonk_terminal/forex/cassettes/test_av_view/test_display_quote[USD-JPY].yaml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
User-Agent:
|
||||
- python-requests/2.27.1
|
||||
method: GET
|
||||
uri: https://www.alphavantage.co/query?apikey=MOCK_API_KEY&from_currency=USD&function=CURRENCY_EXCHANGE_RATE&to_currency=JPY
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAAA3zQwU7DMAwG4Pue4lfOLGqyDWhvo4PDhNDUlcO4IKv1WEWboCRIoGnvjtoJCEzg
|
||||
qz//lr0fAYAomNrQdIz81Tk21Tuu36odmSdGQYFFhv0AB6wkbpztHr9sbuueiPv1Qpx9O/3b3VF3
|
||||
dKYJXGMdKLDHwrYtuXhwIlHa0/jlahOr6U/1Gb6kFzLsGRs2MZ/Jk5uEUqlUM5X0FdtziVvyAQVv
|
||||
Hfsd1z3WidbjZDLWCkplOs2mOp65kCj7Dz5YczyyzOP2pcRVU2Plmurf1anE3D//4QZ2GB0+AAAA
|
||||
//8DAPApKWm2AQAA
|
||||
headers:
|
||||
Allow:
|
||||
- GET, HEAD, OPTIONS
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 6ef659fe1dfb970e-AMS
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Mon, 21 Mar 2022 11:29:45 GMT
|
||||
Expect-CT:
|
||||
- max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Cookie
|
||||
Via:
|
||||
- 1.1 vegur
|
||||
X-Frame-Options:
|
||||
- SAMEORIGIN
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
1912
tests/gamestonk_terminal/forex/csv/test_av_model/test_get_historical[CHF-USD].csv
vendored
Normal file
1912
tests/gamestonk_terminal/forex/csv/test_av_model/test_get_historical[CHF-USD].csv
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5001
tests/gamestonk_terminal/forex/csv/test_av_model/test_get_historical[EUR-USD].csv
vendored
Normal file
5001
tests/gamestonk_terminal/forex/csv/test_av_model/test_get_historical[EUR-USD].csv
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5001
tests/gamestonk_terminal/forex/csv/test_av_model/test_get_historical[GBP-EUR].csv
vendored
Normal file
5001
tests/gamestonk_terminal/forex/csv/test_av_model/test_get_historical[GBP-EUR].csv
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5001
tests/gamestonk_terminal/forex/csv/test_av_model/test_get_historical[USD-JPY].csv
vendored
Normal file
5001
tests/gamestonk_terminal/forex/csv/test_av_model/test_get_historical[USD-JPY].csv
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5001
tests/gamestonk_terminal/forex/technical_analysis/csv/test_ta_controller/data_df.csv
vendored
Normal file
5001
tests/gamestonk_terminal/forex/technical_analysis/csv/test_ta_controller/data_df.csv
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
# IMPORTATION STANDARD
|
||||
|
||||
# IMPORTATION THIRDPARTY
|
||||
|
||||
# IMPORTATION INTERNAL
|
||||
from gamestonk_terminal.helper_classes import ModelsNamespace as _models
|
||||
from gamestonk_terminal.forex.technical_analysis import ta_api
|
||||
|
||||
|
||||
def test_models():
|
||||
assert isinstance(ta_api.models, _models)
|
||||
@@ -0,0 +1,493 @@
|
||||
# IMPORTATION STANDARD
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
# IMPORTATION THIRDPARTY
|
||||
import pandas as pd
|
||||
import pytest
|
||||
|
||||
# IMPORTATION INTERNAL
|
||||
from gamestonk_terminal.forex.technical_analysis import ta_controller
|
||||
|
||||
# pylint: disable=E1101
|
||||
# pylint: disable=W0603
|
||||
# pylint: disable=E1111
|
||||
|
||||
EMPTY_DF = pd.DataFrame()
|
||||
MOCK_STOCK_DF = pd.read_csv(
|
||||
"tests/gamestonk_terminal/forex/technical_analysis/csv/test_ta_controller/data_df.csv",
|
||||
index_col=0,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vcr_config():
|
||||
return {
|
||||
"filter_headers": [("User-Agent", None)],
|
||||
"filter_query_parameters": [
|
||||
("period1", "MOCK_PERIOD_1"),
|
||||
("period2", "MOCK_PERIOD_2"),
|
||||
("date", "MOCK_DATE"),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"queue, expected",
|
||||
[
|
||||
(["load", "help"], []),
|
||||
(["quit", "help"], ["help"]),
|
||||
],
|
||||
)
|
||||
def test_menu_with_queue(expected, mocker, queue):
|
||||
path_controller = "gamestonk_terminal.forex.technical_analysis.ta_controller"
|
||||
|
||||
# MOCK SWITCH
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.TechnicalAnalysisController.switch",
|
||||
return_value=["quit"],
|
||||
)
|
||||
|
||||
result_menu = ta_controller.TechnicalAnalysisController(
|
||||
ticker="MOCK_TICKER",
|
||||
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
|
||||
interval="MOCK_INTERVAL",
|
||||
data=MOCK_STOCK_DF,
|
||||
queue=queue,
|
||||
).menu()
|
||||
|
||||
assert result_menu == expected
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
def test_menu_without_queue_completion(mocker):
|
||||
path_controller = "gamestonk_terminal.forex.technical_analysis.ta_controller"
|
||||
|
||||
# ENABLE AUTO-COMPLETION : HELPER_FUNCS.MENU
|
||||
mocker.patch(
|
||||
target="gamestonk_terminal.feature_flags.USE_PROMPT_TOOLKIT",
|
||||
new=True,
|
||||
)
|
||||
mocker.patch(
|
||||
target="gamestonk_terminal.parent_classes.session",
|
||||
)
|
||||
mocker.patch(
|
||||
target="gamestonk_terminal.parent_classes.session.prompt",
|
||||
return_value="quit",
|
||||
)
|
||||
|
||||
# DISABLE AUTO-COMPLETION : CONTROLLER.COMPLETER
|
||||
|
||||
# DISABLE AUTO-COMPLETION
|
||||
mocker.patch.object(
|
||||
target=ta_controller.gtff,
|
||||
attribute="USE_PROMPT_TOOLKIT",
|
||||
new=True,
|
||||
)
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.session",
|
||||
)
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.session.prompt",
|
||||
return_value="quit",
|
||||
)
|
||||
|
||||
result_menu = ta_controller.TechnicalAnalysisController(
|
||||
ticker="MOCK_TICKER",
|
||||
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
|
||||
interval="MOCK_INTERVAL",
|
||||
data=MOCK_STOCK_DF,
|
||||
queue=None,
|
||||
).menu()
|
||||
|
||||
assert result_menu == []
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"mock_input",
|
||||
["help", "homee help", "home help", "mock"],
|
||||
)
|
||||
def test_menu_without_queue_sys_exit(mock_input, mocker):
|
||||
path_controller = "gamestonk_terminal.forex.technical_analysis.ta_controller"
|
||||
|
||||
# DISABLE AUTO-COMPLETION
|
||||
mocker.patch.object(
|
||||
target=ta_controller.gtff,
|
||||
attribute="USE_PROMPT_TOOLKIT",
|
||||
new=False,
|
||||
)
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.session",
|
||||
return_value=None,
|
||||
)
|
||||
|
||||
# MOCK USER INPUT
|
||||
mocker.patch("builtins.input", return_value=mock_input)
|
||||
|
||||
# MOCK SWITCH
|
||||
class SystemExitSideEffect:
|
||||
def __init__(self):
|
||||
self.first_call = True
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
if self.first_call:
|
||||
self.first_call = False
|
||||
raise SystemExit()
|
||||
return ["quit"]
|
||||
|
||||
mock_switch = mocker.Mock(side_effect=SystemExitSideEffect())
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.TechnicalAnalysisController.switch",
|
||||
new=mock_switch,
|
||||
)
|
||||
|
||||
result_menu = ta_controller.TechnicalAnalysisController(
|
||||
ticker="MOCK_TICKER",
|
||||
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
|
||||
interval="MOCK_INTERVAL",
|
||||
data=MOCK_STOCK_DF,
|
||||
queue=None,
|
||||
).menu()
|
||||
|
||||
assert result_menu == []
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.record_stdout
|
||||
@pytest.mark.parametrize(
|
||||
"start",
|
||||
[
|
||||
datetime.strptime("2021-12-01", "%Y-%m-%d"),
|
||||
],
|
||||
)
|
||||
def test_print_help(start):
|
||||
controller = ta_controller.TechnicalAnalysisController(
|
||||
ticker="MOCK_TICKER",
|
||||
start=start,
|
||||
interval="MOCK_INTERVAL",
|
||||
data=MOCK_STOCK_DF,
|
||||
queue=None,
|
||||
)
|
||||
controller.print_help()
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"an_input, expected_queue",
|
||||
[
|
||||
("", []),
|
||||
(
|
||||
"/help",
|
||||
[
|
||||
"home",
|
||||
"help",
|
||||
],
|
||||
),
|
||||
("help/help", ["help", "help"]),
|
||||
("q", ["quit"]),
|
||||
("h", []),
|
||||
(
|
||||
"r",
|
||||
[
|
||||
"quit",
|
||||
"quit",
|
||||
"reset",
|
||||
"forex",
|
||||
"from MOCK_FROM",
|
||||
"to MOCK_TO",
|
||||
"load",
|
||||
"ta",
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_switch(an_input, expected_queue):
|
||||
controller = ta_controller.TechnicalAnalysisController(
|
||||
ticker="MOCK_FROM/MOCK_TO",
|
||||
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
|
||||
interval="MOCK_INTERVAL",
|
||||
data=MOCK_STOCK_DF,
|
||||
queue=None,
|
||||
)
|
||||
queue = controller.switch(an_input=an_input)
|
||||
|
||||
assert queue == expected_queue
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
def test_call_cls(mocker):
|
||||
mocker.patch("os.system")
|
||||
controller = ta_controller.TechnicalAnalysisController(
|
||||
ticker="MOCK_TICKER/MOCK_TICKER",
|
||||
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
|
||||
interval="MOCK_INTERVAL",
|
||||
data=MOCK_STOCK_DF,
|
||||
queue=None,
|
||||
)
|
||||
controller.call_cls([])
|
||||
|
||||
assert controller.queue == []
|
||||
os.system.assert_called_once_with("cls||clear")
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"func, queue, expected_queue",
|
||||
[
|
||||
(
|
||||
"call_exit",
|
||||
[],
|
||||
["quit", "quit", "quit"],
|
||||
),
|
||||
("call_exit", ["help"], ["quit", "quit", "quit", "help"]),
|
||||
("call_home", [], ["quit", "quit"]),
|
||||
("call_help", [], []),
|
||||
("call_quit", [], ["quit"]),
|
||||
("call_quit", ["help"], ["quit", "help"]),
|
||||
(
|
||||
"call_reset",
|
||||
[],
|
||||
[
|
||||
"quit",
|
||||
"quit",
|
||||
"reset",
|
||||
"forex",
|
||||
"from MOCK_FROM",
|
||||
"to MOCK_TO",
|
||||
"load",
|
||||
"ta",
|
||||
],
|
||||
),
|
||||
(
|
||||
"call_reset",
|
||||
["help"],
|
||||
[
|
||||
"quit",
|
||||
"quit",
|
||||
"reset",
|
||||
"forex",
|
||||
"from MOCK_FROM",
|
||||
"to MOCK_TO",
|
||||
"load",
|
||||
"ta",
|
||||
"help",
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_call_func_expect_queue(expected_queue, func, queue):
|
||||
controller = ta_controller.TechnicalAnalysisController(
|
||||
ticker="MOCK_FROM/MOCK_TO",
|
||||
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
|
||||
interval="MOCK_INTERVAL",
|
||||
data=MOCK_STOCK_DF,
|
||||
queue=queue,
|
||||
)
|
||||
result = getattr(controller, func)([])
|
||||
|
||||
assert result is None
|
||||
assert controller.queue == expected_queue
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"tested_func, other_args, mocked_func, called_args, called_kwargs",
|
||||
[
|
||||
(
|
||||
"call_ema",
|
||||
[
|
||||
"1,2",
|
||||
"--offset=2",
|
||||
"--export=csv",
|
||||
],
|
||||
"overlap_view.view_ma",
|
||||
[],
|
||||
dict(),
|
||||
),
|
||||
(
|
||||
"call_sma",
|
||||
[
|
||||
"1,2",
|
||||
"--offset=2",
|
||||
"--export=csv",
|
||||
],
|
||||
"overlap_view.view_ma",
|
||||
[],
|
||||
dict(),
|
||||
),
|
||||
(
|
||||
"call_zlma",
|
||||
[
|
||||
"1,2",
|
||||
"--offset=2",
|
||||
"--export=csv",
|
||||
],
|
||||
"overlap_view.view_ma",
|
||||
[],
|
||||
dict(),
|
||||
),
|
||||
(
|
||||
"call_cci",
|
||||
[
|
||||
"--length=1",
|
||||
"--scalar=2",
|
||||
"--export=csv",
|
||||
],
|
||||
"momentum_view.display_cci",
|
||||
[],
|
||||
dict(),
|
||||
),
|
||||
(
|
||||
"call_macd",
|
||||
[
|
||||
"--fast=1",
|
||||
"--slow=2",
|
||||
"--signal=3",
|
||||
"--export=csv",
|
||||
],
|
||||
"momentum_view.display_macd",
|
||||
[],
|
||||
dict(),
|
||||
),
|
||||
(
|
||||
"call_rsi",
|
||||
[
|
||||
"1",
|
||||
"--scalar=2",
|
||||
"--drift=3",
|
||||
"--export=csv",
|
||||
],
|
||||
"momentum_view.display_rsi",
|
||||
[],
|
||||
dict(),
|
||||
),
|
||||
(
|
||||
"call_stoch",
|
||||
[
|
||||
"--fastkperiod=1",
|
||||
"--slowdperiod=2",
|
||||
"--slowkperiod=3",
|
||||
"--export=csv",
|
||||
],
|
||||
"momentum_view.display_stoch",
|
||||
[],
|
||||
dict(),
|
||||
),
|
||||
(
|
||||
"call_fisher",
|
||||
[
|
||||
"1",
|
||||
"--export=csv",
|
||||
],
|
||||
"momentum_view.display_fisher",
|
||||
[],
|
||||
dict(),
|
||||
),
|
||||
(
|
||||
"call_cg",
|
||||
[
|
||||
"1",
|
||||
"--export=csv",
|
||||
],
|
||||
"momentum_view.display_cg",
|
||||
[],
|
||||
dict(),
|
||||
),
|
||||
(
|
||||
"call_adx",
|
||||
[
|
||||
"1",
|
||||
"--scalar=2",
|
||||
"--drift=3",
|
||||
"--export=csv",
|
||||
],
|
||||
"trend_indicators_view.display_adx",
|
||||
[],
|
||||
dict(),
|
||||
),
|
||||
(
|
||||
"call_aroon",
|
||||
[
|
||||
"1",
|
||||
"--scalar=2",
|
||||
"--export=csv",
|
||||
],
|
||||
"trend_indicators_view.display_aroon",
|
||||
[],
|
||||
dict(),
|
||||
),
|
||||
(
|
||||
"call_bbands",
|
||||
[
|
||||
"1",
|
||||
"--std=2",
|
||||
"--mamode=MOCK_MAMODE",
|
||||
"--export=csv",
|
||||
],
|
||||
"volatility_view.display_bbands",
|
||||
[],
|
||||
dict(),
|
||||
),
|
||||
(
|
||||
"call_donchian",
|
||||
[
|
||||
"--length_upper=1",
|
||||
"--length_lower=2",
|
||||
"--export=csv",
|
||||
],
|
||||
"volatility_view.display_donchian",
|
||||
[],
|
||||
dict(),
|
||||
),
|
||||
(
|
||||
"call_fib",
|
||||
[
|
||||
"1",
|
||||
"--start=2021-12-01",
|
||||
"--end=2021-12-02",
|
||||
"--export=csv",
|
||||
],
|
||||
"custom_indicators_view.fibonacci_retracement",
|
||||
[],
|
||||
dict(),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_call_func(
|
||||
tested_func, mocked_func, other_args, called_args, called_kwargs, mocker
|
||||
):
|
||||
path_controller = "gamestonk_terminal.forex.technical_analysis.ta_controller"
|
||||
|
||||
if mocked_func:
|
||||
mock = mocker.Mock()
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.{mocked_func}",
|
||||
new=mock,
|
||||
)
|
||||
|
||||
controller = ta_controller.TechnicalAnalysisController(
|
||||
ticker="MOCK_FROM/MOCK_TO",
|
||||
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
|
||||
interval="MOCK_INTERVAL",
|
||||
data=MOCK_STOCK_DF,
|
||||
queue=None,
|
||||
)
|
||||
# controller.screen_tickers = ["PM"]
|
||||
getattr(controller, tested_func)(other_args)
|
||||
|
||||
if called_args or called_kwargs:
|
||||
mock.assert_called_once_with(*called_args, **called_kwargs)
|
||||
else:
|
||||
mock.assert_called_once()
|
||||
else:
|
||||
controller = ta_controller.TechnicalAnalysisController(
|
||||
ticker="MOCK_FROM/MOCK_TO",
|
||||
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
|
||||
interval="MOCK_INTERVAL",
|
||||
data=MOCK_STOCK_DF,
|
||||
queue=None,
|
||||
)
|
||||
# controller.screen_tickers = ["PM"]
|
||||
getattr(controller, tested_func)(other_args)
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
Currency Pair Loaded: MOCK_TICKER (from 2021-12-01)
|
||||
|
||||
Overlap:
|
||||
ema exponential moving average
|
||||
sma simple moving average
|
||||
zlma zero lag moving average
|
||||
Momentum:
|
||||
cci commodity channel index
|
||||
macd moving average convergence/divergence
|
||||
rsi relative strength index
|
||||
stoch stochastic oscillator
|
||||
fisher fisher transform
|
||||
cg centre of gravity
|
||||
Trend:
|
||||
adx average directional movement index
|
||||
aroon aroon indicator
|
||||
Volatility:
|
||||
bbands bollinger bands
|
||||
donchian donchian channels
|
||||
Custom:
|
||||
fib fibonacci retracement
|
||||
|
||||
34
tests/gamestonk_terminal/forex/test_av_model.py
Normal file
34
tests/gamestonk_terminal/forex/test_av_model.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# IMPORTATION STANDARD
|
||||
|
||||
# IMPORTATION THIRDPARTY
|
||||
import pytest
|
||||
|
||||
# IMPORTATION INTERNAL
|
||||
from gamestonk_terminal.forex import av_model
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vcr_config():
|
||||
return {
|
||||
"filter_query_parameters": [
|
||||
("apikey", "MOCK_API_KEY"),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.vcr
|
||||
@pytest.mark.parametrize(
|
||||
"from_symbol, to_symbol",
|
||||
[
|
||||
("EUR", "USD"),
|
||||
("USD", "JPY"),
|
||||
("GBP", "EUR"),
|
||||
("CHF", "USD"),
|
||||
],
|
||||
)
|
||||
def test_get_historical(from_symbol, to_symbol, recorder):
|
||||
result = av_model.get_historical(
|
||||
to_symbol=to_symbol,
|
||||
from_symbol=from_symbol,
|
||||
)
|
||||
recorder.capture(result)
|
||||
30
tests/gamestonk_terminal/forex/test_av_view.py
Normal file
30
tests/gamestonk_terminal/forex/test_av_view.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# IMPORTATION STANDARD
|
||||
|
||||
# IMPORTATION THIRDPARTY
|
||||
import pytest
|
||||
|
||||
# IMPORTATION INTERNAL
|
||||
from gamestonk_terminal.forex import av_view
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vcr_config():
|
||||
return {
|
||||
"filter_query_parameters": [
|
||||
("apikey", "MOCK_API_KEY"),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.vcr
|
||||
@pytest.mark.parametrize(
|
||||
"from_symbol, to_symbol",
|
||||
[
|
||||
("EUR", "USD"),
|
||||
("USD", "JPY"),
|
||||
("GBP", "EUR"),
|
||||
("CHF", "USD"),
|
||||
],
|
||||
)
|
||||
def test_display_quote(from_symbol, to_symbol):
|
||||
av_view.display_quote(from_symbol=from_symbol, to_symbol=to_symbol)
|
||||
12
tests/gamestonk_terminal/forex/test_forex_api.py
Normal file
12
tests/gamestonk_terminal/forex/test_forex_api.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# IMPORTATION STANDARD
|
||||
|
||||
# IMPORTATION THIRDPARTY
|
||||
|
||||
# IMPORTATION INTERNAL
|
||||
from gamestonk_terminal.helper_classes import ModelsNamespace as _models
|
||||
from gamestonk_terminal.forex import forex_api
|
||||
|
||||
|
||||
def test_models():
|
||||
assert isinstance(forex_api.oanda.models, _models)
|
||||
assert isinstance(forex_api.ta.models, _models)
|
||||
385
tests/gamestonk_terminal/forex/test_forex_controller.py
Normal file
385
tests/gamestonk_terminal/forex/test_forex_controller.py
Normal file
@@ -0,0 +1,385 @@
|
||||
# IMPORTATION STANDARD
|
||||
import os
|
||||
|
||||
# IMPORTATION THIRDPARTY
|
||||
import pandas as pd
|
||||
import pytest
|
||||
|
||||
# IMPORTATION INTERNAL
|
||||
from gamestonk_terminal.forex import forex_controller
|
||||
from gamestonk_terminal.forex.technical_analysis.ta_controller import (
|
||||
TechnicalAnalysisController,
|
||||
)
|
||||
|
||||
# pylint: disable=E1101
|
||||
# pylint: disable=W0603
|
||||
# pylint: disable=E1111
|
||||
|
||||
NON_EMPTY_DF = pd.DataFrame.from_dict(
|
||||
data={
|
||||
pd.Timestamp("2020-11-30 00:00:00"): {
|
||||
"Close": 75.75,
|
||||
},
|
||||
pd.Timestamp("2020-12-01 00:00:00"): {
|
||||
"Close": 77.02999877929688,
|
||||
},
|
||||
},
|
||||
orient="index",
|
||||
)
|
||||
EMPTY_DF = pd.DataFrame()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vcr_config():
|
||||
return {
|
||||
"filter_headers": [("User-Agent", None)],
|
||||
"filter_query_parameters": [
|
||||
("period1", "MOCK_PERIOD_1"),
|
||||
("period2", "MOCK_PERIOD_2"),
|
||||
("date", "MOCK_DATE"),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"queue, expected",
|
||||
[
|
||||
(["load", "help"], []),
|
||||
(["quit", "help"], ["help"]),
|
||||
],
|
||||
)
|
||||
def test_menu_with_queue(expected, mocker, queue):
|
||||
path_controller = "gamestonk_terminal.forex.forex_controller"
|
||||
|
||||
# MOCK SWITCH
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.ForexController.switch",
|
||||
return_value=["quit"],
|
||||
)
|
||||
result_menu = forex_controller.ForexController(queue=queue).menu()
|
||||
|
||||
assert result_menu == expected
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
def test_menu_without_queue_completion(mocker):
|
||||
path_controller = "gamestonk_terminal.forex.forex_controller"
|
||||
|
||||
# ENABLE AUTO-COMPLETION : HELPER_FUNCS.MENU
|
||||
mocker.patch(
|
||||
target="gamestonk_terminal.feature_flags.USE_PROMPT_TOOLKIT",
|
||||
new=True,
|
||||
)
|
||||
mocker.patch(
|
||||
target="gamestonk_terminal.parent_classes.session",
|
||||
)
|
||||
mocker.patch(
|
||||
target="gamestonk_terminal.parent_classes.session.prompt",
|
||||
return_value="quit",
|
||||
)
|
||||
|
||||
# DISABLE AUTO-COMPLETION : CONTROLLER.COMPLETER
|
||||
mocker.patch.object(
|
||||
target=forex_controller.gtff,
|
||||
attribute="USE_PROMPT_TOOLKIT",
|
||||
new=True,
|
||||
)
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.session",
|
||||
)
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.session.prompt",
|
||||
return_value="quit",
|
||||
)
|
||||
|
||||
result_menu = forex_controller.ForexController(queue=None).menu()
|
||||
|
||||
assert result_menu == []
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"mock_input",
|
||||
["help", "homee help", "home help", "mock"],
|
||||
)
|
||||
def test_menu_without_queue_sys_exit(mock_input, mocker):
|
||||
path_controller = "gamestonk_terminal.forex.forex_controller"
|
||||
|
||||
# DISABLE AUTO-COMPLETION
|
||||
mocker.patch.object(
|
||||
target=forex_controller.gtff,
|
||||
attribute="USE_PROMPT_TOOLKIT",
|
||||
new=False,
|
||||
)
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.session",
|
||||
return_value=None,
|
||||
)
|
||||
|
||||
# MOCK USER INPUT
|
||||
mocker.patch("builtins.input", return_value=mock_input)
|
||||
|
||||
# MOCK SWITCH
|
||||
class SystemExitSideEffect:
|
||||
def __init__(self):
|
||||
self.first_call = True
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
if self.first_call:
|
||||
self.first_call = False
|
||||
raise SystemExit()
|
||||
return ["quit"]
|
||||
|
||||
mock_switch = mocker.Mock(side_effect=SystemExitSideEffect())
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.ForexController.switch",
|
||||
new=mock_switch,
|
||||
)
|
||||
|
||||
result_menu = forex_controller.ForexController(queue=None).menu()
|
||||
|
||||
assert result_menu == []
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.record_stdout
|
||||
def test_print_help():
|
||||
controller = forex_controller.ForexController(queue=None)
|
||||
controller.print_help()
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"an_input, expected_queue",
|
||||
[
|
||||
("", []),
|
||||
("/help", ["home", "help"]),
|
||||
("help/help", ["help", "help"]),
|
||||
("q", ["quit"]),
|
||||
("h", []),
|
||||
(
|
||||
"r",
|
||||
[
|
||||
"quit",
|
||||
"reset",
|
||||
"forex",
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_switch(an_input, expected_queue):
|
||||
controller = forex_controller.ForexController(queue=None)
|
||||
queue = controller.switch(an_input=an_input)
|
||||
|
||||
assert queue == expected_queue
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
def test_call_cls(mocker):
|
||||
mocker.patch("os.system")
|
||||
controller = forex_controller.ForexController(queue=None)
|
||||
controller.call_cls([])
|
||||
|
||||
assert controller.queue == []
|
||||
os.system.assert_called_once_with("cls||clear")
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"func, queue, expected_queue",
|
||||
[
|
||||
(
|
||||
"call_exit",
|
||||
[],
|
||||
["quit", "quit"],
|
||||
),
|
||||
("call_exit", ["help"], ["quit", "quit", "help"]),
|
||||
("call_home", [], ["quit"]),
|
||||
("call_help", [], []),
|
||||
("call_quit", [], ["quit"]),
|
||||
("call_quit", ["help"], ["quit", "help"]),
|
||||
(
|
||||
"call_reset",
|
||||
[],
|
||||
[
|
||||
"quit",
|
||||
"reset",
|
||||
"forex",
|
||||
],
|
||||
),
|
||||
(
|
||||
"call_reset",
|
||||
["help"],
|
||||
[
|
||||
"quit",
|
||||
"reset",
|
||||
"forex",
|
||||
"help",
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_call_func_expect_queue(expected_queue, func, queue):
|
||||
controller = forex_controller.ForexController(queue=queue)
|
||||
result = getattr(controller, func)([])
|
||||
|
||||
assert result is None
|
||||
assert controller.queue == expected_queue
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"tested_func, other_args, mocked_func, called_args, called_kwargs",
|
||||
[
|
||||
("call_to", ["GBP"], None, [], dict()),
|
||||
("call_from", ["EUR"], None, [], dict()),
|
||||
(
|
||||
"call_ta",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
),
|
||||
(
|
||||
"call_ta",
|
||||
[],
|
||||
"ForexController.load_class",
|
||||
[TechnicalAnalysisController],
|
||||
dict(
|
||||
ticker="MOCK_TICKER/MOCK_TICKER",
|
||||
data=NON_EMPTY_DF,
|
||||
start=pd.Timestamp("2020-11-30 00:00:00"),
|
||||
interval="",
|
||||
queue=[],
|
||||
),
|
||||
),
|
||||
(
|
||||
"call_oanda",
|
||||
None,
|
||||
"ForexController.load_class",
|
||||
None,
|
||||
None,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_call_func(
|
||||
tested_func, mocked_func, other_args, called_args, called_kwargs, mocker
|
||||
):
|
||||
path_controller = "gamestonk_terminal.forex.forex_controller"
|
||||
|
||||
if mocked_func:
|
||||
mock = mocker.Mock()
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.{mocked_func}",
|
||||
new=mock,
|
||||
)
|
||||
|
||||
controller = forex_controller.ForexController(queue=None)
|
||||
controller.data = NON_EMPTY_DF
|
||||
controller.to_symbol = "MOCK_TICKER"
|
||||
controller.from_symbol = "MOCK_TICKER"
|
||||
controller.interval = ""
|
||||
|
||||
getattr(controller, tested_func)(other_args)
|
||||
|
||||
if called_args or called_kwargs:
|
||||
mock.assert_called_once_with(*called_args, **called_kwargs)
|
||||
else:
|
||||
mock.assert_called_once()
|
||||
else:
|
||||
controller = forex_controller.ForexController(queue=None)
|
||||
controller.stock = EMPTY_DF
|
||||
controller.to_symbol = "MOCK_TICKER"
|
||||
controller.from_symbol = "MOCK_TICKER"
|
||||
controller.interval = "1440min"
|
||||
|
||||
getattr(controller, tested_func)(other_args)
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"func",
|
||||
[
|
||||
"call_from",
|
||||
"call_to",
|
||||
"call_load",
|
||||
"call_candle",
|
||||
],
|
||||
)
|
||||
def test_call_func_no_parser(func, mocker):
|
||||
# MOCK PARSE_KNOWN_ARGS_AND_WARN
|
||||
mocker.patch(
|
||||
target="gamestonk_terminal.forex.forex_controller.parse_known_args_and_warn",
|
||||
return_value=None,
|
||||
)
|
||||
controller = forex_controller.ForexController(queue=None)
|
||||
|
||||
func_result = getattr(controller, func)(other_args=list())
|
||||
assert func_result is None
|
||||
assert controller.queue == []
|
||||
getattr(forex_controller, "parse_known_args_and_warn").assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"func",
|
||||
[
|
||||
"call_candle",
|
||||
"call_ta",
|
||||
# "call_qa",
|
||||
# "call_pred",
|
||||
],
|
||||
)
|
||||
def test_call_func_no_ticker(func, mocker):
|
||||
# MOCK PARSE_KNOWN_ARGS_AND_WARN
|
||||
mocker.patch(
|
||||
"gamestonk_terminal.forex.forex_controller.parse_known_args_and_warn",
|
||||
return_value=True,
|
||||
)
|
||||
|
||||
controller = forex_controller.ForexController(queue=None)
|
||||
|
||||
func_result = getattr(controller, func)([])
|
||||
assert func_result is None
|
||||
assert controller.queue == []
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"from_symbol, to_symbol, expected",
|
||||
[
|
||||
(None, None, []),
|
||||
("MOCK_FROM", None, []),
|
||||
(None, "MOCK_TO", []),
|
||||
("MOCK_FROM", "MOCK_TO", ["forex", "from MOCK_FROM", "to MOCK_TO"]),
|
||||
],
|
||||
)
|
||||
def test_custom_reset(from_symbol, to_symbol, expected):
|
||||
controller = forex_controller.ForexController(queue=None)
|
||||
controller.from_symbol = from_symbol
|
||||
controller.to_symbol = to_symbol
|
||||
|
||||
result = controller.custom_reset()
|
||||
|
||||
assert result == expected
|
||||
|
||||
|
||||
# @pytest.mark.vcr
|
||||
# def test_call_load(mocker):
|
||||
# # FORCE SINGLE THREADING
|
||||
# yf_download = forex_controller.forex_helper.yf.download
|
||||
|
||||
# def mock_yf_download(*args, **kwargs):
|
||||
# kwargs["threads"] = False
|
||||
# return yf_download(*args, **kwargs)
|
||||
|
||||
# mocker.patch("yfinance.download", side_effect=mock_yf_download)
|
||||
|
||||
# controller = forex_controller.ForexController(queue=None)
|
||||
# other_args = [
|
||||
# "TSLA",
|
||||
# "--start=2021-12-17",
|
||||
# "--end=2021-12-18",
|
||||
# ]
|
||||
# controller.call_load(other_args=other_args)
|
||||
18
tests/gamestonk_terminal/forex/txt/test_forex_controller/test_print_help.txt
vendored
Normal file
18
tests/gamestonk_terminal/forex/txt/test_forex_controller/test_print_help.txt
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
from select the "from" currency in a forex pair
|
||||
to select the "to" currency in a forex pair
|
||||
|
||||
From: USD
|
||||
To:
|
||||
[dim]
|
||||
[AlphaVantage]
|
||||
quote get last quote
|
||||
load get historical data
|
||||
candle show candle plot for loaded data
|
||||
|
||||
> ta technical analysis for loaded coin, e.g.: ema, macd, rsi, adx, bbands, obv
|
||||
|
||||
[/dim]
|
||||
Forex brokerages:
|
||||
> oanda Oanda menu
|
||||
|
||||
Reference in New Issue
Block a user