Hi, I built an strategy based on someone else post, which applies Kalman Filter to calculate the hedge ratio, but there is one problem of my algorithm. Also, I include more than one pair in my portfolio.

I manage to keep the leverage of my portfolio under 1, so I use the function computeHoldingsPct(yShares, xShares, yPrice, xPrice) and for each pair I keep the percentage as y_target_pct / float(context.num_pairs), but why my leverage still go over 1 all the time?

Thanks

53
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
 Returns 1 Month 3 Month 6 Month 12 Month
 Alpha 1 Month 3 Month 6 Month 12 Month
 Beta 1 Month 3 Month 6 Month 12 Month
 Sharpe 1 Month 3 Month 6 Month 12 Month
 Sortino 1 Month 3 Month 6 Month 12 Month
 Volatility 1 Month 3 Month 6 Month 12 Month
 Max Drawdown 1 Month 3 Month 6 Month 12 Month
import numpy as np
import pytz

def initialize(context):
# Quantopian backtester specific variables
set_slippage(slippage.VolumeShareSlippage(volume_limit=0.025, price_impact=0.03))
set_commission(commission.PerShare(cost=0.0075))

context.stock_pairs = [(sid(26807), sid(28320)),
(sid(14516), sid(14517))]

context.num_pairs = len(context.stock_pairs)

context.delta = 0.0001
context.Vw = context.delta / (1 - context.delta) * np.eye(2)
context.Ve = 0.001

context.beta = [np.zeros(2)]*context.num_pairs
context.P = [np.zeros((2, 2))]*context.num_pairs
context.R = [None] * context.num_pairs

context.pos = [None] * context.num_pairs
context.day = None

def handle_data(context, data):
# Record and plot the leverage of our portfolio over time.
record(leverage = context.account.leverage)

exchange_time = get_datetime().astimezone(pytz.timezone('US/Eastern'))

# update Kalman filter and exectue a trade during the last 1 hour of trading each day
if exchange_time.hour == 15 and exchange_time.minute >= 0:

# only execute this once per day
if context.day is not None and context.day == exchange_time.day:
return

context.day = exchange_time.day

if get_open_orders():
return

for i in range(context.num_pairs):

(stock_y, stock_x) = context.stock_pairs[i]

x = np.asarray([data[stock_x].price, 1.0]).reshape((1, 2))
y = data[stock_y].price

# update Kalman filter with latest price
if context.R[i] is not None:
context.R[i] = context.P[i] + context.Vw
else:
context.R[i] = np.zeros((2, 2))

yhat = x.dot(context.beta[i])

Q = x.dot(context.R[i]).dot(x.T) + context.Ve
sqrt_Q = np.sqrt(Q)
e = y - yhat
K = context.R[i].dot(x.T) / Q
context.beta[i] = context.beta[i] + K.flatten() * e
context.P[i] = context.R[i] - K * x.dot(context.R[i])

if context.pos[i] is not None:

if context.pos[i] == 'long' and e >= -sqrt_Q:
#log.info('closing long')
order_target(stock_x, 0)
order_target(stock_y, 0)
context.pos[i] = None

elif context.pos[i] == 'short' and e < sqrt_Q:
#log.info('closing short')
order_target(stock_x, 0)
order_target(stock_y, 0)
context.pos[i] = None

if context.pos[i] is None:
if e < -sqrt_Q:
#log.info('opening long')
y_target_shares = 1
x_target_shares = -context.beta[i][0]
(y_target_pct, x_target_pct) = computeHoldingsPct(y_target_shares,x_target_shares, y, x[0,0])

order_target_percent(stock_y , y_target_pct / float(context.num_pairs))
order_target_percent(stock_x , x_target_pct / float(context.num_pairs))
context.pos[i] = 'long'

elif e > sqrt_Q:
y_target_shares = -1
x_target_shares = context.beta[i][0]
#log.info('opening short')
(y_target_pct, x_target_pct) = computeHoldingsPct(y_target_shares,x_target_shares, y, x[0,0])

order_target_percent(stock_y , y_target_pct / float(context.num_pairs))
order_target_percent(stock_x , x_target_pct / float(context.num_pairs))
context.pos[i] = 'short'

def computeHoldingsPct(yShares, xShares, yPrice, xPrice):
yDol = yShares * yPrice
xDol = xShares * xPrice
notionalDol =  abs(yDol) + abs(xDol)
y_target_pct = yDol / notionalDol
x_target_pct = xDol / notionalDol
return (y_target_pct, x_target_pct)
There was a runtime error.
6 responses

