diff --git a/ROADMAP.md b/ROADMAP.md
index 57d332b0f50..699d5329397 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -141,13 +141,12 @@
## Portfolio Analysis
### Short term
-* [ ] Add Brokers (td, alpaca, webull, etc)
-* [ ] Merge data from different brokers
-* [ ] Refactor
+- [x] Add alpaca (@jmaslek) - [PR #259](https://github.com/DidierRLopes/GamestonkTerminal/pull/229)
- [x] Add robinhood (@jmaslek) - [PR #229](https://github.com/DidierRLopes/GamestonkTerminal/pull/229)
-- [ ] Add Brokers (td, alpaca, webull, etc) (@jmaslek)
+- [ ] Add Brokers (td, webull, etc) (@jmaslek)
- [ ] Merge data from different brokers (@jmaslek)
+- [ ] Refactoring
### Long term
diff --git a/gamestonk_terminal/portfolio/README.md b/gamestonk_terminal/portfolio/README.md
index 52eb70cf214..4ba78a602bf 100644
--- a/gamestonk_terminal/portfolio/README.md
+++ b/gamestonk_terminal/portfolio/README.md
@@ -4,6 +4,7 @@ This page shows the available brokers for loading in data. If yours is not list
Current brokers:
* Robinhood
+* Alpaca
Once this screen is accessed, the first command to be run is
@@ -15,31 +16,40 @@ The brokers should just be your brokers from the list shown on the first menu.
````
login rh
````
-When td is added, the command will be
+To login to both robinhood and alpaca:
````
-login rh td
+login rh alp
````
After logging in, the help menu will display which brokers you logged into.
-Your login information should be stored as environment variables in [config file](/gamestonk_terminal/config_terminal.py)
-
-Robinhood has Two Factor Authentication, so you will likely be prompted to enter a code that is texted/emailed to you.
-###NOTE THAT LOGGING IN WILL SAVE A TOKEN TO `os.path.expanduser("~")/.tokens` WHICH CAN BE USED TO LOGIN EVEN IF USER CREDENTIALS ARE INCORRECT.
+Your login information should be stored as environment variables in [config file](/gamestonk_terminal/config_terminal.py), but check each individual section for specifics.
[ROBINHOOD](#ROBINHOOD)
-* [hold](#hold)
+* [rhhold](#rhhold)
* Look at current (stock only) holdings
* [rhhist](#rhhist)
* Get and plot historical portfolio
+
+[ALPACA](#ALPACA)
+* [alphold](#alphold)
+ * Look at current (stock only) holdings
+* [alphist](#rhhist)
+ * Get and plot historical portfolio
## ROBINHOOD
+Robinhood has Two Factor Authentication, so you will likely be prompted to enter a code that is texted/emailed to you.
+###NOTE THAT LOGGING IN WILL SAVE A TOKEN TO `os.path.expanduser("~")/.tokens` WHICH CAN BE USED TO LOGIN EVEN IF USER CREDENTIALS ARE INCORRECT.
-### hold
+In order to login, the [config file](/gamestonk_terminal/config_terminal.py) requires two variables:
+* "GT_RH_USERNAME"
+* "GT_RH_PASSWORD"
+
+### rhhold
Displays current holdings to the console:
````
-usage: hold
+usage: rhhold
````
There are no additional flags. This just prints all current stonks, their last price, previous close, your equity and
the % change from previous close.
@@ -60,4 +70,39 @@ usage: rhhist [-s --span] [-i --interval]
Example Default Output:
-
\ No newline at end of file
+
+## ALPACA
+
+Alpaca has a nicely maintained python API that is used here. To login, the best approach is to just save the following environment variables:
+* "APCA_API_BASE_URL"
+* "APCA_API_KEY_ID"
+* "APCA_API_SECRET_KEY"
+
+If these are defined, then the login command does not explicitly need to be run, but the login command verifies all those keys are defined.
+
+Note that alpaca does support paper trading, so if your base_url is "https://paper-api.alpaca.markets", then this will be importing
+your paper trading account.
+
+### alphold
+
+Displays current holdings to the console. Same as rhhold
+
+````
+usage: alphold
+````
+There are no additional flags. This just prints all current stonks, their last price, previous close, your equity and
+the % change from previous close.
+
+Example Output:
+
+
+### alphist
+Plots historical portfolio. The output plot in this case will not be a candle, as the api provides your equity at a gven time,
+unlike the robin_stocks, which gives high/low at a given time.
+
+````
+usage: alphist [-p --period] [-t --timeframe]
+````
+
+* -p/--period The duration of the data in \ + \, such as 1D, where can be D for day, W for week, M for month and A for year. Defaults to 1M
+* -t/--timeframe The resolution of time window. 1Min, 5Min, 15Min, 1H, or 1D. If omitted, 1Min for less than 7 days period, 15Min for less than 30 days, or otherwise 1D.
diff --git a/gamestonk_terminal/portfolio/alp_api.py b/gamestonk_terminal/portfolio/alp_api.py
new file mode 100644
index 00000000000..2583f6c628c
--- /dev/null
+++ b/gamestonk_terminal/portfolio/alp_api.py
@@ -0,0 +1,97 @@
+import argparse
+import os
+from pandas.plotting import register_matplotlib_converters
+import matplotlib.pyplot as plt
+import alpaca_trade_api as alp_api
+from termcolor import colored
+from gamestonk_terminal.config_plot import PLOT_DPI
+from gamestonk_terminal.helper_funcs import (
+ parse_known_args_and_warn,
+ plot_autoscale,
+)
+from gamestonk_terminal import feature_flags as gtff
+
+register_matplotlib_converters()
+
+
+def login():
+
+ if not (
+ os.getenv("APCA_API_KEY_ID")
+ and os.getenv("APCA_API_SECRET_KEY")
+ and os.getenv("APCA_API_BASE_URL")
+ ):
+ raise ValueError("Alpaca Keys not defined in env")
+
+
+def show_holdings():
+ api = alp_api.REST()
+ positions = api.list_positions()
+ print("")
+ print("Stonk\t last price \t prev close \t equity \t % Change")
+
+ for pos in positions:
+ stonk = pos.symbol
+ last_price = round(float(pos.current_price), 2)
+ prev_close = round(float(pos.lastday_price), 2)
+ eq = round(float(pos.market_value), 2)
+ pct_change = round(float(pos.change_today), 3)
+ to_print = f"{stonk}\t {last_price}\t\t {prev_close}\t\t {eq}\t\t {pct_change}"
+ if pct_change >= 0:
+ print(colored(to_print, "green"))
+ else:
+ print(colored(to_print, "red"))
+ print("")
+
+
+def plot_historical(l_args):
+ api = alp_api.REST()
+ parser = argparse.ArgumentParser(
+ add_help=False,
+ prog="Port",
+ description="""
+ Historical Portfolio Info
+ """,
+ )
+
+ parser.add_argument(
+ "-p",
+ "--period",
+ dest="period",
+ type=str,
+ default="1M",
+ help="Duration of data ( + )",
+ )
+
+ parser.add_argument(
+ "-t",
+ "--timeframe",
+ dest="timeframe",
+ default="1D",
+ type=str,
+ help="Resolution of data ( + )",
+ )
+
+ try:
+ ns_parser = parse_known_args_and_warn(parser, l_args)
+ port_hist = api.get_portfolio_history(
+ period=ns_parser.period, timeframe=ns_parser.timeframe
+ ).df
+
+ plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
+ plt.plot(port_hist.index, port_hist.equity)
+ plt.xlim(port_hist.index[0], port_hist.index[-1])
+ plt.ylabel("Equity")
+ 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.show()
+
+ if gtff.USE_ION:
+ plt.ion()
+ print("")
+
+ except Exception as e:
+ print(e)
+ print("")
+ return
diff --git a/gamestonk_terminal/portfolio/port_controller.py b/gamestonk_terminal/portfolio/port_controller.py
index 597ffeeb094..b8c533fb76e 100644
--- a/gamestonk_terminal/portfolio/port_controller.py
+++ b/gamestonk_terminal/portfolio/port_controller.py
@@ -6,33 +6,25 @@ 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.portfolio import rh_api
+from gamestonk_terminal.portfolio import rh_api, alp_api
class PortfolioController:
- CHOICES = [
- "help",
- "q",
- "quit",
- "login",
- "hold",
- "rhhist",
- ]
+ CHOICES = ["help", "q", "quit", "login", "rhhold", "rhhist", "alphold", "alphist"]
BROKERS = [
"rh",
+ "alp",
]
def __init__(self):
self.port_parser = argparse.ArgumentParser(add_help=False, prog="port")
self.port_parser.add_argument("cmd", choices=self.CHOICES)
- self.brokers_list = set()
+ self.broker_list = set()
@staticmethod
def print_help(broker_list):
- if not broker_list:
- broker_list = [" None"]
""" Print help """
print("\nPortfolio:")
@@ -42,24 +34,33 @@ class PortfolioController:
)
print(" quit quit to abandon program, logs out of brokers")
print(" login login to your brokers")
- print(f"\nCurrent Brokers : {('', ', '.join(broker_list))[bool(broker_list)]}")
+ print(
+ f"\nCurrent Brokers : {('None', ', '.join(broker_list))[bool(broker_list)]}"
+ )
print("\nCurrently Supported :")
print(" rh Robinhood - fuck these guys")
+ print(" alp Alpaca ")
print("\nCommands (login required):")
- print("")
- print(" hold view rh holdings")
+ print("\nRobinhood:")
+ print(" rhhold view rh holdings")
print(" rhhist plot rh portfolio history")
+ print("\nAlpaca:")
+ print(" alphold view alp holdings")
+ print(" alphist view alp portfolio history")
print("")
def print_portfolio_menu(self):
- print_broke = " "
- for broker in self.brokers_list:
- print_broke += broker + " "
- print("\nCurrent Brokers :" + print_broke)
- print("")
- print(" hold check holdings")
+ print(
+ f"\nCurrent Brokers : {('None', ', '.join(self.broker_list))[bool(self.broker_list)]}"
+ )
+ print("\nRobinhood:")
+ print(" rhhold check holdings")
print(" rhhist plot historical RH portfolio")
+ print("\nAlpaca:")
+ print(" alphold view alpaca holdings")
+ print(" alphist plot historical alpaca portfolio")
+
print("")
def switch(self, an_input: str):
@@ -80,7 +81,7 @@ class PortfolioController:
def call_help(self, _):
"""Process Help command"""
- self.print_help(self.brokers_list)
+ self.print_help(self.broker_list)
def call_q(self, _):
"""Process Q command - quit the menu"""
@@ -102,7 +103,7 @@ class PortfolioController:
api = broker + "_api"
try:
eval(api + ".login()")
- self.brokers_list.add(broker)
+ self.broker_list.add(broker)
logged_in = True
except Exception as e:
print("")
@@ -119,19 +120,29 @@ class PortfolioController:
def call_rhhist(self, other_args: List[str]):
rh_api.plot_historical(other_args)
- def call_hold(self, _):
+ def call_rhhold(self, _):
try:
rh_api.show_holdings()
except Exception as e:
print(e)
print("")
+ def call_alphold(self, _):
+ try:
+ alp_api.show_holdings()
+ except Exception as e:
+ print(e)
+ print("")
+
+ def call_alphist(self, other_args: List[str]):
+ alp_api.plot_historical(other_args)
+
def menu():
"""Portfolio Analysis Menu"""
port_controller = PortfolioController()
- port_controller.print_help(port_controller.brokers_list)
+ port_controller.print_help(port_controller.broker_list)
while True:
# Get input command from user