Back to Community
How can I close a position using a strategy signal.

Hello guys!, I´m analyzing the results provided by the backtest environment and I am wondering how can I close a position using a strategy signal rather than closing the order in the inmediatly next day as it is doing right now the algorithm.

The strategy to open positions is just a simple moving average condition, which one consist on open the trade if the price > sma20, price < sma50 and price < sma200, with a candle pattern signal of hammer or a bullish engulfing pattern.

I´m trying to close the trade with a strategy signal that is sma50 < sma20.

When I put all of this together and run the full backtest, when I load the transaction I see that every position I take is closed inmediatly on the next day and not taking the strategy signal of close that I want.

Any idea to fix this?

Thanks a lot.

Clone Algorithm
1
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.algorithm import order_optimal_portfolio
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline import CustomFactor
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.filters import QTradableStocksUS
import quantopian.optimize as opt
from quantopian.pipeline.data import Fundamentals
from quantopian.pipeline import Pipeline
from quantopian.pipeline.factors import SimpleMovingAverage,RSI, MACDSignal, SimpleBeta, RollingPearsonOfReturns
from quantopian.pipeline.filters import Q1500US, QTradableStocksUS, Q500US, Q3000US
from quantopian.pipeline.data import USEquityPricing
from quantopian.pipeline.factors import CustomFactor
from quantopian.pipeline import CustomFilter
import pandas as pd
import numpy as np

class engulf(CustomFilter):
    # this is a class to construct a engulfing candle pattern signal.
    
    inputs = [USEquityPricing.close, USEquityPricing.low, USEquityPricing.open, USEquityPricing.high]  

    window_length = 2
    
    def compute(self, today, assets, out, close, low, open1, high): 
        
        condition_1 = (high[-1] > high[-2]) & (low[-1]<low[-2])
        
        condition_2 = (close[-1] > close[-2]) & (open1[-1]<open1[-2])
        
        condition_3 = close[-1] > open1[-1]
        
        condition_4 = (close[-1] > open1[-2]) & (open1[-1]<close[-2])
        
        
        result = condition_1 & condition_2 & condition_3 & condition_4 
        
        out[:] = result   
        
        
class volume(CustomFilter):
    
    # this is a class for build a relative volume indicator and select a particular range
    
    inputs = [USEquityPricing.volume]
    window_length = 6
    def compute(self, today, assets, out, vol):     
                
        low_condition_1 = vol[-1] / np.mean(vol, axis = 0)>=1
        
        low_condition_2 = (vol[-1] / np.mean(vol, axis = 0))<2
                                
        out[:] =  low_condition_1 & low_condition_2
         


def initialize(context):
   
    schedule_function(
        my_rebalance,
        date_rules.every_day(),
        time_rules.market_open()
    )

    
    my_pipe = make_pipeline()
    attach_pipeline(my_pipe, 'my_pipeline')

def make_pipeline():
    sector = Fundamentals.morningstar_sector_code.latest
    industry = Fundamentals.morningstar_industry_code.latest
    #universe= Q3000US() & sector.element_of([206]) 
    universe = Q3000US() & QTradableStocksUS()   
    sma20 = SimpleMovingAverage(inputs = [USEquityPricing.close], window_length=20 )
    sma50 = SimpleMovingAverage(inputs = [USEquityPricing.close], window_length=50, mask = universe )
    sma200 = SimpleMovingAverage(inputs = [USEquityPricing.close], window_length=200 )
    price = SimpleMovingAverage(inputs = [USEquityPricing.close], window_length=1, mask = universe )
    high = SimpleMovingAverage(inputs = [USEquityPricing.high], window_length=1, mask = universe )
    low = SimpleMovingAverage(inputs = [USEquityPricing.low], window_length=1, mask = universe )
    close = SimpleMovingAverage(inputs = [USEquityPricing.close], window_length=1, mask = universe )
    open1 = SimpleMovingAverage(inputs = [USEquityPricing.open], window_length=1, mask = universe )
    rsi = RSI()
    macd = MACDSignal()
    corr = RollingPearsonOfReturns(target=symbol('SPY'),returns_length = 20, correlation_length =20 )
    
    
    #1
    
    condition_1 = rsi < 40
    condition_2 = macd < -1.5
    
    technical_condition =  condition_2 & condition_1
    
    #2   
    condition_1 = price > sma20
    condition_2 = price < sma50
    condition_3 = price < sma200 
    condition_4 = 5 < price <120
    
    sma_condition = condition_1 & condition_4 & condition_3 & condition_2 
          
    #3 
    
    mid_price = (high+low)/2
    box = (close-open1).abs()
    limit_box  = (high-low)*0.25
    condition_5 = close > mid_price
    condition_6 = open1 > mid_price
    condition_7 = box <= limit_box
    # this is way to construct a hammer candle pattern
    hammer = condition_5 & condition_6 & condition_7 
    
    #in order to select hammer candle pattern or engulf pattern
    
    price_action = (hammer &  volume() | (engulf()  )  )   
       
    indicator =   sma_condition
    
    #technical_condition | 
    
    longs1 =  ((corr<0.4)&(corr>0.3)) & indicator & price_action
    
    #a strategy signal to close the trade
   
    exits1 =  sma50 < sma20
    
    #exits2 = rsi > 50
                                
    return Pipeline(columns={ "longs1": longs1, 
                            "exits1": exits1} ,screen = universe  ) 
    

