Back to Community
No order placed in the backtest

I'm new to Quantopian and I'm trying to implement an algorithm based on custom factors derived from PsychSignal dataset. This algorithm is intended for trading a single stock. However, no order was placed throughout the time in the backtest. Could someone help me with this issue?

Clone Algorithm
3
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
import quantopian.algorithm as algo
from quantopian.pipeline import Pipeline
from quantopian.pipeline.filters import StaticAssets
from quantopian.pipeline.data.psychsignal import twitter_withretweets
from quantopian.pipeline.factors import SimpleMovingAverage

STOCK_TO_TEST = symbols('AAPL')


def initialize(context):
    algo.attach_pipeline(make_pipeline(), 'pipeline')
    
    algo.schedule_function(handle_data, date_rules.every_day(), time_rules.market_open())


def make_pipeline():
    '''
    Define computation for custom factors factor_10 and factor_30
    '''
    universe = StaticAssets(STOCK_TO_TEST)
    
    bull_scored_messages_10 = SimpleMovingAverage(
    inputs=[twitter_withretweets.bull_scored_messages],
    window_length=10,
    mask=universe) 
    bear_scored_messages_10 = SimpleMovingAverage(
    inputs=[twitter_withretweets.bear_scored_messages],
    window_length=10,
    mask=universe)   
    bull_scored_messages_30 = SimpleMovingAverage(
    inputs=[twitter_withretweets.bull_scored_messages],
    window_length=30,
    mask=universe)    
    bear_scored_messages_30 = SimpleMovingAverage(
    inputs=[twitter_withretweets.bear_scored_messages],
    window_length=30,
    mask=universe)   
    
    bull_over_combined_10 = bull_scored_messages_10 / (bull_scored_messages_10 + bear_scored_messages_10)
    bull_over_combined_30 = bull_scored_messages_30 / (bull_scored_messages_30 + bear_scored_messages_30)
    bullish_intensity_10 = SimpleMovingAverage(
    inputs=[twitter_withretweets.bullish_intensity],
    window_length=10,
    mask=universe)
    bullish_intensity_30 = SimpleMovingAverage(
    inputs=[twitter_withretweets.bullish_intensity],
    window_length=30,
    mask=universe)
 
    factor_10 = bull_over_combined_10.zscore() + bullish_intensity_10.zscore()
    factor_30 = bull_over_combined_30.zscore() + bullish_intensity_30.zscore()
    
    pipe = Pipeline(
        columns={
            'factor_10': factor_10,
            'factor_30': factor_30
        }
    )
    return pipe


def before_trading_start(context, data):
    context.output = algo.pipeline_output('pipeline')


def handle_data(context, data):
    '''
    Define how buys and sells are executed
    '''
    # Go long if factor_10 is greater than factor_30
    long_rules = 'factor_10 > factor_30'
    long_these = context.output.query(long_rules).index.tolist()
    
    for stock in long_these:
        order_target_percent(stock, 1)
        log.info("Buying %s" % (stock))  
    
    # Go short if factor_10 is less than factor_30
    short_rules = 'factor_10 < factor_30'
    short_these = context.output.query(short_rules).index.tolist()
    
    for stock in short_these:
        order_target_percent(stock, -1)
        log.info("Short Selling %s" % (stock)) 
There was a runtime error.
2 responses

The issue is with the zscore method. It doesn't like trying to zscore with a population of only a single value. A zscore represents the number of standard deviations a particular value is from the mean of a population of values. That population size needs to be at least two and, realistically, much larger than that. The method doesn't know how to calculate a value when the population size is one and therefore returns nan. It's because the zscore is nan that your algo never trades. The pieces of code below are some of the culprits

STOCK_TO_TEST = symbols('AAPL')  
universe = StaticAssets(STOCK_TO_TEST)

bull_scored_messages_10 = SimpleMovingAverage(  
    inputs=[twitter_withretweets.bull_scored_messages],  
    window_length=10,  
    mask=universe) 

bull_over_combined_10 = bull_scored_messages_10 / (bull_scored_messages_10 + bear_scored_messages_10)

factor_10 = bull_over_combined_10.zscore() + bullish_intensity_10.zscore()

The key is to set up a zscore mask which defines what population, or universe, of stocks one wants to rank against. It needs to have at least two stocks and include all the stocks which one wants to calculate a score for. It's algo specific which universe to use but typically the Q500US to rank against big well known stocks or the QTradableStocksUS if one wants to compare to the broader market. By using the groupby parameter one can also just zscore against other stocks in the same group, typically sector. One can later screen or mask by just the stocks one wants to trade.

