def updateParameters(self, zipline_data: BarData): """Update ETF parameters; specifically, the asset allocations weights, the log return (over the configuration lookback window), the variance, and the synthetic ETF prices over the lookback window. Arguments: zipline_data {BarData} -- Instance zipline data bundle. """ # Get historical price data for lookback window from config historical_data = zipline_data.history( symbols(*self.tickers), 'price', bar_count=config.setf_lookback_window, frequency=config.setf_data_frequency) # Filling na values historical_data = historical_data.fillna(method='bfill') historical_data = historical_data.fillna(method='ffill') # Computing prices, restructuring per the period in the configuration setf_prices = np.array([]) first_run = True # Flag for first run for idx, row in historical_data.iterrows(): if (self.backtest_util.isRestructureTriggered(current_date=idx, log_flag=False) or first_run): # Recompute allocation weights alloc_weights = np.array(row / row.sum()) # Update first run flag first_run = False # Computing synthetic ETF price setf_price = np.dot(alloc_weights, row.values) # Appending to prices array setf_prices = np.append(setf_prices, setf_price) if (np.count_nonzero(np.isnan(setf_prices)) > 0): logging.error('NA values detected in Synthetic ETF prices') raise Exception # Computing ETF log returns self.log_rets = np.diff(np.log(setf_prices)) # Computing single-period ETF log return (sum of log returns) self.period_log_ret = np.sum(self.log_rets) # Computing ETF variance self.variance = np.var(self.log_rets) # Casting synthetic ETF prices to DataFrame with original index, binding self.setf_prices = pd.DataFrame(setf_prices, index=historical_data.index)
def test_old_new_data_api_paths(self): """ Test that the new and old data APIs hit the same code paths. We want to ensure that the old data API(data[sid(N)].field and similar) and the new data API(data.current(sid(N), field) and similar) hit the same code paths on the DataPortal. """ test_start_minute = self.env.market_minutes_for_day( self.sim_params.trading_days[0] )[1] test_end_minute = self.env.market_minutes_for_day( self.sim_params.trading_days[0] )[-1] bar_data = BarData( self.data_portal, lambda: test_end_minute, "minute" ) ohlcvp_fields = [ "open", "high", "low" "close", "volume", "price", ] spot_value_meth = 'zipline.data.data_portal.DataPortal.get_spot_value' def assert_get_spot_value_called(fun, field): """ Assert that get_spot_value was called during the execution of fun. Takes in a function fun and a string field. """ with patch(spot_value_meth) as gsv: fun() gsv.assert_called_with( self.asset1, field, test_end_minute, 'minute' ) # Ensure that data.current(sid(n), field) has the same behaviour as # data[sid(n)].field. for field in ohlcvp_fields: assert_get_spot_value_called( lambda: getattr(bar_data[self.asset1], field), field, ) assert_get_spot_value_called( lambda: bar_data.current(self.asset1, field), field, ) history_meth = 'zipline.data.data_portal.DataPortal.get_history_window' def assert_get_history_window_called(fun, is_legacy): """ Assert that get_history_window was called during fun(). Takes in a function fun and a boolean is_legacy. """ with patch(history_meth) as ghw: fun() # Slightly hacky, but done to get around the fact that # history( explicitly passes an ffill param as the last arg, # while data.history doesn't. if is_legacy: ghw.assert_called_with( [self.asset1, self.asset2, self.asset3], test_end_minute, 5, "1m", "volume", True ) else: ghw.assert_called_with( [self.asset1, self.asset2, self.asset3], test_end_minute, 5, "1m", "volume", ) test_sim_params = SimulationParameters( period_start=test_start_minute, period_end=test_end_minute, data_frequency="minute", env=self.env ) history_algorithm = self.create_algo( history_algo, sim_params=test_sim_params ) assert_get_history_window_called( lambda: history_algorithm.run(self.data_portal), is_legacy=True ) assert_get_history_window_called( lambda: bar_data.history( [self.asset1, self.asset2, self.asset3], "volume", 5, "1m" ), is_legacy=False )