Ryan,

I'm not 100% on how that is happening, but I think it is some interaction of order_target and order_target_percent. If you reorder your logic so that you only every make one call to order_target_percent per security, per day, then that would potentially solve the problem.

If you replace lines 88-89 and 99-100 with...

                    qty_y=int((y_target_pct / float(context.num_pairs))*context.portfolio.portfolio_value/data[stock_y].price)
qty_x=int((x_target_pct / float(context.num_pairs))*context.portfolio.portfolio_value/data[stock_x].price)
order(stock_y, qty_y)
order(stock_x, qty_x)


...does it work like you would expect?

You may want to examine the following.

Clone the backtest and look at the position values and transactions details for the last day.

See log file:

2011-05-31 PRINT executing
2011-05-31 PRINT Currently have: (0, 0) of (Equity(28320 [USO]), Equity(26807 [GLD]))
2011-05-31 handle_data:107 INFO opening short
2011-05-31 PRINT X: Ordered 0.0 of Equity(28320 [USO]) (TARGET qty=0.0)
2011-05-31 PRINT Y: Ordered -0.5 of Equity(26807 [GLD]) (TARGET qty=-66.8717400027)
2011-05-31 PRINT Currently have: (0, 0) of (Equity(14517 [EWC]), Equity(14516 [EWA]))
2011-05-31 handle_data:107 INFO opening short
2011-05-31 PRINT X: Ordered 0.0 of Equity(14517 [EWC]) (TARGET qty=0.0)
2011-05-31 PRINT Y: Ordered -0.5 of Equity(14516 [EWA]) (TARGET qty=-376.081233546)
2011-06-01 PRINT executing
2011-06-01 PRINT Currently have: (0, -66) of (Equity(28320 [USO]), Equity(26807 [GLD]))
2011-06-01 PRINT Currently have: (0, -376) of (Equity(14517 [EWC]), Equity(14516 [EWA]))
2011-06-02 PRINT executing
2011-06-02 PRINT Currently have: (0, -66) of (Equity(28320 [USO]), Equity(26807 [GLD]))
2011-06-02 handle_data:82 INFO closing short: Equity(28320 [USO]),Equity(26807 [GLD])
2011-06-02 handle_data:90 INFO opening long
2011-06-02 PRINT X: Ordered -0.249924421497 of Equity(28320 [USO]) (TARGET qty=-127.141834195)
2011-06-02 PRINT Y: Ordered 0.250075578503 of Equity(26807 [GLD]) (TARGET qty=33.803291719)
2011-06-02 PRINT Currently have: (0, -376) of (Equity(14517 [EWC]), Equity(14516 [EWA]))
2011-06-02 handle_data:82 INFO closing short: Equity(14517 [EWC]),Equity(14516 [EWA])
2011-06-03 PRINT executing
2011-06-03 PRINT Currently have: (-127, 99) of (Equity(28320 [USO]), Equity(26807 [GLD]))
2011-06-03 handle_data:76 INFO closing long: Equity(28320 [USO]),Equity(26807 [GLD])
2011-06-03 handle_data:107 INFO opening short
2011-06-03 PRINT X: Ordered 0.249915937777 of Equity(28320 [USO]) (TARGET qty=127.676488314)
2011-06-03 PRINT Y: Ordered -0.250084062223 of Equity(26807 [GLD]) (TARGET qty=-33.7519884182)
2011-06-03 PRINT Currently have: (0, 0) of (Equity(14517 [EWC]), Equity(14516 [EWA]))

7
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
 Returns 1 Month 3 Month 6 Month 12 Month
 Alpha 1 Month 3 Month 6 Month 12 Month
 Beta 1 Month 3 Month 6 Month 12 Month
 Sharpe 1 Month 3 Month 6 Month 12 Month
 Sortino 1 Month 3 Month 6 Month 12 Month
 Volatility 1 Month 3 Month 6 Month 12 Month
 Max Drawdown 1 Month 3 Month 6 Month 12 Month
import numpy as np
import pytz

def initialize(context):
# Quantopian backtester specific variables
set_slippage(slippage.VolumeShareSlippage(volume_limit=0.025, price_impact=0.03))
set_commission(commission.PerShare(cost=0.0075))

context.stock_pairs = [(sid(26807), sid(28320)),
(sid(14516), sid(14517))]

context.num_pairs = len(context.stock_pairs)

context.delta = 0.0001
context.Vw = context.delta / (1 - context.delta) * np.eye(2)
context.Ve = 0.001

