Back to Community
No Trade in Backtest - Help

Hi everyone,
I am trying to develop basic moving average crossover algorithm to learn step by step. When I backtested the strategy it didn't take any position, neither short nor long. Can someone help me with this?

Trading logic:
When 55 sma > 100 sma > 200 trend is up for that stock and vice versa.

To take long position on stock:
21 sma > 55 sma

To take short position on stock:
21 sma < 55 sma

Please feel free to correct me, about any code in the algorithm, to develop a better algorithm

Clone Algorithm
3
Loading...
Backtest from to with initial capital
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.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import SimpleMovingAverage
from quantopian.pipeline import CustomFactor
from quantopian.pipeline.data import morningstar


class MarketCap(CustomFactor):
    inputs = [USEquityPricing.close, morningstar.valuation.shares_outstanding]
    window_length = 1

    def compute(self, today, assets, out, close, shares):
        out[:] = close[-1] * shares[-1]



def initialize(context):
    """
    Called once at the start of the algorithm.
    """
    #how many stocks I would like to invest every time
    context.long_limit = 10
    context.short_limit = 10

    schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(hours=1))
    schedule_function(my_record_vars, date_rules.every_day(), time_rules.market_close())

    attach_pipeline(my_pipeline(context), 'my_pipeline')


def my_pipeline(context):
    """
    A function to create our dynamic stock selector (pipeline). Documentation on
    pipeline can be found here: https://www.quantopian.com/help#pipeline-title
    """
    pipe = Pipeline()

    market_cap = MarketCap()
    sma_21 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=21)
    sma_55 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=55)
    sma_100 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=100)
    sma_200 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=200)

    pipe.add(sma_21, 'sma_21')
    pipe.add(sma_55, 'sma_55')
    pipe.add(sma_100, 'sma100')
    pipe.add(sma_200, 'sma_200')
    pipe.add(market_cap, 'market_cap')

    #Create a filter which will return boolean value
    isTrendUp = (sma_55 > sma_100 > sma_200)
    isTrendDown = (sma_55 < sma_100 < sma_200)

    # only want the 20 highest market_cap stocks that pass the isTrendUp and isTrendDown screen
    market_cap_rank = market_cap.rank(mask=isTrendUp & isTrendDown , ascending=False)
    top20_market_cap = market_cap_rank <= 20

    pipe.add(market_cap_rank, 'market_cap_rank')

    #To inform our pipeline that each day we want to throw away any rows for whose assets
    #our filters produced False value we will set screen
    pipe.set_screen(isTrendUp & isTrendDown & top20_market_cap)

    return pipe

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

    context.long_signal = (context.output['sma_21'] > context.output['sma_55']).to_dict()
    context.short_signal = (context.output['sma_21'] < context.output['sma_55']).to_dict()

def my_rebalance(context, data):
    for stock in context.long_signal:
        if context.long_signal[stock] & data.can_trade(stock) & (stock not in context.portfolio.positions):
            order_target_percent(stock, 1./context.long_limit)

    for stock in context.short_signal:
        if context.short_signal[stock] & data.can_trade(stock) & (stock not in
            context.portfolio.positions):
            order_target_percent(stock, 1. / context.short_limit)

    for stock in context.portfolio.positions:
        if data.can_trade(stock) & (stock not in context.output.index):
            order_target(stock, 0)

def my_record_vars(context, data):
    """
    Plot variables at the end of each day.
    """
    longs, shorts = 0, 0
    for position in context.portfolio.positions.values():
        if position.amount < 0:
            #log.info("PROOF: Have a position of {} in {}".format(position.amount, position.sid.symbol))
            shorts += 1
        else:
            longs += 1
            
    record(leverage=context.account.leverage,
           longs = longs,
           shorts = shorts)




There was a runtime error.
14 responses

Anyone?

Hi Gerard,

There are two issues with your code here.