def compute_target_weights(context, data):
    
    weights = {}
    
    if context.longs1:
        long_weight = 1 / len(context.longs1)        
              
    else:
        return weights
    
    for security in context.portfolio.positions:
        
        if security in context.longs1  and data.can_trade(security):
           weights[security] = 0
            
     
    for security in context.longs1:
        weights[security] = long_weight
        
    #for security in context.longs2:
        #weights[security] = long2_weight

    
    return weights

def before_trading_start(context, data):
    """
    Get pipeline results.
    """

    # Gets our pipeline output every day.
    pipe_results = pipeline_output('my_pipeline')

    # Go long in securities for which the 'longs' value is True,
    # and check if they can be traded.
    context.longs1 = []
    for sec in pipe_results[pipe_results['longs1']].index.tolist():
        if data.can_trade(sec):
            context.longs1.append(sec)
            
    context.exits1 = []
    for sec in pipe_results[pipe_results['exits1']].index.tolist():
        if data.can_trade(sec):
           context.exits1.append(sec)
    

def my_rebalance(context, data):
    
    
    target_weights = compute_target_weights(context, data)
    
    if target_weights:
        order_optimal_portfolio(
            objective=opt.TargetWeights(target_weights),
            constraints=[],
        )
        
    #for sec in context.portfolio.positions:
     #   if sec in context.exits1:
      #     order_target_percent(sec,0)
       #    print(sec)
There was a runtime error.
4 responses

One thing to remember with the order_optimal_portfolio, and using the TargetWeights objective, is it will close anything that isn't explicitly given a weight. Therefore, all one really needs to define are the securities one ultimately wishes to hold. Any other securities in the current portfolio will be automatically closed.

There are of course a lot of ways to code for exiting positions, but I like using sets. Simply combine the securities currently held with those to open but then exclude anything to exit. Something like this

def my_rebalance(context, data):  
    # Get todays pipeline output.  
    pipe_results = pipeline_output('my_pipeline')

    # Get the current positions, stocks we want to go long, and exits  
    # Use sets for ease in combining by using boolean logic  
    current_positions = set(context.portfolio.positions.keys())  
    longs = set(pipe_results.query('longs1').index)  
    exits = set(pipe_results.query('exits1').index)  
    universe = set(pipe_results.index)

    # Hold anything in our current universe and we currently hold,  
    # (ie must be in the current universe),  
    # Exclude anything we have but want to exit.  
    # Include anything we want to go long.  
    holds = ((universe & current_positions)-exits) | longs

    # Make a series of equal weights for anything we want to hold  
    # If qty of holds is zero then set weight to zero (avoid divide by 0)  
    qty_of_holds = len(holds)  
    weight = 1.0 / qty_of_holds if qty_of_holds else 0  
    target_weights = pd.Series(weight, index=holds)

    # Place orders to get to our target_weights  
    # Anything not in holds will be closed  
    order_optimal_portfolio(  
            objective=opt.TargetWeights(target_weights),  
            constraints=[],  
        )

Notice this also closes anything which isn't in the current universe. This keeps any currently held securities, which happen to fall out of the trading universe, from remaining open indefinitely.

See the attached algo.

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
from quantopian.algorithm import order_optimal_portfolio
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline, CustomFactor, CustomFilter

