Back to Community
RSI strategy - help needed

Hi there,

I have been trying to build a simple algorithm that buys small market cap stock (below 2M€) for which the RSI(14) is below 45 and RSI(7) is below 20.
Ideally, the positions should be hold on to until RSI(7) reaches 50.

Not sure my order to sell when RSI>50 works for stocks in portfolio? I have been trying to modify the value but keep getting the same results.
I don't understand where I am wrong. Could anyone please help?

Thanks !

Brian

Clone Algorithm
15
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
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import RSI
from quantopian.pipeline.factors import SimpleMovingAverage
from quantopian.pipeline.factors import AverageDollarVolume
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data import Fundamentals
from quantopian.pipeline.data import morningstar
# verifier comment importer market cap

def initialize(context):
    pipe = Pipeline()
    attach_pipeline(pipe, name='zazapachoulia')
    rsi_7 = RSI(inputs=[USEquityPricing.close], window_length=7)
    rsi_14 = RSI(inputs=[USEquityPricing.close], window_length=14)
    marketcap = morningstar.valuation.market_cap.latest
    pipe.add (marketcap, 'marketcap')
    rsi7_under_20 = (rsi_7 < 20)
    rsi14_under_20 = (rsi_14 < 45)
    Low_Cap = marketcap < 2000000
    pipe.add(rsi_7, 'rsi_7')
    
    pipe.set_screen(rsi7_under_20 & rsi14_under_20 & Low_Cap)
    
    schedule_function(order_picks, date_rules.every_day(), time_rules.market_close(hours=2,minutes=0))
    

def before_trading_start(context, data):
    # Access results using the name passed to `attach_pipeline`.
    results = pipeline_output('zazapachoulia')
    print results.head(10)
    
    # Store pipeline results for use by the rest of the algorithm.
    context.stocks = results
   
                     
def order_picks(context,data):
    
    # context.output = pipeline_output('my_pipeline')
    
    m = len(context.stocks)
    
    record(n_stocks = m)
    record(leverage = context.account.leverage)
    
    weight = 1.0/5
    
    rsi_5 = RSI(inputs=[USEquityPricing.close], window_length=5)
    
    for stock in context.stocks.index:
            if data.can_trade(stock):
                order_target_percent(stock, -weight)
                
    for stock in context.portfolio.positions.keys():
        if rsi_5>70 & data.can_trade(stock):
                order_target_percent(stock,0)
There was a runtime error.
5 responses

Anyone?

You are trying to use a pipeline factor to set the rsi_5 value outside of pipeline. I'm not sure, but don't believe that is going to work. Your screen is also exclusive of high RSI values, so as it stands your pipeline isn't going to contain the information you need for when to close out positions. You either need to allow stocks with high RSI values back into the pipeline or use the RSI function from talib to calculate RSIs outside of pipeline to determine when to exit. (check the Classic RSI2 Mean Reversion algorithm to see how to do that)

But I believe you have a bigger problem. Your n_stocks plot shows that your screen hardly ever returns more than 1 or 2 stocks. That doesn't give you a lot of activity to evaluate strategy performance with, or many opportunities to trade for that matter. $2 million is much too low to set as an upper limit.

You're also having problems filling orders, which is understandable because you tested with an account size of 10 million, and a weight per stock of 1/5 account value. That means your algorithm was trying to by $2 million worth of each stock, which equates to trying to buy these companies outright.

Mainly adding some tools. Increased market cap for more stocks and some other playing around without thinking too much.

                        min              mean               max  
marketcap         1845842.0     5987020.85714         7704596.0  
   rsi_14     2.32558139535     14.8112531422      36.301369863  
    rsi_5     7.46268656716     20.0177953699     36.5853658537  
    rsi_7               5.0     9.25093879312     14.3808255659 

    ... rsi_5 highs  
                      marketcap     rsi_14      rsi_5      rsi_7  
Equity(32323 [IMUC])  6648647.0  36.301370  36.585366   5.000000  
Equity(35088 [RSLS])  4993457.0  10.624071  25.961538  14.380826  
Equity(42528 [CHFS])  7704596.0  27.058824  19.148936   7.692308  
Equity(7842 [USEG])   7668132.0   4.545455  19.047619  11.764706  

RSI examples: https://www.google.com/search?q="import+RSI"+site:quantopian.com