context.beta = [np.zeros(2)]*context.num_pairs
context.P = [np.zeros((2, 2))]*context.num_pairs
context.R = [None] * context.num_pairs

context.pos = [None] * context.num_pairs
context.day = None

def handle_data(context, data):
# Record and plot the leverage of our portfolio over time.
record(leverage = context.account.leverage)

exchange_time = get_datetime().astimezone(pytz.timezone('US/Eastern'))

# update Kalman filter and exectue a trade during the last 1 hour of trading each day
if exchange_time.hour == 15 and exchange_time.minute >= 0:

# only execute this once per day
if context.day is not None and context.day == exchange_time.day:
return

context.day = exchange_time.day

if get_open_orders():
return

print "executing"

for i in range(context.num_pairs):

(stock_y, stock_x) = context.stock_pairs[i]

qty_x = context.portfolio.positions[stock_x].amount
qty_y = context.portfolio.positions[stock_y].amount
print "Currently have: ({}, {}) of ({}, {})".format(qty_x,qty_y,stock_x,stock_y)

x = np.asarray([data[stock_x].price, 1.0]).reshape((1, 2))
y = data[stock_y].price

# update Kalman filter with latest price
if context.R[i] is not None:
context.R[i] = context.P[i] + context.Vw
else:
context.R[i] = np.zeros((2, 2))

yhat = x.dot(context.beta[i])

Q = x.dot(context.R[i]).dot(x.T) + context.Ve
sqrt_Q = np.sqrt(Q)
e = y - yhat
K = context.R[i].dot(x.T) / Q
context.beta[i] = context.beta[i] + K.flatten() * e
context.P[i] = context.R[i] - K * x.dot(context.R[i])

if context.pos[i] is not None:

if context.pos[i] == 'long' and e >= -sqrt_Q:
log.info('closing long: {},{}'.format(stock_x,stock_y))
order_target(stock_x, 0)
order_target(stock_y, 0)
context.pos[i] = None

elif context.pos[i] == 'short' and e < sqrt_Q:
log.info('closing short: {},{}'.format(stock_x,stock_y))
order_target(stock_x, 0)
order_target(stock_y, 0)
context.pos[i] = None

if context.pos[i] is None:
if e < -sqrt_Q:
log.info('opening long')
y_target_shares = 1
x_target_shares = -context.beta[i][0]
(y_target_pct, x_target_pct) = computeHoldingsPct(y_target_shares,x_target_shares, y, x[0,0])
pct_x = x_target_pct / float(context.num_pairs)
pct_y = y_target_pct / float(context.num_pairs)
order_target_percent(stock_y , pct_y)
order_target_percent(stock_x , pct_x)
target_qty_x = (pct_x * context.portfolio.portfolio_value) / data[stock_x].price
target_qty_y = (pct_y * context.portfolio.portfolio_value) / data[stock_y].price
print "X: Ordered {} of {} (TARGET qty={})".format(pct_x, stock_x, target_qty_x)
print "Y: Ordered {} of {} (TARGET qty={})".format(pct_y, stock_y, target_qty_y)

context.pos[i] = 'long'

elif e > sqrt_Q:
log.info('opening short')
y_target_shares = -1
x_target_shares = context.beta[i][0]
(y_target_pct, x_target_pct) = computeHoldingsPct(y_target_shares,x_target_shares, y, x[0,0])

pct_x = x_target_pct / float(context.num_pairs)
pct_y = y_target_pct / float(context.num_pairs)
order_target_percent(stock_y , pct_y)
order_target_percent(stock_x , pct_x)
target_qty_x = (pct_x * context.portfolio.portfolio_value) / data[stock_x].price
target_qty_y = (pct_y * context.portfolio.portfolio_value) / data[stock_y].price
print "X: Ordered {} of {} (TARGET qty={})".format(pct_x, stock_x, target_qty_x)
print "Y: Ordered {} of {} (TARGET qty={})".format(pct_y, stock_y, target_qty_y)
context.pos[i] = 'short'

def computeHoldingsPct(yShares, xShares, yPrice, xPrice):
yDol = yShares * yPrice
xDol = xShares * xPrice
notionalDol =  abs(yDol) + abs(xDol)
y_target_pct = yDol / notionalDol
x_target_pct = xDol / notionalDol
return (y_target_pct, x_target_pct)
There was a runtime error.

Sorry for so many posts - I just remembered order_target_percent doesn't account for already submitted orders, so that's why.

How would you fix this then...i'm shooting a blank

