Looking for thoughts on how to boost my returns...

Would appreciate any thoughts on how I can maximise my return on this algorithm. Perhaps I've over-complicated things; but I don't seem to be fully exploiting the upside of the index where possible.

2
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
# This is a sample mean-reversion algorithm on Quantopian to test and adapt.

# Algorithm investment thesis:
# Top-performing stocks from last week will do worse this week, and vice-versa.

# Every Monday, rank high-volume stocks based on their previous 5 day returns.
# Long the bottom 15% of stocks with the WORST returns over the past 5 days.
# Short the   top 15% of stocks with the BEST  returns over the past 5 days.

# This type of algorithm may be used in live trading and in the Quantopian Open.

# Import the libraries to use
import numpy as np

# The initialize function is the place to set the tradable universe and define any parameters.
def initialize(context):

# Use the top 5% of stocks defined by average daily trading volume.
set_universe(universe.DollarVolumeUniverse(95, 100))

# set trading guard to avoid ordering over-leveraged ETFs
set_do_not_order_list(security_lists.leveraged_etf_list)

# Define the other variables
context.long_leverage    =  1.0
context.short_leverage   = -1.0    # higher = lower beta
context.lower_percentile = 15
context.upper_percentile = 85
context.returns_lookback = 4

# Rebalance every day (or the first trading day if it's a holiday).
# At 11AM ET, which is 1 hour and 30 minutes after market open.
schedule_function(rebalance,
date_rules.every_day(),
time_rules.market_open(hours=1, minutes=30))

schedule_function(info, date_rules.every_day(), time_rules.market_close())
c = context
c.cash_low = c.portfolio.starting_cash
c.max_lvrg = 0
c.max_shrt = 0
c.risk_hi  = 0
c.date_prv = ''
c.date_end = str(get_environment('end').date())
print '{} to {}  {}'.format(str(get_datetime().date()) , c.date_end, int(c.cash_low))

def handle_data(context,data):
#record(leverage = context.account.leverage)

# Also want to monitor the number of long and short positions
# in the portfolio over time. This loop will check positition sizes
# and add the count of longs and shorts to the plot.
longs = shorts = 0
for position in context.portfolio.positions.itervalues():
if position.amount > 0:
longs += 1
if position.amount < 0:
shorts += 1
#record(long_count=longs, short_count=shorts)

def rebalance(context,data):
# Get the last N days of prices for every stock in the universe.
prices = history(context.returns_lookback, '1d', 'price')
# Calculate the past 5 days' returns for each security.
returns = (prices.iloc[-1] - prices.iloc) / prices.iloc
# Remove stocks with missing prices.
# Remove any stocks ordered last time that still have open orders.
# Get the cutoff return percentiles for the long and short portfolios.
returns = returns.dropna()
open_orders = get_open_orders()
if open_orders:
eligible_secs = [sec for sec in data if sec not in open_orders]
returns = returns[eligible_secs]

# Lower percentile is the threshhold for the bottom 15%, upper percentile is for the top 15%.
lower, upper = np.percentile(returns, [context.lower_percentile,
context.upper_percentile])
# Select the X% worst performing securities to go long.
long_secs = returns[returns <= lower]
# Select the Y% best performing securities to short.
short_secs = returns[returns >= upper]
# Set the allocations to even weights in each portfolio.
long_weight = context.long_leverage / len(long_secs)
short_weight = context.short_leverage / len(short_secs)
for security in data:

# Check if the security is eligible in the Quantopian Open
if security in security_lists.leveraged_etf_list:
log.info("%s is an over-leveraged ETF, not purchasing" % (security.symbol))

# Buy/rebalance securities in the long leg of the portfolio.
elif security in long_secs:
if get_open_orders(security): continue
order_target_percent(security, long_weight)

# Sell/rebalance securities in the short leg of the portfolio.
elif security in short_secs:
if get_open_orders(security): continue
order_target_percent(security, short_weight)

# Close any positions that fell out of the list of securities to long or short.
else:
if get_open_orders(security): continue
if context.portfolio.positions[security].amount == 0: continue
order_target(security, 0)
log.info("This week's longs: "+", ".join([long_.symbol for long_ in long_secs.index]))
log.info("This week's shorts: "  +", ".join([short_.symbol for short_ in short_secs.index]))