from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.data import Fundamentals

from quantopian.pipeline.factors import SimpleMovingAverage, RSI, MACDSignal, SimpleBeta, RollingPearsonOfReturns, Latest
from quantopian.pipeline.filters import Q1500US, QTradableStocksUS, Q500US, Q3000US

import quantopian.optimize as opt

import pandas as pd
import numpy as np

class engulf(CustomFilter):
    # this is a class to construct a engulfing candle pattern signal.
    
    inputs = [USEquityPricing.close, 
              USEquityPricing.low, 
              USEquityPricing.open, 
              USEquityPricing.high]  

    window_length = 2
    
    def compute(self, today, assets, out, close, low, open1, high): 
        condition_1 = (high[-1] > high[-2]) & (low[-1]<low[-2])
        condition_2 = (close[-1] > close[-2]) & (open1[-1]<open1[-2])
        condition_3 = close[-1] > open1[-1]
        condition_4 = (close[-1] > open1[-2]) & (open1[-1]<close[-2])
        
        result = condition_1 & condition_2 & condition_3 & condition_4 
    
        out[:] = result   
        
        
class volume(CustomFilter):
    
    # this is a class for build a relative volume indicator and select a particular range
    inputs = [USEquityPricing.volume]
    window_length = 6
    
    def compute(self, today, assets, out, vol):     
        low_condition_1 = vol[-1] / np.mean(vol, axis = 0)>=1
        low_condition_2 = (vol[-1] / np.mean(vol, axis = 0))<2
                                
        out[:] =  low_condition_1 & low_condition_2

def initialize(context):
   
    schedule_function(
        my_rebalance,
        date_rules.every_day(),
        time_rules.market_open()
    )

    my_pipe = make_pipeline()
    attach_pipeline(my_pipe, 'my_pipeline')

def make_pipeline():
    sector = Fundamentals.morningstar_sector_code.latest
    industry = Fundamentals.morningstar_industry_code.latest
    universe = Q3000US() & QTradableStocksUS()   
    
    sma20 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=20 )
    sma50 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=50, mask = universe )
    sma200 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=200 )
    
    price = Latest(inputs=[USEquityPricing.close], mask=universe)
    high = Latest(inputs=[USEquityPricing.high], mask=universe)
    low = Latest(inputs=[USEquityPricing.low], mask=universe)
    close = Latest(inputs=[USEquityPricing.close], mask=universe)
    open1 = Latest(inputs=[USEquityPricing.open], mask=universe)
    
    rsi = RSI()
    macd = MACDSignal()
    corr = RollingPearsonOfReturns(target=symbol('SPY'),returns_length = 20, correlation_length =20 )
    
    #1
    condition_1 = rsi < 40
    condition_2 = macd < -1.5
    technical_condition =  condition_2 & condition_1
    
    #2   
    condition_1 = price > sma20
    condition_2 = price < sma50
    condition_3 = price < sma200 
    condition_4 = 5 < price <120
    sma_condition = condition_1 & condition_4 & condition_3 & condition_2 
          
    #3 
    mid_price = (high+low)/2
    box = (close-open1).abs()
    limit_box  = (high-low)*0.25
    condition_5 = close > mid_price
    condition_6 = open1 > mid_price
    condition_7 = box <= limit_box
    
    # this is way to construct a hammer candle pattern
    hammer = condition_5 & condition_6 & condition_7 
    
    #in order to select hammer candle pattern or engulf pattern
    price_action = (hammer & volume() | (engulf()))
    indicator =   sma_condition
    
    #technical_condition
    longs1 =  ((corr<0.4)&(corr>0.3)) & indicator & price_action
    
    #a strategy signal to close the trade
    exits1 =  sma50 < sma20
    
    #exits2 = rsi > 50
    pass

    return Pipeline(columns={ "longs1": longs1, 
                            "exits1": exits1,
                            } ,
                    screen = universe  ) 

