def run_after(self): """ 回测之后的自定义动作 :return: """ simulation_logger.info('str_id:{}, 结束模拟盘回测:{}'.format( self.strategy.strategy_id, datetime.now()))
def run_before(self): """ 回测之前的自定义动作 :return: """ simulation_logger.info('str_id:{}, 开始模拟盘回测:{}'.format( self.strategy.strategy_id, datetime.now()))
def __run(self): """ 开始 :return: """ # 回测之前 self.run_before() # 开始回测 if self.__bars.size(): __bar = self.__bars.top() # bar前 for __func in __bar.befores: __func(__bar) # bar中 simulation_logger.info('str_id:{}, bar:{}'.format( self.strategy.strategy_id, __bar)) self.__process(bar=__bar) # bar后 for __func in __bar.afters: __func(__bar) simulation_logger.info('str_id:{}, bar.size:{}'.format( self.strategy.strategy_id, self.__bars.size())) # 回测之后 self.run_after()
def run_after(self): self.strategy.data_update['simulation_success_flag'] = 10609 self.strategy.data_update['simulation_message'] = '实盘成功' # 通知Java # urlPost(self.strategy.data_update) # 保存成功后的因子信息 self.strategy.data_update['simulation_new_flag'] = 0 self.strategy.data_update['simulation_cost_time'] = ( datetime.now() - self.strategy.now).seconds self.strategy_data.save_strategy_data(data=self.strategy.data_update) simulation_logger.info('str_id:{}, run after') simulation_logger.info('str_id:{}, 运行后的position:{}'.format( self.strategy.strategy_id, self.position.positions)) simulation_logger.info('str_id:{}, 运行后的cash:{}'.format( self.strategy.strategy_id, self.position.cash)) simulation_logger.info('str_id:{}, 运行后的持仓比例:{}'.format( self.strategy.strategy_id, self.position.percent)) simulation_logger.info( 'str_id:{}, 模拟所花时间:{} s-------------------------------------------------------------------------------\n\n' .format(self.strategy.strategy_id, self.strategy.data_update['simulation_cost_time']))
def xbar(self, stock, day=None, type=0): stock = stock[0:6] df = cache.get('hq_{}'.format(stock)) if df is None or len(df) == 0: df = self.Hdf.select('/df_{}'.format(stock)) cache.add('hq_{}'.format(stock), df) if df is not None: if type == 0: try: close, ltm = df.loc[day] return [{'day': day, 'close': round(close, 2), 'ltm': ltm}] except Exception as e: simulation_logger.info('miss:{}:{}:{}'.format( e, stock, day)) return [] else: df = df[df.index <= day] if not df.empty: close = df['close'].values[-1] ltm = df['ltm'].values[-1] day = df.index[-1] return [{'day': day, 'close': round(close, 2), 'ltm': ltm}] else: return [] else: return []
def wrapper(*args, **kwargs): for __i in range(5): try: time.sleep(__i) func(*args, **kwargs) break except Exception as e: simulation_logger.info("db操作失败:{}".format(e))
def process(self, bar): """ 自定义回测 :param bar: :return: """ simulation_logger.info('str_id:{}, 自定义回测,bar:{}。'.format( self.strategy.strategy_id, bar))
def finances(self, befor_day=None): """ 选股 :param befor_day: :return: """ # 交集和并集需要讨论 start = time.time() istocks = self.plate_select(befor_day) # print('istocks:{}'.format(istocks)) pcodes = self.strategy.hqDB.find_suspendeds(befor_day) istocks = (istocks - set(pcodes)) day = '/quota_' + str(befor_day).replace('-', '') df1 = self.strategy.hqDB.mysql_Hdf.select(day) if df1 is not None: df = df1[df1['ltm'] != 1][df1.index.isin(istocks)] if len(self.finance): for row in self.finance: key, parms = row # 3.6 pandas bug if key == 'opcfg': df[key] = df[key].astype('float64') for parm in parms: _p = str(parm[1]).split('&') if len(_p) == 1: df = df.query('{}{}{}'.format( key, parm[0], parm[1])) else: df = df.query('{}{}{} and {}{}'.format( key, parm[0], _p[0], key, _p[1])) if len(self.technical): for row in self.technical: key, parms = row for parm in parms: df = df.query('{}{}{}'.format(key, parm[0], parm[1])) if len(self.orders['valuation']) > 0: for order in self.orders['valuation']: if order[1]['period'] == '1': df['{}_xrank'.format( order[0])] = df[order[0]].rank(ascending=True) else: df['{}_xrank'.format( order[0])] = df[order[0]].rank(ascending=False) df['xcrank'] = df[[ col for col in df.columns if col.endswith('_xrank') ]].apply(lambda x: x.sum(), axis=1) df = df.sort_values(by=['xcrank'], ascending=False) codes1 = list(df.index) simulation_logger.info(u'选择的股票数为:{},时间:{}:花费时间:{}'.format( len(codes1), befor_day, time.time() - start)) return codes1, df['cname'].values else: return ([], [])
def create_new_contract(self, parameters_dic): """创建合约""" __interface = '/externalService/external/cntr/add' cntr_id = self.strategy_id + str( time.time()).split('.')[0][-3:] # 生成合约来源ID if not parameters_dic.get('cntrCapAmt'): parameters_dic['cntrCapAmt'] = '30000000' # 合约总资金 parameters_dic['srcCntrId'] = cntr_id # 合约来源编号 self.cntr_id = cntr_id parameters_dic['usrId'] = self.usr_id # 用户ID parameters_dic['usrNm'] = self.usr_name # 用户名称 parameters_dic['drCrDt'] = datetime.today().strftime( "%Y-%m-%d %H:%M:%S") # 借款时间(默认当前) parameters_dic['repayDt'] = "2099-12-31 00:00:00" # 还款时间 parameters_dic['altAmt'] = "0.00" # 预警金额 parameters_dic['stpLosAmt'] = "0.00" # 止损金额 parameters_dic['drCrAmt'] = 0 # 合约借贷金额 parameters_dic['gurtyAmt'] = parameters_dic[ 'cntrCapAmt'] # 合约保证金 cntr_cap_amt="100000", 必须和cntrCapAmt(合约金额)一致 # parameters_dic['riskParam'] = ",,,13001:1,,,,,,,,," # , "riskParam": ",,,13001:1,,,,,,,,," "param": "0000000000000", # parameters_dic['param'] = "0000000000000" print('parameters_dic:', parameters_dic) res_data = self.con_interface.get_data(__interface, parameters_dic) print( 'res_data::', res_data ) # {'cntrId': 41227001, 'srcCntrId': 0, 'flwId': 1545903498541358689, 'respMessage': '成功', 'respCode': '000'} if res_data.get('respCode') == '000': update_info = { 'strategy_id': self.strategy_id, # 策略ID 'contract_source_id': cntr_id, # 合约来源ID 'contract_id': res_data['cntrId'], # 合约编号(3.0) 'user_id': self.usr_id, 'type': 0, # 暂时默认为0 } # 保存新合约信息 with MysqlManager('quant').Session as session: for i in range(3): try: session.merge(ContractInfo(**update_info)) except: simulation_logger.info( 'STR_ID:{},【创建合约】数据库出现异常-{}:{}'.format( self.strategy_id, i, traceback.print_exc())) continue break else: with open( MY_PROJECT_PATH + '/error_doc/ContractInfo_error.txt', 'a') as f: f.write(json.dumps(update_info) + '\n') res_data['srcCntrId'] = cntr_id return res_data
def sell_all(self): """ 卖出所有的股票 :return: """ simulation_logger.info('str_id:{},pending order sell all'.format(self.strategy.strategy_id)) # 如果持仓为空则直接返回 if self.position.positions.empty: return True,{'message':'持仓为空,不用清仓。'} # 查询清仓的股票 __before_stocks = set(self.position.shareholding) time1 = time.time() info = self.contract_operator.security_clearance(order=True,sleep_m=8) simulation_logger.info('str_id:{},sell all cost:{} s'.format(self.strategy.strategy_id,time.time() - time1)) if info['respCode'] == '000': # 保存交易流水 trade_time = datetime.now() before_assets = self.position.total_assets before_position = self.position.positions self.position.update_master(query_hold_days=False) simulation_logger.info('str_id:{},卖出所有股票后的更新仓位后的现金:{}'.format(self.strategy.strategy_id,self.position.cash)) __after_stocks = set(self.position.shareholding) clearance_stocks = __before_stocks-__after_stocks # 保存交易流水的内容 after_assets = self.position.total_assets after_position = self.position.positions # transaction_save(before_position=before_position, after_position=after_position, # before_assets=before_assets, # after_assets=after_assets, strategy_id=self.strategy.strategy_id, trade_type='sell_all', # trade_time=trade_time) __kwargs = {'before_position': before_position.to_dict(), 'after_position': after_position.to_dict(), 'before_assets': before_assets, 'after_assets': after_assets, 'strategy_id': self.strategy.strategy_id, 'trade_type': 'sell_all', 'trade_time': trade_time.strftime('%Y-%m-%d %H:%M:%S')} transaction_save.apply_async(queue='transaction', kwargs=__kwargs) # 删除持仓表信息 try: if clearance_stocks: with MysqlManager('quant').Session as session: for __stock in clearance_stocks: # 删除这只股票的持仓信息 simulation_logger.info('str_id:{},清仓一只股票:{},删除相应持仓'.format(self.strategy.strategy_id,__stock)) session.query(ContractPositionsInfo).filter(ContractPositionsInfo.strategy_id==str(self.strategy.polId), ContractPositionsInfo.contract_source_id == str(self.strategy.contract_source_id), ContractPositionsInfo.stock_code == str(__stock)).delete() except Exception as e: simulation_logger.info('str_id:{},清仓时删除持仓表信息出错:{}'.format(self.strategy.strategy_id,e)) return True,info else: return False,info
def request_cash(self): """ 请求cash :return: """ result = self.contract_operator.query_assets() if result.get('respCode', '404') == '000': result = round(float(result.get('curAvalCapAmt', 0)), 2) else: result = 0 if result: self.cash_flag = 1 else: self.cash_flag = -1 simulation_logger.info('str_id:{},现金:{}'.format( self.strategy.strategy_id, result)) return result
def request_total_assets(self): """ 请求总资产 :return: """ result = self.contract_operator.query_assets() if result.get('respCode', '404') == '000': result = round(float(result.get('tTAstAmt', 0)), 2) else: result = 0 if result: # 标识查询后不为空 self.total_assets_flag = 1 else: # 标识为查询后为空 self.total_assets_flag = -1 simulation_logger.info('str_id:{},总资产::{}'.format( self.strategy.strategy_id, result)) return result
def update_percent(self, refresh=False): """ 更新仓位 :param refresh: :return: """ if not refresh: __positions = self.get_positions() __total_assets = self.get_total_assets() else: __total_assets = self.refresh_total_assets() __positions = self.refresh_positions() if self.total_assets_flag == 1 and self.positions_flag == 1: __equity = (__positions['market_value']).sum() self.percent = round(float(__equity / __total_assets), 6) simulation_logger.info('str_id:{},update percent:{}'.format( self.strategy.strategy_id, self.__percent)) else: self.percent = 0
def termination_contract(self, parameters_dic='', delete_instruction=False): """终止合约""" __interface = '/externalService/external/cntr/end' res_info = { 'respCode': '400', 'respMessage': '请求失败!请输入正确的操作指令!【delete_instruction】', } if delete_instruction is False: return res_info if not parameters_dic: parameters_dic = dict() parameters_dic['srcCntrId'] = self.cntr_id # 合约来源ID res_data = self.con_interface.get_data(__interface, parameters_dic) # 终止合约处理合约表和合约持仓表的信息 with MysqlManager('quant').Session as session: for i in range(3): try: session.query(ContractInfo).filter( ContractInfo.contract_source_id == self.cntr_id).update({'flag': 0}) # 合约表合约信息软删除 session.query(ContractPositionsInfo).filter( ContractPositionsInfo.contract_source_id == self.cntr_id).delete() # 合约持仓表信息删除 except: simulation_logger.info( 'STR_ID:{},【终止合约】数据库出现异常-{}:{}'.format( self.strategy_id, i, traceback.print_exc())) continue break else: with open( MY_PROJECT_PATH + '/error_doc/termination_contract_error.txt', 'a') as f: save_info = { 'strategy_id': self.strategy_id, # 策略ID 'cntr_id': self.cntr_id # 合约来源ID } f.write(json.dumps(save_info) + '\n') return res_data
def __status_track(self, entrust_number, sleep_m): """合约委托的状态追踪,超时强制撤单""" # 追踪状态 flag = 0 wait_count = 0 res_info = {} while True: if wait_count >= 3: # 进行撤单操作 # print('准备进行撤单操作...') revoke_entrust_res = self.revoke_entrust({ "entrustNo": entrust_number }) # 撤销委托,创建委托返回的委托编号 # revoke_entrust_res = simulation_logger.info( '撤单动作返回信息:{}'.format(revoke_entrust_res)) # print('revoke_entrust_res:', revoke_entrust_res) # if revoke_entrust_res.get('respCode') == '154_6': # 已经成功,撤销失败 # flag = 1 # res_info = self.__get_today_trans_info(entrust_number, res_info) # 再次判断有没有成交(当日成交) today_transaction_res = self.today_transaction() for tmp in today_transaction_res.get('list', []): transId = tmp.get('delgtId', '') if str(transId) == entrust_number: flag = 1 res_info = self.__get_today_trans_info( entrust_number, res_info) break else: simulation_logger.info('撤单成功!{}'.format(entrust_number)) break wait_count += 1 time.sleep(sleep_m) res_info = self.__get_today_trans_info( entrust_number, res_info) # 在当天成交中查询 该笔委托的成交相关信息 if res_info: flag = 1 break return wait_count, flag, res_info
def sell(self,sell_info): """ 卖出 :param sell_info:卖出信息 :return: """ simulation_logger.info('str_id:{},pending order sell:{}'.format(self.strategy.strategy_id,sell_info)) time1 = time.time() info = self.contract_operator.place_an_order(stock_info=sell_info,trade_type='sell') simulation_logger.info('str_id:{},sell cost:{} s'.format(self.strategy.strategy_id,time.time() - time1)) info['used_money'] = round(float(info.get('useAmt',0)),2) info['trade_count'] = int(info.get('useQty',0)) if 'useAmt' in info.keys(): info.pop('useAmt') if 'transQty' in info.keys(): info.pop('transQty') if info['respCode'] == '000': if info['trade_count']: # 保存交易流水 trade_time = datetime.now() before_assets = self.position.total_assets before_position = self.position.positions self.position.update_master(query_hold_days=False) after_assets = self.position.total_assets after_position = self.position.positions # transaction_save(before_position=before_position,after_position=after_position,before_assets=before_assets, # after_assets=after_assets,strategy_id=self.strategy.strategy_id,trade_type='sell_one', # trade_time=trade_time,order_info=sell_info,result_info=info) __kwargs = {'before_position':before_position.to_dict(),'after_position':after_position.to_dict(),'before_assets':before_assets, 'after_assets':after_assets,'strategy_id':self.strategy.strategy_id,'trade_type':'sell_one', 'trade_time':trade_time.strftime('%Y-%m-%d %H:%M:%S'),'order_info':sell_info,'result_info':info} transaction_save.apply_async(queue='transaction',kwargs=__kwargs) # 删除持仓表信息 try: # 在持仓表中删除清仓的股票 if int(before_position.loc[before_position['inst'] == sell_info['stockCode'],'quantity'].values[0])==int(info['trade_count']): simulation_logger.info('str_id:{},清仓一只股票成功:{},删除持仓表中数据'.format(self.strategy.strategy_id,sell_info['stockCode'])) with MysqlManager('quant').Session as session: session.query(ContractPositionsInfo).filter(ContractPositionsInfo.strategy_id==str(self.strategy.strategy_id), ContractPositionsInfo.contract_source_id == str(self.strategy.contract_source_id), ContractPositionsInfo.stock_code == str(sell_info['stockCode'])).delete() except Exception as e: simulation_logger.info('str_id:{},卖出单只股票时删除持仓表信息出错:{}'.format(self.strategy.strategy_id, e)) return True,info else: return False,info
def process(self, bar): """ 自定义回测函数 :param bar: bar流 :return: """ if bar.stop: # 卖完所有股票 self.sell_all() else: # 卖出一只股票 self.sell_one() # 浮盈加减仓 self.step(bar) # 选股 self.select_stock(bar) # 买入选出的股票 self.buy_select(bar) self.statistics(bar) simulation_logger.info('str_id:{}, end')
def select_stock(self, bar): """ 选股 :param bar: :return: """ simulation_logger.info('str_id:{}, select stock'.format( self.strategy.strategy_id)) simulation_logger.info(self.position.percent) # 还有仓位才进行选股 if float( self.strategy.maxPosPct) - self.position.percent > 0 and float( bar.weight) - self.position.percent > 0: self.buyStocks, self.buyNames = self.strategy.order.finances( bar.date) else: self.buyStocks, self.buyNames = [], [] simulation_logger.info('str_id:{}, select stocks:{},names:{}'.format( self.strategy.strategy_id, self.buyStocks, self.buyNames).format(self.strategy.strategy_id))
def sell_all(self): """ 清仓 :return: """ simulation_logger.info( '\nstr_id:{}, sell all------------------------------------------------------------------------------.' .format(self.strategy.strategy_id)) flag, message = self.pending_order.sell_all() if flag: simulation_logger.info('str_id:{}, 清仓成功,info:{}'.format( self.strategy.strategy_id, message)) self.position.update_master(query_hold_days=False) simulation_logger.info('str_id:{}, 成功更新后的cash:{}'.format( self.strategy.strategy_id, self.position.cash)) else: simulation_logger.info('str_id:{}, 清仓失败,info:{}'.format( self.strategy.strategy_id, message)) self.position.update_master(query_hold_days=False) simulation_logger.info('str_id:{}, 失败更新后的cash:{}'.format( self.strategy.strategy_id, self.position.cash)) # 更新可用现金 if not self.strategy.maxBuyStockCash: self.strategy.available_cash_day = self.position.cash simulation_logger.info( 'str_id:{}, end sell all------------------------------------------------------------------------------.\n' .format(self.strategy.strategy_id))
def statistics(self, bar): simulation_logger.info('str_id:{}, statistics'.format( self.strategy.strategy_id))
def place_an_order(self, stock_info, sleep_m=5, trade_type='buy'): """ 新策略下单 trade_type: 默认为1(买), 2为卖 """ # 返回数据(模板) res_info = { 'respCode': '000', 'respMessage': '成功', } stock_code = str(stock_info.get('stockCode')) if trade_type == 'buy': # 买 stock_info['tradeType'] = "10801" elif trade_type == 'sell': # 卖 stock_info['tradeType'] = "10802" # 【买/卖】 business_res = self.business_instructions(stock_info) # 委托 # print('business_res:', business_res) if business_res.get( 'status_code') or business_res.get('respCode') != '000': res_info['respCode'] = '404' res_info['respMessage'] = '请求异常,请重新尝试!详情:{}'.format(business_res) return res_info entrust_number = business_res.get('entrustNo') # 委托编号 print('entrust_number:', entrust_number) # 状态追踪 wait_count, flag, new_res_info = self.__status_track(entrust_number, sleep_m=sleep_m) simulation_logger.info( 'STR_ID:{}【追踪结果】wait_count:{},flag:{},stockCode:{}'.format( self.strategy_id, str(wait_count), str(flag), stock_code)) if flag == 0: # 下单失败 res_info['respCode'] = '404' res_info['respMessage'] = '下单失败!' return res_info if trade_type == 'buy': # 买 # 保存合约信息到python合约持仓表 update_info = { 'strategy_id': str(self.strategy_id), # 策略ID 'contract_source_id': str(self.cntr_id), # 合约来源ID # 'contract_id': self.cntr_id, # 传过来的合约编号(3.0) 'stock_code': stock_code, # 股票号码 'user_id': self.usr_id, 'type': 0, # 暂时默认为0 'flag': flag } # print('update_info:', update_info) simulation_logger.info('STR_ID:{},【持仓信息存储】:{},stockCode:{}'.format( self.strategy_id, update_info, stock_info.get('stockCode'))) # 下单成功后保存持仓信息 with MysqlManager('quant').Session as session: for i in range(3): try: session.merge(ContractPositionsInfo(**update_info)) except: simulation_logger.info( 'STR_ID:{},【下单】数据库异常-{}:{}'.format( self.strategy_id, i, traceback.print_exc())) continue break else: with open( MY_PROJECT_PATH + '/error_doc/ContractPositionsInfo_error.txt', 'a') as f: f.write(json.dumps(update_info) + '\n') res_info['useQty'] = new_res_info.get('useQty') # 成交数量 res_info['useAmt'] = new_res_info.get('useAmt') # 成交金额 return res_info
def sell_one(self): """ 清仓一只股票 :return: """ simulation_logger.info( 'str_id:{}, \nsell one--------------------------------------------------------------------------------.' .format(self.strategy.strategy_id)) for _, row in self.position.positions.iterrows(): # 不是T+1日不能卖出 if row.hold_days <= 1: simulation_logger.info('str_id:{}, 不是T+1日不能卖出,row:{}'.format( self.strategy.strategy_id, row.hold_days)) simulation_logger.info( 'str_id:{}, -----------------------------------------------------------------------------------------------------\n' .format(self.strategy.strategy_id)) continue # 判断个股是否达到离场条件 status, msg = self.strategy.order.leave(row) # 如果达到离场条件则卖出这只股票 if status: simulation_logger.info( 'str_id:{}, 达到单只股票离场条件,stock:{},info:{}'.format( self.strategy.strategy_id, row.inst, msg)) # 判断是否跌停和停牌退市 # 跌停不能卖出 limit_move_status, _ = self.strategy.hqDB.get_limit_move( stock=row.inst) if limit_move_status == -1 or limit_move_status == 2: simulation_logger.info( 'str_id:{}, 跌停不能卖出,stock:{},limit_move_status:{}'. format(self.strategy.strategy_id, row.inst, limit_move_status)) simulation_logger.info( 'str_id:{}, -----------------------------------------------------------------------------------------------------\n' .format(self.strategy.strategy_id)) continue # 获取当前股票是否停牌和最新价,停牌不能卖出 status, price = self.strategy.hqDB.get_status_price( stock=row.inst) if not status: simulation_logger.info( 'str_id:{}, 停牌不能卖出,stock:{},status:{}'.format( self.strategy.strategy_id, row.inst, status)) simulation_logger.info( 'str_id:{}, -----------------------------------------------------------------------------------------------------\n' .format(self.strategy.strategy_id)) continue # 构造下单的信息,10801:买入,10802:卖出 sell_info = { "stockCode": "{}".format(f14(row.inst)), "stockName": row['name'], "entrustPrice": "{}".format(price), "entrustCount": str(int(int(row['quantity_sell']) / 100) * 100) } __flag, __info = self.pending_order.sell(sell_info) if __flag: simulation_logger.info('str_id:{}, 下单卖出成功,info:{}'.format( self.strategy.strategy_id, __info)) # self.position.update_master(query_hold_days=False) simulation_logger.info('str_id:{}, 成功更新后的cash:{}'.format( self.strategy.strategy_id, self.position.cash)) else: self.position.update_master(query_hold_days=False) simulation_logger.info('str_id:{}, 失败更新后的cash:{}'.format( self.strategy.strategy_id, self.position.cash)) simulation_logger.info('str_id:{}, 下单卖出失败,info:{}'.format( self.strategy.strategy_id, __info)) else: simulation_logger.info('str_id:{}, 没有达到离场条件:{}'.format( self.strategy.strategy_id, msg)) simulation_logger.info( 'str_id:{}, -----------------------------------------------------------------------------------------------------\n' .format(self.strategy.strategy_id)) # 更新可用现金 if not self.strategy.maxBuyStockCash: self.strategy.available_cash_day = self.position.cash simulation_logger.info( 'str_id:{}, end sell one--------------------------------------------------------------------------------.\n\n' .format(self.strategy.strategy_id))
def buy_select(self, bar): """ 得到委托数量和委托价格 :param bar: :return: """ simulation_logger.info( 'str_id:{}, buy select--------------------------------------------------------------------------------' .format(self.strategy.strategy_id)) if self.strategy.maxPositionStockNum: self.strategy.maxPositionStockNum = int( self.strategy.maxPositionStockNum) if self.strategy.maxBuyStockNum: self.strategy.maxBuyStockNum = int(self.strategy.maxBuyStockNum) bar.weight = round(float(bar.weight), 4) self.strategy.maxPosPct = round(float(self.strategy.maxPosPct), 6) self.strategy.sglMaxPosPct = round(float(self.strategy.sglMaxPosPct), 6) # 最大持仓股票数量,如果用户输入大于20,则取20 # maxPositionStockNum:最大持股数量 # 根据最大持仓 / 单票进场仓位得出一个可以持股的数量,两者取其小 # max_buy:根据最大持仓和单只股票进场仓位得到的持有股票数限制 max_buy_stocks = self.strategy.max_buy if self.strategy.maxPositionStockNum: max_buy_stocks = min(max_buy_stocks, self.strategy.maxPositionStockNum) # 将stocks进行和名字的对应组合成列表 stocks = list(zip(self.buyStocks, self.buyNames)) # 如果股票优先级是随机买入则随机取股票 if self.strategy.randomSort: random.shuffle(stocks) # 如果用户设置了单日最大买入股票数量,则先将股票数量限制在这个数量 if self.strategy.maxBuyStockNum: stocks = stocks[0:self.strategy.maxBuyStockNum] for stock, name in stocks: # 可用金额过小则不买入 __available_cash_tmp = min(self.position.cash, self.strategy.available_cash_day) if __available_cash_tmp < 1000: return # 超过最大买入数,不得再买 # print('len:{},max buy stocks:{}'.format(self.strategy.strategy_id,len(self.position.positions),max_buy_stocks)) if len(self.position.positions) >= max_buy_stocks: simulation_logger.info( 'str_id:{}, 超过最大买入数,不得再买,len of positions:{},max_buy_stocks:{}' .format(self.strategy.strategy_id, len(self.position.positions), max_buy_stocks)) return # 如果当前持仓大于等于基准择时持仓则不买入,只有现有仓位小于用户设置的牛熊市最大持仓才会进行买入操作 if self.position.percent >= bar.weight: simulation_logger.info( 'str_id:{}, 当前持仓大于等于基准择时持仓则不买入,position.percent:{},bar.weight:{}' .format(self.strategy.strategy_id, self.position.percent, bar.weight)) return # 如果当前持仓大于等于最大持仓则不买入 if self.position.percent >= self.strategy.maxPosPct: simulation_logger.info( 'str_id:{}, 当前持仓大于等于最大持仓则不买入,position.percent:{},maxPosPct:{}' .format(self.strategy.strategy_id, self.position.percent, self.strategy.maxPosPct)) return # 如果不能重复买入 并且仓位有这只股票,则不买入,rmRepetitionSort:去重买入 if self.strategy.rmRepetitionSort and stock in self.position.shareholding: simulation_logger.info( 'str_id:{}, 不能重复买入 并且仓位有这只股票,则不买入,position.shareholding:{},stock:{}' .format(self.strategy.strategy_id, self.position.shareholding, stock)) continue # 单只股票进场仓位 single_entry_position = self.strategy.sglPosPct # 得到单只股票限制后的进场仓位 __single_status, single_position, __message = self.__get_single_restrict_position( single_entry_position=single_entry_position, stock=stock) if not __single_status: simulation_logger.info('str_id:{}, 单只股票限制后的进场仓位为空:{}'.format( self.strategy.strategy_id, __message)) continue # 根据单只股票进场仓位,执行买入操作,包括更新 __status, __info = self.__buy_by_position( stock=stock, name=name, single_position=single_position, bar=bar) if not __status: simulation_logger.info('str_id:{}, 买入失败:{}'.format( self.strategy.strategy_id, __info)) continue simulation_logger.info( 'str_id:{}, end buy select--------------------------------------------------------------------------------\n\n' .format(self.strategy.strategy_id))
def step(self, bar): """ 浮盈加减仓 :param bar: :return: """ simulation_logger.info( 'str_id:{}, step--------------------------------------------------------------------------------------------' ) for _, row in self.position.positions.iterrows(): status, init2, msg = self.strategy.order.step(row) # 没有达到条件则跳过 if not status: simulation_logger.info( 'str_id:{}, 没有达到条件则跳过,,message:{}\n'.format( self.strategy.strategy_id, msg)) continue # 获取当前股票是否停牌和最新价,停牌不能卖出 status, price = self.strategy.hqDB.get_status_price(stock=row.inst) if not status: simulation_logger.info('str_id:{}, 停牌退市不能卖出,stock:{}'.format( self.strategy.strategy_id, row.inst)) continue # 加仓的情况 init2 = round(float(init2), 4) if init2 > 0: simulation_logger.info( 'str_id:{}, 加仓--------------------------------------------------------------------------------------------' .format(self.strategy.strategy_id)) simulation_logger.info( 'str_id:{}, 浮盈加减仓,加仓,stock:{},name:{},init2:{},msg:{}'. format(self.strategy.strategy_id, row.inst, row['name'], init2, msg)) # 如果没有持则不操作 if not row.get('percent'): continue # 得到单只股票最大仓位限制后的剩余仓位 __single_remain = self.strategy.sglMaxPosPct - float( row['percent']) # 得到本来的进场仓位 __origin_position = init2 * float(row['percent']) # 得到以上限制后的进场仓位,还要比单只股票持仓上限要小 single_position = round( min(__single_remain, __origin_position), 6) # 如果持仓小于等于0则跳过 if single_position <= 0: simulation_logger.info( 'str_id:{}, 持仓小于等于0则跳过,stock:{},single_position:{}'. format(self.strategy.strategy_id, row.inst, single_position)) simulation_logger.info( 'str_id:{}, 结束加仓--------------------------------------------------------------------------------------------\n' .format(self.strategy.strategy_id)) continue # 根据单只股票进场仓位,执行买入操作,包括更新 __status, __info = self.__buy_by_position( stock=row.inst, name=row['name'], single_position=single_position, bar=bar) if not __status: simulation_logger.info('str_id:{}, 买入失败:{},持仓已更新'.format( self.strategy.strategy_id, __info)) else: simulation_logger.info('str_id:{}, 买入成功:{},持仓已更新'.format( self.strategy.strategy_id, __info)) simulation_logger.info( 'str_id:{}, 结束加仓--------------------------------------------------------------------------------------------\n' .format(self.strategy.strategy_id)) continue # 减仓的情况 elif init2 < 0: simulation_logger.info( 'str_id:{}, 减仓--------------------------------------------------------------------------------------------' .format(self.strategy.strategy_id)) simulation_logger.info( 'str_id:{}, 浮盈加减仓,减仓,stock:{},name:{},init2:{},msg:{}'. format(self.strategy.strategy_id, row.inst, row['name'], init2, msg)) # 不是T+1日不能卖出 if row.hold_days <= 1: simulation_logger.info( 'str_id:{}, 不是T+1日不能卖出,stock:{},row.hold_days:{}'. format(self.strategy.strategy_id, row.inst, row.hold_days)) simulation_logger.info( 'str_id:{}, 结束减仓--------------------------------------------------------------------------------------------\n' .format(self.strategy.strategy_id)) continue # 判断是否跌停和停牌退市 # 跌停不能卖出 limit_move_status, _ = self.strategy.hqDB.get_limit_move( stock=row.inst) if limit_move_status == -1 or limit_move_status == 2: simulation_logger.info( 'str_id:{}, 跌停不能卖出,stock:{},limit_move_status:{}'. format(self.strategy.strategy_id, row.inst, limit_move_status)) simulation_logger.info( 'str_id:{}, 结束减仓--------------------------------------------------------------------------------------------\n' .format(self.strategy.strategy_id)) continue # 获取当前股票是否停牌和最新价,停牌不能卖出 status, price = self.strategy.hqDB.get_status_price( stock=row.inst) if not status: simulation_logger.info( 'str_id:{}, 跌停不能卖出,stock:{},status:{}'.format( self.strategy.strategy_id, row.inst, status)) simulation_logger.info( 'str_id:{}, 结束减仓--------------------------------------------------------------------------------------------\n' .format(self.strategy.strategy_id)) continue # 得到可卖出的数量 __sell_count = min(int(row['quantity_sell']), int(row['quantity'] * abs(init2))) __sell_count = str(int(__sell_count / 100) * 100) # 构造下单的信息,10801:买入,10802:卖出 sell_info = { "stockCode": "{}".format(f14(row.inst)), "stockName": row['name'], "entrustPrice": "{}".format(price), "entrustCount": __sell_count } __flag, __info = self.pending_order.sell(sell_info) if __flag: simulation_logger.info('str_id:{}, 下单卖出成功,info:{}'.format( self.strategy.strategy_id, __info)) self.position.update_master(query_hold_days=False) simulation_logger.info('str_id:{}, 成功更新后的cash:{}'.format( self.strategy.strategy_id, self.position.cash)) else: self.position.update_master(query_hold_days=False) simulation_logger.info('str_id:{}, 失败更新后的cash:{}'.format( self.strategy.strategy_id, self.position.cash)) simulation_logger.info('str_id:{}, 下单卖出失败,info:{}'.format( self.strategy.strategy_id, __info)) # 更新可用现金 if not self.strategy.maxBuyStockCash: self.strategy.available_cash_day = self.position.cash simulation_logger.info( 'str_id:{}, 结束减仓--------------------------------------------------------------------------------------------\n' .format(self.strategy.strategy_id)) else: continue simulation_logger.info( 'str_id:{}, end step-------------------------------------------------------------------------------------------------\n' .format(self.strategy.strategy_id))
ContractPositionsInfo.stock_code == str(sell_info['stockCode'])).delete() except Exception as e: simulation_logger.info('str_id:{},卖出单只股票时删除持仓表信息出错:{}'.format(self.strategy.strategy_id, e)) return True,info else: return False,info if __name__ == '__main__': data = {'polId':100000000000021,'currentuserid':2000000000000000} # 初始化实例信息 strategy = Strategy() with MysqlManager('quant').Session as session: query_res = session.query(ContractInfo.contract_source_id).filter(ContractInfo.strategy_id==data['polId']).all() if not query_res: # 没有查询到对应的合约来源ID simulation_logger.info('该策略没有创建合约,需重先创建新合约') module_func = ModuleFunc(strategy_id=data['polId'], usr_id=data['currentuserid'], usr_name='--') # 用户来源ID __info = module_func.create_new_contract({'cntrCapAmt': '30000000'}) # 创建新的合约 if __info['respCode'] != '000': simulation_logger.info('合约创建失败:{}'.format(__info)) exit(0) else: # 将合约id保存到strategy中 strategy.contract_source_id = __info['srcCntrId'] strategy.contract_id = __info['cntrId'] else: simulation_logger.info('该策略已有合约,直接初始化') contract_source_id = query_res[0][0] # 得到合约的持仓实例 strategy.contract_id = query_res[0][1] strategy.contract_source_id = query_res[0][0]
def __buy_by_position(self, stock, name, single_position: float, bar): """ 根据单只股票进场仓位,执行总仓位限制后的买入操作,包括得到最终下单信息、下单、更新仓位和相关指标 :param stock: 股票 :param name: 股票名 :param single_position: 单只股票买入仓位 :return: """ if single_position <= 0: return False, '单只股票买入仓位小于等于0,买入失败,stock:{},single_position:{}'.format( stock, single_position) # 得到总仓位限制后的进场仓位 # 最大持仓的剩余仓位 __max_remain = self.strategy.maxPosPct - self.position.percent # 得到牛熊市最大持仓限制之后的仓位 __weight_remain = bar.weight - self.position.percent # 得到以上限制后的进场仓位,还要比单只股票持仓上限要小 single_position = min(single_position, __max_remain, __weight_remain, self.strategy.sglMaxPosPct) # 如果持仓小于等于0则跳过 if single_position <= 0: return False, '持仓小于等于0则跳过,买入失败,stock:{},single_position:{}'.format( stock, single_position) # 得到买入的金额 simulation_logger.info( 'str_id:{}, total_assets:{},single_position:{}'.format( self.strategy.strategy_id, self.position.total_assets, single_position)) buy_money = round(self.position.total_assets * single_position, 2) # 买入金额不能大于现金,也不能大于最大单日买入金额 simulation_logger.info( 'str_id:{}, buy_money:{},cash:{},available_cash:{}'.format( self.strategy.strategy_id, buy_money, self.position.cash, self.strategy.available_cash_day)) # 得到能用的现金 available_cash_tmp = min(self.position.cash, self.strategy.available_cash_day) # 得到买入金额,买入金额和手续费要小于等于可用余额 if buy_money * 1.0005 >= available_cash_tmp: buy_money = int(available_cash_tmp / 1.0005) # 金额过小则不买入 if buy_money < 1000: return False, '金额过小则不买入,买入失败,stock:{},buy_money:{}'.format( stock, buy_money) # 涨停不能买入 limit_move_status, _ = self.strategy.hqDB.get_limit_move(stock=stock) if limit_move_status == 1 or limit_move_status == 2: return False, '涨停不能买入,买入失败,stock:{},limit_move_status:{}'.format( stock, limit_move_status) # 获取当前股票是否停牌和最新价 status, price = self.strategy.hqDB.get_status_price(stock=stock) # 构造下单的信息,10801:买入,10802:卖出 buy_info = { "stockCode": "{}".format(f14(stock)), "stockName": "{}".format(name), "entrustPrice": "{}".format(price) } # 停牌不买入 if not status: return False, '停牌不买入,买入失败,stock:{},status:{}'.format(stock, status) # 得到买入数量 buy_count = int(buy_money / (price * 100)) * 100 # 买入数量不足一手不交易 if buy_count < 100: return False, '买入数量不足一手不交易,买入失败,stock:{},buy_count:{}'.format( stock, buy_count) # 加入到买入信息中 buy_info['entrustCount'] = "{}".format(buy_count) # 下单 flag, info = self.pending_order.buy(buy_info=buy_info) # simulation_logger.info('str_id:{}, flag:{}'.format(self.strategy.strategy_id,flag)) # 成交则更新持仓 if not flag: self.position.update_master(query_hold_days=False) simulation_logger.info('str_id:{}, 失败更新后的cash:{}'.format( self.strategy.strategy_id, self.position.cash)) # simulation_logger.info('str_id:{}, ------------------------------------------------------------------------------------------------------------\n\n') return False, '下单买入失败:{}'.format(info) else: self.strategy.available_cash_day = self.strategy.available_cash_day - round( float(info['used_money']) * 1.0005, 2) simulation_logger.info('str_id:{}, available money:{}'.format( self.strategy.strategy_id, self.strategy.available_cash_day)) self.position.update_master(query_hold_days=False) simulation_logger.info('str_id:{}, 成功更新后的cash:{}'.format( self.strategy.strategy_id, self.position.cash)) simulation_logger.info( 'str_id:{}, ------------------------------------------------------------------------------------------------------------\n\n' .format(self.strategy.strategy_id)) return True, info
def run_before(self): """ 开始回测之前 :return: """ simulation_logger.info('str_id:{},hq:{}'.format( self.strategy.strategy_id, self.strategy.hq)) simulation_logger.info('str_id:{}, 保存的因子信息:{}'.format( self.strategy.strategy_id, self.strategy.data_update)) # 更新保存策略信息 self.strategy_data.save_strategy_data(data=self.strategy.data_update) # 判断今天是否是交易日,如果不是则不买卖 clear_flag = False __trade_flag = self.__judge_trade_date() if not __trade_flag: clear_flag = True # 设置只保存,也不买卖 if not self.strategy.data_update.get('save_or_simulation', 0): clear_flag = True if clear_flag: self.clear_bar() # 得到当日最大可用资金 simulation_logger.info('str_id:{}, run before') if self.strategy.maxBuyStockCash: self.strategy.available_cash_day = round( float(self.strategy.maxBuyStockCash) * self.position.total_assets, 2) else: self.strategy.available_cash_day = self.position.cash simulation_logger.info('str_id:{}, 运行前的持仓:{}'.format( self.strategy.strategy_id, self.position.positions)) simulation_logger.info('str_id:{}, 运行前的总资产:{}'.format( self.strategy.strategy_id, self.position.total_assets)) simulation_logger.info('str_id:{}, 运行前的当日可用现金:{}'.format( self.strategy.strategy_id, self.strategy.available_cash_day)) simulation_logger.info('str_id:{}, 运行前的现金:{}'.format( self.strategy.strategy_id, self.position.cash))
def simulation_entrance(data, save_or_simulation=0): """ 模拟盘函数入口,用户新对接模拟盘会在下一个交易日买卖,9:35之前的会在今天买卖 :param data: :param save_or_simulation: 进行保存还是保存并模拟,0:只保存,1:保存并模拟 :return: """ # 如果data为空则不模拟 if not data or 'list' not in data.keys(): return # 初始化数据 # 10607实盘中,10608实盘出错,10609实盘成功 data['simulation_success_flag'] = 10607 if 'back_success_flag' in data.keys() and int( data['back_success_flag']) != 10605: data['simulation_success_flag'] = 10608 data['simulation_time'] = datetime.now().strftime('%Y-%m-%d') data['simulation_flag'] = 1 data['simulation_message'] = '实盘中' data['strategy_message'] = '实盘模拟' data['save_or_simulation'] = int(save_or_simulation) # 用于java data['bkstResponMessage'] = u'实盘中' data['bkstResponCode'] = 10607 data['trade_type'] = 'simulation' # 通知JAVA # if data.get('simulation_new_flag',0) > 0: # urlPost(data) # 如果回测失败则直接退出模拟 # if data['back_success_flag'] == 10606: # return # 解析语法 data_calculate = parse(data) # 整理策略 strategy = Strategy() strategy.hqDB = DataCollection() strategy.init_data(data_calculate) simulation_logger.info( '\n\n-----------------------------------------------------------------------------------------------------------------------' ) simulation_logger.info('str_id:{},strategy:{}'.format( strategy.strategy_id, strategy)) # 初始化实例信息 with MysqlManager('quant').Session as session: query_res = session.query( ContractInfo.contract_source_id, ContractInfo.contract_id).filter_by( strategy_id=data_calculate['strategy_id'], flag=1).all() if not query_res: # 没有查询到对应的合约来源ID simulation_logger.info('str_id:{},该策略没有创建合约,需重先创建新合约'.format( strategy.strategy_id)) module_func = ModuleFunc(strategy_id=data_calculate['strategy_id'], usr_id=data_calculate['user_id'], usr_name='--') # 用户来源ID __info = module_func.create_new_contract({'cntrCapAmt': '30000000'}) # 创建新的合约 if __info['respCode'] != '000': simulation_logger.info('str_id:{},合约创建失败:{}'.format( strategy.strategy_id, __info)) exit(0) else: # 将合约id保存到strategy中 strategy.contract_id = __info['cntrId'] strategy.contract_source_id = __info['srcCntrId'] data['contract_id'] = int(__info['cntrId']) data['contract_source_id'] = int(__info['srcCntrId']) else: simulation_logger.info('str_id:{},该策略已有合约,直接初始化'.format( strategy.strategy_id)) cntr_id = query_res[0][0] data['contract_id'] = int(query_res[0][1]) data['contract_source_id'] = int(query_res[0][0]) module_func = ModuleFunc(strategy_id=data_calculate['strategy_id'], cntr_id=cntr_id, usr_id=data_calculate['user_id'], usr_name='--') # 用户来源ID # 将合约id保存到strategy中 strategy.contract_id = query_res[0][1] strategy.contract_source_id = query_res[0][0] # 保存更新策略因子信息 # print('得到的数据:{}'.format(data)) strategy.data_update = data # 得到合约的持仓实例 position = Position(contract_operator=module_func, strategy=strategy) # 得到合约的挂单实例 pending_order = PendingOrder(contract_operator=module_func, position=position, strategy=strategy) # 得到策略数据操作类型 strategy_data = StrategyData() # 装在模拟盘回测框架 simulation_trader = prototype.clone('simulation_trader', strategy=strategy, position=position, pending_order=pending_order, strategy_data=strategy_data) # 正式开始回测交易 simulation_trader.start() # 关闭数据库连接 strategy.hqDB.close()
def buy(self,buy_info): """ 买入 :param buy_info: :return: """ # simulation_logger.info('\n---------------------------------------------------------------------------------------------------------------------') simulation_logger.info('str_id:{},pending order buy:{}'.format(self.strategy.strategy_id,buy_info)) time1 = time.time() info = self.contract_operator.place_an_order(stock_info=buy_info, trade_type='buy') simulation_logger.info('str_id:{},buy cost:{} s'.format(self.strategy.strategy_id,time.time()-time1)) simulation_logger.info('str_id:{},买入结果:{}'.format(self.strategy.strategy_id,info)) info['used_money'] = round(float(info.get('useAmt',0)),2) info['trade_count'] = int(info.get('useQty',0)) if 'useAmt' in info.keys(): info.pop('useAmt') if 'transQty' in info.keys(): info.pop('transQty') if info['respCode'] == '000': # 保存交易流水 simulation_logger.info('str_id:{},trade count:{}'.format(self.strategy.strategy_id,info['trade_count'])) if info['trade_count']: # 保存交易流水 trade_time = datetime.now() before_assets = self.position.total_assets before_position = self.position.positions self.position.update_master(query_hold_days=False) after_assets = self.position.total_assets after_position = self.position.positions # transaction_save(before_position=before_position,after_position=after_position,before_assets=before_assets, # after_assets=after_assets,strategy_id=self.strategy.strategy_id,trade_type='buy_one', # trade_time=trade_time,order_info=buy_info,result_info=info) __kwargs = {'before_position':before_position.to_dict(),'after_position':after_position.to_dict(),'before_assets':before_assets, 'after_assets':after_assets,'strategy_id':self.strategy.strategy_id,'trade_type':'buy_one', 'trade_time':trade_time.strftime('%Y-%m-%d %H:%M:%S'),'order_info':buy_info,'result_info':info, 'user_id':self.strategy.user_id,'contract_source_id':self.strategy.contract_source_id} transaction_save.apply_async(queue='transaction',kwargs=__kwargs) # 保存到持仓表中 try: if buy_info['stockCode'] in list(before_position.inst): __sql = "INSERT INTO contract_positions_info (date,create_time,update_time,strategy_id, contract_source_id, stock_code,user_id,type,flag,positions_days) \ VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) ON DUPLICATE KEY UPDATE type=0,flag=1" else: __sql = "INSERT INTO contract_positions_info (date,create_time,update_time,strategy_id, contract_source_id, stock_code,user_id,type,flag,positions_days) \ VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) ON DUPLICATE KEY UPDATE type=0,flag=1,positions_days=1" __values = [datetime.now(),datetime.now(),datetime.now(),str(self.strategy.strategy_id), str(self.strategy.contract_source_id), str(buy_info['stockCode']),str(self.strategy.user_id),0,1,1] # 申请资源 mysql = Mysql() ids = mysql.update(sql=__sql, param=__values) simulation_logger.info('str_id:{},买入添加持仓表信息成功 sql:{},id:{}'.format(self.strategy.strategy_id,__sql,ids)) # 释放资源 mysql.dispose() except Exception as e: simulation_logger.info('str_id:{},买入添加持仓信息出错:{}'.format(self.strategy.strategy_id,e)) return True,info else: return False,info