def handle_remain_orders(self, bs, price): ideal_order_in_active = False for coid, o in list(self.active_orders.items()): if bs != o.bs: continue if price == o.entrust_price and not o.last_cancel_time: ideal_order_in_active = True else: now = arrow.now().float_timestamp if not o.first_cancel_time: o.first_cancel_time = now factor = self.active_orders[coid].cancel_times + 1 if not o.last_cancel_time or now - o.last_cancel_time > self.config.cancel_order_interval * factor: if o.eoid: qb.fut(self.cancel_order(eoid=o.eoid)) else: qb.fut(self.cancel_order(coid=o.coid)) self.active_orders[coid].last_cancel_time = now self.active_orders[coid].cancel_times += 1 if self.active_orders[ coid].cancel_times > self.config.max_cancel_times: logging.warning( f'{o.coid} {o.eoid} cancel times exceed limit') self.active_orders.pop(o.coid, None) return ideal_order_in_active
async def do_action(self, bs: str, price: float, amt: float, force_maker: bool): ideal_order_in_active = self.handle_remain_orders(bs, price) if ideal_order_in_active: return if len(self.active_orders) > self.config.max_pending_orders: return now = arrow.now().float_timestamp if now - self.last_trade_time < self.config.trade_interval: return else: self.last_trade_time = now if await self.rc_work(): return if bs == 'b': amt = min(self.tk2.asks[0][1], self.config.place_amt) elif bs == 's': amt = min(self.tk2.bids[0][1], self.config.place_amt) opt = self.get_options(self.c1, bs, amt, force_maker=force_maker) coid = self.new_coid(self.c1, bs) o = Order() o.coid = coid o.entrust_price = price o.bs = bs o.entrust_amount = amt o.entrust_time = arrow.now().float_timestamp o.opt = opt ext = Extra() ext.tk1_bbo_place = self.tk1 ext.tk2_bbo_place = self.tk2 o.extra = ext self.active_orders[coid] = o qb.fut(self.place_maker_order(o))
async def run(self): await self.init() await self.update_info() self.asset_by_ws = self.asset_by_rest self.pos_by_ws = self.pos_by_rest await self.cancel_all() await self.config.sync() while True: await asyncio.sleep(1) if len(self.ticks) + len(self.bbo) != 2: logging.warning('waiting tick comes') continue else: break self.inited = True logging.info("all inited, start...") qb.fut(self.daemon_consume_update_bbo()) qb.fut(qb.autil.loop_call(self.update_info, 30, panic_on_fail=False)) qb.fut(qb.autil.loop_call(self.config.sync, 30)) qb.fut(qb.autil.loop_call(self.match_pos, 10))
def __enter__(self): self.start = arrow.now() def __exit__(self, *args, **kwargs): self.end = arrow.now() key = 'elapse' diff = (self.end - self.start).total_seconds() timerInflux.add_point(key=key, tags=self.tags, fields={'value': diff}) async def main(): print('main') # s = Strategy(stid=stid) # await s.run() async def play(): print('play demo') if __name__ == '__main__': qb.init(env='prod') docopt = docoptinit(__doc__) stid = str(docopt['--stid']) logging.info(f'stid: {stid}') if docopt['--play']: qb.fut(play()) else: qb.fut(main()) qb.run_forever()
async def order_callback(self, orig: qbxt.model.OrderUpdate): order = orig.order if order.contract == self.c1: # 如果不在程序肯定有问题 order_in_memory = self.active_orders.get(order.client_oid, None) if not order_in_memory: logging.warning( f'{order.client_oid}, {order.exchange_oid} not in active orders' ) return now = arrow.now().float_timestamp elapse = now - order_in_memory.entrust_time if order_in_memory.first_cancel_time: elapse = now - order_in_memory.first_cancel_time self.gauge('order-elapse', elapse, tags={'status': order.status}) if order.status == qbxt.model.Order.PENDING and order.contract == self.c1: return if 'deal' in order.status and order.contract == self.c1: # 如果不在程序肯定有问题 order_in_memory = self.active_orders.get(order.client_oid, None) if not order_in_memory: logging.warning( f'{order.client_oid}, {order.exchange_oid} not in active orders' ) return amt = order.dealt_amount - order_in_memory.dealt_amt self.active_orders[order.client_oid].dealt_amt = order.dealt_amount self.active_orders[order.client_oid].extra.tk1_bbo_dealt = self.tk1 self.active_orders[order.client_oid].extra.tk2_bbo_dealt = self.tk2 if amt > 0: bs = 'b' if order.bs == 's' else 's' qb.fut( self.place_hedge_order(bs, amt, order.average_dealt_price)) # c2_price = self.tk2.bid1 if order.bs == 'b' else self.tk2.ask1 self.gauge('place-price', order.entrust_price, {'bs': order.bs}, ts=order_in_memory.entrust_time) self.gauge('dealt-amt', amt, tags={'bs': order.bs}) self.gauge_order_extra(self.active_orders[order.client_oid]) if order.status == qbxt.model.Order.DEALT and order.contract == self.c2: c1_dealt_price = self.dealt_info.get(order.client_oid, None) if c1_dealt_price: self.gauge('dealt-diff', c1_dealt_price / order.average_dealt_price, tags={'bs': qb.util.op_bs(order.bs)}) self.dealt_info.pop(order.client_oid, None) if 'deal' in order.status: con = 'tick1' if order.contract == self.c1 else 'tick2' self.gauge('dealt', order.average_dealt_price, tags={ 'bs': order.bs, 'con_symbol': order.contract, 'con': con }) if order.status in qbxt.model.Order.END_SET and order.contract == self.c1: self.active_orders.pop(order.client_oid, None) return
async def main_callback(self): if not self.inited: return value = { 'tk1-bid1': self.tk1.bid1, 'tk1-ask1': self.tk1.ask1, 'tk2-bid1': self.tk2.bid1, 'tk2-ask1': self.tk2.ask1, 'tk-diff': self.tk1.middle / self.tk2.middle, 'bid-d-ask': self.tk1.bid1 / self.tk2.ask1, 'ask-d-bid': self.tk1.ask1 / self.tk2.bid1 } self.gauge('quote', value) core = math.pow(self.config.diff, -self.pos1 / self.config.amt) * self.config.middle core_a = self.tk2.ask1 * core core_b = self.tk2.bid1 * core # logging.info('c2: ',self.tk2.ask1/self.tk2.bid1) # logging.info('core_a/core_b: ',core_a/core_b) pb = core_b / math.sqrt(self.config.earn) * self.config.taker_return ps = core_a * math.sqrt(self.config.earn) / self.config.taker_return # logging.info('ps/pb:',ps,pb, ps/pb) tb = pb * self.config.taker_return ts = ps / self.config.taker_return tb = self.rounding(tb, self.min_change(self.c1), math.floor) ts = self.rounding(ts, self.min_change(self.c1), math.ceil) # mb = min(self.tk1.aks1- self.min_change(self.c1), pb * self.config.maker_return) # ms = max(self.tk1.bid1+ self.min_change(self.c1), ps / self.config.maker_return) # logging.info('before rounding', mb,ms, ms/mb) mb = pb * self.config.maker_return ms = ps / self.config.maker_return mb = self.rounding(mb, self.min_change(self.c1), math.floor) ms = self.rounding(ms, self.min_change(self.c1), math.ceil) # logging.info('after rouding', mb,ms, ms/mb) self.gauge('core', core) self.gauge('diff-ideal-b', { 'maker': mb / self.tk2.bid1, 'taker': tb / self.tk2.bid1 }) self.gauge('diff-ideal-s', { 'maker': ms / self.tk2.ask1, 'taker': ts / self.tk2.ask1 }) # pass if dryrun: return if self.pos1 < self.config.amt: if tb >= self.tk1.ask1: # logging.info(f'try to place taker b {tb}') qb.fut(self.do_action('b', tb, self.config.place_amt, False)) else: mb = min(mb, self.tk1.bid1) qb.fut( self.do_action('b', mb, self.config.place_amt, self.config.force_maker)) if -self.config.amt < self.pos1: if ts <= self.tk1.bid1: # logging.info(f'try to place taker s {ts}') qb.fut(self.do_action('s', ts, self.config.place_amt, False)) else: ms = max(ms, self.tk1.ask1) qb.fut( self.do_action('s', ms, self.config.place_amt, self.config.force_maker)) return
def rc_trigger(self, second, reason): if not self.rc_working: qb.fut(self.cancel_all()) logging.warning('rc trigger', reason, second, self.rc_until) self.gauge('rc-work', 1, {'type': reason}) self.rc_until = max(self.rc_until, arrow.now().shift(seconds=second))