First, on lines 53 and 54, double inequalities like that aren't supported. To get the appropriate functionality, you would do something like this instead:

isTrendUp = ((sma_55 > sma_100) & (sma_100 > sma_200))  
isTrendDown = ((sma_55 < sma_100) & (sma_100 < sma_200))  

Second, on lines 57 and 64: Note that isTrendUp and isTrendDown are mutually exclusive. So you should be checking if one or the other is true, not if both are true (which is never the case). So your mask should be isTrendUp | isTrendDown and your screen should be the following:

pipe.set_screen((isTrendUp | isTrendDown) & top20_market_cap)  

This can also be abbreviated to pipe.set_screen(top20_market_cap), since top20_market_cap is masked by the trends, meaning it will only give True for those that already follow one of the trends.

I hope this helps. If you ever have another specific code question like this, I would recommend asking Support by clicking on Learn & Support > Contact Support in the top menu bar. That will help us get to your question faster.

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.

Thank you Nathan that worked perfect.

Thanks Nathan!

Last version of the code with RSI.
I also tried to add MACD but couldn't manage.

Clone Algorithm
5
Loading...
Backtest from to with initial capital
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.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import SimpleMovingAverage, RSI
from quantopian.pipeline import CustomFactor
from quantopian.pipeline.data import morningstar
import talib

class MarketCap(CustomFactor):
    inputs = [USEquityPricing.close, morningstar.valuation.shares_outstanding]
    window_length = 1

    def compute(self, today, assets, out, close, shares):
        out[:] = close[-1] * shares[-1]
        
        
def initialize(context):
    """
    Called once at the start of the algorithm.
    """
    #how many stocks I would like to invest every time
    context.long_limit = 10
    context.short_limit = 10
    
    schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(hours=1))
    schedule_function(my_record_vars, date_rules.every_day(), time_rules.market_close())

    attach_pipeline(my_pipeline(context), 'my_pipeline')


def my_pipeline(context):
    """
    A function to create our dynamic stock selector (pipeline). Documentation on
    pipeline can be found here: https://www.quantopian.com/help#pipeline-title
    """
    pipe = Pipeline()

    market_cap = MarketCap()
    sma_21 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=21)
    sma_55 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=55)
    sma_100 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=100)
    sma_200 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=200)
    rsi_14 = RSI(inputs=[USEquityPricing.close], window_length= 14)
    
    pipe.add(sma_21, 'sma_21')
    pipe.add(sma_55, 'sma_55')
    pipe.add(sma_100, 'sma100')
    pipe.add(sma_200, 'sma_200')
    pipe.add(rsi_14, 'rsi_14')
    pipe.add(market_cap, 'market_cap')
    
    #Create a filter which will return boolean value
    isTrendUp = (sma_55 > sma_100) & (sma_100 > sma_200)
    isTrendDown = (sma_55 < sma_100) & (sma_100 < sma_200)
    isOverBought = rsi_14 > 80
    isOverSold = rsi_14 < 20

    # only want the 20 highest market_cap stocks that pass the isTrendUp and isTrendDown screen
    market_cap_rank = market_cap.rank(mask=isTrendUp | isTrendDown , ascending=False)
    top20_market_cap = market_cap_rank <= 100

    pipe.add(market_cap_rank, 'market_cap_rank')
    
    #add rsi criteria filters to pipeline
    pipe.add(isOverBought, 'isOverBought')
    pipe.add(isOverSold, 'isOverSold')
    #To inform our pipeline that each day we want to throw away any rows for whose assets
    #our filters produced False value we will set screen
    pipe.set_screen(top20_market_cap)

    return pipe

def before_trading_start(context, data):
    context.output = pipeline_output('my_pipeline')
        
        
    context.long_signal = ((context.output['sma_21'] > context.output['sma_55']) & (context.output['isOverSold'] == True)).to_dict()
    
    context.short_signal = ((context.output['sma_21'] < context.output['sma_55']) & (context.output['isOverBought'] == True)).to_dict()
    
   
        
