diff --git a/gamestonk_terminal/README.md b/gamestonk_terminal/README.md
index 211b0a008e7..a3aab2939c8 100644
--- a/gamestonk_terminal/README.md
+++ b/gamestonk_terminal/README.md
@@ -14,7 +14,7 @@
* [Comparison Analysis](#Comparison-Analysis-)
* [Exploratory Data Analysis](#Exploratory-Data-Analysis-)
* [Residual Analysis](#Residual-Analysis-)
-* [FRED](#FRED-)
+* [Economic Data](#Econ-)
* [Options](#Options-)
* [Screener](#Screener-)
* [Forex](#Forex-)
@@ -323,9 +323,10 @@ Command|Description|Source
`independence` |tests independent and identically distributed (BDS) | [Wikipedia](https://en.wikipedia.org/wiki/Pearson%27s_chi-squared_test#Testing_for_statistical_independence)
-## FRED [»](fred/README.md)
+## ECON [»](econ/README.md)
Command|Description
------ | --------
+FRED Data |
`gdp` | GDP
`unemp` | Unemployment Rate
`t1` | 1-Year Treasury Constant Maturity Rate
@@ -337,7 +338,8 @@ Command|Description
`moodAAA` | Moody's Seasoned AAA Corporate Bond Yield
`usdcad` | Canada / U.S. Foreign Exchange Rate (CAD per 1 USD)
`cust` | User Specified FRED Data - Please Specify --id
-
+VIX Data |
+`view` | Plots VIX Historical Data
## Options [»](options/README.md)
diff --git a/gamestonk_terminal/due_diligence/reddit_view.py b/gamestonk_terminal/due_diligence/reddit_view.py
index 5c536add200..3348e8638eb 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,8 @@ def due_diligence(other_args: List[str], ticker: str):
"stocks",
"investing",
"wallstreetbets",
+ "forex",
+ "Forexstrategy",
]
submissions = psaw_api.search_submissions(
diff --git a/gamestonk_terminal/fred/README.md b/gamestonk_terminal/econ/README.md
similarity index 93%
rename from gamestonk_terminal/fred/README.md
rename to gamestonk_terminal/econ/README.md
index f781a1ac1e4..b2b8149b044 100644
--- a/gamestonk_terminal/fred/README.md
+++ b/gamestonk_terminal/econ/README.md
@@ -1,4 +1,6 @@
-# FRED
+# ECON
+
+## FRED
https://fred.stlouisfed.org
@@ -121,4 +123,12 @@ Date
2021-03-11 1.1978
````
-Note the NaN on 2-15, which corresponds to President's Day.
\ No newline at end of file
+Note the NaN on 2-15, which corresponds to President's Day.
+
+## VIX
+
+This command pulls historical VIX data from yfinance module.
+````
+usage: view [-s START_DATE]
+````
+* -s Start date to plot historical VIX data
\ No newline at end of file
diff --git a/gamestonk_terminal/econ/__init__.py b/gamestonk_terminal/econ/__init__.py
new file mode 100644
index 00000000000..e19b8205271
--- /dev/null
+++ b/gamestonk_terminal/econ/__init__.py
@@ -0,0 +1 @@
+from . import econ_controller # noqa: F401
diff --git a/gamestonk_terminal/fred/fred_controller.py b/gamestonk_terminal/econ/econ_controller.py
similarity index 83%
rename from gamestonk_terminal/fred/fred_controller.py
rename to gamestonk_terminal/econ/econ_controller.py
index 6068bf62256..1e5eccc3c27 100644
--- a/gamestonk_terminal/fred/fred_controller.py
+++ b/gamestonk_terminal/econ/econ_controller.py
@@ -1,4 +1,4 @@
-""" Fred Controller """
+""" Econ Controller """
__docformat__ = "numpy"
import argparse
@@ -8,11 +8,11 @@ from prompt_toolkit.completion import NestedCompleter
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.helper_funcs import get_flair
from gamestonk_terminal.menu import session
-from gamestonk_terminal.fred import fred_view
+from gamestonk_terminal.econ import fred_view, vix_view
-class FredController:
- """Fred Controller"""
+class EconController:
+ """Econ Controller"""
# Command choices
CHOICES = [
@@ -30,11 +30,12 @@ class FredController:
"moodAAA",
"usdcad",
"cust",
+ "view",
]
def __init__(self):
"""Constructor"""
- self.disc_parser = argparse.ArgumentParser(add_help=False, prog="fred")
+ self.disc_parser = argparse.ArgumentParser(add_help=False, prog="econ")
self.disc_parser.add_argument(
"cmd",
choices=self.CHOICES,
@@ -44,11 +45,15 @@ class FredController:
def print_help():
"""Print help"""
- print("\nFred Economic Data:")
+ print("\nEconomic Data:")
print(" help show this menu again")
print(" q quit this menu, and shows back to main menu")
print(" quit quit to abandon program")
print(" ")
+ print("VIX:")
+ print(" view plot VIX")
+ print(" ")
+ print("FRED:")
print(" gdp GDP")
print(" unemp Unemployment Rate")
print(" t1 1-Year Treasury Constant Maturity Rate")
@@ -59,7 +64,6 @@ class FredController:
print(" fedrate Effective Federal Funds Rate")
print(" moodAAA Moody's Seasoned AAA Corporate Bond Yield")
print(" usdcad Canada / U.S. Foreign Exchange Rate (CAD per 1 USD)")
- print("")
print(" cust User Specified FRED Data - Please Specify --id")
print("")
return
@@ -112,36 +116,38 @@ class FredController:
"""Process Fred Data call"""
fred_view.get_fred_data(other_args, cmd)
+ def call_view(self, other_args: List[str]):
+ """Process view for VIX Data."""
+ vix_view.view_vix(other_args)
+
def call_cust(self, other_args: List[str]):
"""Process cust call"""
fred_view.custom_data(other_args)
def menu():
- """Fred Menu"""
-
- fred_controller = FredController()
+ """Econ Menu"""
+ econ_controller = EconController()
plt.close("all")
-
- fred_controller.print_help()
+ econ_controller.print_help()
# Loop forever and ever
while True:
# Get input command from user
if session and gtff.USE_PROMPT_TOOLKIT:
completer = NestedCompleter.from_nested_dict(
- {c: None for c in fred_controller.CHOICES}
+ {c: None for c in econ_controller.CHOICES}
)
an_input = session.prompt(
- f"{get_flair()} (fred)> ",
+ f"{get_flair()} (econ)> ",
completer=completer,
)
else:
- an_input = input(f"{get_flair()} (fred)> ")
+ an_input = input(f"{get_flair()} (econ)> ")
try:
- process_input = fred_controller.switch(an_input)
+ process_input = econ_controller.switch(an_input)
if process_input is not None:
return process_input
diff --git a/gamestonk_terminal/fred/fred_view.py b/gamestonk_terminal/econ/fred_view.py
similarity index 100%
rename from gamestonk_terminal/fred/fred_view.py
rename to gamestonk_terminal/econ/fred_view.py
diff --git a/gamestonk_terminal/econ/vix_view.py b/gamestonk_terminal/econ/vix_view.py
new file mode 100644
index 00000000000..82565e0338b
--- /dev/null
+++ b/gamestonk_terminal/econ/vix_view.py
@@ -0,0 +1,63 @@
+""" VIX View """
+__docformat__ = "numpy"
+import argparse
+
+from typing import List
+from pandas.plotting import register_matplotlib_converters
+import matplotlib.pyplot as plt
+import yfinance as yf
+from gamestonk_terminal.helper_funcs import (
+ parse_known_args_and_warn,
+ valid_date,
+ plot_autoscale,
+)
+from gamestonk_terminal.config_plot import PLOT_DPI
+from gamestonk_terminal import feature_flags as gtff
+
+register_matplotlib_converters()
+
+
+def view_vix(other_args: List[str]):
+ """
+ Plot vix from a starting date using yfinance
+ Parameters
+ ----------
+ other_args: List[str]
+ List of argparse arguments
+ Returns
+ -------
+ Plots VIX historical data
+ """
+ parser = argparse.ArgumentParser(prog="vix_plot", add_help=False)
+ parser.add_argument(
+ "-s",
+ "--start",
+ type=valid_date,
+ default="2020-01-01",
+ dest="start_date",
+ help="The starting date (format YYYY-MM-DD) of the plot",
+ )
+
+ try:
+ ns_parser = parse_known_args_and_warn(parser, other_args)
+ if not ns_parser:
+ return
+
+ vix = yf.download("^VIX", start=ns_parser.start_date, progress=False)
+ _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
+ vix.plot(y="Adj Close", ax=ax)
+ plt.ylabel("VIX")
+ plt.grid(b=True, which="major", color="#666666", linestyle="-")
+ plt.minorticks_on()
+ plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)
+ plt.title("Historical VIX Data")
+
+ print("")
+
+ if gtff.USE_ION:
+ plt.ion()
+ plt.show()
+
+ except Exception as e:
+ print(e)
+ print("")
diff --git a/gamestonk_terminal/forex/README.md b/gamestonk_terminal/forex/README.md
index 5c27d28a4fd..5e127cee0a3 100644
--- a/gamestonk_terminal/forex/README.md
+++ b/gamestonk_terminal/forex/README.md
@@ -30,6 +30,14 @@ 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]
+* [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
@@ -108,13 +116,21 @@ Specify the instrument to use in the format EUR_USD
## candles
```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. (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. (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
```text
@@ -144,3 +160,38 @@ Plots the current orderbook for loaded instrument if one is provided by Oanda. N
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.
+
+## 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)
+
+## eda
+```text
+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 b1ab4dc7c68..b796f83ff25 100644
--- a/gamestonk_terminal/forex/fx_controller.py
+++ b/gamestonk_terminal/forex/fx_controller.py
@@ -6,6 +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, reddit_view
+from gamestonk_terminal.behavioural_analysis import ba_controller
+from gamestonk_terminal.exploratory_data_analysis import eda_controller
+import pandas as pd
+
account = cfg.OANDA_ACCOUNT
@@ -31,6 +36,10 @@ class ForexController:
"candles",
"pending",
"calendar",
+ "news",
+ "reddit",
+ "eda",
+ "ba",
]
def __init__(self):
@@ -69,6 +78,16 @@ 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(
+ " reddit search reddit for posts about the loaded instrument"
+ )
+ print(
+ " eda > exploratory data analysis, e.g.: decompose, cusum, residuals analysis"
+ )
+ print(
+ " ba > behavioural analysis, from: reddit, stocktwits, twitter, google"
+ )
print("")
def switch(self, an_input: str):
@@ -152,6 +171,28 @@ 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_reddit(self, other_args: List[str]):
+ instrument = fx_view.format_instrument(self.instrument, " ")
+ reddit_view.due_diligence(other_args, 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 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 cc4f1c59842..0ac34599671 100644
--- a/gamestonk_terminal/forex/fx_view.py
+++ b/gamestonk_terminal/forex/fx_view.py
@@ -12,8 +12,13 @@ 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
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
@@ -236,6 +241,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": {
@@ -250,37 +260,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]):
@@ -327,21 +325,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 +364,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:
@@ -417,8 +426,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 +478,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]):
@@ -515,71 +527,164 @@ 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 = []
+ 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
-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.bbands:
+ bbands = ta.bbands(df["Close"])
+ bbands = bbands.drop("BBB_5_2.0", axis=1)
+ bbands_plot = mpf.make_addplot(bbands, panel=0)
+ plots_to_add.append(bbands_plot)
+ legends.extend(["Lower BBand", "Middle BBand", "Upper BBand"])
+
+ if ns_parser.cci:
+ cci = ta.cci(df["High"], df["Low"], df["Close"])
+ cci_plot = mpf.make_addplot(cci, panel=panel_number)
+ plots_to_add.append(cci_plot)
+ subplot_legends.extend([panel_number * 2, ["CCI"]])
+ panel_number += 1
+
+ if ns_parser.ema:
+ ema = ta.ema(df["Close"])
+ ema_plot = mpf.make_addplot(ema, panel=0)
+ plots_to_add.append(ema_plot)
+ legends.append("10 EMA")
+
+ if ns_parser.rsi:
+ rsi = ta.rsi(df["Close"])
+ rsi_plot = mpf.make_addplot(rsi, panel=panel_number)
+ plots_to_add.append(rsi_plot)
+ subplot_legends.extend([panel_number * 2, ["RSI"]])
+ panel_number += 1
+
+ if ns_parser.obv:
+ obv = ta.obv(df["Close"], df["Volume"])
+ obv_plot = mpf.make_addplot(obv, panel=panel_number)
+ plots_to_add.append(obv_plot)
+ subplot_legends.extend([panel_number * 2, ["OBV"]])
+ panel_number += 1
+
+ if ns_parser.sma:
+ sma_length = [20, 50]
+ for length in sma_length:
+ sma = ta.sma(df["Close"], length=length)
+ sma_plot = mpf.make_addplot(sma, panel=0)
+ plots_to_add.append(sma_plot)
+ legends.append(f"{length} SMA")
+
+ if ns_parser.vwap:
+ vwap = ta.vwap(df["High"], df["Low"], df["Close"], df["Volume"])
+ vwap_plot = mpf.make_addplot(vwap, panel=0)
+ plots_to_add.append(vwap_plot)
+ legends.append("vwap")
+
+ return plots_to_add, legends, subplot_legends
def calendar(instrument, other_args: List[str]):
@@ -665,6 +770,38 @@ 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 +877,16 @@ 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("")
diff --git a/gamestonk_terminal/fred/__init__.py b/gamestonk_terminal/fred/__init__.py
deleted file mode 100644
index 844e04cb87e..00000000000
--- a/gamestonk_terminal/fred/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import fred_controller # noqa: F401
diff --git a/gamestonk_terminal/main_helper.py b/gamestonk_terminal/main_helper.py
index b253c8933d7..2c813d24a46 100644
--- a/gamestonk_terminal/main_helper.py
+++ b/gamestonk_terminal/main_helper.py
@@ -55,9 +55,7 @@ def print_help(s_ticker, s_start, s_interval, b_is_market_open):
" scr screener stocks, \t\t e.g. overview/performance, using preset filters"
)
print(" mill papermill menu, \t\t\t menu to generate notebook reports")
- print(
- " fred economic data, \t\t\t from: Federal Reserve Bank of St. Louis "
- )
+ print(" econ economic data, \t\t\t from: FRED, VIX")
print(" pa portfolio analysis, \t\t supports: robinhood, alpaca, ally ")
print(" crypto cryptocurrencies, \t\t uses coingecko api")
print(
diff --git a/terminal.py b/terminal.py
index 923865dbf28..6765b0f68f5 100644
--- a/terminal.py
+++ b/terminal.py
@@ -26,7 +26,7 @@ from gamestonk_terminal.technical_analysis import ta_controller
from gamestonk_terminal.comparison_analysis import ca_controller
from gamestonk_terminal.exploratory_data_analysis import eda_controller
from gamestonk_terminal.options import op_controller
-from gamestonk_terminal.fred import fred_controller
+from gamestonk_terminal.econ import econ_controller
from gamestonk_terminal.residuals_analysis import ra_controller
from gamestonk_terminal.portfolio import port_controller
from gamestonk_terminal.cryptocurrency import crypto_controller
@@ -83,7 +83,7 @@ def main():
"pred",
"ca",
"op",
- "fred",
+ "econ",
"pa",
"crypto",
"ra",
@@ -281,8 +281,8 @@ def main():
s_ticker, df_stock["5. adjusted close"].values[-1]
)
- elif ns_known_args.opt == "fred":
- b_quit = fred_controller.menu()
+ elif ns_known_args.opt == "econ":
+ b_quit = econ_controller.menu()
elif ns_known_args.opt == "pa":
b_quit = port_controller.menu()
diff --git a/tests/test_fred_fred_view.py b/tests/test_fred_fred_view.py
index 96b97919c1b..9187649de93 100644
--- a/tests/test_fred_fred_view.py
+++ b/tests/test_fred_fred_view.py
@@ -1,11 +1,11 @@
-""" fred/fred_view.py tests """
+""" econ/fred_view.py tests """
import unittest
from unittest import mock
from io import StringIO
import pandas as pd
# pylint: disable=unused-import
-from gamestonk_terminal.fred.fred_view import get_fred_data # noqa: F401
+from gamestonk_terminal.econ.fred_view import get_fred_data # noqa: F401
fred_data_mock = """
,GDP
@@ -21,7 +21,7 @@ fred_data_mock = """
class TestFredFredView(unittest.TestCase):
- @mock.patch("gamestonk_terminal.fred.fred_view.Fred.get_series")
+ @mock.patch("gamestonk_terminal.econ.fred_view.Fred.get_series")
def test_get_fred_data(self, mock_get_series):
fred_data = pd.read_csv(StringIO(fred_data_mock), header=0, index_col=0)