Back to Community
Leverage/Slippage/Commission Factor Against a Long Only Model

Could a resident Q Guro take a look and provide feedback on this taking into account the following:

In an institutional capacity, there is no leverage factor.

Commissions are close to 5% of retail (even free commission has a slippage factor).

All of the dialogue on Q centers around leverage costs and commissions as the ultimate test of whether a model is "worthy" or not.

However, the Q fund should have an institutional footprint where these costs are not as important when measuring the viability of the underlying model. From what I've been reading (mind you I am only a sideline observer), it appears many good models are developed with the intention of gaining access to Q fund capital, but are being written as if they will be charged margin rates and retail commissions.

Is this perhaps a way for Q to create a built in cushion that results in an increase in absolute returns purely from finance cost and commission savings?

With this algo, assuming zero capital costs, and adjusting data granularity such that trade frequency is increased without having to worry about commissions eating into profits would create something that while not worthy of the Q fund, could be worthy of a hedge fund.

I would love to see variations on this algo with the above assumptions factored in that produce significantly higher returns without worrying about excess volatility. At the hedge fund level, portfolio stops can be used to control downside risk. I haven't figured out how to incorporate a portfolio level stop on Q such that when the portfolio is stopped out and goes to 100% cash (or alternative security), the book is reestablished as if the algo was reset the next day.

Any additional insight would be highly educational for me.

Clone Algorithm
21
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

import numpy as np
import pandas as pd

        
def initialize(context):

    set_asset_restrictions(security_lists.restrict_leveraged_etfs)
    
    schedule_function(rebalance,
        date_rule=date_rules.month_start(),
        time_rule=time_rules.market_open(minutes = 20))

    schedule_function(buy,
        date_rule=date_rules.every_day(),
        time_rule=time_rules.market_open(minutes = 30))

    context.m = sid(24744)
    context.f = sid(23921)

    context.longs = None
    
    context.last_month = -1
    context.start = True
    
    
def before_trading_start(context, data):
    
    month = get_datetime().month
    if context.last_month == month:
        return
    context.last_month = month

    df = get_fundamentals(
        query(fundamentals.valuation_ratios.ev_to_ebitda,
            fundamentals.valuation_ratios.sales_yield,
            fundamentals.operation_ratios.roic)
        .filter(fundamentals.company_reference.primary_exchange_id.in_(["NYSE", "NYS"]))
        .filter(fundamentals.operation_ratios.total_debt_equity_ratio != None)
        .filter(fundamentals.valuation.market_cap != None)
        .filter(fundamentals.valuation.shares_outstanding != None)  
        .filter(fundamentals.company_reference.primary_exchange_id != "OTCPK") # no pink sheets
        .filter(fundamentals.company_reference.primary_exchange_id != "OTCBB") # no pink sheets
        .filter(fundamentals.asset_classification.morningstar_sector_code != None) # require sector
        .filter(fundamentals.share_class_reference.security_type == 'ST00000001') # common stock only
        .filter(~fundamentals.share_class_reference.symbol.contains('_WI')) # drop when-issued
        .filter(fundamentals.share_class_reference.is_primary_share == True) # remove ancillary classes
        .filter(((fundamentals.valuation.market_cap*1.0) / (fundamentals.valuation.shares_outstanding*1.0)) > 1.0)  # stock price > $1
        .filter(fundamentals.share_class_reference.is_depositary_receipt == False) # !ADR/GDR
        .filter(~fundamentals.company_reference.standard_name.contains(' LP')) # exclude LPs
        .filter(~fundamentals.company_reference.standard_name.contains(' L P'))
        .filter(~fundamentals.company_reference.standard_name.contains(' L.P'))
        .filter(fundamentals.balance_sheet.limited_partnership == None) # exclude LPs
        .order_by(fundamentals.valuation.market_cap.desc())
        .limit(1500))
    
    dft = df.T
    
    context.score = pd.Series(0, index = dft.index)
    
    # EV/EBITDA, in-order (lower is better), nan goes last
    context.score += dft['ev_to_ebitda'].rank(ascending=True, na_option='bottom')
    
    # sales yield, inverse (higher is better), nan goes last
    context.score += dft['sales_yield'].rank(ascending=False, na_option='top')
    
    # return on invested capital, inverse (higher is better), nan goes last
    context.score += dft['roic'].rank(ascending=False, na_option='top')

    
def rebalance(context, data):

    context.longs = context.score.dropna().order().head(5).index

    P = data.history(context.m, 'price', 100, '1d')
    if P.tail(10).median() < P.median():
        context.longs = [context.f] 

        
def buy(context, data):
    
    for s in context.portfolio.positions:
        if s in context.longs:
            continue
        if not data.can_trade(s):
            continue        
        order_target(s, 0)    
        print (" Selling stocks: " + str(s))   
    if get_open_orders():
            return
    for s in context.longs:
        if not data.can_trade(s):
            continue
        
        order_target_percent(s, 1.0 / len(context.longs))
        print(" Buying stocks: "+str(s) + " shares: "+ str(context.portfolio.positions[s].amount))                     
    record(leverage = context.account.leverage,
          exposure = context.account.net_leverage)                           
    
    
def handle_data(context, data):
    
    if context.start:
        rebalance(context, data)
        buy(context, data)
        context.start = False


    
    
There was a runtime error.
1 response

Sorry for the slow reply on this - I had it in my to-reply list and forgot about it.

it definitely is worthwhile to evaluate algorithms with low/no commissions and see how they do. You can evaluate without slippage, but you really need to make sure slippage comes back in when you start considering the capacity of your algorithm. When we evaluate algorithms for the fund we definitely take all of this into consideration.

When we look at algorithms for allocations we use a pyfolio tearsheet of a long backtest. You've provided the long backtest so I ran the tearsheet, attached. As you know, we're not looking for long only algorithms. That said, a good long-only algorithm can often be morphed into a hedged one. Some thoughts:

  • It has some painful drawdowns. Over the 13 year test it has 5 drawdowns of 20%. That's more drawdown that we'd prefer.
  • The monthly returns are very inconsistent. Most of the algorithm's success is driven by 5 or 6 "lucky" months. We're looking for algorithms that have a more consistent profile. It's hard to predict if there are more lucky months in this algorithm's future.
  • It spends long periods of time in a single ETF. That's an exposure to a single name that we wouldn't want to make a large allocation to.
  • When it is "in" the market, it appears to be in 5 names at a time. That's still higher exposure than we'd like. We'd rather see a max exposure to a single name of 10% max.
Loading notebook preview...
Notebook previews are currently unavailable.
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.