def my_rebalance(context, data):                                     
    for stock in context.long_signal:
        
        if context.long_signal[stock] & data.can_trade(stock) & (stock not in context.portfolio.positions):
            order_target_percent(stock, 0.5/context.long_limit)

    for stock in context.short_signal:
        if context.short_signal[stock] & data.can_trade(stock) & (stock not in
            context.portfolio.positions):
            order_target_percent(stock, -0.5 / context.short_limit)

    for stock in context.portfolio.positions:
        if data.can_trade(stock) & (stock not in context.output.index):
            order_target(stock, 0)

def my_record_vars(context, data):
    """
    Plot variables at the end of each day.
    """
    longs, shorts = 0, 0
    for position in context.portfolio.positions.values():
        if position.amount < 0:
            #log.info("PROOF: Have a position of {} in {}".format(position.amount, position.sid.symbol))
            shorts += 1
        else:
            longs += 1
            
    record(leverage=context.account.leverage,
           longs = longs,
           shorts = shorts)




There was a runtime error.

Hi Gerard,

I was able to incorporate a pipeline factor that calculates MACD.

It is by no means a finished product, but an example of using ta-lib within a Pipeline Factor.

Best,
Lotanna Ezenwa

Clone Algorithm
0
Loading...
Backtest from to with initial capital
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.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing as USEP
from quantopian.pipeline.factors import SimpleMovingAverage, RSI
from quantopian.pipeline import CustomFactor
from quantopian.pipeline.data import morningstar

import talib as ta

from numpy import nan, isnan

class MarketCap(CustomFactor):
    # Pre-declare inputs and window_length
    inputs = [USEP.close, morningstar.valuation.shares_outstanding]
    window_length = 1

    # Compute market cap value
    def compute(self, today, assets, out, close, shares):
        out[:] = close[-1] * shares[-1]




class MACDFactor(CustomFactor):
    inputs = [USEP.close]
    window_length = 35
    
    def compute(self, today, assets, out, close):
        """
        Compute MACD each column of high, low, and close.
        """
        outs = []
        for t in close.T:
            if isnan(t).any():
                outs.append(0)
                continue
            m,s,h = ta.MACD(t)
            outs.append(m[-1])
        out[:] = outs
        
def initialize(context):
    """
    Called once at the start of the algorithm.
    """
    #how many stocks I would like to invest every time
    context.long_limit = 10
    context.short_limit = 10
    
    schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(hours=1))
    schedule_function(my_record_vars, date_rules.every_day(), time_rules.market_close())

    attach_pipeline(my_pipeline(context), 'my_pipeline')


def my_pipeline(context):
    """
    A function to create our dynamic stock selector (pipeline). Documentation on
    pipeline can be found here: https://www.quantopian.com/help#pipeline-title
    """
    pipe = Pipeline()

    market_cap = MarketCap()

    market_cap_filter = market_cap.top(10)
    MACD = MACDFactor([USEP.close],mask=market_cap_filter)
    
    pipe.add(MACD, 'MACD')
    pipe.add(market_cap, 'market_cap')
    
    #Create a filter which will return boolean value
    isMACD = (MACD > 0)
    #pipe.set_screen(isMACD)

    # only want the 20 highest market_cap stocks that pass the isTrendUp and isTrendDown screen
    market_cap = morningstar.valuation.market_cap.latest
    market_cap_filter = (market_cap > 200000000000)
    pipe.set_screen(market_cap_filter)
    #add rsi criteria filters to pipeline
    #To inform our pipeline that each day we want to throw away any rows for whose assets
    #our filters produced False value we will set screen

    return pipe

def before_trading_start(context, data):
    context.output = pipeline_output('my_pipeline')
        
        
    context.long_signal = (context.output['MACD'] > 0 ).to_dict()
    
    context.short_signal = (context.output['MACD'] < 0 ).to_dict()
    
   
        
