Quantopian to QC - Modified QUALITY+IN-OUT Strat

Back

Hi - I am trying to convert a Quantopian Code to QC but its taking much more effort and time than what I expected since I am a beginner in Python. I wanted to ask if anyone here could help me convert the algo to QC?

Thanks!

Update Backtest







 
0

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 QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect 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. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Perhaps you can clone one on the existing versions of in and out strategy and then modify it to include the updated logic?  The Flex 4 version looks like a solid one to work with.

0

Thanks but I need a bit more help than this! :)

I yet don't know how to code the stock selection part into it. If anyone would like to cooperate I can share the code.

0

Are you specifying a number of stocks to trade into in place of QQQ/SPY, or do you want auto picking based on criteria?  Specifying predetermined stocks should be easy.  I don't know the autopicking routines.  I'm also very new to this platform.

1

Hi S.T.E.,

Consider reviewing our Universe Selection documentation and Framework Boot Camp for a better understanding of stock selection. For further assistance, publish the Quantopian code to this forum.

Best,
Derek Melchin

0

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 QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect 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. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


"""
Based on 'In & Out' strategy by Peter Guenther 10-04-2020
expanded by Tentor Testivis and coded by Dan Whitnable (Quantopian)
AND
'Quality companies in an uptrend' by Chris Cain 11-22-2019
coded by Dan Whitnable (Quantopian) with bonds weights  fixed by Vladimir
"""
# Import necessary algo and Pipeline modules
import quantopian.algorithm as algo
from quantopian.pipeline import Pipeline
# Import specific filters and factors which will be used
from quantopian.pipeline.factors import CustomFactor, Returns
from quantopian.pipeline.filters import Q500US, Q1500US, Q3000US, QTradableStocksUS, StaticAssets
from quantopian.pipeline.factors import SimpleMovingAverage as SMA 
# Import datasets which will be used
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.data.morningstar import Fundamentals as ms
# import optimize
import quantopian.optimize as opt
# Import numpy and pandas because they rock
import numpy as np
import pandas as pd
class Mean_Over_STD(CustomFactor):
    window_length = 756
    def compute(self, today, assets, out, value):
            out[:] = np.nanmean(value, axis=0) / np.nanstd(value, axis=0)
class total_earning_growth(CustomFactor):
    window_length = 1
    def compute(self, today, assets, out, eps, dps, equity):
            out[:] = eps[0]+dps[0]+equity[0]
class acc_earning_growth(CustomFactor):
    window_length = 140
    def compute(self, today, assets, out, eps, dps, equity):
            out[:] = (eps[-1]+dps[-1]+equity[-1])/(eps[1]+dps[1]+equity[1])
class sales_growth(CustomFactor):
    window_length = 252
    def compute(self, today, assets, out, sales):
            out[:] = (sales[-1])/(sales[1])
