def _get_forecast_scalar_fixed(self, instrument_code, rule_variation_name): """ Get the scalar to apply to raw forecasts In this simple version it's the same for all instruments, and fixed We get the scalars from: (a) configuration file in parent system (b) or if missing: uses the scalar from systems.defaults.py :param instrument_code: :type str: :param rule_variation_name: :type str: name of the trading rule variation :returns: float >>> from systems.tests.testdata import get_test_object_futures_with_rules >>> from systems.basesystem import System >>> (rules, rawdata, data, config)=get_test_object_futures_with_rules() >>> system1=System([rawdata, rules, ForecastScaleCapFixed()], data, config) >>> >>> ## From config >>> system1.forecastScaleCap.get_forecast_scalar("EDOLLAR", "ewmac8") 5.3 >>> >>> ## default >>> unused=config.trading_rules['ewmac8'].pop('forecast_scalar') >>> system3=System([rawdata, rules, ForecastScaleCapFixed()], data, config) >>> system3.forecastScaleCap.get_forecast_scalar("EDOLLAR", "ewmac8") 1.0 >>> >>> ## other config location >>> setattr(config, 'forecast_scalars', dict(ewmac8=11.0)) >>> system4=System([rawdata, rules, ForecastScaleCapFixed()], data, config) >>> system4.forecastScaleCap.get_forecast_scalar("EDOLLAR", "ewmac8") 11.0 """ system = self.parent try: scalar = system.config.trading_rules[rule_variation_name][ 'forecast_scalar'] except: try: # can also put somewhere else ... scalar = system.config.forecast_scalars[rule_variation_name] except: # go with defaults scalar = get_default_config_key_value('forecast_scalar') return scalar
def get_private_then_default_key_value(key_name, system_defaults_dict=arg_not_supplied, private_config_dict=arg_not_supplied, raise_error=True): key_value = get_private_config_key_value( key_name, private_config_dict=private_config_dict) if key_value is missing_data: key_value = get_default_config_key_value( key_name, system_defaults_dict=system_defaults_dict) if key_value is missing_data and raise_error: raise KeyError( "Can't find key %s in private %s or default %s config .yaml files" % (key_value, PRIVATE_CONFIG_FILE, DEFAULT_FILENAME)) return key_value
def forecast_scalar(cs_forecasts, window=250000, min_periods=500, backfill=True): """ Work out the scaling factor for xcross such that T*x has an abs value of 10 (or whatever the average absolute forecast is) :param cs_forecasts: forecasts, cross sectionally :type cs_forecasts: pd.DataFrame TxN :param span: :type span: int :param min_periods: :returns: pd.DataFrame """ backfill = str2Bool(backfill) # in yaml will come in as text # We don't allow this to be changed in config target_abs_forecast = get_default_config_key_value( "average_absolute_forecast") if target_abs_forecast is missing_data: raise Exception( "average_absolute_forecast not defined in system defaults file") # Remove zeros/nans copy_cs_forecasts = copy(cs_forecasts) copy_cs_forecasts[copy_cs_forecasts == 0.0] = np.nan # Take CS average first # we do this before we get the final TS average otherwise get jumps in # scalar when new markets introduced if copy_cs_forecasts.shape[1] == 1: x = copy_cs_forecasts.abs().iloc[:, 0] else: x = copy_cs_forecasts.ffill().abs().median(axis=1) # now the TS avg_abs_value = x.rolling(window=window, min_periods=min_periods).mean() scaling_factor = target_abs_forecast / avg_abs_value if backfill: scaling_factor = scaling_factor.fillna(method="bfill") return scaling_factor
def get_subsystem_position(self, instrument_code): """ Get scaled position (assuming for now we trade our entire capital for one instrument) KEY OUTPUT :param instrument_code: instrument to get values for :type instrument_code: str :returns: Tx1 pd.DataFrame >>> from systems.tests.testdata import get_test_object_futures_with_comb_forecasts >>> from systems.basesystem import System >>> (comb, fcs, rules, rawdata, data, config)=get_test_object_futures_with_comb_forecasts() >>> system=System([rawdata, rules, fcs, comb, PositionSizing()], data, config) >>> >>> system.positionSize.get_subsystem_position("EDOLLAR").tail(2) ss_position 2015-12-10 1.811465 2015-12-11 2.544598 >>> >>> system2=System([rawdata, rules, fcs, comb, PositionSizing()], data, config) >>> system2.positionSize.get_subsystem_position("EDOLLAR").tail(2) ss_position 2015-12-10 1.811465 2015-12-11 2.544598 """ self.log.msg( "Calculating subsystem position for %s" % instrument_code, instrument_code=instrument_code, ) """ We don't allow this to be changed in config """ avg_abs_forecast = get_default_config_key_value( "average_absolute_forecast") vol_scalar = self.get_volatility_scalar(instrument_code) forecast = self.get_combined_forecast(instrument_code) vol_scalar = vol_scalar.reindex(forecast.index).ffill() subsystem_position = vol_scalar * forecast / avg_abs_forecast return subsystem_position
def forecast_turnover_for_list(self, instrument_code_list, rule_variation_name): """ Get the average turnover for a rule, over instrument_code_list :param instrument_code_list: instruments to get values for :type instrument_code_list: list of str :param rule_variation_name: rule to get values for :type rule_variation_name: str :returns: float """ average_forecast_for_turnover = get_default_config_key_value( 'average_absolute_forecast') forecast_list = [ self.get_capped_forecast(instrument_code, rule_variation_name) for instrument_code in instrument_code_list ] turnovers = [ turnover(forecast, average_forecast_for_turnover) for forecast in forecast_list ] if len(instrument_code_list) == 1: return turnovers[0] # weight by length forecast_lengths = [len(forecast.index) for forecast in forecast_list] total_length = sum(forecast_lengths) weighted_turnovers = [ tover * fc_length / total_length for (tover, fc_length) in zip(turnovers, forecast_lengths) ] avg_turnover = sum(weighted_turnovers) return avg_turnover
def get_forecast_diversification_multiplier_fixed(self, instrument_code): """ Get the diversification multiplier for this instrument From: system.config.instrument_weights :param instrument_code: instrument to get multiplier for :type instrument_code: str :returns: Tx1 pd.DataFrame >>> from systems.tests.testdata import get_test_object_futures_with_rules_and_capping >>> from systems.basesystem import System >>> (fcs, rules, rawdata, data, config)=get_test_object_futures_with_rules_and_capping() >>> system=System([rawdata, rules, fcs, ForecastCombineFixed()], data, config) >>> >>> ## from config >>> system.combForecast.get_forecast_diversification_multiplier("EDOLLAR").tail(2) fdm 2015-12-10 1.1 2015-12-11 1.1 >>> >>> config.forecast_div_multiplier=dict(EDOLLAR=2.0) >>> system2=System([rawdata, rules, fcs, ForecastCombineFixed()], data, config) >>> system2.combForecast.get_forecast_diversification_multiplier("EDOLLAR").tail(2) fdm 2015-12-10 2 2015-12-11 2 >>> >>> ## defaults >>> del(config.forecast_div_multiplier) >>> system3=System([rawdata, rules, fcs, ForecastCombineFixed()], data, config) >>> system3.combForecast.get_forecast_diversification_multiplier("EDOLLAR").tail(2) fdm 2015-12-10 1 2015-12-11 1 """ system = self.parent self.log.msg("Calculating diversification multiplier for %s" % (instrument_code), instrument_code=instrument_code) # Let's try the config if hasattr(system.config, "forecast_div_multiplier"): if isinstance(system.config.forecast_div_multiplier, float): fixed_div_mult = system.config.forecast_div_multiplier elif instrument_code in system.config.forecast_div_multiplier.keys( ): # dict fixed_div_mult = system.config.forecast_div_multiplier[ instrument_code] else: error_msg = "FDM in config needs to be either float, or dict with instrument_code keys" self.log.critical(error_msg, instrument_code=instrument_code) else: # try defaults fixed_div_mult = get_default_config_key_value( "forecast_div_multiplier") if fixed_div_mult is missing_data: error_msg = "Need to specify FDM in config or system_defaults" self.log.critical(error_msg, instrument_code=instrument_code) # Now we have a dict, fixed_weights. # Need to turn into a timeseries covering the range of forecast dates # get forecast weights first forecast_weights = self.get_forecast_weights(instrument_code) weight_ts = forecast_weights.index ts_fdm = pd.Series([fixed_div_mult] * len(weight_ts), index=weight_ts) return ts_fdm