From 9897f9c581c0a5fddab1d32d6e6563c54100d959 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 18 Apr 2021 01:40:35 -0600 Subject: [PATCH 1/8] stocktwits integration --- gamestonk_terminal/forex/fx_controller.py | 26 ++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/gamestonk_terminal/forex/fx_controller.py b/gamestonk_terminal/forex/fx_controller.py index d867fe0b476..4c621784979 100644 --- a/gamestonk_terminal/forex/fx_controller.py +++ b/gamestonk_terminal/forex/fx_controller.py @@ -6,10 +6,11 @@ from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.menu import session from gamestonk_terminal.forex import fx_view from gamestonk_terminal import config_terminal as cfg +from gamestonk_terminal.due_diligence import news_view +from gamestonk_terminal.behavioural_analysis import stocktwits_view account = cfg.OANDA_ACCOUNT - class ForexController: """Oanda Controller class""" @@ -31,6 +32,9 @@ class ForexController: "candles", "pending", "calendar", + "news", + "bullbear", + "messages", ] def __init__(self): @@ -51,12 +55,14 @@ class ForexController: print(" q Quit this menu and goes back to main menu") print(" quit Quit to abandon program") print("") + print(" bullbear Call bullbear from stocktwits") print(" cancel Cancel a pending order by ID -i order ID") print(" candles Show candles") print(" calendar Show calendar") print(" closetrade Close a trade by id") print(" list List order history") print(" load Load an instrument to use") + print(" news Get news") print(" order Place limit order -u # of units -p price") print(" orderbook Print orderbook") print(" pending Get information on pending orders") @@ -148,6 +154,24 @@ class ForexController: """Call calendar""" fx_view.calendar(self.instrument, other_args) + def call_news(self, other_args: List[str]): + news_view.news(other_args, self.instrument) + + def call_bullbear(self, other_args: List[str]): + instrument = remove_underscore(self.instrument) + stocktwits_view.bullbear(other_args, instrument) + + def call_messages(self, other_args: List[str]): + instrument = remove_underscore(self.instrument) + stocktwits_view.messages(other_args, instrument) + + +def remove_underscore(instrument): + instrument_list = list(instrument) + instrument_list.pop(3) + adjusted_instrument = "".join(map(str, instrument_list)) + return adjusted_instrument + def menu(): """Oanda Menu""" From 34fc705d630d63b20afdcd1584ed3557ff459ba9 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 18 Apr 2021 08:28:10 -0600 Subject: [PATCH 2/8] Added ta indicators --- gamestonk_terminal/forex/fx_controller.py | 8 ++ gamestonk_terminal/forex/fx_view.py | 147 ++++++++++++++++++++-- gamestonk_terminal/oanda/__init.py__ | 0 3 files changed, 148 insertions(+), 7 deletions(-) delete mode 100644 gamestonk_terminal/oanda/__init.py__ diff --git a/gamestonk_terminal/forex/fx_controller.py b/gamestonk_terminal/forex/fx_controller.py index 4c621784979..33482255d57 100644 --- a/gamestonk_terminal/forex/fx_controller.py +++ b/gamestonk_terminal/forex/fx_controller.py @@ -8,6 +8,8 @@ from gamestonk_terminal.forex import fx_view from gamestonk_terminal import config_terminal as cfg from gamestonk_terminal.due_diligence import news_view from gamestonk_terminal.behavioural_analysis import stocktwits_view +from gamestonk_terminal.exploratory_data_analysis import eda_api +import pandas as pd account = cfg.OANDA_ACCOUNT @@ -35,6 +37,7 @@ class ForexController: "news", "bullbear", "messages", + "edasummary", ] def __init__(self): @@ -62,6 +65,7 @@ class ForexController: print(" closetrade Close a trade by id") print(" list List order history") print(" load Load an instrument to use") + print(" messages Get messages from stocktwits") print(" news Get news") print(" order Place limit order -u # of units -p price") print(" orderbook Print orderbook") @@ -165,6 +169,10 @@ class ForexController: instrument = remove_underscore(self.instrument) stocktwits_view.messages(other_args, instrument) + def call_edasummary(self, other_args: List[str]): + df = pd.read_csv(".candles.csv") + eda_api.summary(other_args, df) + def remove_underscore(instrument): instrument_list = list(instrument) diff --git a/gamestonk_terminal/forex/fx_view.py b/gamestonk_terminal/forex/fx_view.py index fafa478f9ad..df0210c73f4 100644 --- a/gamestonk_terminal/forex/fx_view.py +++ b/gamestonk_terminal/forex/fx_view.py @@ -14,6 +14,7 @@ from gamestonk_terminal import config_plot as cfgPlot from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.helper_funcs import parse_known_args_and_warn, plot_autoscale import pandas as pd +import pandas_ta as ta import mplfinance as mpf import matplotlib.pyplot as plt import matplotlib.ticker as mticker @@ -317,13 +318,16 @@ def show_candles(accountID, instrument, other_args: List[str]): description="Display Candle Data", ) parser.add_argument( - "-g", - "--granularity", - dest="granularity", - action="store", - type=str, - default="D", - required=False, + "-a", + "--aroon", + dest="aroon", + action="store_true", + ) + parser.add_argument( + "-b", + "--bollinger-bands", + dest="bbands", + action="store_true", ) parser.add_argument( "-c", @@ -334,6 +338,76 @@ def show_candles(accountID, instrument, other_args: List[str]): type=int, required=False, ) + parser.add_argument("-d", "--ad", dest="ad", action="store_true",) + parser.add_argument( + "-e", + "--ema", + dest="ema", + action="store_true", + ) + parser.add_argument( + "-f", + "--fwma", + dest="fwma", + action="store_true" + ) + parser.add_argument( + "-g", + "--granularity", + dest="granularity", + action="store", + type=str, + default="D", + required=False, + ) + parser.add_argument( + "-i", + "--cci", + dest="cci", + action="store_true", + ) + parser.add_argument( + "-m", + "--macd", + dest="macd", + action="store_true", + ) + parser.add_argument( + "-o", + "--obv", + dest="obv", + action="store_true", + ) + parser.add_argument( + "-r", + "--rsi", + dest="rsi", + action="store_true", + ) + parser.add_argument( + "-s", + "--sma", + dest="sma", + action="store_true", + ) + parser.add_argument( + "-t", + "--stoch", + dest="stoch", + action="store_true", + ) + parser.add_argument( + "-v", + "--vwap", + dest="vwap", + action="store_true", + ) + parser.add_argument( + "-x", + "--adx", + dest="adx", + action="store_true", + ) ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: @@ -350,14 +424,73 @@ def show_candles(accountID, instrument, other_args: List[str]): df = pd.read_csv(".candles.csv", index_col=0) df.index = pd.to_datetime(df.index) df.columns = ["Open", "High", "Low", "Close", "Volume"] + + plots_to_add = [] + + if ns_parser.ad: + ad = ta.ad(df["High"], df["Low"], df["Close"], df["Volume"]) + ad_plot = mpf.make_addplot(ad, panel=3) + plots_to_add.append(ad_plot) + if ns_parser.adx: + adx = ta.adx(df["High"], df["Low"], df["Close"]) + adx_plot = mpf.make_addplot(adx, panel=3) + plots_to_add.append(adx_plot) + if ns_parser.aroon: + aroon = ta.aroon(df["High"], df["Low"]) + aroon_plot = mpf.make_addplot(aroon, panel=3) + plots_to_add.append(aroon_plot) + if ns_parser.cci: + cci = ta.cci(df["High"], df["Low"], df["Close"], length=20) + cci_plot = mpf.make_addplot(cci, panel=3) + plots_to_add.append(cci_plot) + 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) + if ns_parser.ema: + ema = ta.ema(df["Close"]) + ema_plot = mpf.make_addplot(ema, panel=0) + plots_to_add.append(ema_plot) + if ns_parser.fwma: + fwma = ta.fwma(df["Close"]) + fwma_plot = mpf.make_addplot(fwma, panel=0) + plots_to_add.append(fwma_plot) + if ns_parser.rsi: + rsi = ta.rsi(df["Close"], length=14) + rsi_plot = mpf.make_addplot(rsi, panel=2) + plots_to_add.append(rsi_plot) + if ns_parser.macd: + macd = ta.macd(df["Close"], fast=12, slow=26, signal=9) + macd_plot = mpf.make_addplot(macd, panel=3) + plots_to_add.append(macd_plot) + if ns_parser.obv: + obv = ta.obv(df["Close"], df["Volume"]) + obv_plot = mpf.make_addplot(obv, panel=3) + plots_to_add.append(obv_plot) + if ns_parser.sma: + sma = ta.sma(df["Close"]) + sma_plot = mpf.make_addplot(sma, panel=0) + plots_to_add.append(sma_plot) + if ns_parser.stoch: + stoch = ta.stoch(df["High"], df["Low"], df["Close"]) + stoch_plot = mpf.make_addplot(stoch, panel=0) + plots_to_add.append(stoch_plot) + 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) + if gtff.USE_ION: plt.ion() + mpf.plot( df, type="candle", style="charles", volume=True, title=f"{instrument} {ns_parser.granularity}", + addplot=plots_to_add, ) except Exception as e: print(e) diff --git a/gamestonk_terminal/oanda/__init.py__ b/gamestonk_terminal/oanda/__init.py__ deleted file mode 100644 index e69de29bb2d..00000000000 From 49959ad6243bf04f6971573a3772d120f3d86909 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 18 Apr 2021 08:59:09 -0600 Subject: [PATCH 3/8] Ran Linter --- gamestonk_terminal/forex/fx_controller.py | 1 + gamestonk_terminal/forex/fx_view.py | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/gamestonk_terminal/forex/fx_controller.py b/gamestonk_terminal/forex/fx_controller.py index 33482255d57..b3acd9efc83 100644 --- a/gamestonk_terminal/forex/fx_controller.py +++ b/gamestonk_terminal/forex/fx_controller.py @@ -13,6 +13,7 @@ import pandas as pd account = cfg.OANDA_ACCOUNT + class ForexController: """Oanda Controller class""" diff --git a/gamestonk_terminal/forex/fx_view.py b/gamestonk_terminal/forex/fx_view.py index df0210c73f4..453ab458843 100644 --- a/gamestonk_terminal/forex/fx_view.py +++ b/gamestonk_terminal/forex/fx_view.py @@ -338,7 +338,12 @@ def show_candles(accountID, instrument, other_args: List[str]): type=int, required=False, ) - parser.add_argument("-d", "--ad", dest="ad", action="store_true",) + parser.add_argument( + "-d", + "--ad", + dest="ad", + action="store_true", + ) parser.add_argument( "-e", "--ema", @@ -349,7 +354,7 @@ def show_candles(accountID, instrument, other_args: List[str]): "-f", "--fwma", dest="fwma", - action="store_true" + action="store_true", ) parser.add_argument( "-g", From 8289cf4bd295ef46019cf234a9821c07e1c1d9a8 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 18 Apr 2021 09:01:39 -0600 Subject: [PATCH 4/8] reorganized candle switches --- gamestonk_terminal/forex/fx_view.py | 95 ++++------------------------- 1 file changed, 13 insertions(+), 82 deletions(-) diff --git a/gamestonk_terminal/forex/fx_view.py b/gamestonk_terminal/forex/fx_view.py index 453ab458843..445c2befd3a 100644 --- a/gamestonk_terminal/forex/fx_view.py +++ b/gamestonk_terminal/forex/fx_view.py @@ -317,102 +317,33 @@ def show_candles(accountID, instrument, other_args: List[str]): prog="show_candles", description="Display Candle Data", ) - parser.add_argument( - "-a", - "--aroon", - dest="aroon", - action="store_true", - ) - parser.add_argument( - "-b", - "--bollinger-bands", - dest="bbands", - action="store_true", - ) parser.add_argument( "-c", "--count", dest="candlecount", action="store", default=180, - type=int, - required=False, - ) - parser.add_argument( - "-d", - "--ad", - dest="ad", - action="store_true", - ) - parser.add_argument( - "-e", - "--ema", - dest="ema", - action="store_true", - ) - parser.add_argument( - "-f", - "--fwma", - dest="fwma", - action="store_true", ) parser.add_argument( "-g", "--granularity", dest="granularity", action="store", - type=str, default="D", - required=False, - ) - parser.add_argument( - "-i", - "--cci", - dest="cci", - action="store_true", - ) - parser.add_argument( - "-m", - "--macd", - dest="macd", - action="store_true", - ) - parser.add_argument( - "-o", - "--obv", - dest="obv", - action="store_true", - ) - parser.add_argument( - "-r", - "--rsi", - dest="rsi", - action="store_true", - ) - parser.add_argument( - "-s", - "--sma", - dest="sma", - action="store_true", - ) - parser.add_argument( - "-t", - "--stoch", - dest="stoch", - action="store_true", - ) - parser.add_argument( - "-v", - "--vwap", - dest="vwap", - action="store_true", - ) - parser.add_argument( - "-x", - "--adx", - dest="adx", - action="store_true", ) + parser.add_argument("-a", "--ad", dest="ad", action="store_true") + parser.add_argument("-A", "--adx", dest="adx", action="store_true") + parser.add_argument("-b", "--bollinger-bands", dest="bbands", action="store_true") + parser.add_argument("-C", "--cci", dest="cci", action="store_true") + parser.add_argument("-e", "--ema", dest="ema", action="store_true") + parser.add_argument("-f", "--fwma", dest="fwma", action="store_true") + parser.add_argument("-m", "--macd", dest="macd", action="store_true") + parser.add_argument("-o", "--obv", dest="obv", action="store_true") + parser.add_argument("-r", "--rsi", dest="rsi", action="store_true") + parser.add_argument("-R", "--aroon", dest="aroon", action="store_true") + parser.add_argument("-s", "--sma", dest="sma", action="store_true") + parser.add_argument("-S", "--stoch", dest="stoch", action="store_true") + parser.add_argument("-v", "--vwap", dest="vwap", action="store_true") ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: From fd31f7ac3190be4de266420ebd54fc718ff5b0d7 Mon Sep 17 00:00:00 2001 From: DJ Date: Tue, 20 Apr 2021 06:41:42 -0600 Subject: [PATCH 5/8] test --- gamestonk_terminal/forex/fx_view.py | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/gamestonk_terminal/forex/fx_view.py b/gamestonk_terminal/forex/fx_view.py index 445c2befd3a..01ba8b406d4 100644 --- a/gamestonk_terminal/forex/fx_view.py +++ b/gamestonk_terminal/forex/fx_view.py @@ -532,12 +532,43 @@ def calendar(instrument, other_args: List[str]): print(f"Actual: {actual}") if "previous" in response[i]: previous = response[i]["previous"] +<<<<<<< Updated upstream unit = response[i]["unit"] if unit != "Index": print(f"Previous: {previous}{unit}") else: print(f"Previous: {previous}") print("-" * 30) +======= + 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("") + +>>>>>>> Stashed changes except V20Error as e: print(e) @@ -611,3 +642,7 @@ def book_plot(df, instrument, book_type): if gtff.USE_ION: plt.ion() plt.show() + +def format_instrument(instrument, char): + if char not in instrument: + From 13f4e112fe1c0a62e8d827191da4fd39d4ba9aeb Mon Sep 17 00:00:00 2001 From: DJ Date: Fri, 23 Apr 2021 05:12:26 -0600 Subject: [PATCH 6/8] Added TA --- gamestonk_terminal/forex/README.md | 160 +++++ gamestonk_terminal/forex/fx_controller.py | 103 ++- gamestonk_terminal/forex/fx_view.py | 745 ++++++++++++++-------- 3 files changed, 682 insertions(+), 326 deletions(-) create mode 100644 gamestonk_terminal/forex/README.md diff --git a/gamestonk_terminal/forex/README.md b/gamestonk_terminal/forex/README.md new file mode 100644 index 00000000000..29cbc6be2de --- /dev/null +++ b/gamestonk_terminal/forex/README.md @@ -0,0 +1,160 @@ +# Forex + +This menu utilizes oanda to enable support for trading forex. + +* [summary](#summary) + * Display a summary of your account +* [calendar](#calendar) + * Get information about past or upcoming events which may impact the price +* [list](#list) + * List your order history +* [pending](#pending) + * Get information about pending orders +* [cancel](#cancel) + * Cancel a pending order by ID +* [positions](#positions) + * Get information about your positions +* [trades](#trades) + * See a list of open trades +* [closetrade](#closetrade) + * Close a trade by ID +* [load](#load) + * Specify an instrument to use +* [candles](#candles) + * Get a candlestick chart for the forex instrument +* [price](#price) + * Show the current price for the forex instrument +* [order](#order) + * Place a limit order +* [orderbook](#orderbook) + * Display the orderbook if Oanda provides one for the forex instrument +* [positionbook](#positionbook) + * Display the positionbook if Oanda provides one for the forex instrument + +## summary + ```text + usage: summary + ``` + + Displays current balance, net asset value, unrealized P/L, total P/L, count of open trades, how much margin is available, how much margin you've used, the margin call value, the percent of margin until closeout, and the total position value. + +## calendar + +```text +usage: calendar [-d DAYS] +``` + +Display information about past or upcoming events up to 30 days before or from todays date. The information includes the Title of the event, the time the event is scheduled, the rated impact, Oanda's forecast, the market forecast, the currency involved, the region from which the event originates, the actual value (for events that have finished), and the previous value. + +* -d : The number of days to search (7 is the default, 30 is the maximum that information is provided for). Use a positive value to search for upcoming events and a negative value to search for past events. + +## list +```text +usage: list [-s STATE] [-c COUNT] +``` + +Retrieves a list of orders you've made. It includes the ID, the instrument, the number of units, the price, the state of the order, and the type of order. + +* -s : The state of the orders to filter. You can specify: pending, filled, triggered, canceled or all. (default=all) +* -c : The number of orders to retrieve. (default = 20) + +## pending +```text +usage: pending +``` +A shortcut to list the pending orders for your account. It includes the ID, the instrument, the price, the number of units, the time the order was created and the time in force. + +## cancel +```text +usage: cancel [-i ORDERID] +``` + +Cancel a pending order by its order ID. + +* -i : Specify the order ID. You don't need to include this, simply include the order number. + +## positions +```text +usage: positions +``` + +Lists your open positions. The information includes the instrument, the current number of long units, the total profit/loss from long positions for the account, the unrealized profit/loss from long positions, the current number of short units, the total profit/loss from short positions for the account, and the unrealized profit/loss from short positions. + +## trades +```text +usage: trades +``` + +Lists the current open trades for your account. The information includes the ID, the instrument, the initial units opened for the trade, the current number of units open for the trade, it's entry price, and the unrealized profit/loss for the trade. + +## closetrade +```text +usage: closetrade [-i ORDERID] [-u UNITS] +``` + +Closes a trade by ID. Displays the order ID closed, the instrument, the number of units that were closed, the price, and the profit/loss + +* -i : Specify the order ID. You don't need to include this, simply include the order number. +* -u : Specify the number of units to close for that trade. (default=all) + +## load +```text +usage: load [-i INSTRUMENT] +``` + +Specify the instrument to use in the format EUR_USD + +* -i : Specify the instrument. You don't need to include this, simply include the instrument. + +## candles +```text +usage: candles [-g GRANULARITY] [-c CANDLECOUNT] [-aAbcCemorRstv] +``` + +Retrieve a candlestick chart for the currently loaded instrument. Not a live chart. + +* -g : Specify the granularity, which is the timeframe each candle represents. The available granularities are Seconds: S5, S10, S15, S30 Minutes: M1, M2, M4, M5, M10, M15, M30 Hours: H1, H2, H3, H4, H6, H8, H12 Day: D, Week: W Month: M. (default=D) +* -c : Specify the number of candles to retrieve. (default=180) + +* -a : Adds AD (Accumulation/Distribution Index) to the chart. AD is a cumulative indicator that uses volume and price to assess whether a stock is being accumulated or distributed. This provides insight into how strong a trend is. [Investopedia](#https://www.investopedia.com/terms/a/accumulationdistribution.asp) +* -A : Adds ADX (Average Directional Index) to the chart. ADX is used to determine the strength of a trend. [Investopedia](#https://www.investopedia.com/terms/a/adx.asp) +* -b : Adds Bollinger Bands to the chart. Bollinger bands are a set of trendlines plotted two standard deviations (positively and negatively) away from a simple moving average of a security's price. [Investopedia](#https://www.investopedia.com/terms/b/bollingerbands.asp) +* -C : Adds CCI (Commodity Channel Index) to the chart. CCI is a momentum-based oscillator used to help determine when an investment vehicle is reaching a condition of being overbought or oversold. [Investopedia](#https://www.investopedia.com/terms/c/commoditychannelindex.asp) +* -e : Adds EMA (Exponential Moving Average) to the chart. EMA is a moving average that places a greater weight and significance on the most recent data points. [Investopedia](#https://www.investopedia.com/terms/e/ema.asp) +* -m : Adds MACD (Moving Average Convergence Divergence) to the chart. MACD is a trend-following momentum indicator that shows the relationship between two moving averages of a security’s price. [Investopedia](#https://www.investopedia.com/terms/m/macd.asp) +* -o : Adds OBV (On Balance Volume) to the chart. OBV is a momentum indicator that uses volume flow to predict changes in price. [Investopedia](#https://www.investopedia.com/terms/o/onbalancevolume.asp) +* -r : Adds RSI (Relative Strength Index) to the chart. RSI is a momentum indicator that measures the magnitude of recent price changes to evaluate overbought or oversold conditions. [Investopedia](#https://www.investopedia.com/terms/r/rsi.asp) +* -R : Adds Aroon to the chart. Aroon is used to identify trend changes in the price of an asset, as well as the strength of that trend by measureing the time between highs and the time between lows over a time period. [Investopedia](#https://www.investopedia.com/terms/a/aroon.asp) +* -s : Adds SMA (Simple Moving Average) to the chart. SMA calculates the average of a selected range of prices, usually closing prices, by the number of periods in that range. [Investopedia](#https://www.investopedia.com/terms/s/sma.asp) +* -t : Adds Stochastic Oscillator the chart. Stochastic Oscillator is a momentum indicator comparing a particular closing price of a security to a range of its prices over a certain period of time. [Investopedia](#https://www.investopedia.com/terms/s/stochasticoscillator.asp) +* -v : Adds VWAP (Volume Weighted Average Price) to the chart. VWAP gives the average price a security has traded at throughout the day, based on both volume and price. [Investopedia](#https://www.investopedia.com/terms/v/vwap.asp) + +## price +```text +usage: price +``` + +Gets the current price for the currently loaded instrument + +## order +```text +usage: order -u UNITS -p PRICE +``` + +Places a limit order for the specified number of units at the specified price. Both arguments are required. + +* -u : Specify the number of units for the order. +* -p : Specify the price for the limit order. + +## orderbook +```text +usage: orderbook +``` +Plots the current orderbook for loaded instrument if one is provided by Oanda. Not a live chart. + +## positionbook +```text +usage: positionbook +``` +Plots the current positionbook for the loaded instrument if one is provided by Oanda. Not a live chart. + diff --git a/gamestonk_terminal/forex/fx_controller.py b/gamestonk_terminal/forex/fx_controller.py index b3acd9efc83..b1ab4dc7c68 100644 --- a/gamestonk_terminal/forex/fx_controller.py +++ b/gamestonk_terminal/forex/fx_controller.py @@ -6,10 +6,6 @@ from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.menu import session from gamestonk_terminal.forex import fx_view from gamestonk_terminal import config_terminal as cfg -from gamestonk_terminal.due_diligence import news_view -from gamestonk_terminal.behavioural_analysis import stocktwits_view -from gamestonk_terminal.exploratory_data_analysis import eda_api -import pandas as pd account = cfg.OANDA_ACCOUNT @@ -35,10 +31,6 @@ class ForexController: "candles", "pending", "calendar", - "news", - "bullbear", - "messages", - "edasummary", ] def __init__(self): @@ -51,31 +43,32 @@ class ForexController: self.instrument = None @staticmethod - def print_help(): + def print_help(self): """Print help""" print("\nForex Mode:") - print(" help Show this menu again") - print(" q Quit this menu and goes back to main menu") - print(" quit Quit to abandon program") + print(" help show this menu again") + print(" q quit this menu and goes back to main menu") + print(" quit quit to abandon program") print("") - print(" bullbear Call bullbear from stocktwits") - print(" cancel Cancel a pending order by ID -i order ID") - print(" candles Show candles") - print(" calendar Show calendar") - print(" closetrade Close a trade by id") - print(" list List order history") - print(" load Load an instrument to use") - print(" messages Get messages from stocktwits") - print(" news Get news") - print(" order Place limit order -u # of units -p price") - print(" orderbook Print orderbook") - print(" pending Get information on pending orders") - print(" positionbook Print positionbook") - print(" positions Get open positions") - print(" price Shows price for selected instrument") - print(" summary Shows account summary") - print(" trades List open trades") + 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): @@ -95,7 +88,7 @@ class ForexController: def call_help(self, _): """Process Help Command""" - self.print_help() + self.print_help(self) def call_q(self, _): """Process Q command - quit the menu""" @@ -105,24 +98,24 @@ class ForexController: """Process Quit command - exit the program""" return True - def call_price(self, _): + def call_price(self, other_args): """Process Price Command""" - fx_view.get_fx_price(account, self.instrument) + fx_view.get_fx_price(account, self.instrument, other_args) def call_load(self, other_args): self.instrument = fx_view.load(other_args) - def call_summary(self, _): + def call_summary(self, other_args): """Process account summary command""" - fx_view.get_account_summary(account) + fx_view.get_account_summary(account, other_args) - def call_orderbook(self, _): + def call_orderbook(self, other_args): """Process Oanda Order Book""" - fx_view.get_order_book(self.instrument) + fx_view.get_order_book(self.instrument, other_args) - def call_positionbook(self, _): + def call_positionbook(self, other_args): """Process Oanda Position Book""" - fx_view.get_position_book(self.instrument) + fx_view.get_position_book(self.instrument, other_args) def call_list(self, other_args: List[str]): """Process list orders command""" @@ -136,13 +129,13 @@ class ForexController: """Cancel pending order by ID""" fx_view.cancel_pending_order(account, other_args) - def call_positions(self, _): + def call_positions(self, other_args): """Get Open Positions""" - fx_view.get_open_positions(account) + fx_view.get_open_positions(account, other_args) - def call_pending(self, _): + def call_pending(self, other_args): """See up to 25 pending orders""" - fx_view.get_pending_orders(account) + fx_view.get_pending_orders(account, other_args) def call_closetrade(self, other_args: List[str]): """Close a trade by id""" @@ -151,36 +144,14 @@ class ForexController: def call_candles(self, other_args: List[str]): fx_view.show_candles(account, self.instrument, other_args) - def call_trades(self, _): + def call_trades(self, other_args): """List open trades""" - fx_view.get_open_trades(account) + fx_view.get_open_trades(account, other_args) def call_calendar(self, other_args: List[str]): """Call calendar""" fx_view.calendar(self.instrument, other_args) - def call_news(self, other_args: List[str]): - news_view.news(other_args, self.instrument) - - def call_bullbear(self, other_args: List[str]): - instrument = remove_underscore(self.instrument) - stocktwits_view.bullbear(other_args, instrument) - - def call_messages(self, other_args: List[str]): - instrument = remove_underscore(self.instrument) - stocktwits_view.messages(other_args, instrument) - - def call_edasummary(self, other_args: List[str]): - df = pd.read_csv(".candles.csv") - eda_api.summary(other_args, df) - - -def remove_underscore(instrument): - instrument_list = list(instrument) - instrument_list.pop(3) - adjusted_instrument = "".join(map(str, instrument_list)) - return adjusted_instrument - def menu(): """Oanda Menu""" diff --git a/gamestonk_terminal/forex/fx_view.py b/gamestonk_terminal/forex/fx_view.py index 01ba8b406d4..37c2f990292 100644 --- a/gamestonk_terminal/forex/fx_view.py +++ b/gamestonk_terminal/forex/fx_view.py @@ -26,7 +26,16 @@ client = API(access_token=cfg.OANDA_TOKEN, environment="live") account = cfg.OANDA_ACCOUNT -def get_fx_price(accountID, instrument): +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) @@ -35,74 +44,129 @@ def get_fx_price(accountID, instrument): ask = response["prices"][0]["asks"][0]["price"] print(instrument + " Bid: " + bid) print(instrument + " Ask: " + ask) + print("") + except V20Error as e: - print(e) + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") -def get_account_summary(accountID): +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) - balance = response["account"]["balance"] - margin_available = response["account"]["marginAvailable"] - margin_closeout = response["account"]["marginCloseoutNAV"] - margin_closeout_percent = response["account"]["marginCloseoutPercent"] - margin_closeout_position_value = response["account"][ - "marginCloseoutPositionValue" - ] - margin_used = response["account"]["marginUsed"] - net_asset_value = response["account"]["NAV"] - open_trade_count = response["account"]["openTradeCount"] - total_pl = response["account"]["pl"] - unrealized_pl = response["account"]["unrealizedPL"] - print(f"Balance: {balance}") - print(f"NAV: {net_asset_value}") - print(f"Unrealized P/L: {unrealized_pl}") - print(f"Total P/L: {total_pl}") - print(f"Open Trade Count: {open_trade_count}") - print(f"Margin Available: ${margin_available}") - print(f"Margin Used: ${margin_used}") - print(f"Margin Closeout {margin_closeout}") - print(f"Margin Closeout Percent: {margin_closeout_percent}") - print(f"Margin Closeout Position Value: {margin_closeout_position_value}") + 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: - print(e) + 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="listorders", + prog="list", description="List order history", ) parser.add_argument( - "-s", "--state", dest="state", action="store", default="ALL", required=False + "-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=50, required=False + "-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 + 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: - print(e) + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") -def get_order_book(instrument): +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 ) @@ -111,26 +175,42 @@ def get_order_book(instrument): 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: - print(e) + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") -def get_position_book(instrument): +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: - print(e) + 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="create_order", + prog="order", description="Create order", ) parser.add_argument( @@ -141,6 +221,8 @@ def create_order(accountID, instrument, other_args: List[str]): 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", @@ -149,6 +231,7 @@ def create_order(accountID, instrument, other_args: List[str]): action="store", type=float, required=True, + help="The price to set for the limit order. ", ) ns_parser = parse_known_args_and_warn(parser, other_args) @@ -168,16 +251,44 @@ def create_order(accountID, instrument, other_args: List[str]): try: request = orders.OrderCreate(accountID, data) response = client.request(request) - print(response) - except V20Error as e: + + if "orderFillTransaction" in response["orderCreateTransaction"]: + order_id = response["orderCreateTransaction"]["orderFillTransaction"]["id"] + order_instrument = response["orderCreateTransaction"][ + "orderFillTransaction" + ]["instrument"] + units = response["orderCreateTransaction"]["orderFillTransaction"]["units"] + price = response["orderCreateTransaction"]["orderFillTransaction"]["price"] + print("Order Filled:") + print(f"ID: {order_id}") + print(f"Instrument: {order_instrument}") + print(f"Units: {units}") + print(f"Price: {price}") + print("") + else: + order_creation_id = response["orderCreateTransaction"]["id"] + order_instrument = response["orderCreateTransaction"]["instrument"] + units = response["orderCreateTransaction"]["units"] + price = response["orderCreateTransaction"]["price"] + print("Order created:") + print(f"ID: {order_creation_id}") + print(f"Instrument: {order_instrument}") + print(f"Units: {units}") + print(f"Price: {price}") + print("") + + except Exception as e: print(e) + except V20Error as e: + d_error = eval(e.msg) + print(d_error["message"], "\n") def cancel_pending_order(accountID, other_args: List[str]): parser = argparse.ArgumentParser( add_help=False, - prog="cancelpendingorder", - description="Cancel Pending Order", + prog="cancel", + description="Cancel Pending Order ", ) parser.add_argument( "-i", @@ -185,44 +296,67 @@ def cancel_pending_order(accountID, other_args: List[str]): dest="orderID", action="store", type=str, - required=True, + 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) - client.request(request) + response = client.request(request) + order_id = response["orderCancelTransaction"]["orderID"] + print(f"Order {order_id} canceled.") + print("") except V20Error as e: - print(e) + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") -def get_pending_orders(accountID): +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) for i in range(len(response["orders"])): - try: - order_id = response["orders"][i]["id"] - instrument = response["orders"][i]["instrument"] - price = response["orders"][i]["price"] - units = response["orders"][i]["units"] - create_time = response["orders"][i]["createTime"] - time_in_force = response["orders"][i]["timeInForce"] - print(f"Order ID: {order_id}") - print(f"Instrument: {instrument}") - print(f"Price: {price}") - print(f"Units: {units}") - print(f"Time created: {create_time}") - print(f"Time in force: {time_in_force}") - print("-" * 30) - except IndexError: - break + order_id = response["orders"][i]["id"] + instrument = response["orders"][i]["instrument"] + price = response["orders"][i]["price"] + units = response["orders"][i]["units"] + create_time = response["orders"][i]["createTime"] + time_in_force = response["orders"][i]["timeInForce"] + print(f"Order ID: {order_id}") + print(f"Instrument: {instrument}") + print(f"Price: {price}") + print(f"Units: {units}") + print(f"Time created: {create_time}") + print(f"Time in force: {time_in_force}") + print("-" * 30) + print("") except V20Error as e: - print(e) + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") -def get_open_positions(accountID): +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) @@ -242,44 +376,65 @@ def get_open_positions(accountID): print(f"Total Short P/L: {short_pl}") print(f"Short Unrealized P/L: {short_upl}") print("-" * 30 + "\n") + print("") + except V20Error as e: - print(e) + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") -def get_open_trades(accountID): +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) - df = pd.DataFrame.from_dict(response["trades"]) - df = df[ - [ - "id", - "instrument", - "initialUnits", - "currentUnits", - "price", - "unrealizedPL", + 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", - } - ) - print(df) + df = df.rename( + columns={ + "id": "ID", + "instrument": "Instrument", + "initialUnits": "Initial Units", + "currentUnits": "Current Units", + "price": "Entry Price", + "unrealizedPL": "Unrealized P/L", + } + ) + print(df) + print("") + except KeyError: + print("No trades were found") + print("") except V20Error as e: - print(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="close_trade", - description="close a trade", + prog="closetrade", + description="Close a trade by id.", ) parser.add_argument( "-i", @@ -287,63 +442,104 @@ def close_trade(accountID, other_args: List[str]): dest="orderID", action="store", type=str, - required=True, + required=False, + help="The Trade ID to close. ", ) parser.add_argument( "-u", "--units", dest="units", action="store", - type=str, 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 = { - "units": ns_parser.units, - } + + 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) - print(response) + + order_id = response["orderCreateTransaction"]["tradeClose"]["tradeID"] + order_instrument = response["orderFillTransaction"]["instrument"] + units = response["orderCreateTransaction"]["units"] + price = response["orderFillTransaction"]["price"] + pl = response["orderFillTransaction"]["pl"] + print("Order closed:") + print(f"ID: {order_id}") + print(f"Instrument: {order_instrument}") + print(f"Units: {units}") + print(f"Price: {price}") + print(f"P/L: {pl}") + print("") + + except Exception as e: + print(e, "\n") except V20Error as e: - print(e) + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") def show_candles(accountID, instrument, other_args: List[str]): parser = argparse.ArgumentParser( add_help=False, - prog="show_candles", + 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( - "-g", - "--granularity", - dest="granularity", - action="store", - default="D", - ) - parser.add_argument("-a", "--ad", dest="ad", action="store_true") - parser.add_argument("-A", "--adx", dest="adx", action="store_true") - parser.add_argument("-b", "--bollinger-bands", dest="bbands", action="store_true") - parser.add_argument("-C", "--cci", dest="cci", action="store_true") - parser.add_argument("-e", "--ema", dest="ema", action="store_true") - parser.add_argument("-f", "--fwma", dest="fwma", action="store_true") - parser.add_argument("-m", "--macd", dest="macd", action="store_true") - parser.add_argument("-o", "--obv", dest="obv", action="store_true") - parser.add_argument("-r", "--rsi", dest="rsi", action="store_true") - parser.add_argument("-R", "--aroon", dest="aroon", action="store_true") - parser.add_argument("-s", "--sma", dest="sma", action="store_true") - parser.add_argument("-S", "--stoch", dest="stoch", action="store_true") - parser.add_argument("-v", "--vwap", dest="vwap", action="store_true") + parser.add_argument("-a", "--ad", dest="ad", action="store_true", + help="Adds ad (Accumulation/Distribution Indicator) to the chart") + parser.add_argument("-A", "--adx", dest="adx", action="store_true", + help="Adds adx (Average Directional Index) the 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("-m", "--macd", dest="macd", action="store_true", + help="Adds macd (Moving Average Convergence Divergence) 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("-R", "--aroon", dest="aroon", action="store_true", + help="Adds Aroon 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("-t", "--stoch", dest="stoch", action="store_true", + help="Adds stochastic oscillator 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: @@ -353,120 +549,128 @@ def show_candles(accountID, instrument, other_args: List[str]): parameters["granularity"] = ns_parser.granularity parameters["count"] = ns_parser.candlecount try: - request = instruments.InstrumentsCandles(instrument, params=parameters) - response = client.request(request) - process_candle_response(response) - oanda_fix_date(".temp_candles.csv") - df = pd.read_csv(".candles.csv", index_col=0) - df.index = pd.to_datetime(df.index) - df.columns = ["Open", "High", "Low", "Close", "Volume"] + instrument = format_instrument(instrument, "_") + df = get_candles_dataframe(accountID, instrument, parameters) - plots_to_add = [] - - if ns_parser.ad: - ad = ta.ad(df["High"], df["Low"], df["Close"], df["Volume"]) - ad_plot = mpf.make_addplot(ad, panel=3) - plots_to_add.append(ad_plot) - if ns_parser.adx: - adx = ta.adx(df["High"], df["Low"], df["Close"]) - adx_plot = mpf.make_addplot(adx, panel=3) - plots_to_add.append(adx_plot) - if ns_parser.aroon: - aroon = ta.aroon(df["High"], df["Low"]) - aroon_plot = mpf.make_addplot(aroon, panel=3) - plots_to_add.append(aroon_plot) - if ns_parser.cci: - cci = ta.cci(df["High"], df["Low"], df["Close"], length=20) - cci_plot = mpf.make_addplot(cci, panel=3) - plots_to_add.append(cci_plot) - 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) - if ns_parser.ema: - ema = ta.ema(df["Close"]) - ema_plot = mpf.make_addplot(ema, panel=0) - plots_to_add.append(ema_plot) - if ns_parser.fwma: - fwma = ta.fwma(df["Close"]) - fwma_plot = mpf.make_addplot(fwma, panel=0) - plots_to_add.append(fwma_plot) - if ns_parser.rsi: - rsi = ta.rsi(df["Close"], length=14) - rsi_plot = mpf.make_addplot(rsi, panel=2) - plots_to_add.append(rsi_plot) - if ns_parser.macd: - macd = ta.macd(df["Close"], fast=12, slow=26, signal=9) - macd_plot = mpf.make_addplot(macd, panel=3) - plots_to_add.append(macd_plot) - if ns_parser.obv: - obv = ta.obv(df["Close"], df["Volume"]) - obv_plot = mpf.make_addplot(obv, panel=3) - plots_to_add.append(obv_plot) - if ns_parser.sma: - sma = ta.sma(df["Close"]) - sma_plot = mpf.make_addplot(sma, panel=0) - plots_to_add.append(sma_plot) - if ns_parser.stoch: - stoch = ta.stoch(df["High"], df["Low"], df["Close"]) - stoch_plot = mpf.make_addplot(stoch, panel=0) - plots_to_add.append(stoch_plot) - 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) + plots_to_add, legends, subplots = add_plots(df, ns_parser) if gtff.USE_ION: plt.ion() - mpf.plot( + fig, ax = mpf.plot( df, type="candle", style="charles", volume=True, title=f"{instrument} {ns_parser.granularity}", + returnfig=True, addplot=plots_to_add, ) - except Exception as e: - print(e) - except NameError as e: + ax[0].set_title(f"{instrument} {ns_parser.granularity}") + print(legends) + ax[0].legend(legends) + for i in range(0, len(subplots), 2): + ax[subplots.pop(0)].legend(subplots.pop(0)) + print("") + except V20Error as e: + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") + except TypeError as e: print(e) -def process_candle_response(response): - with open(".temp_candles.csv", "w") as out: - for i in range(len(response["candles"])): - time = response["candles"][i]["time"] - volume = response["candles"][i]["volume"] - o = response["candles"][i]["mid"]["o"] - h = response["candles"][i]["mid"]["h"] - low = response["candles"][i]["mid"]["l"] - c = response["candles"][i]["mid"]["c"] - out.write( - str(time) - + "," - + str(o) - + "," - + str(h) - + "," - + str(low) - + "," - + str(c) - + "," - + str(volume) - + "\n" - ) +def add_plots(df, ns_parser): + panel_number = 2 + plots_to_add = [] + legends = [] + subplots = [] + 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) + subplots.extend([panel_number*2, ["AD"]]) + panel_number +=1 -def oanda_fix_date(file): - with open(file) as candle_file: - lines = candle_file.readlines() - with open(".candles.csv", "w") as out: - out.write("Datetime, Open, High, Low, Close, Volume\n") - for line in lines: - with open(".candles.csv", "a") as output: - output.write(line[:10] + " " + line[11:19] + line[30:]) + if ns_parser.adx: + adx = ta.adx(df["High"], df["Low"], df["Close"]) + adx_plot = mpf.make_addplot(adx, panel=panel_number) + plots_to_add.append(adx_plot) + subplots.extend([panel_number*2, ["ADX"]]) + panel_number +=1 + + if ns_parser.aroon: + aroon = ta.aroon(df["High"], df["Low"]) + aroon_plot = mpf.make_addplot(aroon, panel=panel_number) + plots_to_add.append(aroon_plot) + subplots.extend([panel_number*2, ["Aroon"]]) + 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) + subplots.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("20 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) + subplots.extend([panel_number*2, ["RSI"]]) + panel_number += 1 + + if ns_parser.macd: + macd = ta.macd(df["Close"]) + macd_plot = mpf.make_addplot(macd, panel=panel_number) + plots_to_add.append(macd_plot) + subplots.append(panel_number*2) + subplots.append(["Fast","Slow","Signal"]) + print(subplots) + 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) + subplots.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.stoch: + stoch = ta.stoch(df["High"], df["Low"], df["Close"]) + stoch_plot = mpf.make_addplot(stoch, panel=panel_number) + plots_to_add.append(stoch_plot) + subplots.extend([panel_number*2, ["Stoch"]]) + panel_number += 1 + + 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, subplots def calendar(instrument, other_args: List[str]): @@ -483,6 +687,8 @@ def calendar(instrument, other_args: List[str]): 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: @@ -492,54 +698,32 @@ def calendar(instrument, other_args: List[str]): try: request = labs.Calendar(params=parameters) response = client.request(request) + + l_data = [] for i in range(len(response)): - if "title" in response[i]: - title = response[i]["title"] - print(f"Title: {title}") - if "timestamp" in response[i]: - timestamp = response[i]["timestamp"] - time = datetime.fromtimestamp(timestamp) - print(f"Time: {time}") - if "impact" in response[i]: - impact = response[i]["impact"] - print(f"Impact: {impact}") if "forecast" in response[i]: forecast = response[i]["forecast"] - unit = response[i]["unit"] - if unit != "Index": - print(f"Forecast: {forecast}{unit}") - else: - print(f"Forecast: {forecast}") + if response[i]["unit"] != "Index": + forecast += response[i]["unit"] + else: + forecast = "" + if "market" in response[i]: market = response[i]["market"] - unit = response[i]["unit"] - if unit != "Index": - print(f"Market Forecast: {market}{unit}") - else: - print(f"Market Forecast: {market}") - if "currency" in response[i]: - currency = response[i]["currency"] - print(f"Currency: {currency}") - if "region" in response[i]: - region = response[i]["region"] - print(f"Region: {region}") + if response[i]["unit"] != "Index": + market += response[i]["unit"] + else: + market = "" + if "actual" in response[i]: actual = response[i]["actual"] - unit = response[i]["unit"] - if unit != "Index": - print(f"Actual: {actual}{unit}") - else: - print(f"Actual: {actual}") + if response[i]["unit"] != "Index": + actual += response[i]["unit"] + else: + actual = "" + if "previous" in response[i]: previous = response[i]["previous"] -<<<<<<< Updated upstream - unit = response[i]["unit"] - if unit != "Index": - print(f"Previous: {previous}{unit}") - else: - print(f"Previous: {previous}") - print("-" * 30) -======= if response[i]["unit"] != "Index": previous += response[i]["unit"] else: @@ -550,7 +734,6 @@ def calendar(instrument, other_args: List[str]): else: impact = "" - l_data.append( { "Title": response[i]["title"], @@ -568,16 +751,46 @@ def calendar(instrument, other_args: List[str]): print(pd.DataFrame(l_data).to_string(index=False)) print("") ->>>>>>> Stashed changes except V20Error as e: - print(e) + d_error = eval(e.msg) + print(d_error["message"], "\n") + + +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: + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") def load(other_args: List[str]): """Load a forex instrument to use""" parser = argparse.ArgumentParser( add_help=False, - prog="Forex", + prog="load", description="Forex using oanda", ) @@ -587,7 +800,7 @@ def load(other_args: List[str]): required=True, type=str, dest="instrument", - help="Instrument to use for function calls", + help="Forex pair to use. ", ) try: @@ -596,12 +809,15 @@ def load(other_args: List[str]): 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) + print(e, "\n") + return None def book_plot(df, instrument, book_type): @@ -643,6 +859,15 @@ def book_plot(df, instrument, book_type): plt.ion() plt.show() -def format_instrument(instrument, char): - if char not in instrument: +def format_instrument(instrument, char): + try: + if char not in instrument: + instrument_list = list(instrument) + instrument_list.pop(3) + instrument_list.insert(3, char) + instrument = "".join(map(str, instrument_list)) + return instrument + except TypeError: + print("Please load an instrument") + print("") From 1fc835d48c4760758174e1e6c4bb867c694e64fa Mon Sep 17 00:00:00 2001 From: DJ Date: Fri, 23 Apr 2021 14:20:58 -0600 Subject: [PATCH 7/8] Reddit/EDA integration and added TA --- .../due_diligence/reddit_view.py | 7 + gamestonk_terminal/forex/README.md | 6 +- gamestonk_terminal/forex/fx_controller.py | 57 +++++ gamestonk_terminal/forex/fx_view.py | 203 +++++++----------- 4 files changed, 146 insertions(+), 127 deletions(-) diff --git a/gamestonk_terminal/due_diligence/reddit_view.py b/gamestonk_terminal/due_diligence/reddit_view.py index 5c536add200..7fcb1d8af67 100644 --- a/gamestonk_terminal/due_diligence/reddit_view.py +++ b/gamestonk_terminal/due_diligence/reddit_view.py @@ -85,6 +85,10 @@ def due_diligence(other_args: List[str], ticker: str): "News", "Advice", "Chart", + "Charts and Setups", + "Fundamental Analysis", + "forex", + "Trade Idea", ] l_sub_reddits = [ "pennystocks", @@ -94,6 +98,9 @@ def due_diligence(other_args: List[str], ticker: str): "stocks", "investing", "wallstreetbets", + "forex", + "Daytrading", + "Forexstrategy", ] submissions = psaw_api.search_submissions( diff --git a/gamestonk_terminal/forex/README.md b/gamestonk_terminal/forex/README.md index 29cbc6be2de..b038fcc3535 100644 --- a/gamestonk_terminal/forex/README.md +++ b/gamestonk_terminal/forex/README.md @@ -108,7 +108,7 @@ Specify the instrument to use in the format EUR_USD ## candles ```text -usage: candles [-g GRANULARITY] [-c CANDLECOUNT] [-aAbcCemorRstv] +usage: candles [-g GRANULARITY] [-c CANDLECOUNT] [-abcCeorsv] ``` Retrieve a candlestick chart for the currently loaded instrument. Not a live chart. @@ -117,16 +117,12 @@ Retrieve a candlestick chart for the currently loaded instrument. Not a live cha * -c : Specify the number of candles to retrieve. (default=180) * -a : Adds AD (Accumulation/Distribution Index) to the chart. AD is a cumulative indicator that uses volume and price to assess whether a stock is being accumulated or distributed. This provides insight into how strong a trend is. [Investopedia](#https://www.investopedia.com/terms/a/accumulationdistribution.asp) -* -A : Adds ADX (Average Directional Index) to the chart. ADX is used to determine the strength of a trend. [Investopedia](#https://www.investopedia.com/terms/a/adx.asp) * -b : Adds Bollinger Bands to the chart. Bollinger bands are a set of trendlines plotted two standard deviations (positively and negatively) away from a simple moving average of a security's price. [Investopedia](#https://www.investopedia.com/terms/b/bollingerbands.asp) * -C : Adds CCI (Commodity Channel Index) to the chart. CCI is a momentum-based oscillator used to help determine when an investment vehicle is reaching a condition of being overbought or oversold. [Investopedia](#https://www.investopedia.com/terms/c/commoditychannelindex.asp) * -e : Adds EMA (Exponential Moving Average) to the chart. EMA is a moving average that places a greater weight and significance on the most recent data points. [Investopedia](#https://www.investopedia.com/terms/e/ema.asp) -* -m : Adds MACD (Moving Average Convergence Divergence) to the chart. MACD is a trend-following momentum indicator that shows the relationship between two moving averages of a security’s price. [Investopedia](#https://www.investopedia.com/terms/m/macd.asp) * -o : Adds OBV (On Balance Volume) to the chart. OBV is a momentum indicator that uses volume flow to predict changes in price. [Investopedia](#https://www.investopedia.com/terms/o/onbalancevolume.asp) * -r : Adds RSI (Relative Strength Index) to the chart. RSI is a momentum indicator that measures the magnitude of recent price changes to evaluate overbought or oversold conditions. [Investopedia](#https://www.investopedia.com/terms/r/rsi.asp) -* -R : Adds Aroon to the chart. Aroon is used to identify trend changes in the price of an asset, as well as the strength of that trend by measureing the time between highs and the time between lows over a time period. [Investopedia](#https://www.investopedia.com/terms/a/aroon.asp) * -s : Adds SMA (Simple Moving Average) to the chart. SMA calculates the average of a selected range of prices, usually closing prices, by the number of periods in that range. [Investopedia](#https://www.investopedia.com/terms/s/sma.asp) -* -t : Adds Stochastic Oscillator the chart. Stochastic Oscillator is a momentum indicator comparing a particular closing price of a security to a range of its prices over a certain period of time. [Investopedia](#https://www.investopedia.com/terms/s/stochasticoscillator.asp) * -v : Adds VWAP (Volume Weighted Average Price) to the chart. VWAP gives the average price a security has traded at throughout the day, based on both volume and price. [Investopedia](#https://www.investopedia.com/terms/v/vwap.asp) ## price diff --git a/gamestonk_terminal/forex/fx_controller.py b/gamestonk_terminal/forex/fx_controller.py index b1ab4dc7c68..b35698e9c17 100644 --- a/gamestonk_terminal/forex/fx_controller.py +++ b/gamestonk_terminal/forex/fx_controller.py @@ -6,6 +6,9 @@ from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.menu import session from gamestonk_terminal.forex import fx_view from gamestonk_terminal import config_terminal as cfg +from gamestonk_terminal.due_diligence import news_view, reddit_view +from gamestonk_terminal.behavioural_analysis import stocktwits_view +from gamestonk_terminal.exploratory_data_analysis import eda_api account = cfg.OANDA_ACCOUNT @@ -31,6 +34,15 @@ class ForexController: "candles", "pending", "calendar", + "news", + "bullbear", + "messages", + "edasummary", + "edarolling", + "edadecompose", + "edacusum", + "edahist", + "reddit", ] def __init__(self): @@ -69,6 +81,14 @@ class ForexController: 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(" bullbear estimate quick sentiment from last 30 messages on stocktwits") + print(" messages output up to the 30 last messages on the stocktwits board") + print(" reddit search reddit for posts about the loaded instrument") + print(" edasummary brief summary statistics using exploratory data analysis") + print(" edarolling rolling mean and std deviation") + print(" edadecompose decomposition in cyclic-trend, season, and residuals") + print(" edacusum detects abrupt changes using cumulative sum algorithm") print("") def switch(self, an_input: str): @@ -152,6 +172,43 @@ class ForexController: """Call calendar""" fx_view.calendar(self.instrument, other_args) + def call_news(self, other_args: List[str]): + """Call news [News API]""" + news_view.news(other_args, self.instrument) + + def call_bullbear(self, other_args): + """Call bullbear fom stocktwits""" + instrument = fx_view.format_instrument(self.instrument, " ") + stocktwits_view.bullbear(other_args, instrument) + + def call_messages(self, other_args): + """Call messages from stocktwits""" + instrument = fx_view.format_instrument(self.instrument, " ") + stocktwits_view.messages(other_args, instrument) + + def call_edasummary(self, other_args: List[str]): + df = fx_view.get_candles_dataframe(account, self.instrument, None) + eda_api.summary(other_args, df) + + def call_edarolling(self, other_args: List[str]): + df = fx_view.get_candles_dataframe(account, self.instrument, None) + df.columns = ["Open", "Low", "High", "5. adjusted close", "Volume"] + eda_api.rolling(other_args, self.instrument, df) + + def call_edadecompose(self, other_args: List[str]): + df = fx_view.get_candles_dataframe(account, self.instrument, None) + df.columns = ["Open", "Low", "High", "5. adjusted close", "Volume"] + eda_api.decompose(other_args, self.instrument, df) + + def call_edacusum(self, other_args: List[str]): + df = fx_view.get_candles_dataframe(account, self.instrument, None) + df.columns = ["Open", "Low", "High", "5. adjusted close", "Volume"] + eda_api.cusum(other_args, self.instrument, df) + + def call_reddit(self, other_args: List[str]): + instrument = fx_view.format_instrument(self.instrument, " ") + reddit_view.due_diligence(other_args, instrument) + def menu(): """Oanda Menu""" diff --git a/gamestonk_terminal/forex/fx_view.py b/gamestonk_terminal/forex/fx_view.py index 37c2f990292..64f202c8e04 100644 --- a/gamestonk_terminal/forex/fx_view.py +++ b/gamestonk_terminal/forex/fx_view.py @@ -229,7 +229,7 @@ def create_order(accountID, instrument, other_args: List[str]): "--price", dest="price", action="store", - type=float, + type=str, required=True, help="The price to set for the limit order. ", ) @@ -251,37 +251,25 @@ def create_order(accountID, instrument, other_args: List[str]): 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"], + } + ) - if "orderFillTransaction" in response["orderCreateTransaction"]: - order_id = response["orderCreateTransaction"]["orderFillTransaction"]["id"] - order_instrument = response["orderCreateTransaction"][ - "orderFillTransaction" - ]["instrument"] - units = response["orderCreateTransaction"]["orderFillTransaction"]["units"] - price = response["orderCreateTransaction"]["orderFillTransaction"]["price"] - print("Order Filled:") - print(f"ID: {order_id}") - print(f"Instrument: {order_instrument}") - print(f"Units: {units}") - print(f"Price: {price}") - print("") - else: - order_creation_id = response["orderCreateTransaction"]["id"] - order_instrument = response["orderCreateTransaction"]["instrument"] - units = response["orderCreateTransaction"]["units"] - price = response["orderCreateTransaction"]["price"] - print("Order created:") - print(f"ID: {order_creation_id}") - print(f"Instrument: {order_instrument}") - print(f"Units: {units}") - print(f"Price: {price}") - print("") + df = pd.DataFrame.from_dict(order_data) + print(df.to_string(index=False)) + print("") - except Exception as e: - print(e) except V20Error as e: d_error = eval(e.msg) - print(d_error["message"], "\n") + print(d_error["errorMessage"], "\n") + except Exception as e: + print(e) def cancel_pending_order(accountID, other_args: List[str]): @@ -328,20 +316,26 @@ def get_pending_orders(accountID, other_args): try: request = orders.OrdersPending(accountID) response = client.request(request) + pending_data = [] for i in range(len(response["orders"])): - order_id = response["orders"][i]["id"] - instrument = response["orders"][i]["instrument"] - price = response["orders"][i]["price"] - units = response["orders"][i]["units"] - create_time = response["orders"][i]["createTime"] - time_in_force = response["orders"][i]["timeInForce"] - print(f"Order ID: {order_id}") - print(f"Instrument: {instrument}") - print(f"Price: {price}") - print(f"Units: {units}") - print(f"Time created: {create_time}") - print(f"Time in force: {time_in_force}") - print("-" * 30) + 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]["units"], + "Time Created": response["orders"][i]["units"], + "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: d_error = eval(e.msg) @@ -360,22 +354,22 @@ def get_open_positions(accountID, other_args): try: request = positions.OpenPositions(accountID) response = client.request(request) + position_data = [] for i in range(len(response["positions"])): - instrument = response["positions"][i]["instrument"] - long_units = response["positions"][i]["long"]["units"] - long_pl = response["positions"][i]["long"]["pl"] - long_upl = response["positions"][i]["long"]["unrealizedPL"] - short_units = response["positions"][i]["short"]["units"] - short_pl = response["positions"][i]["short"]["pl"] - short_upl = response["positions"][i]["short"]["unrealizedPL"] - print(f"Instrument: {instrument}\n") - print(f"Long Units: {long_units}") - print(f"Total Long P/L: {long_pl}") - print(f"Long Unrealized P/L: {long_upl}\n") - print(f"Short Units: {short_units}") - print(f"Total Short P/L: {short_pl}") - print(f"Short Unrealized P/L: {short_upl}") - print("-" * 30 + "\n") + 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: @@ -418,7 +412,7 @@ def get_open_trades(accountID, other_args): "unrealizedPL": "Unrealized P/L", } ) - print(df) + print(df.to_string(index=False)) print("") except KeyError: print("No trades were found") @@ -468,24 +462,25 @@ def close_trade(accountID, other_args: List[str]): request = trades.TradeClose(accountID, ns_parser.orderID, data) response = client.request(request) - order_id = response["orderCreateTransaction"]["tradeClose"]["tradeID"] - order_instrument = response["orderFillTransaction"]["instrument"] - units = response["orderCreateTransaction"]["units"] - price = response["orderFillTransaction"]["price"] - pl = response["orderFillTransaction"]["pl"] - print("Order closed:") - print(f"ID: {order_id}") - print(f"Instrument: {order_instrument}") - print(f"Units: {units}") - print(f"Price: {price}") - print(f"P/L: {pl}") + 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 Exception as e: - print(e, "\n") except V20Error as e: 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]): @@ -518,26 +513,18 @@ def show_candles(accountID, instrument, other_args: List[str]): ) parser.add_argument("-a", "--ad", dest="ad", action="store_true", help="Adds ad (Accumulation/Distribution Indicator) to the chart") - parser.add_argument("-A", "--adx", dest="adx", action="store_true", - help="Adds adx (Average Directional Index) the 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("-m", "--macd", dest="macd", action="store_true", - help="Adds macd (Moving Average Convergence Divergence) 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("-R", "--aroon", dest="aroon", action="store_true", - help="Adds Aroon 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("-t", "--stoch", dest="stoch", action="store_true", - help="Adds stochastic oscillator to the chart") parser.add_argument("-v", "--vwap", dest="vwap", action="store_true", help="Adds vwap (Volume Weighted Average Price) to the chart") @@ -546,13 +533,13 @@ def show_candles(accountID, instrument, other_args: List[str]): return parameters = {} - parameters["granularity"] = ns_parser.granularity + 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, subplots = add_plots(df, ns_parser) + plots_to_add, legends, subplot_legends = add_plots(df, ns_parser) if gtff.USE_ION: plt.ion() @@ -567,10 +554,9 @@ def show_candles(accountID, instrument, other_args: List[str]): addplot=plots_to_add, ) ax[0].set_title(f"{instrument} {ns_parser.granularity}") - print(legends) ax[0].legend(legends) - for i in range(0, len(subplots), 2): - ax[subplots.pop(0)].legend(subplots.pop(0)) + for i in range(0, len(subplot_legends), 2): + ax[subplot_legends[i]].legend(subplot_legends[i+1]) print("") except V20Error as e: d_error = eval(e.msg) @@ -583,29 +569,15 @@ def add_plots(df, ns_parser): panel_number = 2 plots_to_add = [] legends = [] - subplots = [] + 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) - subplots.extend([panel_number*2, ["AD"]]) + subplot_legends.extend([panel_number*2, ["AD"]]) panel_number +=1 - if ns_parser.adx: - adx = ta.adx(df["High"], df["Low"], df["Close"]) - adx_plot = mpf.make_addplot(adx, panel=panel_number) - plots_to_add.append(adx_plot) - subplots.extend([panel_number*2, ["ADX"]]) - panel_number +=1 - - if ns_parser.aroon: - aroon = ta.aroon(df["High"], df["Low"]) - aroon_plot = mpf.make_addplot(aroon, panel=panel_number) - plots_to_add.append(aroon_plot) - subplots.extend([panel_number*2, ["Aroon"]]) - panel_number += 1 - if ns_parser.bbands: bbands = ta.bbands(df["Close"]) bbands = bbands.drop("BBB_5_2.0", axis=1) @@ -617,7 +589,7 @@ def add_plots(df, ns_parser): cci = ta.cci(df["High"], df["Low"], df["Close"]) cci_plot = mpf.make_addplot(cci, panel=panel_number) plots_to_add.append(cci_plot) - subplots.extend([panel_number*2, ["CCI"]]) + subplot_legends.extend([panel_number*2, ["CCI"]]) panel_number += 1 if ns_parser.ema: @@ -630,47 +602,31 @@ def add_plots(df, ns_parser): rsi = ta.rsi(df["Close"]) rsi_plot = mpf.make_addplot(rsi, panel=panel_number) plots_to_add.append(rsi_plot) - subplots.extend([panel_number*2, ["RSI"]]) - panel_number += 1 - - if ns_parser.macd: - macd = ta.macd(df["Close"]) - macd_plot = mpf.make_addplot(macd, panel=panel_number) - plots_to_add.append(macd_plot) - subplots.append(panel_number*2) - subplots.append(["Fast","Slow","Signal"]) - print(subplots) + 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) - subplots.extend([panel_number*2, ["OBV"]]) + subplot_legends.extend([panel_number*2, ["OBV"]]) panel_number += 1 if ns_parser.sma: - sma_length = [20,50] + 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.stoch: - stoch = ta.stoch(df["High"], df["Low"], df["Close"]) - stoch_plot = mpf.make_addplot(stoch, panel=panel_number) - plots_to_add.append(stoch_plot) - subplots.extend([panel_number*2, ["Stoch"]]) - panel_number += 1 - 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, subplots + return plots_to_add, legends, subplot_legends def calendar(instrument, other_args: List[str]): @@ -865,8 +821,11 @@ def format_instrument(instrument, char): if char not in instrument: instrument_list = list(instrument) instrument_list.pop(3) - instrument_list.insert(3, char) - instrument = "".join(map(str, instrument_list)) + 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") From 8cefa05a8f31296209762aab696db08de1d98ab3 Mon Sep 17 00:00:00 2001 From: DJ Date: Fri, 23 Apr 2021 14:44:25 -0600 Subject: [PATCH 8/8] ran linter --- gamestonk_terminal/forex/fx_controller.py | 24 +++- gamestonk_terminal/forex/fx_view.py | 134 +++++++++++++++------- 2 files changed, 108 insertions(+), 50 deletions(-) diff --git a/gamestonk_terminal/forex/fx_controller.py b/gamestonk_terminal/forex/fx_controller.py index b35698e9c17..390da986f50 100644 --- a/gamestonk_terminal/forex/fx_controller.py +++ b/gamestonk_terminal/forex/fx_controller.py @@ -82,13 +82,25 @@ class ForexController: print(" orderbook print orderbook") print(" positionbook print positionbook") print(" news print news [News API]") - print(" bullbear estimate quick sentiment from last 30 messages on stocktwits") - print(" messages output up to the 30 last messages on the stocktwits board") - print(" reddit search reddit for posts about the loaded instrument") - print(" edasummary brief summary statistics using exploratory data analysis") + print( + " bullbear estimate quick sentiment from last 30 messages on stocktwits" + ) + print( + " messages output up to the 30 last messages on the stocktwits board" + ) + print( + " reddit search reddit for posts about the loaded instrument" + ) + print( + " edasummary brief summary statistics using exploratory data analysis" + ) print(" edarolling rolling mean and std deviation") - print(" edadecompose decomposition in cyclic-trend, season, and residuals") - print(" edacusum detects abrupt changes using cumulative sum algorithm") + print( + " edadecompose decomposition in cyclic-trend, season, and residuals" + ) + print( + " edacusum detects abrupt changes using cumulative sum algorithm" + ) print("") def switch(self, an_input: str): diff --git a/gamestonk_terminal/forex/fx_view.py b/gamestonk_terminal/forex/fx_view.py index 64f202c8e04..4128e367cf8 100644 --- a/gamestonk_terminal/forex/fx_view.py +++ b/gamestonk_terminal/forex/fx_view.py @@ -324,11 +324,11 @@ def get_pending_orders(accountID, other_args): "Instrument": response["orders"][i]["instrument"], "Price": response["orders"][i]["price"], "Units": response["orders"][i]["units"], - "Time Created": response["orders"][i]["units"], - "Time Created": 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) @@ -357,15 +357,19 @@ def get_open_positions(accountID, other_args): 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"], - } + { + "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) @@ -464,14 +468,14 @@ def close_trade(accountID, other_args: List[str]): 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"], - } - ) + { + "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("") @@ -511,22 +515,62 @@ def show_candles(accountID, instrument, other_args: List[str]): 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") + 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: @@ -556,7 +600,7 @@ def show_candles(accountID, instrument, other_args: List[str]): ax[0].set_title(f"{instrument} {ns_parser.granularity}") ax[0].legend(legends) for i in range(0, len(subplot_legends), 2): - ax[subplot_legends[i]].legend(subplot_legends[i+1]) + ax[subplot_legends[i]].legend(subplot_legends[i + 1]) print("") except V20Error as e: d_error = eval(e.msg) @@ -575,8 +619,8 @@ def add_plots(df, ns_parser): 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 + subplot_legends.extend([panel_number * 2, ["AD"]]) + panel_number += 1 if ns_parser.bbands: bbands = ta.bbands(df["Close"]) @@ -589,7 +633,7 @@ def add_plots(df, ns_parser): 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"]]) + subplot_legends.extend([panel_number * 2, ["CCI"]]) panel_number += 1 if ns_parser.ema: @@ -602,14 +646,14 @@ def add_plots(df, ns_parser): 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"]]) + 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"]]) + subplot_legends.extend([panel_number * 2, ["OBV"]]) panel_number += 1 if ns_parser.sma: @@ -721,11 +765,13 @@ def get_candles_dataframe(accountID, instrument, parameters): try: request = instruments.InstrumentsCandles(instrument, params=parameters) response = client.request(request) - candles_data=[] + candles_data = [] for i in range(len(response["candles"])): candles_data.append( { - "Date": response["candles"][i]["time"][:10] + " " + response["candles"][i]["time"][11:19], + "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"]),