def handle_bar(self, event_market): print("-- this is handle_bar() method @ {0}".format(event_market.dt)) # 取当前bar的持仓情况 available_position_dict = {} for position in self.context.bar_position_data_list: available_position_dict[position.symbol] = position # 当前bar的具体时间,时间str转换成int,方便后面取数据时过滤 current_date_int = date_str_to_int(event_market.dt) cur_start_date = to_datetime(event_market.dt) - Timedelta(days=40) cur_start_date_int = date_str_to_int(str(cur_start_date)[:10]) # 循环遍历股票池 for stock in self.universe: # 取当前股票的数据 close_price = self.get_data.get_market_data( self.context.daily_data, stock_code=[stock], field=["close"], start=cur_start_date_int, end=current_date_int) close_array = array(close_price) if len(close_array) > 0: # 利用talib计算MA ma5 = talib.MA(array(close_price), timeperiod=5) ma20 = talib.MA(array(close_price), timeperiod=20) # 过滤因为停牌没有数据 if current_date_int in close_price.keys() and (not isnull( ma5[-1])) and (not isnull(ma20[-1])): # 如果5日均线突破20日均线,并且没有持仓,则买入这只股票100股,委托价为当前bar的收盘价 if ma5[-1] > ma20[ -1] and stock not in available_position_dict.keys( ): comments = "限价买入" self.buy(event_market.dt, self.account[0]['name'], stock, close_array[-1], 100, False, comments) print("发出委托买入股票 {0} {1} 股,委托价为 {2},资金账号为 {3} ".format( stock, 100, close_array[-1], self.account[0]['name'])) # 如果20日均线突破5日均线,并且有持仓,则卖出这只股票100股,委托价为当前bar的收盘价 elif ma5[-1] < ma20[ -1] and stock in available_position_dict.keys(): pos_abs = abs(available_position_dict[stock].volume) comments = "限价卖出" self.sell(event_market.dt, self.account[0]['name'], stock, close_array[-1], pos_abs, False, comments) print("发出委托卖出股票 {0} {1} 股,委托价为 {2},资金账号为 {3} ".format( stock, pos_abs, close_array[-1], self.account[0]['name']))
def update_bar(self, event_market): """新出现市场事件 event_bar 时的监听/回调函数,在回测模式下,模拟委托单的撮合动作""" print("this is update_bar() @ {0}".format(event_market.dt)) self.bar_index += 1 if self.run_mode == RunMode_BACKTESTING: # 回测模式下的报单反馈 # 处理股票今日持仓的冻结数量(股票当日买入不能卖出) self.update_position_frozen(event_market.dt) # 取最新的市场数据 cur_mkt_data = {'low': {}, 'high': {}, 'open': {}} for uii in self.universe: cur_date = date_str_to_int(event_market.dt) cur_mkt_data['low'][uii] = self.context.daily_data['low'].loc[ uii][cur_date] cur_mkt_data['high'][uii] = self.context.daily_data[ 'high'].loc[uii][cur_date] cur_mkt_data['open'][uii] = self.context.daily_data[ 'open'].loc[uii][cur_date] self.cross_limit_order(event_market, cur_mkt_data) # 处理委托时间早于当前bar的未成交限价单 self.cross_stop_order(event_market, cur_mkt_data) # 处理委托时间早于当前bar的未成交止损单 else: # live模式下的报单反馈(未完成) pass self.handle_bar(event_market) self.update_bar_info(event_market)
def init_strategy(self): # 父类中有定义,但是没有实现,此处实现 # 设置运行模式,回测或者交易 self.run_mode = RunMode_BACKTESTING self.start = date_str_to_int("20050104") # 设置回测起止时间 self.end = date_str_to_int("20060222") self.interval = "d" # 设置运行周期 self.account = {"acc0": 1000000, "acc1": 1000} # 设置回测资金账号及初始资金量 self.benchmark = "000300.SH" # 设置回测基准 self.rights_adjustment = RightsAdjustment_NONE # 设置复权方式 self.get_data = GetMongoData() # 设置股票池 self.universe = ['000001.SZ', '000002.SZ', '600000.SH', '600001.SH'] # self.context.logger.info(self.universe) # 回测滑点设置 self.set_slippage_type = 'FIX'
def init_strategy(self): # 父类中有定义,但是没有实现,此处实现 # 设置运行模式,回测或者交易 self.gateway = 'ctp' self.run_mode = RunMode_BACKTESTING self.start = date_str_to_int("20050104") # 设置回测起止时间 self.end = date_str_to_int("20060222") # self.interval = "d" # 设置运行周期 self.account = [{ 'name': 'acc0', 'equity': 1000000 }, { 'name': 'acc1', 'equity': 1000 }] # 设置资金账号及初始资金量 self.benchmark = "000300.SH" # 设置回测基准 # self.rights_adjustment = RightsAdjustment_NONE # 设置复权方式 # 设置股票池 # self.universe = ['000001.SZ', '000002.SZ', '600000.SH', '600001.SH'] self.universe = ['000001.SZ', '600000.SH'] # 组合中所有合约的持仓量初始化 self.pos = {} for ss in self.universe: self.pos[ss] = 0 # 回测滑点设置 self.set_slippage(stock_type=Product_STOCK, slippage_type=SLIPPAGE_FIX, value=0.01) # 回测股票手续费和印花税,卖出印花税,千分之一;开仓手续费,万分之三;平仓手续费,万分之三,最低手续费,5元 # 沪市,卖出有万分之二的过户费,加入到卖出手续费 self.set_commission(stock_type=Product_STOCK_SH, tax=0.001, open_commission=0.0003, close_commission=0.0003, close_today_commission=0, min_commission=5) # 深市不加过户费 self.set_commission(stock_type=Product_STOCK_SZ, tax=0.001, open_commission=0.0003, close_commission=0.0005, close_today_commission=0, min_commission=5)
def init_strategy(self, log_full_path: list, color_log=True, start_str='20180101', end_str='20200222'): # 确定参数值 self.gateway = 'ctp' self.run_mode = RunMode.BACKTESTING self.start = date_str_to_int(start_str) self.end = date_str_to_int(end_str) self.account = [{'name': 'acc0', 'equity': 200000}] self.benchmark = "000300.SH" # self.rights_adjustment = RightsAdjustment.NONE # 只在画 K 线时有效,对策略运行无影响 # 动态股票池(沪深300指数成分股) self.is_universe_dynamic = '000300.SH' # 固定股票池 # self.is_universe_dynamic = False # self.universe = ['000562.SZ', '600710.SH', '600757.SH', '600761.SH'] # 设置回测滑点 self.set_slippage(symbol_type=Product.STOCK.value, slippage_type=Slippage.FIX, value=0.01) # 设置沪市交易成本 self.set_commission(symbol_type=Product.STOCK_SH, tax=0.001, open_commission=0.0003, close_commission=0.0005, close_today_commission=0, min_commission=5) # 设置深市交易成本 self.set_commission(symbol_type=Product.STOCK_SZ, tax=0.001, open_commission=0.0003, close_commission=0.0003, close_today_commission=0, min_commission=5) if not color_log: self.context.logger = Logger(log_full_path[0]) else: self.context.logger = ColorLogger(log_full_path[0] + log_full_path[1]) # color log
def get_all_market_data(self, stock_code=None, field=None, start="", end="", interval=Interval.DAILY): """从mongodb取数据""" if interval == Interval.DAILY: db_name = MongoDbName.MARKET_DATA_DAILY.value if isinstance(start, str): start = date_str_to_int(start) end = date_str_to_int(end) values = [] colum = {"_id": 0, "timetag": 1} for i in field: colum[i] = 1 for stock in stock_code: self.conn.check_connected() stock_market_data = self.conn.select_colum( db_name=db_name, table=stock, value={"timetag": { "$gte": start, "$lte": end }}, colum=colum) stock_market_data_list = list(stock_market_data) if stock_market_data_list: df = pd.DataFrame(stock_market_data_list) values.append( pd.DataFrame(df[field].values, index=df['timetag'], columns=field)) market_data = pd.concat(values, keys=stock_code) else: market_data = None return market_data
def handle_bar(self, bar): self.context.logger.info("\n遇到 EventMarket 类事件,回调函数 MaStrategy.handle_bar 按最新市场数据计算一次") # 取当前bar的持仓情况 available_position_dict = {} for position in Context.bar_position_data_list: available_position_dict[position.instrument + "." + position.exchange] = position.position # 当前bar的具体时间 current_date = timestamp_to_datetime(timestamp=self.timestamp, format="%Y-%m-%d") # 时间str转换成int,方便后面取数据时过滤 current_date_int = date_str_to_int(current_date) print("开始日期 {0}, 当前日期 {1}".format(self.start, current_date)) # 循环遍历股票池 for stock in self.universe: # 取当前股票的收盘价 close_price = self.get_data.get_market_data(Context.daily_data, stock_code=[stock], field=["close"], end=current_date) close_array = array(close_price) if len(close_array) > 0: # 利用talib计算MA ma5 = talib.MA(array(close_price), timeperiod=5) ma20 = talib.MA(array(close_price), timeperiod=20) # print(type(close_price.keys())) # 计算交易信号是否被触发 if current_date_int in close_price.keys(): # 如果5日均线突破20日均线,并且没有持仓,则买入这只股票100股,委托价为当前bar的收盘价 if ma5[-1] > ma20[-1] and stock not in available_position_dict.keys(): order_data = OrderData(symbol=stock, exchange=, orderid=, type=OrderType_LIMIT, direction=Direction_LONG, offset=Offset_OPEN, price=close_price.loc[], volumne=100.0, ) self.handle_order(order_data) print("买入股票 {0} {1} 股,委托价为 {2},资金账号为 {3} ...". format(stock, 100, close_price[current_date_int], self.account[list(self.account.keys())[0]])) # 如果20日均线突破5日均线,并且有持仓,则卖出这只股票100股,委托价为当前bar的收盘价 elif ma5[-1] < ma20[-1] and stock in available_position_dict.keys(): order_data = OrderData(symbol=stock, exchange=, orderid=, type=OrderType_LIMIT, direction=Direction_LONG, offset=Offset_OPEN, price=close_price.loc[], volumne=100.0 ) self.handle_order(order_data) print("卖出股票 {0} {1} 股,委托价为 {2},资金账号为 {3} ...". format(stock, 100, close_price[current_date_int], self.account[list(self.account.keys())[0]]))
def get_market_data(self, market_data, all_symbol_code=None, field=None, start="", end="", count=-1): """ 从 dataframe 解析数据成最终的数据格式 因为停牌或者其他原因取不到数据的,1 2 3 返回的是-1,其他返回的是 pandas 的空或者 NaN,所以可以使用 >0判断是否取到值 """ if start != "": if isinstance(start, str): start = date_str_to_int(start) else: start = 0 if end != "": if isinstance(end, str): end = date_str_to_int(end) else: end = 0 # (1)代码-1,字段-1,时间-1, return float if len(all_symbol_code) == 1 and len(field) == 1 and ( start == end) and count == -1: try: return market_data[field[0]].loc[all_symbol_code[0], end] # 停牌或者其他情情况取不到数据的返回-1 except BaseException: return -1 # (2)代码-n,字段-1,时间-1, return Series elif len(all_symbol_code) > 1 and len(field) == 1 and ( start == end) and count == -1: result_dict = {} for stock in all_symbol_code: try: result_dict[stock] = market_data[field[0]].loc[stock, end] except BaseException: result_dict[stock] = -1 return pd.Series(result_dict) # (3)代码-1,字段-n,时间-1, return Series elif len(all_symbol_code) == 1 and len(field) > 1 and ( start == end) and count == -1: result_dict = {} for field_one in field: try: result_dict[field_one] = market_data[field_one].loc[ all_symbol_code[0], end] except BaseException: result_dict[field_one] = -1 return pd.Series(result_dict) # (4)代码-1,字段-1,时间-n, return Series elif len(all_symbol_code) == 1 and len(field) == 1 and ( start != end) and count == -1: try: series = market_data[field[0]].loc[all_symbol_code[0]] except KeyError: return pd.Series() series = series[series.index >= start] series = series[series.index <= end] return series # (5)代码-n,字段-1,时间-n, return dataframe 行-timestamp,列-代码 elif len(all_symbol_code) > 1 and len(field) == 1 and ( start != end) and count == -1: result_dict = {} for stock in all_symbol_code: index = market_data.loc[stock].index index = index[index <= end] index = index[index >= start] result_dict[stock] = market_data[field[0]].loc[stock][index] return pd.DataFrame(result_dict) # (6)代码-n,字段-n,时间-1, return dataframe 行-字段,列-代码 elif len(all_symbol_code) > 1 and len(field) > 1 and ( start == end) and count == -1: result_dict = {} for stock in all_symbol_code: try: result_dict[stock] = market_data.loc[stock, end] except BaseException: result_dict[stock] = pd.Series() return pd.DataFrame(result_dict).loc[field] # (7)代码-1,字段-n,时间-n, return dataframe 行-timestamp,列-字段 elif len(all_symbol_code) == 1 and len(field) > 1 and ( start != end) and count == -1: index = market_data.loc[all_symbol_code[0]].index index = index[index <= end] index = index[index >= start] return market_data.ix[all_symbol_code[0]][field].loc[index] # 代码-n,字段-n,时间-n, return dataframe 行-代码-timestamp(多层索引),列-字段 else: result_dict = {} for stock in all_symbol_code: index = market_data.loc[stock].index index = index[index <= end] index = index[index >= start] result_dict[stock] = market_data.loc[stock][field].loc[index] return pd.concat(result_dict, keys=all_symbol_code)
def handle_bar(self, event_bar): self.context.logger.info("handle_bar() @ {0}".format(event_bar.dt)) self.activate_trade_signal = False available_position_dict = self.context.bar_position_data_dict account_data = self.context.current_account_data current_date_int = date_str_to_int(event_bar.dt) cur_start_date = to_datetime(event_bar.dt) - Timedelta(days=60) cur_start_date_int = date_str_to_int(str(cur_start_date)[:10]) # 循环处理旧持仓 for pos_symbol in available_position_dict.keys(): close_price = self.get_data.get_market_data( self.context.daily_data, all_symbol_code=[pos_symbol], field=["close"], start=cur_start_date_int, end=current_date_int) if len(close_price) > 0: # 指标计算 ma5 = talib.MA(array(close_price), timeperiod=5) ma20 = talib.MA(array(close_price), timeperiod=20) if current_date_int in close_price.keys() and (not isnull( ma5[-1])) and (not isnull(ma20[-1])): if ma5[-1] < ma20[-1]: if available_position_dict[ pos_symbol].volume > 0 and available_position_dict[ pos_symbol].direction == Direction.LONG: self.activate_trade_signal = True pos_abs = abs( available_position_dict[pos_symbol].volume) comments = "限价卖出" self.sell(event_bar.dt, self.account[0]['name'], pos_symbol, close_price.iloc[-1], pos_abs, False, comments) self.context.logger.info( "-- 资金账号 {3} 发出委托卖出 {0} {1} 股,委托价为 {2}".format( pos_symbol, pos_abs, close_price.iloc[-1], self.account[0]['name'])) # 循环遍历股票池,看是否有新信号 for symbol in self.universe: # 取当前股票的数据 close_price = self.get_data.get_market_data( self.context.daily_data, all_symbol_code=[symbol], field=["close"], start=cur_start_date_int, end=current_date_int) # close_array = array(close_price) if len(close_price) > 0: # 指标计算 ma5 = talib.MA(array(close_price), timeperiod=5) ma20 = talib.MA(array(close_price), timeperiod=20) # 数据有效时才判断 if current_date_int in close_price.keys() and (not isnull( ma5[-1])) and (not isnull(ma20[-1])): # 如果5日均线突破20日均线,并且没有持仓,则买入这只股票100股,委托价为当前bar的收盘价 if ma5[-1] > ma20[-1]: # live模式下,此处需要更新一次持仓状态,这样判别持仓才准确 if symbol not in available_position_dict.keys(): self.activate_trade_signal = True comments = "限价买入" # psize = self.calc_position_size(account_data.available, close_price.iloc[-1]) psize = 300 if psize > 0: self.buy(event_bar.dt, self.account[0]['name'], symbol, close_price.iloc[-1], psize, False, comments) self.context.logger.info( "-- 资金账号 {0} 发出委托买入 {1} {2} 股,委托价为 {3}". format(self.account[0]['name'], symbol, psize, close_price.iloc[-1]))