def initialize(context): 
    set_commission(commission.PerShare(cost=0.00, min_trade_cost=0.00))
    #########################################
    ## In & Out parameters ##################
    ## based on "New Strategy - In & out" ##
    context.WAIT_DAYS = 15 # min days of being out; 3 trading weeks
    context.XLI = symbol('XLI') # industrials
    context.DBB = symbol('DBB') # materials
    context.BIL = symbol('SHY') # bond / interest rate
    context.UUP = symbol('UUP') # USD index
    context.VIXY = symbol('TVIX')
    context.SIGNAL_UNIVERSE = symbols('XLI', 'DBB', 'SHY', 'UUP', 'TVIX')
    # Assets and associated weights when 'out' of the market
    # Any asset held but not in the list will be closed
    context.TRADE_WEIGHTS_OUT = {symbol('TIPZ'): 0.0, 
                                symbol('TYD'): 0.5, symbol('TMF'): 0.3
                                }
    ##############################################
    ## Your stock selection strategy parameters ##
    ## based on "New Strategy - Quality companies in an uptrend ##
    # Set target number of securities to hold and top ROE qty to filter  
    context.TARGET_SECURITIES = 10  
    context.TOP_ROIC_QTY = 50 #First sort by ROE
    # This is for the trend following filter  
    context.SPY = symbol('SPY')  
    context.TF_LOOKBACK = 126  
    context.TF_CURRENT_LOOKBACK = 10
    # This is for the determining momentum  
    context.MOMENTUM_LOOKBACK_DAYS = 126 #Momentum lookback  
    context.MOMENTUM_SKIP_DAYS = 6 
    # Initialize any other variables before being used  
    context.stock_weights = pd.Series()
    # Schedule functions  
    schedule_function(  
        # daily rebalance if OUT of the market
        rebalance_when_out_of_the_market,  
        date_rules.every_day(),
        time_rules.market_open(minutes = 30)
    ) 
    schedule_function(  
        # weekly rebalance if IN the market
        rebalance_when_in_the_market,  
        date_rules.week_start(days_offset=4),
        time_rules.market_open(minutes = 30)
    )
    schedule_function(  
        # record signals every day
        record_signals,  
        date_rules.every_day(),
        time_rules.market_open()
    )
    algo.attach_pipeline(make_pipeline(context), 'pipeline')  
   # context.tvix= sid(40515)#1.5x leveraged VIX ETF
   # context.vix=sid(38054)#2x leveraged VIX
   # schedule_function(ma_crossover_handling, date_rules.every_day(),time_rules.market_open(hours=1))
class Days_Since_True(CustomFactor): 
    """
    Finds the number of days since a signal was last True
    Max lookback is 100
    """
    window_length = 100
    def compute(self, today, assets, out, input_factor): 
        # Flip the factor first to last so argmax finds the last occurance
        # Set the earliest day to True so it always finds at least 1 True 
        input_factor[0] = True
        out[:] = np.argmax(np.flipud(input_factor), axis=0)        
def make_pipeline(context):
    ###############################
    ## In & Out pipeline content ##
    # Set universe to only the assets we use for signals
    mask_io = StaticAssets(context.SIGNAL_UNIVERSE)
    # Get the returns for basic metals, industrials and bonds
    # Get the cost of debt which is the inverse the bond returns
    returns = Returns(window_length=58, mask=mask_io)
    basic_metals_down = returns[context.DBB] < -.07
    industrial_sector_down = returns[context.XLI] < -.07
    short_term_bond_down = returns[context.BIL] < -.006
    cost_of_debt_up = short_term_bond_down
    dollar_up = returns[context.UUP] > .07
    vix_up = returns[context.VIXY] > 0.25
    # Bear signal if any one of the above is true
    bear_signal = (basic_metals_down |
                   industrial_sector_down |
                   cost_of_debt_up |
                   dollar_up |
                   vix_up
                   )-
    days_since_last_bear_signal = Days_Since_True([bear_signal], mask=mask_io)
    # Go out of the market if bear in recent days
    go_out_of_the_market = days_since_last_bear_signal < context.WAIT_DAYS 
    ##################################################
    #Your stock selection strategy pipeline content ##
    universe = QTradableStocksUS() & Q3000US() & Q500US()
    cash_return = ms.cash_return.latest.rank(mask=universe) 
    fcf_yield = ms.fcf_yield.latest.rank(mask=universe)
    ey = ms.earning_yield.latest.rank(mask=universe)
    rev_growth = ms.revenue_growth.latest.rank(mask=universe)
    roic = ms.roic.latest.rank(mask=universe)
    stable_roic = Mean_Over_STD(
        inputs=[ms.roic],
        mask=universe
    ).rank(mask=universe)
    ltd_to_eq = ms.long_term_debt_equity_ratio.latest.rank(mask=universe)  
    value = (cash_return + fcf_yield).rank()
    qs=sales_growth(inputs=[ms.total_revenue],mask=universe).rank(mask=universe)
    profit=(ms.operation_income_growth.latest.rank(mask=universe))
    pg=ms.diluted_eps_growth.latest.rank().rank(mask=universe)
    quality = roic + ltd_to_eq + stable_roic
    # Create a 'momentum' factor.  
    returns_overall = Returns(window_length=context.MOMENTUM_LOOKBACK_DAYS+context.MOMENTUM_SKIP_DAYS)  
    returns_recent = Returns(window_length=context.MOMENTUM_SKIP_DAYS)  
    momentum = returns_overall - returns_recent  
    # Filters for top quality and momentum to use in our selection criteria  
    top_quality = quality.top(context.TOP_ROIC_QTY, mask=universe)  
    top_quality_momentum = momentum.top(context.TARGET_SECURITIES, mask=top_quality)  
    pipe = Pipeline(columns={ 
            ## In & out pipeline output ##
            'go_out_of_the_market': go_out_of_the_market,
            'bear_signal': bear_signal,
            'days_since_last_bear_signal': days_since_last_bear_signal,
            'basic_metals_down': Days_Since_True([basic_metals_down], mask=mask_io) < context.WAIT_DAYS,
            'industrial_sector_down': Days_Since_True([industrial_sector_down], mask=mask_io) < context.WAIT_DAYS,
            'cost_of_debt_up': Days_Since_True([cost_of_debt_up], mask=mask_io) < context.WAIT_DAYS,
            'dollar_up': Days_Since_True([dollar_up], mask=mask_io) < context.WAIT_DAYS,
            'mask_io': mask_io,
            ## Your stock selection strategy pipeline output ##  
            'top_quality_momentum': top_quality_momentum,
            'universe': universe,
            },
    )
    return pipe