def info(context, data):
''' Custom chart and/or log of profit_vs_risk returns and related information
'''
# # # # # # # # # #  Options  # # # # # # # # # #
record_max_lvrg = 1          # maximum leverage encountered
record_leverage = 1          # Leverage (context.account.leverage)
record_q_return = 0          # Quantopian returns (percentage)
record_pvr      = 1          # Profit vs Risk returns (percentage)
record_pnl      = 0          # Profit-n-Loss
record_shorting = 1          # Total value of any shorts
record_risk     = 0          # Risked, maximum cash spent or shorts in excess of cash at any time
record_risk_hi  = 1          # Highest risk overall
record_cash     = 0          # Cash available
record_cash_low = 0          # Any new lowest cash level
logging         = 1          # Also log to the logging window conditionally (1) or not (0)
log_method      = 'risk_hi'  # 'daily' or 'risk_hi'

c = context                          # For brevity
new_cash_low = 0                     # To trigger logging in cash_low case
date = str(get_datetime().date())    # To trigger logging in daily case
cash = c.portfolio.cash

if int(cash) < c.cash_low:    # New cash low
new_cash_low = 1
c.cash_low   = int(cash)
if record_cash_low:
record(CashLow = int(c.cash_low))

pvr_rtrn      = 0        # Profit vs Risk returns based on maximum spent
q_rtrn        = 0        # Returns by Quantopian
profit_loss   = 0        # Profit-n-loss
shorts        = 0        # Shorts value
start         = c.portfolio.starting_cash
cash_dip      = int(max(0, start - cash))

if record_cash:
record(cash = int(c.portfolio.cash))  # Cash

if record_leverage:
record(Lvrg = c.account.leverage)     # Leverage

if record_max_lvrg:
if c.account.leverage > c.max_lvrg:
c.max_lvrg = c.account.leverage
record(MaxLvrg = c.max_lvrg)      # Maximum leverage

if record_pnl:
profit_loss = c.portfolio.pnl
record(PnL = profit_loss)             # "Profit and Loss" in dollars

for p in c.portfolio.positions:
shrs = c.portfolio.positions[p].amount
if shrs < 0:
shorts += int(abs(shrs * data[p].price))

if record_shorting:
record(Shorts = shorts)               # Shorts value as a positve

# Shorts in excess of cash to cover them, a positive value
shorts_excess = int(shorts - cash) if shorts > cash else 0
c.max_shrt    = int(max(c.max_shrt, shorts_excess))

risk = int(max(cash_dip, shorts_excess, shorts))
if record_risk:
record(Risk = risk)                   # Amount in play, maximum of shorts or cash used

new_risk_hi = 0
if risk > c.risk_hi:
c.risk_hi = risk
new_risk_hi = 1

if record_risk_hi:
record(Risk_hi = c.risk_hi)       # Highest risk overall

if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)
if c.risk_hi != 0:     # Avoid zero-divide
pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.risk_hi
record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

if record_q_return:
q_rtrn = 100 * (c.portfolio.portfolio_value - start) / start
record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

from pytz import timezone
if logging:
if log_method == 'risk_hi' and new_risk_hi \
or log_method == 'daily' and c.date_prv != date \
or c.date_end == date \
or new_cash_low:
mxlv   = 'MaxLv '   + '%.1f' % c.max_lvrg   if record_max_lvrg else ''
qret   = 'QRet '    + '%.1f' % q_rtrn       if record_q_return else ''
pvr    = 'PvR_Ret ' + '%.1f' % pvr_rtrn     if record_pvr      else ''
pnl    = 'PnL '     + '%.0f' % profit_loss  if record_pnl      else ''
csh    = 'Cash '    + '%.0f' % cash         if record_cash     else ''
csh_lw = 'CshLw '   + '%.0f' % c.cash_low   if record_cash_low else ''
shrt   = 'Shrt '    + '%.0f' % shorts       if record_shorting else ''
risk   = 'Risk '    + '%.0f' % risk         if record_risk     else ''
rsk_hi = 'RskHi '   + '%.0f' % c.risk_hi    if record_risk_hi  else ''
minute = get_datetime().astimezone(timezone('US/Eastern')).time().minute
log.info('{} {} {} {} {} {} {} {} {} {}'.format(
minute, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, risk, rsk_hi))

if c.date_end == date:    # Log on last day, like cash 125199  portfolio 126890
log.info('cash {}  portfolio {}'.format(
int(cash), int(c.portfolio.portfolio_value)))

c.date_prv = date

There was a runtime error.
1 response