"There's more than one way to do it"

16
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
 Returns 1 Month 3 Month 6 Month 12 Month
 Alpha 1 Month 3 Month 6 Month 12 Month
 Beta 1 Month 3 Month 6 Month 12 Month
 Sharpe 1 Month 3 Month 6 Month 12 Month
 Sortino 1 Month 3 Month 6 Month 12 Month
 Volatility 1 Month 3 Month 6 Month 12 Month
 Max Drawdown 1 Month 3 Month 6 Month 12 Month
import numpy as np
import pytz

def initialize(context):
# Quantopian backtester specific variables
set_slippage(slippage.VolumeShareSlippage(volume_limit=0.025, price_impact=0.03))
set_commission(commission.PerShare(cost=0.0075))

context.stock_pairs = [(sid(26807), sid(28320)),
(sid(14516), sid(14517))]

context.num_pairs = len(context.stock_pairs)

context.delta = 0.0001
context.Vw = context.delta / (1 - context.delta) * np.eye(2)
context.Ve = 0.001

context.beta = [np.zeros(2)]*context.num_pairs
context.P = [np.zeros((2, 2))]*context.num_pairs
context.R = [None] * context.num_pairs

context.pos = [None] * context.num_pairs
context.day = None

def handle_data(context, data):
# Record and plot the leverage of our portfolio over time.
record(leverage = context.account.leverage)

exchange_time = get_datetime().astimezone(pytz.timezone('US/Eastern'))

# update Kalman filter and exectue a trade during the last 1 hour of trading each day
if exchange_time.hour == 15 and exchange_time.minute >= 0:

# only execute this once per day
if context.day is not None and context.day == exchange_time.day:
return

context.day = exchange_time.day

if get_open_orders():
return

for i in range(context.num_pairs):

OrderTargetPct = {} # keep track of what to order (if anything)

(stock_y, stock_x) = context.stock_pairs[i]

x = np.asarray([data[stock_x].price, 1.0]).reshape((1, 2))
y = data[stock_y].price

# update Kalman filter with latest price
if context.R[i] is not None:
context.R[i] = context.P[i] + context.Vw
else:
context.R[i] = np.zeros((2, 2))

yhat = x.dot(context.beta[i])

Q = x.dot(context.R[i]).dot(x.T) + context.Ve
sqrt_Q = np.sqrt(Q)
e = y - yhat
K = context.R[i].dot(x.T) / Q
context.beta[i] = context.beta[i] + K.flatten() * e
context.P[i] = context.R[i] - K * x.dot(context.R[i])

if context.pos[i] is not None:

if context.pos[i] == 'long' and e >= -sqrt_Q:
#log.info('closing long')
OrderTargetPct[stock_x] = 0
OrderTargetPct[stock_y] = 0
context.pos[i] = None

elif context.pos[i] == 'short' and e < sqrt_Q:
#log.info('closing short')
OrderTargetPct[stock_x] = 0
OrderTargetPct[stock_y] = 0
context.pos[i] = None

if context.pos[i] is None:
if e < -sqrt_Q:
#log.info('opening long')
y_target_shares = 1
x_target_shares = -context.beta[i][0]
(y_target_pct, x_target_pct) = computeHoldingsPct(y_target_shares,x_target_shares, y, x[0,0])

OrderTargetPct[stock_x] = x_target_pct / float(context.num_pairs)
OrderTargetPct[stock_y] = y_target_pct / float(context.num_pairs)

context.pos[i] = 'long'

elif e > sqrt_Q:
y_target_shares = -1
x_target_shares = context.beta[i][0]
#log.info('opening short')
(y_target_pct, x_target_pct) = computeHoldingsPct(y_target_shares,x_target_shares, y, x[0,0])

OrderTargetPct[stock_x] = x_target_pct / float(context.num_pairs)
OrderTargetPct[stock_y] = y_target_pct / float(context.num_pairs)

context.pos[i] = 'short'

# order
for sid in OrderTargetPct.keys():
order_target_percent(sid, OrderTargetPct[sid])

def computeHoldingsPct(yShares, xShares, yPrice, xPrice):
yDol = yShares * yPrice
xDol = xShares * xPrice
notionalDol =  abs(yDol) + abs(xDol)
y_target_pct = yDol / notionalDol
x_target_pct = xDol / notionalDol
return (y_target_pct, x_target_pct)
There was a runtime error.

@James, thanks, so what does the code below do exactly?

for sid in OrderTargetPct.keys():
order_target_percent(sid, OrderTargetPct[sid])