Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Optimize API gives worse results than without it

Hello,

here is an algorithm with and without using optimise api. The algorithm is not doing well, but it demonstrates that optimise api can misfire. Pl tell me what is the problem with the usage of the API (assuming API is not the problem).

thanks

-kamal

import math
import numpy as np

from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline import CustomFactor
from quantopian.pipeline.factors import SimpleMovingAverage
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.data import morningstar

class FCFRatio(CustomFactor):
# Pre-declare inputs and window_length
inputs = [morningstar.valuation_ratios.cf_yield]
window_length = 1
# Compute market cap value
def compute(self, today, assets, out, cf_yield):
out[:] = cf_yield[-1]

class PERatio(CustomFactor):
# Pre-declare inputs and window_length
inputs = [morningstar.valuation_ratios.pe_ratio]
window_length = 1
# Compute market cap value
def compute(self, today, assets, out, pe_ratio):
out[:] = pe_ratio[-1]

class MarketCap(CustomFactor):

# Pre-declare inputs and window_length  
inputs = [USEquityPricing.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]

Put any initialization logic here. The context object will be passed to

the other methods in your algorithm.

def initialize(context):
context.longs = 0
context.shorts = 0

# create a pipleine  
pipe = Pipeline()  
attach_pipeline(pipe, 'pe_ratio')  

mkt_cap = MarketCap()  
# create market cap filter  
top_2000 = mkt_cap.top(2000)  

pe_ratio = PERatio()  
fcf_ratio = FCFRatio()  

# add factors  
pipe.add(mkt_cap, 'mkt_cap')  
pipe.add(pe_ratio, "pe_ratio")  
pipe.add(fcf_ratio, "fcf_ratio")  

pipe.set_screen(top_2000)  
# Scedule my rebalance function

schedule_function(func=rebalance,  
                  date_rule=date_rules.every_day(),  
              time_rule=time_rules.market_close(minutes=60))  

schedule_function(func=rebalance,

                 # date_rule=date_rules.month_start(days_offset=15),  
                 # time_rule=time_rules.market_open(hours=5,minutes= 30),  

half_days=True)

def before_trading_start(context, data):
context.output = pipeline_output('pe_ratio')
my_list = context.output
context.short_list = my_list.sort(["fcf_ratio"], ascending=True).iloc[:5]
context.long_list = my_list.sort(["mkt_cap"], ascending=False).iloc[:10]

Will be called on every trade event for the securities you specify.

def handle_data(context, data):

record(leverage=context.account.leverage)  
record(longs=context.longs)  
record(shorts=context.shorts)

log.info("\n" + str(context.long_list.sort(['pe_ratio'], ascending=True)))

This rebalancing is called according to our schedule_function settings.

def rebalance(context,data):
context.longs = 0
context.shorts = 0

for long_stock in context.long_list.index:  
    if data.can_trade(long_stock):  
        order_target_percent(long_stock, 0.06)  
        context.longs = context.longs + 1  

# beta pf -1 and return of ~6 and sharpe of -0.1  
for short_stock in context.short_list.index:  
    if data.can_trade(short_stock):  
        order_target_percent(short_stock, -0.08)  
        context.shorts = context.shorts + 1



'''  
for stock in context.portfolio.positions.iterkeys():  
    if stock not in context.long_list.index and stock not in context.short_list.index:  
        order_target(stock, 0)  
'''  
for stock in context.portfolio.positions.iterkeys():  
    if stock not in context.long_list.index and stock not in context.short_list:  
        order_target(stock, 0)  

using optimise api:-
-----# Changed sell universe to context.portfolio.positions from context.my_securities

Barred leveraged ETF trading

Trading 30 minutes after market open to avoid morning volatility

60/40 Long/Short split

Using Optimize API to order optimal portfolio

""" Import necessary libraries and factors
"""

import numpy as np
from scipy import stats
from quantopian.pipeline import Pipeline
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import CustomFactor
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import AverageDollarVolume
from quantopian.pipeline.data import morningstar
from quantopian.pipeline.filters import Q1500US
import quantopian.optimize as opt
import quantopian.algorithm as algo

Constraint parameters

MAX_GROSS_LEVERAGE = 1.0 # Only use capital from our portfolio
MAX_SHORT_POSITION_SIZE = 0.1 # 15%
MAX_LONG_POSITION_SIZE = 0.1 # 15%

class MarketCap(CustomFactor):