def my_rebalance(context, data):
    # Gets our pipeline output.
    pipe_results = pipeline_output('my_pipeline')
    
    # Get the current positions, stocks we want to go long, and exits
    # Use sets for ease in combining using boolean logic
    current_positions = set(context.portfolio.positions.keys())
    longs = set(pipe_results.query('longs1').index)
    exits = set(pipe_results.query('exits1').index)
    universe = set(pipe_results.index)
    
    # Hold anything in our current universe and we currently hold,
    # (ie must be in the current universe),
    # Exclude anything we have but want to exit.
    # Include anything we want to go long.
    holds = ((universe & current_positions)-exits) | longs
        
    # Make a series of equal weights for anything we want to hold
    # If qty of holds is zero then set weight to zero (avoid divide by 0)
    qty_of_holds = len(holds) 
    weight = 1.0 / qty_of_holds if qty_of_holds else 0 
    target_weights = pd.Series(weight, index=holds)
        
    # Place orders to get to our target_weights
    # Anything not in holds will be closed
    order_optimal_portfolio(
            objective=opt.TargetWeights(target_weights),
            constraints=[],
        )
        
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.

Hey Dan!! thank you a lot for your early reply, it helps me a lot with my code development, I would not figure out that logical structure by myself haha

Hello Dan! how are you doing? I want to thanks again to you for the statement provided in order to hold trades day by day until the exit strategy get executed. But now I'm wondering how can I "freeze" the adding of stocks in the Target_weight series once the leverage reach specific level, I was trying to do something like this


if  context.account.leverage < 0.9:  
    holds = ((universe & current_positions)-exits) | longs  
 if context.account.leverage > 0.9:  
    holds = set([])

But what it does is to close all opened trades before the exit strategy get executed and once the leverage is below the level it starts to execute trades again, what I'm trying to code is, if the leverage is reached, stop trading but keep the opened positions until the exit strategy (or a fixed stop loss) is executed and start to freeing up space to the leverage for start to trade once again.

Thanks!

Clone Algorithm
0
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.algorithm import order_optimal_portfolio
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline, CustomFactor, CustomFilter

from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.data import Fundamentals

from quantopian.pipeline.factors import SimpleMovingAverage, RSI, MACDSignal, SimpleBeta, RollingPearsonOfReturns, Latest
from quantopian.pipeline.filters import Q1500US, QTradableStocksUS, Q500US, Q3000US

import quantopian.optimize as opt

import pandas as pd
import numpy as np

class engulf(CustomFilter):
    # this is a class to construct a engulfing candle pattern signal.
    
    inputs = [USEquityPricing.close, 
              USEquityPricing.low, 
              USEquityPricing.open, 
              USEquityPricing.high]  

    window_length = 2
    
    def compute(self, today, assets, out, close, low, open1, high): 
        condition_1 = (high[-1] > high[-2]) & (low[-1]<low[-2])
        condition_2 = (close[-1] > close[-2]) & (open1[-1]<open1[-2])
        condition_3 = close[-1] > open1[-1]
        condition_4 = (close[-1] > open1[-2]) & (open1[-1]<close[-2])
        
        result = condition_1 & condition_2 & condition_3 & condition_4 
    
        out[:] = result   
        
        
class volume(CustomFilter):
    
    # this is a class for build a relative volume indicator and select a particular range
    inputs = [USEquityPricing.volume]
    window_length = 6
    
    def compute(self, today, assets, out, vol):     
        low_condition_1 = vol[-1] / np.mean(vol, axis = 0)>=1
        low_condition_2 = (vol[-1] / np.mean(vol, axis = 0))<2
                                
        out[:] =  low_condition_1 & low_condition_2

def initialize(context):
   
    schedule_function(
        my_rebalance,
        date_rules.every_day(),
        time_rules.market_open()
    )

    my_pipe = make_pipeline()
    attach_pipeline(my_pipe, 'my_pipeline')

