Merge pull request #259 from jmaslek/port_alp

Add Alpaca Broker
This commit is contained in:
jmaslek
2021-03-22 16:19:47 -04:00
committed by GitHub
4 changed files with 191 additions and 39 deletions

View File

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

View File

@@ -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 <a name="ROBINHOOD"></a>
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 <a name="hold"></a>
In order to login, the [config file](/gamestonk_terminal/config_terminal.py) requires two variables:
* "GT_RH_USERNAME"
* "GT_RH_PASSWORD"
### rhhold <a name="rhhold"></a>
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:
![rhhist](https://user-images.githubusercontent.com/18151143/111718919-36da8e00-8831-11eb-99e1-957c8eccb583.png)
![rhhist](https://user-images.githubusercontent.com/18151143/111718919-36da8e00-8831-11eb-99e1-957c8eccb583.png)
## ALPACA <a name="ALPACA"></a>
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 <a name="alphold"></a>
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:
![](https://user-images.githubusercontent.com/18151143/112039340-536d1380-8b1a-11eb-99d1-44d1edd4d54a.png)
### alphist <a name="alphist"></a>
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 \<number> + \<unit>, such as 1D, where <unit> 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.

View File

@@ -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 (<number> + <unit>)",
)
parser.add_argument(
"-t",
"--timeframe",
dest="timeframe",
default="1D",
type=str,
help="Resolution of data (<number> + <unit>)",
)
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

View File

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