It's very important to set a defined universe and mask. Without one, the zscore will score against all stocks in the Quantopian database. This incudes ETFs, leveraged ETFs, ADRs, and some penny stocks. Probably not the population one generally wants to rank against. The filter can either be explicitly set as a zscore mask or implied by the factor.

One other requirement of the zscore method is no factor values can be infinite (either positive or negative). Infinite values don't play well when calculating means and standard deviations. It's good practice to always include in the mask a isfinite() filter to prevent this. Nan values are fine (they are ignored) but the isfinite() filter actually filters those too just for good measure.

I made a few changes to your algo and it places trades.

Good luck.

Clone Algorithm
3
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
import quantopian.algorithm as algo
from quantopian.pipeline import Pipeline
from quantopian.pipeline.filters import StaticAssets, Q500US
from quantopian.pipeline.data.psychsignal import twitter_withretweets
from quantopian.pipeline.factors import SimpleMovingAverage

STOCK_TO_TEST = symbols('AAPL')

def initialize(context):
    algo.attach_pipeline(make_pipeline(), 'pipeline')
    
    algo.schedule_function(rebalance, date_rules.every_day(), time_rules.market_open())

def make_pipeline():
    '''
    Define computation for custom factors factor_10 and factor_30
    '''
    # Define the universe of stocks we want to trade
    trading_universe = StaticAssets(STOCK_TO_TEST)
    
    # Define the universe of stocks we want to base zscores on
    # Ensure the trading_universe is included
    zscore_universe = Q500US() | trading_universe

    bull_scored_messages_10 = SimpleMovingAverage(
        inputs=[twitter_withretweets.bull_scored_messages],
        window_length=10,
        mask=zscore_universe) 
    bear_scored_messages_10 = SimpleMovingAverage(
        inputs=[twitter_withretweets.bear_scored_messages],
        window_length=10,
        mask=zscore_universe)   
    bull_scored_messages_30 = SimpleMovingAverage(
        inputs=[twitter_withretweets.bull_scored_messages],
        window_length=30,
        mask=zscore_universe)    
    bear_scored_messages_30 = SimpleMovingAverage(
        inputs=[twitter_withretweets.bear_scored_messages],
        window_length=30,
        mask=zscore_universe)   
    
    bull_over_combined_10 = bull_scored_messages_10 / (bull_scored_messages_10 + 
                                                       bear_scored_messages_10)
    bull_over_combined_30 = bull_scored_messages_30 / (bull_scored_messages_30 +
                                                       bear_scored_messages_30)
    bullish_intensity_10 = SimpleMovingAverage(
        inputs=[twitter_withretweets.bullish_intensity],
        window_length=10,
        mask=zscore_universe)
    bullish_intensity_30 = SimpleMovingAverage(
        inputs=[twitter_withretweets.bullish_intensity],
        window_length=30,
        mask=zscore_universe)
    
    factor_10 = (bull_over_combined_10.zscore(mask=bull_over_combined_10.isfinite()) 
                 + bullish_intensity_10.zscore(mask=bullish_intensity_10.isfinite()))
    factor_30 = (bull_over_combined_30.zscore(mask=bull_over_combined_30.isfinite()) 
                 + bullish_intensity_30.zscore(mask=bullish_intensity_30.isfinite()))

    pipe = Pipeline(
        columns={
            'factor_10': factor_10,
            'factor_30': factor_30,
            'bull_over_combined_10': bull_over_combined_10,
            'bullish_intensity_10': bullish_intensity_10,
            'bull_over_combined_30': bull_over_combined_30,
            'bullish_intensity_30': bullish_intensity_30,
        },
        screen=trading_universe
    )
    return pipe

def rebalance(context, data):
    '''
    Define how buys and sells are executed
    '''
    pipe_output = algo.pipeline_output('pipeline')
    
    # Go long if factor_10 is greater than factor_30
    long_rules = 'factor_10 > factor_30'
    long_these = pipe_output.query(long_rules).index.tolist()
    
    for stock in long_these:
        order_target_percent(stock, 1)
        log.info("Buying %s" % (stock))  
    
    # Go short if factor_10 is less than factor_30
    short_rules = 'factor_10 < factor_30'
    short_these = pipe_output.query(short_rules).index.tolist()
    
    for stock in short_these:
        order_target_percent(stock, -1)
        log.info("Short Selling %s" % (stock))
There was a runtime error.
Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

Dan, thank you very much for helping me out.