def my_rebalance(context, data):                                     
    for stock in context.long_signal:
        
        if context.long_signal[stock] & data.can_trade(stock) & (stock not in context.portfolio.positions):
            order_target_percent(stock, 0.5/context.long_limit)

    for stock in context.short_signal:
        if context.short_signal[stock] & data.can_trade(stock) & (stock not in
            context.portfolio.positions):
            order_target_percent(stock, -0.5 / context.short_limit)

    for stock in context.portfolio.positions:
        if data.can_trade(stock) & (stock not in context.output.index):
            order_target(stock, 0)

def my_record_vars(context, data):
    """
    Plot variables at the end of each day.
    """
    longs, shorts = 0, 0
    for position in context.portfolio.positions.values():
        if position.amount < 0:
            #log.info("PROOF: Have a position of {} in {}".format(position.amount, position.sid.symbol))
            shorts += 1
        else:
            longs += 1
            
    record(leverage=context.account.leverage,
           longs = longs,
           shorts = shorts)




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.

Thanks Lotenna! Great work.

I am improving the algorithm step by step. When I added MACD criteria algorithm gets long positions but short. There is no short position at all. When I delete the MACD < 0 from context.short_list it takes short position.
Can someone help me?

Clone Algorithm
5
Loading...
Backtest from to with initial capital
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.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import SimpleMovingAverage, RSI
from quantopian.pipeline import CustomFactor
from quantopian.pipeline.data import morningstar
import talib
from numpy import nan, isnan

class MarketCap(CustomFactor):
    inputs = [USEquityPricing.close, morningstar.valuation.shares_outstanding]
    window_length = 1

    def compute(self, today, assets, out, close, shares):
        out[:] = close[-1] * shares[-1]
        
class MACDFactor(CustomFactor):
    inputs = [USEquityPricing.close]
    window_length = 35
    
    def compute(self, today, assets, out, close):
        """
        Compute MACD each column of high, low, and close.
        """
        outs = []
        for t in close.T:
            if isnan(t).any():
                outs.append(0)
                continue
            m,s,h = talib.MACD(t, fastperiod=12, 
                                        slowperiod=26, signalperiod=9)
            outs.append(m[-1])
        out[:] = outs   
        
def initialize(context):
    """
    Called once at the start of the algorithm.
    """
    #how many stocks I would like to invest every time
    context.long_limit = 10
    context.short_limit = 10
    
    schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(hours=1))
    schedule_function(my_record_vars, date_rules.every_day(), time_rules.market_close())

    attach_pipeline(my_pipeline(context), 'my_pipeline')

def my_pipeline(context):
    """
    A function to create our dynamic stock selector (pipeline). Documentation on
    pipeline can be found here: https://www.quantopian.com/help#pipeline-title
    """
    pipe = Pipeline()

    market_cap = MarketCap()
    sma_21 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=21)
    sma_55 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=55)
    sma_100 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=100)
    sma_200 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=200)
    rsi_14 = RSI(inputs=[USEquityPricing.close], window_length= 14)
    
    pipe.add(sma_21, 'sma_21')
    pipe.add(sma_55, 'sma_55')
    pipe.add(sma_100, 'sma100')
    pipe.add(sma_200, 'sma_200')
    pipe.add(rsi_14, 'rsi_14')
    pipe.add(market_cap, 'market_cap')
    
    #Create a filter which will return boolean value
    isTrendUp = (sma_55 > sma_100) & (sma_100 > sma_200)
    isTrendDown = (sma_55 < sma_100) & (sma_100 < sma_200)
    isOverBought = rsi_14 > 80
    isOverSold = rsi_14 < 20

    # only want the 20 highest market_cap stocks that pass the isTrendUp and isTrendDown screen
    market_cap_rank = market_cap.rank(mask=isTrendUp | isTrendDown , ascending=False)
    top100_market_cap = market_cap_rank <= 100
    
    MACD = MACDFactor([USEquityPricing.close], mask=top100_market_cap)

    pipe.add(market_cap_rank, 'market_cap_rank')
    pipe.add(MACD, 'MACD')

    #add rsi criteria filters to pipeline
    pipe.add(isOverBought, 'isOverBought')
    pipe.add(isOverSold, 'isOverSold')
    #To inform our pipeline that each day we want to throw away any rows for whose assets
    #our filters produced False value we will set screen
    pipe.set_screen(top100_market_cap)

    return pipe

