from IPython.display import HTML
HTML('''<script>
code_show=true;
function code_toggle() {
if (code_show){
$('div.input').hide();
} else {
$('div.input').show();
}
code_show = !code_show
}
$( document ).ready(code_toggle);
</script>
<form action="javascript:code_toggle()"><input type="submit" value="Click here to toggle on/off the raw code."></form>''')
First, we have to get all the imports out of the way and then log in to our Robin_stocks account.
Note: I separately created a robin_credentials json file with my login info. I recommend you take a similar approach in case you intend to share your code.
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output, Input
import dash_renderer
import dash_table
import plotly
import plotly.graph_objs as go
import random
from collections import deque
import robin_stocks as rs
import pandas as pd
import matplotlib.pyplot as plt
with open("robin_credentials.json", "r") as file:
creds = json.load(file)
# Instantiate the rs login to login to your Robinhood account.
rs.login(creds['USER_ID'], creds['PASSCODE'])
# you can look at the list of your stocks
my_stocks = rs.build_holdings()
my_stocks
For instance, you can use build_holdings as shown above to look at your portfolio. You can use order_buy_market to make conditional stock purchases. You can build_user_profile and order_sell_limit to create user profiles and make limit sells of specific stocks you hold once it reaches a certain price point.
A few examples are provided at the quick start section of the robin_stocks website here.
df = pd.DataFrame(my_stocks)
df
# The dataframe looks off with the variable names/features as index and the ticker as the column, so let's fix that first
df = df.T
df['ticker'] = df.index
df = df.reset_index(drop=True)
df.head()
# We can look at the ticker price history over the course of the past 'x' span where x can be day, week, year, or 5year
for tick in df['ticker']:
data = rs.stocks.get_historicals(tick,span='year',bounds='regular')
tick_df = pd.DataFrame(data)
#switching to floats so pyplot can read the numbers
column_list = ['close_price','high_price','low_price','open_price']
# tick_df['portfolio_share'] = tick_df['close_price']*df['quantity'].astype(float)
for i in column_list:
tick_df[i] = tick_df[i].astype(float)
# I will look at the close_price for now, however, we can look for other things too that might interest us. For instance, we can look at the changes in high_price
# We can also track the percent_change, equity_change, or pe_ratio of each of our stocks
tick_df['portfolio_share'].plot(legend=True,figsize=(20,5))
# tick_df['open_price'].plot(legend=True,figsize=(12,5))
# tick_df['high_price'].plot(legend=True,figsize=(12,5))
# tick_df['low_price'].plot(legend=True,figsize=(12,5))
# We can look at the ticker price history over the course of the past 'x' span where x can be day, week, year, or 5year
for tick in df['ticker']:
data = rs.stocks.get_historicals(tick,span='year',bounds='regular')
tick_df = pd.DataFrame(data)
#switching to floats so pyplot can read the numbers
column_list = ['close_price','high_price','low_price','open_price']
for i in column_list:
tick_df[i] = tick_df[i].astype(float)
# I will look at the close_price for now, however, we can look for other things too that might interest us. For instance, we can look at the changes in high_price
# We can also track the percent_change, equity_change, or pe_ratio of each of our stocks
tick_df['close_price'].plot(legend=True,figsize=(20,5))
# tick_df['open_price'].plot(legend=True,figsize=(12,5))
# tick_df['high_price'].plot(legend=True,figsize=(12,5))
# tick_df['low_price'].plot(legend=True,figsize=(12,5))
rs.profiles.load_account_profile()
import datetime
date_created = rs.profiles.load_account_profile(info='created_at')
date_object = datetime.datetime.strptime(date_created, '%Y-%m-%dT%H:%M:%S.%fZ').date()
print(date_object)
new_fmt = "%Y, %m, %d"
print (date_object.strftime(new_fmt))
start_date = date_object.strftime(new_fmt)
# Alternatively, you could use the approach below for a standard python timetuple
# start_date = date_object.timetuple()
# start_date
# We can get the start year, month, and day for our profile by doing the following
start_year = int(start_date[0:4])
start_year
start_month = int(start_date[6:8])
start_month
start_day = int(start_date[9:])
start_day
import matplotlib as mpl
mpl.get_backend()
import datetime as dt
import matplotlib.pyplot as plt
from matplotlib import style
import pandas as pd
import pandas_datareader.data as web
from mpl_finance import candlestick_ohlc
import matplotlib.dates as mdates
%matplotlib inline
style.use('ggplot')
# Let's inspect data from 2015 through now. I arbitrarily picked Jan 1st of 2015. However, you may chose to start it from any other day/date.
start = datetime.datetime(start_year,start_month,start_day)
end = datetime.datetime.now()
for tick in df['ticker']:
df_new = web.get_data_yahoo(tick, start, end)
df_new.reset_index(inplace=True)
df_new.set_index("Date", inplace=True)
print(df_new.head())
df_new.to_csv(tick + '.csv')
df_new = pd.read_csv(tick + '.csv', parse_dates=True, index_col=0)
df_new.plot()
df_new['Adj Close'].plot()
plt.show()
df_new['100ma'] = df_new['Adj Close'].rolling(window=100).mean()
print(df.head())
df_new['100ma'] = df_new['Adj Close'].rolling(window=100,min_periods=0).mean()
print(df.head())
ax1 = plt.subplot2grid((8,1), (0,0), rowspan=5, colspan=1)
ax2 = plt.subplot2grid((8,1), (5,0), rowspan=1, colspan=1,sharex=ax1)
ax1.plot(df_new.index, df_new['Adj Close'])
ax1.plot(df_new.index, df_new['100ma'])
ax2.bar(df_new.index, df_new['Volume'])
plt.show()
df_ohlc = df_new['Adj Close'].resample('10D').ohlc()
df_volume = df_new['Volume'].resample('10D').sum()
print(df_ohlc.head())
df_ohlc = df_ohlc.reset_index()
df_ohlc['Date'] = df_ohlc['Date'].map(mdates.date2num)
fig = plt.figure()
ax1 = plt.subplot2grid((8,1), (0,0), rowspan=5, colspan=1)
ax2 = plt.subplot2grid((8,1), (5,0), rowspan=1, colspan=1,sharex=ax1)
ax1.xaxis_date
candlestick_ohlc(ax1, df_ohlc.values, width=4, colorup='g')
ax2.fill_between(df_volume.index.map(mdates.date2num),df_volume.values,0)
plt.show()
df_ohlc = df['Adj Close'].resample('10D').ohlc()
df_volume = df['Volume'].resample('10D').sum()
print(df_ohlc.head())
df_ohlc = df_ohlc.reset_index()
from mpl_finance import candlestick_ohlc
import matplotlib.dates as mdates
df_ohlc['Date'] = df_ohlc['Date'].map(mdates.date2num)
fig = plt.figure()
ax1 = plt.subplot2grid((6,1), (0,0), rowspan=5, colspan=1)
ax2 = plt.subplot2grid((6,1), (5,0), rowspan=1, colspan=1,sharex=ax1)
ax1.xaxis_date
candlestick_ohlc(ax1, df_ohlc.values, width=2, colorup='g')
ax2.fill_between(df_volume.index.map(mdates.date2num),df_volume.values,0)
plt.show()
import bs4 as bs
import datetime as dt
import matplotlib.pyplot as plt
from matplotlib import style
import numpy as np
import os
import pandas as pd
import pandas_datareader.data as web
import pickle
import requests
style.use('ggplot')
def save_sp500_tickers():
resp = requests.get('http://en.wikipedia.org/wiki/List_of_S%26P_500_companies')
soup = bs.BeautifulSoup(resp.text, 'lxml')
table = soup.find('table', {'class': 'wikitable sortable'})
tickers = []
for row in table.findAll('tr')[1:]:
ticker = row.findAll('td')[0].text
tickers.append(ticker)
with open("sp500tickers.pickle", "wb") as f:
pickle.dump(tickers, f)
return tickers
# save_sp500_tickers()
def get_data_from_yahoo(reload_sp500=False):
if reload_sp500:
tickers = save_sp500_tickers()
else:
with open("sp500tickers.pickle", "rb") as f:
tickers = pickle.load(f)
if not os.path.exists('stock_dfs'):
os.makedirs('stock_dfs')
start = dt.datetime(2010, 1, 1)
end = dt.datetime.now()
for ticker in tickers:
# just in case your connection breaks, we'd like to save our progress!
if not os.path.exists('stock_dfs/{}.csv'.format(ticker)):
df = web.DataReader(ticker, 'morningstar', start, end)
df.reset_index(inplace=True)
df.set_index("Date", inplace=True)
df = df.drop("Symbol", axis=1)
df.to_csv('stock_dfs/{}.csv'.format(ticker))
else:
print('Already have {}'.format(ticker))
def compile_data():
with open("sp500tickers.pickle", "rb") as f:
tickers = pickle.load(f)
main_df = pd.DataFrame()
for count, ticker in enumerate(tickers):
df = pd.read_csv('stock_dfs/{}.csv'.format(ticker))
df.set_index('Date', inplace=True)
df.rename(columns={'Adj Close': ticker}, inplace=True)
df.drop(['Open', 'High', 'Low', 'Close', 'Volume'], 1, inplace=True)
if main_df.empty:
main_df = df
else:
main_df = main_df.join(df, how='outer')
if count % 10 == 0:
print(count)
print(main_df.head())
main_df.to_csv('sp500_joined_closes.csv')
def visualize_data():
df = pd.read_csv('sp500_joined_closes.csv')
df_corr = df.corr()
print(df_corr.head())
df_corr.to_csv('sp500corr.csv')
data1 = df_corr.values
fig1 = plt.figure()
ax1 = fig1.add_subplot(111)
heatmap1 = ax1.pcolor(data1, cmap=plt.cm.RdYlGn)
fig1.colorbar(heatmap1)
ax1.set_xticks(np.arange(data1.shape[1]) + 0.5, minor=False)
ax1.set_yticks(np.arange(data1.shape[0]) + 0.5, minor=False)
ax1.invert_yaxis()
ax1.xaxis.tick_top()
column_labels = df_corr.columns
row_labels = df_corr.index
ax1.set_xticklabels(column_labels)
ax1.set_yticklabels(row_labels)
plt.xticks(rotation=90)
heatmap1.set_clim(-1, 1)
plt.tight_layout()
plt.show()
visualize_data()
import robin_stocks as rs
import pandas as pd
import json
def update_trade_history(symbols, holdings_data, file_name):
""" Writes data about a trade to a JSON file, containing the sell date, buy date,
price at which the stock was bought and sold at, etc.
Args:
symbols(list): List of strings, strings are the symbols of the stocks we've just sold and want to write data for.
holdings_data(dict): dict obtained from get_modified_holdings() method. We need this method rather than r.build_holdings() to get a stock's buying date
file_name(str): name of the file we are writing the data to. Should be "tradehistory.txt" if this method is normally called by scan_stocks().
If you want to write to another file, create a new text file with two empty brackets with an empty line between them, to meet JSON formatting standards.
"""
with open(file_name) as json_file:
data = json.load(json_file)
current_time = str(pd.Timestamp("now"))
data[current_time] = ({})
for symbol in symbols:
data[current_time].update({symbol: holdings_data[symbol]})
with open(file_name, 'w') as outfile:
json.dump(data, outfile)
def read_trade_history(file_name):
""" Reads data about previous trades from JSON file and prints it out
Args:
file_name(str): name of the file we are reading from. Should be "tradehistory.txt" by default
"""
with open(file_name) as json_file:
data = json.load(json_file)
for sell_date, event in data.items():
print(sell_date + ": ")
for symbol, dict in event.items():
quantity, price, change, percent, bought_at = str(int(float(dict.get("quantity")))), dict.get("price"), dict.get("equity_change"), dict.get("percent_change"), dict.get("bought_at")
print("\tSold " + quantity + " shares of "+ symbol + " at " + price + ", " + change + " (" +
percent + "%) profit/loss, bought on " + bought_at)
def get_total_gains_minus_dividends():
""" Returns the amount of money you've gained/lost through trading since the creation of your account, minus dividends
"""
profileData = rs.load_portfolio_profile()
print(profileData)
allTransactions = rs.get_bank_transfers()
deposits = sum(float(x['amount']) for x in allTransactions if (x['direction'] == 'deposit')) # and (x['state'] == 'completed'))
withdrawals = sum(float(x['amount']) for x in allTransactions if (x['direction'] == 'withdraw') and (x['state'] == 'completed'))
money_invested = deposits - withdrawals
print(deposits)
dividends = rs.get_total_dividends()
percentDividend = dividends/money_invested*100
totalGainMinusDividends =float(profileData['extended_hours_equity'])-dividends-money_invested
return totalGainMinusDividends
import robin_stocks as r
import pandas as pd
import numpy as np
from pandas.plotting import register_matplotlib_converters
import ta as ta
from ta import *
from misc import *
from tradingstats import *
#Log in to Robinhood
login = r.login(creds['USER_ID'], creds['PASSCODE'])
def get_watchlist_symbols():
"""
Returns: the symbol for each stock in your watchlist as a list of strings
"""
my_list_names = []
symbols = []
for name in r.get_all_watchlists(info='name'):
my_list_names.append(name)
for name in my_list_names:
list = r.get_watchlist_by_name(name)
for item in list:
instrument_data = r.get_instrument_by_url(item.get('instrument'))
symbol = instrument_data['symbol']
symbols.append(symbol)
return symbols
def get_portfolio_symbols():
"""
Returns: the symbol for each stock in your portfolio as a list of strings
"""
symbols = []
holdings_data = r.get_current_positions()
for item in holdings_data:
if not item:
continue
instrument_data = r.get_instrument_by_url(item.get('instrument'))
symbol = instrument_data['symbol']
symbols.append(symbol)
return symbols
def get_position_creation_date(symbol, holdings_data):
"""Returns the time at which we bought a certain stock in our portfolio
Args:
symbol(str): Symbol of the stock that we are trying to figure out when it was bought
holdings_data(dict): dict returned by r.get_current_positions()
Returns:
A string containing the date and time the stock was bought, or "Not found" otherwise
"""
instrument = r.get_instruments_by_symbols(symbol)
url = instrument[0].get('url')
for dict in holdings_data:
if(dict.get('instrument') == url):
return dict.get('created_at')
return "Not found"
def get_modified_holdings():
""" Retrieves the same dictionary as r.build_holdings, but includes data about
when the stock was purchased, which is useful for the read_trade_history() method
in tradingstats.py
Returns:
the same dict from r.build_holdings, but with an extra key-value pair for each
position you have, which is 'bought_at': (the time the stock was purchased)
"""
holdings = r.build_holdings()
holdings_data = r.get_current_positions()
for symbol, dict in holdings.items():
bought_at = get_position_creation_date(symbol, holdings_data)
bought_at = str(pd.to_datetime(bought_at))
holdings[symbol].update({'bought_at': bought_at})
return holdings
def get_last_crossing(df, days, symbol="", direction=""):
"""Searches for a crossing between two indicators for a given stock
Args:
df(pandas.core.frame.DataFrame): Pandas dataframe with columns containing the stock's prices, both indicators, and the dates
days(int): Specifies the maximum number of days that the cross can occur by
symbol(str): Symbol of the stock we're querying. Optional, used for printing purposes
direction(str): "above" if we are searching for an upwards cross, "below" if we are searching for a downwaords cross. Optional, used for printing purposes
Returns:
1 if the short-term indicator crosses above the long-term one
0 if there is no cross between the indicators
-1 if the short-term indicator crosses below the long-term one
"""
prices = df.loc[:,"Price"]
shortTerm = df.loc[:,"Indicator1"]
LongTerm = df.loc[:,"Indicator2"]
dates = df.loc[:,"Dates"]
lastIndex = prices.size - 1
index = lastIndex
found = index
recentDiff = (shortTerm.at[index] - LongTerm.at[index]) >= 0
if((direction == "above" and not recentDiff) or (direction == "below" and recentDiff)):
return 0
index -= 1
while(index >= 0 and found == lastIndex and not np.isnan(shortTerm.at[index]) and not np.isnan(LongTerm.at[index]) \
and ((pd.Timestamp("now", tz='UTC') - dates.at[index]) <= pd.Timedelta(str(days) + " days"))):
if(recentDiff):
if((shortTerm.at[index] - LongTerm.at[index]) < 0):
found = index
else:
if((shortTerm.at[index] - LongTerm.at[index]) > 0):
found = index
index -= 1
if(found != lastIndex):
if((direction == "above" and recentDiff) or (direction == "below" and not recentDiff)):
print(symbol + ": Short SMA crossed" + (" ABOVE " if recentDiff else " BELOW ") + "Long SMA at " + str(dates.at[found]) \
+", which was " + str(pd.Timestamp("now", tz='UTC') - dates.at[found]) + " ago", ", price at cross: " + str(prices.at[found]) \
+ ", current price: " + str(prices.at[lastIndex]))
return (1 if recentDiff else -1)
else:
return 0
def five_year_check(stockTicker):
"""Figure out if a stock has risen or been created within the last five years.
Args:
stockTicker(str): Symbol of the stock we're querying
Returns:
True if the stock's current price is higher than it was five years ago, or the stock IPO'd within the last five years
False otherwise
"""
instrument = r.get_instruments_by_symbols(stockTicker)
list_date = instrument[0].get("list_date")
if ((pd.Timestamp("now") - pd.to_datetime(list_date)) < pd.Timedelta("5 Y")):
return True
fiveyear = r.get_historicals(stockTicker,span='5year',bounds='regular')
closingPrices = []
for item in fiveyear:
closingPrices.append(float(item['close_price']))
recent_price = closingPrices[len(closingPrices) - 1]
oldest_price = closingPrices[0]
return (recent_price > oldest_price)
def golden_cross(stockTicker, n1, n2, days, direction=""):
"""Determine if a golden/death cross has occured for a specified stock in the last X trading days
Args:
stockTicker(str): Symbol of the stock we're querying
n1(int): Specifies the short-term indicator as an X-day moving average.
n2(int): Specifies the long-term indicator as an X-day moving average.
(n1 should be smaller than n2 to produce meaningful results, e.g n1=50, n2=200)
days(int): Specifies the maximum number of days that the cross can occur by
direction(str): "above" if we are searching for an upwards cross, "below" if we are searching for a downwaords cross. Optional, used for printing purposes
Returns:
1 if the short-term indicator crosses above the long-term one
0 if there is no cross between the indicators
-1 if the short-term indicator crosses below the long-term one
False if direction == "above" and five_year_check(stockTicker) returns False, meaning that we're considering whether to
buy the stock but it hasn't risen overall in the last five years, suggesting it contains fundamental issues
"""
if(direction == "above" and not five_year_check(stockTicker)):
return False
history = r.get_historicals(stockTicker,span='year',bounds='regular')
closingPrices = []
dates = []
for item in history:
closingPrices.append(float(item['close_price']))
dates.append(item['begins_at'])
price = pd.Series(closingPrices)
dates = pd.Series(dates)
dates = pd.to_datetime(dates)
sma1 = ta.volatility.bollinger_mavg(price, n=int(n1), fillna=False)
sma2 = ta.volatility.bollinger_mavg(price, n=int(n2), fillna=False)
series = [price.rename("Price"), sma1.rename("Indicator1"), sma2.rename("Indicator2"), dates.rename("Dates")]
df = pd.concat(series, axis=1)
cross = get_last_crossing(df, days, symbol=stockTicker, direction=direction)
# if(cross):
# show_plot(price, sma1, sma2, dates, symbol=stockTicker, label1=str(n1)+" day SMA", label2=str(n2)+" day SMA")
return cross
df['30_MA_Close'] = df['Close'].rolling(window=30).mean()
#calculating 20 days rolling standard devtaion
df['20_std_Close'] = df['Close'].rolling(window=20).std()
def sell_holdings(symbol, holdings_data):
""" Place an order to sell all holdings of a stock.
Args:
symbol(str): Symbol of the stock we want to sell
holdings_data(dict): dict obtained from get_modified_holdings() method
"""
shares_owned = int(float(holdings_data[symbol].get("quantity")))
r.order_sell_market(symbol, shares_owned)
print("####### Selling " + str(shares_owned) + " shares of " + symbol + " #######")
def buy_holdings(potential_buys, profile_data, holdings_data):
""" Places orders to buy holdings of stocks. This method will try to order
an appropriate amount of shares such that your holdings of the stock will
roughly match the average for the rest of your portfoilio. If the share
price is too high considering the rest of your holdings and the amount of
buying power in your account, it will not order any shares.
Args:
potential_buys(list): List of strings, the strings are the symbols of stocks we want to buy
symbol(str): Symbol of the stock we want to sell
holdings_data(dict): dict obtained from r.build_holdings() or get_modified_holdings() method
"""
cash = float(profile_data.get('cash'))
portfolio_value = float(profile_data.get('equity')) - cash
ideal_position_size = (portfolio_value/len(holdings_data)+cash/len(potential_buys))/(2 * len(potential_buys))
prices = r.get_latest_price(potential_buys)
for i in range(0, len(potential_buys)):
stock_price = float(prices[i])
if(ideal_position_size < stock_price < ideal_position_size*1.5):
num_shares = int(ideal_position_size*1.5/stock_price)
elif (stock_price < ideal_position_size):
num_shares = int(ideal_position_size/stock_price)
else:
print("####### Tried buying shares of " + potential_buys[i] + ", but not enough buying power to do so#######")
break
print("####### Buying " + str(num_shares) + " shares of " + potential_buys[i] + " #######")
r.order_buy_market(potential_buys[i], num_shares)
def scan_stocks():
""" The main method. Sells stocks in your portfolio if their 50 day moving average crosses
below the 200 day, and buys stocks in your watchlist if the opposite happens.
###############################################################################################
WARNING: Comment out the sell_holdings and buy_holdings lines if you don't actually want to execute the trade.
###############################################################################################
If you sell a stock, this updates tradehistory.txt with information about the position,
how much you've earned/lost, etc.
"""
print("----- Starting scan... -----\n")
register_matplotlib_converters()
watchlist_symbols = get_watchlist_symbols()
portfolio_symbols = get_portfolio_symbols()
holdings_data = get_modified_holdings()
potential_buys = []
sells = []
print("Current Portfolio: " + str(portfolio_symbols) + "\n")
print("Current Watchlist: " + str(watchlist_symbols) + "\n")
print("----- Scanning portfolio for stocks to sell -----\n")
for symbol in portfolio_symbols:
cross = golden_cross(symbol, n1=50, n2=200, days=30, direction="below")
if(cross == -1):
sell_holdings(symbol, holdings_data)
sells.append(symbol)
profile_data = r.build_user_profile()
print("\n----- Scanning watchlist for stocks to buy -----\n")
for symbol in watchlist_symbols:
if(symbol not in portfolio_symbols):
cross = golden_cross(symbol, n1=50, n2=200, days=10, direction="above")
if(cross == 1):
potential_buys.append(symbol)
if(len(potential_buys) > 0):
buy_holdings(potential_buys, profile_data, holdings_data)
if(len(sells) > 0):
update_trade_history(sells, holdings_data, "tradehistory.txt")
print("----- Scan over -----\n")
#execute the scan
scan_stocks()
from yahoo_fin import stock_info as si
si.get_live_price("FB")