def process_data(self, df): if (self.data_freq == 'm') and (self.freq > 1): xdf = self.proc_func(df, **self.proc_args) else: xdf = df for i in range(len(self.channel)): xdf['chan_h' + str(i)] = self.chan_high( xdf, self.channel[i], **self.chan_func['high']['args']).shift(1) xdf['chan_l' + str(i)] = self.chan_low( xdf, self.channel[i], **self.chan_func['low']['args']).shift(1) xdf['ATR'] = dh.ATR(xdf, n=self.SL_win).shift(1).fillna(method='bfill') xdf.ix[:, 'tr_stop_h'] = xdf.ix[:, 'chan_h0'] - ( xdf.ix[:, 'ATR'] * self.SL / self.tick_base).astype('int') * self.tick_base xdf.ix[:, 'tr_stop_l'] = xdf.ix[:, 'chan_l0'] + ( xdf.ix[:, 'ATR'] * self.SL / self.tick_base).astype('int') * self.tick_base xdf['long_exit'] = pd.concat([xdf['chan_l1'], xdf['tr_stop_h']], join='outer', axis=1).max(axis=1) xdf['short_exit'] = pd.concat([xdf['chan_h1'], xdf['tr_stop_l']], join='outer', axis=1).min(axis=1) if (self.data_freq == 'm') and (self.freq > 1): xdf = df.join( xdf[['long_exit', 'short_exit', 'chan_h0', 'chan_l0']], how='left').fillna(method='ffill') self.df = xdf.dropna().copy()
def process_data(self, mdf): if self.freq == 1: xdf = mdf else: freq_str = str(self.freq) + "min" xdf = dh.conv_ohlc_freq(mdf, freq_str, extra_cols=['contract']) xdf['ATR'] = dh.ATR(xdf, n=self.atr_len) xdf['ATRMA'] = dh.MA(xdf, n=self.atrma_len, field='ATR') xdf['RSI'] = dh.RSI(xdf, n=self.rsi_len) self.df = xdf self.df['datetime'] = self.df.index self.df['cost'] = 0.0 self.df['pos'] = 0.0 self.df['traded_price'] = self.df['open']
def chanbreak_sim(mdf, config): freq = config['freq'] str_freq = str(freq) + 'Min' xdf = dh.conv_ohlc_freq(mdf, str_freq) tcost = config['trans_cost'] offset = config['offset'] win = config['win'] chan_func = config['channel_func'] upper_chan_func = eval(chan_func[0]) lower_chan_func = eval(chan_func[1]) entry_chan = win[0] exit_chan = win[1] exit_min = config.get('exit_min', 2057) close_daily = config.get('close_daily', False) stoploss = config.get('stoploss', 2.0) xdf['H1'] = upper_chan_func(xdf, entry_chan) xdf['L1'] = lower_chan_func(xdf, entry_chan) xdf['H2'] = upper_chan_func(xdf, exit_chan) xdf['L2'] = lower_chan_func(xdf, exit_chan) xdf['ATR'] = dh.ATR(xdf, entry_chan) xdata = pd.concat([xdf['H1'], xdf['L1'], xdf['H2'], xdf['L2'], xdf['ATR']], \ axis=1, keys=['H1', 'L1', 'H2', 'L2', 'ATR']).shift(1) mdf = mdf.join(xdata, how='left').fillna(method='ffill') mdf['close_ind'] = np.isnan(mdf['close'].shift(-3)) if close_daily: mdf['close_ind'] = (mdf['min_id'] >= exit_min) | mdf['close_ind'] long_signal = pd.Series(np.nan, index=mdf.index) long_signal[(mdf['open'] >= mdf['H1'])] = 1 long_signal[(mdf['open'] <= mdf['L2'])] = 0 if stoploss > 0: long_signal[(mdf['open'] <= mdf['H1'] - mdf['ATR'] * stoploss)] = 0 long_signal[mdf['close_ind']] = 0 long_signal = long_signal.fillna(method='ffill').fillna(0) short_signal = pd.Series(np.nan, index=mdf.index) short_signal[(mdf['open'] <= mdf['L1'])] = -1 short_signal[(mdf['open'] >= mdf['H2'])] = 0 if stoploss > 0: short_signal[(mdf['open'] >= mdf['L1'] + mdf['ATR'] * stoploss)] = 0 short_signal[mdf['close_ind']] = 0 short_signal = short_signal.fillna(method='ffill').fillna(0) mdf['pos'] = long_signal + short_signal mdf['cost'] = abs(mdf['pos'] - mdf['pos'].shift(1)) * (offset + mdf['open'] * tcost) mdf['cost'] = mdf['cost'].fillna(0.0) mdf['traded_price'] = mdf.open + (mdf['pos'] - mdf['pos'].shift(1)) * offset closed_trades = backtest.simdf_to_trades1(mdf, slippage=offset) return (mdf, closed_trades)
def process_data(self, mdf): xdf = self.proc_func(mdf, **self.proc_args) if self.win == -1: tr = pd.concat( [xdf.high - xdf.low, abs(xdf.close - xdf.close.shift(1))], join='outer', axis=1).max(axis=1) elif self.win == 0: tr = pd.concat( [(pd.rolling_max(xdf.high, 2) - pd.rolling_min(xdf.close, 2)) * self.multiplier, (pd.rolling_max(xdf.close, 2) - pd.rolling_min(xdf.low, 2)) * self.multiplier, xdf.high - xdf.close, xdf.close - xdf.low], join='outer', axis=1).max(axis=1) else: tr = pd.concat([ pd.rolling_max(xdf.high, self.win) - pd.rolling_min(xdf.close, self.win), pd.rolling_max(xdf.close, self.win) - pd.rolling_min(xdf.low, self.win) ], join='outer', axis=1).max(axis=1) xdf['TR'] = tr xdf['chanh'] = self.chan_high(xdf['high'], self.chan, **self.chan_func['high']['args']) xdf['chanl'] = self.chan_low(xdf['low'], self.chan, **self.chan_func['low']['args']) xdf['ATR'] = dh.ATR(xdf, n=self.atr_len) xdf['MA'] = dh.MA(xdf, n=self.atr_len, field='close') xdata = pd.concat([ xdf['TR'].shift(1), xdf['MA'].shift(1), xdf['ATR'].shift(1), xdf['chanh'].shift(1), xdf['chanl'].shift(1), xdf['open'] ], axis=1, keys=['tr', 'ma', 'atr', 'chanh', 'chanl', 'dopen']).fillna(0) self.df = mdf.join(xdata, how='left').fillna(method='ffill') self.df['datetime'] = self.df.index self.df['cost'] = 0 self.df['pos'] = 0 self.df['traded_price'] = self.df['open']
def chanbreak_sim(mdf, ddf, config): freq = config['freq'] str_freq = str(freq) + 'Min' xdf = dh.conv_ohlc_freq(mdf, str_freq) start_equity = config['capital'] tcost = config['trans_cost'] unit = config['unit'] k = config['scaler'] marginrate = config['marginrate'] offset = config['offset'] win = config['win'] chan_func = config['channel_func'] upper_chan_func = chan_func[0] lower_chan_func = chan_func[1] entry_chan = win exit_chan = int(entry_chan / k[1]) xdf['H1'] = upper_chan_func(xdf, entry_chan).shift(1) xdf['L1'] = lower_chan_func(xdf, entry_chan).shift(1) xdf['H2'] = upper_chan_func(xdf, exit_chan).shift(1) xdf['L2'] = lower_chan_func(xdf, exit_chan).shift(1) ddf['ATR'] = dh.ATR(ddf, entry_chan) ll = mdf.shape[0] mdf['pos'] = pd.Series([0] * ll, index=mdf.index) mdf['cost'] = pd.Series([0] * ll, index=mdf.index) curr_pos = [] closed_trades = [] end_d = mdf.index[-1].date() tradeid = 0 x_idx = 0 max_idx = len(xdf.index) for idx, dd in enumerate(mdf.index): mslice = mdf.ix[dd] min_id = agent.get_min_id(dd) d = dd.date() dslice = ddf.ix[d] while (x_idx < max_idx - 1) and (xdf.index[x_idx + 1] < dd): x_idx += 1 xslice = xdf.iloc[x_idx] if len(curr_pos) == 0: pos = 0 else: pos = curr_pos[0].pos mdf.ix[dd, 'pos'] = pos if np.isnan(dslice.ATR): continue if (min_id >= config['exit_min']): if (pos != 0) and (d == end_d): curr_pos[0].close(mslice.close - misc.sign(pos) * offset, dd) tradeid += 1 curr_pos[0].exit_tradeid = tradeid closed_trades.append(curr_pos[0]) curr_pos = [] mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) continue else: if (pos != 0): curr_pos[0].trail_update(mslice.close) if curr_pos[0].trail_check(mslice.close, dslice.ATR * k[0]): curr_pos[0].close(mslice.close - misc.sign(pos) * offset, dd) tradeid += 1 curr_pos[0].exit_tradeid = tradeid closed_trades.append(curr_pos[0]) pos = 0 curr_pos = [] if ((mslice.close >= xslice.H2) and (pos < 0)) or ((mslice.close <= xslice.L2) and (pos > 0)): curr_pos[0].close(mslice.close - misc.sign(pos) * offset, dd) tradeid += 1 curr_pos[0].exit_tradeid = tradeid closed_trades.append(curr_pos[0]) curr_pos = [] mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) pos = 0 if ((mslice.close >= xslice.H1) and (pos <= 0)) or ((mslice.close <= xslice.L1) and (pos >= 0)): if (pos == 0): target_pos = (mslice.close >= xslice.H1) * unit - ( mslice.close <= xslice.L1) * unit new_pos = strat.TradePos([mslice.contract], [1], target_pos, mslice.close, mslice.close) tradeid += 1 new_pos.entry_tradeid = tradeid new_pos.open(mslice.close + misc.sign(target_pos) * offset, dd) curr_pos.append(new_pos) mdf.ix[dd, 'cost'] -= abs(target_pos) * (offset + mslice.close * tcost) mdf.ix[dd, 'pos'] = pos else: print "something wrong with position=%s, close =%s, upBnd=%s, lowBnd=%s" % ( pos, mslice.close, xslice.H1, xslice.L1) (res_pnl, ts) = backtest.get_pnl_stats(mdf, start_equity, marginrate, 'm') res_trade = backtest.get_trade_stats(closed_trades) res = dict(res_pnl.items() + res_trade.items()) return (res, closed_trades, ts)
def turtle_sim(mdf, config): ddf = config['ddf'] marginrate = config['marginrate'] offset = config['offset'] start_equity = config['capital'] tcost = config['trans_cost'] chan = config['chan'] unit = config['unit'] param = config['param'] max_pos = param[1] max_loss = param[0] ma_ratio = config['ma_ratio'] use_ma = config['use_ma'] chan_func = config['chan_func'] pos_update = config['pos_update'] ddf['ATR'] = dh.ATR(ddf, n=chan[0]).shift(1) ddf['OL_1'] = eval(chan_func['high']['func'])(ddf, chan[0]).shift(1) ddf['OS_1'] = eval(chan_func['low']['func'])(ddf, chan[0]).shift(1) ddf['CL_1'] = eval(chan_func['low']['func'])(ddf, chan[1]).shift(1) ddf['CS_1'] = eval(chan_func['high']['func'])(ddf, chan[1]).shift(1) ddf['MA1'] = dh.MA(ddf, ma_ratio * chan[0]).shift(1) ddf['MA2'] = dh.MA(ddf, chan[1]).shift(1) ll = mdf.shape[0] mdf['pos'] = pd.Series([0] * ll, index=mdf.index) mdf['cost'] = pd.Series([0] * ll, index=mdf.index) curr_pos = [] tradeid = 0 closed_trades = [] end_d = mdf.index[-1].date() curr_atr = 0 for idx, dd in enumerate(mdf.index): mslice = mdf.ix[dd] min_id = mslice.min_id d = mslice.date dslice = ddf.ix[d] tot_pos = sum([trade.pos for trade in curr_pos]) mdf.ix[dd, 'pos'] = tot_pos if np.isnan(dslice.ATR): continue if (min_id >= config['exit_min']): if (tot_pos != 0) and (d == end_d): for trade_pos in curr_pos: trade_pos.close( mslice.close - misc.sign(trade_pos.pos) * offset, dd) tradeid += 1 trade_pos.exit_tradeid = tradeid closed_trades.append(trade_pos) mdf.ix[dd, 'cost'] -= abs( trade_pos.pos) * (offset + mslice.close * tcost) curr_pos = [] tot_pos = 0 else: if tot_pos == 0: curr_atr = dslice.ATR direction = 0 dol = dslice.OL_1 dos = dslice.OS_1 if (mslice.close >= dol) and ((use_ma == False) or (mslice.MA1 >= mslice.MA2)): direction = 1 elif (mslice.close <= dos) and ((use_ma == False) or (mslice.MA1 <= mslice.MA2)): direction = -1 pos = direction * unit if direction != 0: new_pos = strat.TradePos([mslice.contract], [1], pos, mslice.close, mslice.close) tradeid += 1 new_pos.entry_tradeid = tradeid new_pos.open(mslice.close + direction * offset, dd) mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) curr_pos.append(new_pos) else: direction = curr_pos[0].direction #exit position out of channel if (direction == 1 and mslice.close <= dslice.CL_1) or \ (direction == -1 and mslice.close >= dslice.CS_1): for trade_pos in curr_pos: trade_pos.close( mslice.close - misc.sign(trade_pos.pos) * offset, dd) tradeid += 1 trade_pos.exit_tradeid = tradeid closed_trades.append(trade_pos) mdf.ix[dd, 'cost'] -= abs( trade_pos.pos) * (offset + mslice.close * tcost) curr_pos = [] #stop loss position partially elif curr_pos[-1].check_exit(mslice.close, curr_atr * max_loss): for trade_pos in curr_pos: if trade_pos.check_exit(mslice.close, curr_atr * max_loss): trade_pos.close( mslice.close - misc.sign(trade_pos.pos) * offset, dd) tradeid += 1 trade_pos.exit_tradeid = tradeid closed_trades.append(trade_pos) mdf.ix[dd, 'cost'] -= abs(trade_pos.pos) * ( offset + mslice.close * tcost) curr_pos = [ trade for trade in curr_pos if not trade.is_closed ] #add positions elif (len(curr_pos) < max_pos ) and (mslice.close - curr_pos[-1].entry_price ) * direction > curr_atr / max_pos * max_loss: for trade_pos in curr_pos: #trade.exit_target += curr_atr/max_pos*max_loss * direction trade_pos.set_exit(mslice.close) new_pos = strat.TradePos([mslice.contract], [1], direction * unit, mslice.close, mslice.close) tradeid += 1 new_pos.entry_tradeid = tradeid new_pos.open(mslice.close + direction * offset, dd) mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) curr_pos.append(new_pos) if (len(curr_pos) > 0) and pos_update: for trade_pos in curr_pos: trade_pos.update_price(mslice.close) mdf.ix[dd, 'pos'] = sum([trade.pos for trade in curr_pos]) return (mdf, closed_trades)
def chanbreak_sim(mdf, config): freq = config['freq'] str_freq = str(freq) + 'Min' xdf = dh.conv_ohlc_freq(mdf, str_freq) tcost = config['trans_cost'] unit = config['unit'] offset = config['offset'] win = config['win'] pos_class = config['pos_class'] pos_args = config['pos_args'] chan_func = config['channel_func'] upper_chan_func = chan_func[0] lower_chan_func = chan_func[1] entry_chan = win[0] exit_chan = win[1] xdf['H1'] = upper_chan_func(xdf, entry_chan) xdf['L1'] = lower_chan_func(xdf, entry_chan) xdf['H2'] = upper_chan_func(xdf, exit_chan) xdf['L2'] = lower_chan_func(xdf, exit_chan) xdf['ATR'] = dh.ATR(xdf, entry_chan) xdata = pd.concat([xdf['H1'], xdf['L1'], xdf['H2'], xdf['L2'], xdf['ATR'], xdf['high'], xdf['low']], \ axis=1, keys=['H1', 'L1', 'H2', 'L2', 'ATR', 'xhigh', 'xlow']).shift(1) ll = mdf.shape[0] mdf = mdf.join(xdata, how='left').fillna(method='ffill') mdf['pos'] = pd.Series([0] * ll, index=mdf.index) mdf['cost'] = pd.Series([0] * ll, index=mdf.index) curr_pos = [] closed_trades = [] end_d = mdf.index[-1].date() tradeid = 0 for idx, dd in enumerate(mdf.index): mslice = mdf.ix[dd] min_id = mslice.min_id cnt_id = count_min_id(dd) if len(curr_pos) == 0: pos = 0 else: pos = curr_pos[0].pos mdf.ix[dd, 'pos'] = pos #if np.isnan(mslice.ATR): # continue if (min_id >= config['exit_min']): if (pos != 0) and (dd.date() == end_d): curr_pos[0].close(mslice.close - misc.sign(pos) * offset, dd) tradeid += 1 curr_pos[0].exit_tradeid = tradeid closed_trades.append(curr_pos[0]) curr_pos = [] pos = 0 mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) continue else: if (pos != 0): if (cnt_id % freq) == 0: curr_pos[0].update_bar(mslice) check_price = (pos > 0) * mslice.low + (pos < 0) * mslice.high if curr_pos[0].check_exit(check_price, 0): curr_pos[0].close(mslice.close - misc.sign(pos) * offset, dd) tradeid += 1 curr_pos[0].exit_tradeid = tradeid closed_trades.append(curr_pos[0]) pos = 0 curr_pos = [] if ((mslice.high >= mslice.H2) and (pos < 0)) or ((mslice.low <= mslice.L2) and (pos > 0)): curr_pos[0].close(mslice.close - misc.sign(pos) * offset, dd) tradeid += 1 curr_pos[0].exit_tradeid = tradeid closed_trades.append(curr_pos[0]) curr_pos = [] mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) pos = 0 if ((mslice.high >= mslice.H1) and (pos <= 0)) or ((mslice.low <= mslice.L1) and (pos >= 0)): if (pos == 0): pos = (mslice.high >= mslice.H1) * unit - (mslice.low <= mslice.L1) * unit exit_target = (mslice.high >= mslice.H1) * mslice.L2 + ( mslice.low <= mslice.L1) * mslice.H2 new_pos = pos_class([mslice.contract], [1], pos, mslice.close, exit_target, 1, **pos_args) tradeid += 1 new_pos.entry_tradeid = tradeid new_pos.open(mslice.close + misc.sign(pos) * offset, dd) curr_pos.append(new_pos) mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) mdf.ix[dd, 'pos'] = pos return (mdf, closed_trades)
def run_vec_sim(self): xdf = self.proc_func(self.df, **self.proc_args) if self.win == -1: tr = pd.concat( [xdf.high - xdf.low, abs(xdf.close - xdf.close.shift(1))], join='outer', axis=1).max(axis=1) elif self.win == 0: tr = pd.concat( [(pd.rolling_max(xdf.high, 2) - pd.rolling_min(xdf.close, 2)) * self.multiplier, (pd.rolling_max(xdf.close, 2) - pd.rolling_min(xdf.low, 2)) * self.multiplier, xdf.high - xdf.close, xdf.close - xdf.low], join='outer', axis=1).max(axis=1) else: tr = pd.concat([ pd.rolling_max(xdf.high, self.win) - pd.rolling_min(xdf.close, self.win), pd.rolling_max(xdf.close, self.win) - pd.rolling_min(xdf.low, self.win) ], join='outer', axis=1).max(axis=1) xdf['tr'] = tr xdf['chan_h'] = self.chan_high(xdf, self.chan, **self.chan_func['high']['args']) xdf['chan_l'] = self.chan_low(xdf, self.chan, **self.chan_func['low']['args']) xdf['atr'] = dh.ATR(xdf, self.machan) xdf['ma'] = pd.rolling_mean(xdf.close, self.machan) xdf['rng'] = pd.DataFrame( [self.min_rng * xdf['open'], self.k * xdf['tr'].shift(1)]).max() xdf['upper'] = xdf['open'] + xdf['rng'] * ( 1 + (xdf['open'] < xdf['ma'].shift(1)) * self.f) xdf['lower'] = xdf['open'] - xdf['rng'] * ( 1 + (xdf['open'] > xdf['ma'].shift(1)) * self.f) xdata = pd.concat([ xdf['upper'], xdf['lower'], xdf['chan_h'].shift(1), xdf['chan_l'].shift(1), xdf['open'] ], axis=1, keys=['upper', 'lower', 'chan_h', 'chan_l', 'xopen']).fillna(0) mdf = self.df.join(xdata, how='left').fillna(method='ffill') mdf['dt_signal'] = np.nan if self.price_mode == "HL": up_price = mdf['high'] dn_price = mdf['low'] elif self.price_mode == "TP": up_price = (mdf['high'] + mdf['low'] + mdf['close']) / 3.0 dn_price = up_price elif self.price_mode == "CL": up_price = mdf['close'] dn_price = mdf['close'] else: print "unsupported price mode" mdf.ix[up_price >= mdf['upper'], 'dt_signal'] = 1 mdf.ix[dn_price <= mdf['lower'], 'dt_signal'] = -1 if self.close_daily: mdf.ix[mdf['min_id'] >= self.exit_min, 'dt_signal'] = 0 addon_signal = copy.deepcopy(mdf['dt_signal']) mdf['dt_signal'] = mdf['dt_signal'].fillna(method='ffill').fillna(0) mdf['chan_sig'] = np.nan if combo_signal: mdf.ix[(up_price >= mdf['chan_h']) & (addon_signal > 0), 'chan_sig'] = 1 mdf.ix[(dn_price <= mdf['chan_l']) & (addon_signal < 0), 'chan_sig'] = -1 else: mdf.ix[(mdf['high'] >= mdf['chan_h']), 'chan_sig'] = 1 mdf.ix[(mdf['low'] <= mdf['chan_l']), 'chan_sig'] = -1 mdf['chan_sig'] = mdf['chan_sig'].fillna(method='ffill').fillna(0) pos = mdf['dt_signal'] * ( self.unit[0] + (mdf['chan_sig'] * mdf['dt_signal'] > 0) * self.unit[1]) mdf['pos'] = pos.shift(1).fillna(0) mdf.ix[-3:, 'pos'] = 0 mdf['cost'] = abs(mdf['pos'] - mdf['pos'].shift(1)) * ( self.offset + mdf['open'] * self.tcost) mdf['cost'] = mdf['cost'].fillna(0.0) mdf['traded_price'] = mdf['open'] self.closed_trades = backtest.simdf_to_trades1(mdf, slippage=self.offset) return mdf, self.closed_trades
def dual_thrust_sim(mdf, config): close_daily = config['close_daily'] price_mode = config.get('mode', "HL") offset = config['offset'] k = config['param'][0] win = config['param'][1] multiplier = config['param'][2] f = config['param'][3] combo_signal = config['combo_signal'] proc_func = config['proc_func'] proc_args = config['proc_args'] chan_func = config['chan_func'] chan_high = eval(chan_func['high']['func']) chan_low = eval(chan_func['low']['func']) tcost = config['trans_cost'] unit = config['unit'] SL = config['stoploss'] min_rng = config['min_range'] chan = config['chan'] machan = config['machan'] xdf = proc_func(mdf, **proc_args) if win == -1: tr = pd.concat( [xdf.high - xdf.low, abs(xdf.close - xdf.close.shift(1))], join='outer', axis=1).max(axis=1) elif win == 0: tr = pd.concat( [(pd.rolling_max(xdf.high, 2) - pd.rolling_min(xdf.close, 2)) * multiplier, (pd.rolling_max(xdf.close, 2) - pd.rolling_min(xdf.low, 2)) * multiplier, xdf.high - xdf.close, xdf.close - xdf.low], join='outer', axis=1).max(axis=1) else: tr = pd.concat([ pd.rolling_max(xdf.high, win) - pd.rolling_min(xdf.close, win), pd.rolling_max(xdf.close, win) - pd.rolling_min(xdf.low, win) ], join='outer', axis=1).max(axis=1) xdf['tr'] = tr xdf['chan_h'] = chan_high(xdf, chan, **chan_func['high']['args']) xdf['chan_l'] = chan_low(xdf, chan, **chan_func['low']['args']) xdf['atr'] = dh.ATR(xdf, machan) xdf['ma'] = pd.rolling_mean(xdf.close, machan) xdf['rng'] = pd.DataFrame([min_rng * xdf['open'], k * xdf['tr'].shift(1)]).max() xdf['upper'] = xdf['open'] + xdf['rng'] * ( 1 + (xdf['open'] < xdf['ma'].shift(1)) * f) xdf['lower'] = xdf['open'] - xdf['rng'] * ( 1 + (xdf['open'] > xdf['ma'].shift(1)) * f) xdata = pd.concat([ xdf['upper'], xdf['lower'], xdf['chan_h'].shift(1), xdf['chan_l'].shift(1), xdf['open'] ], axis=1, keys=['upper', 'lower', 'chan_h', 'chan_l', 'xopen']).fillna(0) mdf = mdf.join(xdata, how='left').fillna(method='ffill') mdf['dt_signal'] = np.nan if price_mode == "HL": up_price = mdf['high'] dn_price = mdf['low'] elif price_mode == "TP": up_price = (mdf['high'] + mdf['low'] + mdf['close']) / 3.0 dn_price = up_price elif price_mode == "CL": up_price = mdf['close'] dn_price = mdf['close'] else: print "unsupported price mode" mdf.ix[up_price >= mdf['upper'], 'dt_signal'] = 1 mdf.ix[dn_price <= mdf['lower'], 'dt_signal'] = -1 if close_daily: mdf.ix[mdf['min_id'] >= config['exit_min'], 'dt_signal'] = 0 addon_signal = copy.deepcopy(mdf['dt_signal']) mdf['dt_signal'] = mdf['dt_signal'].fillna(method='ffill').fillna(0) mdf['chan_sig'] = np.nan if combo_signal: mdf.ix[(up_price >= mdf['chan_h']) & (addon_signal > 0), 'chan_sig'] = 1 mdf.ix[(dn_price <= mdf['chan_l']) & (addon_signal < 0), 'chan_sig'] = -1 else: mdf.ix[(mdf['high'] >= mdf['chan_h']), 'chan_sig'] = 1 mdf.ix[(mdf['low'] <= mdf['chan_l']), 'chan_sig'] = -1 mdf['chan_sig'] = mdf['chan_sig'].fillna(method='ffill').fillna(0) pos = mdf['dt_signal'] * ( unit[0] + (mdf['chan_sig'] * mdf['dt_signal'] > 0) * unit[1]) mdf['pos'] = pos.shift(1).fillna(0) mdf.ix[-3:, 'pos'] = 0 mdf['cost'] = abs(mdf['pos'] - mdf['pos'].shift(1)) * (offset + mdf['open'] * tcost) mdf['cost'] = mdf['cost'].fillna(0.0) mdf['traded_price'] = mdf['open'] closed_trades = backtest.simdf_to_trades1(mdf, slippage=offset) return (mdf, closed_trades)
def turtle_sim(ddf, mdf, config): marginrate = config['marginrate'] offset = config['offset'] start_equity = config['capital'] tcost = config['trans_cost'] signals = config['signals'] unit = config['unit'] NN = config['max_loss'] max_pos = config['max_pos'] start_idx = 0 ddf['ATR'] = pd.Series(dh.ATR(ddf, n=signals[0]).shift(1)) ddf['OL_1'] = pd.Series(dh.DONCH_H(ddf, signals[0]).shift(1)) ddf['OS_1'] = pd.Series(dh.DONCH_L(ddf, signals[0]).shift(1)) ddf['CL_1'] = pd.Series(dh.DONCH_L(ddf, signals[1]).shift(1)) ddf['CS_1'] = pd.Series(dh.DONCH_H(ddf, signals[1]).shift(1)) ddf['MA1'] = pd.Series(dh.MA(ddf, signals[2]).shift(1)) ddf['MA2'] = pd.Series(dh.MA(ddf, signals[3]).shift(1)) ll = mdf.shape[0] mdf['pos'] = pd.Series([0] * ll, index=mdf.index) mdf['cost'] = pd.Series([0] * ll, index=mdf.index) curr_pos = [] tradeid = 0 closed_trades = [] curr_atr = 0 for idx, dd in enumerate(mdf.index): mslice = mdf.ix[dd] d = dd.date() dslice = ddf.ix[d] tot_pos = sum([trade.pos for trade in curr_pos]) mdf.ix[dd, 'pos'] = tot_pos if (idx < start_idx) or np.isnan(dslice.ATR): continue if len(curr_pos) == 0 and idx < len(mdf.index) - NO_OPEN_POS_PROTECT: curr_atr = dslice.ATR direction = 0 up_MA = False down_MA = False if np.isnan(dslice.MA1) or (dslice.MA1 < dslice.MA2): up_MA = True if np.isnan(dslice.MA1) or (dslice.MA1 > dslice.MA2): down_MA = True if (mslice.close >= dslice.OL_1) and up_MA: direction = 1 elif (mslice.close <= dslice.OS_1) and down_MA: direction = -1 pos = direction * unit if direction != 0: new_pos = strat.TradePos([mslice.contract], [1], pos, mslice.close, mslice.close - direction * curr_atr * NN) tradeid += 1 new_pos.entry_tradeid = tradeid new_pos.open(mslice.close + direction * offset, dd) mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) curr_pos.append(new_pos) curr_atr = dslice.ATR elif (idx >= len(mdf.index) - NO_OPEN_POS_PROTECT): if len(curr_pos) > 0: for trade_pos in curr_pos: trade_pos.close( mslice.close - misc.sign(trade_pos.pos) * offset, dd) tradeid += 1 trade_pos.exit_tradeid = tradeid closed_trades.append(trade_pos) mdf.ix[dd, 'cost'] -= abs( trade_pos.pos) * (offset + mslice.close * tcost) curr_pos = [] else: direction = curr_pos[0].direction #exit position out of channel if (direction == 1 and mslice.close <= dslice.CL_1) or \ (direction == -1 and mslice.close >= dslice.CS_1): for trade_pos in curr_pos: trade_pos.close( mslice.close - misc.sign(trade_pos.pos) * offset, dd) tradeid += 1 trade_pos.exit_tradeid = tradeid closed_trades.append(trade_pos) mdf.ix[dd, 'cost'] -= abs( trade_pos.pos) * (offset + mslice.close * tcost) curr_pos = [] #stop loss position partially elif (curr_pos[-1].exit_target - mslice.close) * direction >= 0: for trade_pos in curr_pos: if (trade_pos.exit_target - mslice.close) * direction > 0: trade_pos.close( mslice.close - misc.sign(trade_pos.pos) * offset, dd) tradeid += 1 trade_pos.exit_tradeid = tradeid closed_trades.append(trade_pos) mdf.ix[dd, 'cost'] -= abs( trade_pos.pos) * (offset + mslice.close * tcost) curr_pos = [trade for trade in curr_pos if not trade.is_closed] #add positions elif (len(curr_pos) < max_pos) and (mslice.close - curr_pos[-1].entry_price ) * direction > curr_atr / max_pos * NN: for trade_pos in curr_pos: #trade.exit_target += curr_atr/max_pos*NN * direction trade_pos.exit_target = mslice.close - direction * curr_atr * NN new_pos = strat.TradePos( [mslice.contract], [1], direction * unit, mslice.close, mslice.close - direction * curr_atr * NN) tradeid += 1 new_pos.entry_tradeid = tradeid new_pos.open(mslice.close + direction * offset, dd) mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) curr_pos.append(new_pos) mdf.ix[dd, 'pos'] = sum([trade.pos for trade in curr_pos]) (res_pnl, ts) = backtest.get_pnl_stats(mdf, start_equity, marginrate, 'm') res_trade = backtest.get_trade_stats(closed_trades) res = dict(res_pnl.items() + res_trade.items()) return (res, closed_trades, ts)
def ttl_soup_sim(mdf, config): close_daily = config['close_daily'] marginrate = config['marginrate'] offset = config['offset'] pos_update = config['pos_update'] pos_class = config['pos_class'] pos_args = config['pos_args'] proc_func = config['proc_func'] proc_args = config['proc_args'] start_equity = config['capital'] tcost = config['trans_cost'] unit = config['unit'] SL = config['stoploss'] chan = config['chan'] exit_ratio = config['exit_ratio'] exit_chan = int(chan * exit_ratio) gap_win = config['gap_win'] no_trade_set = config['no_trade_set'] ll = mdf.shape[0] xdf = proc_func(mdf, **proc_args) donch_data = dh.DONCH_IDX(xdf, chan) hh_str = 'DONCH_H%s' % str(chan) hidx_str = 'DONIDX_H%s' % str(chan) ll_str = 'DONCH_L%s' % str(chan) lidx_str = 'DONIDX_L%s' % str(chan) xdf['exit_hh'] = pd.rolling_max(xdf.high, exit_chan) xdf['exit_ll'] = pd.rolling_min(xdf.low, exit_chan) xdf['ssetup'] = (xdf['close'] >= donch_data[hh_str].shift(1)) & ( donch_data[hidx_str] >= gap_win) xdf['bsetup'] = (xdf['close'] <= donch_data[ll_str].shift(1)) & ( donch_data[lidx_str] >= gap_win) atr = dh.ATR(xdf, chan) donch_data['prevhh'] = donch_data[hh_str].shift(1) donch_data['prevll'] = donch_data[ll_str].shift(1) xdata = pd.concat([ donch_data[hidx_str], donch_data[hh_str], donch_data[lidx_str], donch_data[ll_str], xdf['ssetup'], xdf['bsetup'], atr, donch_data[hh_str].shift(1), donch_data[ll_str].shift(1) ], axis=1, keys=[ 'hh_idx', 'hh', 'll_idx', 'll', 'ssetup', 'bsetup', 'ATR', 'prev_hh', 'prev_ll' ]).fillna(0) xdata = xdata.shift(1) mdf = mdf.join(xdata, how='left').fillna(method='ffill') mdf['pos'] = pd.Series([0] * ll, index=mdf.index) mdf['cost'] = pd.Series([0] * ll, index=mdf.index) curr_pos = [] closed_trades = [] end_d = mdf.index[-1].date #prev_d = start_d - datetime.timedelta(days=1) tradeid = 0 for idx, dd in enumerate(mdf.index): mslice = mdf.ix[dd] min_id = mslice.min_id min_cnt = (min_id - 300) / 100 * 60 + min_id % 100 + 1 if len(curr_pos) == 0: pos = 0 else: pos = curr_pos[0].pos mdf.ix[dd, 'pos'] = pos if (mslice.prev_hh == 0): continue if (min_id >= config['exit_min']) and (close_daily or (mslice.datetime.date == end_d)): if (pos != 0): curr_pos[0].close(mslice.close - misc.sign(pos) * offset, dd) tradeid += 1 curr_pos[0].exit_tradeid = tradeid closed_trades.append(curr_pos[0]) curr_pos = [] mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) pos = 0 elif min_id not in no_trade_set: if (pos != 0): exit_flag = False if ((pos > 0) and (mslice.close <= mslice.exit_ll)) or ( (pos < 0) and (mslice.close >= mslice.exit_hh)): exit_flag = True if exit_flag or curr_pos[0].check_exit(mslice.close, 0): curr_pos[0].close(mslice.close - offset * misc.sign(pos), dd) tradeid += 1 curr_pos[0].exit_tradeid = tradeid closed_trades.append(curr_pos[0]) curr_pos = [] mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) pos = 0 elif pos_update and (min_cnt % config['pos_freq'] == 0): curr_pos[0].update_price(mslice.close) if mslice.bsetup and (pos == 0) and (mslice.close >= mslice.prev_ll): new_pos = pos_class([mslice.contract], [1], unit, mslice.close + offset, mslice.low, **pos_args) tradeid += 1 new_pos.entry_tradeid = tradeid new_pos.open(mslice.close + offset, dd) curr_pos.append(new_pos) pos = unit mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) elif mslice.ssetup and (pos == 0) and mslice.close <= mslice.prev_hh: new_pos = pos_class([mslice.contract], [1], -unit, mslice.close - offset, mslice.high, **pos_args) tradeid += 1 new_pos.entry_tradeid = tradeid new_pos.open(mslice.close - offset, dd) curr_pos.append(new_pos) pos = -unit mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) mdf.ix[dd, 'pos'] = pos (res_pnl, ts) = backtest.get_pnl_stats(mdf, start_equity, marginrate, 'm') res_trade = backtest.get_trade_stats(closed_trades) res = dict(res_pnl.items() + res_trade.items()) return (res, closed_trades, ts)
def dual_thrust_sim(mdf, config): (pos_class, pos_args) = config['pos_param'] update_freq = config.get('update_freq', 0) ddf = config['ddf'] close_daily = config['close_daily'] offset = config['offset'] k = config['param'][0] win = config['param'][1] multiplier = config['param'][2] f = config['param'][3] chan = config['chan'] chan_func = config['chan_func'] use_chan = config['use_chan'] tcost = config['trans_cost'] unit = config['unit'] SL = config['stoploss'] min_rng = config['min_range'] if win == -1: tr = pd.concat([ddf.high - ddf.low, ddf.close - ddf.close.shift(1)], join='outer', axis=1).max(axis=1).shift(1) elif win == 0: tr = pd.concat( [(pd.rolling_max(ddf.high, 2) - pd.rolling_min(ddf.close, 2)) * multiplier, (pd.rolling_max(ddf.close, 2) - pd.rolling_min(ddf.low, 2)) * multiplier, ddf.high - ddf.close, ddf.close - ddf.low], join='outer', axis=1).max(axis=1).shift(1) else: tr = pd.concat([ pd.rolling_max(ddf.high, win) - pd.rolling_min(ddf.close, win), pd.rolling_max(ddf.close, win) - pd.rolling_min(ddf.low, win) ], join='outer', axis=1).max(axis=1).shift(1) ddf['TR'] = tr ddf['ATR'] = dh.ATR(ddf, win) if use_chan: ddf['CH_H'] = eval(chan_func['high']['func'])( ddf, chan, **chan_func['high']['args']).shift(1) ddf['CH_L'] = eval(chan_func['low']['func'])( ddf, chan, **chan_func['low']['args']).shift(1) if update_freq > 1: xdf = dh.conv_ohlc_freq(mdf, str(update_freq) + 'Min') else: xdf = mdf xdata = pd.concat([xdf['open'], xdf['high'], xdf['low'], xdf['close']], axis=1, keys=['xopen', 'xhigh', 'xlow', 'xclose']) mdf = mdf.join(xdata, how='left').fillna(method='ffill') ll = mdf.shape[0] mdf['pos'] = 0 mdf['cost'] = 0 start_d = ddf.index[0] end_d = mdf.index[-1].date() prev_d = start_d - datetime.timedelta(days=1) curr_pos = [] closed_trades = [] tradeid = 0 for idx, dd in enumerate(mdf.index): mslice = mdf.ix[dd] min_id = mslice.min_id d = mslice.date dslice = ddf.ix[d] if np.isnan(dslice.TR) or (mslice.close == 0): continue if use_chan and np.isnan(dslice.CH_H): continue if len(curr_pos) == 0: pos = 0 else: pos = curr_pos[0].pos mdf.ix[dd, 'pos'] = pos d_open = dslice.open if (d_open <= 0): continue rng = max(min_rng * d_open, k * dslice.TR) if (prev_d < d): d_open = mslice.open else: d_open = dslice.open prev_d = d buytrig = d_open + rng selltrig = d_open - rng stoploss = SL * dslice.ATR tmp_args = pos_args.copy() if 'reset_margin' in pos_args: tmp_args['reset_margin'] = dslice.ATR * pos_args['reset_margin'] stoploss = tmp_args['reset_margin'] * stoploss close_pos = False if pos != 0: if ((pos > 0) and (mslice.high < buytrig)) or ((pos < 0) and (mslice.low > selltrig)): close_pos = curr_pos[0].check_exit(mslice.close, stoploss) if (close_pos == False) and (update_freq > 0) and ( (mslice.min_id + 1) % update_freq == 0): xslice = conv_xslice(mslice) curr_pos[0].update_bar(xslice) if close_pos or ((mslice.min_id >= config['exit_min']) and (close_daily or (d == end_d))): curr_pos[0].close(mslice.close - misc.sign(pos) * offset, dd) tradeid += 1 curr_pos[0].exit_tradeid = tradeid closed_trades.append(curr_pos[0]) curr_pos = [] mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) pos = 0 if mslice.min_id < config['exit_min']: if (mslice.high >= buytrig) and (pos <= 0): if len(curr_pos) > 0: curr_pos[0].close(mslice.close + offset, dd) tradeid += 1 curr_pos[0].exit_tradeid = tradeid closed_trades.append(curr_pos[0]) curr_pos = [] mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) if (use_chan == False) or (use_chan and (mslice.high >= dslice.CH_H)): new_pos = eval(pos_class)([mslice.contract], [1], unit, mslice.close + offset, mslice.close + offset, **tmp_args) tradeid += 1 new_pos.entry_tradeid = tradeid new_pos.open(mslice.close + offset, dd) curr_pos.append(new_pos) pos = unit mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) elif (mslice.low <= selltrig) and (pos >= 0): if len(curr_pos) > 0: curr_pos[0].close(mslice.close - offset, dd) tradeid += 1 curr_pos[0].exit_tradeid = tradeid closed_trades.append(curr_pos[0]) curr_pos = [] mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) if (use_chan == False) or (use_chan and (mslice.low <= dslice.CH_L)): new_pos = eval(pos_class)([mslice.contract], [1], -unit, mslice.close - offset, mslice.close - offset, **tmp_args) tradeid += 1 new_pos.entry_tradeid = tradeid new_pos.open(mslice.close - offset, dd) curr_pos.append(new_pos) pos = -unit mdf.ix[dd, 'cost'] -= abs(pos) * (offset + mslice.close * tcost) mdf.ix[dd, 'pos'] = pos return (mdf, closed_trades)