def before_trading_start(context, data):
    context.output = pipeline_output('my_pipeline')
        
    context.long_signal = ((context.output['sma_21'] > context.output['sma_55']) & (context.output['isOverSold'] == True) & context.output['MACD'] > 0).to_dict()
    
    context.short_signal = ((context.output['sma_21'] < context.output['sma_55']) & (context.output['isOverBought'] == True) & context.output['MACD'] < 0).to_dict()
    
        
def my_rebalance(context, data):                                     
    for stock in context.long_signal:
        
        if context.long_signal[stock] & data.can_trade(stock) & (stock not in context.portfolio.positions):
            order_target_percent(stock, 0.5/context.long_limit)

    for stock in context.short_signal:
        if context.short_signal[stock] & data.can_trade(stock) & (stock not in
            context.portfolio.positions):
            order_target_percent(stock, -0.5 / context.short_limit)

    for stock in context.portfolio.positions:
        if data.can_trade(stock) & (stock not in context.output.index):
            order_target(stock, 0)

def my_record_vars(context, data):
    """
    Plot variables at the end of each day.
    """
    longs, shorts = 0, 0
    for position in context.portfolio.positions.values():
        if position.amount < 0:
            #log.info("PROOF: Have a position of {} in {}".format(position.amount, position.sid.symbol))
            shorts += 1
        else:
            longs += 1
            
    record(leverage=context.account.leverage,
           longs = longs,
           shorts = shorts)




There was a runtime error.

Hi Gerard,

I noticed an issue in your algorithm.
For your purposes, you'll be wanting to use the h signal from ta.MACD(), as it is the trading signal that calculates the difference between 'm' and 's'.

As for the logic under before_trading_start, you can set them all as pipeline screens, to speed up computations and help readability.
More information can be found in our API documentation covering screens and factors.

I would also suggest employing a mask on your MACD factor, as it is rather expensive. The mask will allow you to only run MACD on certain equities that you choose.

Best,

Lotanna Ezenwa

I changed MACD as 'h' but still no short position.

In line 97 and 99, you forgot to include parentheses around context.['MACD'] >< 0, so the logic is not executing properly.

Also, I apologize for suggesting switch from 'm' to 'h', either signal is a MACD signal, but they measure different things.

Hi Lotanna!
I've made the changes you suggested but there is no short position. I think I am doing something wrong in pipeline but I could't figure it out.

Here's the backtest showing the short positions

Clone Algorithm
0
Loading...
Backtest from to with initial capital
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.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import SimpleMovingAverage, RSI
from quantopian.pipeline import CustomFactor
from quantopian.pipeline.data import morningstar
import talib
from numpy import nan, isnan

class MarketCap(CustomFactor):
    inputs = [USEquityPricing.close, morningstar.valuation.shares_outstanding]
    window_length = 1

    def compute(self, today, assets, out, close, shares):
        out[:] = close[-1] * shares[-1]
        
class MACDFactor(CustomFactor):
    inputs = [USEquityPricing.close]
    window_length = 35
    
    def compute(self, today, assets, out, close):
        """
        Compute MACD each column of high, low, and close.
        """
        outs = []
        for t in close.T:
            if isnan(t).any():
                outs.append(0)
                continue
            m,s,h = talib.MACD(t, fastperiod=12, 
                                        slowperiod=26, signalperiod=9)
            outs.append(m[-1])
        out[:] = outs   
        
