From 9897f9c581c0a5fddab1d32d6e6563c54100d959 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 18 Apr 2021 01:40:35 -0600 Subject: [PATCH 01/21] 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 02/21] 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 03/21] 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 04/21] 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 05/21] 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 06/21] 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 07/21] 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 08/21] 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"]), From 7f2ec4f782b8ccfaa139ce372bd8fe6094467f99 Mon Sep 17 00:00:00 2001 From: DJ Date: Fri, 23 Apr 2021 15:34:17 -0600 Subject: [PATCH 09/21] README modifications --- gamestonk_terminal/forex/README.md | 88 ++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/gamestonk_terminal/forex/README.md b/gamestonk_terminal/forex/README.md index b038fcc3535..129d2f8227c 100644 --- a/gamestonk_terminal/forex/README.md +++ b/gamestonk_terminal/forex/README.md @@ -30,6 +30,21 @@ This menu utilizes oanda to enable support for trading forex. * Display the orderbook if Oanda provides one for the forex instrument * [positionbook](#positionbook) * Display the positionbook if Oanda provides one for the forex instrument +* [news](#news) + * Latest news of the currency provided by [News API] +* [bullbear](#bullbear) + * Estimate quick sentiment from last 30 messages on board +* [messages](#messages) + * Output up to the 30 last messages on the board +* [reddit](#reddit) + * Gets due diligence from another user's post [Reddit] +* [edasummary](#edasummary) + * Brief summary statistics using exploratory data analysis +* [edarolling](#edarolling) + * Rolling mean and std deviation +* [edadecompose](#edadecompose) + * decomposition in cyclic-trend, season, and residuald +* [edacusum](#detects abrupt changes using cumulative sum algorithm) ## summary ```text @@ -154,3 +169,76 @@ usage: positionbook ``` Plots the current positionbook for the loaded instrument if one is provided by Oanda. Not a live chart. +## news + +```text +news [-n N_NUM] +``` + +Prints latest news about currency, including date, title and web link. [Source: News API] + +* -n : Number of latest news being printed. Default 10. + +## bullbear +``` +usage: bullbear [-t S_TICKER] +``` +Print bullbear sentiment based on last 30 messages on the board. Also prints the watchlist_count. [Source: Stocktwits] + * -t : ticker to gather sentiment from. + +## messages +``` +usage: messages [-t S_TICKER] [-l N_LIM] +``` +Print up to 30 of the last messages on the board. [Source: Stocktwits] + * -t : get board messages from this ticker. Default pre-loaded. + * -l : limit messages shown. Default 30. + +## reddit + +```text +usage: red [-l N_LIMIT] [-d N_DAYS] [-a] +``` + +Print top stock's due diligence from other users. [Source: Reddit] + +* -l : limit of posts to retrieve +* -d : number of prior days to look for +* -a : "search through all flairs (apart from Yolo and Meme). Default False (i.e. use flairs: DD, technical analysis, Catalyst, News, Advice, Chart, Charts and Setups, Fundamental Analysis, Forex, Trade Idea) + +## edasummary + +```text +usage: edasummary +``` +Summary statistics + +## edarolling + +```text +usage: edarolling [-w ROLLING_WINDOW] +``` +Rolling mean and std deviation + +* -w : rolling window. Default 100. + +## edadecompose + +```text +usage: edadecompose [-m] +``` +Decompose time series as: + - Additive Time Series = Level + CyclicTrend + Residual + Seasonality + - Multiplicative Time Series = Level * CyclicTrend * Residual * Seasonality + +* -m : multiplicative model. Default additive. + +## edacusum + +```text +usage: edacusum [-t THRESHOLD] [-d DRIFT] +``` +Cumulative sum algorithm (CUSUM) to detect abrupt changes in data + +* -t : threshold. Default (MAX-min)/40. +* -d : drift. Default (MAX-min)/80. From f7d09d2f4a70b8105b3915ff0177b064781ac318 Mon Sep 17 00:00:00 2001 From: DJ Date: Fri, 23 Apr 2021 15:38:29 -0600 Subject: [PATCH 10/21] resolve conflicts --- gamestonk_terminal/forex/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/gamestonk_terminal/forex/README.md b/gamestonk_terminal/forex/README.md index 02e905a3eb4..e87a3255801 100644 --- a/gamestonk_terminal/forex/README.md +++ b/gamestonk_terminal/forex/README.md @@ -243,4 +243,3 @@ Cumulative sum algorithm (CUSUM) to detect abrupt changes in data * -d : drift. Default (MAX-min)/80. ======= Plots the current positionbook for the loaded instrument if one is provided by Oanda. Not a live chart. ->>>>>>> 9b906277b4534b4f65d24b95fc019bd402b6d78e From 9b7cf669fe5b79666798b0e7c7953f61315d5fbe Mon Sep 17 00:00:00 2001 From: DJ Date: Fri, 23 Apr 2021 15:45:14 -0600 Subject: [PATCH 11/21] Fixed link in README --- gamestonk_terminal/forex/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gamestonk_terminal/forex/README.md b/gamestonk_terminal/forex/README.md index e87a3255801..0d4b3339b8f 100644 --- a/gamestonk_terminal/forex/README.md +++ b/gamestonk_terminal/forex/README.md @@ -44,7 +44,8 @@ This menu utilizes oanda to enable support for trading forex. * Rolling mean and std deviation * [edadecompose](#edadecompose) * decomposition in cyclic-trend, season, and residuald -* [edacusum](#detects abrupt changes using cumulative sum algorithm) +* [edacusum](#edacusum) + * detects abrupt changes using cumulative sum algorithm ## summary ```text From 51de77ed6d8086d7c3ea4a0b16fa12f7e535a8e5 Mon Sep 17 00:00:00 2001 From: DJ Date: Fri, 23 Apr 2021 15:48:55 -0600 Subject: [PATCH 12/21] README --- gamestonk_terminal/forex/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/gamestonk_terminal/forex/README.md b/gamestonk_terminal/forex/README.md index 0d4b3339b8f..8ee6d44f6b9 100644 --- a/gamestonk_terminal/forex/README.md +++ b/gamestonk_terminal/forex/README.md @@ -242,5 +242,3 @@ Cumulative sum algorithm (CUSUM) to detect abrupt changes in data * -t : threshold. Default (MAX-min)/40. * -d : drift. Default (MAX-min)/80. -======= -Plots the current positionbook for the loaded instrument if one is provided by Oanda. Not a live chart. From 97e7c62e982d59bd975eba833ccbdc1097ed71e9 Mon Sep 17 00:00:00 2001 From: DJ Date: Fri, 23 Apr 2021 16:01:25 -0600 Subject: [PATCH 13/21] Ran linter --- gamestonk_terminal/forex/fx_view.py | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/gamestonk_terminal/forex/fx_view.py b/gamestonk_terminal/forex/fx_view.py index c33f63f83a4..94a514ee3fb 100644 --- a/gamestonk_terminal/forex/fx_view.py +++ b/gamestonk_terminal/forex/fx_view.py @@ -51,17 +51,6 @@ def get_fx_price(accountID, instrument, other_args: List[str]): 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 - -< def get_account_summary(accountID, other_args: List[str]): parser = argparse.ArgumentParser( add_help=False, @@ -281,7 +270,7 @@ def create_order(accountID, instrument, other_args: List[str]): print(d_error["errorMessage"], "\n") except Exception as e: print(e) - + def cancel_pending_order(accountID, other_args: List[str]): parser = argparse.ArgumentParser( @@ -348,7 +337,7 @@ def get_pending_orders(accountID, other_args): else: print(df.to_string(index=False)) print("") - + except V20Error as e: d_error = eval(e.msg) print(d_error["errorMessage"], "\n") @@ -442,7 +431,7 @@ def get_open_trades(accountID, other_args): print(df.to_string(index=False)) print("") - + except KeyError: print("No trades were found") print("") @@ -512,7 +501,6 @@ def close_trade(accountID, other_args: List[str]): print(e, "\n") - def show_candles(accountID, instrument, other_args: List[str]): parser = argparse.ArgumentParser( add_help=False, @@ -623,12 +611,12 @@ def show_candles(accountID, instrument, other_args: List[str]): returnfig=True, addplot=plots_to_add, ) - + 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]) - + print("") except V20Error as e: d_error = eval(e.msg) @@ -637,7 +625,6 @@ def show_candles(accountID, instrument, other_args: List[str]): print(e) - def add_plots(df, ns_parser): panel_number = 2 plots_to_add = [] @@ -817,7 +804,6 @@ def get_candles_dataframe(accountID, instrument, parameters): print(d_error["errorMessage"], "\n") - def load(other_args: List[str]): """Load a forex instrument to use""" parser = argparse.ArgumentParser( @@ -906,4 +892,3 @@ def format_instrument(instrument, char): except TypeError: print("Please load an instrument") print("") - From 1a306cc1efacdad42fb2b4071f8ea7040c7bf17b Mon Sep 17 00:00:00 2001 From: DJ Date: Fri, 23 Apr 2021 16:04:20 -0600 Subject: [PATCH 14/21] Ran flake --- gamestonk_terminal/forex/fx_view.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/gamestonk_terminal/forex/fx_view.py b/gamestonk_terminal/forex/fx_view.py index 94a514ee3fb..fc4ceb82041 100644 --- a/gamestonk_terminal/forex/fx_view.py +++ b/gamestonk_terminal/forex/fx_view.py @@ -382,17 +382,6 @@ def get_open_positions(accountID, other_args): 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 - - def get_open_trades(accountID, other_args): parser = argparse.ArgumentParser( add_help=False, From ed4162c057049f4e6254e68c7125bef52057db7e Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 25 Apr 2021 05:54:16 -0600 Subject: [PATCH 15/21] infer and sentiment from twitter --- .../due_diligence/reddit_view.py | 1 - gamestonk_terminal/forex/README.md | 21 +++++++++++++++++++ gamestonk_terminal/forex/fx_controller.py | 12 +++++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/gamestonk_terminal/due_diligence/reddit_view.py b/gamestonk_terminal/due_diligence/reddit_view.py index 7fcb1d8af67..3348e8638eb 100644 --- a/gamestonk_terminal/due_diligence/reddit_view.py +++ b/gamestonk_terminal/due_diligence/reddit_view.py @@ -99,7 +99,6 @@ def due_diligence(other_args: List[str], ticker: str): "investing", "wallstreetbets", "forex", - "Daytrading", "Forexstrategy", ] diff --git a/gamestonk_terminal/forex/README.md b/gamestonk_terminal/forex/README.md index 8ee6d44f6b9..05a9b9e8856 100644 --- a/gamestonk_terminal/forex/README.md +++ b/gamestonk_terminal/forex/README.md @@ -38,6 +38,10 @@ This menu utilizes oanda to enable support for trading forex. * Output up to the 30 last messages on the board * [reddit](#reddit) * Gets due diligence from another user's post [Reddit] +* [infer](#infer) + * Infer about stock's sentiment from latest tweets +* [sentiment](#sentiment) + * In-depth sentiment prediction from tweets over time * [edasummary](#edasummary) * Brief summary statistics using exploratory data analysis * [edarolling](#edarolling) @@ -206,6 +210,23 @@ Print top stock's due diligence from other users. [Source: Reddit] * -d : number of prior days to look for * -a : "search through all flairs (apart from Yolo and Meme). Default False (i.e. use flairs: DD, technical analysis, Catalyst, News, Advice, Chart, Charts and Setups, Fundamental Analysis, Forex, Trade Idea) +## infer +``` +usage: infer [-n N_NUM] +``` +Print quick sentiment inference from last tweets that contain the ticker. This model splits the text into character-level tokens and uses the DistilBERT model to make predictions. DistilBERT is a distilled version of the powerful BERT transformer model. Not only time period of these, but also frequency. Inspired by https://towardsdatascience.com/sentiment-analysis-for-stock-price-prediction-in-python-bed40c65d178. [Source: Twitter] + +* -n : num of latest tweets to infer from. Default 100. + +## sentiment +``` +usage: sentiment [-n N_NUM] [-d N_DAYS_PAST] +``` +Plot in-depth sentiment extracted from tweets from last days that contain pre-defined ticker. This model splits the text into character-level tokens and uses the DistilBERT model to make predictions. DistilBERT is a distilled version of the powerful BERT transformer model. Note that a big num of tweets extracted per hour in conjunction with a high number of days in the past, will make the algorithm take a long period of time to estimate sentiment. Inspired by https://towardsdatascience.com/sentiment-analysis-for-stock-price-prediction-in-python-bed40c65d178. [Source: Twitter] + +* -n : num of tweets to extract per hour. Default 100. +* -d : num of days in the past to extract tweets. Default 7. + ## edasummary ```text diff --git a/gamestonk_terminal/forex/fx_controller.py b/gamestonk_terminal/forex/fx_controller.py index b844f2042d0..8dcbee82cef 100644 --- a/gamestonk_terminal/forex/fx_controller.py +++ b/gamestonk_terminal/forex/fx_controller.py @@ -7,7 +7,7 @@ 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.behavioural_analysis import stocktwits_view, twitter_view from gamestonk_terminal.exploratory_data_analysis import eda_api account = cfg.OANDA_ACCOUNT @@ -37,12 +37,14 @@ class ForexController: "news", "bullbear", "messages", + "reddit", + "infer", + "sentiment", "edasummary", "edarolling", "edadecompose", "edacusum", "edahist", - "reddit", ] def __init__(self): @@ -222,6 +224,12 @@ class ForexController: instrument = fx_view.format_instrument(self.instrument, " ") reddit_view.due_diligence(other_args, instrument) + def call_infer(self, other_args: List[str]): + twitter_view.inference(other_args, self.instrument) + + def call_sentiment(self, other_args: List[str]): + twitter_view.sentiment(other_args, self.instrument) + def menu(): """Oanda Menu""" From 27e9fe3db80091788cfa68ff85a16d50ffc32917 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 25 Apr 2021 07:40:54 -0600 Subject: [PATCH 16/21] requested changes --- gamestonk_terminal/forex/README.md | 8 +-- gamestonk_terminal/forex/fx_controller.py | 59 ++++++----------------- gamestonk_terminal/forex/fx_view.py | 2 +- 3 files changed, 20 insertions(+), 49 deletions(-) diff --git a/gamestonk_terminal/forex/README.md b/gamestonk_terminal/forex/README.md index 05a9b9e8856..62f7fa8b29e 100644 --- a/gamestonk_terminal/forex/README.md +++ b/gamestonk_terminal/forex/README.md @@ -137,11 +137,11 @@ 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) * -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) +* -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. (default=14) [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. (default=10) [Investopedia](#https://www.investopedia.com/terms/e/ema.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) -* -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) +* -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. (default=14) [Investopedia](#https://www.investopedia.com/terms/r/rsi.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. (defaults= 20, 50) [Investopedia](#https://www.investopedia.com/terms/s/sma.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 8dcbee82cef..db7b2ded370 100644 --- a/gamestonk_terminal/forex/fx_controller.py +++ b/gamestonk_terminal/forex/fx_controller.py @@ -7,8 +7,10 @@ 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, twitter_view -from gamestonk_terminal.exploratory_data_analysis import eda_api +from gamestonk_terminal.behavioural_analysis import stocktwits_view +from gamestonk_terminal.exploratory_data_analysis import eda_controller +import pandas as pd + account = cfg.OANDA_ACCOUNT @@ -38,13 +40,7 @@ class ForexController: "bullbear", "messages", "reddit", - "infer", - "sentiment", - "edasummary", - "edarolling", - "edadecompose", - "edacusum", - "edahist", + "eda", ] def __init__(self): @@ -94,16 +90,8 @@ class ForexController: " reddit search reddit for posts about the loaded instrument" ) print( - " edasummary brief summary statistics using exploratory data analysis" + " eda exploratory data analysis, e.g.: decompose, cusum, residuals 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): @@ -191,44 +179,27 @@ class ForexController: """Call news [News API]""" news_view.news(other_args, self.instrument) - def call_bullbear(self, other_args): + def call_bullbear(self, other_args: List[str]): """Call bullbear fom stocktwits""" instrument = fx_view.format_instrument(self.instrument, " ") stocktwits_view.bullbear(other_args, instrument) - def call_messages(self, other_args): + def call_messages(self, other_args: List[str]): """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 call_infer(self, other_args: List[str]): - twitter_view.inference(other_args, self.instrument) - - def call_sentiment(self, other_args: List[str]): - twitter_view.sentiment(other_args, self.instrument) + def call_eda(self, other_args): + df = fx_view.get_candles_dataframe(account, self.instrument, None) + df = df.rename(columns={"Close": "5. adjusted close"}) + instrument = self.instrument + s_start = pd.to_datetime(df.index.values[0]) + s_interval = "1440min" + eda_controller.menu(df, instrument, s_start, s_interval) def menu(): diff --git a/gamestonk_terminal/forex/fx_view.py b/gamestonk_terminal/forex/fx_view.py index fc4ceb82041..2f2e92f8c87 100644 --- a/gamestonk_terminal/forex/fx_view.py +++ b/gamestonk_terminal/forex/fx_view.py @@ -645,7 +645,7 @@ def add_plots(df, ns_parser): ema = ta.ema(df["Close"]) ema_plot = mpf.make_addplot(ema, panel=0) plots_to_add.append(ema_plot) - legends.append("20 EMA") + legends.append("10 EMA") if ns_parser.rsi: rsi = ta.rsi(df["Close"]) From 88fb002d2a0a0f9a6ef65f44db68b65b41523789 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 25 Apr 2021 08:03:04 -0600 Subject: [PATCH 17/21] README changes --- gamestonk_terminal/forex/README.md | 67 +++--------------------------- 1 file changed, 5 insertions(+), 62 deletions(-) diff --git a/gamestonk_terminal/forex/README.md b/gamestonk_terminal/forex/README.md index 05a9b9e8856..2cde4fb252a 100644 --- a/gamestonk_terminal/forex/README.md +++ b/gamestonk_terminal/forex/README.md @@ -38,18 +38,8 @@ This menu utilizes oanda to enable support for trading forex. * Output up to the 30 last messages on the board * [reddit](#reddit) * Gets due diligence from another user's post [Reddit] -* [infer](#infer) - * Infer about stock's sentiment from latest tweets -* [sentiment](#sentiment) - * In-depth sentiment prediction from tweets over time -* [edasummary](#edasummary) - * Brief summary statistics using exploratory data analysis -* [edarolling](#edarolling) - * Rolling mean and std deviation -* [edadecompose](#edadecompose) - * decomposition in cyclic-trend, season, and residuald -* [edacusum](#edacusum) - * detects abrupt changes using cumulative sum algorithm +* [eda](#eda) + * This menu aims to perform exploratory data analysis on a pre-loaded forex instrument. ## summary ```text @@ -210,56 +200,9 @@ Print top stock's due diligence from other users. [Source: Reddit] * -d : number of prior days to look for * -a : "search through all flairs (apart from Yolo and Meme). Default False (i.e. use flairs: DD, technical analysis, Catalyst, News, Advice, Chart, Charts and Setups, Fundamental Analysis, Forex, Trade Idea) -## infer -``` -usage: infer [-n N_NUM] -``` -Print quick sentiment inference from last tweets that contain the ticker. This model splits the text into character-level tokens and uses the DistilBERT model to make predictions. DistilBERT is a distilled version of the powerful BERT transformer model. Not only time period of these, but also frequency. Inspired by https://towardsdatascience.com/sentiment-analysis-for-stock-price-prediction-in-python-bed40c65d178. [Source: Twitter] - -* -n : num of latest tweets to infer from. Default 100. - -## sentiment -``` -usage: sentiment [-n N_NUM] [-d N_DAYS_PAST] -``` -Plot in-depth sentiment extracted from tweets from last days that contain pre-defined ticker. This model splits the text into character-level tokens and uses the DistilBERT model to make predictions. DistilBERT is a distilled version of the powerful BERT transformer model. Note that a big num of tweets extracted per hour in conjunction with a high number of days in the past, will make the algorithm take a long period of time to estimate sentiment. Inspired by https://towardsdatascience.com/sentiment-analysis-for-stock-price-prediction-in-python-bed40c65d178. [Source: Twitter] - -* -n : num of tweets to extract per hour. Default 100. -* -d : num of days in the past to extract tweets. Default 7. - -## edasummary - +## eda ```text -usage: edasummary +usage: eda ``` -Summary statistics -## edarolling - -```text -usage: edarolling [-w ROLLING_WINDOW] -``` -Rolling mean and std deviation - -* -w : rolling window. Default 100. - -## edadecompose - -```text -usage: edadecompose [-m] -``` -Decompose time series as: - - Additive Time Series = Level + CyclicTrend + Residual + Seasonality - - Multiplicative Time Series = Level * CyclicTrend * Residual * Seasonality - -* -m : multiplicative model. Default additive. - -## edacusum - -```text -usage: edacusum [-t THRESHOLD] [-d DRIFT] -``` -Cumulative sum algorithm (CUSUM) to detect abrupt changes in data - -* -t : threshold. Default (MAX-min)/40. -* -d : drift. Default (MAX-min)/80. +Access the exploratory data analysis menu. From 0c1b5c0557f2ced8ed1a165c4e94b536edca3312 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 25 Apr 2021 09:56:07 -0600 Subject: [PATCH 18/21] changes --- gamestonk_terminal/forex/README.md | 27 ++++++-------------- gamestonk_terminal/forex/fx_controller.py | 31 ++++++++--------------- gamestonk_terminal/forex/fx_view.py | 9 +++++-- 3 files changed, 26 insertions(+), 41 deletions(-) diff --git a/gamestonk_terminal/forex/README.md b/gamestonk_terminal/forex/README.md index 94a07621c50..5e127cee0a3 100644 --- a/gamestonk_terminal/forex/README.md +++ b/gamestonk_terminal/forex/README.md @@ -32,14 +32,12 @@ This menu utilizes oanda to enable support for trading forex. * Display the positionbook if Oanda provides one for the forex instrument * [news](#news) * Latest news of the currency provided by [News API] -* [bullbear](#bullbear) - * Estimate quick sentiment from last 30 messages on board -* [messages](#messages) - * Output up to the 30 last messages on the board * [reddit](#reddit) * Gets due diligence from another user's post [Reddit] * [eda](#eda) * This menu aims to perform exploratory data analysis on a pre-loaded forex instrument. +* [ba](#ba) + * This menu aims to extrapolate behavioural analysis regarding a pre-loaded forex instrument. ## summary ```text @@ -173,21 +171,6 @@ Prints latest news about currency, including date, title and web link. [Source: * -n : Number of latest news being printed. Default 10. -## bullbear -``` -usage: bullbear [-t S_TICKER] -``` -Print bullbear sentiment based on last 30 messages on the board. Also prints the watchlist_count. [Source: Stocktwits] - * -t : ticker to gather sentiment from. - -## messages -``` -usage: messages [-t S_TICKER] [-l N_LIM] -``` -Print up to 30 of the last messages on the board. [Source: Stocktwits] - * -t : get board messages from this ticker. Default pre-loaded. - * -l : limit messages shown. Default 30. - ## reddit ```text @@ -206,3 +189,9 @@ usage: eda ``` Access the exploratory data analysis menu. + +## ba +```text +usage: ba +``` +Access the behavioural analysis menu. diff --git a/gamestonk_terminal/forex/fx_controller.py b/gamestonk_terminal/forex/fx_controller.py index db7b2ded370..3420f97f4a7 100644 --- a/gamestonk_terminal/forex/fx_controller.py +++ b/gamestonk_terminal/forex/fx_controller.py @@ -7,7 +7,7 @@ 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.behavioural_analysis import stocktwits_view, ba_controller from gamestonk_terminal.exploratory_data_analysis import eda_controller import pandas as pd @@ -37,10 +37,9 @@ class ForexController: "pending", "calendar", "news", - "bullbear", - "messages", "reddit", "eda", + "ba", ] def __init__(self): @@ -80,17 +79,14 @@ 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( - " eda exploratory data analysis, e.g.: decompose, cusum, residuals analysis" + " eda > exploratory data analysis, e.g.: decompose, cusum, residuals analysis" + ) + print( + " ba > behavioural analysis, from: reddit, stocktwits, twitter, google" ) print("") @@ -179,16 +175,6 @@ class ForexController: """Call news [News API]""" news_view.news(other_args, self.instrument) - def call_bullbear(self, other_args: List[str]): - """Call bullbear fom stocktwits""" - instrument = fx_view.format_instrument(self.instrument, " ") - stocktwits_view.bullbear(other_args, instrument) - - def call_messages(self, other_args: List[str]): - """Call messages from stocktwits""" - instrument = fx_view.format_instrument(self.instrument, " ") - stocktwits_view.messages(other_args, instrument) - def call_reddit(self, other_args: List[str]): instrument = fx_view.format_instrument(self.instrument, " ") reddit_view.due_diligence(other_args, instrument) @@ -201,6 +187,11 @@ class ForexController: s_interval = "1440min" eda_controller.menu(df, instrument, s_start, s_interval) + def call_ba(self, other_args: List[str]): + instrument = fx_view.format_instrument(self.instrument, " ") + df = fx_view.get_candles_dataframe(account, self.instrument, None) + s_start = pd.to_datetime(df.index.values[0]) + ba_controller.menu(instrument, s_start) def menu(): """Oanda Menu""" diff --git a/gamestonk_terminal/forex/fx_view.py b/gamestonk_terminal/forex/fx_view.py index 2f2e92f8c87..6a61abf8c5d 100644 --- a/gamestonk_terminal/forex/fx_view.py +++ b/gamestonk_terminal/forex/fx_view.py @@ -12,7 +12,7 @@ from oandapyV20.exceptions import V20Error from gamestonk_terminal import config_terminal as cfg 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 +from gamestonk_terminal.helper_funcs import parse_known_args_and_warn, plot_autoscale, check_non_negative import pandas as pd import pandas_ta as ta import mplfinance as mpf @@ -229,7 +229,7 @@ def create_order(accountID, instrument, other_args: List[str]): "--price", dest="price", action="store", - type=str, + type=float, required=True, help="The price to set for the limit order. ", ) @@ -237,6 +237,11 @@ def create_order(accountID, instrument, other_args: List[str]): ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return + if "JPY" or "THB" or "HUF" in instrument: + ns_parser.price = round(ns_parser.price, 3) + else: + ns_parser.price = round(ns_parser.price, 5) + check_non_negative(ns_parser.price) data = { "order": { From f01ea83dc45896431e32ca7db0b9f8a7657d59a1 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 25 Apr 2021 09:58:03 -0600 Subject: [PATCH 19/21] Run linter --- gamestonk_terminal/forex/fx_controller.py | 1 + gamestonk_terminal/forex/fx_view.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gamestonk_terminal/forex/fx_controller.py b/gamestonk_terminal/forex/fx_controller.py index 3420f97f4a7..791fb4f52f1 100644 --- a/gamestonk_terminal/forex/fx_controller.py +++ b/gamestonk_terminal/forex/fx_controller.py @@ -193,6 +193,7 @@ class ForexController: s_start = pd.to_datetime(df.index.values[0]) ba_controller.menu(instrument, s_start) + def menu(): """Oanda Menu""" fx_controller = ForexController() diff --git a/gamestonk_terminal/forex/fx_view.py b/gamestonk_terminal/forex/fx_view.py index 6a61abf8c5d..0ac34599671 100644 --- a/gamestonk_terminal/forex/fx_view.py +++ b/gamestonk_terminal/forex/fx_view.py @@ -12,7 +12,11 @@ from oandapyV20.exceptions import V20Error from gamestonk_terminal import config_terminal as cfg 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, check_non_negative +from gamestonk_terminal.helper_funcs import ( + parse_known_args_and_warn, + plot_autoscale, + check_non_negative, +) import pandas as pd import pandas_ta as ta import mplfinance as mpf From 40dd1a2a58150dde55268f7af967be16522690f0 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 25 Apr 2021 10:00:29 -0600 Subject: [PATCH 20/21] run flake --- gamestonk_terminal/forex/fx_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gamestonk_terminal/forex/fx_controller.py b/gamestonk_terminal/forex/fx_controller.py index 791fb4f52f1..2a01965ce03 100644 --- a/gamestonk_terminal/forex/fx_controller.py +++ b/gamestonk_terminal/forex/fx_controller.py @@ -7,7 +7,7 @@ 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, ba_controller +from gamestonk_terminal.behavioural_analysis import, ba_controller from gamestonk_terminal.exploratory_data_analysis import eda_controller import pandas as pd From 48b5a8958b0d37841ec653409f2897d60b932132 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 25 Apr 2021 10:01:32 -0600 Subject: [PATCH 21/21] run flake --- gamestonk_terminal/forex/fx_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gamestonk_terminal/forex/fx_controller.py b/gamestonk_terminal/forex/fx_controller.py index 2a01965ce03..b796f83ff25 100644 --- a/gamestonk_terminal/forex/fx_controller.py +++ b/gamestonk_terminal/forex/fx_controller.py @@ -7,7 +7,7 @@ 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, ba_controller +from gamestonk_terminal.behavioural_analysis import ba_controller from gamestonk_terminal.exploratory_data_analysis import eda_controller import pandas as pd