This page shows how simulation adapters can be added for a simple momentum strategy.
SigTech platform's flexible data adapter system allows data to be replaced with artificial data. This is useful for scenario analysis, running simulations and checking strategies for possible overfitting.
Construct a simple momentum signal of the Eurostoxx index and create a tradable version of the index to trade using the signal. The index is plotted in the following example.
Use obj.get, a generic object retrieving API that can be used on any platform object, such as individual instruments or strategies. This goes to a database, an object cache or builds the object on the fly.
A signal is defined in the example below by providing a new signal function, which retrieves the index data and evaluates the average return for the last 21 periods. The negative sign of this average is then used for a mean-reversion signal.
As the historic performance has been obtained, the performance that would be achieved if the data were modified is important. This can be achieved by introducing a simulation adapter, which can be dynamically added or removed from the data service. It obtained the raw data from the underlying data adapters and feed a modified version to the user. This allows various modifications to be performed.
A few examples include:
Moving a window of original data to a new time to be replayed. The replay can either be in absolute, difference or return space.
Perturbing the original data.
Substituting the original data with randomly generated data.
In the following example we obtain a simulation adapter, push it on to the data service and add a modifier. In this case the modifier acts on the Eurostoxx index data by shifted the original level by a normally distributed random quantity.
The strategy can be run multiple times using different perturbations, which are defined by a seed on the simulation adapter. The returns are all plotted below for 128 runs and the total return each time is recorded.
returns = []
for s in range(128):
sim_adapter.clear_simulation()
sig.config.configured_env().object.clear_object_data()
sim_adapter.seed = s
total_return = example_strategy_1.history().dropna()
returns.append((total_return.iloc[-1] / total_return.iloc[0]) - 1)
np.log(total_return).plot(color='r', alpha=0.01)
The distribution of returns:
pd.Series(returns).hist(bins=10);
As the current simulation adapter is no longer needed, remove it from the data service and clear the caches:
Create a simulation adapter and push it to the data service. In this example, add modifiers to move data from the past in to the future. As forward is now being included in the strategy, move the curves:
We can also hedge using FX Barrier options. The following example defines a strategy to do this:
classFXBarrierHedgingStrategy(sig.DailyStrategy):# Input Checking strategy_name =StringType(required=True) maturity =StringType(required=True) rebalance_frequency =StringType(required=True) group_name =StringType(required=True) bdc =StringType(default=sig.calendar.BDC_PRECEDING)def__init__(self):super(FXBarrierHedgingStrategy, self).__init__() self.option_group = sig.obj.get(self.group_name) self._strategy = sig.obj.get(self.strategy_name) self.over = self._strategy.currency self.under = self.currencydefschedule_information(self): """ set the holiday calendar for rebalancing - Here we use the the same holidays as the global trading manager """
returnStrategySchedule(holidays=sig.TradingManager.instance().trading_holidays)defget_rebalance_dates(self):""" helper function to get all the rebalance dates based on the rebalance schedule and market holidays """ first_date = self.calendar_schedule().next_data_date(self.start_date) all_dates = sig.SchedulePeriodic(self.start_date, self.calculation_end_date(), self.history_schedule().approximate_holidays(), frequency=self.rebalance_frequency, bdc=self.bdc).all_data_dates()[1:]return [first_date] + all_datesdefstrategy_initialization(self,dt):""" This is the first function to start the strategy building process. """ self.add_method(self.first_entry_date, self.push_strategy) rebalance_dates = self.get_rebalance_dates()for d in rebalance_dates: self.add_method(d, self.enter_barrier_option_positions)defpush_strategy(self,dt): self.add_position_target(dt, self.strategy_name, 1.0, unit_type='WEIGHT') self.add_method(self.valuation_dt(dt.date()), self.clean_up_foreign_cash)defclean_up_foreign_cash(self,dt):for cash_instrument, quantity in self.positions.iterate_cash_positions(dt):if cash_instrument.currency != self.currency: self.add_fx_spot_trade(dt, self.currency, cash_instrument.currency, -quantity)defenter_barrier_option_positions(self,dt):""" This process determines the barrier option parameters. """# first close out existing fx forwardsfor instrument, quantity in self.positions.iterate_instruments(dt):if instrument.is_option(): self.add_position_target(dt, instrument.name, 0.0, unit_type='WEIGHT') size_date = self.size_date_from_decision_dt(dt) notional_ccy = self.valuation_price(size_date, ccy=self.over) maturity_date = self.option_group.convert_maturity_tenor_to_date( size_date, self.maturity, target_maturity_weekday=3)# Use the spot price atm = sig.obj.get(self.option_group.underlying).history().asof(pd.to_datetime(size_date)) barrier_option = self.option_group.get_option('Put', atm, size_date, maturity_date, barrier_type='KI', barrier=atm *0.99) self.add_position_target(dt, barrier_option.name, notional_ccy)