async def process_kline(self, data): """ process kline data """ if not self._klines_init: logger.info("klines not init. current:", len(self.klines), caller=self) return channel = data.get("ch") symbol = self._c_to_s[channel] d = data.get("tick") # print("process_kline", symbol) # print(d) info = { "platform": self._platform, "id": int(d["id"]), "symbol": symbol, "open": "%.8f" % d["open"], "high": "%.8f" % d["high"], "low": "%.8f" % d["low"], "close": "%.8f" % d["close"], "volume": int(d["vol"]), "amount": "%.8f" % d["amount"], "timestamp": int(data.get("ts")), "kline_type": MARKET_TYPE_KLINE } kline = Kline(**info) if kline.id == self._klines[-1].id: self._klines.pop() self._klines.append(kline) SingleTask.run(self._kline_update_callback, copy.copy(kline))
def ticker(self): """ 启动心跳, 每interval间隔执行一次 """ self._count += 1 # 打印心跳次数 if self._print_interval > 0: if self._count % int(self._print_interval) == 0: logger.info("do server heartbeat, count:", self._count, caller=self) # 设置下一次心跳回调 asyncio.get_event_loop().call_later(self._interval, self.ticker) # 执行任务回调 for task_id, task in self._tasks.items(): interval = task["interval"] if self._count % int(interval) != 0: continue func = task["func"] args = task["args"] kwargs = task["kwargs"] kwargs["task_id"] = task_id kwargs["heart_beat_count"] = self._count asyncio.get_event_loop().create_task(func(*args, **kwargs))
async def place_orders(self): """ 下单 """ orders_data = [] if self.trader.position and self.trader.position.short_quantity: # 平空单 price = round(self.ask1_price - 0.1, 1) quantity = -self.trader.position.short_quantity action = ORDER_ACTION_BUY new_price = str(price) # 将价格转换为字符串,保持精度 if quantity: orders_data.append({"price": new_price, "quantity": quantity, "action": action, "order_type": ORDER_TYPE_LIMIT, "lever_rate": 1}) self.last_ask_price = self.ask1_price if self.trader.assets and self.trader.assets.assets.get(self.raw_symbol): # 开空单 price = round(self.bid1_price + 0.1, 1) volume = float(self.trader.assets.assets.get(self.raw_symbol).get("free")) * price // 100 if volume >= 1: quantity = - volume # 空1张 action = ORDER_ACTION_SELL new_price = str(price) # 将价格转换为字符串,保持精度 if quantity: orders_data.append({"price": new_price, "quantity": quantity, "action": action, "order_type": ORDER_TYPE_LIMIT, "lever_rate": 1}) self.last_bid_price = self.bid1_price if orders_data: order_nos, error = await self.trader.create_orders(orders_data) if error: logger.error(self.strategy, "create future order error! error:", error, caller=self) logger.info(self.strategy, "create future orders success:", order_nos, caller=self)
async def on_ticker(self, *args, **kwargs): if not self.trade_init_result: logger.error("trade not init.", caller=self) return if not self.market.init_data(): logger.error("not init market data.", caller=self) return if not self.trader.init_data(): logger.error("not init trader data.", caller=self) return klines = self.market.klines if klines[-1].id == self.last_open_order_time: logger.info("haved order. ordertime:", self.last_open_order_time, caller=self) return ma_point = interval_handler(values=klines, periods=self.periods, vtype="close") if ma_point[self.periods[0]][1] < ma_point[self.periods[1]][1]: if ma_point[self.periods[0]][0] >= ma_point[self.periods[1]][0]: print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), "开多平空") elif ma_point[self.periods[0]][1] > ma_point[self.periods[1]][1]: if ma_point[self.periods[0]][0] <= ma_point[self.periods[1]][0]: print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), "开空平多")
async def cancel_orders(self): """ 取消订单 """ order_nos = [orderno for orderno in self.trader.orders] #if order_nos and self.last_mark_price != self.mark_price: if order_nos: _, errors = await self.trader.revoke_order(*order_nos) if errors: logger.error(self.strategy, "cancel option order error! error:", errors, caller=self) # 出错则取消所有挂单 _, errors = await self.trader.revoke_order() if errors: logger.error(self.strategy, "cancel all option orders error! error:", errors, caller=self) else: logger.info(self.strategy, "cancel all option orders success!", caller=self) else: logger.info(self.strategy, "cancel option order:", order_nos, caller=self)
async def place_orders(self): """ 下单 """ orders_data = [] if self.trader.position and self.trader.position.short_quantity: # 平空单 price = round(self.mark_price - self.spread, 1) quantity = -self.trader.position.short_quantity action = ORDER_ACTION_BUY new_price = str(price) # 将价格转换为字符串,保持精度 if quantity: orders_data.append({"price": new_price, "quantity": quantity, "action": action, "order_type": ORDER_TYPE_LIMIT }) self.last_mark_price = self.mark_price if self.trader.position and self.trader.position.long_quantity: # 平多单 price = round(self.mark_price + self.spread, 1) quantity = self.trader.position.long_quantity action = ORDER_ACTION_SELL new_price = str(price) # 将价格转换为字符串,保持精度 if quantity: orders_data.append({"price": new_price, "quantity": quantity, "action": action, "order_type": ORDER_TYPE_LIMIT }) self.last_mark_price = self.mark_price if self.trader.assets and self.trader.assets.assets.get(self.raw_symbol).get("free"): # 开空单 if self.trader.position and self.trader.position.short_quantity and self.trader.position.short_quantity >= self.max_quantity: logger.warn("option short position exceeds the max quantity: ", self.symbol, self.trader.position.short_quantity, self.max_quantity, caller=self) else: price = round(self.mark_price + self.spread, 1) volume = self.volume if volume: quantity = - volume # 空张 action = ORDER_ACTION_SELL new_price = str(price) # 将价格转换为字符串,保持精度 if quantity: orders_data.append({"price": new_price, "quantity": quantity, "action": action, "order_type": ORDER_TYPE_LIMIT }) self.last_mark_price = self.mark_price if self.trader.assets and self.trader.assets.assets.get(self.partition_symbol).get("free"): # 开多单 if self.trader.position and self.trader.position.long_quantity and self.trader.position.long_quantity >= self.max_quantity: logger.warn("option long position exceeds the max quantity: ", self.symbol, self.trader.position.long_quantity, self.max_quantity, caller=self) else: price = round(self.mark_price - self.spread, 1) volume = self.volume if volume: quantity = volume # 多张 action = ORDER_ACTION_BUY new_price = str(price) # 将价格转换为字符串,保持精度 if quantity: orders_data.append({"price": new_price, "quantity": quantity, "action": action, "order_type": ORDER_TYPE_LIMIT }) self.last_mark_price = self.mark_price if orders_data: order_nos, error = await self.trader.create_orders(orders_data) if error: logger.error(self.strategy, "create future order error! error:", error, caller=self) logger.info(self.strategy, "create future orders success:", order_nos, caller=self)
def start(self): """Start the event loop.""" def keyboard_interrupt(s, f): print("KeyboardInterrupt (ID: {}) has been caught. Cleaning up...".format(s)) self.loop.stop() signal.signal(signal.SIGINT, keyboard_interrupt) logger.info("start io loop ...", caller=self) self.loop.run_forever()
async def cancel_orders(self): """ 取消订单 """ order_nos = [ orderno for orderno in self.trader.orders ] if order_nos and self.last_ask_price != self.ask1_price: _, errors = await self.trader.revoke_order(*order_nos) if errors: logger.error(self.strategy,"cancel future order error! error:", errors, caller=self) else: logger.info(self.strategy,"cancel future order:", order_nos, caller=self)
def on_klines_update(self, kline: Kline): tt = pd.to_datetime(str(kline.timestamp), unit='ms') # 每隔5分钟计算一次乖离率, if tt.minute % 2 == 0: logger.info("kline update:", kline, caller=self) mytime = pd.Timestamp(tt.year, tt.month, tt.day, tt.hour, tt.minute) self.df.loc[mytime, 'close'] = kline.close logger.info(self.df.reset_index().to_json(orient="records"))
async def _connect(self): logger.info("url:", self._url, caller=self) METHOD_LOCKERS = {} proxy = config.proxy session = aiohttp.ClientSession() try: self.ws = await session.ws_connect(self._url, proxy=proxy) except aiohttp.client_exceptions.ClientConnectorError: logger.error("connect to server error! url:", self._url, caller=self) return asyncio.get_event_loop().create_task(self.connected_callback()) asyncio.get_event_loop().create_task(self.receive())
async def on_event_order_update(self, order: Order): """ 订单状态更新 """ logger.info("order update:", order, caller=self) notifyts = order.utime createts = order.ctime nowtime = time.time() self.total_count += 1 if nowtime * 1000 - notifyts > 1000: self.exceed_1s_count += 1 logger.error("order info:", order, caller=self) if nowtime * 1000 - notifyts > 100: self.exceed_100ms_count += 1 if nowtime * 1000 - notifyts > 50: self.exceed_50ms_count += 1
async def on_ticker(self, *args, **kwargs): """ 定时执行任务 """ ts_diff = int(time.time() * 1000) - self.last_orderbook_timestamp if ts_diff > self.orderbook_invalid_seconds * 1000: logger.warn("received orderbook timestamp exceed:", self.strategy, self.symbol, ts_diff, caller=self) return logger.info( f"{self.symbol}:{self.bid1_price}, {self.bid1_volume}, {self.ask1_price}, {self.ask1_volume}" ) await self.cancel_orders() await self.place_orders()
def initMongodb(host="127.0.0.1", port=27017, username="", password="", dbname="admin"): """ Initialize a connection pool for MongoDB. Args: host: Host for MongoDB server. port: Port for MongoDB server. username: Username for MongoDB server. password: Username for MongoDB server. dbname: DB name to connect for, default is `admin`. """ if username and password: uri = "mongodb://{username}:{password}@{host}:{port}/{dbname}".format(username=quote_plus(username), password=quote_plus(password), host=quote_plus(host), port=port, dbname=dbname) else: uri = "mongodb://{host}:{port}/{dbname}".format(host=host, port=port, dbname=dbname) mongo_client = motor.motor_asyncio.AsyncIOMotorClient(uri) global MONGO_CONN MONGO_CONN = mongo_client logger.info("create mongodb connection pool.")
async def process_hist_kline(self, data): """ process kline data """ # channel = data.get("ch") # symbol = self._c_to_s[channel] # d = data.get("tick") # info = { # "platform": self._platform, # "symbol": symbol, # "open": "%.8f" % d["open"], # "high": "%.8f" % d["high"], # "low": "%.8f" % d["low"], # "close": "%.8f" % d["close"], # "volume": "%.8f" % d["amount"], # "timestamp": int(data.get("ts")), # "kline_type": MARKET_TYPE_KLINE # } # kline = Kline(**info) # self._klines.append(kline) # SingleTask.run(self._kline_update_callback, copy.copy(kline)) # logger.debug("symbol:", symbol, "kline:", kline, caller=self) logger.info(data)
async def on_event_position_update(self, position: Position): """ 仓位更新 """ logger.info("position update:", position, caller=self)
async def on_event_asset_update(self, asset: Asset): """ 资产更新 """ logger.info("asset update:", asset, caller=self)
async def on_event_order_update(self, order: Order): """ 订单状态更新 """ logger.info("order update:", order, caller=self)
def _update_order(self, order_info): # print("_update_order") # print(order_info) if order_info["symbol"] != self._symbol: return if order_info["contract_type"] != self._contract_type: return order_no = str(order_info["order_id"]) status = order_info["status"] order = self._orders.get(order_no) if not order: if order_info["direction"] == "buy": if order_info["offset"] == "open": trade_type = TRADE_TYPE_BUY_OPEN else: trade_type = TRADE_TYPE_BUY_CLOSE else: if order_info["offset"] == "close": trade_type = TRADE_TYPE_SELL_CLOSE else: trade_type = TRADE_TYPE_SELL_OPEN info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "order_no": order_no, "action": ORDER_ACTION_BUY if order_info["direction"] == "buy" else ORDER_ACTION_SELL, "symbol": self._symbol + '/' + self._contract_type, "price": order_info["price"], "quantity": order_info["volume"], "trade_type": trade_type } order = Order(**info) self._orders[order_no] = order if status in [1, 2, 3]: order.status = ORDER_STATUS_SUBMITTED elif status == 4: order.status = ORDER_STATUS_PARTIAL_FILLED order.remain = int(order.quantity) - int(order_info["trade_volume"]) elif status == 6: order.status = ORDER_STATUS_FILLED order.remain = 0 elif status in [5, 7]: order.status = ORDER_STATUS_CANCELED order.remain = int(order.quantity) - int(order_info["trade_volume"]) else: return order.avg_price = order_info["trade_avg_price"] order.ctime = order_info["created_at"] order.utime = order_info["ts"] SingleTask.run(self._order_update_callback, copy.copy(order)) # Delete order that already completed. if order.status in [ORDER_STATUS_FAILED, ORDER_STATUS_CANCELED, ORDER_STATUS_FILLED]: self._orders.pop(order_no) # publish order logger.info("symbol:", order.symbol, "order:", order, caller=self)
def __init__(self): """ 初始化 """ self.strategy = config.strategy self.platform = config.accounts[0]["platform"] self.account = config.accounts[0]["account"] self.access_key = config.accounts[0]["access_key"] self.secret_key = config.accounts[0]["secret_key"] self.host = config.accounts[0]["host"] self.wss = config.accounts[0]["wss"] self.symbol = config.symbol self.contract_type = config.contract_type self.channels = config.markets[0]["channels"] self.orderbook_length = config.markets[0]["orderbook_length"] self.orderbooks_length = config.markets[0]["orderbooks_length"] self.klines_length = config.markets[0]["klines_length"] self.trades_length = config.markets[0]["trades_length"] self.market_wss = config.markets[0]["wss"] self.contract_size = 0.01 self.orderbook_invalid_seconds = 10 self.last_bid_price = 0 # 上次的买入价格 self.last_ask_price = 0 # 上次的卖出价格 self.last_orderbook_timestamp = 0 # 上次的orderbook时间戳 # self.raw_symbol = self.symbol.split('-')[0] self.ask1_price = 0 self.bid1_price = 0 self.ask1_volume = 0 self.bid1_volume = 0 self.tradeema = TradeEMA() # # 交易模块 # cc = { # "strategy": self.strategy, # "platform": self.platform, # "symbol": self.symbol, # "contract_type": self.contract_type, # "account": self.account, # "access_key": self.access_key, # "secret_key": self.secret_key, # "host": self.host, # "wss": self.wss, # "order_update_callback": self.on_event_order_update, # "asset_update_callback": self.on_event_asset_update, # "position_update_callback": self.on_event_position_update, # "init_success_callback": self.on_event_init_success_callback, # } # self.trader = Trade(**cc) # 行情模块 for s in self.symbol: logger.info(s) cc = { "platform": self.platform, "symbols": [s], "channels": self.channels, "orderbook_length": self.orderbook_length, "orderbooks_length": self.orderbooks_length, "klines_length": self.klines_length, "trades_length": self.trades_length, "wss": self.market_wss, "orderbook_update_callback": self.on_event_orderbook_update, "kline_update_callback": self.on_event_kline_update, "trade_update_callback": self.on_event_trade_update } market = Market(**cc) # 60秒执行1次 LoopRunTask.register(self.on_ticker, 10)
def _get_version(self): """ get software version """ logger.info("version:", VERSION, caller=self)
def stop(self): """Stop the event loop.""" logger.info("stop io loop.", caller=self) self.loop.stop()
async def cur_ts(**kwargs): logger.info(ts_to_datetime_str())
def stop(self): """Stop the event loop.""" logger.info("Stop \"{}\" io loop.".format(config.project), caller=self) self.loop.stop()
def _update_order(self, order_info): """ Order update. Args: order_info: Order information. """ if order_info["contract_code"] != self._symbol: return order_no = str(order_info["order_id"]) status = order_info["status"] order = self._orders.get(order_no) if not order: if order_info["direction"] == "buy": if order_info["offset"] == "open": trade_type = TRADE_TYPE_BUY_OPEN else: trade_type = TRADE_TYPE_BUY_CLOSE else: if order_info["offset"] == "close": trade_type = TRADE_TYPE_SELL_CLOSE else: trade_type = TRADE_TYPE_SELL_OPEN info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "order_no": order_no, "client_order_id": order_info.get("client_order_id"), "order_price_type": order_info.get("order_price_type"), "order_type": order_info["order_type"], "action": ORDER_ACTION_BUY if order_info["direction"] == "buy" else ORDER_ACTION_SELL, "symbol": self._symbol + '/' + self._contract_type, "price": order_info["price"], "quantity": order_info["volume"], "trade_type": trade_type } order = Order(**info) self._orders[order_no] = order order.trade_quantity = None order.trade_price = None if order_info.get("trade"): quantity = 0 price = 0 amount = 0 count = len(order_info.get("trade")) for trade in order_info.get("trade"): order.role = trade.get("role") quantity += float(trade.get("trade_volume")) amount += float( trade.get("trade_volume") * trade.get("trade_price")) price = amount / quantity order.trade_quantity = int(quantity) order.trade_price = price if status in [1, 2, 3]: order.status = ORDER_STATUS_SUBMITTED elif status == 4: order.status = ORDER_STATUS_PARTIAL_FILLED order.remain = int(order.quantity) - int( order_info["trade_volume"]) elif status == 6: order.status = ORDER_STATUS_FILLED order.remain = 0 elif status in [5, 7]: order.status = ORDER_STATUS_CANCELED order.remain = int(order.quantity) - int( order_info["trade_volume"]) else: return order.avg_price = order_info["trade_avg_price"] order.ctime = order_info["created_at"] order.utime = order_info["ts"] SingleTask.run(self._order_update_callback, copy.copy(order)) # Delete order that already completed. if order.status in [ ORDER_STATUS_FAILED, ORDER_STATUS_CANCELED, ORDER_STATUS_FILLED ]: self._orders.pop(order_no) # publish order logger.info("symbol:", order.symbol, "order:", order, caller=self)
async def delta_hedging(self, *args, **kwargs): """ delta hedge """ logger.debug("delta hedging", caller=self) option_delta = 0 assets, error = await self.trader.rest_api.get_asset_info( self.raw_symbol) if error: logger.error(self.strategy, "get option asset error! error:", error, caller=self) else: for item in assets["data"]: if item["symbol"] == self.raw_symbol: o_margin_balance = item["margin_balance"] o_delta = item["delta"] o_gamma = item["gamma"] o_theta = item["theta"] o_vega = item["vega"] option_delta = o_delta + o_margin_balance #增加delta对冲,使用期货对冲。 accounts, error = await self.future_trader.rest_api.get_account_position( self.raw_symbol) if error: logger.error(self.strategy, "get future account and position error! error:", error, caller=self) else: margin_balance = accounts["data"][0]["margin_balance"] long_position = 0 short_position = 0 delta_long = 0 delta_short = 0 long_last_price = 0 short_last_price = 0 for position in accounts["data"][0]["positions"]: if position["direction"] == "buy": long_position = position["volume"] long_cost_open = position["cost_open"] long_last_price = position["last_price"] if position["direction"] == "sell": short_position = position["volume"] short_cost_open = position["cost_open"] short_last_price = position["last_price"] if long_position: delta_long = self.future_volume_usd * int( long_position) / float(long_last_price) if short_position: delta_short = self.future_volume_usd * int( short_position) / float(short_last_price) future_delta = margin_balance - delta_short + delta_long t_delta = option_delta + future_delta orders_data = [] # 对冲对应数量的币 if abs(t_delta) >= self.delta_limit: if t_delta > 0: # 开空单 price = 0 volume = int(t_delta * long_last_price / self.future_volume_usd) if volume: quantity = -volume # action = ORDER_ACTION_SELL new_price = str(price) # 将价格转换为字符串,保持精度 if quantity: orders_data.append({ "price": new_price, "quantity": quantity, "action": action, "order_type": ORDER_TYPE_MARKET }) else: # 开多单 price = 0 volume = abs( int(t_delta * long_last_price / self.future_volume_usd)) if volume: quantity = volume # action = ORDER_ACTION_BUY new_price = str(price) # 将价格转换为字符串,保持精度 if quantity: orders_data.append({ "price": new_price, "quantity": quantity, "action": action, "order_type": ORDER_TYPE_MARKET }) if orders_data: order_nos, error = await self.future_trader.create_orders( orders_data) if error: logger.error(self.strategy, "create future order error! error:", error, caller=self) else: logger.info(self.strategy, "create future orders success:", order_nos, caller=self)