def rebalance_when_out_of_the_market(context, data):
    # Get signal. All rows with non-missing values are the same. Just choose the first one.
    df = algo.pipeline_output('pipeline') 
    go_out_of_the_market = df.loc[df.mask_io].go_out_of_the_market[0]
    if go_out_of_the_market:
        # Go all bonds
        order_optimal_portfolio(  
            objective = opt.TargetWeights(context.TRADE_WEIGHTS_OUT), 
            constraints = []  
        )
def rebalance_when_in_the_market(context, data):
    # Get signal. All rows with non-missing values are the same. Just choose the first one.
    df = algo.pipeline_output('pipeline') 
    go_out_of_the_market = df.loc[df.mask_io].go_out_of_the_market[0]
    if not go_out_of_the_market:
        ## Your stock selection strategy ##
        df = algo.pipeline_output('pipeline')  
        current_holdings = context.portfolio.positions
        #rule = 'top_quality_momentum & (trend_up or (not trend_up & index in @current_holdings))'
        rule = 'top_quality_momentum'
        stocks_to_hold = df[df['universe']].query(rule).index
        # Set desired stock weights  
        # Equally weight  
        stock_weight = 1.0 / context.TARGET_SECURITIES  
        context.stock_weights = pd.Series(index=stocks_to_hold, data=stock_weight)  
        order_optimal_portfolio(  
            objective = opt.TargetWeights(context.stock_weights), 
            constraints = []  
        )
def record_signals(context, data):
    df = algo.pipeline_output('pipeline') 
    in_the_market = not df.loc[df.mask_io].go_out_of_the_market[0]
    dollar = df.dollar_up[0]
    debt = df.cost_of_debt_up[0]
    metals = df.basic_metals_down[0]
    industrials = df.industrial_sector_down[0]
    # Record values for testing
    record(in_the_market=in_the_market)
    record(dollar=dollar, debt=debt, metals=metals, industrials=industrials)

0

Hi, I had a similar-ish strategy, but I was wondering whether there was anyway to do the six month momentum indicator faster? I found a similar strategy to what I wanted to do on the quant connect forums, but it's taking way too long to backtest. 

0


Hi Thanks for sharing, I think the momentum factor in the text strat performs better. I am still trying to get it work on Quantconnect but not even close!

0

Update Backtest





0

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 QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect 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. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Loading...

This discussion is closed