mirror of
https://github.com/wassname/catalyst.git
synced 2026-06-30 16:47:49 +08:00
168 lines
4.6 KiB
Python
168 lines
4.6 KiB
Python
import sys
|
|
import logbook
|
|
import numpy as np
|
|
|
|
from zipline.finance import commission
|
|
|
|
zipline_logging = logbook.NestedSetup([
|
|
logbook.NullHandler(),
|
|
logbook.StreamHandler(sys.stdout, level=logbook.INFO),
|
|
logbook.StreamHandler(sys.stderr, level=logbook.ERROR),
|
|
])
|
|
zipline_logging.push_application()
|
|
|
|
STOCKS = ['AMD', 'CERN', 'COST', 'DELL', 'GPS', 'INTC', 'MMM']
|
|
|
|
|
|
# On-Line Portfolio Moving Average Reversion
|
|
|
|
# More info can be found in the corresponding paper:
|
|
# http://icml.cc/2012/papers/168.pdf
|
|
def initialize(algo, eps=1, window_length=5):
|
|
algo.stocks = STOCKS
|
|
algo.sids = [algo.symbol(symbol) for symbol in algo.stocks]
|
|
algo.m = len(algo.stocks)
|
|
algo.price = {}
|
|
algo.b_t = np.ones(algo.m) / algo.m
|
|
algo.last_desired_port = np.ones(algo.m) / algo.m
|
|
algo.eps = eps
|
|
algo.init = True
|
|
algo.days = 0
|
|
algo.window_length = window_length
|
|
|
|
algo.set_commission(commission.PerShare(cost=0))
|
|
|
|
|
|
def handle_data(algo, data):
|
|
algo.days += 1
|
|
if algo.days < algo.window_length:
|
|
return
|
|
|
|
if algo.init:
|
|
rebalance_portfolio(algo, data, algo.b_t)
|
|
algo.init = False
|
|
return
|
|
|
|
m = algo.m
|
|
|
|
x_tilde = np.zeros(m)
|
|
|
|
# find relative moving average price for each asset
|
|
mavgs = data.history(algo.sids, 'price', algo.window_length, '1d').mean()
|
|
for i, sid in enumerate(algo.sids):
|
|
price = data.current(sid, "price")
|
|
# Relative mean deviation
|
|
x_tilde[i] = mavgs[sid] / price
|
|
|
|
###########################
|
|
# Inside of OLMAR (algo 2)
|
|
x_bar = x_tilde.mean()
|
|
|
|
# market relative deviation
|
|
mark_rel_dev = x_tilde - x_bar
|
|
|
|
# Expected return with current portfolio
|
|
exp_return = np.dot(algo.b_t, x_tilde)
|
|
weight = algo.eps - exp_return
|
|
variability = (np.linalg.norm(mark_rel_dev)) ** 2
|
|
|
|
# test for divide-by-zero case
|
|
if variability == 0.0:
|
|
step_size = 0
|
|
else:
|
|
step_size = max(0, weight / variability)
|
|
|
|
b = algo.b_t + step_size * mark_rel_dev
|
|
b_norm = simplex_projection(b)
|
|
np.testing.assert_almost_equal(b_norm.sum(), 1)
|
|
|
|
rebalance_portfolio(algo, data, b_norm)
|
|
|
|
# update portfolio
|
|
algo.b_t = b_norm
|
|
|
|
|
|
def rebalance_portfolio(algo, data, desired_port):
|
|
# rebalance portfolio
|
|
desired_amount = np.zeros_like(desired_port)
|
|
current_amount = np.zeros_like(desired_port)
|
|
prices = np.zeros_like(desired_port)
|
|
|
|
if algo.init:
|
|
positions_value = algo.portfolio.starting_cash
|
|
else:
|
|
positions_value = algo.portfolio.positions_value + \
|
|
algo.portfolio.cash
|
|
|
|
for i, sid in enumerate(algo.sids):
|
|
current_amount[i] = algo.portfolio.positions[sid].amount
|
|
prices[i] = data.current(sid, "price")
|
|
|
|
desired_amount = np.round(desired_port * positions_value / prices)
|
|
|
|
algo.last_desired_port = desired_port
|
|
diff_amount = desired_amount - current_amount
|
|
|
|
for i, sid in enumerate(algo.sids):
|
|
algo.order(sid, diff_amount[i])
|
|
|
|
|
|
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])
|
|
>>> proj # doctest: +NORMALIZE_WHITESPACE
|
|
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 2013 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
|
|
|
|
|
|
# Note: this function can be removed if running
|
|
# this algorithm on quantopian.com
|
|
def analyze(context=None, results=None):
|
|
import matplotlib.pyplot as plt
|
|
fig = plt.figure()
|
|
ax = fig.add_subplot(111)
|
|
results.portfolio_value.plot(ax=ax)
|
|
ax.set_ylabel('Portfolio value (USD)')
|
|
plt.show()
|
|
|
|
|
|
def _test_args():
|
|
"""Extra arguments to use when zipline's automated tests run this example.
|
|
"""
|
|
import pandas as pd
|
|
|
|
return {
|
|
'start': pd.Timestamp('2004', tz='utc'),
|
|
'end': pd.Timestamp('2008', tz='utc'),
|
|
}
|