# Picking stocks based on the Kelly criterion
import math
import numpy as np
import pandas
# Pick stocks with the shortest time for Vn > V0 with ~98% probability
def time(context,data,g,mean,var):
k = 2.0 # Standard deviation
t = (k**2.0*var*context.f**2.0/g**2.0)
t.sort()
return t.head(context.securities)
# Pick stocks with the least variance
def var(context,data,g,mean,var):
var.sort()
return var.head(context.securities)
# Pick stocks with the highest estimated growth rate
def growth(context,data,g,mean,var):
return g.tail(context.securities)
# The initialize function is the place to set your tradable universe and define any parameters.
def initialize(context):
set_do_not_order_list(security_lists.leveraged_etf_list)
set_universe(universe.DollarVolumeUniverse(floor_percentile=95.0,ceiling_percentile=100.0))
context.securities = 10
context.delisted = []
context.lookback = 252
context.apr_min_filter = 0.01
context.apr_max_filter = 1.00
context.picks = pandas.Series()
# bet size of each security for estimated growth
# multiplied by 2 to underbet (half kelly)
context.f = 1.0/context.securities*2.0
context.pick_option = 2
context.pick_method = {
1 : time,
2 : var,
3 : growth,
}
schedule_function(repick,
date_rules.month_start(),
time_rules.market_open(hours = 0, minutes = 15))
schedule_function(rebalance,
date_rules.week_start(),
time_rules.market_open(hours = 1, minutes = 0))
def handle_data(context,data):
if not '_peak_leverage' in context:
context._peak_leverage = 0
if not '_min_cash' in context:
context._min_cash = context.portfolio.cash
context._peak_leverage = max(context._peak_leverage,context.account.leverage)
context._min_cash = min(context._min_cash,context.portfolio.cash)
# record(peak_lev = context._peak_leverage)
record(leverage = context.account.leverage)
# record(min_cash = context._min_cash)
pos_sum = 0
delisted_sum = 0
for sec,v in context.portfolio.positions.iteritems():
if sec in context.picks.index:
pos_sum += np.abs(v.amount*v.last_sale_price)
elif sec in context.delisted:
delisted_sum += np.abs(v.amount*v.last_sale_price)
record(correct_lev = pos_sum/context.portfolio.portfolio_value)
record(delisted_lev = delisted_sum/context.portfolio.portfolio_value)
def repick(context,data):
prices = history(bar_count=context.lookback, frequency='1d', field='price').dropna(axis=1)
r = prices.pct_change().dropna()
mean = r.mean()
var = r.var()
# g = r + f(m-r) - s^2*f^2/2
# instantaneous growth rate with assumed bet size and risk free r
f = context.f
g = mean*f - (var*f**2)/2
gyr = g.apply(lambda x: np.exp(x*252.0)-1.0)
g = (g[gyr <= context.apr_max_filter*f])[gyr >= context.apr_min_filter*f]
g = g[~g.index.isin(context.delisted)]
g = g[~g.index.isin(security_lists.leveraged_etf_list)]
mean = mean[g.index]
var = var[g.index]
context.picks = context.pick_method[context.pick_option](context,data,g,mean,var)
record(delisted = len(context.delisted))
log.info('Picks: {}'.format(context.picks))
def rebalance(context,data):
positions = 0
for security,w in context.picks.iteritems():
if security not in data:
repick(context,data)
break
for sec,v in context.portfolio.positions.iteritems():
if sec not in context.picks.index:
order_target_value(sec,0)
if data[sec].sid.end_date < get_datetime():
if not (sec in context.delisted):
context.delisted.append(sec)
for security,w in context.picks.iteritems():
if get_open_orders(security): continue
if security in data:
order_target_percent(security,1.0/context.securities)
positions += 1
# record(picked_size = context.picks.size)
record(positions = positions)