def make_pipeline(): all_assets_filter = (USEquityPricing.close.latest > 0) returns_5 = Returns(window_length=5) rsi = RSI(inputs=[USEquityPricing.close]) macd = MovingAverageConvergenceDivergenceSignal(mask=all_assets_filter) ema = ExponentialWeightedMovingAverage(mask=all_assets_filter, inputs=[USEquityPricing.close], window_length=30, decay_rate=(1 - (2.0 / (1 + 15.0)))) mean_5 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=5, mask=all_assets_filter) mean_10 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=10, mask=all_assets_filter) bb = BollingerBands(inputs=[USEquityPricing.close], window_length=20, k=2) return Pipeline( columns={ 'returns_5': returns_5, 'RSI': rsi, 'MACD': macd, 'EMA': ema, 'SMA_5': mean_5, 'SMA_10': mean_10, 'bb_upper': bb.upper, 'bb_middle': bb.middle, 'bb_lower': bb.lower }, screen=all_assets_filter, )
def make_screener(context): """ Daily screener for securities to trade """ # Average volume for last 2 weeks. average_volume = SimpleMovingAverage(inputs=[USEquityPricing.volume], window_length=10) # SMA for last 2 weeks. sma_10 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=10) # ATR for last 2 weeks average_true_range = SimpleMovingAverage(inputs=[USEquityPricing.high], window_length=10) - SimpleMovingAverage(inputs=[USEquityPricing.low], window_length=10) long, short = TrendFactor() # Takin securities with price between [5, 50], average volume over million and ATR >= 0.5 return Pipeline( columns={ 'average_volume': average_volume, 'average_true_range': average_true_range, 'long': long, 'short': short }, screen=( (average_volume > minimum_daily_volume) & (sma_10 >= 5) & (sma_10 <= 50) & (average_true_range >= minimum_atr) ) )
def make_pipeline(): sentiment_score = SimpleMovingAverage(inputs=[stocktwits.bull_minus_bear], window_length=3, mask=QTradableStocksUS()) return Pipeline(columns={ 'sentiment_score': sentiment_score, }, screen=sentiment_score.notnull())
def test_downsample_windowed_factor(self): self.check_downsampled_term( SimpleMovingAverage( inputs=[TestingDataSet.float_col], window_length=5, ) )
def mean_reversion_5day_sector_neutral_smoothed(window_length, universe, sector): """ Generate the mean reversion 5 day sector neutral smoothed factor Parameters ---------- window_length : int Returns window length universe : Zipline Filter Universe of stocks filter sector : Zipline Classifier Sector classifier Returns ------- factor : Zipline Factor Mean reversion 5 day sector neutral smoothed factor """ #TODO: Implement function return SimpleMovingAverage(inputs=[ mean_reversion_5day_sector_neutral(window_length, universe, sector) ], window_length=window_length).rank().zscore()
def mean_reversion_5day_sector_neutral_smoothed(window_length, universe, sector): """ Generate the mean reversion 5 day sector neutral smoothed factor Paramètres ---------- window_length : int Retourne la longueur de la fenêtre universe : Zipline Filter Univers des filtres des stocks sector : Zipline Classifier Classifieur de secteurs Returns ------- factor : Zipline Factor Facteur de réversion moyenne sur 5 jours de secteur neutre(lissé) """ unsmoothed_factor = mean_reversion_5day_sector_neutral( window_length, universe, sector) factor = SimpleMovingAverage(inputs=[unsmoothed_factor], window_length=window_length)\ .rank()\ .zscore() return factor
def test_downsample_non_windowed_factor(self): sma = SimpleMovingAverage( inputs=[TestingDataSet.float_col], window_length=5, ) self.check_downsampled_term(((sma + sma) / 2).rank())
def mean_reversion_5day_sector_neutral_smoothed(window_length, universe, sector): unsmoothed_factor = -Returns( window_length=window_length, mask=universe).demean(groupby=sector).rank().zscore() return SimpleMovingAverage(inputs=[unsmoothed_factor], window_length=window_length).rank().zscore()
def overnight_sentiment_smoothed(cto_window_length, trail_overnight_returns_window_length, universe): unsmoothed_factor = overnight_sentiment( cto_window_length, trail_overnight_returns_window_length, universe) return SimpleMovingAverage(inputs=[unsmoothed_factor], window_length=trail_overnight_returns_window_length) \ .rank() \ .zscore()
def initialize(context): # Create, register and name a pipeline in initialize. pipe = Pipeline() context.attach_pipeline(pipe, 'AAPL') # Construct a simple moving average factor and add it to the pipeline. USEquityPricing需要本地自定义 if True: sma_short = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=10) else: #mid added data = Column(float64) dataset = DataSet() close = data.bind(dataset, 'aapl') sma_short = SimpleMovingAverage(inputs=[close], window_length=10) pipe.add(sma_short, 'sma_short')
def mean_reversion_sector_neutral_smoothed(window_length, universe, sector): """ Smoothed version of mean_reversion_5day_sector_neutral. window_lenghth is used in returns and smoothing computations """ unsmoothed_factor = mean_reversion_sector_neutral(window_length, universe, sector) return SimpleMovingAverage(inputs=[unsmoothed_factor], window_length=window_length) \ .rank() \ .zscore()
def overnight_sentiment_smoothed(cto_window_length, trail_overnight_returns_window_length, universe): cto_out = CTO(mask=universe, window_length=cto_window_length) unsmoothed_factor = TrailingOvernightReturns( inputs=[cto_out], window_length=trail_overnight_returns_window_length).rank().zscore() return SimpleMovingAverage( inputs=[unsmoothed_factor], window_length=trail_overnight_returns_window_length).rank().zscore()
def momentum_smoothed(window_length, smooth_window_length, universe, sector): """ Smoothed version of momentum. window_lenghth is used in returns and smoothing computations Parameters ---------- smooth_window_length : int smoothing factor to applie to SimpleMovingAverage """ unsmoothed_factor = momentum(window_length, universe, sector) return SimpleMovingAverage(inputs=[unsmoothed_factor], window_length=smooth_window_length) \ .rank() \ .zscore()
def test_SMA(self): engine = SimplePipelineEngine( lambda column: self.pipeline_loader, self.env.trading_days, self.finder, ) window_length = 5 assets = self.all_assets dates = date_range( self.first_asset_start + self.trading_day, self.last_asset_end, freq=self.trading_day, ) dates_to_test = dates[window_length:] SMA = SimpleMovingAverage( inputs=(USEquityPricing.close, ), window_length=window_length, ) results = engine.run_pipeline( Pipeline(columns={'sma': SMA}), dates_to_test[0], dates_to_test[-1], ) # Shift back the raw inputs by a trading day because we expect our # computed results to be computed using values anchored on the # **previous** day's data. expected_raw = rolling_mean( self.writer.expected_values_2d( dates - self.trading_day, assets, 'close', ), window_length, min_periods=1, ) expected = DataFrame( # Truncate off the extra rows needed to compute the SMAs. expected_raw[window_length:], index=dates_to_test, # dates_to_test is dates[window_length:] columns=self.finder.retrieve_all(assets), ) self.write_nans(expected) result = results['sma'].unstack() assert_frame_equal(result, expected)
def get_moving_average(self, start_date, end_date, assets=None, window_length=63): moving_average = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=window_length) pipeline = Pipeline(columns={'moving_average': moving_average}) if assets is not None: pipeline.set_screen(StaticAssets(assets)) df = self._run_pipeline(pipeline, start_date, end_date) return df
def make_pipeline(): """ Create a pipeline with the following rules: screen - common stocks only - must be liquid (top 10% by dollar volume) - must be above 20-day moving average - must not be too cheap or too expensive columns - 20-day moving average - prior low - standard deviation of closing prices """ mavg = SimpleMovingAverage(window_length=20, inputs=[EquityPricing.close]) are_common_stocks = SecuritiesMaster.usstock_SecurityType2.latest.eq( "Common Stock") are_liquid = AverageDollarVolume(window_length=30).percentile_between( 90, 100) are_above_mavg = EquityPricing.close.latest > mavg are_not_too_cheap = EquityPricing.close.latest > 10 are_not_too_expensive = EquityPricing.close.latest < 2000 pipeline = Pipeline(columns={ "mavg": mavg, "prior_low": EquityPricing.low.latest, "std": ExponentialWeightedMovingStdDev(inputs=[EquityPricing.close], window_length=63, decay_rate=0.99) }, screen=(are_common_stocks & are_liquid & are_above_mavg & are_not_too_cheap & are_not_too_expensive)) return pipeline
def factor_pipe(context): ''' function to set up a pipeline to retrieve all active syms. We can add filters here as well. ''' pipe = Pipeline() sma_20 = SimpleMovingAverage(inputs=[EquityPricing.close], window_length=20) # Pick the top 50% of stocks ranked by dollar volume dollar_volume = AvgDailyDollarVolumeTraded(window_length=252) high_dollar_volume = dollar_volume.percentile_between(50, 100) # Remove penny stocks no_penny_stocks = sma_20 > 1 filtered_assets = high_dollar_volume & no_penny_stocks pipe.set_screen(filtered_assets) pipe.add(sma_20, 'sma_20') for m in context.agent.models: pipe.add(m.factor(inputs=[EquityPricing.close], window_length=252), m.name) return pipe
def test_window_safety_of_slices(self): """ Test that slices correctly inherit the `window_safe` property of the term from which they are derived. """ col = self.col my_asset = self.asset_finder.retrieve_asset(self.sids[0]) # SimpleMovingAverage is not window safe. sma = SimpleMovingAverage(inputs=[self.col], window_length=10) sma_slice = sma[my_asset] class UsesSlicedInput(CustomFactor): window_length = 2 inputs = [sma_slice] def compute(self, today, assets, out, sma_slice): pass with self.assertRaises(NonWindowSafeInput): self.run_pipeline( Pipeline(columns={'uses_sliced_input': UsesSlicedInput()}), self.pipeline_start_date, self.pipeline_end_date, ) # Make sure that slices of custom factors are not window safe. class MyUnsafeFactor(CustomFactor): window_length = 2 inputs = [col] def compute(self, today, assets, out, col): pass my_unsafe_factor = MyUnsafeFactor() my_unsafe_factor_slice = my_unsafe_factor[my_asset] class UsesSlicedInput(CustomFactor): window_length = 2 inputs = [my_unsafe_factor_slice] def compute(self, today, assets, out, my_unsafe_factor_slice): pass with self.assertRaises(NonWindowSafeInput): self.run_pipeline( Pipeline(columns={'uses_sliced_input': UsesSlicedInput()}), self.pipeline_start_date, self.pipeline_end_date, ) # Create a window safe factor. class MySafeFactor(CustomFactor): window_length = 2 inputs = [col] window_safe = True def compute(self, today, assets, out, col): pass my_safe_factor = MySafeFactor() my_safe_factor_slice = my_safe_factor[my_asset] # Make sure that correlations are not safe if either the factor *or* # the target slice are not window safe. with self.assertRaises(NonWindowSafeInput): my_unsafe_factor.pearsonr( target=my_safe_factor_slice, correlation_length=10, ) with self.assertRaises(NonWindowSafeInput): my_safe_factor.pearsonr( target=my_unsafe_factor_slice, correlation_length=10, )
def test_downsample_nonwindowed_classifier(self): sma = SimpleMovingAverage( inputs=[TestingDataSet.float_col], window_length=5, ) self.check_downsampled_term(sma.quantiles(5))
def test_downsample_nonwindowed_filter(self): sma = SimpleMovingAverage( inputs=[TestingDataSet.float_col], window_length=5, ) self.check_downsampled_term(sma > 5)
def test_downsample_windowed_filter(self): sma = SimpleMovingAverage( inputs=[TestingDataSet.float_col], window_length=5, ) self.check_downsampled_term(All(inputs=[sma.top(4)], window_length=5))
# # Now that you know how build a pipeline and execute it, in this section we will see how we can add factors and filters to our pipeline. These factors and filters will determine the computations we want our pipeline to compute each day. # # We can add both factors and filters to our pipeline using the `.add(column, name)` method of the `Pipeline` class. The `column` parameter represetns the factor or filter to add to the pipeline. The `name` parameter is a string that determines the name of the column in the output Pandas Dataframe for that factor of fitler. As mentioned earlier, each factor and filter will appear as a column in the output dataframe of our pipeline. Let's start by adding a factor to our pipeline. # # ### Factors # # In the code below, we will use Zipline's built-in `SimpleMovingAverage` factor to create a factor that computes the 15-day mean closing price of securities. We will then add this factor to our pipeline and use `.show_graph()` to see a diagram of our pipeline with the factor added. # In[ ]: from zipline.pipeline.factors import SimpleMovingAverage # Create a factor that computes the 15-day mean closing price of securities mean_close_15 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=15) # Add the factor to our pipeline pipeline.add(mean_close_15, "15 Day MCP") # Render the pipeline as a DAG pipeline.show_graph() # In the diagram above we can clearly see the factor we have added. Now, we can run our pipeline again and see its output. The pipeline is run in exactly the same way we did before. # In[ ]: # Set starting and end dates start_date = pd.Timestamp("2014-01-06", tz="utc")
def test_compute_with_adjustments(self): dates, assets = self.dates, self.assets low, high = USEquityPricing.low, USEquityPricing.high apply_idxs = [3, 10, 16] def apply_date(idx, offset=0): return dates[apply_idxs[idx] + offset] adjustments = DataFrame.from_records([ dict( kind=MULTIPLY, sid=assets[1], value=2.0, start_date=None, end_date=apply_date(0, offset=-1), apply_date=apply_date(0), ), dict( kind=MULTIPLY, sid=assets[1], value=3.0, start_date=None, end_date=apply_date(1, offset=-1), apply_date=apply_date(1), ), dict( kind=MULTIPLY, sid=assets[1], value=5.0, start_date=None, end_date=apply_date(2, offset=-1), apply_date=apply_date(2), ), ]) low_base = DataFrame(self.make_frame(30.0)) low_loader = DataFrameLoader(low, low_base.copy(), adjustments=None) # Pre-apply inverse of adjustments to the baseline. high_base = DataFrame(self.make_frame(30.0)) high_base.iloc[:apply_idxs[0], 1] /= 2.0 high_base.iloc[:apply_idxs[1], 1] /= 3.0 high_base.iloc[:apply_idxs[2], 1] /= 5.0 high_loader = DataFrameLoader(high, high_base, adjustments) engine = SimplePipelineEngine( { low: low_loader, high: high_loader }.__getitem__, self.dates, self.asset_finder, ) for window_length in range(1, 4): low_mavg = SimpleMovingAverage( inputs=[USEquityPricing.low], window_length=window_length, ) high_mavg = SimpleMovingAverage( inputs=[USEquityPricing.high], window_length=window_length, ) bounds = product_upper_triangle(range(window_length, len(dates))) for start, stop in bounds: results = engine.run_pipeline( Pipeline(columns={ 'low': low_mavg, 'high': high_mavg }), dates[start], dates[stop], ) self.assertEqual(set(results.columns), {'low', 'high'}) iloc_bounds = slice(start, stop + 1) # +1 to include end date low_results = results.unstack()['low'] assert_frame_equal(low_results, low_base.iloc[iloc_bounds]) high_results = results.unstack()['high'] assert_frame_equal(high_results, high_base.iloc[iloc_bounds])
# In[ ]: from zipline.pipeline.factors import Returns from zipline.pipeline.factors import SimpleMovingAverage # create a pipeline called p p = Pipeline(screen=universe) # create a factor of one year returns, deman by sector, then rank factor = ( Returns(window_length=252, mask=universe).demean(groupby=Sector( )). #we use the custom Sector class that we reviewed earlier rank().zscore()) # Use this factor as input into SimpleMovingAverage, with a window length of 5 # Also rank and zscore (don't need to de-mean by sector, s) factor_smoothed = (SimpleMovingAverage(inputs=[factor], window_length=5).rank().zscore()) # add the unsmoothed factor to the pipeline p.add(factor, 'Momentum_Factor') # add the smoothed factor to the pipeline too p.add(factor_smoothed, 'Smoothed_Momentum_Factor') # ## visualize the pipeline # # Note that if the image is difficult to read in the notebook, right-click and view the image in a separate tab. # In[ ]: p.show_graph(format='png') # ## run pipeline and view the factor data
def run_data_pipeline(engine, universe, start_date, end_date): pipeline = Pipeline(screen=universe) sector = Sector() # Alpha Factors : pipeline.add(DownsideRisk(), 'Downside Risk (Sortino Ratio)') pipeline.add(Vol3M(), '3 Month Volatility') pipeline.add(momentum_1yr(252, universe, sector), 'Momentum_1YR') pipeline.add( mean_reversion_5day_sector_neutral_smoothed(20, universe, sector), 'Mean_Reversion_Sector_Neutral_Smoothed') pipeline.add(overnight_sentiment_smoothed(2, 10, universe), 'Overnight_Sentiment_Smoothed') pipeline.add(rsi_sector_neutral(15, universe, sector), 'RSI_Sector_Neutral_15d') pipeline.add(rsi_sector_neutral(30, universe, sector), 'RSI_Sector_Neutral_30d') beta_factor = (RegressionAgainstTime(mask=universe).beta.rank().zscore()) gamma_factor = (RegressionAgainstTime(mask=universe).gamma.rank().zscore()) conditional_factor = (beta_factor * gamma_factor).rank().zscore() pipeline.add(beta_factor, 'time_beta') pipeline.add(gamma_factor, 'time_gamma') pipeline.add(conditional_factor, 'conditional_factor') # Universal Quant Features : pipeline.add( AnnualizedVolatility(window_length=20, mask=universe).rank().zscore(), 'volatility_20d') pipeline.add( AnnualizedVolatility(window_length=120, mask=universe).rank().zscore(), 'volatility_120d') pipeline.add( AverageDollarVolume(window_length=20, mask=universe).rank().zscore(), 'adv_20d') pipeline.add( AverageDollarVolume(window_length=120, mask=universe).rank().zscore(), 'adv_120d') pipeline.add(sector, 'sector_code') # Regime Features : pipeline.add( SimpleMovingAverage(inputs=[MarketDispersion(mask=universe)], window_length=20), 'dispersion_20d') pipeline.add( SimpleMovingAverage(inputs=[MarketDispersion(mask=universe)], window_length=120), 'dispersion_120d') pipeline.add(MarketVolatility(window_length=20), 'market_vol_20d') pipeline.add(MarketVolatility(window_length=120), 'market_vol_120d') # Target # Let's try to predict the go forward 1-week return. When doing this, it's important to quantize the target. The factor we create is the trailing 5-day return pipeline.add( Returns(window_length=5, mask=universe).quantiles(2), 'return_5d') pipeline.add( Returns(window_length=5, mask=universe).quantiles(25), 'return_5d_p') # Running the Pipeline all_factors = engine.run_pipeline(pipeline, start_date, end_date) # Computing Date Features all_factors = compute_date_features(all_factors, start_date, end_date) # One Hot Encoding Sectors all_factors = one_hot_encode_sectors(all_factors) # Shifted Target For Training The Model all_factors['target'] = all_factors.groupby(level=1)['return_5d'].shift(-5) return all_factors
# In[64]: class MarketDispersion(CustomFactor): inputs = [DailyReturns()] window_length = 1 window_safe = True def compute(self, today, assets, out, returns): # returns are days in rows, assets across columns out[:] = np.sqrt(np.nanmean((returns - np.nanmean(returns))**2)) pipeline.add( SimpleMovingAverage(inputs=[MarketDispersion(mask=universe)], window_length=20), 'dispersion_20d') pipeline.add( SimpleMovingAverage(inputs=[MarketDispersion(mask=universe)], window_length=120), 'dispersion_120d') # In[67]: class MarketVolatility(CustomFactor): inputs = [DailyReturns()] window_length = 1 window_safe = True def compute(self, today, assets, out, returns): mkt_returns = np.nanmean(returns, axis=1) out[:] = np.sqrt(260. * np.nanmean(