def get_ret_lbl(self, stk_mask, ent, ext): ent_idx = self._date_to_idx(ent) ext_idx = self._date_to_idx(ext) ent_px = self.raw_data[stk_mask, ent_idx, get_config().ADJ_CLOSE_DATA_IDX] ext_px = self.raw_data[stk_mask, ext_idx, get_config().ADJ_CLOSE_DATA_IDX] not_tradeable_mask = ~(self.tradable_mask[stk_mask, ext_idx]) if np.sum(not_tradeable_mask) != 0: stk_idxs = np.nonzero(stk_mask)[0] not_tradeable_stk_idxs = stk_idxs[not_tradeable_mask] # stks = self.tickers[stk_mask] # not_tradeable_stks = stks[not_tradeable_mask] fill_idxs = np.nonzero(not_tradeable_mask)[0] for i in range(not_tradeable_stk_idxs.shape[0]): stk_idx = not_tradeable_stk_idxs[i] idx_to_fill = fill_idxs[i] stk_tradeable_mask = self.tradable_mask[stk_idx, :] stk_tradeable_day_idxs = np.nonzero(stk_tradeable_mask)[0] _ext_idx = ent_idx # first tradeable day after after_idxs = np.nonzero(stk_tradeable_day_idxs > ext_idx)[0] if after_idxs.shape[0] > 0: _ext_idx = stk_tradeable_day_idxs[after_idxs[0]] else: before_idxs = np.where(stk_tradeable_day_idxs > ent_idx)[0] if before_idxs.shape[0] > 0: _ext_idx = stk_tradeable_day_idxs[before_idxs[0]] # print("%s %s %d" % (ent.strftime('%Y-%m-%d'), not_tradeable_stks[i], _ext_idx - ent_idx)) _ext_px = self.raw_data[stk_idx, _ext_idx, get_config().ADJ_CLOSE_DATA_IDX] ext_px[idx_to_fill] = _ext_px return (ext_px - ent_px) / ent_px
def __init__(self): self.labels = labels = tf.placeholder(tf.float32, [None], name='labels') self.x = x = tf.placeholder(tf.float32, [None, None, 5], name='input') self.phase = phase = tf.placeholder(tf.bool, name='phase') # keep_prob = tf.placeholder(tf.float32) cells = [] with tf.name_scope('rnn'): idx = 0 for num_units in get_config().LSTM_LAYERS_SIZE: scope_name = 'rnn_layer_%d' % idx with tf.name_scope(scope_name): cell = tf.contrib.rnn.LayerNormBasicLSTMCell(num_units) # cell = tf.contrib.rnn.LSTMCell(num_units) # cell = tf.contrib.rnn.GRUCell(num_units) # cell = tf.contrib.rnn.DropoutWrapper( # cell, output_keep_prob=keep_prob) cells.append(cell) idx += 1 cell = tf.contrib.rnn.MultiRNNCell(cells) # Batch size x time steps x features. output, state = tf.nn.dynamic_rnn(cell, x, dtype=tf.float32) last_idx = tf.shape(output)[1] - 1 output = tf.transpose(output, [1, 0, 2]) self.last = last = tf.gather(output, last_idx, name='rnn_features') x = last idx = 0 for num_units in get_config().FC_LAYERS_SIZE: scope_name = 'fc_layer_%d' % idx with tf.name_scope(scope_name): self.h1 = h1 = tf.contrib.layers.fully_connected(x, num_units, activation_fn=None, scope='dense_%d' % idx) if get_config().BATCH_NORM: self.h2 = h2 = tf.contrib.layers.batch_norm(h1, center=True, scale=True, is_training=phase, scope='bn_%d' % idx) else: h2 = h1 self.h3 = x = tf.nn.tanh(h2, 'tanh_%d' % idx) idx += 1 # final fc layer scope_name = 'fc_layer_%d' % idx with tf.name_scope(scope_name): self.h = x = tf.contrib.layers.fully_connected(x, 1, activation_fn=None, scope='dense_%d' % idx) x = tf.reshape(x,[-1]) with tf.name_scope('loss'): self.returns = x diff = self.returns - labels self.cost = tf.reduce_mean(tf.square(diff)) update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) with tf.control_dependencies(update_ops): optimizer = tf.train.AdamOptimizer() self.optimizer = optimizer.minimize(self.cost) self.saver = tf.train.Saver(tf.global_variables(), max_to_keep=None)
def get_input(self, stk_mask, end): end_idx = self._date_to_idx(end) if get_config().SKIP_NON_TRADING_DAYS: history = get_config().RNN_HISTORY.days valid_trading_day_idxs = np.nonzero( self.trading_day_idxs <= end_idx)[0] history_trading_day_idxs = self.trading_day_idxs[ valid_trading_day_idxs[-history:]] x = self.x[:, history_trading_day_idxs, :] return x[stk_mask, :, :] else: beg = end - get_config().RNN_HISTORY beg_idx = self._date_to_idx(beg) return self.x[stk_mask, beg_idx:end_idx, :]
def __init__(self): print('creating neural network...') self.labels = labels = tf.placeholder(tf.float32, [None, None], name='labels') self.input = input = tf.placeholder(tf.float32, [None, None, 6], name='input') cells = [] with tf.name_scope('rnn'): idx = 0 for num_units in get_config().LSTM_LAYERS_SIZE: scope_name = 'rnn_layer_%d' % idx with tf.name_scope(scope_name): rnn_cell = tf.contrib.rnn.LayerNormBasicLSTMCell(num_units) cells.append(rnn_cell) idx += 1 self.rnn_cell = rnn_cell = tf.contrib.rnn.MultiRNNCell(cells) state = () for s in rnn_cell.state_size: c = tf.placeholder(tf.float32, [None, s.c]) h = tf.placeholder(tf.float32, [None, s.h]) state += (tf.contrib.rnn.LSTMStateTuple(c, h), ) self.state = state # Batch size x time steps x features. output, new_state = tf.nn.dynamic_rnn(rnn_cell, input, initial_state=state) self.new_state = new_state # final fc layer with tf.name_scope('fc_layer'): self.returns = tf.contrib.layers.fully_connected( output, 1, activation_fn=None, scope='dense_%d' % idx) with tf.name_scope('loss'): diff = self.returns - tf.expand_dims(labels, 2) self.cost = tf.reduce_mean(tf.square(diff)) self.optimizer = tf.train.AdamOptimizer().minimize(self.cost) self.sess = tf.Session() self.saver = tf.train.Saver(tf.global_variables(), max_to_keep=None)
def __init__(self): print('loading data...') self._tickers, self.raw_dt, self.raw_data = load_npz_data( 'data/snp/snp_px.npz') print('data load complete') # calc data dimensions self.stks = self.raw_data.shape[0] self.days = self.raw_data.shape[1] # calc data dates range self.HIST_BEG = self._idx_to_date(0) self.HIST_END = self._idx_to_date(-1) # calc tradable_mask, traded_stocks_per_day, trading_day_mask self.tradable_mask = np.all(self.raw_data > 0.0, axis=2) self.traded_stocks_per_day = self.tradable_mask[:, :].sum(0) self.trading_day_mask = self.traded_stocks_per_day > get_config( ).MIN_STOCKS_TRADABLE_PER_TRADING_DAY self.trading_day_idxs = np.nonzero(self.trading_day_mask)[0] # calc snp_mask self.snp_mask = np.full((self.stks, self.days), False) snp_mask_df = pd.read_csv('data/snp/snp_mask.csv') for idx, row in snp_mask_df.iterrows(): _from = datetime.datetime.strptime(row['from'], '%Y-%m-%d').date() _to = datetime.datetime.strptime(row['to'], '%Y-%m-%d').date() _ticker = row['ticker'] stk_idx = self._ticker_to_idx(_ticker) if stk_idx is None: continue _from = max(_from, self.HIST_BEG) _to = min(_to, self.HIST_END) _from_idx = self._date_to_idx(_from) _to_idx = self._date_to_idx(_to) self.snp_mask[stk_idx, _from_idx:_to_idx + 1] = True # prepare input array x = np.zeros((self.stks, self.days, 5)) # fill array for each stock for stk_idx in range(self.stks): stk_raw_data = self.raw_data[stk_idx, :, :] tradable_mask = self.tradable_mask[stk_idx] # #there are some errors in data when let's say we have volume but no prices, or when one px is zero # #threat such days as not tradable to minimize errors - looks like we really can skip such days # vol_tradable_mask = stk_raw_data[:, get_config().VOLUME_DATA_IDX] > 0.0 # xor = tradable_mask != vol_tradable_mask # missmatches = np.sum(xor) # if missmatches > 0: # dt_idx = np.where(xor)[0] # print(self.tickers[stk_idx]) # for idx in range(dt_idx.shape[0]): # date = datetime.datetime.fromtimestamp(self.raw_dt[dt_idx[idx]]).date() # print(date) stk_data = stk_raw_data[tradable_mask, :] a_o = stk_data[:, get_config().ADJ_OPEN_DATA_IDX] a_c = stk_data[:, get_config().ADJ_CLOSE_DATA_IDX] a_h = stk_data[:, get_config().ADJ_HIGH_DATA_IDX] a_l = stk_data[:, get_config().ADJ_LOW_DATA_IDX] a_v = stk_data[:, get_config().ADJ_VOLUME_DATA_IDX] if a_c.shape[0] == 0: # print(self._idx_to_ticker(stk_idx)) continue prev_a_c = np.roll(a_c, 1) prev_a_c[0] = a_c[0] prev_a_v = np.roll(a_v, 1) prev_a_v[0] = a_v[0] # other variant is to use ln(a_c / prev_a_c) - they are almost identical # plus we can scale input by multiplier (100%) x_o = np.log(a_o / prev_a_c) if get_config().LOG_RET else ( a_o - prev_a_c) / prev_a_c x_c = np.log(a_c / prev_a_c) if get_config().LOG_RET else ( a_c - prev_a_c) / prev_a_c x_h = np.log(a_h / prev_a_c) if get_config().LOG_RET else ( a_h - prev_a_c) / prev_a_c x_l = np.log(a_l / prev_a_c) if get_config().LOG_RET else ( a_l - prev_a_c) / prev_a_c x_v = np.log(a_v / prev_a_v) if get_config().LOG_VOL_CHG else ( a_v - prev_a_v) / prev_a_v x[stk_idx, tradable_mask, 0] = get_config().RET_MUL * x_o x[stk_idx, tradable_mask, 1] = get_config().RET_MUL * x_c x[stk_idx, tradable_mask, 2] = get_config().RET_MUL * x_h x[stk_idx, tradable_mask, 3] = get_config().RET_MUL * x_l x[stk_idx, tradable_mask, 4] = get_config().VOL_CHG_MUL * x_v self.x = x
def train(): snp_env = SnpEnv() if get_config().NET_VER == NetVersion.APPLE: net = NetApple() elif get_config().NET_VER == NetVersion.BANANA: net = NetBanana() elif get_config().NET_VER == NetVersion.WORM: net = NetWorm() elif get_config().NET_VER == NetVersion.SNAKE: net = NetSnake() elif get_config().NET_VER == NetVersion.ANTI_SNAKE: net = NetAntiSnake() elif get_config().NET_VER == NetVersion.CAT: net = NetCat() elif get_config().NET_VER == NetVersion.COW: net = NetCow() net.init() train_trading_schedule = [] test_trading_schedule = [] for ent, ext in snp_env.trading_schedule_generator( get_config().TRAIN_BEG, get_config().TRAIN_END, get_config().TRADING_PERIOD_DAYS): train_trading_schedule.append((ent, ext)) for ent, ext in snp_env.trading_schedule_generator( get_config().TEST_BEG, get_config().TEST_END, get_config().TRADING_PERIOD_DAYS): test_trading_schedule.append((ent, ext)) if not os.path.exists(get_config().TRAIN_STAT_PATH): with open(get_config().TRAIN_STAT_PATH, 'a', newline='') as f: writer = csv.writer(f) writer.writerow( ('epoch', 'train dd', 'train y avg', 'train sharpe', 'test dd', 'test y avg', 'test sharpe')) with open(get_config().TRAIN_STAT_PATH, 'a', newline='') as f: writer = csv.writer(f) if get_config().EPOCH_WEIGHTS_TO_LOAD != 0: net.load_weights(get_config().WEIGHTS_PATH, get_config().EPOCH_WEIGHTS_TO_LOAD) epoch = get_config().EPOCH_WEIGHTS_TO_LOAD if get_config().MODE == Mode.TRAIN: epoch += 1 else: epoch = 0 while True: print("Epoch %d" % epoch) # train if get_config().MODE == Mode.TRAIN: print("Training...") if get_config().SHUFFLE: train_schedule = random.sample(train_trading_schedule, len(train_trading_schedule)) else: train_schedule = train_trading_schedule dataset_size = len(train_schedule) curr_progress = 0 passed = 0 for ent, ext in train_schedule: stk_mask = snp_env.get_tradeable_snp_components_mask(ent) # print("%d %s %s" % (np.sum(stk_mask), ent.strftime("%Y-%m-%d"), ext.strftime("%Y-%m-%d"))) x = snp_env.get_input(stk_mask, ent) labels = snp_env.get_ret_lbl(stk_mask, ent, ext) if get_config().NET_VER == NetVersion.COW: variances = calc_variance(x) if get_config().NET_VER == NetVersion.COW: net.fit(x, labels, variances) else: net.fit(x, labels) if passed == 0: if get_config().NET_VER == NetVersion.COW: pl, weights = net.eval(x, labels, variances) else: pl, weights = net.eval(x, labels) print(pl) curr_progress = progress.print_progress( curr_progress, passed, dataset_size) passed += 1 progress.print_progess_end() # eval train print("Eval train...") ret = np.zeros((len(train_trading_schedule))) dataset_size = len(train_trading_schedule) curr_progress = 0 passed = 0 dt = [] for ent, ext in train_trading_schedule: if ext is None: break if len(dt) == 0: dt.append(ent) dt.append(ext) stk_mask = snp_env.get_tradeable_snp_components_mask(ent) x = snp_env.get_input(stk_mask, ent) labels = snp_env.get_ret_lbl(stk_mask, ent, ext) if get_config().NET_VER == NetVersion.COW: variances = calc_variance(x) pl, weights = net.eval(x, labels, variances) else: pl, weights = net.eval(x, labels) if get_config().NET_VER == NetVersion.APPLE: # # net # long_mask = weights >= 0.0 # short_mask = weights <= 0.0 # # int_date = snp_env.find_trading_date(ent + datetime.timedelta(days=1)) # int_r = snp_env.get_ret_lbl(stk_mask, ent, int_date) # port_ret = labels - int_r # # sorted_int_r_idxs = np.argsort(int_r) # long_idxs = np.nonzero(long_mask)[0] # long_int_r = int_r[long_idxs] # sorted_long_int_r_idxs = np.argsort(long_int_r) # long_sel_idxs = long_idxs[sorted_long_int_r_idxs[:get_config().SELECTTION]] # # short_idxs = np.nonzero(short_mask)[0] # short_int_r = int_r[short_idxs] # sorted_short_int_r_idxs = np.argsort(short_int_r) # short_sel_idxs = short_idxs[sorted_short_int_r_idxs[-get_config().SELECTTION:]] # # long_pl = np.mean(port_ret[long_sel_idxs]) # short_pl = np.mean(port_ret[short_sel_idxs]) # # no net # int_date = snp_env.find_trading_date(ent + datetime.timedelta(days=1)) # int_r = snp_env.get_ret_lbl(stk_mask, ent, int_date) # port_ret = labels - int_r # sorted_int_r_idxs = np.argsort(int_r) # long_pl = np.mean(port_ret[sorted_int_r_idxs[:get_config().SELECTTION]]) # short_pl = np.mean(port_ret[sorted_int_r_idxs[-get_config().SELECTTION:]]) # 1d no net prev_date = snp_env.find_prev_trading_date( ent - datetime.timedelta(days=1)) int_stk_mask = snp_env.get_tradeable_snp_components_mask( prev_date) stk_mask &= int_stk_mask int_r = snp_env.get_ret_lbl(stk_mask, prev_date, ent) port_ret = snp_env.get_ret_lbl(stk_mask, ent, ext) sorted_int_r_idxs = np.argsort(int_r) long_pl = np.mean( port_ret[sorted_int_r_idxs[:get_config().SELECTTION]]) short_pl = np.mean( port_ret[sorted_int_r_idxs[-get_config().SELECTTION:]]) # real_ext = snp_env.find_trading_date(ext + datetime.timedelta(days=1)) # if real_ext is None: # real_ext = ext # int_r = labels # port_ret = snp_env.get_ret_lbl(stk_mask, ext, real_ext) # sorted_int_r_idxs = np.argsort(int_r) # long_pl = np.mean(port_ret[sorted_int_r_idxs[:get_config().SELECTTION]]) # short_pl = np.mean(port_ret[sorted_int_r_idxs[-get_config().SELECTTION:]]) pl = 0.7 * long_pl - 0.3 * short_pl # if get_config().NET_VER == NetVersion.SNAKE or get_config().NET_VER == NetVersion.ANTI_SNAKE: # sorted_weights_idxs = np.argsort(weights) # selected_weights_idxs = sorted_weights_idxs[-get_config().SELECTTION:] # pl = np.mean(labels[selected_weights_idxs]) # if get_config().NET_VER == NetVersion.ANTI_SNAKE: # pl = -pl if get_config().PRINT_PREDICTION: print_alloc(pl, ent, snp_env.tickers, stk_mask, weights) if abs(pl) >= 0.3: pl = 0 ret[passed] = pl curr_progress = progress.print_progress( curr_progress, passed, dataset_size) passed += 1 progress.print_progess_end() # print("Train loss: %.4f" % (np.mean(np.sqrt(ret)) * 100)) years = (get_config().TRAIN_END - get_config().TRAIN_BEG).days / 365 capital = get_capital(ret, False) train_dd = get_draw_down(capital, False) train_sharpe = get_sharpe_ratio(ret, years) train_y_avg = get_avg_yeat_ret(ret, years) print('Train dd: %.2f%% y_avg: %.2f%% sharpe: %.2f' % (train_dd * 100, train_y_avg * 100, train_sharpe)) if get_config().MODE == Mode.TEST: plot_equity_curve("Train equity curve", dt, capital) df = pd.DataFrame({'date': dt, 'capital': capital}) df.to_csv('data/tr_eq.csv', index=False) # eval test print("Eval test...") ret = np.zeros((len(test_trading_schedule))) dataset_size = len(test_trading_schedule) curr_progress = 0 passed = 0 dt = [] for ent, ext in test_trading_schedule: if ext.month == 3 and ext.day == 11 and ext.year == 2015: _debug = 0 if ext is None: break if len(dt) == 0: dt.append(ent) dt.append(ext) stk_mask = snp_env.get_tradeable_snp_components_mask(ent) x = snp_env.get_input(stk_mask, ent) labels = snp_env.get_ret_lbl(stk_mask, ent, ext) if get_config().NET_VER == NetVersion.COW: variances = calc_variance(x) pl, weights = net.eval(x, labels, variances) else: pl, weights = net.eval(x, labels) if get_config().NET_VER == NetVersion.APPLE: # # net # long_mask = weights >= 0.0 # short_mask = weights <= 0.0 # # int_date = snp_env.find_trading_date(ent + datetime.timedelta(days=1)) # int_r = snp_env.get_ret_lbl(stk_mask, ent, int_date) # port_ret = labels - int_r # # sorted_int_r_idxs = np.argsort(int_r) # long_idxs = np.nonzero(long_mask)[0] # long_int_r = int_r[long_idxs] # sorted_long_int_r_idxs = np.argsort(long_int_r) # long_sel_idxs = long_idxs[sorted_long_int_r_idxs[:get_config().SELECTTION]] # # short_idxs = np.nonzero(short_mask)[0] # short_int_r = int_r[short_idxs] # sorted_short_int_r_idxs = np.argsort(short_int_r) # short_sel_idxs = short_idxs[sorted_short_int_r_idxs[-get_config().SELECTTION:]] # # long_pl = np.mean(port_ret[long_sel_idxs]) # short_pl = np.mean(port_ret[short_sel_idxs]) # # no net # int_date = snp_env.find_trading_date(ent + datetime.timedelta(days=1)) # int_r = snp_env.get_ret_lbl(stk_mask, ent, int_date) # port_ret = labels - int_r # sorted_int_r_idxs = np.argsort(int_r) # long_pl = np.mean(port_ret[sorted_int_r_idxs[:get_config().SELECTTION]]) # short_pl = np.mean(port_ret[sorted_int_r_idxs[-get_config().SELECTTION:]]) # 1d no net prev_date = snp_env.find_prev_trading_date( ent - datetime.timedelta(days=1)) int_stk_mask = snp_env.get_tradeable_snp_components_mask( prev_date) stk_mask &= int_stk_mask int_r = snp_env.get_ret_lbl(stk_mask, prev_date, ent) port_ret = snp_env.get_ret_lbl(stk_mask, ent, ext) sorted_int_r_idxs = np.argsort(int_r) long_pl = np.mean( port_ret[sorted_int_r_idxs[:get_config().SELECTTION]]) short_pl = np.mean( port_ret[sorted_int_r_idxs[-get_config().SELECTTION:]]) pl = 0.7 * long_pl - 0.3 * short_pl # if get_config().NET_VER == NetVersion.SNAKE or get_config().NET_VER == NetVersion.ANTI_SNAKE: # sorted_weights_idxs = np.argsort(weights) # selected_weights_idxs = sorted_weights_idxs[-get_config().SELECTTION:] # pl = np.mean(labels[selected_weights_idxs]) # if get_config().NET_VER == NetVersion.ANTI_SNAKE: # pl = -pl if get_config().PRINT_PREDICTION: print_alloc(pl, ent, snp_env.tickers, stk_mask, weights) if abs(pl) >= 0.3: pl = 0 ret[passed] = pl curr_progress = progress.print_progress( curr_progress, passed, dataset_size) passed += 1 progress.print_progess_end() # print("Test loss: %.4f" % (np.mean(np.sqrt(ret)) * 100)) years = (get_config().TRAIN_END - get_config().TRAIN_BEG).days / 365 capital = get_capital(ret, False) test_dd = get_draw_down(capital, False) test_sharpe = get_sharpe_ratio(ret, years) test_y_avg = get_avg_yeat_ret(ret, years) print('Test dd: %.2f%% y_avg: %.2f%% sharpe: %.2f' % (test_dd * 100, test_y_avg * 100, test_sharpe)) if get_config().MODE == Mode.TEST: plot_equity_curve("Test equity curve", dt, capital) df = pd.DataFrame({'date': dt, 'capital': capital}) df.to_csv('data/tst_eq.csv', index=False) if get_config().MODE == Mode.TRAIN: net.save_weights(get_config().WEIGHTS_PATH, epoch) writer.writerow((epoch, train_dd, train_y_avg, train_sharpe, test_dd, test_y_avg, test_sharpe)) epoch += 1 f.flush() else: show_plots() break
def calc_variance(x): return np.maximum(np.var(x[:, :, 1], axis=1), get_config().MIN_VARIANCE)
def preprocess_snp_px(): tickers = get_snp_hitorical_components_tickers() preprocess_data(tickers, 'data/snp/snp_px.csv', get_config().HIST_BEG, get_config().HIST_END, 'data/snp/snp_px.npz', get_config().DATA_FEATURES)
def download_snp_px(): tickers = get_snp_hitorical_components_tickers() download_data(tickers, 'data/snp/snp_px.csv', get_config().HIST_BEG, get_config().HIST_END, )