Hi Nyan,
I created this algorithm before 'history()' was released. 'batch_transform' is very outdated and we don't recommend you to use it anymore, instead please use 'history()' which allows you to query for X amount of historical data starting from the backtester's current trading date.
So if you wanted the past 20 days of trading data you would do:
'prices = history(20, '1d', 'price')'
The last version that I have here uses history to query for past data, feel free to use this one instead.
'''
Linear Regression Curves vs. Bollinger Bands
If Close price is greater than average+n*deviation, go short
If Close price is less than average+n*deviation, go long
Both should close when you cross the average/mean
'''
import numpy as np
from scipy import stats
from pytz import timezone, utc
from datetime import datetime, timedelta
from zipline.utils.tradingcalendar import get_early_closes
def initialize(context):
# Enter sid here to use the algo with a single stock
context.stock = sid(8554)
context.dev_multiplier = 2
context.max_notional = 1000000
context.min_notional = -1000000
# Days traded to ensure that we trade only once every twenty days
context.days_traded = 0
# Using an unimportant date just to initialize current_day
context.current_day = 0
# Gets all early close dates
start = datetime(1993, 1, 1, tzinfo=utc)
end = datetime(2050, 1, 1, tzinfo=utc)
context.early_closes = get_early_closes(start,end).date
# Minutes before close that you want to execute your order, so this will execute at 3:55 PM only
context.minutes_early = 5
context.past_prices = None
def handle_data(context, data):
if context.current_day != get_datetime().day:
context.current_day = get_datetime().day
context.days_traded += 1
dev_mult = context.dev_multiplier
notional = context.portfolio.positions_value
context.past_prices = history(20, '1d', 'price')
# Calls get_linear so that moving_average has something to reference by the time it is called
linear = get_linear(context, data)
# Only checks every 20 days
if context.days_traded%20 == 0:
try:
# Uses context.stock
close = data[context.stock].price
moving_average = linear[context.stock]
moving_dev = data[context.stock].stddev(20)
high_band = moving_average + dev_mult*moving_dev
low_band = moving_average - dev_mult*moving_dev
# If close price is greater than band, short 5000 and if less, buy 5000
if close > high_band and notional > context.min_notional:
order(context.stock, -5000)
log.debug("Shorting 5000")
elif close < low_band and notional < context.max_notional:
order(context.stock, 5000)
log.debug("Going long 5000")
except:
return
# Linear regression curve that returns the intercept the curve
# Uses the past 20 days
def get_linear(context, data):
days = [i for i in range(1,21)]
stocks = {}
# Checks if data is emtpty
if len(context.past_prices) < 20:
return
for stock in data:
linear = stats.linregress(days, context.past_prices[stock])[1]
stocks[stock] = linear
return stocks
# Returns True if it's the end of day and False otherwise
def endofday_check(context, minutes_early):
# Converts all time-zones into US EST to avoid confusion
loc_dt = get_datetime().astimezone(timezone('US/Eastern'))
date = get_datetime().date()
# Checks for an early close on special dates such as holidays and the day after thanksgiving
# The market closes at 1:00PM EST on those days
if date in context.early_closes:
# Returns true if it's 1:00PM - minutes so in this case 12:55PM
if loc_dt.hour == 12 and loc_dt.minute == (44-minutes_early):
return True
else:
return False
# Returns true if it's 4:00PM EST - minutes so in this case at 3:40PM
# Daylight savings time are accounted for, so it will automatically adjust to DST
elif loc_dt.hour == 15 and loc_dt.minute == (44-minutes_early):
return True
else:
return False
Seong