def initialize(context):
    """
    Called once at the start of the algorithm.
    """
    #how many stocks I would like to invest every time
    context.long_limit = 10
    context.short_limit = 10
    
    schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(hours=1))
    schedule_function(my_record_vars, date_rules.every_day(), time_rules.market_close())

    attach_pipeline(my_pipeline(context), 'my_pipeline')

def my_pipeline(context):
    """
    A function to create our dynamic stock selector (pipeline). Documentation on
    pipeline can be found here: https://www.quantopian.com/help#pipeline-title
    """
    pipe = Pipeline()

    market_cap = MarketCap()
    sma_21 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=21)
    sma_55 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=55)
    sma_100 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=100)
    sma_200 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=200)
    rsi_14 = RSI(inputs=[USEquityPricing.close], window_length= 14)
    
    pipe.add(sma_21, 'sma_21')
    pipe.add(sma_55, 'sma_55')
    pipe.add(sma_100, 'sma100')
    pipe.add(sma_200, 'sma_200')
    pipe.add(rsi_14, 'rsi_14')
    pipe.add(market_cap, 'market_cap')
    
    #Create a filter which will return boolean value
    isTrendUp = (sma_55 > sma_100) & (sma_100 > sma_200)
    isTrendDown = (sma_55 < sma_100) & (sma_100 < sma_200)
    isOverBought = rsi_14 > 80
    isOverSold = rsi_14 < 20

    # only want the 20 highest market_cap stocks that pass the isTrendUp and isTrendDown screen
    market_cap_rank = market_cap.rank(mask=isTrendUp | isTrendDown , ascending=False)
    top100_market_cap = market_cap_rank <= 100
    
    MACD = MACDFactor([USEquityPricing.close], mask=top100_market_cap)

    pipe.add(market_cap_rank, 'market_cap_rank')
    pipe.add(MACD, 'MACD')

    #add rsi criteria filters to pipeline
    pipe.add(isOverBought, 'isOverBought')
    pipe.add(isOverSold, 'isOverSold')
    #To inform our pipeline that each day we want to throw away any rows for whose assets
    #our filters produced False value we will set screen
    pipe.set_screen(top100_market_cap)

    return pipe

def before_trading_start(context, data):
    context.output = pipeline_output('my_pipeline')
        
    context.long_signal = ((context.output['sma_21'] > context.output['sma_55']) & (context.output['isOverSold'] == True) & (context.output['MACD'] > 0)).to_dict()
    
    context.short_signal = ((context.output['sma_21'] < context.output['sma_55']) & (context.output['isOverBought'] == True) & (context.output['MACD'] < 0)).to_dict()
    
        
def my_rebalance(context, data):                                     
    for stock in context.long_signal:
        
        if context.long_signal[stock] & data.can_trade(stock) & (stock not in context.portfolio.positions):
            order_target_percent(stock, 0.5/context.long_limit)

    for stock in context.short_signal:
        if context.short_signal[stock] & data.can_trade(stock) & (stock not in
            context.portfolio.positions):
            order_target_percent(stock, -0.5 / context.short_limit)

    for stock in context.portfolio.positions:
        if data.can_trade(stock) & (stock not in context.output.index):
            order_target(stock, 0)

def my_record_vars(context, data):
    """
    Plot variables at the end of each day.
    """
    longs, shorts = 0, 0
    for position in context.portfolio.positions.values():
        if position.amount < 0:
            #log.info("PROOF: Have a position of {} in {}".format(position.amount, position.sid.symbol))
            shorts += 1
        else:
            longs += 1
            
    record(leverage=context.account.leverage,
           longs = longs,
           shorts = shorts)




There was a runtime error.

Thanks Lotanna!

I really appreciate the solution.

Best regards,
Gerard