Samuel,
I replaced your order
method with order_for_robinhood
as I recommended in previous posts.
'''
This algorithm defines a target long-only diversified portfolio and rebalances
it at a user-specified frequency.
'''
import datetime
import pytz
import pandas as pd
from zipline.utils.tradingcalendar import get_early_closes
def initialize(context):
# Robinhood only allows long positions, use this trading
# guard in case
set_long_only()
# Since we are trading with Robinhood we can set this to $0!
set_commission(commission.PerTrade(cost=0))
# Define the instruments in the portfolio:
context.sids = {
sid(19662): 0.1,
sid(25902): 0.1,
sid(19659): 0.1,
sid(25903): 0.1,
sid(25905): 0.05,
sid(19658): 0.05,
sid(41959): 0.05,
sid(25906): 0.05,
sid(32277): 0.05,
sid(28073): 0.05,
sid(19661): 0.05,
sid(25908): 0.04,
sid(19660): 0.04,
sid(16827): 0.01,
sid(41961): 0.01,
sid(19657): 0.01,
sid(25904): 0.01,
sid(27804): 0.01,
sid(40533): 0.01,
sid(40708): 0.01,
sid(26669): 0.01,
sid(26670): 0.01,
sid(28075): 0.05,
}
# Define the benchmark (used to get early close dates for reference).
context.spy = sid(8554)
start_date = context.spy.security_start_date
end_date = context.spy.security_end_date
# Initialize context variables the define rebalance logic:
context.rebalance_date = None
context.next_rebalance_Date = None
context.rebalance_days = 15
# Get the dates when the market closes early:
context.early_closes = get_early_closes(start_date, end_date).date
set_commission(commission.PerTrade(cost=0))
def handle_data(context, data):
# Get the current exchange time, in local timezone:
exchange_time = pd.Timestamp(get_datetime()).tz_convert('US/Central')
# If it is rebalance day, rebalance:
if context.rebalance_date == None or exchange_time >= context.next_rebalance_date:
# If we are in rebalance window but there are open orders, wait til next minute
if has_open_orders(data,context) == True:
log.info('Has open orders, not rebalancing.')
else:
# If there are no open orders we can rebalance.
rebalance(context, data, exchange_time)
log.info('Rebalanced portfolio to target weights at %s' % exchange_time)
# Update the current and next rebalance dates
context.rebalance_date = exchange_time
context.next_rebalance_date = context.rebalance_date + datetime.timedelta(days=context.rebalance_days)
print(context.next_rebalance_date)
def rebalance(context,data,exchange_time):
for sid in context.sids:
if data.can_trade(sid):
order_for_robinhood(context, sid, context.sids[sid])
def has_open_orders(data,context):
# Only rebalance when we have zero pending orders.
has_orders = False
open_orders = get_open_orders()
# open_orders is a dictionary keyed by sid, with values that are lists of orders.
if open_orders:
# iterate over the dictionary
for security, orders in open_orders.iteritems():
# iterate over the orders
for oo in orders:
message = 'Open order for {amount} shares in {stock}'
message = message.format(amount=oo.amount, stock=security)
log.info(message)
has_orders = True
return has_orders
def get_percent_held(context, security, portfolio_value):
"""
This calculates the percentage of each security that we currently
hold in the portfolio.
"""
if security in context.portfolio.positions:
position = context.portfolio.positions[security]
value_held = position.last_sale_price * position.amount
percent_held = value_held/float(portfolio_value)
return percent_held
else:
# If we don't hold any positions, return 0%
return 0.0
def order_for_robinhood(context, security, weight,
order_style=None):
"""
This is a custom order method for this particular algorithm and
places orders based on:
(1) How much of each position in context.assets we currently hold
(2) How much cash we currently hold
This means that if you have existing positions (e.g. AAPL),
your positions in that security will not be taken into
account when calculating order amounts.
The portfolio value that we'll be ordering on is labeled
`valid_portfolio_value`.
If you'd like to use a Stop/Limit/Stop-Limit Order please follow the
following format:
STOP - order_style = StopOrder(stop_price)
LIMIT - order_style = LimitOrder(limit_price)
STOPLIMIT - order_style = StopLimitOrder(limit_price=x, stop_price=y)
"""
# We use .95 as the cash because all market orders are converted into
# limit orders with a 5% buffer. So any market order placed through
# Robinhood is submitted as a limit order with (last_traded_price * 1.05)
valid_portfolio_value = context.portfolio.cash * .95
for s in context.sids:
# Calculate dollar amount of each position in context.assets
# that we currently hold
if s in context.portfolio.positions:
position = context.portfolio.positions[s]
valid_portfolio_value += position.last_sale_price * \
position.amount
# Calculate the percent of each security that we want to hold
percent_to_order = weight - get_percent_held(context,
security,
valid_portfolio_value)
# If within 1% of target weight, ignore.
if abs(percent_to_order) < .01:
return
# Calculate the dollar value to order for this security
value_to_order = percent_to_order * valid_portfolio_value
if order_style:
return order_value(security, value_to_order, style=order_style)
else:
return order_value(security, value_to_order)
I recommend that you personally verify that this will serve the behavior that you want.