Clone Algorithm
53
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
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import RSI
from quantopian.pipeline.factors import SimpleMovingAverage
from quantopian.pipeline.factors import AverageDollarVolume
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data import Fundamentals
from quantopian.pipeline.data import morningstar
# verifier comment importer market cap

def initialize(context):
    pipe = Pipeline()
    attach_pipeline(pipe, name='zazapachoulia')
    rsi_5  = RSI(inputs=[USEquityPricing.close], window_length= 5)   
    rsi_7  = RSI(inputs=[USEquityPricing.close], window_length= 7)
    rsi_14 = RSI(inputs=[USEquityPricing.close], window_length=14)
    marketcap      = morningstar.valuation.market_cap.latest
    Low_Cap        = marketcap < 10e6
    rsi7_under_20  = (rsi_7  < 20)
    rsi14_under_20 = (rsi_14 < 45)
    pipe.add(marketcap, 'marketcap')
    pipe.add(rsi_5,     'rsi_5')
    pipe.add(rsi_7,     'rsi_7')
    pipe.add(rsi_14,    'rsi_14')
    
    pipe.set_screen(rsi7_under_20 & rsi14_under_20 & Low_Cap)
    
    schedule_function(order_picks, date_rules.every_day(), time_rules.market_close(hours=2,minutes=0))
    
    for i in range(1, 391):
        schedule_function(pvr,          date_rules.every_day(), time_rules.market_open(minutes=i))
        
        # Try turning this on
        #schedule_function(track_orders, date_rules.every_day(), time_rules.market_open(minutes=i))


def before_trading_start(context, data):
    # Access results using the name passed to `attach_pipeline`.
    context.stocks = pipeline_output('zazapachoulia')
    #print results.head(10)
    
    if 1 or 'log_pipe_done' not in context:       # show pipe info once (my '1 or' says always)
        df = context.stocks ; num = 4
        log_pipe(context, data, df, num)
        #log_pipe(context, data, df, num, details=['rsi_5', 'rsi_7']) # select columns
                     
    record(n_stocks = len(context.stocks))
    record(leverage = context.account.leverage)
    
def order_picks(context,data):
    
    for stock in context.stocks.index:
        if not data.can_trade(stock): continue

        if context.stocks.rsi_5.T[stock] < 10:
            wt = -1 * min(.2, 1.0 / len(context.stocks))
            log.info('order {} {}'.format('%.3f' % wt, stock.symbol))
            order_target_percent(stock, wt)
                
    for stock in context.portfolio.positions.keys():
        if not data.can_trade(stock): continue
        if stock not in context.stocks.index: continue
            
        
        if context.stocks.rsi_5.T[stock] > 70:    # Why .T for transform?
            amt = context.portfolio.positions[stock].amount
            log.info('closing {} {}'.format(amt, stock.symbol))
            order_target(stock, 0)
                
                
