Hello,

In the interest of getting up to speed with the new pipeline API I've attempted to implement my own version of the Piotroski score which I've attached. I've wrapped the CustomFactor around some code that rebalances yearly, and I believe the code to get the pipeline output 'output = pipeline_output('piotroski')' is only being called once. However, based on the logging statements in the compute method of the custom factor I can see that compute is being called many times, which I believe is causing a significant slowdown when executing the algorithm. Could someone please assist with this. Is there a way to speed this algorithm up?

Regards,

Mark

Clone Algorithm

20

Loading...

There was an error loading this backtest.

Backtest from
to
with
initial capital

Cumulative performance:

Algorithm
Benchmark

Custom data:

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.data import morningstar from quantopian.pipeline import CustomFactor import numpy as np import pandas as pd class Piotroski(CustomFactor): inputs = [morningstar.operation_ratios.roa, morningstar.cash_flow_statement.operating_cash_flow, morningstar.operation_ratios.long_term_debt_equity_ratio, morningstar.operation_ratios.gross_margin, morningstar.operation_ratios.current_ratio, morningstar.valuation.shares_outstanding, morningstar.operation_ratios.assets_turnover, morningstar.income_statement.net_income] window_length = 200 #context.days_in_year def compute(self, today, assets, out, roa, ocf, lt_debt_equity, gross_margin, current_ratio, shares_outstanding, assets_turnover,net_income): log.info("in compute") p_score = [0] * len(out) if self.window_length > 1: #**************************************** #Profitability #**************************************** #1 net income temp = np.where(net_income[0] > 0.0,1,0) p_score = p_score + temp #2 operating cash flow temp = np.where(ocf[0] > 0.0,1,0) p_score = p_score + temp #3 return on assets temp = roa[0] - roa[-1] temp = np.where(temp>0,1,0) p_score = p_score + temp #4 quality of earnings temp = ocf[0] - net_income[0] temp = np.where(temp>0,1,0) p_score = p_score + temp #**************************************** #Leverage Liquidity Source of Funds #**************************************** #5 decrease in leverage temp = lt_debt_equity[-1] - lt_debt_equity[0] temp = np.where(temp>0,1,0) p_score = p_score + temp #6 increase in liquidity temp = current_ratio[0] - current_ratio[-1] temp = np.where(temp>0,1,0) p_score = p_score + temp #7 absence of dilution temp = lt_debt_equity[-1] - lt_debt_equity[0] temp = np.where(temp>0,1,0) p_score = p_score + temp #**************************************** #Operating Efficiency #**************************************** #8 gross margin temp = gross_margin[0] - gross_margin[-1] temp = np.where(temp>0,1,0) p_score = p_score + temp #9 asset turnover temp = assets_turnover[0] - assets_turnover[-1] temp = np.where(temp>0,1,0) p_score = p_score + temp out[:] = p_score def initialize(context): context.days_in_year = 200 context.lower_mktcap = 500e6 context.limit = 500 context.shy = symbol('IEF') context.num_positions = 30 context.base_weight = 1.0/context.num_positions context.rebalance_month = 6 #June context.is_rebalance_month = False context.rebalance_day = 0 # Create and attach an empty Pipeline. pipe = Pipeline() pipe = attach_pipeline(pipe, name='piotroski') # Construct Factors. p_score = Piotroski() pipe.add(p_score,'p_score') pipe.set_screen(p_score > 5) #schedule rebalance day and sell and buy dates #separate sell and buy dates by 1 week to allow sell orders to clear before executing buys schedule_function(set_rebalance_day,date_rules.month_start(days_offset=10), time_rules.market_close(minutes=5)) schedule_function(rebalance_sell,date_rules.month_start(days_offset=11), time_rules.market_open(minutes=120)) schedule_function(rebalance_buy,date_rules.month_start(days_offset=16), time_rules.market_open(minutes=120)) def rebalance_sell(context,data): if context.is_rebalance_month : for s in context.portfolio.positions: order_target_percent(s,0) def rebalance_buy(context,data): if context.is_rebalance_month : for s in context.longs: if s in data: order_target_percent(s,context.base_weight) context.is_rebalance_month = False def set_rebalance_day(context,data): today = get_datetime() if today.month == context.rebalance_month: context.is_rebalance_month = True context.rebalance_day = today.day else: context.is_rebalance_month = False def before_trading_start(context, data): today = get_datetime() if context.is_rebalance_month and today.day-1 == context.rebalance_day: #get stocks using traditional sql query getstocks(context) # Pipeline_output returns the constructed dataframe. output = pipeline_output('piotroski') context.longs=[] context.p_score_stocks = output.sort('p_score', ascending=False) #check if selected stocks in p_score list for s in context.stocks: if s in context.p_score_stocks.index: context.longs.append(s) #context.longs = context.my_universe context.longs = context.longs[:context.num_positions] #check all stocks in list log.info("number of longs: " + str(len(context.longs))) update_universe(context.longs) def getstocks(context): sector_code = fundamentals.asset_classification.morningstar_sector_code fundamental_df = get_fundamentals( query( sector_code, fundamentals.valuation.market_cap, fundamentals.balance_sheet.total_assets, ) .filter(fundamentals.share_class_reference.is_primary_share == True) .filter(fundamentals.company_reference.primary_exchange_id != "OTCPK") .filter(fundamentals.share_class_reference.is_depositary_receipt == False) .filter(fundamentals.valuation.market_cap > context.lower_mktcap ) .filter(fundamentals.valuation_ratios.ev_to_ebitda > 0) .order_by(fundamentals.valuation.market_cap.asc()) .limit(context.limit) ) context.stocks = fundamental_df.columns[:context.num_positions] def handle_data(context, data): record(leverage = context.account.leverage)