Skip to content

Commit

Permalink
Fixed market data following google API change, revised README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
omdv committed Sep 19, 2017
1 parent a5742ae commit fa36ed8
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 27 deletions.
24 changes: 16 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Robinhood
# Robinhood Portfolio
Python client to access and analyze the Robinhood portfolio.
Based on unofficial [robinhood-api](https://github.com/Jamonek/Robinhood) and several python libraries for financial analysis, such as:
- [empyrical](https://github.com/quantopian/empyrical)
Expand Down Expand Up @@ -26,26 +26,34 @@ Based on unofficial [robinhood-api](https://github.com/Jamonek/Robinhood) and se

### How To Install:
It is recommended to use virtualenv:
git clone https://github.com/omdv/robinhood-portfolio && cd robinhood-portfolio
virtualenv robinhood && source robinhood/bin/activate && pip3 install -r requirements.txt
```
git clone https://github.com/omdv/robinhood-portfolio && cd robinhood-portfolio
virtualenv robinhood && source robinhood/bin/activate && pip3 install -r requirements.txt
```

### How to Use
python3 app.py
```
python3 app.py
```

### Docker container
Docker container based on Ubuntu is [available](https://hub.docker.com/r/omdv/robinhood-portfolio/). To launch it in a background mode:
docker run -d -p 8080:8080 --name robinhood omdv/robinhood-portfolio:ubuntu
```
docker run -d -p 8080:8080 --name robinhood omdv/robinhood-portfolio:ubuntu
```

Once up and running connect to [http://localhost:8080](http://localhost:8080). If using the older versions of docker you will need to use the ip of the docker-machine.

### Jupyter notebook
Jupyter notebook using the backtrader library with pyfolio in in "notebooks" folder. It is work in progress.
### Jupyter notebook (WORK IN PROGRESS)
You can find the Jupyter notebook using the backtrader library with pyfolio in "notebooks" folder.


### Disclaimer
This tool uses the unofficial Robinhood API to access your account. This code and the corresponding tools are provided on "as is" basis and the user is responsible for the safety of his/her own account.

------------------

# Related

* [empyrical](https://github.com/quantopian/empyrical)
* [portfolioopt](https://github.com/czielinski/portfolioopt)
* [robinhood-api](https://github.com/Jamonek/Robinhood)
Expand Down
3 changes: 2 additions & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

app = Flask(__name__)
app.config['SECRET_KEY'] = 'My_l0ng_very_secure_secret_k3y'
app.debug = True
app.config['DEBUG'] = False
app.config['TESTING'] = False


def plot_returns(data):
Expand Down
10 changes: 8 additions & 2 deletions backend/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ def _validate_user_dict(self):
):
print('Market data is not consistent with Robinhood data')
self.update_market_data(fresh_start=True)
self.user
# check if dates match the actual data
elif (
(self.user['mkt_dates'][0] != self._market.major_axis.min()) or
(self.user['mkt_dates'][1] != self._market.major_axis.max())
):
print('Dates are not consistent with dataset')
self.update_market_data(fresh_start=True)
return self

def _pickle_user_dict(self):
Expand Down Expand Up @@ -141,7 +147,7 @@ def _get_latest_portfolio_snapshot(self):

columns_to_names = OrderedDict([
('cum_size', ['Shares', '{:,.0f}']),
('current_weight', ['Portfolio weight', '{:.2f}']),
('current_weight', ['Portfolio weight', '{:.2f}%']),
('cum_cost_basis', ['Current cost basis', '{:,.2f}']),
('cum_value_close', ['Current value', '{:,.2f}']),
('cum_realized_gain', ['Realized gain', '{:,.2f}']),
Expand Down
40 changes: 25 additions & 15 deletions backend/market_data.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import pandas as pd
import numpy as np
import requests as rq
from pandas_datareader import data
import pandas_datareader as pdr
from pandas_datareader.google.daily import GoogleDailyReader
from io import StringIO


# hack until pandas_datareader is fixed to a new google API
@property
def url(self):
return 'http://finance.google.com/finance/historical'


GoogleDailyReader.url = url


# download market benchmark data
# indices: ^dji, ^spx
class MarketData:
Expand Down Expand Up @@ -35,27 +45,28 @@ def _get_market_index(self, start_date, end_date):

# returns panel using goodle finance server, pause is required to avoid ban
def _get_historical_prices(self, tickers, start_date, end_date):
pf = data.DataReader(tickers, "google", start_date, end_date, pause=10)
# pf = data.DataReader(
# tickers, "google", start_date, end_date, pause=10)
pf = pdr.get_data_google(
tickers, start_date, end_date, pause=10)
pf = pf.astype(np.float32)
return pf

# # download treasury bills yields for different periods for capm model
# def _get_treasury_yields(self, start_date, end_date):
# tickers = ["TB4WK", "TB3MS", "TB1YR"]
# pf = data.DataReader(tickers, "fred", start_date, end_date)
# return pf

# return all stocks and index in one panel
def download_save_market_data(self, tickers, start_date, end_date,
update_existing=False):
start_date = self._date_fmt.format(start_date)
end_date = self._date_fmt.format(end_date)
print("Downloading market data for {}-{}".format(start_date, end_date))
start_date_str = self._date_fmt.format(start_date)
end_date_str = self._date_fmt.format(end_date)
print("Downloading market data for {}-{}".format(
start_date_str, end_date_str))

# add market index
pf = self._get_historical_prices(tickers, start_date, end_date)
pf.loc[:, :, 'market'] = self._get_market_index(start_date, end_date)
# tb = self._get_treasury_yields(start_date, end_date)
pf = self._get_historical_prices(
tickers,
start_date.date(),
end_date.date())
pf.loc[:, :, 'market'] = self._get_market_index(
start_date_str, end_date_str)

if update_existing:
new_dict = {}
Expand All @@ -66,7 +77,6 @@ def download_save_market_data(self, tickers, start_date, end_date,
pf = pd.Panel(new_dict)
else:
pf.to_hdf(self.datafile, 'market')
# tb.to_hdf(self.datafile, 'treasury_bills')
return pf


Expand Down
6 changes: 5 additions & 1 deletion backend/portfolio_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ def daily_portfolio_changes(self):
pf['cum_unrealized_gain'] =\
pf['cum_value_close'] - pf['cum_cost_basis']

# investment return without dividends
pf['investment_return'] = pf['cum_unrealized_gain'] + \
pf['cum_realized_gain']

# total return
pf['cum_total_return'] = pf['cum_unrealized_gain'] +\
pf['cum_dividends'] + pf['cum_realized_gain']
Expand Down Expand Up @@ -281,7 +285,7 @@ def actual_portfolio_stats(self):
pf = self.panelframe

# can choose either a total return or capital gain only
return_to_use = 'cum_total_return'
return_to_use = 'investment_return'

cum_return_D1 = pf[return_to_use].sum(1).shift(1)
cum_return_D2 = pf[return_to_use].sum(1)
Expand Down
Binary file modified data/data.h5
Binary file not shown.
Binary file modified data/user.pkl
Binary file not shown.

0 comments on commit fa36ed8

Please sign in to comment.