def log_pipe(context, data, df, num, details=None):
    ''' Log info about pipeline output or any DataFrame or Series (df)
    See https://www.quantopian.com/posts/overview-of-pipeline-content-easy-to-add-to-your-backtest
    '''
    
    # Options
    log_nan_only = 0
    show_sectors = 0
    show_sorted_details = 1

    if not len(df):
        log.info('Empty')
        return
    
    # Series ......
    context.log_pipe_done = 1 ; nans = 0 ; padmax = 6 ; content = ''
    if 'Series' in str(type(df)):    # is Series, not DataFrame
        nan_count = len(df[df != df])
        nan_count = 'NaNs {}/{}'.format(nan_count, len(df)) if nan_count else ''
        if nan_count: nans = 1
        if (log_nan_only and nans) or not log_nan_only:
            pad = max(6, len(str(df.max())))
            log.info('{}{}{}   Series {}  len {}'.format('min' .rjust(pad+5), 
                'mean'.rjust(pad+5), 'max' .rjust(pad+5),  df.name, len(df)))
            log.info('{}{}{} {}'.format(str(df.min()) .rjust(pad+5), 
                str(df.mean()).rjust(pad+5), str(df.max()) .rjust(pad+5), nan_count
            ))
        return
    
    # DataFrame ......
    content_min_max = [ ['','min','mean','max',''] ]
    for col in df.columns:
        if col == 'sector' and not show_sectors: continue
        nan_count = len(df[col][df[col] != df[col]])
        nan_count = 'NaNs {}/{}'.format(nan_count, len(df)) if nan_count else ''
        padmax    = max( padmax, max(6, len(str(df[col].max()))) )
        if nan_count: nans = 1
        content_min_max.append([col, str(df[col] .min()), str(df[col].mean()), str(df[col] .max()), nan_count])
    if log_nan_only and nans or not log_nan_only:
        content = 'len {}'.format(len(df))
        paddings = [6 for i in range(4)]
        for lst in content_min_max:    # set max lengths
            i = 0
            for val in lst[:4]:    # value in each sub-list
                paddings[i] = max(paddings[i], len(str(val)))
                i += 1
        headr = content_min_max[0]
        content += ('\n{}{}{}{}{}'.format(
            headr[0] .rjust(paddings[0]),
            (headr[1]).rjust(paddings[1]+5),
            (headr[2]).rjust(paddings[2]+5),
            (headr[3]).rjust(paddings[3]+5),
            ''
        ))
        for lst in content_min_max[1:]:    # populate content using max lengths
            content += ('\n{}{}{}{}     {}'.format(
                lst[0].rjust(paddings[0]),
                lst[1].rjust(paddings[1]+5),
                lst[2].rjust(paddings[2]+5),
                lst[3].rjust(paddings[3]+5),
                lst[4],
            ))
        log.info(content)

    if not show_sorted_details: return
    if len(df) == 1: 
        log.info('index {}'.format(df.index[0]))
        return
    if details == None: details = df.columns
    for detail in details:
        if detail == 'sector': continue
        hi = df.sort_values(by=detail, ascending=False).head(num)
        lo = df.sort_values(by=detail, ascending=False).tail(num)
        content  = ''
        content += ('_ _ _   {}   _ _ _'  .format(detail))
        content += ('\n\t... {} highs\n{}'.format(detail, str(hi)))
        content += ('\n\t... {} lows \n{}'.format(detail, str(lo)))
        if log_nan_only and not len(lo[lo[detail] != lo[detail]]):
            continue  # skip if no nans
        log.info(content)

                
