diff --git a/README.md b/README.md index d3d2f8966fb..440bcf03b46 100644 --- a/README.md +++ b/README.md @@ -258,13 +258,11 @@ These are the ones where a key is necessary: * FRED: https://fred.stlouisfed.org/docs/api/api_key.html * News API: https://newsapi.org * Tradier: https://developer.tradier.com/getting_started + * Oanda API: https://developer.oanda.com When these are obtained, don't forget to update [config_terminal.py](/gamestonk_terminal/config_terminal.py). -Alternatively, you can also set them to the following environment variables: GT_API_KEY_ALPHAVANTAGE, -GT_API_KEY_FINANCIALMODELINGPREP, GT_API_KEY_QUANDL, GT_API_REDDIT_CLIENT_ID, GT_API_REDDIT_CLIENT_SECRET, GT_API_REDDIT_USERNAME, -GT_API_REDDIT_USER_AGENT, GT_API_REDDIT_PASSWORD, GT_API_TWITTER_KEY, GT_API_TWITTER_SECRET_KEY, -GT_API_TWITTER_BEARER_TOKEN, GT_API_POLYGON_KEY, GT_FRED_API_KEY, GT_API_NEWS_TOKEN, GT_TRADIER_TOKEN . +Alternatively, you can also set them to the following environment variables: GT_API_KEY_ALPHAVANTAGE, GT_API_KEY_FINANCIALMODELINGPREP, GT_API_KEY_QUANDL, GT_API_REDDIT_CLIENT_ID, GT_API_REDDIT_CLIENT_SECRET, GT_API_REDDIT_USERNAME, GT_API_REDDIT_USER_AGENT, GT_API_REDDIT_PASSWORD, GT_API_TWITTER_KEY, GT_API_TWITTER_SECRET_KEY, GT_API_TWITTER_BEARER_TOKEN, GT_API_POLYGON_KEY, GT_FRED_API_KEY, GT_API_NEWS_TOKEN, GT_OANDA_TOKEN, GT_OANDA_ACCOUNT, GT_TRADIER_TOKEN. Example: ``` diff --git a/gamestonk_terminal/config_terminal.py b/gamestonk_terminal/config_terminal.py index bed32fd3644..556b088f77c 100644 --- a/gamestonk_terminal/config_terminal.py +++ b/gamestonk_terminal/config_terminal.py @@ -39,6 +39,10 @@ API_NEWS_TOKEN = os.getenv("GT_API_NEWS_TOKEN") or "REPLACE_ME" RH_USERNAME = os.getenv("GT_RH_USERNAME") or "REPLACE_ME" RH_PASSWORD = os.getenv("GT_RH_PASSWORD") or "REPLACE_ME" +# https://developer.oanda.com +OANDA_ACCOUNT = os.getenv("GT_OANDA_ACCOUNT") or "REPLACE ME" +OANDA_TOKEN = os.getenv("GT_OANDA_TOKEN") or "REPLACE ME" + # https://tradier.com/products/market-data-api TRADIER_TOKEN = os.getenv("GT_TRADIER_TOKEN") or "REPLACE_ME" diff --git a/gamestonk_terminal/forex/README.md b/gamestonk_terminal/forex/README.md new file mode 100644 index 00000000000..5c27d28a4fd --- /dev/null +++ b/gamestonk_terminal/forex/README.md @@ -0,0 +1,146 @@ +# 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] +``` + +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) + +## 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/__init.py__ b/gamestonk_terminal/forex/__init.py__ new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gamestonk_terminal/forex/fx_controller.py b/gamestonk_terminal/forex/fx_controller.py new file mode 100644 index 00000000000..b1ab4dc7c68 --- /dev/null +++ b/gamestonk_terminal/forex/fx_controller.py @@ -0,0 +1,181 @@ +import argparse +from typing import List +from prompt_toolkit.completion import NestedCompleter +from gamestonk_terminal.helper_funcs import get_flair +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 + +account = cfg.OANDA_ACCOUNT + + +class ForexController: + """Oanda Controller class""" + + CHOICES = [ + "help", + "q", + "quit", + "price", + "summary", + "list", + "orderbook", + "positionbook", + "order", + "load", + "cancel", + "positions", + "closetrade", + "trades", + "candles", + "pending", + "calendar", + ] + + def __init__(self): + """Construct Data""" + self.fx_parser = argparse.ArgumentParser(add_help=False, prog="fx") + self.fx_parser.add_argument( + "cmd", + choices=self.CHOICES, + ) + self.instrument = None + + @staticmethod + 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("") + print(" summary shows account summary") + print(" calendar show calendar") + print(" list list order history") + print(" pending get information on pending orders") + print(" cancel cancel a pending order by ID -i order ID") + print(" positions get open positions") + print(" trades list open trades") + print(" closetrade close a trade by id") + print("") + print(f"Loaded instrument: {self.instrument if self.instrument else ''}") + print("") + print(" load load an instrument to use") + if self.instrument: + print(" candles show candles") + print(" price shows price for selected instrument") + print(" order place limit order -u # of units -p price") + print(" orderbook print orderbook") + print(" positionbook print positionbook") + print("") + + def switch(self, an_input: str): + """Process and dispatch input + Returns + ______ + True, False, or None + False - quit the menu + True - quit the program + None - continue in the menu + """ + (known_args, other_args) = self.fx_parser.parse_known_args(an_input.split()) + + return getattr( + self, "call_" + known_args.cmd, lambda: "command not recognized!" + )(other_args) + + def call_help(self, _): + """Process Help Command""" + self.print_help(self) + + def call_q(self, _): + """Process Q command - quit the menu""" + return False + + def call_quit(self, _): + """Process Quit command - exit the program""" + return True + + def call_price(self, other_args): + """Process Price Command""" + 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, other_args): + """Process account summary command""" + fx_view.get_account_summary(account, other_args) + + def call_orderbook(self, other_args): + """Process Oanda Order Book""" + fx_view.get_order_book(self.instrument, other_args) + + def call_positionbook(self, other_args): + """Process Oanda Position Book""" + fx_view.get_position_book(self.instrument, other_args) + + def call_list(self, other_args: List[str]): + """Process list orders command""" + fx_view.list_orders(account, other_args) + + def call_order(self, other_args: List[str]): + """Place limit order""" + fx_view.create_order(account, self.instrument, other_args) + + def call_cancel(self, other_args: List[str]): + """Cancel pending order by ID""" + fx_view.cancel_pending_order(account, other_args) + + def call_positions(self, other_args): + """Get Open Positions""" + fx_view.get_open_positions(account, other_args) + + def call_pending(self, other_args): + """See up to 25 pending orders""" + fx_view.get_pending_orders(account, other_args) + + def call_closetrade(self, other_args: List[str]): + """Close a trade by id""" + fx_view.close_trade(account, other_args) + + def call_candles(self, other_args: List[str]): + fx_view.show_candles(account, self.instrument, other_args) + + def call_trades(self, other_args): + """List open trades""" + 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 menu(): + """Oanda Menu""" + fx_controller = ForexController() + fx_controller.call_help(None) + while True: + if session and gtff.USE_PROMPT_TOOLKIT: + completer = NestedCompleter.from_nested_dict( + {c: None for c in fx_controller.CHOICES} + ) + + an_input = session.prompt( + f"{get_flair()} (fx)> ", + completer=completer, + ) + else: + an_input = input(f"{get_flair()} (fx)> ") + + try: + process_input = fx_controller.switch(an_input) + + if process_input is not None: + return process_input + + except SystemExit: + print("The command selected doesn't exit\n") + continue diff --git a/gamestonk_terminal/forex/fx_view.py b/gamestonk_terminal/forex/fx_view.py new file mode 100644 index 00000000000..cc4f1c59842 --- /dev/null +++ b/gamestonk_terminal/forex/fx_view.py @@ -0,0 +1,748 @@ +import argparse +from oandapyV20 import API +from typing import List +import oandapyV20.endpoints.pricing as pricing +import oandapyV20.endpoints.accounts as accounts +import oandapyV20.endpoints.orders as orders +import oandapyV20.endpoints.instruments as instruments +import oandapyV20.endpoints.positions as positions +import oandapyV20.endpoints.trades as trades +import oandapyV20.endpoints.forexlabs as labs +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 +import pandas as pd +import mplfinance as mpf +import matplotlib.pyplot as plt +import matplotlib.ticker as mticker +import seaborn as sns +from datetime import datetime + + +client = API(access_token=cfg.OANDA_TOKEN, environment="live") +account = cfg.OANDA_ACCOUNT + + +def get_fx_price(accountID, instrument, other_args: List[str]): + parser = argparse.ArgumentParser( + add_help=False, + prog="price", + description="Get price for selected instrument.", + ) + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + try: + parameters = {"instruments": instrument} + request = pricing.PricingInfo(accountID=accountID, params=parameters) + response = client.request(request) + bid = response["prices"][0]["bids"][0]["price"] + ask = response["prices"][0]["asks"][0]["price"] + print(instrument + " Bid: " + bid) + print(instrument + " Ask: " + ask) + print("") + + except V20Error as e: + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") + + +def get_account_summary(accountID, other_args: List[str]): + parser = argparse.ArgumentParser( + add_help=False, + prog="summary", + description="Print some information about your account.", + ) + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + try: + request = accounts.AccountSummary(accountID=accountID) + response = client.request(request) + + df_summary = pd.DataFrame( + [ + {"Type": "Balance", "Value": response["account"]["balance"]}, + {"Type": "NAV", "Value": response["account"]["NAV"]}, + { + "Type": "Unrealized P/L", + "Value": response["account"]["unrealizedPL"], + }, + {"Type": "Total P/L", "Value": response["account"]["pl"]}, + { + "Type": "Open Trade Count", + "Value": response["account"]["openTradeCount"], + }, + { + "Type": "Margin Available", + "Value": response["account"]["marginAvailable"], + }, + {"Type": "Margin Used", "Value": response["account"]["marginUsed"]}, + { + "Type": "Margin Closeout", + "Value": response["account"]["marginCloseoutNAV"], + }, + { + "Type": "Margin Closeout Percent", + "Value": response["account"]["marginCloseoutPercent"], + }, + { + "Type": "Margin Closeout Position Value", + "Value": response["account"]["marginCloseoutPositionValue"], + }, + ] + ) + + print(df_summary.to_string(index=False, header=False)) + + print("") + except V20Error as e: + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") + + +def list_orders(accountID, other_args: List[str]): + parser = argparse.ArgumentParser( + add_help=False, + prog="list", + description="List order history", + ) + parser.add_argument( + "-s", + "--state", + dest="state", + action="store", + default="ALL", + required=False, + help="Select state for order list. ", + ) + parser.add_argument( + "-c", + "--count", + dest="count", + action="store", + default=20, + required=False, + help="the number of orders to retrieve ", + ) + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + parameters = {} + parameters["state"] = ns_parser.state.upper() + parameters["count"] = ns_parser.count + + try: + request = orders.OrderList(accountID, parameters) + response = client.request(request) + + df = pd.DataFrame.from_dict(response["orders"]) + df = df[["id", "instrument", "units", "price", "state", "type"]] + print(df) + print("") + + except KeyError: + print("No orders were found\n") + except V20Error as e: + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") + + +def get_order_book(instrument, other_args): + parser = argparse.ArgumentParser( + add_help=False, + prog="orderbook", + description="Plot the orderbook for the instrument if Oanda provides one.", + ) + + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + parameters = {"bucketWidth": "1"} + try: + instrument = format_instrument(instrument, "_") + request = instruments.InstrumentsOrderBook( + instrument=instrument, params=parameters + ) + response = client.request(request) + df = pd.DataFrame.from_dict(response["orderBook"]["buckets"]) + pd.set_option("display.max_rows", None) + df = df.take(range(527, 727, 1)) + book_plot(df, instrument, "Order Book") + print("") + + except V20Error as e: + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") + + +def get_position_book(instrument, other_args): + parser = argparse.ArgumentParser( + add_help=False, + prog="positionbook", + description="Plot the positionbook for the instrument if Oanda provides one.", + ) + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + try: + instrument = format_instrument(instrument, "_") + request = instruments.InstrumentsPositionBook(instrument=instrument) + response = client.request(request) + df = pd.DataFrame.from_dict(response["positionBook"]["buckets"]) + pd.set_option("display.max_rows", None) + df = df.take(range(219, 415, 1)) + book_plot(df, instrument, "Position Book") + print("") + + except V20Error as e: + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") + + +def create_order(accountID, instrument, other_args: List[str]): + parser = argparse.ArgumentParser( + add_help=False, + prog="order", + description="Create order", + ) + parser.add_argument( + "-u", + "--unit", + dest="units", + action="store", + type=int, + default=0, + required=True, + help="The number of units to place in the order request. Positive for " + + "a long position and negative for a short position ", + ) + parser.add_argument( + "-p", + "--price", + dest="price", + action="store", + type=float, + required=True, + help="The price to set for the limit order. ", + ) + + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + data = { + "order": { + "price": ns_parser.price, + "instrument": instrument, + "units": ns_parser.units, + "type": "LIMIT", + "timeInForce": "GTC", + "positionFill": "DEFAULT", + } + } + try: + request = orders.OrderCreate(accountID, data) + response = client.request(request) + + 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="cancel", + description="Cancel Pending Order ", + ) + parser.add_argument( + "-i", + "--id", + dest="orderID", + action="store", + type=str, + help="The pending order ID to cancel ", + ) + if other_args: + if "-" not in other_args[0]: + other_args.insert(0, "-i") + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + try: + request = orders.OrderCancel(accountID, ns_parser.orderID) + response = client.request(request) + order_id = response["orderCancelTransaction"]["orderID"] + print(f"Order {order_id} canceled.") + print("") + except V20Error as e: + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") + + +def get_pending_orders(accountID, other_args): + parser = argparse.ArgumentParser( + add_help=False, + prog="pending", + description="Gets information about pending orders.", + ) + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + try: + request = orders.OrdersPending(accountID) + response = client.request(request) + 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) + print("") + except V20Error as e: + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") + + +def get_open_positions(accountID, other_args): + parser = argparse.ArgumentParser( + add_help=False, + prog="positions", + description="Gets information about open positions.", + ) + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + try: + request = positions.OpenPositions(accountID) + response = client.request(request) + 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") + print("") + + except V20Error as e: + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") + + +def get_open_trades(accountID, other_args): + parser = argparse.ArgumentParser( + add_help=False, + prog="trades", + description="Gets information about open trades.", + ) + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + try: + request = trades.OpenTrades(accountID) + response = client.request(request) + try: + df = pd.DataFrame.from_dict(response["trades"]) + df = df[ + [ + "id", + "instrument", + "initialUnits", + "currentUnits", + "price", + "unrealizedPL", + ] + ] + df = df.rename( + columns={ + "id": "ID", + "instrument": "Instrument", + "initialUnits": "Initial Units", + "currentUnits": "Current Units", + "price": "Entry Price", + "unrealizedPL": "Unrealized P/L", + } + ) + print(df) + print("") + except KeyError: + print("No trades were found") + print("") + except V20Error as e: + msg_length = len(e.msg) + msg = e.msg[17 : msg_length - 2] + print(msg) + print("") + + +def close_trade(accountID, other_args: List[str]): + parser = argparse.ArgumentParser( + add_help=False, + prog="closetrade", + description="Close a trade by id.", + ) + parser.add_argument( + "-i", + "--id", + dest="orderID", + action="store", + type=str, + required=False, + help="The Trade ID to close. ", + ) + parser.add_argument( + "-u", + "--units", + dest="units", + action="store", + required=False, + help="The number of units on the trade to close. If not set it " + + "defaults to all units. ", + ) + if other_args: + if "-i" not in other_args[0]: + other_args.insert(0, "-i") + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + data = {} + if ns_parser.units is not None: + data["units"] = ns_parser.units + try: + request = trades.TradeClose(accountID, ns_parser.orderID, data) + response = client.request(request) + + 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: + 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="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 ", + ) + + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + parameters = {} + parameters["granularity"] = ns_parser.granularity + parameters["count"] = ns_parser.candlecount + try: + instrument = format_instrument(instrument, "_") + 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"] + if gtff.USE_ION: + plt.ion() + mpf.plot( + df, + type="candle", + style="charles", + volume=True, + title=f"{instrument} {ns_parser.granularity}", + ) + print("") + except V20Error as e: + d_error = eval(e.msg) + print(d_error["errorMessage"], "\n") + + +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 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:]) + + +def calendar(instrument, other_args: List[str]): + parser = argparse.ArgumentParser( + add_help=False, + prog="calendar", + description="Show Calendar Data", + ) + parser.add_argument( + "-d", + "--days", + dest="days", + action="store", + type=int, + default=7, + required=False, + help="The number of days to search for, up to 30 forward or backward " + + "use negative numbers to search back. ", + ) + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + parameters = {"instrument": instrument, "period": str(ns_parser.days * 86400 * -1)} + try: + request = labs.Calendar(params=parameters) + response = client.request(request) + + l_data = [] + for i in range(len(response)): + if "forecast" in response[i]: + forecast = response[i]["forecast"] + if response[i]["unit"] != "Index": + forecast += response[i]["unit"] + else: + forecast = "" + + if "market" in response[i]: + market = response[i]["market"] + if response[i]["unit"] != "Index": + market += response[i]["unit"] + else: + market = "" + + if "actual" in response[i]: + actual = response[i]["actual"] + if response[i]["unit"] != "Index": + actual += response[i]["unit"] + else: + actual = "" + + if "previous" in response[i]: + previous = response[i]["previous"] + if response[i]["unit"] != "Index": + previous += response[i]["unit"] + else: + previous = "" + + if "impact" in response[i]: + impact = response[i]["impact"] + else: + impact = "" + + l_data.append( + { + "Title": response[i]["title"], + "Time": datetime.fromtimestamp(response[i]["timestamp"]), + "Impact": impact, + "Forecast": forecast, + "Market Forecast": market, + "Currency": response[i]["currency"], + "Region": response[i]["region"], + "Actual": actual, + "Previous": previous, + } + ) + + print(pd.DataFrame(l_data).to_string(index=False)) + print("") + + except V20Error as e: + d_error = eval(e.msg) + print(d_error["message"], "\n") + + +def load(other_args: List[str]): + """Load a forex instrument to use""" + parser = argparse.ArgumentParser( + add_help=False, + prog="load", + description="Forex using oanda", + ) + + parser.add_argument( + "-i", + "--instrument", + required=True, + type=str, + dest="instrument", + help="Forex pair to use. ", + ) + + try: + if other_args: + if "-" not in other_args[0]: + other_args.insert(0, "-i") + + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + print("") + return ns_parser.instrument.upper() + + except Exception as e: + print(e, "\n") + return None + + +def book_plot(df, instrument, book_type): + _, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI) + df = df.apply(pd.to_numeric) + df["shortCountPercent"] = df["shortCountPercent"] * -1 + axis_origin = max( + abs(max(df["longCountPercent"])), abs(max(df["shortCountPercent"])) + ) + ax.set_xlim(-axis_origin, +axis_origin) + + sns.set_style(style="darkgrid") + + sns.barplot( + x="longCountPercent", + y="price", + data=df, + label="Count Percent", + color="green", + orient="h", + ) + + sns.barplot( + x="shortCountPercent", + y="price", + data=df, + label="Prices", + color="red", + orient="h", + ) + + ax.invert_yaxis() + plt.title(f"{instrument} {book_type}") + plt.xlabel("Count Percent") + plt.ylabel("Price") + sns.despine(left=True, bottom=True) + ax.yaxis.set_major_locator(mticker.MultipleLocator(5)) + if gtff.USE_ION: + plt.ion() + plt.show() + + +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)) + return instrument diff --git a/gamestonk_terminal/main_helper.py b/gamestonk_terminal/main_helper.py index 40c8f6714ba..4b5810fef6c 100644 --- a/gamestonk_terminal/main_helper.py +++ b/gamestonk_terminal/main_helper.py @@ -63,6 +63,7 @@ def print_help(s_ticker, s_start, s_interval, b_is_market_open): print( " po portfolio optimization, \t\t optimal portfolio weights from pyportfolioopt" ) + print(" fx forex menu, \t\t\t forex support through Oanda") if s_ticker: print( diff --git a/poetry.lock b/poetry.lock index 1c9dc7b56d1..a5b4f1f7f8e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -837,7 +837,7 @@ idna = ">=2.0" [[package]] name = "importlib-metadata" -version = "4.0.0" +version = "4.0.1" description = "Read metadata from Python packages" category = "main" optional = false @@ -1496,6 +1496,14 @@ category = "main" optional = false python-versions = ">=3.6" +[[package]] +name = "oandapyv20" +version = "0.6.3" +description = "Python wrapper for the OANDA REST-V20 API" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "oauthlib" version = "3.1.0" @@ -2099,7 +2107,7 @@ python-versions = "*" [[package]] name = "pyupgrade" -version = "2.12.0" +version = "2.13.0" description = "A tool to automatically upgrade syntax for newer versions." category = "dev" optional = false @@ -2954,7 +2962,7 @@ prediction = ["fbprophet", "tensorflow", "pmdarima", "transformers", "flair", "t [metadata] lock-version = "1.1" python-versions = "^3.6.8" -content-hash = "fa7c02ebec2d14a1c42f76f8e1328134975893c5f98afa879f2bc672675d6a7f" +content-hash = "2b31cd4c709b83d1a6f1d516036c84dc611cc487ae5d499905658454fa8dba40" [metadata.files] absl-py = [ @@ -3287,30 +3295,51 @@ entrypoints = [ ] ephem = [ {file = "ephem-3.7.7.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:0a6d6ed003383f702695094399728052981c567d423b25aa7163b3fd40de3d5a"}, + {file = "ephem-3.7.7.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3cbc4f643329c4f64de63584e636625fa9f11b50209386ed99cbf508abe19488"}, {file = "ephem-3.7.7.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e73b9997d7b844fabff988d7f66c3bad4e76ec8975ec9c60ec988037bb5c04b1"}, {file = "ephem-3.7.7.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7619297a1103f6b3dac0c609bfa7c31e17678fb1a02653347ec030487e412031"}, + {file = "ephem-3.7.7.1-cp27-cp27m-win32.whl", hash = "sha256:7756218a8b72ca50ee4f290b80828073412e7d7023389df5ead5d14839175bda"}, + {file = "ephem-3.7.7.1-cp27-cp27m-win_amd64.whl", hash = "sha256:b8f9fb5b4184523b98f6629c6c927aeeba80d61df21f07af2844cdae45e1a294"}, {file = "ephem-3.7.7.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:37fd1abbebb821381d2865064a6fa9484ffdcd2161cf92193da8d42da79aa7db"}, {file = "ephem-3.7.7.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:ff4e1584cc232d8bc4aea405346eeb8a24b0fbe84c6331d4edb5d4456d19f1c3"}, {file = "ephem-3.7.7.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:c6081a9791e9de6511cddbeb9529bd5550fdb6f9e2a8dc9c2ec85911d67c6d11"}, + {file = "ephem-3.7.7.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d1d1ac8dc9be47dc6c7ec62800ca6557d03e8be3716c2a10ea0c8f1198c0a701"}, {file = "ephem-3.7.7.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:7f781d73e49caf7989c503a948471f1c8c1f738a450d557413999fb732f4dba3"}, {file = "ephem-3.7.7.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:f0959882bf73c8fe1cb801f42b0989e0ecb9d84b99eb8b8f70d61d16767a1e8a"}, + {file = "ephem-3.7.7.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:06029db6b26f1764ed009360de8530f6cd759411232b3001df1b69ba6afa871d"}, + {file = "ephem-3.7.7.1-cp35-cp35m-manylinux2014_s390x.whl", hash = "sha256:562baaeb3a46cf2248f97d2ad80092d747024e5dea62ebe50386a04b8e506c10"}, {file = "ephem-3.7.7.1-cp35-cp35m-win32.whl", hash = "sha256:2bcd953cced045e4232bcb71d60f6ba237f7944c880e83e74cc959449b192dee"}, {file = "ephem-3.7.7.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fd0b922387df1ad8721f2fafd5dca51ce7105238f968e1532a177b781b8cae0e"}, {file = "ephem-3.7.7.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:e10c2c8afbb9e927d46d9bf3562441da2173ff196790a75ad0e5c5ee920d5922"}, + {file = "ephem-3.7.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:592264e512b5658402e93903343eb26502c6f327df8a71847f3dcaea73908f32"}, {file = "ephem-3.7.7.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:8d586d970c42d62b1fc757a1c7c10679416a43e9e4a77391a05907159ce346f8"}, {file = "ephem-3.7.7.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:1a8ea6a045ff669011f8ecdeffadfc5eaa519fb56ab8dd40508910e9db5ec7b0"}, + {file = "ephem-3.7.7.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c25993d6554820ff11fdd8f2ddb75bf99e70db01327f09700bf8811201e9daef"}, + {file = "ephem-3.7.7.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:9180d953ff4cbe89fc9ccc78aea73f290c0166b8ddb01e9c837b4639a7492ec8"}, {file = "ephem-3.7.7.1-cp36-cp36m-win32.whl", hash = "sha256:72fdb72c813fc9780726aa732eb2193bbe1663549265ca5a746899215ecfbffd"}, {file = "ephem-3.7.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f5af029f8814aa8617e73b2ddcad2bd1c682ca13c31ee3b6f5825dd5cb43e777"}, {file = "ephem-3.7.7.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:e61c68f1c7d304a130bb6b88c1fca6600bbc1a1df6ca9a109b6eba4c6cbd3376"}, + {file = "ephem-3.7.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fdf5f3ed8bd46ce2bf1145b26aec65a2c08ea7c97cf42b6b439af6e7bc6dc94d"}, {file = "ephem-3.7.7.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:31e2a17ca6f8d6bd44832574fea5461938ba034e52bf551e15e1a4c8f8c6c4fb"}, {file = "ephem-3.7.7.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:1ff5abefa230d5ab40846f1cb27d4f5583d46044fecf10e17bfb559bdc766a47"}, + {file = "ephem-3.7.7.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:5ef34e3cfea0683998e867d7b8de642081e8821fafbd5e1d2dcab503c119c4a2"}, + {file = "ephem-3.7.7.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:e2aca9edb59ee2bb3c8c36f17177966bc762da04af994450c8ad61a20fb6acdd"}, {file = "ephem-3.7.7.1-cp37-cp37m-win32.whl", hash = "sha256:71ea3935c658ee184575fd4cc2aa3e7fd6d6bac4a1ccff9ee5549cee67ff0385"}, {file = "ephem-3.7.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:34fd228105b89fefd20608420861a75a184db1edeaf0056cf2f85d2a2f768aee"}, {file = "ephem-3.7.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:189fcaeacb44dc5a1684ad489542f126d99d4a85e87d2fe73864c902f0bc2c71"}, {file = "ephem-3.7.7.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a5ddf46ced515e4b81dfd22e6408fd06a329c31c6abb10af48d392641c490b0e"}, {file = "ephem-3.7.7.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:3530bee3eb7458d86d4c399f51f5b73ad7ea0441f0b2fb03de41d623789935af"}, + {file = "ephem-3.7.7.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:77193de077e5fff453756144dee978e5fd6978c5c03ce3f67a09bb237e78f9d4"}, + {file = "ephem-3.7.7.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:095ad42e8df6a5c91e0aeb5a68d76c0800b0283f72fc5f76e6df370ae1fb5537"}, {file = "ephem-3.7.7.1-cp38-cp38-win32.whl", hash = "sha256:33ad1a71b204aff123250b04e0ff2d5c3b8e9c6c814bf2c675d7752ce5896c02"}, {file = "ephem-3.7.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:4bc5140cc7218a53ee6d949110f6ed413b8e3165294100515cd0fb29aa8e3637"}, + {file = "ephem-3.7.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7aa88d2a6d216c2044be2a3b3cb184e9c8a5022f12464220694017ea9d75209d"}, + {file = "ephem-3.7.7.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:1a9822bde93205c67fd3399b26868204b67d9e4442af21a764307e51ea6dc672"}, + {file = "ephem-3.7.7.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:15dd3dbb3864fb853388e39ac3c915c72873507f54ee6b3ede0033b03465d8ba"}, + {file = "ephem-3.7.7.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5763919cdf38cd0488172711da504c3be003b7c104ccd2516cc496ff05f146ae"}, + {file = "ephem-3.7.7.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:cdea23a7caa1178b322e6e76484d40bd021672446ff51b512f35581e0f1b32b7"}, + {file = "ephem-3.7.7.1-cp39-cp39-win32.whl", hash = "sha256:c3cbc973b334892972445cec3deb99aafd0c57cb4e50679d5f12fc8c51b4e65f"}, + {file = "ephem-3.7.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:1af920c382980fe3566a31f36bd712973e1ca6049cbe07b4624214d5e1f144bc"}, {file = "ephem-3.7.7.1.tar.gz", hash = "sha256:36b51a8dc7cfdeb456dd6b8ab811accab8341b2d562ee3c6f4c86f6d3dbb984e"}, ] fbprophet = [ @@ -3491,8 +3520,8 @@ idna-ssl = [ {file = "idna-ssl-1.1.0.tar.gz", hash = "sha256:a933e3bb13da54383f9e8f35dc4f9cb9eb9b3b78c6b36f311254d6d0d92c6c7c"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.0.0-py3-none-any.whl", hash = "sha256:19192b88d959336bfa6bdaaaef99aeafec179eca19c47c804e555703ee5f07ef"}, - {file = "importlib_metadata-4.0.0.tar.gz", hash = "sha256:2e881981c9748d7282b374b68e759c87745c25427b67ecf0cc67fb6637a1bff9"}, + {file = "importlib_metadata-4.0.1-py3-none-any.whl", hash = "sha256:d7eb1dea6d6a6086f8be21784cc9e3bcfa55872b52309bc5fad53a8ea444465d"}, + {file = "importlib_metadata-4.0.1.tar.gz", hash = "sha256:8c501196e49fb9df5df43833bdb1e4328f64847763ec8a50703148b73784d581"}, ] inflection = [ {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, @@ -3955,6 +3984,9 @@ numpy = [ {file = "numpy-1.19.5-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:a0d53e51a6cb6f0d9082decb7a4cb6dfb33055308c4c44f53103c073f649af73"}, {file = "numpy-1.19.5.zip", hash = "sha256:a76f502430dd98d7546e1ea2250a7360c065a5fdea52b2dffe8ae7180909b6f4"}, ] +oandapyv20 = [ + {file = "oandapyV20-0.6.3.tar.gz", hash = "sha256:173a56b41ab3a19315c2fbea6f9aa3f0c17f64ba84acff014d072c64c1844b28"}, +] oauthlib = [ {file = "oauthlib-3.1.0-py2.py3-none-any.whl", hash = "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"}, {file = "oauthlib-3.1.0.tar.gz", hash = "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889"}, @@ -4305,8 +4337,8 @@ pytz = [ {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, ] pyupgrade = [ - {file = "pyupgrade-2.12.0-py2.py3-none-any.whl", hash = "sha256:024fc6be99c2ccdbc1c9354ac0ab8d5b9e5bf69a5477384677b6c9d8f2ce63bf"}, - {file = "pyupgrade-2.12.0.tar.gz", hash = "sha256:eef4d77e44e57dfbae897a9a61543be1f34701dbbe51cc18462210021bd28dcf"}, + {file = "pyupgrade-2.13.0-py2.py3-none-any.whl", hash = "sha256:1cb5a15573fc63824b952bf7dde5b96f9672a1179ec779ac2d159a845da743c8"}, + {file = "pyupgrade-2.13.0.tar.gz", hash = "sha256:dc6b580b7bc82f357acb354bbba03d8d69da51425ecd5993a2651059bca80518"}, ] pywin32 = [ {file = "pywin32-300-cp35-cp35m-win32.whl", hash = "sha256:1c204a81daed2089e55d11eefa4826c05e604d27fe2be40b6bf8db7b6a39da63"}, @@ -4563,6 +4595,7 @@ scikit-learn = [ {file = "scikit_learn-0.24.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c658432d8a20e95398f6bb95ff9731ce9dfa343fdf21eea7ec6a7edfacd4b4d9"}, {file = "scikit_learn-0.24.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9dfa564ef27e8e674aa1cc74378416d580ac4ede1136c13dd555a87996e13422"}, {file = "scikit_learn-0.24.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:9c6097b6a9b2bafc5e0f31f659e6ab5e131383209c30c9e978c5b8abdac5ed2a"}, + {file = "scikit_learn-0.24.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:5580eba7345a4d3b097be2f067cc71a306c44bab19e8717a30361f279c929bea"}, {file = "scikit_learn-0.24.1-cp36-cp36m-win32.whl", hash = "sha256:7b04691eb2f41d2c68dbda8d1bd3cb4ef421bdc43aaa56aeb6c762224552dfb6"}, {file = "scikit_learn-0.24.1-cp36-cp36m-win_amd64.whl", hash = "sha256:1adf483e91007a87171d7ce58c34b058eb5dab01b5fee6052f15841778a8ecd8"}, {file = "scikit_learn-0.24.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:ddb52d088889f5596bc4d1de981f2eca106b58243b6679e4782f3ba5096fd645"}, @@ -4570,6 +4603,7 @@ scikit-learn = [ {file = "scikit_learn-0.24.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0567a2d29ad08af98653300c623bd8477b448fe66ced7198bef4ed195925f082"}, {file = "scikit_learn-0.24.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:99349d77f54e11f962d608d94dfda08f0c9e5720d97132233ebdf35be2858b2d"}, {file = "scikit_learn-0.24.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:83b21ff053b1ff1c018a2d24db6dd3ea339b1acfbaa4d9c881731f43748d8b3b"}, + {file = "scikit_learn-0.24.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:abe835a851610f87201819cb315f8d554e1a3e8128912783a31e87264ba5ffb7"}, {file = "scikit_learn-0.24.1-cp37-cp37m-win32.whl", hash = "sha256:c3deb3b19dd9806acf00cf0d400e84562c227723013c33abefbbc3cf906596e9"}, {file = "scikit_learn-0.24.1-cp37-cp37m-win_amd64.whl", hash = "sha256:d54dbaadeb1425b7d6a66bf44bee2bb2b899fe3e8850b8e94cfb9c904dcb46d0"}, {file = "scikit_learn-0.24.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:3c4f07f47c04e81b134424d53c3f5e16dfd7f494e44fd7584ba9ce9de2c5e6c1"}, @@ -4577,6 +4611,7 @@ scikit-learn = [ {file = "scikit_learn-0.24.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4ddd2b6f7449a5d539ff754fa92d75da22de261fd8fdcfb3596799fadf255101"}, {file = "scikit_learn-0.24.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:826b92bf45b8ad80444814e5f4ac032156dd481e48d7da33d611f8fe96d5f08b"}, {file = "scikit_learn-0.24.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:259ec35201e82e2db1ae2496f229e63f46d7f1695ae68eef9350b00dc74ba52f"}, + {file = "scikit_learn-0.24.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:9599a3f3bf33f73fed0fe06d1dfa4e6081365a58c1c807acb07271be0dce9733"}, {file = "scikit_learn-0.24.1-cp38-cp38-win32.whl", hash = "sha256:8772b99d683be8f67fcc04789032f1b949022a0e6880ee7b75a7ec97dbbb5d0b"}, {file = "scikit_learn-0.24.1-cp38-cp38-win_amd64.whl", hash = "sha256:ed9d65594948678827f4ff0e7ae23344e2f2b4cabbca057ccaed3118fdc392ca"}, {file = "scikit_learn-0.24.1-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:8aa1b3ac46b80eaa552b637eeadbbce3be5931e4b5002b964698e33a1b589e1e"}, @@ -4584,6 +4619,7 @@ scikit-learn = [ {file = "scikit_learn-0.24.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:087dfede39efb06ab30618f9ab55a0397f29c38d63cd0ab88d12b500b7d65fd7"}, {file = "scikit_learn-0.24.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:895dbf2030aa7337649e36a83a007df3c9811396b4e2fa672a851160f36ce90c"}, {file = "scikit_learn-0.24.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:9a24d1ccec2a34d4cd3f2a1f86409f3f5954cc23d4d2270ba0d03cf018aa4780"}, + {file = "scikit_learn-0.24.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:54be0a60a5a35005ad69c75902e0f5c9f699db4547ead427e97ef881c3242e6f"}, {file = "scikit_learn-0.24.1-cp39-cp39-win32.whl", hash = "sha256:fab31f48282ebf54dd69f6663cd2d9800096bad1bb67bbc9c9ac84eb77b41972"}, {file = "scikit_learn-0.24.1-cp39-cp39-win_amd64.whl", hash = "sha256:4562dcf4793e61c5d0f89836d07bc37521c3a1889da8f651e2c326463c4bd697"}, ] diff --git a/pyproject.toml b/pyproject.toml index a3bbf6762c7..45e9f245261 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,6 +59,7 @@ tabulate = "^0.8.9" pyportfolioopt = "^1.4.1" selenium = "^3.141.0" python-coinmarketcap = "^0.2" +oandapyV20 = "^0.6.3" [tool.poetry.dev-dependencies] pytest = "^6.2.2" diff --git a/requirements-full.txt b/requirements-full.txt index 1dce549665e..15e7172d368 100644 --- a/requirements-full.txt +++ b/requirements-full.txt @@ -1,4 +1,3 @@ -absl-py==0.12.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.2.0" aiohttp==3.7.4.post0; python_version >= "3.6" alpaca-trade-api==1.2.0 alpha-vantage==2.3.1 @@ -6,7 +5,6 @@ ansiwrap==0.8.4; python_version >= "3.6" appdirs==1.4.4; python_version >= "3.6" appnope==0.1.2; platform_system == "Darwin" and python_version >= "3.6" and sys_platform == "darwin" argon2-cffi==20.1.0; python_version >= "3.6" -astunparse==1.6.3 async-generator==1.10; python_full_version >= "3.6.1" and python_version >= "3.6" async-timeout==3.0.1; python_full_version >= "3.5.3" and python_version >= "3.6" attrs==20.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" @@ -14,15 +12,11 @@ backcall==0.2.0; python_version >= "3.6" beautifulsoup4==4.9.3; python_version >= "3.5" black==20.8b1; python_version >= "3.6" bleach==3.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" -bpemb==0.3.3; python_version >= "3.6" bs4==0.0.1 -cachetools==4.2.1; python_version >= "3.5" and python_version < "4.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") certifi==2020.12.5; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" cffi==1.14.5; implementation_name == "pypy" and python_version >= "3.6" chardet==4.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" -click==7.1.2; python_full_version >= "3.6.0" and python_version >= "3.6" -cloudpickle==1.6.0; python_version >= "3.6" -cmdstanpy==0.4.0 +click==7.1.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" colorama==0.4.4; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") convertdate==2.3.2; python_version >= "3.5" cryptography==3.4.7; python_version >= "3.6" @@ -30,49 +24,32 @@ cssselect==1.1.0; python_version >= "2.7" and python_full_version < "3.0.0" or p cvxpy==1.1.12; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.5" cycler==0.10.0; python_version >= "3.6" cython==0.29.17; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "darwin" or python_full_version >= "3.3.0" and python_version >= "3.6" and sys_platform == "darwin" -dataclasses==0.8; python_version >= "3.6" and python_version < "3.7" and python_full_version >= "3.6.2" +dataclasses==0.8; python_version >= "3.6" and python_version < "3.7" datetime==4.3; python_version >= "3.5" decorator==4.4.2; python_full_version >= "3.6.1" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.2.0" and python_version >= "3.6") defusedxml==0.7.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" -deprecated==1.2.12; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" detecta==0.0.5; python_version >= "3.6" ecos==2.0.7.post1; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.5" entrypoints==0.3; python_version >= "3.6" -ephem==3.7.7.1; python_version >= "2.7" and python_version < "4" -fbprophet==0.6 -filelock==3.0.12; python_full_version >= "3.6.0" and python_version >= "3.6" finviz==1.4.1 finvizfinance==0.9.6; python_version >= "3.5" -flair==0.7; python_version >= "3.6" -flatbuffers==1.12 fredapi==0.4.3 -ftfy==6.0.1; python_version >= "3.6" fundamentalanalysis==0.2.7 -future==0.18.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6" -gast==0.3.3; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" -gdown==3.12.2; python_version >= "3.6" -gensim==3.8.2; python_version >= "3.6" -google-auth-oauthlib==0.4.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.2.0" and python_version >= "3.6" -google-auth==1.29.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" -google-pasta==0.2.0 -grpcio==1.32.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.2.0" h5py==2.10.0 hijri-converter==2.1.1; python_version >= "3.6" holidays==0.10.5.2 -hyperopt==0.2.5; python_version >= "3.6" idna-ssl==1.1.0; python_version < "3.7" and python_version >= "3.6" idna==2.10; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version < "3.7" and python_version >= "3.6" and python_full_version >= "3.5.0" -importlib-metadata==4.0.0; python_version < "3.8" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.2.0" and python_version >= "3.6" and python_version < "3.8") +importlib-metadata==4.0.1; python_version < "3.8" and python_version >= "3.6" inflection==0.5.1; python_version >= "3.5" ipykernel==5.5.3; python_version >= "3.6" ipython-genutils==0.2.0; python_full_version >= "3.6.1" and python_version >= "3.6" ipython==7.16.1; python_version >= "3.6" ipywidgets==7.6.3 iso8601==0.1.14 -janome==0.4.1; python_version >= "3.6" jedi==0.18.0; python_version >= "3.6" jinja2==2.11.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" -joblib==1.0.1; python_version >= "3.6" and python_full_version >= "3.6.0" +joblib==1.0.1; python_version >= "3.6" jsonschema==3.2.0; python_version >= "3.6" jupyter-client==6.2.0; python_full_version >= "3.6.1" and python_version >= "3.6" jupyter-console==6.4.0; python_version >= "3.6" @@ -80,19 +57,13 @@ jupyter-core==4.7.1; python_full_version >= "3.6.1" and python_version >= "3.6" jupyter==1.0.0 jupyterlab-pygments==0.1.2; python_version >= "3.6" jupyterlab-widgets==1.0.0; python_version >= "3.6" -keras-preprocessing==1.1.2 kiwisolver==1.3.1; python_version >= "3.6" -konoha==4.6.3; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.6" korean-lunar-calendar==0.2.1 -langdetect==1.0.8; python_version >= "3.6" -lunarcalendar==0.0.9; python_version >= "2.7" and python_version < "4" -lxml==4.6.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" -markdown==3.3.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.2.0" and python_version >= "3.6" +lxml==4.6.3; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" markupsafe==1.1.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" matplotlib==3.3.4; python_version >= "3.6" mistune==0.8.4; python_version >= "3.6" more-itertools==8.7.0; python_version >= "3.5" -mpld3==0.3; python_version >= "3.6" mplfinance==0.12.7a12 msgpack==1.0.2 multidict==5.1.0; python_version >= "3.6" @@ -102,14 +73,12 @@ nbclient==0.5.3; python_full_version >= "3.6.1" and python_version >= "3.6" nbconvert==6.0.7; python_version >= "3.6" nbformat==5.1.3; python_full_version >= "3.6.1" and python_version >= "3.6" nest-asyncio==1.5.1; python_full_version >= "3.6.1" and python_version >= "3.6" -networkx==2.5.1; python_version >= "3.6" notebook==6.3.0; python_version >= "3.6" numpy==1.19.5; python_version >= "3.6" +oandapyv20==0.6.3 oauthlib==3.1.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" -opt-einsum==3.3.0; python_version >= "3.5" osqp==0.6.2.post0; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.5" -overrides==3.1.0; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.6" -packaging==20.9; python_full_version >= "3.6.0" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6") +packaging==20.9; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" pandas-ta==0.2.45b pandas==1.1.5; python_full_version >= "3.6.1" pandocfilters==1.4.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" @@ -127,13 +96,10 @@ prawcore==2.0.0; python_version >= "3.6" and python_version < "4.0" prettytable==2.1.0; python_version >= "3.6" prometheus-client==0.10.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" prompt-toolkit==3.0.18; python_full_version >= "3.6.1" -protobuf==3.15.8; python_full_version >= "3.6.0" and python_version >= "3.6" psaw==0.0.12; python_version >= "3" ptyprocess==0.7.0; os_name != "nt" and python_version >= "3.6" and sys_platform != "win32" py==1.10.0; python_version >= "3.6" and python_full_version < "3.0.0" and implementation_name == "pypy" or implementation_name == "pypy" and python_version >= "3.6" and python_full_version >= "3.4.0" pyally==1.1.2 -pyasn1-modules==0.2.8; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" -pyasn1==0.4.8; python_version >= "3.5" and python_full_version < "3.0.0" and python_version < "4" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") or python_version >= "3.5" and python_version < "4" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") and python_full_version >= "3.6.0" pycoingecko==1.4.1 pycparser==2.20; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" pygments==2.8.1; python_version >= "3.6" @@ -141,16 +107,15 @@ pymeeus==0.5.11; python_version >= "3.5" pyobjc-core==7.1; python_version >= "3.6" and sys_platform == "darwin" pyobjc-framework-cocoa==7.1; python_version >= "3.6" and sys_platform == "darwin" pyotp==2.6.0; python_version >= "3" -pyparsing==2.4.7; python_full_version >= "3.6.0" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") +pyparsing==2.4.7; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" pyportfolioopt==1.4.1; python_full_version >= "3.6.1" and python_full_version < "4.0.0" pyrsistent==0.14.11 pysocks==1.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" -pystan==2.19.1.1 python-coinmarketcap==0.2 -python-dateutil==2.8.1; python_full_version >= "3.6.1" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") and (python_version >= "2.7" and python_full_version < "3.0.0" and python_version < "4" or python_version >= "2.7" and python_version < "4" and python_full_version >= "3.3.0") +python-dateutil==2.8.1; python_full_version >= "3.6.1" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") python-dotenv==0.17.0; python_version >= "3" pytrends==4.7.3 -pytz==2021.1; python_full_version >= "3.6.1" and python_version >= "3.5" and python_version < "4" +pytz==2021.1; python_full_version >= "3.6.1" and python_version >= "3.5" pywin32==300; sys_platform == "win32" and python_version >= "3.6" pywinpty==0.5.7; os_name == "nt" and python_version >= "3.6" pyyaml==5.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" @@ -161,50 +126,35 @@ qtpy==1.9.0; python_version >= "3.6" quandl==3.6.1; python_version >= "3.5" rapidfuzz==1.4.1; python_version >= "3.5" regex==2020.11.13 -requests-oauthlib==1.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" +requests-oauthlib==1.3.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" requests==2.25.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") retrying==1.3.3 robin-stocks==2.0.3; python_version >= "3" -rsa==4.7.2; python_version >= "3.5" and python_version < "4" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") -sacremoses==0.0.45; python_full_version >= "3.6.0" and python_version >= "3.6" scikit-learn==0.24.1; python_version >= "3.6" scipy==1.5.4; python_version >= "3.6" screeninfo==0.6.7 scs==2.1.3; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.5" seaborn==0.11.1; python_version >= "3.6" -segtok==1.5.10; python_version >= "3.6" selenium==3.141.0 send2trash==1.5.0; python_version >= "3.6" -sentencepiece==0.1.91; python_full_version >= "3.6.0" and python_version >= "3.6" -setuptools-git==1.2 -six==1.15.0; python_full_version >= "3.6.1" and python_version >= "3.6" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") and (python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0") and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6") -smart-open==5.0.0; python_version >= "3.6" +six==1.15.0; python_full_version >= "3.6.1" and python_version >= "3.6" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") and (python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0") and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6") soupsieve==2.2.1; python_version >= "3.6" -sqlitedict==1.7.0; python_version >= "3.6" statsmodels==0.12.2; python_version >= "3.6" tabulate==0.8.9 tenacity==7.0.0; python_version >= "3.6" -tensorboard-data-server==0.6.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.2.0" and python_version >= "3.6" -tensorboard-plugin-wit==1.8.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.2.0" -tensorboard==2.5.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.2.0" -tensorflow-estimator==2.4.0 -tensorflow==2.4.1 termcolor==1.1.0 terminado==0.9.4; python_version >= "3.6" testpath==0.4.4; python_version >= "3.6" textwrap3==0.9.2; python_version >= "3.6" threadpoolctl==2.1.0; python_version >= "3.6" timeseriescrossvalidation @ git+https://github.com/DidierRLopes/TimeSeriesCrossValidation.git@master -tokenizers==0.9.3; python_full_version >= "3.6.0" and python_version >= "3.6" toml==0.10.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6" -torch==1.7.1; python_full_version >= "3.6.2" tornado==6.1; python_full_version >= "3.6.1" and python_version >= "3.6" -tqdm==4.60.0; python_full_version >= "3.6.0" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") +tqdm==4.60.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" tradingview-ta==3.2.3; python_version >= "3.5" traitlets==4.3.3; python_full_version >= "3.6.1" and python_version >= "3.6" -transformers==3.5.1; python_full_version >= "3.6.0" typed-ast==1.4.3; python_version >= "3.6" -typing-extensions==3.7.4.3; python_full_version >= "3.6.2" and python_version >= "3.6" and python_version < "3.8" +typing-extensions==3.7.4.3; python_version < "3.8" and python_version >= "3.6" update-checker==0.18.0; python_version >= "3.6" and python_version < "4.0" urllib3==1.26.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.6" user-agent==0.1.9 @@ -212,9 +162,7 @@ wcwidth==0.2.5; python_version >= "3.6" and python_full_version >= "3.6.1" webencodings==0.5.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" websocket-client==0.58.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" websockets==8.1; python_full_version >= "3.6.1" -werkzeug==1.0.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" widgetsnbextension==3.5.1 -wrapt==1.12.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" yarl==1.6.3; python_version >= "3.6" yfinance==0.1.59 zipp==3.4.1; python_version < "3.8" and python_version >= "3.6" diff --git a/requirements.txt b/requirements.txt index bf0985ecfa5..15e7172d368 100644 --- a/requirements.txt +++ b/requirements.txt @@ -40,7 +40,7 @@ hijri-converter==2.1.1; python_version >= "3.6" holidays==0.10.5.2 idna-ssl==1.1.0; python_version < "3.7" and python_version >= "3.6" idna==2.10; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version < "3.7" and python_version >= "3.6" and python_full_version >= "3.5.0" -importlib-metadata==4.0.0; python_version < "3.8" and python_version >= "3.6" +importlib-metadata==4.0.1; python_version < "3.8" and python_version >= "3.6" inflection==0.5.1; python_version >= "3.5" ipykernel==5.5.3; python_version >= "3.6" ipython-genutils==0.2.0; python_full_version >= "3.6.1" and python_version >= "3.6" @@ -75,6 +75,7 @@ nbformat==5.1.3; python_full_version >= "3.6.1" and python_version >= "3.6" nest-asyncio==1.5.1; python_full_version >= "3.6.1" and python_version >= "3.6" notebook==6.3.0; python_version >= "3.6" numpy==1.19.5; python_version >= "3.6" +oandapyv20==0.6.3 oauthlib==3.1.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" osqp==0.6.2.post0; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.5" packaging==20.9; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" diff --git a/terminal.py b/terminal.py index 78e912c761d..368f078bcc3 100644 --- a/terminal.py +++ b/terminal.py @@ -32,6 +32,7 @@ from gamestonk_terminal.portfolio import port_controller from gamestonk_terminal.cryptocurrency import crypto_controller from gamestonk_terminal.screener import screener_controller from gamestonk_terminal.portfolio_optimization import po_controller +from gamestonk_terminal.forex import fx_controller # pylint: disable=too-many-statements,too-many-branches @@ -85,6 +86,7 @@ def main(): "crypto", "ra", "po", + "fx", ] menu_parser.add_argument("opt", choices=choices) @@ -220,6 +222,9 @@ def main(): s_interval, ) + elif ns_known_args.opt == "fx": + b_quit = fx_controller.menu() + elif ns_known_args.opt == "ta": b_quit = ta_controller.menu( df_stock,