class QAStrategyCTABase(): def __init__(self, code='rb1905', frequence='1min', strategy_id='QA_STRATEGY', risk_check_gap=1, portfolio='default', start='2019-01-01', end='2019-10-21', init_cash=1000000, send_wx=False, data_host=eventmq_ip, data_port=eventmq_port, data_user=eventmq_username, data_password=eventmq_password, trade_host=eventmq_ip, trade_port=eventmq_port, trade_user=eventmq_username, trade_password=eventmq_password, taskid=None, mongo_ip=mongo_ip, model= 'py'): """ code 可以传入单个标的 也可以传入一组标的(list) 会自动基于code来判断是什么市场 TODO: 支持多个市场同时存在 """ self.trade_host = trade_host self.code = code self.frequence = frequence self.strategy_id = strategy_id self.portfolio = portfolio self.data_host = data_host self.data_port = data_port self.data_user = data_user self.data_password = data_password self.trade_host = trade_host self.trade_port = trade_port self.trade_user = trade_user self.trade_password = trade_password self.start = start self.end = end self.init_cash = init_cash self.taskid = taskid self.running_time = '' self.market_preset = QA.QAARP.MARKET_PRESET() self._market_data = [] self.risk_check_gap = risk_check_gap self.latest_price = {} self.isupdate = False self.model = model self.new_data = {} self._systemvar = {} self._signal = [] self.send_wx = send_wx if isinstance(self.code, str): self.last_order_towards = {self.code: {'BUY': '', 'SELL': ''}} else: self.last_order_towards = dict(zip(self.code, [{'BUY': '', 'SELL': ''} for i in range(len(self.code))])) self.dt = '' if isinstance(self.code, str): self.market_type = MARKET_TYPE.FUTURE_CN if re.search( r'[a-zA-z]+', self.code) else MARKET_TYPE.STOCK_CN else: self.market_type = MARKET_TYPE.FUTURE_CN if re.search( r'[a-zA-z]+', self.code[0]) else MARKET_TYPE.STOCK_CN self.bar_order = {'BUY_OPEN': 0, 'SELL_OPEN': 0, 'BUY_CLOSE': 0, 'SELL_CLOSE': 0} self._num_cached = 120 self._cached_data = [] self.user_init() @property def bar_id(self): return len(self._market_data) def _debug_sim(self): self.running_mode = 'sim' if self.frequence.endswith('min'): if isinstance(self.code, str): self._old_data = QA.QA_fetch_get_future_min('tdx', self.code.upper(), QA.QA_util_get_last_day( QA.QA_util_get_real_date(str(datetime.date.today()))), str(datetime.datetime.now()), self.frequence)[:-1].set_index(['datetime', 'code']) self._old_data = self._old_data.assign(volume=self._old_data.trade).loc[:, [ 'open', 'high', 'low', 'close', 'volume']] else: self._old_data = pd.concat([QA.QA_fetch_get_future_min('tdx', item.upper(), QA.QA_util_get_last_day( QA.QA_util_get_real_date(str(datetime.date.today()))), str(datetime.datetime.now()), self.frequence)[:-1].set_index(['datetime', 'code']) for item in self.code], sort=False) self._old_data = self._old_data.assign(volume=self._old_data.trade).loc[:, [ 'open', 'high', 'low', 'close', 'volume']] else: self._old_data = pd.DataFrame() self.database = pymongo.MongoClient(mongo_ip).QAREALTIME self.client = self.database.account self.subscriber_client = self.database.subscribe self.acc = QIFI_Account( username=self.strategy_id, password=self.strategy_id, trade_host=mongo_ip, init_cash=self.init_cash) self.acc.initial() self.pub = publisher_routing(exchange='QAORDER_ROUTER', host=self.trade_host, port=self.trade_port, user=self.trade_user, password=self.trade_password) if isinstance(self.code, str): self.subscribe_data(self.code.lower(), self.frequence, self.data_host, self.data_port, self.data_user, self.data_password, self.model) else: self.subscribe_multi(self.code, self.frequence, self.data_host, self.data_port, self.data_user, self.data_password, self.model) print('start sim') self.database.strategy_schedule.job_control.update( {'strategy_id': self.strategy_id}, {'strategy_id': self.strategy_id, 'taskid': self.taskid, 'filepath': os.path.abspath(__file__), 'status': 200}, upsert=True) def debug_sim(self): self._debug_sim() threading.Thread(target=self.sub.start, daemon=True).start() def run_sim(self): self._debug_sim() self.sub.start() def run_backtest(self): self.debug() self.acc.save() risk = QA_Risk(self.acc) risk.save() try: """add rank flow if exist QARank是我们内部用于评价策略ELO的库 此处并不影响正常使用 """ from QARank import QA_Rank QA_Rank(self.acc).send() except: pass def user_init(self): """ 用户自定义的init过程 """ pass def debug(self): self.running_mode = 'backtest' self.database = pymongo.MongoClient(mongo_ip).QUANTAXIS user = QA_User(username="******", password='******') port = user.new_portfolio(self.portfolio) self.acc = port.new_accountpro( account_cookie=self.strategy_id, init_cash=self.init_cash, market_type=self.market_type) self.positions = self.acc.get_position(self.code) print(self.acc) print(self.acc.market_type) data = QA.QA_quotation(self.code.upper(), self.start, self.end, source=QA.DATASOURCE.MONGO, frequence=self.frequence, market=self.market_type, output=QA.OUTPUT_FORMAT.DATASTRUCT) data.data.apply(self.x1, axis=1) def x1(self,item): # print(data) self.latest_price[item.name[1]] = item['close'] if str(item.name[0])[0:10] != str(self.running_time)[0:10]: self.on_dailyclose() self.on_dailyopen() if self.market_type == QA.MARKET_TYPE.STOCK_CN: print('backtest: Settle!') self.acc.settle() self._on_1min_bar() self._market_data.append(item) self.running_time = str(item.name[0]) self.on_bar(item) def debug_t0(self): self.running_mode = 'backtest' self.database = pymongo.MongoClient(mongo_ip).QUANTAXIS user = QA_User(username="******", password='******') port = user.new_portfolio(self.portfolio) self.acc = port.new_accountpro( account_cookie=self.strategy_id, init_cash=self.init_cash, init_hold={ self.code: 100000}, market_type=self.market_type, running_environment=RUNNING_ENVIRONMENT.TZERO) self.positions = self.acc.get_position(self.code) print(self.acc) print(self.acc.market_type) data = QA.QA_quotation(self.code.upper(), self.start, self.end, source=QA.DATASOURCE.MONGO, frequence=self.frequence, market=self.market_type, output=QA.OUTPUT_FORMAT.DATASTRUCT) def x1(item): # print(data) self.latest_price[item.name[1]] = item['close'] if str(item.name[0])[0:10] != str(self.running_time)[0:10]: self.on_dailyclose() for order in self.acc.close_positions_order: order.trade('closebySys', order.price, order.amount, order.datetime) self.on_dailyopen() if self.market_type == QA.MARKET_TYPE.STOCK_CN: print('backtest: Settle!') self.acc.settle() self._on_1min_bar() self._market_data.append(item) self.running_time = str(item.name[0]) self.on_bar(item) data.data.apply(x1, axis=1) def debug_currenttick(self, freq): data = QA.QA_fetch_get_future_transaction_realtime( 'tdx', self.code.upper()) self.running_mode = 'backtest' self.database = pymongo.MongoClient(mongo_ip).QUANTAXIS user = QA_User(username="******", password='******') port = user.new_portfolio(self.portfolio) self.strategy_id = self.strategy_id + 'currenttick_{}_{}'.format(str(datetime.date.today()), freq) self.acc = port.new_accountpro( account_cookie=self.strategy_id, init_cash=self.init_cash, market_type=self.market_type) self.positions = self.acc.get_position(self.code) data = data.assign(price=data.price/1000).loc[:, ['code', 'price', 'volume']].resample( freq).apply({'code': 'last', 'price': 'ohlc', 'volume': 'sum'}).dropna() data.columns = data.columns.droplevel(0) data = data.reset_index().set_index(['datetime', 'code']) def x1(item): self.latest_price[item.name[1]] = item['close'] if str(item.name[0])[0:10] != str(self.running_time)[0:10]: self.on_dailyclose() self.on_dailyopen() self._on_1min_bar() self._market_data.append(item) self.running_time = str(item.name[0]) self.on_bar(item) data.apply(x1, axis=1) def debug_histick(self, freq): data = QA.QA_fetch_get_future_transaction( 'tdx', self.code.upper(), self.start, self.end) self.running_mode = 'backtest' self.database = pymongo.MongoClient(mongo_ip).QUANTAXIS user = QA_User(username="******", password='******') port = user.new_portfolio(self.portfolio) self.strategy_id = self.strategy_id + 'histick_{}_{}_{}'.format(self.start, self.end, freq) self.acc = port.new_accountpro( account_cookie=self.strategy_id, init_cash=self.init_cash, market_type=self.market_type) self.positions = self.acc.get_position(self.code) data = data.assign(price=data.price/1000).loc[:, ['code', 'price', 'volume']].resample( freq).apply({'code': 'last', 'price': 'ohlc', 'volume': 'sum'}).dropna() data.columns = data.columns.droplevel(0) data = data.reset_index().set_index(['datetime', 'code']) def x1(item): self.latest_price[item.name[1]] = item['close'] if str(item.name[0])[0:10] != str(self.running_time)[0:10]: self.on_dailyclose() self.on_dailyopen() self._on_1min_bar() self._market_data.append(item) self.running_time = str(item.name[0]) self.on_bar(item) data.apply(x1, axis=1) def subscribe_data(self, code, frequence, data_host, data_port, data_user, data_password, model='py'): """[summary] Arguments: code {[type]} -- [description] frequence {[type]} -- [description] """ if frequence.endswith('min'): if model == 'py': self.sub = subscriber(exchange='realtime_{}_{}'.format( frequence, code), host=data_host, port=data_port, user=data_user, password=data_password) elif model == 'rust': self.sub = subscriber_routing(exchange='realtime_{}'.format( code), routing_key = frequence, host=data_host, port=data_port, user=data_user, password=data_password) self.sub.callback = self.callback elif frequence.endswith('s'): import re self._num_cached = 2*int(re.findall(r'\d+',self.frequence)[0]) self.sub = subscriber_routing(exchange='CTPX', routing_key=code, host=data_host, port=data_port, user=data_user, password=data_password) self.sub.callback = self.second_callback elif frequence.endswith('tick'): self._num_cached = 1 self.sub = subscriber_routing(exchange='CTPX', routing_key=code, host=data_host, port=data_port, user=data_user, password=data_password) self.sub.callback = self.tick_callback def subscribe_multi(self, codelist, frequence, data_host, data_port, data_user, data_password, model='py'): if frequence.endswith('min'): if model == 'rust': self.sub = subscriber_routing(exchange='realtime_{}'.format( codelist[0]), routing_key=frequence, host=data_host, port=data_port, user=data_user, password=data_password) for item in codelist[1:]: self.sub.add_sub(exchange='realtime_{}'.format( item), routing_key=frequence) elif model == 'py': self.sub = subscriber_routing(exchange='realtime_{}'.format( codelist[0].lower()), routing_key=frequence, host=data_host, port=data_port, user=data_user, password=data_password) for item in codelist[1:]: print(item) self.sub.add_sub(exchange='realtime_{}'.format( item.lower()), routing_key=frequence) self.sub.callback = self.callback elif frequence.endswith('tick'): self._num_cached = 1 self.sub = subscriber_routing(exchange='CTPX', routing_key=codelist[0].lower(), host=data_host, port=data_port, user=data_user, password=data_password) for item in codelist[1:]: print(item) self.sub.add_sub(exchange='CTPX', routing_key=item.lower()) self.sub.callback = self.tick_callback @property def old_data(self): return self._old_data def update(self): """ 此处是切换bar的时候的节点 """ self._old_data = self._market_data self._on_1min_bar() @property def market_datetime(self): return self.market_data.index.levels[0] @property def market_data(self): if self.running_mode == 'sim': return self._market_data elif self.running_mode == 'backtest': return pd.concat(self._market_data[-100:], axis=1, sort=False).T def force_close(self): # 强平 if self.positions.volume_long > 0: self.send_order('SELL', 'CLOSE', price=self.positions.last_price, volume=self.positions.volume_long) if self.positions.volume_short > 0: self.send_order('BUY', 'CLOSE', price=self.positions.last_price, volume=self.positions.volume_short) def upcoming_data(self, new_bar): """upcoming_bar : Arguments: new_bar {pd.DataFrame} -- [description] """ code = new_bar.index.levels[1][0] if len(self._old_data)> 0: self._market_data = pd.concat([self._old_data, new_bar], sort=False) else: self._market_data = new_bar #QA.QA_util_log_info(self._market_data) if self.isupdate: self.update() self.isupdate = False self.update_account() if isinstance(self.code, str): self.positions.on_price_change(float(self.latest_price[code])) else: for item in self.code: self.acc.get_position(item).on_price_change(float(self.latest_price[code])) self.on_bar(json.loads(new_bar.reset_index().to_json(orient='records'))[0]) def ind2str(self, ind, ind_type): z = ind.tail(1).reset_index().to_dict(orient='records')[0] return json.dumps({'topic': ind_type, 'code': self.code, 'type': self.frequence, 'data': z}) def second_callback(self, a, b, c, body): """在strategy的callback中,我们需要的是 1. 更新数据 2. 更新bar 3. 更新策略状态 4. 推送事件 Arguments: a {[type]} -- [description] b {[type]} -- [description] c {[type]} -- [description] body {[type]} -- [description] second ==> 2*second tick b'{"ask_price_1": 4145.0, "ask_price_2": 0, "ask_price_3": 0, "ask_price_4": 0, "ask_price_5": 0, "ask_volume_1": 69, "ask_volume_2": 0, "ask_volume_3": 0, "ask_volume_4": 0, "ask_volume_5": 0, "average_price": 61958.14258714826, "bid_price_1": 4143.0, "bid_price_2": 0, "bid_price_3": 0, "bid_price_4": 0, "bid_price_5": 0, "bid_volume_1": 30, "bid_volume_2": 0, "bid_volume_3": 0, "bid_volume_4": 0, "bid_volume_5": 0, "datetime": "2019-11-20 01:57:08", "exchange": "SHFE", "gateway_name": "ctp", "high_price": 4152.0, "last_price": 4144.0, "last_volume": 0, "limit_down": 3872.0, "limit_up": 4367.0, "local_symbol": "ag1912.SHFE", "low_price": 4105.0, "name": "", "open_interest": 277912.0, "open_price": 4140.0, "preSettlementPrice": 4120.0, "pre_close": 4155.0, "symbol": "ag1912", "volume": 114288}' tick 会基于热数据的量 self._num_cached 来判断更新/重采样 """ self.new_data = json.loads(str(body, encoding='utf-8')) self._cached_data.append(self.new_data) self.latest_price[self.code] = self.new_data['last_price'] # if len(self._cached_data) == self._num_cached: # self.isupdate = True if len(self._cached_data) > 3*self._num_cached: # 控制缓存数据量 self._cached_data = self._cached_data[self._num_cached:] data= pd.DataFrame(self._cached_data).loc[:,['datetime','last_price', 'volume']] data = data.assign(datetime= pd.to_datetime(data.datetime)).set_index('datetime').resample( self.frequence).apply({'last_price': 'ohlc', 'volume': 'last'}).dropna() data.columns = data.columns.droplevel(0) data = data.assign(volume=data.volume.diff(), code=self.code) data = data.reset_index().set_index(['datetime', 'code']) self.acc.on_price_change(self.code, self.latest_price[self.code]) # .loc[:, ['open', 'high', 'low', 'close', 'volume', 'tradetime']] now = datetime.datetime.now() if now.hour == 20 and now.minute == 59 and now.second < 10: self.daily_func() time.sleep(10) self.running_time = self.new_data['datetime'] #print(data.iloc[-1].index[0]) if self.dt != data.index[-1][0]: self.isupdate = True self.dt = data.index[-1][0] self.upcoming_data(data.tail(1)) def tick_callback(self, a, b, c, body): self.new_data = json.loads(str(body, encoding='utf-8')) self.on_tick(self.new_data) def get_code_marketdata(self, code): return self.market_data.loc[(slice(None), code), :] def get_current_marketdata(self): return self.market_data.loc[(self.running_time, slice(None)), :] def callback(self, a, b, c, body): """在strategy的callback中,我们需要的是 1. 更新数据 2. 更新bar 3. 更新策略状态 4. 推送事件 Arguments: a {[type]} -- [description] b {[type]} -- [description] c {[type]} -- [description] body {[type]} -- [description] """ self.new_data = json.loads(str(body, encoding='utf-8')) #print(self.new_data) self.latest_price[self.new_data['code']] = self.new_data['close'] if self.dt != str(self.new_data['datetime'])[0:16]: # [0:16]是分钟线位数 print('update!!!!!!!!!!!!') self.dt = str(self.new_data['datetime'])[0:16] self.isupdate = True self.acc.on_price_change(self.new_data['code'], self.new_data['close']) # .loc[:, ['open', 'high', 'low', 'close', 'volume', 'tradetime']] bar = pd.DataFrame([self.new_data]).set_index(['datetime', 'code']) now = datetime.datetime.now() if now.hour == 20 and now.minute == 59 and now.second < 10: self.daily_func() time.sleep(10) # res = self.job_control.find_one( # {'strategy_id': self.strategy_id, 'strategy_id': self.strategy_id}) # self.control_status(res) self.running_time = self.new_data['datetime'] print('send to upcoming data') self.upcoming_data(bar) def control_status(self, res): print(res) def add_subscriber(self, qaproid): """Add a subscriber 增加订阅者的QAPRO_ID """ self.subscriber_client.insert_one( {'strategy_id': self.strategy_id, 'user_id': qaproid}) @property def subscriber_list(self): """订阅者 Returns: [type] -- [description] """ return list(set([item['user_id'] for item in self.subscriber_client.find({'strategy_id': self.strategy_id})])) def load_strategy(self): raise NotImplementedError def on_dailyopen(self): pass def on_dailyclose(self): pass def on_bar(self, bar): raise NotImplementedError def on_tick(self, tick): raise NotImplementedError def _on_1min_bar(self): #raise NotImplementedError if len(self._systemvar.keys()) > 0: self._signal.append(copy.deepcopy(self._systemvar)) try: self.on_1min_bar() except: pass def on_1min_bar(self): raise NotImplementedError def on_5min_bar(self): raise NotImplementedError def on_15min_bar(self): raise NotImplementedError def on_30min_bar(self): raise NotImplementedError def order_handler(self): self._orders = {} def daily_func(self): QA.QA_util_log_info('DAILY FUNC') def risk_check(self): pass def plot(self, name, data, format): """ plot是可以存储你的临时信息的接口, 后期会接入可视化 Arguments: name {[type]} -- [description] data {[type]} -- [description] format {[type]} -- [description] """ self._systemvar[name] = {'datetime': copy.deepcopy(str( self.running_time)), 'value': data, 'format': format} def get_code(self): if isinstance(self.code, str): return self.code else: return self.code[0] def check_order(self, direction, offset, code= None): """[summary] 同方向不开仓 只对期货市场做限制 buy - open sell - close """ if code == None: code = self.get_code() if self.market_type == QA.MARKET_TYPE.FUTURE_CN: if self.last_order_towards[code][direction] == str(offset): return False else: return True else: return True def on_ordererror(self, direction, offset, price, volume): print('order Error ') def receive_simpledeal(self, code: str, trade_time, trade_amount, direction, offset, trade_price, message='sell_open'): self.send_order(direction=direction, offset=offset, volume=trade_amount, price=trade_price, order_id=QA.QA_util_random_with_topic(self.strategy_id)) def send_order(self, direction='BUY', offset='OPEN', price=3925, volume=10, order_id='', code = None): if code == None: code = self.get_code() towards = eval('ORDER_DIRECTION.{}_{}'.format(direction, offset)) order_id = str(uuid.uuid4()) if order_id == '' else order_id if isinstance(price, float): pass elif isinstance(price, pd.Series): price = price.values[0] if self.running_mode == 'sim': # 在此处拦截无法下单的订单 if (direction == 'BUY' and self.latest_price[code] <= price) or (direction == 'SELL' and self.latest_price[code] >= price): QA.QA_util_log_info( '============ {} SEND ORDER =================='.format(order_id)) QA.QA_util_log_info('direction{} offset {} price{} volume{}'.format( direction, offset, price, volume)) if self.check_order(direction, offset, code=code): #self.last_order_towards = {'BUY': '', 'SELL': ''} self.last_order_towards[code][direction] = offset now = str(datetime.datetime.now()) order = self.acc.send_order( code=code, towards=towards, price=price, amount=volume, order_id=order_id) print(order) order['topic'] = 'send_order' self.pub.pub( json.dumps(order), routing_key=self.strategy_id) self.acc.make_deal(order) self.bar_order['{}_{}'.format( direction, offset)] = self.bar_id if self.send_wx: for user in self.subscriber_list: QA.QA_util_log_info(self.subscriber_list) try: requests.post('http://www.yutiansut.com/signal?user_id={}&template={}&strategy_id={}&realaccount={}&code={}&order_direction={}&order_offset={}&price={}&volume={}&order_time={}'.format( user, "xiadan_report", self.strategy_id, self.acc.user_id, code.lower(), direction, offset, price, volume, now)) except Exception as e: QA.QA_util_log_info(e) else: QA.QA_util_log_info('failed in ORDER_CHECK') else: self.on_ordererror(direction, offset, price, volume) elif self.running_mode == 'backtest': self.bar_order['{}_{}'.format(direction, offset)] = self.bar_id if self.market_type == 'stock_cn': order = self.acc.send_order( code=self.code, amount=volume, time=self.running_time, towards=towards, price=price) order.trade(order.order_id, order.price, order.amount, order.datetime) else: self.acc.receive_simpledeal( code=self.code, trade_time=self.running_time, trade_towards=towards, trade_amount=volume, trade_price=price, order_id=order_id, realorder_id=order_id, trade_id=order_id) self.positions = self.acc.get_position(self.code) def update_account(self): if self.running_mode == 'sim': QA.QA_util_log_info('{} UPDATE ACCOUNT'.format( str(datetime.datetime.now()))) self.accounts = self.acc.account_msg self.orders = self.acc.orders if isinstance(self.code, str): self.positions = self.acc.get_position(self.code) else: pass self.trades = self.acc.trades self.updatetime = self.acc.dtstr elif self.running_mode == 'backtest': self.positions = self.acc.get_position(self.code) def get_exchange(self, code): return self.market_preset.get_exchange(code) def get_positions(self, code): if self.running_mode == 'sim': self.update_account() return self.acc.get_position(code) elif self.running_mode == 'backtest': return self.acc.get_position(code) def get_cash(self): if self.running_mode == 'sim': self.update_account() return self.accounts.get('available', '') elif self.running_mode == 'backtest': return self.acc.cash_available def run(self): while True: time.sleep(self.risk_check_gap) self.risk_check()
class QAStrategyCTABase(): def __init__(self, code='rb1905', frequence='1min', strategy_id='QA_STRATEGY', risk_check_gap=1, portfolio='default', start='2019-01-01', end='2019-10-21', init_cash=1000000, send_wx=False, data_host=eventmq_ip, data_port=eventmq_port, data_user=eventmq_username, data_password=eventmq_password, trade_host=eventmq_ip, trade_port=eventmq_port, trade_user=eventmq_username, trade_password=eventmq_password, taskid=None, mongo_ip=mongo_ip): self.trade_host = trade_host self.code = code self.frequence = frequence self.strategy_id = strategy_id self.portfolio = portfolio self.data_host = data_host self.data_port = data_port self.data_user = data_user self.data_password = data_password self.trade_host = trade_host self.trade_port = trade_port self.trade_user = trade_user self.trade_password = trade_password self.start = start self.end = end self.init_cash = init_cash self.taskid = taskid self.running_time = '' self.market_preset = QA.QAARP.MARKET_PRESET() self._market_data = [] self.risk_check_gap = risk_check_gap self.isupdate = False self.new_data = {} self._systemvar = {} self._signal = [] self.send_wx = send_wx self.last_order_towards = {'BUY': '', 'SELL': ''} self.dt = '' if isinstance(self.code, str): self.market_type = MARKET_TYPE.FUTURE_CN if re.search( r'[a-zA-z]+', self.code) else MARKET_TYPE.STOCK_CN else: self.market_type = MARKET_TYPE.FUTURE_CN if re.search( r'[a-zA-z]+', self.code[0]) else MARKET_TYPE.STOCK_CN self.bar_order = { 'BUY_OPEN': 0, 'SELL_OPEN': 0, 'BUY_CLOSE': 0, 'SELL_CLOSE': 0 } @property def bar_id(self): return len(self._market_data) def _debug_sim(self): self.running_mode = 'sim' if self.frequence.endswith('min'): self._old_data = QA.QA_fetch_get_future_min( 'tdx', self.code.upper(), QA.QA_util_get_last_day( QA.QA_util_get_real_date(str(datetime.date.today()))), str(datetime.datetime.now()), self.frequence)[:-1].set_index(['datetime', 'code']) self._old_data = self._old_data.assign( volume=self._old_data.trade ).loc[:, ['open', 'high', 'low', 'close', 'volume']] else: pass self.database = pymongo.MongoClient(mongo_ip).QAREALTIME self.client = self.database.account self.subscriber_client = self.database.subscribe self.acc = QIFI_Account(username=self.strategy_id, password=self.strategy_id, trade_host=mongo_ip, init_cash=self.init_cash) self.acc.initial() self.pub = publisher_routing(exchange='QAORDER_ROUTER', host=self.trade_host, port=self.trade_port, user=self.trade_user, password=self.trade_password) self.subscribe_data(self.code.lower(), self.frequence, self.data_host, self.data_port, self.data_user, self.data_password) self.database.strategy_schedule.job_control.update( {'strategy_id': self.strategy_id}, { 'strategy_id': self.strategy_id, 'taskid': self.taskid, 'filepath': os.path.abspath(__file__), 'status': 200 }, upsert=True) def debug_sim(self): self._debug_sim() threading.Thread(target=self.sub.start, daemon=True).start() def run_sim(self): self._debug_sim() self.sub.start() def run_backtest(self): self.debug() self.acc.save() risk = QA_Risk(self.acc) risk.save() try: """add rank flow if exist QARank是我们内部用于评价策略ELO的库 此处并不影响正常使用 """ from QARank import QA_Rank QA_Rank(self.acc).send() except: pass def debug(self): self.running_mode = 'backtest' self.database = pymongo.MongoClient(mongo_ip).QUANTAXIS user = QA_User(username="******", password='******') port = user.new_portfolio(self.portfolio) self.acc = port.new_accountpro(account_cookie=self.strategy_id, init_cash=self.init_cash, market_type=self.market_type) self.positions = self.acc.get_position(self.code) print(self.acc) print(self.acc.market_type) data = QA.QA_quotation(self.code.upper(), self.start, self.end, source=QA.DATASOURCE.MONGO, frequence=self.frequence, market=self.market_type, output=QA.OUTPUT_FORMAT.DATASTRUCT) def x1(item): # print(data) if str(item.name[0])[0:10] != str(self.running_time)[0:10]: self.on_dailyclose() self.on_dailyopen() if self.market_type == QA.MARKET_TYPE.STOCK_CN: print('backtest: Settle!') self.acc.settle() self._on_1min_bar() self._market_data.append(item) self.running_time = str(item.name[0]) self.on_bar(item) data.data.apply(x1, axis=1) def subscribe_data(self, code, frequence, data_host, data_port, data_user, data_password): """[summary] Arguments: code {[type]} -- [description] frequence {[type]} -- [description] """ self.sub = subscriber(exchange='realtime_{}_{}'.format( frequence, code), host=data_host, port=data_port, user=data_user, password=data_password) self.sub.callback = self.callback def subscribe_multi(self, codelist, frequence, data_host, data_port, data_user, data_password): self.sub = subscriber_topic(exchange='realtime_{}'.format(frequence), host=data_host, port=data_port, user=data_user, password=data_password) for item in codelist: self.sub.add_sub(exchange='realtime_{}'.format(frequence), routing_key=item) self.sub.callback = self.callback @property def old_data(self): return self._old_data def update(self): """ 此处是切换bar的时候的节点 """ self._old_data = self._market_data self._on_1min_bar() @property def market_datetime(self): return self.market_data.index.levels[0] @property def market_data(self): if self.running_mode == 'sim': return self._market_data elif self.running_mode == 'backtest': return pd.concat(self._market_data[-100:], axis=1, sort=False).T def force_close(self): # 强平 if self.positions.volume_long > 0: self.send_order('SELL', 'CLOSE', price=self.positions.last_price, volume=self.positions.volume_long) if self.positions.volume_short > 0: self.send_order('BUY', 'CLOSE', price=self.positions.last_price, volume=self.positions.volume_short) def upcoming_data(self, new_bar): """upcoming_bar : Arguments: new_bar {json} -- [description] """ self._market_data = pd.concat([self._old_data, new_bar]) # QA.QA_util_log_info(self._market_data) if self.isupdate: self.update() self.isupdate = False self.update_account() self.positions.on_price_change(float(self.new_data['close'])) self.on_bar(self.new_data) def ind2str(self, ind, ind_type): z = ind.tail(1).reset_index().to_dict(orient='records')[0] return json.dumps({ 'topic': ind_type, 'code': self.code, 'type': self.frequence, 'data': z }) def callback(self, a, b, c, body): """在strategy的callback中,我们需要的是 1. 更新数据 2. 更新bar 3. 更新策略状态 4. 推送事件 Arguments: a {[type]} -- [description] b {[type]} -- [description] c {[type]} -- [description] body {[type]} -- [description] """ self.new_data = json.loads(str(body, encoding='utf-8')) if self.dt != str(self.new_data['datetime'])[0:16]: print('update!!!!!!!!!!!!') self.dt = str(self.new_data['datetime'])[0:16] self.isupdate = True self.acc.on_price_change(self.code, self.new_data['close']) # .loc[:, ['open', 'high', 'low', 'close', 'volume', 'tradetime']] bar = pd.DataFrame([self.new_data]).set_index(['datetime', 'code']) now = datetime.datetime.now() if now.hour == 20 and now.minute == 59 and now.second < 10: self.daily_func() time.sleep(10) # res = self.job_control.find_one( # {'strategy_id': self.strategy_id, 'strategy_id': self.strategy_id}) # self.control_status(res) self.running_time = self.new_data['datetime'] self.upcoming_data(bar) def control_status(self, res): print(res) def add_subscriber(self, qaproid): """Add a subscriber 增加订阅者的QAPRO_ID """ self.subscriber_client.insert_one({ 'strategy_id': self.strategy_id, 'user_id': qaproid }) @property def subscriber_list(self): """订阅者 Returns: [type] -- [description] """ return list( set([ item['user_id'] for item in self.subscriber_client.find( {'strategy_id': self.strategy_id}) ])) def load_strategy(self): raise NotImplementedError def on_dailyopen(self): pass def on_dailyclose(self): pass def on_bar(self, bar): raise NotImplementedError def _on_1min_bar(self): #raise NotImplementedError if len(self._systemvar.keys()) > 0: self._signal.append(copy.deepcopy(self._systemvar)) def on_1min_bar(self): raise NotImplementedError def on_5min_bar(self): raise NotImplementedError def on_15min_bar(self): raise NotImplementedError def on_30min_bar(self): raise NotImplementedError def order_handler(self): self._orders = {} def daily_func(self): QA.QA_util_log_info('DAILY FUNC') def risk_check(self): pass def plot(self, name, data, format): """ plot是可以存储你的临时信息的接口, 后期会接入可视化 Arguments: name {[type]} -- [description] data {[type]} -- [description] format {[type]} -- [description] """ self._systemvar[name] = { 'datetime': copy.deepcopy(str(self.running_time)), 'value': data, 'format': format } def check_order(self, direction, offset): """[summary] 同方向不开仓 只对期货市场做限制 buy - open sell - close """ if self.market_type == QA.MARKET_TYPE.FUTURE_CN: if self.last_order_towards[direction] == str(offset): return False else: return True else: return True def receive_simpledeal(self, code: str, trade_time, trade_amount, direction, offset, trade_price, message='sell_open'): self.send_order(direction=direction, offset=offset, volume=trade_amount, price=trade_price, order_id=QA.QA_util_random_with_topic( self.strategy_id)) def send_order( self, direction='BUY', offset='OPEN', price=3925, volume=10, order_id='', ): towards = eval('ORDER_DIRECTION.{}_{}'.format(direction, offset)) order_id = str(uuid.uuid4()) if order_id == '' else order_id if isinstance(price, float): pass elif isinstance(price, pd.Series): price = price.values[0] if self.running_mode == 'sim': QA.QA_util_log_info( '============ {} SEND ORDER =================='.format( order_id)) QA.QA_util_log_info( 'direction{} offset {} price{} volume{}'.format( direction, offset, price, volume)) if self.check_order(direction, offset): self.last_order_towards = {'BUY': '', 'SELL': ''} self.last_order_towards[direction] = offset now = str(datetime.datetime.now()) order = self.acc.send_order(code=self.code, towards=towards, price=price, amount=volume, order_id=order_id) order['topic'] = 'send_order' self.pub.pub(json.dumps(order), routing_key=self.strategy_id) self.acc.make_deal(order) self.bar_order['{}_{}'.format(direction, offset)] = self.bar_id if self.send_wx: for user in self.subscriber_list: QA.QA_util_log_info(self.subscriber_list) try: "oL-C4w2WlfyZ1vHSAHLXb2gvqiMI" """http://www.yutiansut.com/signal?user_id=oL-C4w1HjuPRqTIRcZUyYR0QcLzo&template=xiadan_report&\ strategy_id=test1&realaccount=133496&code=rb1910&order_direction=BUY&\ order_offset=OPEN&price=3600&volume=1&order_time=20190909 """ requests.post( 'http://www.yutiansut.com/signal?user_id={}&template={}&strategy_id={}&realaccount={}&code={}&order_direction={}&order_offset={}&price={}&volume={}&order_time={}' .format(user, "xiadan_report", self.strategy_id, self.acc.user_id, self.code.lower(), direction, offset, price, volume, now)) except Exception as e: QA.QA_util_log_info(e) else: QA.QA_util_log_info('failed in ORDER_CHECK') elif self.running_mode == 'backtest': self.bar_order['{}_{}'.format(direction, offset)] = self.bar_id self.acc.receive_simpledeal(code=self.code, trade_time=self.running_time, trade_towards=towards, trade_amount=volume, trade_price=price, order_id=order_id) self.positions = self.acc.get_position(self.code) def update_account(self): if self.running_mode == 'sim': QA.QA_util_log_info('{} UPDATE ACCOUNT'.format( str(datetime.datetime.now()))) self.accounts = self.acc.account_msg self.orders = self.acc.orders self.positions = self.acc.get_position(self.code) self.trades = self.acc.trades self.updatetime = self.acc.dtstr elif self.running_mode == 'backtest': self.positions = self.acc.get_position(self.code) def get_exchange(self, code): return self.market_preset.get_exchange(code) def get_positions(self, code): if self.running_mode == 'sim': self.update_account() return self.positions elif self.running_mode == 'backtest': return self.acc.get_position(code) def get_cash(self): if self.running_mode == 'sim': self.update_account() return self.accounts.get('available', '') elif self.running_mode == 'backtest': return self.acc.cash_available def run(self): while True: time.sleep(self.risk_check_gap) self.risk_check()
class QAStrategyStockBase(QAStrategyCTABase): def __init__(self, code=['000001'], frequence='1min', strategy_id='QA_STRATEGY', risk_check_gap=1, portfolio='default', start='2019-01-01', end='2019-10-21', send_wx=False, market_type='stock_cn', data_host=eventmq_ip, data_port=eventmq_port, data_user=eventmq_username, data_password=eventmq_password, trade_host=eventmq_ip, trade_port=eventmq_port, trade_user=eventmq_username, trade_password=eventmq_password, taskid=None, mongo_ip=mongo_ip): super().__init__(code=code, frequence=frequence, strategy_id=strategy_id, risk_check_gap=risk_check_gap, portfolio=portfolio, start=start, end=end, send_wx=send_wx, data_host=eventmq_ip, data_port=eventmq_port, data_user=eventmq_username, data_password=eventmq_password, trade_host=eventmq_ip, trade_port=eventmq_port, trade_user=eventmq_username, trade_password=eventmq_password, taskid=taskid, mongo_ip=mongo_ip) self.code = code self.send_wx = send_wx def subscribe_data(self, code, frequence, data_host, data_port, data_user, data_password): """[summary] Arguments: code {[type]} -- [description] frequence {[type]} -- [description] """ self.sub = subscriber_topic(exchange='realtime_stock_{}'.format( frequence), host=data_host, port=data_port, user=data_user, password=data_password, routing_key='') for item in code: self.sub.add_sub(exchange='realtime_stock_{}'.format( frequence), routing_key=item) self.sub.callback = self.callback def upcoming_data(self, new_bar): """upcoming_bar : Arguments: new_bar {json} -- [description] """ self._market_data = pd.concat([self._old_data, new_bar]) # QA.QA_util_log_info(self._market_data) if self.isupdate: self.update() self.isupdate = False self.update_account() # self.positions.on_price_change(float(new_bar['close'])) self.on_bar(new_bar) def ind2str(self, ind, ind_type): z = ind.tail(1).reset_index().to_dict(orient='records')[0] return json.dumps({'topic': ind_type, 'code': self.code, 'type': self.frequence, 'data': z}) def callback(self, a, b, c, body): """在strategy的callback中,我们需要的是 1. 更新数据 2. 更新bar 3. 更新策略状态 4. 推送事件 Arguments: a {[type]} -- [description] b {[type]} -- [description] c {[type]} -- [description] body {[type]} -- [description] """ self.new_data = json.loads(str(body, encoding='utf-8')) self.latest_price[self.new_data['code']] = self.new_data['close'] self.running_time = self.new_data['datetime'] if self.dt != str(self.new_data['datetime'])[0:16]: # [0:16]是分钟线位数 print('update!!!!!!!!!!!!') self.dt = str(self.new_data['datetime'])[0:16] self.isupdate = True self.acc.on_price_change(self.new_data['code'], self.new_data['close']) bar = pd.DataFrame([self.new_data]).set_index(['datetime', 'code'] ).loc[:, ['open', 'high', 'low', 'close', 'volume']] self.upcoming_data(bar) def _debug_sim(self): self.running_mode = 'sim' self._old_data = QA.QA_fetch_stock_min(self.code, QA.QA_util_get_last_day( QA.QA_util_get_real_date(str(datetime.date.today()))), str(datetime.datetime.now()), format='pd', frequence=self.frequence).set_index(['datetime', 'code']) self._old_data = self._old_data.loc[:, [ 'open', 'high', 'low', 'close', 'volume']] self.database = pymongo.MongoClient(mongo_ip).QAREALTIME self.client = self.database.account self.subscriber_client = self.database.subscribe self.acc = QIFI_Account( username=self.strategy_id, password=self.strategy_id, trade_host=mongo_ip) self.acc.initial() self.pub = publisher_routing(exchange='QAORDER_ROUTER', host=self.trade_host, port=self.trade_port, user=self.trade_user, password=self.trade_password) self.subscribe_data(self.code, self.frequence, self.data_host, self.data_port, self.data_user, self.data_password) self.database.strategy_schedule.job_control.update( {'strategy_id': self.strategy_id}, {'strategy_id': self.strategy_id, 'taskid': self.taskid, 'filepath': os.path.abspath(__file__), 'status': 200}, upsert=True) # threading.Thread(target=, daemon=True).start() self.sub.start() def run(self): while True: pass def debug(self): self.running_mode = 'backtest' self.database = pymongo.MongoClient(mongo_ip).QUANTAXIS user = QA_User(username="******", password='******') port = user.new_portfolio(self.portfolio) self.acc = port.new_accountpro( account_cookie=self.strategy_id, init_cash=self.init_cash, market_type=self.market_type) #self.positions = self.acc.get_position(self.code) print(self.acc) print(self.acc.market_type) data = QA.QA_quotation(self.code, self.start, self.end, source=QA.DATASOURCE.MONGO, frequence=self.frequence, market=self.market_type, output=QA.OUTPUT_FORMAT.DATASTRUCT) def x1(item): # print(data) self._on_1min_bar() self._market_data.append(item) if str(item.name[0])[0:10] != str(self.running_time)[0:10]: if self.market_type == QA.MARKET_TYPE.STOCK_CN: print('backtest: Settle!') self.acc.settle() self.running_time = str(item.name[0]) self.on_bar(item) data.data.apply(x1, axis=1) def update_account(self): if self.running_mode == 'sim': QA.QA_util_log_info('{} UPDATE ACCOUNT'.format( str(datetime.datetime.now()))) self.accounts = self.acc.account_msg self.orders = self.acc.orders self.positions = self.acc.positions self.trades = self.acc.trades self.updatetime = self.acc.dtstr elif self.running_mode == 'backtest': #self.positions = self.acc.get_position(self.code) self.positions = self.acc.positions def send_order(self, direction='BUY', offset='OPEN', code=None, price=3925, volume=10, order_id='',): towards = eval('ORDER_DIRECTION.{}_{}'.format(direction, offset)) order_id = str(uuid.uuid4()) if order_id == '' else order_id if self.market_type == QA.MARKET_TYPE.STOCK_CN: """ 在此对于股票的部分做一些转换 """ if towards == ORDER_DIRECTION.SELL_CLOSE: towards = ORDER_DIRECTION.SELL elif towards == ORDER_DIRECTION.BUY_OPEN: towards = ORDER_DIRECTION.BUY if isinstance(price, float): pass elif isinstance(price, pd.Series): price = price.values[0] if self.running_mode == 'sim': QA.QA_util_log_info( '============ {} SEND ORDER =================='.format(order_id)) QA.QA_util_log_info('direction{} offset {} price{} volume{}'.format( direction, offset, price, volume)) if self.check_order(direction, offset): self.last_order_towards = {'BUY': '', 'SELL': ''} self.last_order_towards[direction] = offset now = str(datetime.datetime.now()) order = self.acc.send_order( code=code, towards=towards, price=price, amount=volume, order_id=order_id) order['topic'] = 'send_order' self.pub.pub( json.dumps(order), routing_key=self.strategy_id) self.acc.make_deal(order) self.bar_order['{}_{}'.format(direction, offset)] = self.bar_id if self.send_wx: for user in self.subscriber_list: QA.QA_util_log_info(self.subscriber_list) try: "oL-C4w2WlfyZ1vHSAHLXb2gvqiMI" """http://www.yutiansut.com/signal?user_id=oL-C4w1HjuPRqTIRcZUyYR0QcLzo&template=xiadan_report&\ strategy_id=test1&realaccount=133496&code=rb1910&order_direction=BUY&\ order_offset=OPEN&price=3600&volume=1&order_time=20190909 """ requests.post('http://www.yutiansut.com/signal?user_id={}&template={}&strategy_id={}&realaccount={}&code={}&order_direction={}&order_offset={}&price={}&volume={}&order_time={}'.format( user, "xiadan_report", self.strategy_id, self.acc.user_id, code, direction, offset, price, volume, now)) except Exception as e: QA.QA_util_log_info(e) else: QA.QA_util_log_info('failed in ORDER_CHECK') elif self.running_mode == 'backtest': self.bar_order['{}_{}'.format(direction, offset)] = self.bar_id self.acc.receive_simpledeal( code=code, trade_time=self.running_time, trade_towards=towards, trade_amount=volume, trade_price=price, order_id=order_id)
code = row['code'] code = code.replace(code[0:2],'') print('code', code) d=QA.QA_fetch_stock_day_adv(code=[code],start=day,end=day) d=d.data print(d) close_price = d.xs((day,code))['close'] open_price = d.xs((day,code))['open'] buy_vol = int(money_stock/num/open_price/100)*100 print('buy_vol ',buy_vol,'code', code) date = day+' 09:30:03' acc.on_price_change(code, open_price, date) print('pos',acc.positions) acc.on_price_change(code, close_price, date) buy_order = acc.send_order(code, buy_vol, open_price, ORDER_DIRECTION.BUY,datetime=date+' 09:30:04') #order = acc.send_order('000001', 100, price, QIFIAccount.ORDER_DIRECTION.BUY, datetime=date+' 09:58:00') #撮合成交 #acc.receive_deal(r['instrument_id'], 11.8, r['volume'], r['towards'],acc.dtstr, order_id=buy_order['order_id'], trade_id=str(uuid.uuid4())) #全部成交 acc.make_deal(buy_order) #settle后才会在可视化 acc.settle()