def track_orders(context, data):
    '''  Show orders when made and filled.
           Info: https://www.quantopian.com/posts/track-orders
    '''
    c = context
    if 'trac' not in c:
        c.t_options = {           # __________    O P T I O N S    __________
            'log_neg_cash': 1,    # Show cash only when negative.
            'log_cash'    : 0,    # Show cash values in logging window or not.
            'log_ids'     : 1,    # Include order id's in logging window or not.
            'log_unfilled': 1,    # When orders are unfilled. (stop & limit excluded).
        }    # Move these to initialize() for better efficiency.
        c.trac = {}
        c.t_dates  = {  # To not overwhelm the log window, start/stop dates can be entered.
            'active': 0,
            'start' : [],   # Start dates, option like ['2007-05-07', '2010-04-26']
            'stop'  : []    # Stop  dates, option like ['2008-02-13', '2010-11-15']
        }
    from pytz import timezone     # Python only does once, makes this portable.
                                  #   Move to top of algo for better efficiency.
    # If 'start' or 'stop' lists have something in them, triggers ...
    if c.t_dates['start'] or c.t_dates['stop']:
        date = str(get_datetime().date())
        if   date in c.t_dates['start']:    # See if there's a match to start
            c.t_dates['active'] = 1
        elif date in c.t_dates['stop']:     #   ... or to stop
            c.t_dates['active'] = 0
    else: c.t_dates['active'] = 1           # Set to active b/c no conditions.
    if c.t_dates['active'] == 0: return     # Skip if not active.
    def _minute():   # To preface each line with the minute of the day.
        bar_dt = get_datetime().astimezone(timezone('US/Eastern'))
        return str((bar_dt.hour * 60) + bar_dt.minute - 570).rjust(3) # (-570 = 9:31a)
    def _trac(to_log):      # So all logging comes from the same line number,
        log.info(to_log)    #   for vertical alignment in the logging window.

    for oid in c.trac.copy():               # Existing known orders
      o = get_order(oid)
      if o.dt == o.created: continue        # No chance of fill yet.
      cash = ''
      prc  = data.current(o.sid, 'price') if data.can_trade(o.sid) else c.portfolio.positions[o.sid].last_sale_price
      if (c.t_options['log_neg_cash'] and c.portfolio.cash < 0) or c.t_options['log_cash']:
        cash = 'cash {}'.format(int(c.portfolio.cash))
      if o.status == 2:                      # Canceled
        do = 'Buy' if o.amount > 0 else 'Sell' ; style = ''
        if o.stop:
          style = ' stop {}'.format(o.stop)
          if o.limit: style = ' stop {} limit {}'.format(o.stop, o.limit)
        elif o.limit: style = ' limit {}'.format(o.limit)
        _trac(' {}     Canceled {} {} {}{} at {}   {}  {}'.format(_minute(), do, o.amount,
           o.sid.symbol, style, prc, cash, o.id[-4:] if c.t_options['log_ids'] else ''))
        del c.trac[o.id]
      elif o.filled:                             # Filled at least some.
        filled = '{}'.format(o.amount)
        filled_amt = 0
        if o.status == 1:            # Complete
          if 0 < c.trac[o.id] < o.amount:
            filled   = 'all {}/{}'.format(o.filled - c.trac[o.id], o.amount)
          filled_amt = o.filled
        else:                                    # c.trac[o.id] value is previously filled total
          filled_amt = o.filled - c.trac[o.id]   # filled this time, can be 0
          c.trac[o.id] = o.filled                # save fill value for increments math
          filled = '{}/{}'.format(filled_amt, o.amount)
        if filled_amt:
          now = ' ({})'.format(c.portfolio.positions[o.sid].amount) if c.portfolio.positions[o.sid].amount else ' _'
          pnl = ''  # for the trade only
          amt = c.portfolio.positions[o.sid].amount ; style = ''
          if (amt - o.filled) * o.filled < 0:    # Profit-taking scenario including short-buyback
            cb = c.portfolio.positions[o.sid].cost_basis
            if cb:
              pnl  = -filled_amt * (prc - cb)
              sign = '+' if pnl > 0 else '-'
              pnl  = '  ({}{})'.format(sign, '%.0f' % abs(pnl))
          if o.stop:
            style = ' stop {}'.format(o.stop)
            if o.limit: style = ' stop () limit {}'.format(o.stop, o.limit)
          elif o.limit: style = ' limit {}'.format(o.limit)
          if o.filled == o.amount: del c.trac[o.id]
          _trac(' {}      {} {} {}{} at {}{}{}'.format(_minute(),
            'Bot' if o.amount > 0 else 'Sold', filled, o.sid.symbol, now,
            '%.2f' % prc, pnl, style).ljust(52) + '  {}  {}'.format(cash, o.id[-4:] if c.t_options['log_ids'] else ''))
      elif c.t_options['log_unfilled'] and not (o.stop or o.limit):
        _trac(' {}         {} {}{} unfilled  {}'.format(_minute(), o.sid.symbol, o.amount,
         ' limit' if o.limit else '', o.id[-4:] if c.t_options['log_ids'] else ''))

    oo = get_open_orders().values()
    if not oo: return                       # Handle new orders
    cash = ''
    if (c.t_options['log_neg_cash'] and c.portfolio.cash < 0) or c.t_options['log_cash']:
      cash = 'cash {}'.format(int(c.portfolio.cash))
    for oo_list in oo:
      for o in oo_list:
        if o.id in c.trac: continue         # Only new orders beyond this point
        prc = data.current(o.sid, 'price') if data.can_trade(o.sid) else c.portfolio.positions[o.sid].last_sale_price
        c.trac[o.id] = 0 ; style = ''
        now  = ' ({})'.format(c.portfolio.positions[o.sid].amount) if c.portfolio.positions[o.sid].amount else ' _'
        if o.stop:
          style = ' stop {}'.format(o.stop)
          if o.limit: style = ' stop {} limit {}'.format(o.stop, o.limit)
        elif o.limit: style = ' limit {}'.format(o.limit)
        _trac(' {}   {} {} {}{} at {}{}'.format(_minute(), 'Buy' if o.amount > 0 else 'Sell',
          o.amount, o.sid.symbol, now, '%.2f' % prc, style).ljust(52) + '  {}  {}'.format(cash, o.id[-4:] if c.t_options['log_ids'] else ''))

        
