def notify(self): if self._notify: logger.debug('{}, {}, {}, {} @ {}, lots: {}, execute: {}'.format( self.order_event.date, self.order_event.instrument, self.order_event.order_type, self.order_event.status, self.order_event.price, self.order_event.lots, self.order_event.execute_type))
def start(self): self.notify() if self.check_before(): self.change_status('SUBMITTED') self.notify() else: logger.debug('现金不够,本次交易取消')
def next(self): # if not self.skipped: # self.skipped = True # return self.__update_bar() market_event = Context().MarketEvent events.put(market_event(self)) logger.debug('---------------------feedbase.next--------------------')
def simple_moving_average(self, period, index=-1): close = self.get_preload(period, index, 'close') logger.debug('---close in sma---: {}'.format(close)) sma_close = talib.SMA(close, period) # 返回array,period个数前计算会得到nan,需处理 if np.isnan(sma_close[index]): raise Warning else: return sma_close[index]
def __combine_all_feed(self): """只运行一次,创建一个空Bar,然后将所有feed都整合到一起""" self.bar = Bar('') self.bar._initialize() for feed in self.feed_list: logger.debug('feed.bar.total_dict in main: {}'.format( feed.bar.total_dict)) self.bar._combine_all_feed(feed.bar.total_dict)
def __set_date(self): """将输入的日期转换成datetime对象""" if self.startdate: # logger.debug('self.startdate: {}'.format(self.startdate)) # 转换前 self.startdate = datetime.strptime(self.startdate, "%Y-%m-%d") logger.debug('self.startdate: {}'.format(self.startdate)) # 转换后 if self.enddate: # logger.debug('self.enddate: {}'.format(self.enddate)) # 转换前 self.enddate = datetime.strptime(self.enddate, "%Y-%m-%d") logger.debug("self.enddate: {}".format(self.enddate)) # 转换后
def test_get_trade_bars(self): op = analysis._true_func lenth_bar1 = analysis._get_trade_bars(ohlc_data, trade_log, op) self.assertEquals(lenth_bar1, [19, 9, 3, 2, 8, 5]) logger.info('trade_log: ', trade_log) trade_log1 = trade_log.drop([0, 1]).reset_index(drop=True) logger.debug('----------------------------------') logger.debug('trade_log: ', trade_log1) lenth_bar2 = analysis._get_trade_bars(ohlc_data, trade_log1, op) self.assertEquals(lenth_bar2, [9, 3, 2, 8, 5])
def _update(): bar = next(self._iteration_buffer) # 每次读取一组数据 logger.debug('bar: {}'.format(bar)) bar['time'] = self.__set_bar_date(bar) for i in bar: try: bar[i] = float(bar[i]) except ValueError: pass return bar
def update_cash(self, fill_event): """ 更新现金,现金 = 资产余额 - 本次已缴纳的保证金 """ cur_equity = self.equity[-1] # total_margin = self.margin.total() margin = self.margin[-1] # cash = cur_equity - total_margin cash = cur_equity - margin logger.debug('fill_event.date, cash: {} {}'.format(fill_event.date, cash)) logger.debug('cash in date: {} {}'.format(cash, fill_event.date)) self.cash.add(fill_event.date, cash)
def get_basic_data(self, period, ohlc='close'): """ 获取基础的数据,如 open, high, low, close 等, 最后取得一个列表,长度为period, period为 1 表示当前bar的数据 """ if len(self.bar_list) < period: raise IndexError data_list = self.bar_list[-period:] logger.debug('len(data_list): {}'.format(len(data_list))) data = [i[ohlc] for i in data_list] return np.array(data)
def __update_time_index(self): """每次更新行情后,根据新行情更新仓位、现金、保证金等账户基本信息""" self.fill.update_time_index(self.feed_list) logger.debug('len(self.feed_list) in main: {}'.format( len(self.feed_list))) logger.debug('self.feed_list in main: {}'.format(self.feed_list)) date_dict = {} if len(self.feed_list) > 1: for index, feed in enumerate(self.feed_list): date_dict[str(index)] = feed.cur_bar.cur_date if self.feed_list.count(feed) > 1: raise SyntaxError('行情中出现了相同的日期,数据有误')
def set_dataseries_instrument(self, instrument): self.position.set_instrument(instrument) self.margin.set_instrument(instrument) self.commission.set_instrument(instrument) self.long_commission.set_instrument(instrument) self.short_commission.set_instrument(instrument) self.avg_price.set_instrument(instrument) self.realized_gain_and_loss.set_instrument(instrument) self.long_realized_gain_and_loss.set_instrument(instrument) self.short_realized_gain_and_loss.set_instrument(instrument) logger.debug('realized_gain_and_loss in set_dataseries_instrument: {}'.format(self.realized_gain_and_loss.list)) self.unrealized_gain_and_loss.set_instrument(instrument)
def __init__(self, feed): self.type = 'Market' self.feed = feed self.instrument = feed.instrument self.cur_bar = feed.cur_bar self.bar = feed.bar # logger.debug('self.bar.open[:] in event: {}'.format(self.bar.open[:])) self.per_comm = feed.per_comm self.per_margin = feed.per_margin self.units = feed.units self.execute_mode = feed.execute_mode self.slippage = feed.slippage logger.debug('---slippage in event---:{}'.format(self.slippage))
def update_position(self, fill_event): """ 更新仓位 如果最后一个仓位的执行类型为LIMIT或STOP,仓位不变,更新时间; 否则,仓位更新,加上变更量,并更新时间 """ last_position = self.position[-1] if fill_event.execute_type in ['LIMIT', 'STOP']: position = last_position else: position = int(last_position + fill_event.lots * fill_event.direction) logger.debug('position in date: {} {}'.format(position, fill_event.date)) self.position.add(fill_event.date, position)
def add_new_bar(self, new_bar): """不断更新当前行情数据""" bar_date = datetime.strptime(new_bar['time'], '%Y/%m/%d') logger.debug('---bar_date in barbase---: {}'.format(bar_date)) # bar_list_length = len(self._cur_bar_list) logger.debug('self._cur_bar_list in barbase: {}'.format( self._cur_bar_list)) # if bar_list_length == 2: # # self._cur_bar_list.pop(0) # self._cur_bar_list = [] # elif bar_list_length == 1: # if new_bar['time'] == self._cur_bar_list[0]['time']: # self._cur_bar_list = [] # self._cur_bar_list.append(new_bar) self._cur_bar_list[0] = new_bar
def get_ready(self): """准备数据,设置参数""" feed_list = self.context.feed_list strategy_list = self.context.strategy portfolio = Portfolio self.set_backtest(feed_list, strategy_list, portfolio) commission = self.context.commission margin = self.context.margin units = self.context.units lots = self.context.lots slippage = self.context.slippage logger.debug('---slippage in main---:{}'.format(slippage)) instrument = self.context.instrument self.set_commission(commission, margin, units, lots, slippage, instrument) self.set_cash(self.context.initial_cash)
def dict_to_table(result_dict): # 计算传入字典的key的长度的最大值作为key展示列的宽度 col_width_keys = max([len(key) for key in result_dict.keys()]) # 计算传入字典的value的长度的最大值作为value展示列的宽度 col_width_values = max([len(str(value)) for value in result_dict.values()]) # 列表的头部和尾部用#---#展示 header_footer = ('#--' + (col_width_keys + col_width_values + 2) * '-' + '#') data_list = [] for key, value in result_dict.items(): data_list.append('| ' + '.......'.join([ '{:{}}'.format(key, col_width_keys), '{:{}}'.format( value, col_width_values) ]) + ' \n') table_str = '{}\n{}{}'.format(header_footer, ''.join(data_list), header_footer) logger.debug('table_str: {}'.format(table_str)) return table_str
def max_high(self, period: int, index=0): """ 获取 period 个周期内的最高价, 1 表示当前周期,2 表示上一周期到当前周期,类推, index 为 0 表示 period - 1 日前到当日的最高价,即 period 个周期内的最高价, index 为 1 表示 period 日前到昨日的最高价,也是 period 个周期内的最高价, index 是为比较函数 cross_up 和 cross_down 设置的 """ logger.debug('period, index : {} {}'.format(period, index)) if index not in [0, 1]: logger.warning( 'index must be 0 or 1, please choose the right index') logger.info('index set to 0 by default') index = 0 if not index: high = self.get_basic_data(period, ohlc='high') if index == 1: high = self.get_basic_data(period + 1, ohlc='high')[:-1] return max(high)
def preload(self): self.set_iteration_buffer(self.load_data()) def _update(): bar = next(self._iteration_buffer) # 每次读取一组数据 logger.debug('bar: {}'.format(bar)) bar['time'] = self.__set_bar_date(bar) for i in bar: try: bar[i] = float(bar[i]) except ValueError: pass return bar try: bar = _update() self.preload_bar_list.append(bar) # 添加第一个数据 self.preload_bar_list.append(bar) # 添加第一个数据 logger.debug('barr: {}'.format(bar)) new_bar_date = datetime.strptime(bar['time'], self.date_format) if self.startdate: while new_bar_date < self.startdate: bar = _update() self.preload_bar_list.append(bar) # else: # self.preload_bar_list.pop(-1) # 经过验证bug检查的,最后删除掉一个重复(暂未证实) elif self.startdate is None: pass else: raise SyntaxError("语法错误") except IndexError: pass except StopIteration: logger.debug("不可能的") self.preload_bar_list.reverse()
def __initialization(self): """对所有 feed 和 fill 内各项数据进行初始化""" logger.debug('feed_list in main initialization: {}'.format( self.feed_list)) for feed in self.feed_list: feed.load_once() instrument = feed.instrument self.fill.position.initialize(instrument, 0) self.fill.margin.initialize(instrument, 0) self.fill.commission.initialize(instrument, 0) self.fill.long_commission.initialize(instrument, 0) self.fill.short_commission.initialize(instrument, 0) self.fill.avg_price.initialize(instrument, 0) self.fill.unrealized_gain_and_loss.initialize(instrument, 0) self.fill.realized_gain_and_loss.initialize(instrument, 0) self.fill.long_realized_gain_and_loss.initialize(instrument, 0) self.fill.short_realized_gain_and_loss.initialize(instrument, 0) self.fill.cash.initialize('all', self.fill.initial_cash) self.fill.equity.initialize('all', self.fill.initial_cash) self.__combine_all_feed()
def update_avg_price(self, fill_event): """ 更新均价 """ avg_price = self.avg_price[-1] last = self.position[-2] # 上一个仓位 cur = self.position[-1] # 刚刚更新的仓位 if cur == 0: # 平仓了, avg_price = 0 else: # 未平仓 if fill_event.execute_type in ['LIMIT', 'STOP']: pass # 上一次仓位为0,本次开仓,均价即为本次执行价 elif last == 0: avg_price = fill_event.price # 上一次仓位为多头,即买入了仓位,当最新仓位仍为多头时,均价 = 总成交额 / 最新仓位 # 当最新仓位为负时,即卖出平仓后又卖出开仓了,均价即为卖出的执行价 elif last > 0: if fill_event.order_type == 'BUY': avg_price = (last * avg_price + fill_event.units * fill_event.price) / cur if fill_event.order_type == 'SELL': if cur > 0: avg_price = (last * avg_price - fill_event.units * fill_event.price) / cur elif cur < 0: avg_price = fill_event.price # 上一次仓位为空头时,当最新仓位变为多头时,即买入平仓后又买入开仓了, # 均价即为买入的执行价;否则,均价 = 总成交额 / 最新仓位 elif last < 0: if fill_event.order_type == 'BUY': if cur > 0: avg_price = fill_event.price elif cur < 0: avg_price = (-last * avg_price - fill_event.units * fill_event.price) / cur elif fill_event.order_type == 'SELL': avg_price = (-last * avg_price + fill_event.units * fill_event.price) / cur logger.debug('avg_price in date in update_avg_price: {} {}'.format(avg_price, fill_event.date)) self.avg_price.add(fill_event.date, avg_price)
def set_commission(self, commission, margin, units, lots, slippage, instrument=None): """ 设置手续费、保证金、合约单位及合约品种等参数 commission:手续费,0.0003表示每手收取0.03%的手续费 margin:保证金比例,通常为0.05-0.15 units:合约单位,一般为吨/手 lots:下单手数 """ for feed in self.feed_list: if feed.instrument == instrument or instrument is None: feed.set_per_comm(commission) feed.set_per_margin(margin) feed.set_units(units) feed.set_lots(lots) feed.set_slippage(slippage) logger.debug('---slippage in feed main---:{}'.format( feed.slippage))
def __init__(self): # 设置默认初始资金,如果用户不更改,则用这个资金进行回测 self.initial_cash = 100000 self.position = dataseries.PositionSeries() # 仓位 self.margin = dataseries.MarginSeries() # 保证金 self.avg_price = dataseries.AvgPriceSeries() # 均价 self.commission = dataseries.CommissionSeries() # 手续费 self.long_commission = dataseries.LongCommissionSeries() # 多头手续费 self.short_commission = dataseries.ShortCommissionSeries() # 空头手续费 self.cash = dataseries.CashSeries() # 现金 # 平仓盈亏 self.realized_gain_and_loss = dataseries.RealizedGainAndLossSeries() self.long_realized_gain_and_loss = dataseries.LongRealizedGainAndLossSeries() self.short_realized_gain_and_loss = dataseries.ShortRealizedGainAndLossSeries() logger.debug('realized_gain_and_loss in init: {}'.format(self.realized_gain_and_loss)) # 浮动盈亏 self.unrealized_gain_and_loss = dataseries.UnrealizedGainAndLossSeries() self.equity = dataseries.EquitySeries() # 余额 self._order_list = [] self._trade_list = [] self._completed_list = []
def __set_order_data(self): """初始化各项基本信息,并判断指令种类execute_type""" self._instrument = self._order_data.instrument self._direction = self._order_data.direction self._date = self._order_data.date self._lots = self._order_data.lots self._price = self._order_data.price logger.debug('self._price: {}'.format(self._price)) self._take_profit = self._order_data.take_profit self._stop_loss = self._order_data.stop_loss self._trailing_stop = self._order_data.trailing_stop self._trailing_stop_calc = self._order_data.trailing_stop_calc execute_mode_price = self._order_data.execute_mode_price logger.debug('execute_mode_price: {}'.format(execute_mode_price)) if self._execute_type == 'CLOSE_ALL': return elif self._price > execute_mode_price: self._execute_type = 'STOP' if self._order_type == 'BUY' else 'LIMIT' elif self._price < execute_mode_price: self._execute_type = 'LIMIT' if self._order_type == 'BUY' else 'STOP' elif self._price == execute_mode_price: self._execute_type = 'MARKET'
def close(self, period=1) -> list: """ close(1)[0] 表示当前周期的close close(2)[0] 表示上一周期的close """ close = self.get_basic_data(period, ohlc='close') logger.debug('type(close) :{}'.format(type(close))) logger.debug('close: {}'.format(close)) logger.debug('close[0]: {}'.format(close[0])) return close
def add(self, date, value): logger.debug('self.old_date, date in add dataseries: {} {}'.format( self.old_date, date)) if self.old_date != date: if self._dict[self._instrument][0]['date'] == 'start': self._dict[self._instrument] = [] self._dict[self._instrument].append({ 'date': date, self._name: value }) # logger.debug('self._dict in dataseries: {}'.format(self._dict)) self.old_date = date else: logger.debug('self.old_date, date in add dataseries: {} {}'.format( self.old_date, date)) logger.debug('date in dataseries.add: {}'.format(date)) logger.debug('value in add: {}'.format(value)) self._dict[self._instrument][-1][self._name] = value
def get_re_profit(trade_lots, trade_code): """计算平仓盈亏和手续费""" if trade_code == 1: # 做多 buy_price = f.price # + 0.2 * 1 sell_price = i.price # - 0.2 * 1 elif trade_code == 0: # 做空 buy_price = i.price # + 0.2 * 1 sell_price = f.price # - 0.2 * 1 # re_profit = np.round((f.price - i.price) * trade_lots * f.units * i.direction, 2) re_profit = np.round((sell_price - buy_price) * trade_lots * f.units, 2) logger.debug('re_profit: {} {} {} {} {} {}'.format( re_profit, f.price, i.price, trade_lots, f.units, i. direction)) commission_ = np.round(f.units * f.price * f.per_comm * f.lots, 2) # 平仓手续费 # 单笔交易的手续费(即开仓手续费和平仓手续费之和) commission = commission_ + np.round(i.units * i.price * f.per_comm * i.lots, 2) re_profit_list.append(re_profit) # 加入累计的盈亏 # self.realized_gain_and_loss.add(f.date, sum(re_profit_list)) self.realized_gain_and_loss.add(f.date, re_profit) # 记录每次盈亏 logger.debug('---self.realized_gain_and_loss.add---: {} {}'.format( self.realized_gain_and_loss.list, self.realized_gain_and_loss.dict)) if i.direction > 0: # 多头平仓盈亏(含手续费) self.realized_gain_and_loss.long_poisition_re_profit.append(re_profit - commission) self.long_realized_gain_and_loss.add(f.date, re_profit - commission) self.long_commission.add(f.date, commission) else: # 空头平仓盈亏(含手续费) self.realized_gain_and_loss.short_position_re_profit.append(re_profit - commission) self.short_realized_gain_and_loss.add(f.date, re_profit - commission) self.short_commission.add(f.date, commission) logger.debug('self.realized_gain_and_loss in backtestfill: {}'.format( self.realized_gain_and_loss)) logger.debug('self.realized_gain_and_loss.date in backtestfill: {}'.format( self.realized_gain_and_loss.date)) if len(self.realized_gain_and_loss.date) > 1: if self.realized_gain_and_loss.date[-2] is f.date: new_realized_g_l = ( self.realized_gain_and_loss[-1] + self.realized_gain_and_loss[-2]) # self.realized_gain_and_loss.update_cur(new_realized_g_l) self.realized_gain_and_loss.add(date, new_realized_g_l) self.realized_gain_and_loss.del_last()
def run(self): """主循环""" self.__initialization() while True: try: event = events.get(False) # 当 queue 为空时,raise queue.Empty logger.debug('events.qsize(): {}'.format(events.qsize())) except queue.Empty: self.__load_all_feed() # 加载新行情 logger.debug('self.__check_backtest_finished(): {}'.format( self.__check_backtest_finished())) if not self.__check_backtest_finished(): # cur_bar中数据不足两条,不开始计算 if len(self.feed_list[-1].cur_bar._cur_bar_list) < 1: # logger.debug('events.qsize(): {}'.format(events.qsize())) continue else: self.__update_time_index() # 更新基本信息 logger.debug( 'self.feed_list[-1].cur_bar.cur_date: {}'.format( self.feed_list[-1].cur_bar.cur_date)) self.__check_pending_order() # 检查订单是否成交 else: if event.type == 'Market': self.context.market_event.append(event) self.__pass_to_market(event) # 传递账户基本信息 for strategy in self.strategy_list: strategy(event).run_strategy() elif event.type == 'Signal': self.context.signal_event.append(event) self.portfolio.run_portfolio(event) elif event.type == 'Order': self.context.order_event.append(event) self.broker.run_broker(event) elif event.type == 'Fill': self.context.fill_event.append(event) self.fill.run_fill(event) if self.__check_backtest_finished(): self.__output_summary() break
def update_margin(self, fill_event): """ 更新保证金 根据position确定,多头时保证金为正,空头时保证金为负 暂时只考虑了期货,以当条bar的收盘价为当日结算价 当日交易保证金 = 持仓均价 × 当日结束交易后的持仓总量 × 交易保证金比例 """ margin = 0 cur_position = self.position[-1] avg_price = self.avg_price[-1] if fill_event.execute_type in ['LIMIT', 'STOP']: pass else: cur_close = fill_event.price logger.debug('cur_close 结算价 in date:{} in {}'.format(cur_close, fill_event.date)) logger.debug('fill_event.execute_type in date:{} in {}'.format(fill_event.execute_type, fill_event.date)) # margin = fill_event.per_margin * ( # cur_position * fill_event.mult * cur_close) logger.debug('fill_event.per_margin * cur_position * avg_price * fill_event.units: {}, {}, {}, {}'.format( fill_event.per_margin, cur_position, avg_price, fill_event.units)) # 保证金不能为负,用持仓均价计算,不用当日结算价计算,保留两位小数 margin = np.round(fill_event.per_margin * fill_event.units * avg_price * fill_event.lots, 2) logger.debug('margin in date: {} {}'.format(margin, fill_event.date)) self.margin.add(fill_event.date, margin)
def find(self, dic): for key, value in dic.items(): if key is 'func': pass if key is 'arg': # value 是一个列表 # logger.debug('lenth of value: {}'.format(value)) for i in value: logger.debug('i:', i) if isinstance(i, int) or isinstance(i, float): self._period.append(i) logger.debug('append: {}'.format(i)) # continue elif isinstance(i, dict): self.find(i) # 只要是字典,一定会返回一个值,否则报错 # continue else: self._period.append(0) logger.debug('else: {}'.format(i)) return max(self._period)