Redo some forex menu with basic commands (#903)

* Make oanda menu and add some alpha vantage forex

* ignore csv for codespell

* Added load and candle plot for forex

* Add hugo stuff for forex commands

* fix merge error

Co-authored-by: James Maslek <james@Jamess-MacBook-Pro.local>
This commit is contained in:
jmaslek
2021-11-03 09:44:24 -04:00
committed by GitHub
parent ff8f75f5f5
commit a13be8d678
14 changed files with 1720 additions and 111 deletions

View File

@@ -30,7 +30,7 @@ jobs:
- run: pip install types-pytz types-requests types-termcolor types-tabulate types-PyYAML types-python-dateutil
- run: bandit -r . || true
- run: black --check .
- run: codespell --ignore-words-list=pres,hsi,hist,mape,ba,ser,coo --quiet-level=2 --skip=".git" --skip="*.yaml" --skip="*.ipynb" --skip="*.json" --skip="*.js" --skip="*.css" --skip="*.scss" --skip="*.html" --skip="*.ini" --skip="*.lock"
- run: codespell --ignore-words-list=pres,hsi,hist,mape,ba,ser,coo --quiet-level=2 --skip=".git" --skip="*.yaml" --skip="*.ipynb" --skip="*.json" --skip="*.js" --skip="*.css" --skip="*.scss" --skip="*.html" --skip="*.ini" --skip="*.lock" --skip="*.csv"
- run: flake8 . --count --ignore=E203,W503 --max-line-length=122 --show-source --statistics
- run: mypy --ignore-missing-imports --exclude '/setup\.py$' .
- run: shopt -s globstar && pyupgrade --py36-plus **/*.py

View File

@@ -21,7 +21,7 @@ repos:
hooks:
- id: codespell
entry: codespell
args: [ '--ignore-words-list=pres,coo,navagation,ser,buil,operatio,mape,ba,hist,yeld,ether,hsi', '--quiet-level=2', '--skip=".git,*.html,*.yaml,*.json,./tests"' ]
args: [ '--ignore-words-list=pres,coo,navagation,ser,buil,operatio,mape,ba,hist,yeld,ether,hsi', '--quiet-level=2', '--skip=".git,*.html,*.yaml,*.json,./tests,*.csv"' ]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: 'v0.812'
hooks:

View File

@@ -0,0 +1,160 @@
currency code,currency name
AED,United Arab Emirates Dirham
AFN,Afghan Afghani
ALL,Albanian Lek
AMD,Armenian Dram
ANG,Netherlands Antillean Guilder
AOA,Angolan Kwanza
ARS,Argentine Peso
AUD,Australian Dollar
AWG,Aruban Florin
AZN,Azerbaijani Manat
BAM,Bosnia-Herzegovina Convertible Mark
BBD,Barbadian Dollar
BDT,Bangladeshi Taka
BGN,Bulgarian Lev
BHD,Bahraini Dinar
BIF,Burundian Franc
BMD,Bermudan Dollar
BND,Brunei Dollar
BOB,Bolivian Boliviano
BRL,Brazilian Real
BSD,Bahamian Dollar
BTN,Bhutanese Ngultrum
BWP,Botswanan Pula
BZD,Belize Dollar
CAD,Canadian Dollar
CDF,Congolese Franc
CHF,Swiss Franc
CLF,Chilean Unit of Account UF
CLP,Chilean Peso
CNH,Chinese Yuan Offshore
CNY,Chinese Yuan
COP,Colombian Peso
CUP,Cuban Peso
CVE,Cape Verdean Escudo
CZK,Czech Republic Koruna
DJF,Djiboutian Franc
DKK,Danish Krone
DOP,Dominican Peso
DZD,Algerian Dinar
EGP,Egyptian Pound
ERN,Eritrean Nakfa
ETB,Ethiopian Birr
EUR,Euro
FJD,Fijian Dollar
FKP,Falkland Islands Pound
GBP,British Pound Sterling
GEL,Georgian Lari
GHS,Ghanaian Cedi
GIP,Gibraltar Pound
GMD,Gambian Dalasi
GNF,Guinean Franc
GTQ,Guatemalan Quetzal
GYD,Guyanaese Dollar
HKD,Hong Kong Dollar
HNL,Honduran Lempira
HRK,Croatian Kuna
HTG,Haitian Gourde
HUF,Hungarian Forint
ICP,Internet Computer
IDR,Indonesian Rupiah
ILS,Israeli New Sheqel
INR,Indian Rupee
IQD,Iraqi Dinar
IRR,Iranian Rial
ISK,Icelandic Krona
JEP,Jersey Pound
JMD,Jamaican Dollar
JOD,Jordanian Dinar
JPY,Japanese Yen
KES,Kenyan Shilling
KGS,Kyrgystani Som
KHR,Cambodian Riel
KMF,Comorian Franc
KPW,North Korean Won
KRW,South Korean Won
KWD,Kuwaiti Dinar
KYD,Cayman Islands Dollar
KZT,Kazakhstani Tenge
LAK,Laotian Kip
LBP,Lebanese Pound
LKR,Sri Lankan Rupee
LRD,Liberian Dollar
LSL,Lesotho Loti
LYD,Libyan Dinar
MAD,Moroccan Dirham
MDL,Moldovan Leu
MGA,Malagasy Ariary
MKD,Macedonian Denar
MMK,Myanma Kyat
MNT,Mongolian Tugrik
MOP,Macanese Pataca
MRO,Mauritanian Ouguiya (pre-2018)
MRU,Mauritanian Ouguiya
MUR,Mauritian Rupee
MVR,Maldivian Rufiyaa
MWK,Malawian Kwacha
MXN,Mexican Peso
MYR,Malaysian Ringgit
MZN,Mozambican Metical
NAD,Namibian Dollar
NGN,Nigerian Naira
NOK,Norwegian Krone
NPR,Nepalese Rupee
NZD,New Zealand Dollar
OMR,Omani Rial
PAB,Panamanian Balboa
PEN,Peruvian Nuevo Sol
PGK,Papua New Guinean Kina
PHP,Philippine Peso
PKR,Pakistani Rupee
PLN,Polish Zloty
PYG,Paraguayan Guarani
QAR,Qatari Rial
RON,Romanian Leu
RSD,Serbian Dinar
RUB,Russian Ruble
RUR,Old Russian Ruble
RWF,Rwandan Franc
SAR,Saudi Riyal
SBDf,Solomon Islands Dollar
SCR,Seychellois Rupee
SDG,Sudanese Pound
SDR,Special Drawing Rights
SEK,Swedish Krona
SGD,Singapore Dollar
SHIB,Shiba Inu
SHP,Saint Helena Pound
SLL,Sierra Leonean Leone
SOS,Somali Shilling
SRD,Surinamese Dollar
SYP,Syrian Pound
SZL,Swazi Lilangeni
THB,Thai Baht
TJS,Tajikistani Somoni
TMT,Turkmenistani Manat
TND,Tunisian Dinar
TOP,Tongan Pa'anga
TRY,Turkish Lira
TTD,Trinidad and Tobago Dollar
TWD,New Taiwan Dollar
TZS,Tanzanian Shilling
UAH,Ukrainian Hryvnia
UGX,Ugandan Shilling
USD,United States Dollar
UYU,Uruguayan Peso
UZS,Uzbekistan Som
VND,Vietnamese Dong
VUV,Vanuatu Vatu
WBTC,Wrapped Bitcoin
WST,Samoan Tala
XAF,CFA Franc BEAC
XCD,East Caribbean Dollar
XDR,Special Drawing Rights
XOF,CFA Franc BCEAO
XPF,CFP Franc
YER,Yemeni Rial
ZAR,South African Rand
ZMW,Zambian Kwacha
ZWL,Zimbabwean Dollar
1 currency code currency name
2 AED United Arab Emirates Dirham
3 AFN Afghan Afghani
4 ALL Albanian Lek
5 AMD Armenian Dram
6 ANG Netherlands Antillean Guilder
7 AOA Angolan Kwanza
8 ARS Argentine Peso
9 AUD Australian Dollar
10 AWG Aruban Florin
11 AZN Azerbaijani Manat
12 BAM Bosnia-Herzegovina Convertible Mark
13 BBD Barbadian Dollar
14 BDT Bangladeshi Taka
15 BGN Bulgarian Lev
16 BHD Bahraini Dinar
17 BIF Burundian Franc
18 BMD Bermudan Dollar
19 BND Brunei Dollar
20 BOB Bolivian Boliviano
21 BRL Brazilian Real
22 BSD Bahamian Dollar
23 BTN Bhutanese Ngultrum
24 BWP Botswanan Pula
25 BZD Belize Dollar
26 CAD Canadian Dollar
27 CDF Congolese Franc
28 CHF Swiss Franc
29 CLF Chilean Unit of Account UF
30 CLP Chilean Peso
31 CNH Chinese Yuan Offshore
32 CNY Chinese Yuan
33 COP Colombian Peso
34 CUP Cuban Peso
35 CVE Cape Verdean Escudo
36 CZK Czech Republic Koruna
37 DJF Djiboutian Franc
38 DKK Danish Krone
39 DOP Dominican Peso
40 DZD Algerian Dinar
41 EGP Egyptian Pound
42 ERN Eritrean Nakfa
43 ETB Ethiopian Birr
44 EUR Euro
45 FJD Fijian Dollar
46 FKP Falkland Islands Pound
47 GBP British Pound Sterling
48 GEL Georgian Lari
49 GHS Ghanaian Cedi
50 GIP Gibraltar Pound
51 GMD Gambian Dalasi
52 GNF Guinean Franc
53 GTQ Guatemalan Quetzal
54 GYD Guyanaese Dollar
55 HKD Hong Kong Dollar
56 HNL Honduran Lempira
57 HRK Croatian Kuna
58 HTG Haitian Gourde
59 HUF Hungarian Forint
60 ICP Internet Computer
61 IDR Indonesian Rupiah
62 ILS Israeli New Sheqel
63 INR Indian Rupee
64 IQD Iraqi Dinar
65 IRR Iranian Rial
66 ISK Icelandic Krona
67 JEP Jersey Pound
68 JMD Jamaican Dollar
69 JOD Jordanian Dinar
70 JPY Japanese Yen
71 KES Kenyan Shilling
72 KGS Kyrgystani Som
73 KHR Cambodian Riel
74 KMF Comorian Franc
75 KPW North Korean Won
76 KRW South Korean Won
77 KWD Kuwaiti Dinar
78 KYD Cayman Islands Dollar
79 KZT Kazakhstani Tenge
80 LAK Laotian Kip
81 LBP Lebanese Pound
82 LKR Sri Lankan Rupee
83 LRD Liberian Dollar
84 LSL Lesotho Loti
85 LYD Libyan Dinar
86 MAD Moroccan Dirham
87 MDL Moldovan Leu
88 MGA Malagasy Ariary
89 MKD Macedonian Denar
90 MMK Myanma Kyat
91 MNT Mongolian Tugrik
92 MOP Macanese Pataca
93 MRO Mauritanian Ouguiya (pre-2018)
94 MRU Mauritanian Ouguiya
95 MUR Mauritian Rupee
96 MVR Maldivian Rufiyaa
97 MWK Malawian Kwacha
98 MXN Mexican Peso
99 MYR Malaysian Ringgit
100 MZN Mozambican Metical
101 NAD Namibian Dollar
102 NGN Nigerian Naira
103 NOK Norwegian Krone
104 NPR Nepalese Rupee
105 NZD New Zealand Dollar
106 OMR Omani Rial
107 PAB Panamanian Balboa
108 PEN Peruvian Nuevo Sol
109 PGK Papua New Guinean Kina
110 PHP Philippine Peso
111 PKR Pakistani Rupee
112 PLN Polish Zloty
113 PYG Paraguayan Guarani
114 QAR Qatari Rial
115 RON Romanian Leu
116 RSD Serbian Dinar
117 RUB Russian Ruble
118 RUR Old Russian Ruble
119 RWF Rwandan Franc
120 SAR Saudi Riyal
121 SBDf Solomon Islands Dollar
122 SCR Seychellois Rupee
123 SDG Sudanese Pound
124 SDR Special Drawing Rights
125 SEK Swedish Krona
126 SGD Singapore Dollar
127 SHIB Shiba Inu
128 SHP Saint Helena Pound
129 SLL Sierra Leonean Leone
130 SOS Somali Shilling
131 SRD Surinamese Dollar
132 SYP Syrian Pound
133 SZL Swazi Lilangeni
134 THB Thai Baht
135 TJS Tajikistani Somoni
136 TMT Turkmenistani Manat
137 TND Tunisian Dinar
138 TOP Tongan Pa'anga
139 TRY Turkish Lira
140 TTD Trinidad and Tobago Dollar
141 TWD New Taiwan Dollar
142 TZS Tanzanian Shilling
143 UAH Ukrainian Hryvnia
144 UGX Ugandan Shilling
145 USD United States Dollar
146 UYU Uruguayan Peso
147 UZS Uzbekistan Som
148 VND Vietnamese Dong
149 VUV Vanuatu Vatu
150 WBTC Wrapped Bitcoin
151 WST Samoan Tala
152 XAF CFA Franc BEAC
153 XCD East Caribbean Dollar
154 XDR Special Drawing Rights
155 XOF CFA Franc BCEAO
156 XPF CFP Franc
157 YER Yemeni Rial
158 ZAR South African Rand
159 ZMW Zambian Kwacha
160 ZWL Zimbabwean Dollar

View File

@@ -0,0 +1,114 @@
"""AlphaVantage FX Model"""
import argparse
from typing import Dict
import os
import pandas as pd
import requests
from gamestonk_terminal import config_terminal as cfg
def check_valid_forex_currency(fx_symbol: str) -> str:
"""Check if given symbol is supported on alphavantage
Parameters
----------
fx_symbol : str
Symbol to check
Returns
-------
str
Currency symbol
Raises
------
argparse.ArgumentTypeError
Symbol not valid on alphavantage
"""
path = os.path.join(os.path.dirname(__file__), "av_forex_currencies.csv")
if fx_symbol.upper() in list(pd.read_csv(path)["currency code"]):
return fx_symbol.upper()
raise argparse.ArgumentTypeError(
f"{fx_symbol.upper()} not found in alphavantage supported currency codes. "
)
def get_quote(to_symbol: str, from_symbol: str) -> Dict:
"""Get current exchange rate quote from alpha vantage
Parameters
----------
to_symbol : str
To forex symbol
from_symbol : str
From forex symbol
Returns
-------
Dict
Dictionary of exchange rate
"""
url = "https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RAT"
url += f"E&from_currency={from_symbol}&to_currency={to_symbol}&apikey={cfg.API_KEY_ALPHAVANTAGE}"
r = requests.get(url)
if r.status_code != 200:
return {}
return r.json()
def get_historical(
to_symbol: str,
from_symbol: str,
resolution: str = "d",
interval: int = 5,
start_date: str = "",
) -> pd.DataFrame:
"""Get historical forex data
Parameters
----------
to_symbol : str
To forex symbol
from_symbol : str
From forex symbol
resolution : str, optional
Resolution of data. Can be "i", "d", "w", "m" for intraday, daily, weekly or monthly
interval : int, optional
Interval for intraday data
start_date : str, optional
Start date for data.
Returns
-------
pd.DataFrame
Historical data for forex pair
"""
d_res = {"i": "FX_INTRADAY", "d": "FX_DAILY", "w": "FX_WEEKLY", "m": "FX_MONTHLY"}
url = f"https://www.alphavantage.co/query?function={d_res[resolution]}&from_symbol={from_symbol}"
url += f"&to_symbol={to_symbol}&outputsize=full&apikey={cfg.API_KEY_ALPHAVANTAGE}"
if resolution == "i":
url += f"&interval={interval}min"
r = requests.get(url)
if r.status_code != 200:
return pd.DataFrame()
key = list(r.json().keys())[1]
df = pd.DataFrame.from_dict(r.json()[key], orient="index")
if start_date and resolution != "i":
df = df[df.index > start_date]
df = df.rename(
columns={
"1. open": "Open",
"2. high": "High",
"3. low": "Low",
"4. close": "Close",
}
)
df.index = pd.DatetimeIndex(df.index)
return df.astype(float)

View File

@@ -0,0 +1,80 @@
"""AlphaVantage Forex View"""
__docformat__ = "numpy"
import pandas as pd
from tabulate import tabulate
import mplfinance as mpf
import matplotlib.pyplot as plt
from gamestonk_terminal.forex import av_model
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.helper_funcs import plot_autoscale
def display_quote(to_symbol: str, from_symbol: str):
"""Display current forex pair exchange rate
Parameters
----------
to_symbol : str
To symbol
from_symbol : str
From forex symbol
"""
quote = av_model.get_quote(to_symbol, from_symbol)
if not quote:
print("Quote not pulled from AlphaVantage. Check API key.")
return
df = pd.DataFrame.from_dict(quote)
df.index = df.index.to_series().apply(lambda x: x[3:]).values
df = df.iloc[[0, 2, 5, 4, 7, 8]]
if gtff.USE_TABULATE_DF:
print(tabulate(df, tablefmt="fancy_grid"))
else:
print(df.to_string())
print("")
def display_candle(data: pd.DataFrame, to_symbol: str, from_symbol: str):
"""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
"""
mc = mpf.make_marketcolors(
up="green",
down="red",
edge="black",
wick="black",
volume="in",
ohlc="i",
)
s = mpf.make_mpf_style(marketcolors=mc, gridstyle=":", y_on_right=True)
mpf.plot(
data,
type="candle",
mav=(20, 50),
volume=False,
title=f"\n{to_symbol}/{from_symbol}",
xrotation=10,
style=s,
figratio=(10, 7),
figscale=1.10,
figsize=(plot_autoscale()),
update_width_config=dict(
candle_linewidth=1.0,
candle_width=0.8,
),
)
if gtff.USE_ION:
plt.ion()
plt.show()
print("")

View File

@@ -1,15 +1,15 @@
import argparse
import os
from datetime import timedelta, datetime
from typing import List
# import pandas as pd
import pandas as pd
from prompt_toolkit.completion import NestedCompleter
from gamestonk_terminal import config_terminal as cfg
from colorama import Style
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.forex import fx_view
from gamestonk_terminal.forex.oanda import oanda_controller
from gamestonk_terminal.forex import av_view, av_model
# from gamestonk_terminal.forex.behavioural_analysis import ba_controller
# from gamestonk_terminal.forex.exploratory_data_analysis import eda_controller
from gamestonk_terminal.helper_funcs import (
@@ -17,14 +17,12 @@ from gamestonk_terminal.helper_funcs import (
MENU_GO_BACK,
MENU_QUIT,
MENU_RESET,
parse_known_args_and_warn,
try_except,
valid_date,
)
from gamestonk_terminal.menu import session
# from gamestonk_terminal.stocks.due_diligence import news_view, reddit_view
account = cfg.OANDA_ACCOUNT
class ForexController:
"""Forex Controller class"""
@@ -38,32 +36,11 @@ class ForexController:
"reset",
]
CHOICES_COMMANDS = [
"price",
"summary",
"list",
"orderbook",
"positionbook",
"order",
"load",
"cancel",
"positions",
"closetrade",
"trades",
"candles",
"pending",
"calendar",
# "news",
# "reddit",
]
# CHOICES_MENUS = [
# "eda",
# "ba",
# ]
CHOICES_COMMANDS = ["select", "load", "quote", "candle"]
CHOICES_MENUS = ["oanda"]
CHOICES += CHOICES_COMMANDS
# CHOICES += CHOICES_MENUS
CHOICES += CHOICES_MENUS
def __init__(self):
"""Construct Data"""
@@ -72,44 +49,37 @@ class ForexController:
"cmd",
choices=self.CHOICES,
)
self.instrument = None
self.from_symbol = "USD"
self.to_symbol = ""
self.data = pd.DataFrame()
def print_help(self):
"""Print help"""
print("\nForex Mode:")
print(" cls clear screen")
print(" ?/help show this menu again")
print(" q quit this menu and goes back to main menu")
print(" quit quit to abandon program")
print(" reset reset terminal and reload configs")
print("")
print(" summary shows account summary")
print(" calendar show calendar")
print(" list list order history")
print(" pending get information on pending orders")
print(" cancel cancel a pending order by ID -i order ID")
print(" positions get open positions")
print(" trades list open trades")
print(" closetrade close a trade by id")
print("")
print(f"Loaded instrument: {self.instrument if self.instrument else ''}")
print("")
print(" load load an instrument to use")
if self.instrument:
print(" candles show candles")
print(" price shows price for selected instrument")
print(" order place limit order -u # of units -p price")
print(" orderbook print orderbook")
print(" positionbook print positionbook")
print(" news print news [News API]")
print(
" reddit search reddit for posts about the loaded instrument"
)
# print("")
# print(
# "> ba behavioural analysis, from: reddit, stocktwits, twitter, google"
# )
print("")
dim_bool = self.from_symbol and self.to_symbol
help_str = f"""
>>> FOREX <<<
What would you like to do?
cls clear screen
?/help show this menu again
q quit this menu and goes back to main menu
quit quit to abandon program
reset reset terminal and reload configs
select select fx pair
To: {None or self.to_symbol}
From: {None or self.from_symbol}
{Style.DIM if not dim_bool else ""}
AlphaVantage (API Key required):
quote get last quote
load get historical data
candle show candle plot for loaded data
{Style.RESET_ALL}
Brokerages:
> oanda access oanda menu
"""
print(help_str)
def switch(self, an_input: str):
"""Process and dispatch input
@@ -159,59 +129,147 @@ class ForexController:
"""Process Reset command - reset the program"""
return MENU_RESET
def call_price(self, other_args):
"""Process Price Command"""
fx_view.get_fx_price(account, self.instrument, other_args)
@try_except
def call_select(self, other_args: List[str]):
"""Process select command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="select",
description="Select Forex pair in the form of TO -f FROM",
)
parser.add_argument(
"-t",
"--to",
help="To currency",
type=av_model.check_valid_forex_currency,
dest="to_symbol",
)
parser.add_argument(
"-f",
"--from",
help="From currency",
type=av_model.check_valid_forex_currency,
dest="from_symbol",
default=None,
)
def call_load(self, other_args):
self.instrument = fx_view.load(other_args)
if (
other_args
and "-f" not in other_args[0]
and "--from" not in other_args[0]
and "-t" not in other_args
and "-h" not in other_args
):
other_args.insert(0, "-t")
def call_summary(self, other_args):
"""Process account summary command"""
fx_view.get_account_summary(account, other_args)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
self.to_symbol = ns_parser.to_symbol
if ns_parser.from_symbol:
self.from_symbol = ns_parser.from_symbol
print("")
def call_orderbook(self, other_args):
"""Process Oanda Order Book"""
fx_view.get_order_book(self.instrument, other_args)
@try_except
def call_load(self, other_args: List[str]):
"""Process select command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="load",
description="Load historical exchange rate data.",
)
parser.add_argument(
"-r",
"--resolution",
choices=["i", "d", "w", "m"],
default="d",
help="Resolution of data. Can be intraday, daily, weekly or monthly",
dest="resolution",
)
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.",
dest="interval",
)
parser.add_argument(
"-s",
"--start_date",
default=(datetime.now() - timedelta(days=366)),
type=valid_date,
help="Start date of data.",
dest="start_date",
)
def call_positionbook(self, other_args):
"""Process Oanda Position Book"""
fx_view.get_position_book(self.instrument, other_args)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
def call_list(self, other_args: List[str]):
"""Process list orders command"""
fx_view.list_orders(account, other_args)
if not self.to_symbol or not self.from_symbol:
print(
"Make sure both a to symbol and a from symbol are supplied using <select> \n"
)
return
def call_order(self, other_args: List[str]):
"""Place limit order"""
fx_view.create_order(account, self.instrument, other_args)
self.data = av_model.get_historical(
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"),
)
print("")
def call_cancel(self, other_args: List[str]):
"""Cancel pending order by ID"""
fx_view.cancel_pending_order(account, other_args)
@try_except
def call_candle(self, other_args: List[str]):
"""Process quote command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="candle",
description="Show candle for loaded fx data",
)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
if self.data.empty:
print("No forex historical data loaded. Load first using <load>.")
return
def call_positions(self, other_args):
"""Get Open Positions"""
fx_view.get_open_positions(account, other_args)
av_view.display_candle(self.data, self.to_symbol, self.from_symbol)
def call_pending(self, other_args):
"""See up to 25 pending orders"""
fx_view.get_pending_orders(account, other_args)
@try_except
def call_quote(self, other_args: List[str]):
"""Process quote command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="quote",
description="Get current exchange rate quote",
)
def call_closetrade(self, other_args: List[str]):
"""Close a trade by id"""
fx_view.close_trade(account, other_args)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
def call_candles(self, other_args: List[str]):
fx_view.show_candles(account, self.instrument, other_args)
if not self.to_symbol or not self.from_symbol:
print(
"Make sure both a to symbol and a from symbol are supplied using <select> \n"
)
return
def call_trades(self, other_args):
"""List open trades"""
fx_view.get_open_trades(account, other_args)
av_view.display_quote(self.to_symbol, self.from_symbol)
def call_calendar(self, other_args: List[str]):
"""Call calendar"""
fx_view.calendar(self.instrument, other_args)
# pylint: disable=inconsistent-return-statements
def call_oanda(self, _):
ret = oanda_controller.menu()
if ret:
return True
self.print_help()
# TODO: Add news and reddit commands back

View File

@@ -0,0 +1,221 @@
"""Oanda Controller"""
__docformat__ = "numpy"
import argparse
import os
from typing import List
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.forex.oanda import oanda_view
# from gamestonk_terminal.forex.exploratory_data_analysis import eda_controller
from gamestonk_terminal.helper_funcs import (
get_flair,
MENU_GO_BACK,
MENU_QUIT,
MENU_RESET,
)
from gamestonk_terminal.menu import session
account = cfg.OANDA_ACCOUNT
class OandaController:
CHOICES = [
"cls",
"?",
"help",
"q",
"quit",
"reset",
]
CHOICES_COMMANDS = [
"price",
"summary",
"list",
"orderbook",
"positionbook",
"order",
"load",
"cancel",
"positions",
"closetrade",
"trades",
"candles",
"pending",
"calendar",
# "news",
# "reddit",
]
CHOICES += CHOICES_COMMANDS
def __init__(self):
"""Construct Data"""
self.fx_parser = argparse.ArgumentParser(add_help=False, prog="oanda")
self.fx_parser.add_argument(
"cmd",
choices=self.CHOICES,
)
self.instrument = None
def print_help(self):
"""Print help"""
print(" ?/help show this menu again")
print(" q quit this menu and goes back to main menu")
print(" quit quit to abandon program")
print(" reset reset terminal and reload configs")
print("")
print(" summary shows account summary")
print(" calendar show calendar")
print(" list list order history")
print(" pending get information on pending orders")
print(" cancel cancel a pending order by ID -i order ID")
print(" positions get open positions")
print(" trades list open trades")
print(" closetrade close a trade by id")
print("")
print(f"Loaded instrument: {self.instrument if self.instrument else ''}")
print("")
print(" load load an instrument to use")
if self.instrument:
print(" candles show candles")
print(" price shows price for selected instrument")
print(" order place limit order -u # of units -p price")
print(" orderbook print orderbook")
print(" positionbook print positionbook")
print("")
def switch(self, an_input: str):
"""Process and dispatch input
Returns
-------
MENU_GO_BACK, MENU_QUIT, MENU_RESET
MENU_GO_BACK - Show main context menu again
MENU_QUIT - Quit terminal
MENU_RESET - Reset terminal and go back to same previous menu
"""
# Empty command
if not an_input:
print("")
return None
(known_args, other_args) = self.fx_parser.parse_known_args(an_input.split())
# Help menu again
if known_args.cmd == "?":
self.print_help()
return None
# Clear screen
if known_args.cmd == "cls":
os.system("cls||clear")
return None
return getattr(
self, "call_" + known_args.cmd, lambda: "command not recognized!"
)(other_args)
def call_help(self, _):
"""Process Help Command"""
self.print_help()
def call_q(self, _):
"""Process Q command - quit the menu"""
return MENU_GO_BACK
def call_quit(self, _):
"""Process Quit command - exit the program"""
return MENU_QUIT
def call_reset(self, _):
"""Process Reset command - reset the program"""
return MENU_RESET
def call_price(self, other_args):
"""Process Price Command"""
oanda_view.get_fx_price(account, self.instrument, other_args)
def call_load(self, other_args):
self.instrument = oanda_view.load(other_args)
def call_summary(self, other_args):
"""Process account summary command"""
oanda_view.get_account_summary(account, other_args)
def call_orderbook(self, other_args):
"""Process Oanda Order Book"""
oanda_view.get_order_book(self.instrument, other_args)
def call_positionbook(self, other_args):
"""Process Oanda Position Book"""
oanda_view.get_position_book(self.instrument, other_args)
def call_list(self, other_args: List[str]):
"""Process list orders command"""
oanda_view.list_orders(account, other_args)
def call_order(self, other_args: List[str]):
"""Place limit order"""
oanda_view.create_order(account, self.instrument, other_args)
def call_cancel(self, other_args: List[str]):
"""Cancel pending order by ID"""
oanda_view.cancel_pending_order(account, other_args)
def call_positions(self, other_args):
"""Get Open Positions"""
oanda_view.get_open_positions(account, other_args)
def call_pending(self, other_args):
"""See up to 25 pending orders"""
oanda_view.get_pending_orders(account, other_args)
def call_closetrade(self, other_args: List[str]):
"""Close a trade by id"""
oanda_view.close_trade(account, other_args)
def call_candles(self, other_args: List[str]):
oanda_view.show_candles(account, self.instrument, other_args)
def call_trades(self, other_args):
"""List open trades"""
oanda_view.get_open_trades(account, other_args)
def call_calendar(self, other_args: List[str]):
"""Call calendar"""
oanda_view.calendar(self.instrument, other_args)
def menu():
"""Forex Menu"""
oanda_controller = OandaController()
oanda_controller.call_help(None)
while True:
if session and gtff.USE_PROMPT_TOOLKIT:
completer = NestedCompleter.from_nested_dict(
{c: None for c in oanda_controller.CHOICES}
)
an_input = session.prompt(
f"{get_flair()} (forex)(oanda)> ",
completer=completer,
)
else:
an_input = input(f"{get_flair()} (forex)(oanda)> ")
try:
process_input = oanda_controller.switch(an_input)
if process_input is not None:
return process_input
except SystemExit:
print("The command selected doesn't exit\n")
continue

View File

@@ -0,0 +1,2 @@
"""Oanda Model"""
__docformat__ = "numpy"

View File

@@ -0,0 +1,917 @@
"""Oanda View"""
__docformat__ = "numpy"
import argparse
from datetime import datetime
from typing import List
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import mplfinance as mpf
import pandas as pd
import pandas_ta as ta
import seaborn as sns
from oandapyV20 import API
from oandapyV20.endpoints import (
accounts,
forexlabs,
instruments,
orders,
positions,
pricing,
trades,
)
from oandapyV20.exceptions import V20Error
from gamestonk_terminal import config_plot as cfgPlot
from gamestonk_terminal import config_terminal as cfg
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.helper_funcs import (
check_non_negative,
parse_known_args_and_warn,
plot_autoscale,
)
client = API(access_token=cfg.OANDA_TOKEN, environment=cfg.OANDA_ACCOUNT_TYPE)
account = cfg.OANDA_ACCOUNT
def get_fx_price(accountID, instrument, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
prog="price",
description="Get price for selected instrument.",
)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
try:
parameters = {"instruments": instrument}
request = pricing.PricingInfo(accountID=accountID, params=parameters)
response = client.request(request)
bid = response["prices"][0]["bids"][0]["price"]
ask = response["prices"][0]["asks"][0]["price"]
print(instrument + " Bid: " + bid)
print(instrument + " Ask: " + ask)
print("")
except V20Error as e:
# pylint: disable=W0123
d_error = eval(e.msg)
print(d_error["errorMessage"], "\n")
def get_account_summary(accountID, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
prog="summary",
description="Print some information about your account.",
)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
try:
request = accounts.AccountSummary(accountID=accountID)
response = client.request(request)
df_summary = pd.DataFrame(
[
{"Type": "Balance", "Value": response["account"]["balance"]},
{"Type": "NAV", "Value": response["account"]["NAV"]},
{
"Type": "Unrealized P/L",
"Value": response["account"]["unrealizedPL"],
},
{"Type": "Total P/L", "Value": response["account"]["pl"]},
{
"Type": "Open Trade Count",
"Value": response["account"]["openTradeCount"],
},
{
"Type": "Margin Available",
"Value": response["account"]["marginAvailable"],
},
{"Type": "Margin Used", "Value": response["account"]["marginUsed"]},
{
"Type": "Margin Closeout",
"Value": response["account"]["marginCloseoutNAV"],
},
{
"Type": "Margin Closeout Percent",
"Value": response["account"]["marginCloseoutPercent"],
},
{
"Type": "Margin Closeout Position Value",
"Value": response["account"]["marginCloseoutPositionValue"],
},
]
)
print(df_summary.to_string(index=False, header=False))
print("")
except V20Error as e:
# pylint: disable=W0123
d_error = eval(e.msg)
print(d_error["errorMessage"], "\n")
def list_orders(accountID, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
prog="list",
description="List order history",
)
parser.add_argument(
"-s",
"--state",
dest="state",
action="store",
default="ALL",
required=False,
help="Select state for order list. ",
)
parser.add_argument(
"-c",
"--count",
dest="count",
action="store",
default=20,
required=False,
help="the number of orders to retrieve ",
)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
parameters = {}
parameters["state"] = ns_parser.state.upper()
parameters["count"] = ns_parser.count
try:
request = orders.OrderList(accountID, parameters)
response = client.request(request)
df = pd.DataFrame.from_dict(response["orders"])
df = df[["id", "instrument", "units", "price", "state", "type"]]
print(df)
print("")
except KeyError:
print("No orders were found\n")
except V20Error as e:
# pylint: disable=W0123
d_error = eval(e.msg)
print(d_error["errorMessage"], "\n")
def get_order_book(instrument, other_args):
parser = argparse.ArgumentParser(
add_help=False,
prog="orderbook",
description="Plot the orderbook for the instrument if Oanda provides one.",
)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
parameters = {"bucketWidth": "1"}
try:
instrument = format_instrument(instrument, "_")
request = instruments.InstrumentsOrderBook(
instrument=instrument, params=parameters
)
response = client.request(request)
df = pd.DataFrame.from_dict(response["orderBook"]["buckets"])
pd.set_option("display.max_rows", None)
df = df.take(range(527, 727, 1))
book_plot(df, instrument, "Order Book")
print("")
except V20Error as e:
# pylint: disable=W0123
d_error = eval(e.msg)
print(d_error["errorMessage"], "\n")
def get_position_book(instrument, other_args):
parser = argparse.ArgumentParser(
add_help=False,
prog="positionbook",
description="Plot the positionbook for the instrument if Oanda provides one.",
)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
try:
instrument = format_instrument(instrument, "_")
request = instruments.InstrumentsPositionBook(instrument=instrument)
response = client.request(request)
df = pd.DataFrame.from_dict(response["positionBook"]["buckets"])
pd.set_option("display.max_rows", None)
df = df.take(range(219, 415, 1))
book_plot(df, instrument, "Position Book")
print("")
except V20Error as e:
# pylint: disable=W0123
d_error = eval(e.msg)
print(d_error["errorMessage"], "\n")
def create_order(accountID, instrument, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
prog="order",
description="Create order",
)
parser.add_argument(
"-u",
"--unit",
dest="units",
action="store",
type=int,
default=0,
required=True,
help="The number of units to place in the order request. Positive for "
+ "a long position and negative for a short position ",
)
parser.add_argument(
"-p",
"--price",
dest="price",
action="store",
type=check_non_negative,
required=True,
help="The price to set for the limit order. ",
)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
if "JPY" in instrument or "THB" in instrument or "HUF" in instrument:
ns_parser.price = round(ns_parser.price, 3)
else:
ns_parser.price = round(ns_parser.price, 5)
data = {
"order": {
"price": ns_parser.price,
"instrument": instrument,
"units": ns_parser.units,
"type": "LIMIT",
"timeInForce": "GTC",
"positionFill": "DEFAULT",
}
}
try:
request = orders.OrderCreate(accountID, data)
response = client.request(request)
order_data = []
order_data.append(
{
"Order ID": response["orderCreateTransaction"]["id"],
"Instrument": response["orderCreateTransaction"]["instrument"],
"Price": response["orderCreateTransaction"]["price"],
"Units": response["orderCreateTransaction"]["units"],
}
)
df = pd.DataFrame.from_dict(order_data)
print(df.to_string(index=False))
print("")
except V20Error as e:
# pylint: disable=W0123
d_error = eval(e.msg)
print(d_error["errorMessage"], "\n")
except Exception as e:
print(e)
def cancel_pending_order(accountID, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
prog="cancel",
description="Cancel Pending Order ",
)
parser.add_argument(
"-i",
"--id",
dest="orderID",
action="store",
type=str,
help="The pending order ID to cancel ",
)
if other_args:
if "-" not in other_args[0]:
other_args.insert(0, "-i")
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
try:
request = orders.OrderCancel(accountID, ns_parser.orderID)
response = client.request(request)
order_id = response["orderCancelTransaction"]["orderID"]
print(f"Order {order_id} canceled.")
print("")
except V20Error as e:
# pylint: disable=W0123
d_error = eval(e.msg)
print(d_error["errorMessage"], "\n")
def get_pending_orders(accountID, other_args):
parser = argparse.ArgumentParser(
add_help=False,
prog="pending",
description="Gets information about pending orders.",
)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
try:
request = orders.OrdersPending(accountID)
response = client.request(request)
pending_data = []
for i in range(len(response["orders"])):
pending_data.append(
{
"Order ID": response["orders"][i]["id"],
"Instrument": response["orders"][i]["instrument"],
"Price": response["orders"][i]["price"],
"Units": response["orders"][i]["units"],
"Time Created": response["orders"][i]["createTime"][:10]
+ " "
+ response["orders"][i]["createTime"][11:19],
"Time In Force": response["orders"][i]["timeInForce"],
}
)
df = pd.DataFrame.from_dict(pending_data)
if not pending_data:
print("No pending orders")
else:
print(df.to_string(index=False))
print("")
except V20Error as e:
# pylint: disable=W0123
d_error = eval(e.msg)
print(d_error["errorMessage"], "\n")
def get_open_positions(accountID, other_args):
parser = argparse.ArgumentParser(
add_help=False,
prog="positions",
description="Gets information about open positions.",
)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
try:
request = positions.OpenPositions(accountID)
response = client.request(request)
position_data = []
for i in range(len(response["positions"])):
position_data.append(
{
"Instrument": response["positions"][i]["instrument"],
"Long Units": response["positions"][i]["long"]["units"],
"Total Long P/L": response["positions"][i]["long"]["units"],
"Unrealized Long P/L": response["positions"][i]["long"][
"unrealizedPL"
],
"Short Units": response["positions"][i]["short"]["units"],
"Total Short P/L": response["positions"][i]["short"]["pl"],
"Short Unrealized P/L": response["positions"][i]["short"][
"unrealizedPL"
],
}
)
df = pd.DataFrame.from_dict(position_data)
print(df.to_string(index=False))
print("")
except V20Error as e:
# pylint: disable=W0123
d_error = eval(e.msg)
print(d_error["errorMessage"], "\n")
def get_open_trades(accountID, other_args):
parser = argparse.ArgumentParser(
add_help=False,
prog="trades",
description="Gets information about open trades.",
)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
try:
request = trades.OpenTrades(accountID)
response = client.request(request)
try:
df = pd.DataFrame.from_dict(response["trades"])
df = df[
[
"id",
"instrument",
"initialUnits",
"currentUnits",
"price",
"unrealizedPL",
]
]
df = df.rename(
columns={
"id": "ID",
"instrument": "Instrument",
"initialUnits": "Initial Units",
"currentUnits": "Current Units",
"price": "Entry Price",
"unrealizedPL": "Unrealized P/L",
}
)
print(df.to_string(index=False))
print("")
except KeyError:
print("No trades were found")
print("")
except V20Error as e:
msg_length = len(e.msg)
msg = e.msg[17 : msg_length - 2]
print(msg)
print("")
def close_trade(accountID, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
prog="closetrade",
description="Close a trade by id.",
)
parser.add_argument(
"-i",
"--id",
dest="orderID",
action="store",
type=str,
required=False,
help="The Trade ID to close. ",
)
parser.add_argument(
"-u",
"--units",
dest="units",
action="store",
required=False,
help="The number of units on the trade to close. If not set it "
+ "defaults to all units. ",
)
if other_args:
if "-i" not in other_args[0]:
other_args.insert(0, "-i")
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
data = {}
if ns_parser.units is not None:
data["units"] = ns_parser.units
try:
request = trades.TradeClose(accountID, ns_parser.orderID, data)
response = client.request(request)
close_data = []
close_data.append(
{
"OrderID": response["orderCreateTransaction"]["tradeClose"]["tradeID"],
"Instrument": response["orderFillTransaction"]["instrument"],
"Units": response["orderCreateTransaction"]["units"],
"Price": response["orderFillTransaction"]["price"],
"P/L": response["orderFillTransaction"]["pl"],
}
)
df = pd.DataFrame.from_dict(close_data)
print(df.to_string(index=False))
print("")
except V20Error as e:
# pylint: disable=W0123
d_error = eval(e.msg)
print(d_error["errorMessage"], "\n")
except Exception as e:
print(e, "\n")
def show_candles(accountID, instrument, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
prog="candles",
description="Display Candle Data",
)
parser.add_argument(
"-g",
"--granularity",
dest="granularity",
action="store",
type=str,
default="D",
required=False,
help="The timeframe to get for the candle chart (Seconds: S5, S10, S15, S30 "
+ "Minutes: M1, M2, M4, M5, M10, M15, M30 Hours: H1, H2, H3, H4, H6, H8, H12 "
+ "Day (default): D, Week: W Month: M",
)
parser.add_argument(
"-c",
"--count",
dest="candlecount",
action="store",
default=180,
type=int,
required=False,
help="The number of candles to retrieve. Default:180 ",
)
parser.add_argument(
"-a",
"--ad",
dest="ad",
action="store_true",
help="Adds ad (Accumulation/Distribution Indicator) to the chart",
)
parser.add_argument(
"-b",
"--bbands",
dest="bbands",
action="store_true",
help="Adds Bollinger Bands to the chart",
)
parser.add_argument(
"-C",
"--cci",
dest="cci",
action="store_true",
help="Adds cci (Commodity Channel Index) to the chart",
)
parser.add_argument(
"-e",
"--ema",
dest="ema",
action="store_true",
help="Adds ema (Exponential Moving Average) to the chart",
)
parser.add_argument(
"-o",
"--obv",
dest="obv",
action="store_true",
help="Adds obv (On Balance Volume) to the chart",
)
parser.add_argument(
"-r",
"--rsi",
dest="rsi",
action="store_true",
help="Adds rsi (Relative Strength Index) to the chart",
)
parser.add_argument(
"-s",
"--sma",
dest="sma",
action="store_true",
help="Adds sma (Simple Moving Average) to the chart",
)
parser.add_argument(
"-v",
"--vwap",
dest="vwap",
action="store_true",
help="Adds vwap (Volume Weighted Average Price) to the chart",
)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
parameters = {}
parameters["granularity"] = ns_parser.granularity.upper()
parameters["count"] = ns_parser.candlecount
try:
instrument = format_instrument(instrument, "_")
df = get_candles_dataframe(accountID, instrument, parameters)
plots_to_add, legends, subplot_legends = add_plots(df, ns_parser)
if gtff.USE_ION:
plt.ion()
# pylint: disable=W0612
fig, ax = mpf.plot(
df,
type="candle",
style="charles",
volume=True,
title=f"{instrument} {ns_parser.granularity}",
returnfig=True,
addplot=plots_to_add,
)
ax[0].set_title(f"{instrument} {ns_parser.granularity}")
ax[0].legend(legends)
# pylint: disable=C0200
for i in range(0, len(subplot_legends), 2):
ax[subplot_legends[i]].legend(subplot_legends[i + 1])
print("")
except V20Error as e:
# pylint: disable=W0123
d_error = eval(e.msg)
print(d_error["errorMessage"], "\n")
except TypeError as e:
print(e)
def add_plots(df, ns_parser):
panel_number = 2
plots_to_add = []
legends = []
subplot_legends = []
if ns_parser.ad:
ad = ta.ad(df["High"], df["Low"], df["Close"], df["Volume"])
ad_plot = mpf.make_addplot(ad, panel=panel_number)
plots_to_add.append(ad_plot)
subplot_legends.extend([panel_number * 2, ["AD"]])
panel_number += 1
if ns_parser.bbands:
bbands = ta.bbands(df["Close"])
bbands = bbands.drop("BBB_5_2.0", axis=1)
bbands_plot = mpf.make_addplot(bbands, panel=0)
plots_to_add.append(bbands_plot)
legends.extend(["Lower BBand", "Middle BBand", "Upper BBand"])
if ns_parser.cci:
cci = ta.cci(df["High"], df["Low"], df["Close"])
cci_plot = mpf.make_addplot(cci, panel=panel_number)
plots_to_add.append(cci_plot)
subplot_legends.extend([panel_number * 2, ["CCI"]])
panel_number += 1
if ns_parser.ema:
ema = ta.ema(df["Close"])
ema_plot = mpf.make_addplot(ema, panel=0)
plots_to_add.append(ema_plot)
legends.append("10 EMA")
if ns_parser.rsi:
rsi = ta.rsi(df["Close"])
rsi_plot = mpf.make_addplot(rsi, panel=panel_number)
plots_to_add.append(rsi_plot)
subplot_legends.extend([panel_number * 2, ["RSI"]])
panel_number += 1
if ns_parser.obv:
obv = ta.obv(df["Close"], df["Volume"])
obv_plot = mpf.make_addplot(obv, panel=panel_number)
plots_to_add.append(obv_plot)
subplot_legends.extend([panel_number * 2, ["OBV"]])
panel_number += 1
if ns_parser.sma:
sma_length = [20, 50]
for length in sma_length:
sma = ta.sma(df["Close"], length=length)
sma_plot = mpf.make_addplot(sma, panel=0)
plots_to_add.append(sma_plot)
legends.append(f"{length} SMA")
if ns_parser.vwap:
vwap = ta.vwap(df["High"], df["Low"], df["Close"], df["Volume"])
vwap_plot = mpf.make_addplot(vwap, panel=0)
plots_to_add.append(vwap_plot)
legends.append("vwap")
return plots_to_add, legends, subplot_legends
def calendar(instrument, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
prog="calendar",
description="Show Calendar Data",
)
parser.add_argument(
"-d",
"--days",
dest="days",
action="store",
type=int,
default=7,
required=False,
help="The number of days to search for, up to 30 forward or backward "
+ "use negative numbers to search back. ",
)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
parameters = {"instrument": instrument, "period": str(ns_parser.days * 86400 * -1)}
try:
request = forexlabs.Calendar(params=parameters)
response = client.request(request)
l_data = []
# pylint: disable=C0200
for i in range(len(response)):
if "forecast" in response[i]:
forecast = response[i]["forecast"]
if response[i]["unit"] != "Index":
forecast += response[i]["unit"]
else:
forecast = ""
if "market" in response[i]:
market = response[i]["market"]
if response[i]["unit"] != "Index":
market += response[i]["unit"]
else:
market = ""
if "actual" in response[i]:
actual = response[i]["actual"]
if response[i]["unit"] != "Index":
actual += response[i]["unit"]
else:
actual = ""
if "previous" in response[i]:
previous = response[i]["previous"]
if response[i]["unit"] != "Index":
previous += response[i]["unit"]
else:
previous = ""
if "impact" in response[i]:
impact = response[i]["impact"]
else:
impact = ""
l_data.append(
{
"Title": response[i]["title"],
"Time": datetime.fromtimestamp(response[i]["timestamp"]),
"Impact": impact,
"Forecast": forecast,
"Market Forecast": market,
"Currency": response[i]["currency"],
"Region": response[i]["region"],
"Actual": actual,
"Previous": previous,
}
)
print(pd.DataFrame(l_data).to_string(index=False))
print("")
except V20Error as e:
# pylint: disable=W0123
d_error = eval(e.msg)
print(d_error["message"], "\n")
# pylint: disable=W0613
# pylint: disable=R1710
def get_candles_dataframe(accountID, instrument, parameters):
if not parameters:
parameters = {
"granularity": "D",
"count": "1500",
}
try:
request = instruments.InstrumentsCandles(instrument, params=parameters)
response = client.request(request)
candles_data = []
for i in range(len(response["candles"])):
candles_data.append(
{
"Date": response["candles"][i]["time"][:10]
+ " "
+ response["candles"][i]["time"][11:19],
"Open": float(response["candles"][i]["mid"]["o"]),
"High": float(response["candles"][i]["mid"]["h"]),
"Low": float(response["candles"][i]["mid"]["l"]),
"Close": float(response["candles"][i]["mid"]["c"]),
"Volume": response["candles"][i]["volume"],
}
)
df = pd.DataFrame(candles_data)
df.set_index("Date", inplace=True)
df.index = pd.to_datetime(df.index)
return df
except V20Error as e:
# pylint: disable=W0123
d_error = eval(e.msg)
print(d_error["errorMessage"], "\n")
# pylint: disable=R1710
def load(other_args: List[str]):
"""Load a forex instrument to use"""
parser = argparse.ArgumentParser(
add_help=False,
prog="load",
description="Forex using oanda",
)
parser.add_argument(
"-i",
"--instrument",
required=True,
type=str,
dest="instrument",
help="Forex pair to use. ",
)
try:
if other_args:
if "-" not in other_args[0]:
other_args.insert(0, "-i")
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
print("")
return ns_parser.instrument.upper()
except Exception as e:
print(e, "\n")
return None
def book_plot(df, instrument, book_type):
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI)
df = df.apply(pd.to_numeric)
df["shortCountPercent"] = df["shortCountPercent"] * -1
axis_origin = max(
abs(max(df["longCountPercent"])), abs(max(df["shortCountPercent"]))
)
ax.set_xlim(-axis_origin, +axis_origin)
sns.set_style(style="darkgrid")
sns.barplot(
x="longCountPercent",
y="price",
data=df,
label="Count Percent",
color="green",
orient="h",
)
sns.barplot(
x="shortCountPercent",
y="price",
data=df,
label="Prices",
color="red",
orient="h",
)
ax.invert_yaxis()
plt.title(f"{instrument} {book_type}")
plt.xlabel("Count Percent")
plt.ylabel("Price")
sns.despine(left=True, bottom=True)
ax.yaxis.set_major_locator(mticker.MultipleLocator(5))
if gtff.USE_ION:
plt.ion()
plt.show()
# pylint: disable=R1710
def format_instrument(instrument, char):
try:
if char not in instrument:
instrument_list = list(instrument)
instrument_list.pop(3)
if char == " ":
instrument = "".join(map(str, instrument_list))
else:
instrument_list.insert(3, char)
instrument = "".join(map(str, instrument_list))
return instrument
except TypeError:
print("Please load an instrument")
print("")

10
website/content/forex/candle/_index.md vendored Normal file
View File

@@ -0,0 +1,10 @@
```
usage: candle [-h]
```
Show candle for loaded fx data
```
optional arguments:
-h, --help show this help message (default: False)
```

16
website/content/forex/load/_index.md vendored Normal file
View File

@@ -0,0 +1,16 @@
```
usage: load [-r {i,d,w,m}] [-i {1,5,15,30,60}] [-s START_DATE] [-h]
```
Load historical exchange rate data.
```
optional arguments:
-r {i,d,w,m}, --resolution {i,d,w,m}
Resolution of data. Can be intraday, daily, weekly or monthly (default: d)
-i {1,5,15,30,60}, --interval {1,5,15,30,60}
Interval of intraday data. Can be 1, 5, 15, 30 or 60. (default: 5)
-s START_DATE, --start_date START_DATE
Start date of data. (default: 2020-11-01 16:41:01.349343)
-h, --help show this help message (default: False)
```

10
website/content/forex/quote/_index.md vendored Normal file
View File

@@ -0,0 +1,10 @@
```
usage: quote [-h]
```
Get current exchange rate quote
```
optional arguments:
-h, --help show this help message (default: False)
```

14
website/content/forex/select/_index.md vendored Normal file
View File

@@ -0,0 +1,14 @@
```
usage: select [-t TO_SYMBOL] [-f FROM_SYMBOL] [-h]
```
Select Forex pair in the form of TO -f FROM
```
optional arguments:
-t TO_SYMBOL, --to TO_SYMBOL
To currency (default: None)
-f FROM_SYMBOL, --from FROM_SYMBOL
From currency (default: None)
-h, --help show this help message (default: False)
```

View File

@@ -878,5 +878,12 @@ main:
ref: "/portfolio/portfolio_optimization/ef"
- name: forex
ref: "/forex"
sub:
- name: select
ref: "/forex/select"
- name: load
ref: "/forex/load"
- name: candle
ref: "/forex/candle"
- name: resources
ref: "/resources"