def make_pipeline():
    sector = Fundamentals.morningstar_sector_code.latest
    industry = Fundamentals.morningstar_industry_code.latest
    universe = Q3000US() & QTradableStocksUS()   
    
    sma20 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=20 )
    sma50 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=50, mask = universe )
    sma200 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=200 )
    
    price = Latest(inputs=[USEquityPricing.close], mask=universe)
    high = Latest(inputs=[USEquityPricing.high], mask=universe)
    low = Latest(inputs=[USEquityPricing.low], mask=universe)
    close = Latest(inputs=[USEquityPricing.close], mask=universe)
    open1 = Latest(inputs=[USEquityPricing.open], mask=universe)
    
    rsi = RSI()
    macd = MACDSignal()
    corr = RollingPearsonOfReturns(target=symbol('SPY'),returns_length = 20, correlation_length =20 )
    
    #1
    condition_1 = rsi < 40
    condition_2 = macd < -1.5
    technical_condition =  condition_2 & condition_1
    
    #2   
    condition_1 = price > sma20
    condition_2 = price < sma50
    condition_3 = price < sma200 
    condition_4 = 5 < price <120
    sma_condition = condition_1 & condition_4 & condition_3 & condition_2 
          
    #3 
    mid_price = (high+low)/2
    box = (close-open1).abs()
    limit_box  = (high-low)*0.25
    condition_5 = close > mid_price
    condition_6 = open1 > mid_price
    condition_7 = box <= limit_box
    
    # this is way to construct a hammer candle pattern
    hammer = condition_5 & condition_6 & condition_7 
    
    #in order to select hammer candle pattern or engulf pattern
    price_action = (hammer & volume() | (engulf()))
    indicator =   sma_condition
    
    #technical_condition
    longs1 =  ((corr<0.4)&(corr>0.3)) & indicator & price_action
    
    #a strategy signal to close the trade
    exits1 =  sma50 < sma20
    
    #exits2 = rsi > 50
    pass

    return Pipeline(columns={ "longs1": longs1, 
                            "exits1": exits1,
                            } ,
                    screen = universe  ) 

def my_rebalance(context, data):
    # Gets our pipeline output.
    pipe_results = pipeline_output('my_pipeline')
    context.largos = True
    
    # Get the current positions, stocks we want to go long, and exits
    # Use sets for ease in combining using boolean logic
    
    
    
    current_positions = set(context.portfolio.positions.keys())
    longs = set(pipe_results.query('longs1').index)
    exits = set(pipe_results.query('exits1').index)
    universe = set(pipe_results.index)
    
    # Hold anything in our current universe and we currently hold,
    # (ie must be in the current universe),
    # Exclude anything we have but want to exit.
    # Include anything we want to go long.
    
    if context.largos:
       
        holds = ((universe & current_positions)-exits) | longs
        
    # Make a series of equal weights for anything we want to hold
    # If qty of holds is zero then set weight to zero (avoid divide by 0)
    qty_of_holds = len(holds) 
    weight = 1.0 / qty_of_holds if qty_of_holds else 0 
    target_weights = pd.Series(weight, index=holds)
    
    
        
    # Place orders to get to our target_weights
    # Anything not in holds will be closed
    
    leverage = opt.MaxGrossExposure(1)
    order_optimal_portfolio(
            objective=opt.TargetWeights(target_weights),
            constraints=[leverage],
        )
    if context.account.leverage > 0.9:
        
        context.largos = False
        
    else:
        context.largos = True
            
    #print ( context.largos)
    #print(context.account.leverage)
    print(target_weights)
There was a runtime error.

I think what may get you closer to what you want is this

if  context.account.leverage < 0.9:  
    holds = ((universe & current_positions)-exits) | longs  
 if context.account.leverage > 0.9:  
    holds =  ((universe & current_positions)-exits)

If the leverage is greater than .9, that will keep the current positions but exit anything you want to exit or isn't in the current universe. It won't add any new positions.

While this will function as you stated, it may not get you what you want. What is the motivation for 'freezing' the addition of new stocks? Is it to let the current holdings alone until they are explicitly closed? If so, maybe try simply adding a Frozen constraint. Something like this

freeze_current_holdings = opt.Frozen(current_positions)  
leverage_of_1 = opt.MaxGrossExposure(1)  
order_optimal_portfolio(  
            objective=opt.TargetWeights(target_weights),  
            constraints=[freeze_current_holdings, leverage_of_1],  
        )

While this is maybe closer to what you want it's not ideal. The issue you will have in both these cases is position sizing. As an example, if the algo starts off with 10 'longs' then each will be equally weighted with a position of .1. Now, one of those positions closes and there happens to be 10 more 'longs'. Because the existing positions are frozen, there is .9 of the portfolio in current holdings. The algo will allocate the remaining .1 to those 10 stocks. Each of those new positions would therefore get .01 weight.

You may consider a common approach and target a fixed number of holdings. As an example, assume you choose to always have a target of 10 holdings. Using the above example, after one of those 10 closes, only fill it with 1 long and not all the longs. Often there is some factor which is proportional to expected returns or the probability of success. Take the stock with this largest factor value to fill the 'hole' .

Just some ideas. Good luck.