Hi Bevan, your other code from 'Returns on Algorithms Remain at ...' was similar and returning $1.19 per dollar activated. A couple of the main differences are the universe settings, leverages and percentiles, schedule and then skipping leveraged ETF's (I placed them side-by-side using CompareIt). mmmk, I'm a little confused here, I was going to repost that very algo for your convenience however I have 6 backtest versions of your code and #6 was bad and then #5 was even higher at 135 PvR, don't recall what was up with it so just tossing that out there now, inviting any criticisms. 13 Loading... 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 # This is a sample mean-reversion algorithm on Quantopian to test and adapt. # Algorithm investment thesis: # Top-performing stocks from last week will do worse this week, and vice-versa. # Every Monday, rank high-volume stocks based on their previous 5 day returns. # Long the bottom 15% of stocks with the WORST returns over the past 5 days. # Short the top 15% of stocks with the BEST returns over the past 5 days. # This type of algorithm may be used in live trading and in the Quantopian Open. # Import the libraries to use import numpy as np # The initialize function is the place to set the tradable universe and define any parameters. def initialize(context): # Use the top 0.5% of stocks defined by average daily trading volume. set_universe(universe.DollarVolumeUniverse(99.5, 100)) # Set execution cost assumptions. For live trading with Interactive Brokers # assume a$1.00 minimum per trade fee, with a per share cost of \$0.0075.
# Set market impact assumptions. Limit the simulation to
#   trade up to 2.5% of the traded volume for any one minute,
#   and price impact constant is 0.1.
set_slippage(slippage.VolumeShareSlippage(volume_limit=0.025, price_impact=0.10))
# Define the other variables
context.long_leverage    =  .9
context.short_leverage   = -.3    # higher = lower beta
context.lower_percentile = 8
context.upper_percentile = 93
context.returns_lookback = 4
# Rebalance every Monday (or the first trading day if it's a holiday).
# At 11AM ET, which is 1 hour and 30 minutes after market open.
#schedule_function(rebalance,
#                  date_rules.week_start(days_offset=0),
#                  time_rules.market_open(hours = 1, minutes = 30))
schedule_function(rebalance,
date_rules.week_start(days_offset=0),
time_rules.market_close())

schedule_function(info, date_rules.every_day(), time_rules.market_close())
c = context
c.cash_low = c.portfolio.starting_cash
c.max_lvrg = 0
c.max_shrt = 0
c.risk_hi  = 0
c.date_prv = ''
c.date_end = str(get_environment('end').date())
print '{} to {}  {}'.format(str(get_datetime().date()) , c.date_end, int(c.cash_low))

def handle_data(context,data):
#record(leverage = context.account.leverage)
return

def rebalance(context,data):
# Also want to monitor the number of long and short positions
# in the portfolio over time. This loop will check positition sizes
# and add the count of longs and shorts to the plot.
longs = shorts = 0
for position in context.portfolio.positions.itervalues():
if position.amount > 0:
longs += 1
if position.amount < 0:
shorts += 1
#record(long_count=longs, short_count=shorts)

# Get the last N days of prices for every stock in the universe.
prices = history(context.returns_lookback, '1d', 'price')
# Calculate the past 5 days' returns for each security.
returns = (prices.iloc[-1] - prices.iloc) / prices.iloc
# Remove stocks with missing prices.
# Remove any stocks ordered last time that still have open orders.
# Get the cutoff return percentiles for the long and short portfolios.
returns = returns.dropna()
open_orders = get_open_orders()
if open_orders:
eligible_secs = [sec for sec in data if sec not in open_orders]
returns = returns[eligible_secs]

# Lower percentile is the threshhold for the bottom 15%, upper percentile is for the top 15%.
lower, upper = np.percentile(returns, [context.lower_percentile,
context.upper_percentile])
# Select the X% worst performing securities to go long.
long_secs = returns[returns <= lower]
# Select the Y% best performing securities to short.
short_secs = returns[returns >= upper]
# Set the allocations to even weights in each portfolio.
long_weight = context.long_leverage / len(long_secs)
short_weight = context.short_leverage / len(short_secs)
for security in data:
# Buy/rebalance securities in the long leg of the portfolio.
if security in long_secs:
if get_open_orders(security): continue
order_target_percent(security, long_weight)
# Sell/rebalance securities in the short leg of the portfolio.
elif security in short_secs:
if get_open_orders(security): continue
order_target_percent(security, short_weight)
# Close any positions that fell out of the list of securities to long or short.
else:
if get_open_orders(security): continue
if context.portfolio.positions[security].amount == 0: continue
order_target(security, 0)
log.info("This week's longs: "+", ".join([long_.symbol for long_ in long_secs.index]))
log.info("This week's shorts: "  +", ".join([short_.symbol for short_ in short_secs.index]))