# Pre-declare inputs and window_length  
inputs = [USEquityPricing.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 FCFRatio(CustomFactor):
# Pre-declare inputs and window_length
inputs = [morningstar.valuation_ratios.cf_yield]
window_length = 1
# Compute market cap value
def compute(self, today, assets, out, cf_yield):
out[:] = cf_yield[-1]

class PERatio(CustomFactor):
# Pre-declare inputs and window_length
inputs = [morningstar.valuation_ratios.pe_ratio]
window_length = 1
# Compute market cap value
def compute(self, today, assets, out, pe_ratio):
out[:] = pe_ratio[-1]

def initialize(context):

"""  
Initialize function schedules, benchmarks, commissions and slippage  
"""  

# Set benchmark security as S&P 500 (SPY)  
set_benchmark(sid(8554))  

# Exclude leveraged ETFs  
set_asset_restrictions(security_lists.restrict_leveraged_etfs)  

# Set commissions per share according to Interactive Brokers  
set_commission(commission.PerShare(cost = 0.005, min_trade_cost = 1.0))  

# Set default slippage  
set_slippage(slippage.VolumeShareSlippage(volume_limit = 0.025, price_impact = 0.10))  

# Schedule function to place orders once a day at market open  
schedule_function(rebalance,  
                  date_rules.every_day(),  
                  time_rules.market_open(minutes = 30))  


make_pipeline(context)

def make_pipeline(context):

"""  
Create list of securities to be considered for trading  
"""  

# top 500 by marketcap  
top_500 = morningstar.valuation.market_cap.latest.top(500)  
universe = Q1500US()  
mkt_cap = MarketCap()  
pe_ratio = PERatio()  
fcf_ratio = FCFRatio()  

pipe = Pipeline()  
attach_pipeline(pipe, 'pe_ratio')  

# add factors  
pipe.add(mkt_cap, 'mkt_cap')  
pipe.add(pe_ratio, "pe_ratio")  
pipe.add(fcf_ratio, "fcf_ratio")  
top_2000 = mkt_cap.top(2000)  
pipe.set_screen(top_2000)  



return pipe  

def before_trading_start(context, data):

"""  
Assign list to variable and attach pipeline  
"""  
context.output = pipeline_output('pe_ratio')  
my_list = context.output  
context.short_list = my_list.sort(["fcf_ratio"], ascending=True).iloc[:5]  
context.long_list = my_list.sort(["mkt_cap"], ascending=False).iloc[:5]  

# Pipeline_output returns the constructed dataframe  

output = pipeline_output('tradable_securities')

# Assign variable to output  

context.my_securities = output.index

def calculate_zscore(stock, data):

"""  
Calculate z-score for all stocks in list  
"""  

# Initialize dictionary  
zscore = {}  

# Get pricing data for the last month  
stock_price = data.history(stock, 'price', 30, '1d')  

# Calculate the returns from past prices  
stock_returns = stock_price.pct_change()[1:]  

# Calculate z-score of the current returns  
zscore[stock] = (np.mean(stock_returns[-5]) - np.mean(stock_returns)) / np.std(stock_returns, ddof = 2)  

return zscore

def compute_weights(context, data):

"""  
Calculate the ideal weights for the portfolio  
"""

weights = {}  
for long_stock in context.long_list.index:  
    weights[long_stock] = 0.1  
for short_stock in context.short_list.index:  
    weights[short_stock] = -0.08  



return weights  

def rebalance(context, data):

"""  
Rebalance portfolio using the Optimize API  
"""  

"""  
# Call required funcitons  
weights = compute_weights(context, data)  

# Optimize API variables  
optimize_weights = opt.TargetPortfolioWeights(weights)  
optimize_alpha = opt.MaximizeAlpha(context.zscore)  
leverage_constraint = opt.MaxGrossLeverage(MAX_GROSS_LEVERAGE)  

market_neutral = opt.DollarNeutral()  

algo.order_optimal_portfolio(  
    objective = [  
        optimize_weights,  
        optimize_alpha  
    ],  
    constraints = [  
        leverage_constraint,  
        market_neutral,  
    ],  
)  

"""  
# Call required funcitons  
weights = compute_weights(context, data)  

# Optimize API variables  
objective = opt.TargetWeights(weights)  
leverage_constraint = opt.MaxGrossLeverage(MAX_GROSS_LEVERAGE)  

position_size = opt.PositionConcentration.with_equal_bounds(  
    -MAX_SHORT_POSITION_SIZE,  
    MAX_LONG_POSITION_SIZE,  
)  

market_neutral = opt.DollarNeutral()  

algo.order_optimal_portfolio(  
    objective = objective,  
    constraints = [  
        leverage_constraint,  
        position_size,  
        market_neutral  
    ],  
)  

def plot(context, data):

"""  
Plot leverage, longs, and shorts throughout the course of the backtest  
"""  

longs = 0  
for position in context.portfolio.positions.itervalues():  
    if position.amount > 0:  
        longs += 1  

shorts = 0  
for position in context.portfolio.positions.itervalues():  
    if position.amount < 0:  
        shorts += 1;

record(leverage = context.account.leverage, shorts = shorts, longs = longs)  
5 responses

Hi Kamal, thanks for sharing! The community will have a much easier time helping out if you attach the different backtests so they can easily clone the algorithms in question.

all the best,
Josh

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.

I wish I could attach the entry as it was entered into the contest, but not able to do so.

this is the algorithm without the optimise api. note that when entry was put into contest, it had a backtest requirement for 1 yr. Both altos acquired all 3 badges.

well, why is nobody replying now that I have given the requested data?

thanks
-kamal

Reversing long, short, is as easy as a minus sign in front of the fundamentalsm. Result: +12% instead of -13%

fcf_zscore   = -vr.fcf_yield.latest.zscore(mask=universe)  
yield_zscore = -vr.earning_yield.latest.zscore(mask=universe)

I'd try some of the other fundamentals using https://www.quantopian.com/posts/universal-pipeline-experimentation-learning

Messing around with your code, switched to QTradable, Fundamentals, RiskModelExposure below.

I'd delete that code in the original message, impossible to read and might shy folks away from here.
Link for reference, similar code: https://www.quantopian.com/posts/quantopian-risk-model-in-algorithms