-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
first version of backend with API based on pandas
- Loading branch information
0 parents
commit f4ae9b4
Showing
5 changed files
with
455 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2015 Rohan Pai | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Robinhood | ||
Python client to access and analyze the Robinhood portfolio. | ||
Based on [robinhood-api](https://github.com/Jamonek/Robinhood) | ||
|
||
##Current Features | ||
|
||
###How To Install: | ||
pip install -r requirements.txt | ||
|
||
###How to Use (see [example.py](https://github.com/MeheLLC/Robinhood/blob/master/example.py)) | ||
|
||
from Robinhood import Robinhood | ||
my_trader = Robinhood() | ||
logged_in = my_trader.login(username="USERNAME HERE", password="PASSWORD HERE") | ||
stock_instrument = my_trader.instruments("GEVO")[0] | ||
quote_info = my_trader.quote_data("GEVO") | ||
buy_order = my_trader.place_buy_order(stock_instrument, 1) | ||
sell_order = my_trader.place_sell_order(stock_instrument, 1) | ||
|
||
------------------ | ||
|
||
# Related | ||
|
||
* [robinhood-ruby](https://github.com/rememberlenny/robinhood-ruby) - RubyGem for interacting with Robinhood API | ||
* [robinhood-node](https://github.com/aurbano/robinhood-node) - NodeJS module to make trades with Robinhood Private API |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import os | ||
import pandas as pd | ||
from flask import Flask, render_template, request, jsonify, Response | ||
|
||
|
||
# Initialize | ||
app = Flask(__name__) | ||
app.config['SECRET_KEY'] = 'NOTSECURELOL' | ||
app.debug = True | ||
|
||
|
||
# Define a route for the default URL, which loads the login form | ||
@app.route('/') | ||
def form(): | ||
return render_template('dashboard.html') | ||
|
||
|
||
@app.route('/positions/') | ||
def data(): | ||
# df_div = pd.read_hdf('data/data.h5', 'dividends') | ||
df_pos = pd.read_hdf('data/data.h5', 'positions') | ||
# df_ord = pd.read_hdf('data/data.h5', 'orders') | ||
|
||
# get positions | ||
df_pos = df_pos[df_pos.ratio > 0] | ||
fields = ['symbol', 'balance', 'subtotal', 'ratio', 'return'] | ||
positions = df_pos[fields].to_json(orient='records') | ||
|
||
return Response(positions, mimetype='application/json') | ||
|
||
|
||
if __name__ == '__main__': | ||
PORT = int(os.getenv('PORT', 8080)) | ||
HOST = os.getenv('HOST', '0.0.0.0') | ||
app.run(debug=True, host=HOST, port=PORT) | ||
|
||
|
||
|
||
|
||
# # Accepting: POST requests | ||
# @app.route('/dashboard/', methods=['GET']) | ||
# def account(): | ||
# # username = None | ||
# # password = None | ||
|
||
# # if request.method == 'POST': | ||
# # username=request.form['username'] | ||
# # password=request.form['password'] | ||
|
||
# # logged_in = robinhood.login(username=username, password=password) | ||
|
||
# # positions = rb.positions() | ||
# # orders = rb.order_history() | ||
# # dividends = rb.dividends() | ||
|
||
# # elif request.method == 'GET': | ||
# # name=None | ||
# # password=None | ||
# return render_template('dashboard.html') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
import getpass | ||
import json | ||
import requests | ||
|
||
class Robinhood: | ||
|
||
endpoints = { | ||
"login": "https://api.robinhood.com/api-token-auth/", | ||
"investment_profile": "https://api.robinhood.com/user/investment_profile/", | ||
"accounts":"https://api.robinhood.com/accounts/", | ||
"ach_iav_auth":"https://api.robinhood.com/ach/iav/auth/", | ||
"ach_relationships":"https://api.robinhood.com/ach/relationships/", | ||
"ach_transfers":"https://api.robinhood.com/ach/transfers/", | ||
"applications":"https://api.robinhood.com/applications/", | ||
"dividends":"https://api.robinhood.com/dividends/", | ||
"edocuments":"https://api.robinhood.com/documents/", | ||
"instruments":"https://api.robinhood.com/instruments/", | ||
"margin_upgrades":"https://api.robinhood.com/margin/upgrades/", | ||
"markets":"https://api.robinhood.com/markets/", | ||
"notifications":"https://api.robinhood.com/notifications/", | ||
"orders":"https://api.robinhood.com/orders/", | ||
"password_reset":"https://api.robinhood.com/password_reset/request/", | ||
"portfolios":"https://api.robinhood.com/portfolios/", | ||
"positions":"https://api.robinhood.com/positions/", | ||
"quotes":"https://api.robinhood.com/quotes/", | ||
"historicals":"https://api.robinhood.com/quotes/historicals/", | ||
"document_requests":"https://api.robinhood.com/upload/document_requests/", | ||
"user":"https://api.robinhood.com/user/", | ||
"watchlists":"https://api.robinhood.com/watchlists/", | ||
"news":"https://api.robinhood.com/midlands/news/" | ||
} | ||
|
||
session = None | ||
|
||
username = None | ||
|
||
password = None | ||
|
||
headers = None | ||
|
||
auth_token = None | ||
|
||
|
||
############################## | ||
#Logging in and initializing | ||
############################## | ||
|
||
def __init__(self): | ||
self.session = requests.session() | ||
self.headers = { | ||
"Accept": "*/*", | ||
"Accept-Encoding": "gzip, deflate", | ||
"Accept-Language": "en;q=1, fr;q=0.9, de;q=0.8, ja;q=0.7, nl;q=0.6", | ||
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8", | ||
"X-Robinhood-API-Version": "1.0.0", | ||
"Connection": "keep-alive", | ||
"User-Agent": "Robinhood/823 (iPhone; iOS 7.1.2; Scale/2.00)" | ||
} | ||
self.session.headers = self.headers | ||
|
||
def login_prompt(self): | ||
"""Prompts user for username and password and calls login().""" | ||
username = raw_input("Username: ") | ||
password = getpass.getpass() | ||
return self.login(username=username, password=password) | ||
|
||
def login(self, username, password): | ||
self.username = username | ||
self.password = password | ||
data = {"password" : self.password, "username" : self.username} | ||
res = self.session.post(self.endpoints['login'], data=data) | ||
res = res.json() | ||
try: | ||
self.auth_token = res['token'] | ||
except KeyError: | ||
return False | ||
self.headers['Authorization'] = 'Token '+self.auth_token | ||
return True | ||
|
||
############################## | ||
#GET DATA | ||
############################## | ||
|
||
def investment_profile(self): | ||
self.session.get(self.endpoints['investment_profile']) | ||
|
||
def instruments(self, stock=None): | ||
res = self.session.get(self.endpoints['instruments'],\ | ||
params={'query':stock.upper()}) | ||
res = res.json() | ||
return res['results'] | ||
|
||
def quote_data(self, stock=None): | ||
#Prompt for stock if not entered | ||
if stock is None: | ||
stock = raw_input("Symbol: "); | ||
url = str(self.endpoints['quotes']) + str(stock) + "/" | ||
#Check for validity of symbol | ||
try: | ||
res = requests.get(url).json() | ||
if len(res) > 0: | ||
return res; | ||
else: | ||
raise NameError("Invalid Symbol: " + stock); | ||
except (ValueError): | ||
raise NameError("Invalid Symbol: " + stock); | ||
|
||
def get_quote(self, stock=None): | ||
data = self.quote_data(stock) | ||
return data["symbol"] | ||
|
||
def get_symbol_by_instrument(self, url=None): | ||
return requests.get(url).json()['symbol'] | ||
|
||
def get_name_by_instrument(self, url=None): | ||
return requests.get(url).json()['name'] | ||
|
||
def get_historical_quotes(self,symbol,interval,span,bounds='regular'): | ||
# Valid combination | ||
# interval = 5minute | 10minute + span = day, week | ||
# interval = day + span = year | ||
# interval = week | ||
# bounds can be 'regular' for regular hours or 'extended' for extended hours | ||
res = self.session.get(self.endpoints['historicals'],\ | ||
params={'symbols':','.join(symbol).upper(),\ | ||
'interval':interval, 'span':span, 'bounds':bounds}) | ||
return res.json() | ||
|
||
def get_news(self, symbol): | ||
return self.session.get(self.endpoints['news']+symbol.upper()+"/").json() | ||
|
||
def print_quote(self, stock=None): | ||
data = self.quote_data(stock) | ||
print(data["symbol"] + ": $" + data["last_trade_price"]); | ||
|
||
def print_quotes(self, stocks): | ||
for i in range(len(stocks)): | ||
self.print_quote(stocks[i]); | ||
|
||
def ask_price(self, stock=None): | ||
return float(self.quote_data(stock)['ask_price']); | ||
|
||
def ask_size(self, stock=None): | ||
return float(self.quote_data(stock)['ask_size']); | ||
|
||
def bid_price(self, stock=None): | ||
return float(self.quote_data(stock)['bid_price']); | ||
|
||
def bid_size(self, stock=None): | ||
return float(self.quote_data(stock)['bid_size']); | ||
|
||
def last_trade_price(self, stock=None): | ||
return float(self.quote_data(stock)['last_trade_price']); | ||
|
||
def previous_close(self, stock=None): | ||
return float(self.quote_data(stock)['previous_close']); | ||
|
||
def previous_close_date(self, stock=None): | ||
return self.quote_data(stock)['previous_close_date']; | ||
|
||
def adjusted_previous_close(self, stock=None): | ||
return float(self.quote_data(stock)['adjusted_previous_close']); | ||
|
||
def symbol(self, stock=None): | ||
return self.quote_data(stock)['symbol']; | ||
|
||
def last_updated_at(self, stock=None): | ||
return self.quote_data(stock)['updated_at']; | ||
|
||
def get_account(self): | ||
res = self.session.get(self.endpoints['accounts']) | ||
res = res.json() | ||
return res['results'][0] | ||
|
||
def get_url(self,url): | ||
return self.session.get(url).json() | ||
|
||
############################## | ||
# PORTFOLIOS DATA | ||
############################## | ||
|
||
def portfolios(self): | ||
"""Returns the user's portfolio data.""" | ||
return self.session.get(\ | ||
self.endpoints['portfolios']).json()['results'][0] | ||
|
||
def adjusted_equity_previous_close(self): | ||
return float(self.portfolios()['adjusted_equity_previous_close']) | ||
|
||
def equity(self): | ||
return float(self.portfolios()['equity']) | ||
|
||
def equity_previous_close(self): | ||
return float(self.portfolios()['equity_previous_close']) | ||
|
||
def excess_margin(self): | ||
return float(self.portfolios()['excess_margin']) | ||
|
||
def extended_hours_equity(self): | ||
return float(self.portfolios()['extended_hours_equity']) | ||
|
||
def extended_hours_market_value(self): | ||
return float(self.portfolios()['extended_hours_market_value']) | ||
|
||
def last_core_equity(self): | ||
return float(self.portfolios()['last_core_equity']) | ||
|
||
def last_core_market_value(self): | ||
return float(self.portfolios()['last_core_market_value']) | ||
|
||
def market_value(self): | ||
return float(self.portfolios()['market_value']) | ||
|
||
def order_history(self): | ||
return self.session.get(self.endpoints['orders']).json() | ||
|
||
def dividends(self): | ||
return self.session.get(self.endpoints['dividends']).json() | ||
|
||
############################## | ||
# POSITIONS DATA | ||
############################## | ||
|
||
def positions(self): | ||
"""Returns the user's positions data.""" | ||
return self.session.get(self.endpoints['positions']).json() | ||
|
||
def securities_owned(self): | ||
""" | ||
Returns a list of symbols of securities of which there are more | ||
than zero shares in user's portfolio. | ||
""" | ||
positions = self.positions() | ||
securities = [] | ||
for position in positions['results']: | ||
quantity = float(position['quantity']) | ||
if quantity > 0: | ||
securities.append(\ | ||
self.session.get(position['instrument']).json()['symbol']) | ||
return securities |
Oops, something went wrong.