def info(context, data):
''' Custom chart and/or log of profit_vs_risk returns and related information
'''
# # # # # # # # # #  Options  # # # # # # # # # #
record_max_lvrg = 1          # maximum leverage encountered
record_leverage = 1          # Leverage (context.account.leverage)
record_q_return = 0          # Quantopian returns (percentage)
record_pvr      = 1          # Profit vs Risk returns (percentage)
record_pnl      = 0          # Profit-n-Loss
record_shorting = 1          # Total value of any shorts
record_risk     = 0          # Risked, maximum cash spent or shorts in excess of cash at any time
record_risk_hi  = 1          # Highest risk overall
record_cash     = 0          # Cash available
record_cash_low = 0          # Any new lowest cash level
logging         = 1          # Also log to the logging window conditionally (1) or not (0)
log_method      = 'risk_hi'  # 'daily' or 'risk_hi'

c = context                          # For brevity
new_cash_low = 0                     # To trigger logging in cash_low case
date = str(get_datetime().date())    # To trigger logging in daily case
cash = c.portfolio.cash

if int(cash) < c.cash_low:    # New cash low
new_cash_low = 1
c.cash_low   = int(cash)
if record_cash_low:
record(CashLow = int(c.cash_low))

pvr_rtrn      = 0        # Profit vs Risk returns based on maximum spent
q_rtrn        = 0        # Returns by Quantopian
profit_loss   = 0        # Profit-n-loss
shorts        = 0        # Shorts value
start         = c.portfolio.starting_cash
cash_dip      = int(max(0, start - cash))

if record_cash:
record(cash = int(c.portfolio.cash))  # Cash

if record_leverage:
record(Lvrg = c.account.leverage)     # Leverage

if record_max_lvrg:
if c.account.leverage > c.max_lvrg:
c.max_lvrg = c.account.leverage
record(MaxLvrg = c.max_lvrg)      # Maximum leverage

if record_pnl:
profit_loss = c.portfolio.pnl
record(PnL = profit_loss)             # "Profit and Loss" in dollars

for p in c.portfolio.positions:
shrs = c.portfolio.positions[p].amount
if shrs < 0:
shorts += int(abs(shrs * data[p].price))

if record_shorting:
record(Shorts = shorts)               # Shorts value as a positve

# Shorts in excess of cash to cover them, a positive value
shorts_excess = int(shorts - cash) if shorts > cash else 0
c.max_shrt    = int(max(c.max_shrt, shorts_excess))

risk = int(max(cash_dip, shorts_excess, shorts))
if record_risk:
record(Risk = risk)                   # Amount in play, maximum of shorts or cash used

new_risk_hi = 0
if risk > c.risk_hi:
c.risk_hi = risk
new_risk_hi = 1

if record_risk_hi:
record(Risk_hi = c.risk_hi)       # Highest risk overall

if record_pvr:      # Profit_vs_Risk returns based on max amount actually spent (risk high)
if c.risk_hi != 0:     # Avoid zero-divide
pvr_rtrn = 100 * (c.portfolio.portfolio_value - start) / c.risk_hi
record(PvR = pvr_rtrn)            # Profit_vs_Risk returns

if record_q_return:
q_rtrn = 100 * (c.portfolio.portfolio_value - start) / start
record(QRet = q_rtrn)                 # Quantopian returns to compare to pvr returns curve

from pytz import timezone
if logging:
if log_method == 'risk_hi' and new_risk_hi \
or log_method == 'daily' and c.date_prv != date \
or c.date_end == date \
or new_cash_low:
mxlv   = 'MaxLv '   + '%.1f' % c.max_lvrg   if record_max_lvrg else ''
qret   = 'QRet '    + '%.1f' % q_rtrn       if record_q_return else ''
pvr    = 'PvR_Ret ' + '%.1f' % pvr_rtrn     if record_pvr      else ''
pnl    = 'PnL '     + '%.0f' % profit_loss  if record_pnl      else ''
csh    = 'Cash '    + '%.0f' % cash         if record_cash     else ''
csh_lw = 'CshLw '   + '%.0f' % c.cash_low   if record_cash_low else ''
shrt   = 'Shrt '    + '%.0f' % shorts       if record_shorting else ''
risk   = 'Risk '    + '%.0f' % risk         if record_risk     else ''
rsk_hi = 'RskHi '   + '%.0f' % c.risk_hi    if record_risk_hi  else ''
minute = get_datetime().astimezone(timezone('US/Eastern')).time().minute
log.info('{} {} {} {} {} {} {} {} {} {}'.format(
minute, mxlv, qret, pvr, pnl, csh, csh_lw, shrt, risk, rsk_hi))

if c.date_end == date:    # Log on last day, like cash 125199  portfolio 126890
log.info('cash {}  portfolio {}'.format(
int(cash), int(c.portfolio.portfolio_value)))

c.date_prv = date


There was a runtime error.