Merge pull request #27 from alokan/forex

Forex
This commit is contained in:
DJ
2021-04-23 15:07:56 -06:00
committed by GitHub
4 changed files with 365 additions and 119 deletions

View File

@@ -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(

View File

@@ -108,13 +108,21 @@ Specify the instrument to use in the format EUR_USD
## candles <a name="candles"></a>
```text
usage: candles [-g GRANULARITY] [-c CANDLECOUNT]
usage: candles [-g GRANULARITY] [-c CANDLECOUNT] [-abcCeorsv]
```
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)
* -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)
* -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)
* -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 <a name="price"></a>
```text
@@ -143,4 +151,4 @@ Plots the current orderbook for loaded instrument if one is provided by Oanda. N
```text
usage: positionbook
```
Plots the current positionbook for the loaded instrument if one is provided by Oanda. Not a live chart.
Plots the current positionbook for the loaded instrument if one is provided by Oanda. Not a live chart.

View File

@@ -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,27 @@ 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 +185,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"""

View File

@@ -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
@@ -50,6 +51,17 @@ 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,
@@ -228,7 +240,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. ",
)
@@ -250,38 +262,26 @@ 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]):
parser = argparse.ArgumentParser(
@@ -327,21 +327,28 @@ 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]["createTime"][:10]
+ " "
+ response["orders"][i]["createTime"][11:19],
"Time In Force": response["orders"][i]["timeInForce"],
}
)
df = pd.DataFrame.from_dict(pending_data)
if not pending_data:
print("No pending orders")
else:
print(df.to_string(index=False))
print("")
except V20Error as e:
d_error = eval(e.msg)
print(d_error["errorMessage"], "\n")
@@ -359,22 +366,26 @@ 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:
@@ -382,6 +393,17 @@ 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,
@@ -417,8 +439,10 @@ 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")
print("")
@@ -467,24 +491,26 @@ 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]):
@@ -515,71 +541,165 @@ 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",
)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
parameters = {}
parameters["granularity"] = ns_parser.granularity
parameters["granularity"] = ns_parser.granularity.upper()
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"]
df = get_candles_dataframe(accountID, instrument, parameters)
plots_to_add, legends, subplot_legends = 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,
)
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)
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 = []
subplot_legends = []
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.ad:
ad = ta.ad(df["High"], df["Low"], df["Close"], df["Volume"])
ad_plot = mpf.make_addplot(ad, panel=panel_number)
plots_to_add.append(ad_plot)
subplot_legends.extend([panel_number * 2, ["AD"]])
panel_number += 1
if ns_parser.bbands:
bbands = ta.bbands(df["Close"])
bbands = bbands.drop("BBB_5_2.0", axis=1)
bbands_plot = mpf.make_addplot(bbands, panel=0)
plots_to_add.append(bbands_plot)
legends.extend(["Lower BBand", "Middle BBand", "Upper BBand"])
if ns_parser.cci:
cci = ta.cci(df["High"], df["Low"], df["Close"])
cci_plot = mpf.make_addplot(cci, panel=panel_number)
plots_to_add.append(cci_plot)
subplot_legends.extend([panel_number * 2, ["CCI"]])
panel_number += 1
if ns_parser.ema:
ema = ta.ema(df["Close"])
ema_plot = mpf.make_addplot(ema, panel=0)
plots_to_add.append(ema_plot)
legends.append("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)
subplot_legends.extend([panel_number * 2, ["RSI"]])
panel_number += 1
if ns_parser.obv:
obv = ta.obv(df["Close"], df["Volume"])
obv_plot = mpf.make_addplot(obv, panel=panel_number)
plots_to_add.append(obv_plot)
subplot_legends.extend([panel_number * 2, ["OBV"]])
panel_number += 1
if ns_parser.sma:
sma_length = [20, 50]
for length in sma_length:
sma = ta.sma(df["Close"], length=length)
sma_plot = mpf.make_addplot(sma, panel=0)
plots_to_add.append(sma_plot)
legends.append(f"{length} SMA")
if ns_parser.vwap:
vwap = ta.vwap(df["High"], df["Low"], df["Close"], df["Volume"])
vwap_plot = mpf.make_addplot(vwap, panel=0)
plots_to_add.append(vwap_plot)
legends.append("vwap")
return plots_to_add, legends, subplot_legends
def calendar(instrument, other_args: List[str]):
@@ -665,6 +785,39 @@ def calendar(instrument, other_args: List[str]):
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(
@@ -740,9 +893,17 @@ def book_plot(df, instrument, book_type):
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
try:
if char not in instrument:
instrument_list = list(instrument)
instrument_list.pop(3)
if char == " ":
instrument = "".join(map(str, instrument_list))
else:
instrument_list.insert(3, char)
instrument = "".join(map(str, instrument_list))
return instrument
except TypeError:
print("Please load an instrument")
print("")