def __init__(self, param_dict): super(ValParamMovingAverage, self).__init__(param_dict) self.type = 'moving_average' if 'window_size' in param_dict.keys(): self.window_size = param_dict['window_size'] else: error('window_size needs to be given when method = moving_average') if 'tolerance_up' in param_dict.keys(): self.tolerance_up = param_dict['tolerance_up'] else: self.tolerance_up = 100 if 'tolerance_down' in param_dict.keys(): self.tolerance_down = param_dict['tolerance_down'] else: self.tolerance_down = 1 if 'calculation_period' in param_dict.keys(): self.calculation_period = param_dict['calculation_period'] else: self.calculation_period = 2 if 'damping_factor' in param_dict.keys(): self.damping_factor = param_dict['damping_factor'] else: self.damping_factor = 1
def get_close_moving_average(self, window_size, time=None, damping_factor=None): if time is None: time_end = self.data.index[-1] time_start = self.data.index[0] else: time_end = time time_start = time - timedelta(days=window_size) df = self.get_price_close() if damping_factor is None: ma_series = df.truncate(before=time_start, after=time_end) ma_series = ma_series.rolling(window_size).mean() ma_series = ma_series.dropna() else: series = df.truncate(before=time_start, after=time_end) series_size = len(series) if series_size < window_size: error('The input series is shorter than the window size.') ma_series = pd.Series() for i in range(series_size - window_size): avg = 0.0 for j in range(window_size): scalar = damping_factor ** j avg = avg + scalar * series.iloc[window_size + i - j - 1] avg = avg / window_size ma_series.at[window_size + i - 1] = avg return ma_series
def signal_average_envelope(env, asset, time=None, val_param=None): if val_param is None: val_param = { 'window_size': [5], 'tolerance_up': 0.025, 'tolerance_down': 0.025, 'damping_factor': 1.0 } ma_param = ValParamMovingAverage(val_param) if len(ma_param.window_size) != 1: error('The window size list needs to be of size 1') window_size = ma_param.window_size[0] section = env.get_section(asset) if time is None: time = section.data.index[-1] if section.data.size < window_size + 2: error('Cannot calculate the moving average. The series is too short.') factor = ma_param.damping_factor price = section.get_price_close(time) ma = section.get_close_moving_average(window_size, time, factor) if price > (1 + ma_param.tolerance_up) * ma.iloc[-1]: return -1 elif price < (1 - ma_param.tolerance_down) * ma.iloc[-1]: return 1 else: return 0
def plot_close_moving_average(self, window_sizes, damping_factor=None): if len(window_sizes) == 0: error('List of window sizes needs to be provided.') for size in window_sizes: series = self.get_close_moving_average(size, None, damping_factor) plt.plot(series.index, series.values) plt.show()
def value(self, asset_prices): total = self.cash + self.wife_pocket for asset in self.holdings.keys(): if asset not in asset_prices.keys(): error('The price of ' + asset + 'is not given in the input dict.') total = total + self.holdings[asset] * asset_prices[asset] return total
def get_price_close(self, time=None): if 'price_close' in self.data.columns: price_close = 'price_close' elif self.target + '_price_close' in self.data.columns: price_close = self.target + '_price_close' else: error('Price close is not in the env section data.') if time is None: return self.data[price_close] series = self.data[price_close] series_trunc = series.truncate(after=time) return series_trunc.iloc[-1]
def sim_price_close(self, num_regimes=1, time_start=None, time_end=None): time_spot = self.data.index[-1] sim_price_close = pd.Series() sim_price_close.index.name = 'time_close' sim_price_close.rename('price_close_sim') sim_price_close[time_spot] = self.get_price_close(time_spot) if num_regimes <= 0: error('num_regimes needs to be positive integer.') if time_start is None: time_start = self.data.index[0] if time_end is None: time_end = self.data.index[-1] time_start = self._round_time(time_start) time_end = self._round_time(time_end) inter_length = (time_end - time_start) / num_regimes for i in range(num_regimes): hist_start = time_start + i * inter_length hist_end = hist_start + inter_length hist_start = self._round_time(hist_start) hist_end = self._round_time(hist_end) statistics = self.stat(hist_start, hist_end) avg = statistics['period_log_return']['mean'] stdev = statistics['period_log_return']['std'] count = int(statistics['period_log_return']['count']) # TODO: use ARIMA for sampling # Here we use p(t_i+1) = p(t_i) * exp( (avg+stdev*N(0,1)) * (t_i+1-t_i) ) norm_vec = np.random.normal(avg, stdev, count - 1) for j in range(count - 1): # TODO: only handle hourly or daily data. if self.data_info['period_id'] == '1HRS': sim_time = sim_price_close.index[-1] + timedelta(hours=1) else: sim_time = sim_price_close.index[-1] + timedelta(days=1) sim_time = self._round_time(sim_time) sim_price_close[sim_time] = sim_price_close.values[ -1] * np.exp(norm_vec[j]) return sim_price_close
def signal_double_dip(env, asset, time=None, val_param=None): if val_param is None: val_param = {'window_size': [3, 20]} if len(val_param['window_size']) != 2: error( 'For double dip, two window sizes need to be provided in the rule.' ) dip_param = ValParamMovingAverage(val_param) window_ma = dip_param.window_size[0] window_dip = dip_param.window_size[1] section = env.get_section(asset) series = section.get_price_close() if time is None: time = series.index[-1] if series.index[0] + timedelta(days=window_ma + window_dip) > time: error( 'The given time is early than the series start date plus window.') series_trunc = series.rolling(window=window_ma, center=False).mean().dropna() end_date = time start_date = time - timedelta(days=window_dip) series_trunc = series_trunc.truncate(before=start_date, after=end_date) y = np.array(list(series_trunc.values)) x = np.arange(len(y)) # t = np.array(list(series_trunc.index)) z = np.polyfit(x, y, 4) a = 4 * z.item(0) b = 3 * z.item(1) c = 2 * z.item(2) d = z.item(3) ind_roots = -27 * a**2 * d**2 + 18 * a * b * c * d - 4 * a * c**3 - 4 * b**3 * d + b**2 * c**2 if z.item(0) > 0: if ind_roots > 0: return 1 else: if ind_roots > 0: return -1 return 0
def signal_consecutive_moves(env, asset, time=None, val_param=None): if val_param is None: val_param = { 'method': 'moving_average', 'window_size': [10], 'tolerance_up': 0.03, 'tolerance_down': 0.03 } if val_param['method'] == 'moving_average': ma_param = ValParamMovingAverage(val_param) section = env.get_section(asset) window_size = ma_param.window_size[0] if time is None: time = section.data.index[-1] if section.data.size < window_size + 2: error( 'Cannot calculate the moving average. The series is too short.' ) factor = ma_param.damping_factor is_bull = True is_bear = True average = [] for i in range(ma_param.calculation_period): time_end = time - timedelta(days=i) average.append( section.get_close_moving_average(window_size, time_end, factor).iloc[-1]) for i in range(len(average) - 1): is_bull = is_bull and ( average[i] > (1 - ma_param.tolerance_up) * average[i + 1]) is_bear = is_bear and ( average[i] < (1 - ma_param.tolerance_down) * average[i + 1]) if is_bear: return -1 elif is_bull: return 1 else: return 0 else: error('Only moving average is implemented for bear market indicator.')
def __init__(self, section_list): """ create a collection of market environment based on a list of envr sections :param section_list: """ model_dict = {} if len(section_list) > 0 and 'period_id' in section_list[0].data_info: freq = section_list[0].data_info['period_id'] for i in range(len(section_list)): model_dict[section_list[i].target] = section_list[i] if 'period_id' in section_list[i].data_info: if section_list[0].data_info['period_id'] != freq: error( 'All the sections in the list should have the data with same freq.' ) self.model_dict = model_dict
def signal_long_short_crossing(env, asset, time=None, val_param=None): if val_param is None: val_param = { 'window_size': [5, 30], 'tolerance_up': 0.0, 'tolerance_down': 0.0 } if val_param['window_size'][0] > val_param['window_size'][1]: window_short = val_param['window_size'][1] window_long = val_param['window_size'][0] else: window_short = val_param['window_size'][0] window_long = val_param['window_size'][1] ma_param = ValParamMovingAverage(val_param) section = env.get_section(asset) if time is None: time = section.data.index[-1] if section.data.size < window_long + 2: error('Cannot calculate the moving average. The series is too short.') factor = ma_param.damping_factor series_short = section.get_close_moving_average(window_short, time, factor) series_long = section.get_close_moving_average(window_long, time, factor) if series_short.iloc[-1] > (1 + ma_param.tolerance_up) * series_long.iloc[-1]: return -1 elif series_short.iloc[-1] < ( 1 - ma_param.tolerance_down) * series_long.iloc[-1]: return 1 else: return 0