Add YahooFinance to forex load (#1544)

* Load yf forex

* Fix linting

* Adding forex dictionary

* Fix tests failing

* Fix tests failing

* Remove inheritance to oanda model

* Adding -s to --source

* Addressed comments

* Fix tests

* Fix oanda menu entry

* Add av api limit error handling

* Fix start date being in different formats across sources

* Silence "too much data" warning

* Add gst script to test currency sources

Co-authored-by: Theodore Aptekarev <aptekarev@gmail.com>
Co-authored-by: jmaslek <jmaslek11@gmail.com>
This commit is contained in:
minhhoang1023
2022-03-25 20:11:26 +07:00
committed by GitHub
parent 81e81f4a2b
commit 93aba1e2cd
14 changed files with 20755 additions and 104 deletions

View File

@@ -18,7 +18,7 @@ logger = logging.getLogger(__name__)
@log_start_end(log=logger)
def get_currency_list() -> List:
"""Load AV currency codes from a local file."""
path = os.path.join(os.path.dirname(__file__), "av_forex_currencies.csv")
path = os.path.join(os.path.dirname(__file__), "data/av_forex_currencies.csv")
return list(pd.read_csv(path)["currency code"])
@@ -137,6 +137,8 @@ def get_historical(
# If the returned data was unsuccessful
if "Error Message" in r.json():
console.print(r.json()["Error Message"])
elif "Note" in r.json():
console.print(r.json()["Note"])
else:
# check if json is empty
if not r.json():

View File

@@ -2,17 +2,13 @@
__docformat__ = "numpy"
import logging
from typing import Optional, List
import matplotlib.pyplot as plt
import mplfinance as mpf
import pandas as pd
from gamestonk_terminal.config_terminal import theme
from gamestonk_terminal.decorators import check_api_key
from gamestonk_terminal.decorators import log_start_end
from gamestonk_terminal.forex import av_model
from gamestonk_terminal.helper_funcs import plot_autoscale, print_rich_table
from gamestonk_terminal.helper_funcs import print_rich_table
from gamestonk_terminal.rich_config import console
logger = logging.getLogger(__name__)
@@ -32,7 +28,15 @@ def display_quote(to_symbol: str, from_symbol: str):
"""
quote = av_model.get_quote(to_symbol, from_symbol)
if not quote:
if not bool(quote) or "Note" in quote:
if "Note" in quote:
console.print(quote["Note"])
console.print(
"\n[red]"
+ "No historical data loaded.\n"
+ "Make sure 'av' supports the requested pair and you aren't hitting your API call limits."
+ "[/red]\n"
)
return
df = pd.DataFrame.from_dict(quote)
@@ -44,63 +48,3 @@ def display_quote(to_symbol: str, from_symbol: str):
title=f"[bold]{from_symbol}/{to_symbol} Quote [/bold]",
)
console.print("")
@log_start_end(log=logger)
def display_candle(
data: pd.DataFrame,
to_symbol: str,
from_symbol: str,
external_axes: Optional[List[plt.Axes]] = None,
):
"""Show candle plot for fx data.
Parameters
----------
data : pd.DataFrame
Loaded fx historical data
to_symbol : str
To forex symbol
from_symbol : str
From forex symbol
external_axes: Optional[List[plt.Axes]]
External axes (1 axis are expected in the list), by default None
"""
candle_chart_kwargs = {
"type": "candle",
"style": theme.mpf_style,
"mav": (20, 50),
"volume": False,
"xrotation": theme.xticks_rotation,
"scale_padding": {"left": 0.3, "right": 1, "top": 0.8, "bottom": 0.8},
"update_width_config": {
"candle_linewidth": 0.6,
"candle_width": 0.8,
"volume_linewidth": 0.8,
"volume_width": 0.8,
},
"warn_too_much_data": 10000,
}
# This plot has 2 axes
if not external_axes:
candle_chart_kwargs["returnfig"] = True
candle_chart_kwargs["figratio"] = (10, 7)
candle_chart_kwargs["figscale"] = 1.10
candle_chart_kwargs["figsize"] = plot_autoscale()
fig, ax = mpf.plot(data, **candle_chart_kwargs)
fig.suptitle(
f"{from_symbol}/{to_symbol}",
x=0.055,
y=0.965,
horizontalalignment="left",
)
theme.visualize_output(force_tight_layout=False)
ax[0].legend()
else:
if len(external_axes) != 1:
logger.error("Expected list of 1 axis items.")
console.print("[red]Expected list of 1 axis items./n[/red]")
return
(ax1,) = external_axes
candle_chart_kwargs["ax"] = ax1
mpf.plot(data, **candle_chart_kwargs)

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ from gamestonk_terminal.helper_classes import ModelsNamespace as _models
# pylint: disable=unused-import
# Context menus
from .av_view import display_candle as candle
from .forex_helper 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

View File

@@ -12,11 +12,13 @@ from prompt_toolkit.completion import NestedCompleter
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.decorators import log_start_end
from gamestonk_terminal.forex import av_model, av_view
from gamestonk_terminal.forex import av_view, forex_helper
from gamestonk_terminal.forex.forex_helper import FOREX_SOURCES, SOURCES_INTERVALS
from gamestonk_terminal.helper_funcs import parse_known_args_and_warn, valid_date
from gamestonk_terminal.menu import session
from gamestonk_terminal.parent_classes import BaseController
from gamestonk_terminal.rich_config import console
from gamestonk_terminal.decorators import check_api_key
# pylint: disable=R1710,import-outside-toplevel
@@ -37,12 +39,14 @@ class ForexController(BaseController):
self.from_symbol = "USD"
self.to_symbol = ""
self.source = "yf"
self.data = pd.DataFrame()
if session and gtff.USE_PROMPT_TOOLKIT:
choices: dict = {c: {} for c in self.controller_choices}
choices["to"] = {c: None for c in av_model.CURRENCY_LIST}
choices["from"] = {c: None for c in av_model.CURRENCY_LIST}
choices["to"] = {c: None for c in forex_helper.YF_CURRENCY_LIST}
choices["from"] = {c: None for c in forex_helper.YF_CURRENCY_LIST}
choices["load"]["--source"] = {c: None for c in FOREX_SOURCES}
self.completer = NestedCompleter.from_nested_dict(choices)
def print_help(self):
@@ -53,17 +57,16 @@ class ForexController(BaseController):
from select the "from" currency in a forex pair
to select the "to" currency in a forex pair[/cmds]
[param]From: [/param]{None or self.from_symbol}
[param]To: [/param]{None or self.to_symbol}[cmds]
{has_symbols_start}
[src][AlphaVantage][/src][cmds]
quote get last quote
[param]From: [/param]{None or self.from_symbol}
[param]To: [/param]{None or self.to_symbol}
[param]Source: [/param]{None or FOREX_SOURCES[self.source]}[cmds]{has_symbols_start}
[cmds]
quote get last quote [src][AlphaVantage][/src]
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}
[/menu]{has_symbols_end}
[info]Forex brokerages:[/info][menu]
> oanda Oanda menu[/menu][/cmds]
"""
@@ -90,7 +93,7 @@ class ForexController(BaseController):
"-n",
"--name",
help="To currency",
type=av_model.check_valid_forex_currency,
type=forex_helper.check_valid_yf_forex_currency,
dest="to_symbol",
)
@@ -107,7 +110,9 @@ class ForexController(BaseController):
self.to_symbol = ns_parser.to_symbol
console.print(
f"\nSelected pair\nFrom: {self.from_symbol}\nTo: {self.to_symbol}\n\n"
f"\nSelected pair\nFrom: {self.from_symbol}\n"
f"To: {self.to_symbol}\n"
f"Source: {FOREX_SOURCES[self.source]}\n\n"
)
@log_start_end(log=logger)
@@ -123,7 +128,7 @@ class ForexController(BaseController):
"-n",
"--name",
help="From currency",
type=av_model.check_valid_forex_currency,
type=forex_helper.check_valid_yf_forex_currency,
dest="from_symbol",
)
@@ -139,7 +144,9 @@ class ForexController(BaseController):
if ns_parser:
self.from_symbol = ns_parser.from_symbol
console.print(
f"\nSelected pair\nFrom: {self.from_symbol}\nTo: {self.to_symbol}\n\n"
f"\nSelected pair\nFrom: {self.from_symbol}\n"
f"To: {self.to_symbol}\n"
f"Source: {FOREX_SOURCES[self.source]}\n\n"
)
@log_start_end(log=logger)
@@ -149,8 +156,20 @@ class ForexController(BaseController):
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="load",
description="Load historical exchange rate data.",
description="Load historical exchange rate data."
"Available data sources are Alpha Advantage and YahooFinance"
"By default main source used for analysis is YahooFinance (yf). To change it use --source av",
)
parser.add_argument(
"--source",
help="Source of historical data",
dest="source",
choices=("yf", "av"),
default="yf",
required=False,
)
parser.add_argument(
"-r",
"--resolution",
@@ -162,32 +181,54 @@ class ForexController(BaseController):
parser.add_argument(
"-i",
"--interval",
choices=[1, 5, 15, 30, 60],
default="5",
help="Interval of intraday data. Can be 1, 5, 15, 30 or 60.",
choices=SOURCES_INTERVALS["yf"],
default="5min",
help="""Interval of intraday data. Options:
[YahooFinance] 1min, 2min, 5min, 15min, 30min, 60min, 90min, 1hour, 1day, 5day, 1week, 1month, 3month.
[AlphaAdvantage] 1min, 5min, 15min, 30min, 60min""",
dest="interval",
)
parser.add_argument(
"-s",
"--start_date",
default=(datetime.now() - timedelta(days=366)),
default=(datetime.now() - timedelta(days=59)),
type=valid_date,
help="Start date of data.",
dest="start_date",
)
ns_parser = parse_known_args_and_warn(parser, other_args)
if ns_parser:
if self.to_symbol and self.from_symbol:
self.data = av_model.get_historical(
self.data = forex_helper.load(
to_symbol=self.to_symbol,
from_symbol=self.from_symbol,
resolution=ns_parser.resolution,
interval=ns_parser.interval,
start_date=ns_parser.start_date.strftime("%Y-%m-%d"),
source=ns_parser.source,
)
self.data.index.name = "date"
if self.data.empty:
console.print(
"\n[red]"
+ "No historical data loaded.\n"
+ f"Make sure you have appropriate access for the '{ns_parser.source}' data source "
+ f"and that '{ns_parser.source}' supports the requested range."
+ "[/red]\n"
)
else:
self.data.index.name = "date"
self.source = ns_parser.source
console.print(
f"Loaded historic data from {self.from_symbol} to {self.to_symbol}"
f"\nSelected pair\nFrom: {self.from_symbol}\n"
f"To: {self.to_symbol}\n"
f"Source: {FOREX_SOURCES[self.source]}\n\n"
)
else:
logger.error(
@@ -209,7 +250,7 @@ class ForexController(BaseController):
ns_parser = parse_known_args_and_warn(parser, other_args)
if ns_parser:
if not self.data.empty:
av_view.display_candle(self.data, self.to_symbol, self.from_symbol)
forex_helper.display_candle(self.data, self.to_symbol, self.from_symbol)
else:
logger.error(
"No forex historical data loaded. Load first using <load>."
@@ -236,16 +277,24 @@ class ForexController(BaseController):
"Make sure both a 'to' symbol and a 'from' symbol are selected."
)
console.print(
'[red]Make sure both a "to" symbol and a "from" symbol are selected.[/red]\n'
"[red]Make sure both a 'to' symbol and a 'from' symbol are selected.[/red]\n"
)
# MENUS
@log_start_end(log=logger)
@check_api_key(["OANDA_ACCOUNT_TYPE", "OANDA_ACCOUNT", "OANDA_TOKEN"])
def call_oanda(self, _):
"""Enter Oanda menu."""
from gamestonk_terminal.forex.oanda.oanda_controller import OandaController
self.queue = self.load_class(OandaController, self.queue)
if self.to_symbol and self.from_symbol:
self.queue = self.load_class(
OandaController,
queue=self.queue,
)
else:
console.print("No currency pair data is loaded. Use 'load' to load data.\n")
@log_start_end(log=logger)
def call_ta(self, _):
@@ -259,6 +308,7 @@ class ForexController(BaseController):
self.queue = self.load_class(
TechnicalAnalysisController,
ticker=f"{self.from_symbol}/{self.to_symbol}",
source=self.source,
data=self.data,
start=self.data.index[0],
interval="",

View File

@@ -0,0 +1,201 @@
from datetime import datetime
from typing import List, Optional, Dict
import os
import argparse
import logging
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
import mplfinance as mpf
from gamestonk_terminal.forex import av_model
from gamestonk_terminal.rich_config import console
from gamestonk_terminal.decorators import log_start_end
from gamestonk_terminal.helper_funcs import plot_autoscale
from gamestonk_terminal.config_terminal import theme
FOREX_SOURCES: Dict = {
"yf": "YahooFinance",
"av": "AlphaAdvantage",
}
SOURCES_INTERVALS: Dict = {
"yf": [
"1min",
"2min",
"5min",
"15min",
"30min",
"60min",
"90min",
"1hour",
"1day",
"5day",
"1week",
"1month",
"3month",
],
"av": ["1min", "5min", "15min", "30min", "60min"],
}
INTERVAL_MAPS: Dict = {
"yf": {
"1min": "1m",
"2min": "2m",
"5min": "5m",
"15min": "15m",
"30min": "30m",
"60min": "60m",
"90min": "90m",
"1hour": "1h",
"1day": "1d",
"5day": "5d",
"1week": "1wk",
"1month": "1mo",
"3month": "3mo",
},
"av": {"1min": 1, "5min": 5, "15min": 15, "30min": 30, "60min": 60},
}
logger = logging.getLogger(__name__)
@log_start_end(log=logger)
def load(
to_symbol: str,
from_symbol: str,
resolution: str,
interval: str,
start_date: str,
source: str = "yf",
):
interval_map = INTERVAL_MAPS[source]
if interval not in interval_map.keys():
console.print(
f"Interval not supported by {FOREX_SOURCES[source]}. Need to be one of the following options",
interval_map.keys(),
)
return pd.DataFrame()
if source == "av":
df = av_model.get_historical(
to_symbol=to_symbol,
from_symbol=from_symbol,
resolution=resolution,
interval=interval_map[interval],
start_date=start_date,
)
if source == "yf":
df = yf.download(
f"{from_symbol}{to_symbol}=X",
end=datetime.now(),
start=datetime.strptime(start_date, "%Y-%m-%d"),
interval=interval_map[interval],
progress=False,
)
return df
@log_start_end(log=logger)
def get_yf_currency_list() -> List:
"""Load YF list of forex pair a local file."""
path = os.path.join(os.path.dirname(__file__), "data/yahoofinance_forex.json")
return sorted(list(set(pd.read_json(path)["from_symbol"])))
YF_CURRENCY_LIST = get_yf_currency_list()
@log_start_end(log=logger)
def check_valid_yf_forex_currency(fx_symbol: str) -> str:
"""Check if given symbol is supported on Yahoo Finance
Parameters
----------
fx_symbol : str
Symbol to check
Returns
-------
str
Currency symbol
Raises
------
argparse.ArgumentTypeError
Symbol not valid on YahooFinance
"""
if fx_symbol.upper() in get_yf_currency_list():
return fx_symbol.upper()
raise argparse.ArgumentTypeError(
f"{fx_symbol.upper()} not found in YahooFinance supported currency codes. "
)
@log_start_end(log=logger)
def display_candle(
data: pd.DataFrame,
to_symbol: str,
from_symbol: str,
external_axes: Optional[List[plt.Axes]] = None,
):
"""Show candle plot for fx data.
Parameters
----------
data : pd.DataFrame
Loaded fx historical data
to_symbol : str
To forex symbol
from_symbol : str
From forex symbol
external_axes: Optional[List[plt.Axes]]
External axes (1 axis are expected in the list), by default None
"""
candle_chart_kwargs = {
"type": "candle",
"style": theme.mpf_style,
"mav": (20, 50),
"volume": False,
"xrotation": theme.xticks_rotation,
"scale_padding": {"left": 0.3, "right": 1, "top": 0.8, "bottom": 0.8},
"update_width_config": {
"candle_linewidth": 0.6,
"candle_width": 0.8,
"volume_linewidth": 0.8,
"volume_width": 0.8,
},
"warn_too_much_data": 20000,
}
# This plot has 2 axes
if not external_axes:
candle_chart_kwargs["returnfig"] = True
candle_chart_kwargs["figratio"] = (10, 7)
candle_chart_kwargs["figscale"] = 1.10
candle_chart_kwargs["figsize"] = plot_autoscale()
fig, ax = mpf.plot(data, **candle_chart_kwargs)
fig.suptitle(
f"{from_symbol}/{to_symbol}",
x=0.055,
y=0.965,
horizontalalignment="left",
)
theme.visualize_output(force_tight_layout=False)
ax[0].legend()
else:
if len(external_axes) != 1:
logger.error("Expected list of 1 axis items.")
console.print("[red]Expected list of 1 axis items./n[/red]")
return
(ax1,) = external_axes
candle_chart_kwargs["ax"] = ax1
mpf.plot(data, **candle_chart_kwargs)

View File

@@ -10,7 +10,8 @@ from prompt_toolkit.completion import NestedCompleter
from gamestonk_terminal import config_terminal as cfg
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.decorators import log_start_end
from gamestonk_terminal.forex import av_model
from gamestonk_terminal.forex import av_model, forex_helper
from gamestonk_terminal.forex.forex_helper import FOREX_SOURCES
from gamestonk_terminal.forex.oanda import oanda_view
from gamestonk_terminal.helper_funcs import (
check_non_negative_float,
@@ -55,6 +56,7 @@ class OandaController(BaseController):
self.from_symbol = ""
self.to_symbol = ""
self.source = "Oanda"
self.instrument: Union[str, None] = None
if session and gtff.USE_PROMPT_TOOLKIT:
@@ -64,8 +66,8 @@ class OandaController(BaseController):
# In Oanda they have their own list of available instruments. It would be
# Great to fetch these lists and store them locally like it's done for
# other currency codes (see ./av_forex_currencies.csv and how it's handled).
choices["to"] = {c: None for c in av_model.CURRENCY_LIST}
choices["from"] = {c: None for c in av_model.CURRENCY_LIST}
choices["to"] = {c: None for c in forex_helper.YF_CURRENCY_LIST}
choices["from"] = {c: None for c in forex_helper.YF_CURRENCY_LIST}
self.completer = NestedCompleter.from_nested_dict(choices)
def print_help(self):
@@ -87,6 +89,9 @@ class OandaController(BaseController):
to select the "to" currency in a forex pair[/cmds]
[param]Loaded instrument: [/param]{self.instrument if self.instrument else ""}[cmds]
[param]From: [/param]{None or self.from_symbol}
[param]To: [/param]{None or self.to_symbol}
[param]Source: [/param]{None or FOREX_SOURCES[self.source]}[cmds]
{has_instrument_start}
candles show candles
@@ -128,7 +133,9 @@ class OandaController(BaseController):
self.instrument = f"{self.from_symbol}_{self.to_symbol}"
console.print(
f"\nSelected pair\nFrom: {self.from_symbol}\nTo: {self.to_symbol}\n\n"
f"\nSelected pair\nFrom: {self.from_symbol}\n"
f"To: {self.to_symbol}\n"
f"Source: {FOREX_SOURCES[self.source]}\n\n"
)
@log_start_end(log=logger)
@@ -163,7 +170,9 @@ class OandaController(BaseController):
self.instrument = f"{self.from_symbol}_{self.to_symbol}"
console.print(
f"\nSelected pair\nFrom: {self.from_symbol}\nTo: {self.to_symbol}\n\n"
f"\nSelected pair\nFrom: {self.from_symbol}\n"
f"To: {self.to_symbol}\n"
f"Source: {FOREX_SOURCES[self.source]}\n\n"
)
@log_start_end(log=logger)

View File

@@ -27,6 +27,7 @@ from gamestonk_terminal.helper_funcs import (
parse_known_args_and_warn,
valid_date,
)
from gamestonk_terminal.forex.forex_helper import FOREX_SOURCES
from gamestonk_terminal.menu import session
from gamestonk_terminal.parent_classes import StockBaseController
from gamestonk_terminal.rich_config import console
@@ -59,6 +60,7 @@ class TechnicalAnalysisController(StockBaseController):
def __init__(
self,
ticker: str,
source: str,
start: datetime,
interval: str,
data: pd.DataFrame,
@@ -68,6 +70,7 @@ class TechnicalAnalysisController(StockBaseController):
super().__init__(queue)
self.ticker = ticker
self.source = source
self.start = start
self.interval = interval
self.data = data
@@ -82,6 +85,7 @@ class TechnicalAnalysisController(StockBaseController):
currency_str = f" {self.ticker} (from {self.start.strftime('%Y-%m-%d')})"
help_text = f"""[cmds]
[param]Currency Pair Loaded: [/param]{currency_str}
[param]Source: [/param]{FOREX_SOURCES[self.source]}
[info]Overlap:[/info]
ema exponential moving average

12
scripts/test_forex_load.gst vendored Normal file
View File

@@ -0,0 +1,12 @@
forex
to gbp
load
load --source av
load --source av -s 2021-01-02
load --source yf -s 2021-01-02
load -s 2022-01-20
load -s 2022-02-20
load -i 1week -s 2021-01-20
load -r w --source av
load -r w --source av -s 2021-01-20
exit

View File

@@ -52,6 +52,7 @@ def test_menu_with_queue(expected, mocker, queue):
result_menu = ta_controller.TechnicalAnalysisController(
ticker="MOCK_TICKER",
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
source="yf",
interval="MOCK_INTERVAL",
data=MOCK_STOCK_DF,
queue=queue,
@@ -95,6 +96,7 @@ def test_menu_without_queue_completion(mocker):
result_menu = ta_controller.TechnicalAnalysisController(
ticker="MOCK_TICKER",
source="yf",
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
interval="MOCK_INTERVAL",
data=MOCK_STOCK_DF,
@@ -145,6 +147,7 @@ def test_menu_without_queue_sys_exit(mock_input, mocker):
result_menu = ta_controller.TechnicalAnalysisController(
ticker="MOCK_TICKER",
source="yf",
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
interval="MOCK_INTERVAL",
data=MOCK_STOCK_DF,
@@ -165,6 +168,7 @@ def test_menu_without_queue_sys_exit(mock_input, mocker):
def test_print_help(start):
controller = ta_controller.TechnicalAnalysisController(
ticker="MOCK_TICKER",
source="yf",
start=start,
interval="MOCK_INTERVAL",
data=MOCK_STOCK_DF,
@@ -206,6 +210,7 @@ def test_print_help(start):
def test_switch(an_input, expected_queue):
controller = ta_controller.TechnicalAnalysisController(
ticker="MOCK_FROM/MOCK_TO",
source="yf",
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
interval="MOCK_INTERVAL",
data=MOCK_STOCK_DF,
@@ -221,6 +226,7 @@ def test_call_cls(mocker):
mocker.patch("os.system")
controller = ta_controller.TechnicalAnalysisController(
ticker="MOCK_TICKER/MOCK_TICKER",
source="yf",
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
interval="MOCK_INTERVAL",
data=MOCK_STOCK_DF,
@@ -280,6 +286,7 @@ def test_call_cls(mocker):
def test_call_func_expect_queue(expected_queue, func, queue):
controller = ta_controller.TechnicalAnalysisController(
ticker="MOCK_FROM/MOCK_TO",
source="yf",
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
interval="MOCK_INTERVAL",
data=MOCK_STOCK_DF,
@@ -469,6 +476,7 @@ def test_call_func(
controller = ta_controller.TechnicalAnalysisController(
ticker="MOCK_FROM/MOCK_TO",
source="yf",
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
interval="MOCK_INTERVAL",
data=MOCK_STOCK_DF,
@@ -484,6 +492,7 @@ def test_call_func(
else:
controller = ta_controller.TechnicalAnalysisController(
ticker="MOCK_FROM/MOCK_TO",
source="yf",
start=datetime.strptime("2021-12-01", "%Y-%m-%d"),
interval="MOCK_INTERVAL",
data=MOCK_STOCK_DF,

View File

@@ -1,5 +1,6 @@
Currency Pair Loaded: MOCK_TICKER (from 2021-12-01)
Source: YahooFinance
Overlap:
ema exponential moving average

View File

@@ -248,6 +248,7 @@ def test_call_func_expect_queue(expected_queue, func, queue):
[TechnicalAnalysisController],
dict(
ticker="MOCK_TICKER/MOCK_TICKER",
source="yf",
data=NON_EMPTY_DF,
start=pd.Timestamp("2020-11-30 00:00:00"),
interval="",
@@ -290,6 +291,7 @@ def test_call_func(
else:
controller = forex_controller.ForexController(queue=None)
controller.stock = EMPTY_DF
controller.source = "yf"
controller.to_symbol = "MOCK_TICKER"
controller.from_symbol = "MOCK_TICKER"
controller.interval = "1440min"

View File

@@ -2,16 +2,15 @@
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
From: USD
To:
Source: YahooFinance[dim]
quote get last quote [AlphaVantage]
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