def pvr(context, data):
    ''' Custom chart and/or logging of profit_vs_risk returns and related information
        http://quantopian.com/posts/pvr
    '''
    import time
    from datetime import datetime as _dt
    from pytz import timezone      # Python will only do once, makes this portable.
                                   #   Move to top of algo for better efficiency.
    c = context  # Brevity is the soul of wit -- Shakespeare [for readability]
    if 'pvr' not in c:

        # For real money, you can modify this to total cash input minus any withdrawals
        manual_cash = c.portfolio.starting_cash
        time_zone   = 'US/Pacific'   # Optionally change to your own time zone for wall clock time

        c.pvr = {
            'options': {
                # # # # # # # # # #  Options  # # # # # # # # # #
                'logging'         : 0,    # Info to logging window with some new maximums
                'log_summary'     : 126,  # Summary every x days. 252/yr

                'record_pvr'      : 0,    # Profit vs Risk returns (percentage)
                'record_pvrp'     : 0,    # PvR (p)roportional neg cash vs portfolio value
                'record_cash'     : 0,    # Cash available
                'record_max_lvrg' : 1,    # Maximum leverage encountered
                'record_max_risk' : 1,    # Highest risk overall
                'record_shorting' : 0,    # Total value of any shorts
                'record_max_shrt' : 1,    # Max value of shorting total
                'record_cash_low' : 1,    # Any new lowest cash level
                'record_q_return' : 0,    # Quantopian returns (percentage)
                'record_pnl'      : 0,    # Profit-n-Loss
                'record_risk'     : 0,    # Risked, max cash spent or shorts beyond longs+cash
                'record_leverage' : 0,    # End of day leverage (context.account.leverage)
                # All records are end-of-day or the last data sent to chart during any day.
                # The way the chart operates, only the last value of the day will be seen.
                # # # # # # # # #  End options  # # # # # # # # #
            },
            'pvr'        : 0,      # Profit vs Risk returns based on maximum spent
            'cagr'       : 0,
            'max_lvrg'   : 0,
            'max_shrt'   : 0,
            'max_risk'   : 0,
            'days'       : 0.0,
            'date_prv'   : '',
            'date_end'   : get_environment('end').date(),
            'cash_low'   : manual_cash,
            'cash'       : manual_cash,
            'start'      : manual_cash,
            'tz'         : time_zone,
            'begin'      : time.time(),  # For run time
            'run_str'    : '{} to {}  ${}  {} {}'.format(get_environment('start').date(), get_environment('end').date(), int(manual_cash), _dt.now(timezone(time_zone)).strftime("%Y-%m-%d %H:%M"), time_zone)
        }
        if c.pvr['options']['record_pvrp']: c.pvr['options']['record_pvr'] = 0 # if pvrp is active, straight pvr is off
        if get_environment('arena') not in ['backtest', 'live']: c.pvr['options']['log_summary'] = 1 # Every day when real money
        log.info(c.pvr['run_str'])
    p = c.pvr ; o = c.pvr['options'] ; pf = c.portfolio ; pnl = pf.portfolio_value - p['start']
    def _pvr(c):
        p['cagr'] = ((pf.portfolio_value / p['start']) ** (1 / (p['days'] / 252.))) - 1
        ptype = 'PvR' if o['record_pvr'] else 'PvRp'
        log.info('{} {} %/day   cagr {}   Portfolio value {}   PnL {}'.format(ptype, '%.4f' % (p['pvr'] / p['days']), '%.3f' % p['cagr'], '%.0f' % pf.portfolio_value, '%.0f' % pnl))
        log.info('  Profited {} on {} activated/transacted for PvR of {}%'.format('%.0f' % pnl, '%.0f' % p['max_risk'], '%.1f' % p['pvr']))
        log.info('  QRet {} PvR {} CshLw {} MxLv {} MxRisk {} MxShrt {}'.format('%.2f' % (100 * pf.returns), '%.2f' % p['pvr'], '%.0f' % p['cash_low'], '%.2f' % p['max_lvrg'], '%.0f' % p['max_risk'], '%.0f' % p['max_shrt']))
    def _minut():
        dt = get_datetime().astimezone(timezone(p['tz']))
        return str((dt.hour * 60) + dt.minute - 570).rjust(3)  # (-570 = 9:31a)
    date = get_datetime().date()
    if p['date_prv'] != date:
        p['date_prv'] = date
        p['days'] += 1.0
    do_summary = 0
    if o['log_summary'] and p['days'] % o['log_summary'] == 0 and _minut() == '100':
        do_summary = 1              # Log summary every x days
    if do_summary or date == p['date_end']:
        p['cash'] = pf.cash
    elif p['cash'] == pf.cash and not o['logging']: return  # for speed

    shorts = sum([z.amount * z.last_sale_price for s, z in pf.positions.items() if z.amount < 0])
    new_key_hi = 0                  # To trigger logging if on.
    cash       = pf.cash
    cash_dip   = int(max(0, p['start'] - cash))
    risk       = int(max(cash_dip, -shorts))

    if o['record_pvrp'] and cash < 0:   # Let negative cash ding less when portfolio is up.
        cash_dip = int(max(0, cash_dip * p['start'] / pf.portfolio_value))
        # Imagine: Start with 10, grows to 1000, goes negative to -10, should not be 200% risk.

    if int(cash) < p['cash_low']:             # New cash low
        new_key_hi = 1
        p['cash_low'] = int(cash)             # Lowest cash level hit
        if o['record_cash_low']: record(CashLow = p['cash_low'])

    if c.account.leverage > p['max_lvrg']:
        new_key_hi = 1
        p['max_lvrg'] = c.account.leverage    # Maximum intraday leverage
        if o['record_max_lvrg']: record(MxLv    = p['max_lvrg'])

    if shorts < p['max_shrt']:
        new_key_hi = 1
        p['max_shrt'] = shorts                # Maximum shorts value
        if o['record_max_shrt']: record(MxShrt  = p['max_shrt'])

    if risk > p['max_risk']:
        new_key_hi = 1
        p['max_risk'] = risk                  # Highest risk overall
        if o['record_max_risk']:  record(MxRisk = p['max_risk'])

    # Profit_vs_Risk returns based on max amount actually invested, long or short
    if p['max_risk'] != 0: # Avoid zero-divide
        p['pvr'] = 100 * pnl / p['max_risk']
        ptype = 'PvRp' if o['record_pvrp'] else 'PvR'
        if o['record_pvr'] or o['record_pvrp']: record(**{ptype: p['pvr']})

    if o['record_shorting']: record(Shorts = shorts)             # Shorts value as a positve
    if o['record_leverage']: record(Lv     = c.account.leverage) # Leverage
    if o['record_cash']    : record(Cash   = cash)               # Cash
    if o['record_risk']    : record(Risk   = risk)  # Amount in play, maximum of shorts or cash used
    if o['record_q_return']: record(QRet   = 100 * pf.returns)
    if o['record_pnl']     : record(PnL    = pnl)                # Profit|Loss

    if o['logging'] and new_key_hi:
        log.info('{}{}{}{}{}{}{}{}{}{}{}{}'.format(_minut(),
            ' Lv '     + '%.1f' % c.account.leverage,
            ' MxLv '   + '%.2f' % p['max_lvrg'],
            ' QRet '   + '%.1f' % (100 * pf.returns),
            ' PvR '    + '%.1f' % p['pvr'],
            ' PnL '    + '%.0f' % pnl,
            ' Cash '   + '%.0f' % cash,
            ' CshLw '  + '%.0f' % p['cash_low'],
            ' Shrt '   + '%.0f' % shorts,
            ' MxShrt ' + '%.0f' % p['max_shrt'],
            ' Risk '   + '%.0f' % risk,
            ' MxRisk ' + '%.0f' % p['max_risk']
        ))
    if do_summary: _pvr(c)
    if get_datetime() == get_environment('end'):   # Summary at end of run
        _pvr(c) ; elapsed = (time.time() - p['begin']) / 60  # minutes
        log.info( '{}\nRuntime {} hr {} min'.format(p['run_str'], int(elapsed / 60), '%.1f' % (elapsed % 60)))
There was a runtime error.

Thanks Shawn Emhe II! That makes total sense. your feedback really helps.

BlueSeahawk, as I am quite new and do not master all coding part yet, I might get back to you with some extra questions on your input if that is ok? Thank you.

Hi Blue seahawk,

So is your algorithm only shorting stocks, and not longing any? I'm new to quantopian and just wanna make sure I'm getting that right.