Skip to content

Commit

Permalink
Merge 45b27ce into ec055b6
Browse files Browse the repository at this point in the history
  • Loading branch information
twiecki committed Jan 13, 2015
2 parents ec055b6 + 45b27ce commit 0135574
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 6 deletions.
20 changes: 20 additions & 0 deletions docs/release-notes/zipline-0.7.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,23 @@
> - shorts count
> - longs count
* Forward arguments from __init__ to the user-defined initialize().
[PR456](https://github.com/quantopian/zipline/pull/456)

> If you used the new way of creating an algorithm by defining an
`initialize()` and a `handle_data()` function that you pass to
`TradingAlgorithm` it was not possible to externally set variables in `initialize`. This is quite an impediment to parameter optimization where you want to be able to run the `TradingAlgorithm` many times passing in different parameter values that you set in `initialize()`.

> Example:
> ```python
> def initialize(context, param=0):
> context.param = param
> def handle_data(context, data):
> # use param in some way
> ...
> # Instantiate algorithm, setting param to 3.
> algo = zipline.TradingAlgorithm(initialize,
handle_data,
param=3)
> perf = algo.run(data)
> ```
17 changes: 17 additions & 0 deletions tests/test_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,23 @@ def test_account_in_init(self):

output, _ = drain_zipline(self, zipline)

def test_passing_of_args(self):
"""Test that passing of args and kwargs to initialize via __init__ works
as expected."""
test_algo = TradingAlgorithm(
script="""
def initialize(context, kwarg=False):
context.kwarg = kwarg
def handle_data(context, data):
pass""",
sim_params=self.sim_params,
initialize_params={'kwarg': True}
)
set_algo_instance(test_algo)

self.assertEqual(test_algo.kwarg, True)


class TestHistory(TestCase):
@classmethod
Expand Down
19 changes: 13 additions & 6 deletions zipline/algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ def handle_data(context, data):
AUTO_INITIALIZE = True

def __init__(self, *args, **kwargs):
"""Initialize sids and other state variables.
"""
Initialize sids and other state variables.
:Arguments:
:Optional:
Expand All @@ -139,7 +140,11 @@ def __init__(self, *args, **kwargs):
Whether to fill orders immediately or on next bar.
environment : str <default: 'zipline'>
The environment that this algorithm is running in.
"""
:Note:
All other *args and **kwargs passed will be forwarded to the
user-defined initialize() function.
"""
self.datetime = None

self.registered_transforms = {}
Expand All @@ -150,7 +155,7 @@ def __init__(self, *args, **kwargs):
self.trading_controls = []

self._recorded_vars = {}
self.namespace = kwargs.get('namespace', {})
self.namespace = kwargs.pop('namespace', {})

self._platform = kwargs.pop('platform', 'zipline')

Expand Down Expand Up @@ -258,14 +263,16 @@ def initialize(self, *args, **kwargs):
Call self._initialize with `self` made available to Zipline API
functions.
"""
initialize_params = kwargs.pop('initialize_params', {})

with ZiplineAPI(self):
self._initialize(self)
self._initialize(self, **initialize_params)

def before_trading_start(self):
def before_trading_start(self, *args, **kwargs):
if self._before_trading_start is None:
return

self._before_trading_start(self)
self._before_trading_start(self, *args, **kwargs)

def handle_data(self, data):
self._most_recent_data = data
Expand Down
5 changes: 5 additions & 0 deletions zipline/examples/olmar_ext.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[Defaults]
algofile=olmar_ext.py
symbols=CERN,DLTR,ROST,MSFT,SBUX
start=2010-1-1
end=2014-1-1
175 changes: 175 additions & 0 deletions zipline/examples/olmar_ext.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import sys
import logbook
import numpy as np

zipline_logging = logbook.NestedSetup([
logbook.NullHandler(level=logbook.DEBUG, bubble=True),
logbook.StreamHandler(sys.stdout, level=logbook.INFO),
logbook.StreamHandler(sys.stderr, level=logbook.ERROR),
])
zipline_logging.push_application()

from zipline.api import (
symbols,
schedule_function,
date_rules,
time_rules,
add_history,
history,
order_target_percent,
record,
get_open_orders,
get_datetime
)


def initialize(context, eps=1, window_length=30):
context.stocks = symbols('CERN', 'DLTR', 'ROST', 'MSFT', 'SBUX')
context.i = 0
context.m = len(context.stocks)
context.b_t = np.ones(context.m) / context.m
context.eps = eps
context.window_length = window_length
context.init = False
context.previous_datetime = None
add_history(window_length, '1d', 'price')
add_history(window_length, '1d', 'volume')
schedule_function(daily, date_rule=date_rules.every_day(),
time_rule=time_rules.market_open())


def handle_data(context, data):
pass


def daily(context, data):
context.i += 1
if context.i < context.window_length:
return

cash = context.portfolio.cash
record(cash=cash)

if not context.init:
# Equal weighting portfolio
for stock, percent in zip(context.stocks, context.b_t):
order_target_percent(stock, percent)
context.init = True

# skip tic if any orders are open or any stocks did not trade
for stock in context.stocks:
if bool(get_open_orders(stock)) or \
data[stock].datetime < get_datetime():
return

# compute current portfolio allocations
for i, stock in enumerate(context.stocks):
context.b_t[i] = context.portfolio.positions[
stock].amount * data[stock].price

# Bring portfolio vector to unit length
context.b_t = context.b_t / np.sum(context.b_t)

# Compute new portfolio weights according to OLMAR algo.
size = context.window_length - 2
b_norm = np.zeros((context.m, size))
x_tilde = np.zeros((context.m, size))
for k in range(size):
b_norm[:, k], x_tilde[:, k] = olmar(context, k + 3)

s = np.zeros(size)
b_norm_opt = np.zeros(context.m)
s_sum = 0
for k in range(size):
s[k] = np.dot(b_norm[:, k], x_tilde[:, k])
b_norm[:, k] = np.multiply(s[k], b_norm[:, k])
b_norm_opt += b_norm[:, k]
s_sum += s[k]

b_norm_opt = np.divide(b_norm_opt, s_sum)

print(b_norm_opt)

# Rebalance Portfolio
for stock, percent in zip(context.stocks, b_norm_opt):
order_target_percent(stock, percent)


def olmar(context, window):
"""Logic of the olmar algorithm.
:Returns: b_norm : vector for new portfolio
"""

# get history -- prices and volums of the last 5 days (at close)
p = history(context.window_length, '1d', 'price')
v = history(context.window_length, '1d', 'volume')

prices = p.ix[context.window_length - window:-1]
volumes = v.ix[context.window_length - window:-1]

# find relative moving volume weighted average price for each secuirty
x_tilde = np.zeros(context.m)
for i, stock in enumerate(context.stocks):
vwa_price = np.dot(
prices[stock], volumes[stock]) / np.sum(volumes[stock])
x_tilde[i] = vwa_price / prices[stock].ix[-1]

###########################
# Inside of OLMAR (algo 2)
x_bar = x_tilde.mean()

# Calculate terms for lambda (lam)
dot_prod = np.dot(context.b_t, x_tilde)
num = context.eps - dot_prod
denom = (np.linalg.norm((x_tilde - x_bar))) ** 2

# test for divide-by-zero case
if denom == 0.0:
lam = 0 # no portolio update
else:
lam = max(0, num / denom)

b = context.b_t + lam * (x_tilde - x_bar)

b_norm = simplex_projection(b)

return b_norm, x_tilde


def simplex_projection(v, b=1):
"""Projection vectors to the simplex domain
Implemented according to the paper: Efficient projections onto the
l1-ball for learning in high dimensions, John Duchi, et al. ICML 2008.
Implementation Time: 2011 June 17 by Bin@libin AT pmail.ntu.edu.sg
Optimization Problem: min_{w}\| w - v \|_{2}^{2}
s.t. sum_{i=1}^{m}=z, w_{i}\geq 0
Input: A vector v \in R^{m}, and a scalar z > 0 (default=1)
Output: Projection vector w
:Example:
>>> proj = simplex_projection([.4 ,.3, -.4, .5])
>>> print proj
array([ 0.33333333, 0.23333333, 0. , 0.43333333])
>>> print proj.sum()
1.0
Original matlab implementation: John Duchi (jduchi@cs.berkeley.edu)
Python-port: Copyright 2012 by Thomas Wiecki (thomas.wiecki@gmail.com).
"""

v = np.asarray(v)
p = len(v)

# Sort v into u in descending order
v = (v > 0) * v
u = np.sort(v)[::-1]
sv = np.cumsum(u)

rho = np.where(u > (sv - b) / np.arange(1, p + 1))[0][-1]
theta = np.max([0, (sv[rho] - b) / (rho + 1)])
w = (v - theta)
w[w < 0] = 0
return w
37 changes: 37 additions & 0 deletions zipline/examples/olmar_optimize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from datetime import datetime
import pytz
import numpy as np

from zipline.algorithm import TradingAlgorithm
from zipline.utils.factory import load_bars_from_yahoo

import olmar_ext as olmar

STOCKS = ['CERN', 'DLTR', 'ROST', 'MSFT', 'SBUX']


def run_algo(eps=1, window_length=30):
start = datetime(2010, 1, 1, 0, 0, 0, 0, pytz.utc)
end = datetime(2014, 1, 1, 0, 0, 0, 0, pytz.utc)
data = load_bars_from_yahoo(
stocks=STOCKS, indexes={}, start=start, end=end)

initialize_params = {'eps': eps,
'window_length': window_length}

algo = TradingAlgorithm(initialize=olmar.initialize,
handle_data=olmar.handle_data,
initialize_params=initialize_params)

perf = algo.run(data) # flake8: noqa
# Minimize negative sharpe = maximize sharpe
return -algo.risk_report['twelve_month'][-1]['sharpe']

test_eps = np.linspace(1, 20, 5)
results_eps = map(run_algo, test_eps)
print(results_eps)

test_window_length = np.arange(10, 50, 10)
results_window_length = map(lambda x: run_algo(window_length=x),
test_window_length)
print(results_window_length)

0 comments on commit 0135574

Please sign in to comment.