def add_dividends(csv_writer, cache_mode): dividends = client.get_dividends(cache_mode=cache_mode) instrument_ids = [get_last_id_from_url(d['instrument']) for d in dividends] instruments = client.get_instruments(instrument_ids) instrument_by_id = {i['id']: i for i in instruments} for dividend in dividends: paid_at = dividend['paid_at'] if not paid_at: continue paid_at = parse(paid_at) rate = Decimal(dividend['rate']) amount = Decimal(dividend['amount']) quantity = int(float(dividend['position'])) instrument_id = get_last_id_from_url(dividend['instrument']) instrument = instrument_by_id[instrument_id] name = instrument['simple_name'] or instrument['name'] symbol = instrument['symbol'] csv_writer.writerow({ 'symbol': symbol, 'name': name, 'type': 'dividend', 'side': 'receive', 'quantity': quantity, 'price': '{:.2f}'.format(rate), 'amount': '{:.2f}'.format(amount), 'date': paid_at.date(), 'fees': 0, })
def display_sp500_movers(): print('') print('----------------- Top S&P 500 Movers -----------------') for direction in ['up', 'down']: extra_percentage_symbol = '+' if direction == 'up' else '' movers = client.get_sp500_movers(direction, cache_mode=FORCE_LIVE) if direction == 'down': movers.reverse() instruments = client.get_instruments([ get_last_id_from_url(mover['instrument_url']) for mover in movers ]) instruments_by_id = {i['id']: i for i in instruments} for mover in movers: last_price = Decimal( mover['price_movement']['market_hours_last_price']) movement_pct = Decimal( mover['price_movement']['market_hours_last_movement_pct']) instrument = instruments_by_id[get_last_id_from_url( mover['instrument_url'])] print('${:.2f}\t({}{}%)\t{}\t({})'.format( last_price, extra_percentage_symbol, movement_pct, instrument['symbol'], instrument['simple_name'] or instrument['name']))
def display_pending_options_orders(): orders = client.get_options_orders() pending_orders = [order for order in orders if order['state'] in ['queued', 'confirmed']] if len(pending_orders) == 0: print('\tNo pending orders') exit() chain_ids = [order['chain_id'] for order in pending_orders] chains = client.get_options_chains(chain_ids=chain_ids) chain_by_id = { chain['id']: chain for chain in chains } instrument_id_by_chain_id = { chain['id']: get_last_id_from_url(chain['underlying_instruments'][0]['instrument']) for chain in chains } instruments = client.get_instruments(list(instrument_id_by_chain_id.values())) instrument_by_id = {i['id']: i for i in instruments} options_instrument_id_by_order_id = { order['id']: get_last_id_from_url(order['legs'][0]['option']) for order in pending_orders } options_instruments = client.get_options_instruments(options_instrument_ids=list(set(options_instrument_id_by_order_id.values()))) option_instruments_by_id = { options_instrument['id']: options_instrument for options_instrument in options_instruments } instrument_id_to_orders = defaultdict(list) for order in pending_orders: instrument_id_to_orders[instrument_id_by_chain_id[order['chain_id']]].append(order) for instrument_id, instrument_orders in instrument_id_to_orders.items(): instrument = instrument_by_id[instrument_id] print('{} ({})'.format(instrument['symbol'], instrument['simple_name'] or instrument['name'])) for order in instrument_orders: order_id = order['id'] order_state = order['state'] order_type = order['type'] order_side = DIRECTION_TO_ORDER_SIDE[order['direction']] order_quantity = int(float(order['quantity'])) order_price = Decimal(order['price']) order_premium = Decimal(order['premium']) order_options_instrument = option_instruments_by_id[options_instrument_id_by_order_id[order_id]] order_option_type = order_options_instrument['type'] order_option_expires = order_options_instrument['expiration_date'] order_option_strike = Decimal(order_options_instrument['strike_price']) print('\t{}\t{} {}\t{} @ ${:.2f} (${:.2f} per share)\t({})'.format( order_state, order_type, order_side, order_quantity, order_premium, order_price, order_id)) print('\t\t${:.2f} {} expiring {}'.format(order_option_strike, order_option_type, order_option_expires))
def add_transfers(csv_writer, cache_mode): for transfer in client.get_ach_transfers(cache_mode=cache_mode): transfer_amount = Decimal(transfer['amount']) early_access_amount = Decimal(transfer['early_access_amount']) updated_at = parse(transfer['updated_at']).astimezone( pytz.timezone('US/Pacific')).date() amount = early_access_amount or transfer_amount direction = 'deposit_early_access' if early_access_amount else transfer[ 'direction'] relationship = client.get_ach_relationship_by_id(get_last_id_from_url( transfer['ach_relationship']), cache_mode=cache_mode) csv_writer.writerow({ 'symbol': '', 'name': relationship['bank_account_nickname'], 'type': 'transfer', 'side': direction, 'quantity': 1, 'price': amount, 'amount': amount, 'date': updated_at, 'fees': 0, })
def add_orders(csv_writer, cache_mode): orders = client.get_orders(cache_mode=cache_mode) instrument_ids = [get_last_id_from_url(o['instrument']) for o in orders] instruments = client.get_instruments(instrument_ids) instrument_by_id = {i['id']: i for i in instruments} for order in orders: order_id = order['id'] state = order['state'] if state != 'filled': if state not in ['queued', 'confirmed', 'cancelled']: print( 'Skipping order {} with state {} that may need to be handled...' .format(order_id, state)) continue fees = Decimal(order['fees']) side = order['side'] instrument_id = get_last_id_from_url(order['instrument']) instrument = instrument_by_id[instrument_id] name = instrument['simple_name'] or instrument['name'] symbol = instrument['symbol'] for execution in order['executions']: price = Decimal(execution['price']) quantity = int(float(execution['quantity'])) amount = quantity * price transaction_on = parse(execution['timestamp']).astimezone( pytz.timezone('US/Pacific')).date() csv_writer.writerow({ 'symbol': symbol, 'name': name, 'type': 'order', 'side': side, 'quantity': quantity, 'price': '{:.2f}'.format(price), 'amount': '{:.2f}'.format(amount), 'date': transaction_on.isoformat(), 'fees': fees, }) # Don't duplicate fees in multiple executions if fees: fees = Decimal(0)
def display_pending_orders(): orders = client.get_orders(cache_mode=FORCE_LIVE) pending_orders = [order for order in orders if order['state'] in ['queued', 'confirmed']] if len(pending_orders) == 0: print('\tNo pending orders') exit() instrument_ids = [get_last_id_from_url(o['instrument']) for o in pending_orders] instruments = client.get_instruments(instrument_ids) instrument_by_id = { i['id']: i for i in instruments } pending_orders_by_instrument = defaultdict(list) for order in pending_orders: instrument_id = get_last_id_from_url(order['instrument']) pending_orders_by_instrument[instrument_id].append(order) for instrument_id, instrument_orders in pending_orders_by_instrument.items(): instrument = instrument_by_id[instrument_id] print('{} ({})'.format(instrument['symbol'], instrument['simple_name'] or instrument['name'])) try: position = client.get_position_by_instrument_id(instrument['id']) except NotFound: print('\tNo current position (or ever)') else: position_quantity = int(float(position['quantity'])) if not position_quantity: print('\tNo current position (sold off)') else: position_average_buy_price = Decimal(position['average_buy_price']) position_equity_cost = position_quantity * position_average_buy_price print('\tcurrent position\t\t{} @ ${:.2f}'.format( position_quantity, position_average_buy_price, position_equity_cost)) for order in instrument_orders: order_id = order['id'] order_state = order['state'] order_type = order['type'] order_side = order['side'] order_quantity = int(float(order['quantity'])) order_price = Decimal(order['price']) print('\t{}\t{} {}\t{} @ ${:.2f}\t({})'.format( order_state, order_type, order_side, order_quantity, order_price, order_id))
def add_rewards(csv_writer, cache_mode): referrals = client.get_referrals(cache_mode=cache_mode) instrument_ids = [ get_last_id_from_url(r['reward']['stocks'][0]['instrument_url']) for r in referrals if r['reward']['stocks'] ] instruments = client.get_instruments(instrument_ids) instrument_by_id = {i['id']: i for i in instruments} for referral in referrals: direction = referral['direction'] if direction != 'from': continue if not referral['reward']['stocks']: continue assert len(referral['reward']['stocks']) == 1 if referral['reward']['stocks'][0]['state'] != 'granted': continue cost_basis = Decimal(referral['reward']['stocks'][0]['cost_basis']) quantity = int(referral['reward']['stocks'][0]['quantity']) updated_at = parse(referral['updated_at']).astimezone( pytz.timezone('US/Pacific')).date() instrument_id = get_last_id_from_url( referral['reward']['stocks'][0]['instrument_url']) instrument = instrument_by_id[instrument_id] name = instrument['simple_name'] or instrument['name'] symbol = instrument['symbol'] csv_writer.writerow({ 'symbol': symbol, 'name': name, 'type': 'reward', 'side': 'receive', 'quantity': quantity, 'price': '{:.2f}'.format(cost_basis), 'amount': '{:.2f}'.format(quantity * cost_basis), 'date': updated_at.isoformat(), 'fees': 0, })
def download_portfolio(cache_mode): with open('portfolio.csv', 'w', newline='') as csv_file: fieldnames = [ 'symbol', 'name', 'quantity', 'average_buy_price', 'equity_cost', 'last_price', 'day_price_change', 'day_percentage_change', 'total_price_change', 'total_percentage_change', 'equity_worth', 'equity_percentage', 'equity_idx', 'robinhood_holders', 'buy_rating', 'sell_rating', ] csv_writer = csv.DictWriter(csv_file, fieldnames=fieldnames) csv_writer.writeheader() positions = client.get_positions(cache_mode=cache_mode) position_by_instrument_id = {} for position in positions: quantity = int(float(position['quantity'])) average_buy_price = Decimal(position['average_buy_price']) instrument_id = get_last_id_from_url(position['instrument']) position_by_instrument_id[instrument_id] = { 'quantity': quantity, 'average_buy_price': average_buy_price, 'equity_cost': quantity * average_buy_price, } instrument_ids = list(position_by_instrument_id.keys()) instruments = client.get_instruments(instrument_ids) for instrument in instruments: instrument_id = instrument['id'] position_by_instrument_id[instrument_id]['symbol'] = instrument[ 'symbol'] position_by_instrument_id[instrument_id][ 'simple_name'] = instrument['simple_name'] position_by_instrument_id[instrument_id]['full_name'] = instrument[ 'name'] symbols = [p['symbol'] for p in position_by_instrument_id.values()] fundamentals = client.get_fundamentals(instrument_ids, cache_mode=cache_mode) for fundamental in fundamentals: instrument_id = get_last_id_from_url(fundamental['instrument']) position_by_instrument_id[instrument_id]['last_open'] = Decimal( fundamental['open']) popularities = client.get_popularities(instrument_ids, cache_mode=cache_mode) for popularity in popularities: instrument_id = get_last_id_from_url(popularity['instrument']) position_by_instrument_id[instrument_id][ 'robinhood_holders'] = popularity['num_open_positions'] ratings = client.get_ratings(instrument_ids, cache_mode=cache_mode) for rating in ratings: instrument_id = rating['instrument_id'] num_ratings = sum( v for _, v in rating['summary'].items()) if rating['summary'] else None if num_ratings: percent_buy = rating['summary'][ 'num_buy_ratings'] * 100 / num_ratings percent_sell = rating['summary'][ 'num_sell_ratings'] * 100 / num_ratings position_by_instrument_id[instrument_id][ 'buy_rating'] = 'N/A' if not num_ratings else '{:.2f}'.format( percent_buy) position_by_instrument_id[instrument_id][ 'sell_rating'] = 'N/A' if not num_ratings else '{:.2f}'.format( percent_sell) position_quotes = client.get_quotes(instrument_ids, cache_mode=cache_mode) for quote in position_quotes: instrument_id = get_last_id_from_url(quote['instrument']) position = position_by_instrument_id[instrument_id] position['last_price'] = Decimal(quote['last_trade_price']) position[ 'equity_worth'] = position['quantity'] * position['last_price'] total_equity = sum(position['equity_worth'] for position in position_by_instrument_id.values()) positions_by_equity_worth = sorted(position_by_instrument_id.values(), key=lambda p: p['equity_worth'], reverse=True) for idx, position in enumerate(positions_by_equity_worth): total_price_change = position['last_price'] - position[ 'average_buy_price'] day_price_change = position['last_price'] - position['last_open'] day_percentage_change = day_price_change * 100 / position[ 'last_open'] total_percentage_change = total_price_change * 100 / position[ 'average_buy_price'] if position['average_buy_price'] else 100 csv_writer.writerow({ 'symbol': position['symbol'], 'name': position['simple_name'] or position['full_name'], 'quantity': position['quantity'], 'average_buy_price': round(position['average_buy_price'], 2), 'equity_cost': round(position['equity_cost'], 2), 'last_price': round(position['last_price'], 2), 'day_price_change': round(day_price_change, 2), 'day_percentage_change': round(day_percentage_change, 2), 'total_price_change': round(total_price_change, 2), 'total_percentage_change': round(total_percentage_change, 2), 'equity_worth': round(position['equity_worth'], 2), 'equity_percentage': round(position['equity_worth'] * 100 / total_equity, 2), 'equity_idx': idx + 1, 'buy_rating': position['buy_rating'], 'sell_rating': position['sell_rating'], 'robinhood_holders': position['robinhood_holders'], })
def display_options_quote(client, options_type, symbol, dates, strike, cache_mode): try: instrument = client.get_instrument_by_symbol(symbol) except NotFound: print('symbol {} was not found'.format(symbol)) exit() else: instrument_id = instrument['id'] name = instrument['simple_name'] or instrument['name'] options_chains = [ c for c in client.get_options_chains(instrument_ids=[instrument_id]) if c['can_open_position'] ] if len(options_chains) != 1: raise Exception( 'Expected exactly one options chains listing, but got: {}'.format( json.dumps(options_chains, indent=4))) options_chain = options_chains[0] if len(options_chain['underlying_instruments']) != 1: raise Exception( 'Expected exactly one underlying instrument, but got: {}'.format( json.dumps(options_chain, indent=4))) if not options_chain['can_open_position']: raise Exception("Can't open position: {}".format( json.dumps(options_chain, indent=4))) chain_id = options_chain['id'] multiplier = Decimal(options_chain['trade_value_multiplier']) kwargs = {} if dates: kwargs['expiration_dates'] = dates if options_type: kwargs['options_type'] = options_type potential_options_instruments = client.get_options_instruments( chain_id=chain_id, tradability='tradable', state='active', **kwargs) if not potential_options_instruments: raise Exception('No options found') options_instruments = [] for potential_options_instrument in potential_options_instruments: if strike is None or strike == float( potential_options_instrument['strike_price']): options_instruments.append(potential_options_instrument) if not options_instruments: raise Exception('No options found') options_instrument_by_id = { options_instrument['id']: options_instrument for options_instrument in options_instruments } options_quotes = client.get_options_marketdatas([ options_instrument['id'] for options_instrument in options_instruments ]) for options_quote in options_quotes: break_even_price = Decimal(options_quote['break_even_price']) ask_size = options_quote['ask_size'] ask_price = Decimal(options_quote['ask_price']) bid_size = options_quote['bid_size'] bid_price = Decimal(options_quote['bid_price']) adjusted_mark_price = Decimal(options_quote['adjusted_mark_price']) mark_price = Decimal(options_quote['mark_price']) max_loss = multiplier * adjusted_mark_price bid_spread = ask_price - bid_price implied_volatility = Decimal(options_quote['implied_volatility'] or 1) * 100 high_price = Decimal(options_quote['high_price'] or 0) low_price = Decimal(options_quote['low_price'] or 0) hl_spread = high_price - low_price last_trade_size = options_quote['last_trade_size'] last_trade_price = Decimal(options_quote['last_trade_price'] or 0) open_interest = options_quote['open_interest'] volume = options_quote['volume'] options_instrument_id = get_last_id_from_url( options_quote['instrument']) options_instrument = options_instrument_by_id[options_instrument_id] expiration_date = options_instrument['expiration_date'] option_strike = Decimal(options_instrument['strike_price']) print('') print('${:.2f} {} ({}) {}'.format( option_strike, symbol, name, options_type[0].upper() + options_type[1:])) print('Break even\t ${:.2f}'.format(break_even_price)) print('Expires\t\t {}'.format(expiration_date)) print('Spread\t\t ${:.2f} ({}) <-> ${:.2f} ({})'.format( bid_price, bid_size, ask_price, ask_size)) print('\t\t\t${:.2f} ({:.2f}%)'.format( bid_spread, bid_spread * 100 / adjusted_mark_price)) print('Low/High\t ${:.2f} <-> ${:.2f}'.format(low_price, high_price)) print('\t\t\t${:.2f} ({:.2f}%)'.format( hl_spread, hl_spread * 100 / adjusted_mark_price)) print('Max loss\t ${:.2f}'.format(max_loss)) print('Impl Volatil\t {:.2f}%'.format(implied_volatility)) print('Last\t\t {} @ ${:.2f}'.format(last_trade_size, last_trade_price)) print('Open Int\t {}'.format(open_interest)) print('Volume\t\t {}'.format(volume))
def show_potentials(cache_mode): # First, get the portfolio positions = client.get_positions(cache_mode=cache_mode) position_by_instrument_id = {} symbol_to_instrument_id = {} for position in positions: quantity = int(float(position['quantity'])) average_buy_price = Decimal(position['average_buy_price']) instrument_id = get_last_id_from_url(position['instrument']) instrument = client.get_instrument_by_id(instrument_id) fundamental = client.get_fundamental(instrument_id, cache_mode=cache_mode) symbol_to_instrument_id[instrument['symbol']] = instrument_id position_by_instrument_id[instrument_id] = { 'quantity': quantity, 'average_buy_price': average_buy_price, 'equity_cost': quantity * average_buy_price, 'symbol': instrument['symbol'], 'simple_name': instrument['simple_name'], 'full_name': instrument['name'], 'last_open': Decimal(fundamental['open']), } position_quotes = client.get_quotes(list(position_by_instrument_id.keys()), cache_mode=cache_mode) for quote in position_quotes: instrument_id = get_last_id_from_url(quote['instrument']) position = position_by_instrument_id[instrument_id] position['last_price'] = Decimal(quote['last_trade_price']) position[ 'equity_worth'] = position['quantity'] * position['last_price'] # Next, augment with sentiment. with open('sentiment.json', 'r') as sentiment_file: sentiment_json = json.load(sentiment_file) for symbol, symbol_sentiment in sentiment_json.items(): instrument_id = symbol_to_instrument_id[symbol] position = position_by_instrument_id[instrument_id] assert 'sentiment' not in position position['sentiment'] = symbol_sentiment position['category'] = symbol_sentiment.get('category', 'N/A') position['priority'] = symbol_sentiment.get('priority', 99) if 'equity_target' in symbol_sentiment: equity_target = symbol_sentiment['equity_target'] position['equity_target'] = equity_target equity_left = equity_target - position['equity_worth'] if equity_left <= 0: position['equity_left'] = 0 position['shares_needed'] = 0 else: position['equity_left'] = equity_left position[ 'shares_needed'] = equity_left / position['last_price'] equity_needed = equity_target # How many needed position['paused'] = symbol_sentiment.get('paused', False) positions_by_priority = sorted(position_by_instrument_id.values(), key=lambda p: p.get('priority', 999)) print('pri\tsym\teq_tot\tlast\twrth\ttrgt\tlft\tsh\tcat') for position in positions_by_priority: if position.get('paused'): continue print('{}\t{}\t{:.2f}\t{:.2f}\t{:.2f}\t{}\t{:.2f}\t{:.2f}\t{}'.format( position.get('priority', 'N/A'), position['symbol'], position['equity_cost'], position['last_price'], position['equity_worth'], position.get('equity_target', 'N/A'), position.get('equity_left', 0), position.get('shares_needed', 0), position.get('category')))
def display_pending_orders(): orders = client.get_orders(cache_mode=FORCE_LIVE) pending_orders = [ order for order in orders if order['state'] in ['queued', 'confirmed'] ] if len(pending_orders) == 0: print('\tNo pending orders') exit() instrument_ids = [ get_last_id_from_url(o['instrument']) for o in pending_orders ] instruments = client.get_instruments(instrument_ids) instrument_by_id = {i['id']: i for i in instruments} quotes = client.get_quotes(instrument_ids, cache_mode=FORCE_LIVE) quote_by_id = { get_last_id_from_url(quote['instrument']): quote for quote in quotes } pending_orders_by_instrument = defaultdict(list) for order in pending_orders: instrument_id = get_last_id_from_url(order['instrument']) pending_orders_by_instrument[instrument_id].append(order) order_amounts = Decimal(0) for instrument_id, instrument_orders in pending_orders_by_instrument.items( ): instrument = instrument_by_id[instrument_id] print('{} ({})'.format(instrument['symbol'], instrument['simple_name'] or instrument['name'])) # Show position try: position = client.get_position_by_instrument_id(instrument['id']) except NotFound: print('\tNo current position (or ever)') else: position_quantity = int(float(position['quantity'])) if not position_quantity: print('\tNo current position (sold off)') else: position_average_buy_price = Decimal( position['average_buy_price']) position_equity_cost = position_quantity * position_average_buy_price print('\tcurrent position\t\t{} @ ${:.2f}'.format( position_quantity, position_average_buy_price, position_equity_cost)) # Show quote quote = quote_by_id[instrument_id] last_trade_price = Decimal(quote['last_trade_price']) bid_price = Decimal(quote['bid_price']) bid_size = int(quote['bid_size']) ask_price = Decimal(quote['ask_price']) ask_size = int(quote['ask_size']) print('\tspread:\t${:.2f} ({}) <-> ${:.2f} ({})'.format( bid_price, bid_size, ask_price, ask_size)) print('\tlast:\t${:.2f}'.format(last_trade_price)) # Show orders for order in instrument_orders: order_id = order['id'] order_state = order['state'] order_type = order['type'] order_side = order['side'] order_quantity = int(float(order['quantity'])) order_price = Decimal(order['price']) multiplier = 1 order_total = order_quantity * order_price if order_side == 'sell': order_amounts -= order_total else: order_amounts += order_total print('\t{}\t{} {}\t{} @ ${:.2f}\t({})'.format( order_state, order_type, order_side, order_quantity, order_price, order_id)) print() print( 'Combined order totals (negative means added account value) = ${:.2f}'. format(order_amounts))
def display_options_discoveries(symbol, cache_mode): try: instrument = client.get_instrument_by_symbol(symbol) except NotFound: print('symbol {} was not found'.format(symbol)) exit() else: instrument_id = instrument['id'] name = instrument['simple_name'] or instrument['name'] options_chains = [ c for c in client.get_options_chains(instrument_ids=[instrument_id]) if c['can_open_position'] ] if len(options_chains) != 1: raise Exception( 'Expected exactly one options chains listing, but got: {}'.format( json.dumps(options_chains, indent=4))) options_chain = options_chains[0] if len(options_chain['underlying_instruments']) != 1: raise Exception( 'Expected exactly one underlying instrument, but got: {}'.format( json.dumps(options_chain, indent=4))) if not options_chain['can_open_position']: raise Exception("Can't open position: {}".format( json.dumps(options_chain, indent=4))) chain_id = options_chain['id'] options_instrument_id = options_chain['underlying_instruments'][0]['id'] multiplier = Decimal(options_chain['trade_value_multiplier']) discoveries = client.get_options_discoveries(chain_id) option_instrument_ids = [ discovery['legs'][0]['option_id'] for discovery in discoveries ] options_instruments = client.get_options_instruments( options_instrument_ids=option_instrument_ids) options_instruments_by_id = { options_instrument['id']: options_instrument for options_instrument in options_instruments } options_quotes = client.get_options_marketdatas(option_instrument_ids) options_quote_by_id = { get_last_id_from_url(options_quote['instrument']): options_quote for options_quote in options_quotes } for discovery in discoveries: print('') print('----------------------------------------------') print(discovery['description']) print(discovery['strategy_type']) print(discovery['strategy_category']) print(', '.join(discovery['tags'])) print(discovery['legs'][0]['side']) option_instrument_id = discovery['legs'][0]['option_id'] option_instrument = options_instruments_by_id[option_instrument_id] print('Option') print('\tType\t{}'.format(option_instrument['type'])) print('\tExpires\t{}'.format(option_instrument['expiration_date'])) print('\tStrike\t{}'.format(Decimal( option_instrument['strike_price']))) options_quote = options_quote_by_id[option_instrument_id] adjusted_mark_price = Decimal(options_quote['adjusted_mark_price']) print('\tPrice\t${:.2f}'.format(adjusted_mark_price)) print('\tCost\t${:.2f}'.format(adjusted_mark_price * 100))