Back to Community
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.

Clone Algorithm
2
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 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[0]) / prices.iloc[0]
    # 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.

Clone Algorithm
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_commission(commission.PerShare(cost=0.0075, min_trade_cost=1.00))
    # 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[0]) / prices.iloc[0]
    # 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.