class TestGeneralSocketMethods(unittest.TestCase): def setUp(self): self.socket = Socket(PAIR) def tearDown(self): self.socket.close() def test_bind(self): endpoint = self.socket.bind(SOCKET_ADDRESS) self.assertNotEqual(None, endpoint) def test_connect(self): endpoint = self.socket.connect(SOCKET_ADDRESS) self.assertNotEqual(None, endpoint) def test_is_open_is_true_when_open(self): self.assertTrue(self.socket.is_open()) def test_is_open_is_false_when_closed(self): self.socket.close() self.assertFalse(self.socket.is_open()) def test_set_and_get_int_option(self): expected = 500 self.socket.set_int_option(SOL_SOCKET, SNDBUF, expected) actual = self.socket.get_int_option(SOL_SOCKET, SNDBUF) self.assertEqual(expected, actual)
class TestGeneralSocketMethods(unittest.TestCase): def setUp(self): self.socket = Socket(PAIR) def tearDown(self): self.socket.close() def test_bind(self): endpoint = self.socket.bind(SOCKET_ADDRESS) self.assertNotEqual(None, endpoint) def test_connect(self): endpoint = self.socket.connect(SOCKET_ADDRESS) self.assertNotEqual(None, endpoint) def test_is_open_is_true_when_open(self): self.assertTrue(self.socket.is_open()) def test_is_open_is_false_when_closed(self): self.socket.close() self.assertFalse(self.socket.is_open()) def test_set_and_get_int_option(self): expected = 500 self.socket.set_int_option(SOL_SOCKET, SNDBUF, expected) actual = self.socket.get_int_option(SOL_SOCKET, SNDBUF) self.assertEqual(expected, actual)
def sender(queue, addresses, stype): """ Bind a queue to a connecting nanomsg socket's multiple endpoints in a separate thread. Parameters ---------- queue : Queue A Queue object to be emptied by sender socket addresses : list A list of strings of format '<protocol>://<ip>:<port>' to bind to stype : int One of the nanomsg scalability socket types: PUSH, PULL, PUB, SUB, PAIR Returns ------- nanomsg socket object """ with err.applicationbound(): out_sock = Socket(stype) out_sock.set_int_option(SOL_SOCKET, SNDTIMEO, 1000) for address in addresses: endpoint = out_sock.connect(address) out_endpoints.append(endpoint) def send_messages(): """ """ while True: try: out_sock.send(queue.get(block=True)) log.info("Message has been sent") except NanoMsgAPIError as e: log.debug(e) log.debug(dir(e)) receiver = threading.Thread(target=send_messages) receiver.start() return out_sock
def sender(queue, addresses, stype): """ Bind a queue to a connecting nanomsg socket's multiple endpoints in a separate thread. Parameters ---------- queue : Queue A Queue object to be emptied by sender socket addresses : list A list of strings of format '<protocol>://<ip>:<port>' to bind to stype : int One of the nanomsg scalability socket types: PUSH, PULL, PUB, SUB, PAIR Returns ------- nanomsg socket object """ with err.applicationbound(): out_sock = Socket(stype) out_sock.set_int_option(SOL_SOCKET, SNDTIMEO, 1000) for address in addresses: endpoint = out_sock.connect(address) out_endpoints.append(endpoint) def send_messages(): """ """ while True: try: out_sock.send(queue.get(block=True)) log.info("Message has been sent") except NanoMsgAPIError as e: log.debug(e) log.debug(dir(e)) receiver = threading.Thread(target=send_messages) receiver.start() return out_sock
class ServiceDiscovery(object): def __init__(self, port, deadline=5000): self.socket = Socket(SURVEYOR) self.port = port self.deadline = deadline self.services = defaultdict(set) def bind(self): self.socket.bind('tcp://172.30.42.174:%s' % self.port) self.socket.set_int_option(SURVEYOR, SURVEYOR_DEADLINE, self.deadline) def discover(self): if not self.socket.is_open(): return self.services self.services = defaultdict(set) self.socket.send('service query') while True: try: response = self.socket.recv() except NanoMsgAPIError: break service, address = response.split('|') self.services[service].add(address) return self.services def resolve(self, service): providers = self.services[service] if not providers: return None return random.choice(tuple(providers)) def close(self): self.socket.close()
class ServiceDiscovery(object): def __init__(self, port, deadline=5000): self.socket = Socket(SURVEYOR) self.port = port self.deadline = deadline self.services = defaultdict(set) def bind(self): self.socket.bind('tcp://*:%s' % self.port) self.socket.set_int_option(SURVEYOR, SURVEYOR_DEADLINE, self.deadline) def discover(self): if not self.socket.is_open(): return self.services self.services = defaultdict(set) self.socket.send('service query') while True: try: response = self.socket.recv() except NanoMsgAPIError: break service, address = response.split('|') self.services[service].add(address) return self.services def resolve(self, service): providers = self.services[service] if not providers: return None return random.choice(tuple(providers)) def close(self): self.socket.close()
def sender(queue, network, stype=PUSH): out_sock = Socket(stype) out_sock.set_int_option(SOL_SOCKET, SNDTIMEO, 1000) for node in network['nodes']: endpoint = out_sock.connect('tcp://{ip}:{port}'.format( ip=node['ip'], port=node['port'])) out_endpoints.append(endpoint) def send_messages(): while True: try: out_sock.send(queue.get(block=True)) print("Message has been sent") except NanoMsgAPIError as e: print(e) print(dir(e)) receiver = threading.Thread(target=send_messages) receiver.start() return out_sock
def sender(queue, network, stype=PUSH): out_sock = Socket(stype) out_sock.set_int_option(SOL_SOCKET, SNDTIMEO, 1000) for node in network['nodes']: endpoint = out_sock.connect('tcp://{ip}:{port}'.format(ip=node['ip'], port=node['port'])) out_endpoints.append(endpoint) def send_messages(): while True: try: out_sock.send(queue.get(block=True)) print("Message has been sent") except NanoMsgAPIError as e: print(e) print(dir(e)) receiver = threading.Thread(target=send_messages) receiver.start() return out_sock
class RecorderEngine(BaseEngine): """ market data recorder """ config_filename = "config_server.yaml" setting_filename = "data_recorder_setting.json" # init def __init__(self, configfile: str = '', gateway: str = "CTP.MD"): super(RecorderEngine, self).__init__(event_engine=EventEngine(10)) """ two sockets to send and recv msg """ self.__active = False self._thread = Thread(target=self._run) self.id = os.getpid() self.engine_type = EngineType.LIVE self._recv_sock = Socket(SUB) self._send_sock = Socket(PUSH) if configfile: self.config_filename = configfile if gateway: self.gateway = gateway filepath = Path.cwd().joinpath("etc/" + self.config_filename) with open(filepath, encoding='utf8') as fd: self._config = yaml.load(fd) self.tick_recordings = {} self.bar_recordings = {} self.bar_generators = {} self.contracts = {} self.subscribed = False self.dayswitched = False self.init_engine() # init functions def init_engine(self): self.init_nng() self.load_contract() self.load_setting() self.register_event() self.put_event() def init_nng(self): self._recv_sock.set_string_option(SUB, SUB_SUBSCRIBE, '') # receive msg start with all self._recv_sock.set_int_option(SOL_SOCKET, RCVTIMEO, 100) self._recv_sock.connect(self._config['serverpub_url']) self._send_sock.connect(self._config['serverpull_url']) def load_contract(self): contractfile = Path.cwd().joinpath("etc/ctpcontract.yaml") with open(contractfile, encoding='utf8') as fc: contracts = yaml.load(fc) print('loading contracts, total number:', len(contracts)) for sym, data in contracts.items(): contract = ContractData( symbol=data["symbol"], exchange=Exchange(data["exchange"]), name=data["name"], product=PRODUCT_CTP2VT[str(data["product"])], size=data["size"], pricetick=data["pricetick"], net_position=True if str(data["positiontype"]) == THOST_FTDC_PT_Net else False, long_margin_ratio=data["long_margin_ratio"], short_margin_ratio=data["short_margin_ratio"], full_symbol=data["full_symbol"]) # For option only if contract.product == Product.OPTION: contract.option_underlying = data["option_underlying"], contract.option_type = OPTIONTYPE_CTP2VT.get( str(data["option_type"]), None), contract.option_strike = data["option_strike"], contract.option_expiry = datetime.strptime( str(data["option_expiry"]), "%Y%m%d"), self.contracts[contract.full_symbol] = contract def load_setting(self): """""" setting = load_json(self.setting_filename) self.tick_recordings = setting.get("tick", {}) self.bar_recordings = setting.get("bar", {}) def save_setting(self): """""" setting = {"tick": self.tick_recordings, "bar": self.bar_recordings} save_json(self.setting_filename, setting) def register_event(self): """""" self.event_engine.register(EventType.TICK, self.process_tick_event) self.event_engine.register(EventType.CONTRACT, self.process_contract_event) self.event_engine.register(EventType.RECORDER_CONTROL, self.process_recordercontrol_event) self.event_engine.register(EventType.HEADER, self.process_general_event) self.event_engine.register(EventType.TIMER, self.process_timer_event) def init_subcribe(self, src: str = 'CTP.MD'): symset = set(self.tick_recordings.keys()) symset.update(self.bar_recordings.keys()) for sym in symset: self.subscribe(sym, src) self.subscribed = True # event handler def process_timer_event(self, event): # auto subscribe at 8:55, 20:55 nowtime = datetime.now().time() if (nowtime > time(hour=8, minute=50)) and (nowtime < time( hour=8, minute=51)) and (not self.subscribed): self.init_subcribe() self.dayswitched = False if (nowtime > time(hour=20, minute=50)) and (nowtime < time( hour=20, minute=51)) and (not self.subscribed): self.init_subcribe() self.dayswitched = False # reset at 16:00 and 3:00 if (nowtime > time(hour=16, minute=0)) and (nowtime < time( hour=16, minute=1)) and (not self.dayswitched): self.subscribed = False self.dayswitched = True if (nowtime > time(hour=3, minute=0)) and (nowtime < time( hour=3, minute=1)) and (not self.dayswitched): self.subscribed = False self.dayswitched = True def process_general_event(self, event): pass def process_tick_event(self, event: Event): """""" tick = event.data dayclosetime = tick.datetime.time() < time( hour=9, minute=0) and tick.datetime.time() > time(hour=8, minute=0) nightclosetime = tick.datetime.time() < time( hour=21, minute=0) and tick.datetime.time() > time(hour=16, minute=0) if dayclosetime or nightclosetime: return # exclude onrtnsubscribe return first tick which time not in trade time if (tick.open_price) and tick.last_price and tick.ask_price_1: if tick.full_symbol in self.tick_recordings: self.record_tick(tick) if tick.full_symbol in self.bar_recordings: bg = self.get_bar_generator(tick.full_symbol) bg.update_tick(tick) def process_contract_event(self, event: Event): """""" contract = event.data self.contracts[contract.full_symbol] = contract def process_recordercontrol_event(self, event: Event): msgtype = event.msg_type deslist = ['@*', str(self.id), '@' + str(self.id)] if (event.destination not in deslist): return elif (msgtype == MSG_TYPE.MSG_TYPE_RECORDER_STATUS): m = Event(type=EventType.RECORDER_CONTROL, des='@0', src=str(self.id), data=str(self.__active), msgtype=MSG_TYPE.MSG_TYPE_RECORDER_STATUS) self._send_sock.send(m.serialize()) self.put_event() elif (msgtype == MSG_TYPE.MSG_TYPE_RECORDER_ADD_TICK): full_symbol = event.data self.add_tick_recording(full_symbol, event.source) elif (msgtype == MSG_TYPE.MSG_TYPE_RECORDER_ADD_BAR): full_symbol = event.data self.add_bar_recording(full_symbol, event.source) elif (msgtype == MSG_TYPE.MSG_TYPE_RECORDER_REMOVE_TICK): full_symbol = event.data self.remove_tick_recording(full_symbol) elif (msgtype == MSG_TYPE.MSG_TYPE_RECORDER_REMOVE_BAR): full_symbol = event.data self.remove_bar_recording(full_symbol) elif (msgtype == MSG_TYPE.MSG_TYPE_RECORDER_START): self.init_subcribe() elif (msgtype == MSG_TYPE.MSG_TYPE_RECORDER_STOP): self.clear() elif (msgtype == MSG_TYPE.MSG_TYPE_RECORDER_RELOAD): pass elif (msgtype == MSG_TYPE.MSG_TYPE_RECORDER_RESET): pass elif (msgtype == MSG_TYPE.MSG_TYPE_RECORDER_GET_DATA): self.put_event() def put_event(self): """""" tick_symbols = list(self.tick_recordings.keys()) tick_symbols.sort() bar_symbols = list(self.bar_recordings.keys()) bar_symbols.sort() data = {"tick": tick_symbols, "bar": bar_symbols} msg = json.dumps(data) m = Event(type=EventType.RECORDER_CONTROL, data=msg, des='@0', src=str(self.id), msgtype=MSG_TYPE.MSG_TYPE_RECORDER_RTN_DATA) self._send_sock.send(m.serialize()) def add_bar_recording(self, full_symbol: str, src: str = 'CTP.MD'): """""" if full_symbol in self.bar_recordings: self.write_log(f"已在K线记录列表中:{full_symbol}") return contract = self.contracts.get(full_symbol, None) if not contract: self.write_log(f"找不到合约:{full_symbol}") return self.bar_recordings[full_symbol] = { "symbol": contract.symbol, "exchange": contract.exchange.value, "gateway_name": self.gateway } self.subscribe(full_symbol, src) self.save_setting() self.put_event() self.write_log(f"添加K线记录成功:{full_symbol}") def add_tick_recording(self, full_symbol: str, src: str = 'CTP.MD'): """""" if full_symbol in self.tick_recordings: self.write_log(f"已在Tick记录列表中:{full_symbol}") return contract = self.contracts.get(full_symbol, None) if not contract: self.write_log(f"找不到合约:{full_symbol}") return self.tick_recordings[full_symbol] = { "symbol": contract.symbol, "exchange": contract.exchange.value, "gateway_name": self.gateway } self.subscribe(full_symbol, src) self.save_setting() self.put_event() self.write_log(f"添加Tick记录成功:{full_symbol}") def remove_bar_recording(self, full_symbol: str): """""" if full_symbol not in self.bar_recordings: self.write_log(f"不在K线记录列表中:{full_symbol}") return self.bar_recordings.pop(full_symbol) self.save_setting() self.put_event() self.write_log(f"移除K线记录成功:{full_symbol}") def remove_tick_recording(self, full_symbol: str): """""" if full_symbol not in self.tick_recordings: self.write_log(f"不在Tick记录列表中:{full_symbol}") return self.tick_recordings.pop(full_symbol) self.save_setting() self.put_event() self.write_log(f"移除Tick记录成功:{full_symbol}") def record_tick(self, tick: TickData): """""" database_manager.save_tick_data([tick]) def record_bar(self, bar: BarData): """""" database_manager.save_bar_data([bar]) def get_bar_generator(self, full_symbol: str): """""" bg = self.bar_generators.get(full_symbol, None) if not bg: bg = BarGenerator(self.record_bar) self.bar_generators[full_symbol] = bg return bg def subscribe(self, full_symbol: str, src: str = 'CTP.MD'): contract = self.contracts.get(full_symbol, None) if contract: m = Event(type=EventType.SUBSCRIBE, msgtype=MSG_TYPE.MSG_TYPE_SUBSCRIBE_MARKET_DATA) m.destination = src m.source = str(self.id) req = SubscribeRequest() if src == 'CTP.MD': req.sym_type = SYMBOL_TYPE.CTP req.content = contract.symbol else: req.sym_type = SYMBOL_TYPE.FULL req.content = full_symbol m.data = req self._send_sock.send(m.serialize()) else: self.write_log(f"行情订阅失败,找不到合约{full_symbol}") def clear(self): self.bar_recordings.clear() self.tick_recordings.clear() self.save_setting() self.put_event() def _run(self): while self.__active: try: msgin = self._recv_sock.recv(flags=0) msgin = msgin.decode("utf-8") if msgin is not None and msgin.index('|') > 0: if msgin[0] == '@': print('recorder(pid = %d) rec @ msg:' % (self.id), msgin, 'at ', datetime.now()) if msgin[-1] == '\0': msgin = msgin[:-1] if msgin[-1] == '\x00': msgin = msgin[:-1] m = Event() m.deserialize(msgin) self.event_engine.put(m) except Exception as e: pass #print("TradeEngineError {0}".format(str(e.args[0])).encode("utf-8")) # start and stop def start(self): """ start the dispatcher thread and begin to recv msg through nng """ print('tradeclient started ,pid = %d ' % os.getpid()) self.event_engine.start() self.__active = True self._thread.start() def stop(self): """ stop """ self.__active = False self.event_engine.stop() self._thread.join() def write_log(self, msg: str): """ Create engine log event. """ # log = LogData(msg=msg, gateway_name="CtaStrategy") # event = Event(type=EVENT_CTA_LOG, data=log) # self.event_engine.put(event) print(msg)
class ClientMq(object): def __init__(self, config, ui_event_engine, outgoing_queue): self._ui_event_engine = ui_event_engine self._outgoing_queue = outgoing_queue self._config = config self._active = False self._thread = Thread(target=self._run) def _run(self): # os.system("taskset -cp 5 %d " % os.getpid()) while self._active: try: # response msg from server msgin = self._recv_sock.recv(flags=0) msgin = msgin.decode("utf-8") if msgin is not None and msgin.index('|') > 0: # print('client rec msg:',msgin,'at ', datetime.now()) if msgin[-1] == '\0': msgin = msgin[:-1] if msgin[-1] == '\x00': msgin = msgin[:-1] m = Event() m.deserialize(msgin) self._ui_event_engine.put(m) except Exception as e: pass try: # request, qry msg to server msgout = self._outgoing_queue.get(False) print('outgoing get msg,begin send', msgout, datetime.now()) # self._send_sock.send(bytes(msgout,"ascii"), flags=0) self._send_sock.send(msgout, flags=1) print('outgoing end send', msgout, datetime.now()) except Exception as e: pass def start(self, timer=True): """ start the mq thread """ self._recv_sock = Socket(SUB) self._send_sock = Socket(PUSH) self._monitor_sock = Socket(SUB) # print(os.getpid()) self._recv_sock.connect(self._config['serverpub_url']) self._recv_sock.set_string_option(SUB, SUB_SUBSCRIBE, '') self._recv_sock.set_int_option(SOL_SOCKET, RCVTIMEO, 100) self._send_sock.connect(self._config['serverpull_url']) self._monitor_sock.connect(self._config['serversub_url']) self._active = True if not self._thread.isAlive(): self._thread.start() def stop(self): """ stop the mq thread """ self._active = False if self._thread.isAlive(): self._thread.join()
class StrategyEngine(BaseEngine): """ Send to and receive from msg server ,used for strategy """ config_filename = "config_server.yaml" setting_filename = "cta_strategy_setting.json" data_filename = "cta_strategy_data.json" # init def __init__(self, configfile: str = '', id: int = 1): super(StrategyEngine, self).__init__(event_engine=EventEngine(10)) """ two sockets to send and recv msg """ self.__active = False self.id = os.getpid() self.engine_type = EngineType.LIVE self._recv_sock = Socket(SUB) self._send_sock = Socket(PUSH) self._handlers = defaultdict(list) if configfile: self.config_filename = configfile filepath = Path.cwd().joinpath("etc/" + self.config_filename) with open(filepath, encoding='utf8') as fd: self._config = yaml.load(fd) self.ordercount = 0 # stragegy manage self.strategy_setting = {} # strategy_name: dict self.strategy_data = {} # strategy_name: dict self.classes = {} # class_name: stategy_class self.strategies = {} # strategy_name: strategy # self.classes_id = {} # class_id : strategy # self.strategies_id = {} # strategy_ID: strategy self.symbol_strategy_map = defaultdict( list) # full_symbol: strategy list self.orderid_strategy_map = {} # vt_orderid: strategy self.strategy_orderid_map = defaultdict( set) # strategy_name: client_order_id list self.stop_order_count = 0 # for generating stop_orderid self.stop_orders = {} # stop_orderid: stop_order self.init_thread = None self.init_queue = Queue() # order,tick,position ,etc manage self.ticks = {} self.orders = {} # clientorder id list self.trades = {} self.positions = {} self.accounts = {} self.contracts = {} self.active_orders = {} # SQ id list self.rq_client = None self.rq_symbols = set() self.offset_converter = OffsetConverter(self) self.autoinited = False self.autostarted = False self.dayswitched = False self.init_engine() # init functions def init_engine(self): self.init_nng() self.init_rqdata() self.load_contract() self.load_strategy_class() self.load_strategy_setting() self.load_strategy_data() self.register_event() def init_nng(self): self._recv_sock.set_string_option( SUB, SUB_SUBSCRIBE, '') # receive msg start with all self._recv_sock.set_int_option(SOL_SOCKET, RCVTIMEO, 100) self._recv_sock.connect(self._config['serverpub_url']) self._send_sock.connect(self._config['serverpull_url']) def init_rqdata(self): result = rqdata_client.init() if result: self.write_log("RQData数据接口初始化成功") def load_contract(self): contractfile = Path.cwd().joinpath("etc/ctpcontract.yaml") with open(contractfile, encoding='utf8') as fc: contracts = yaml.load(fc) print('loading contracts, total number:', len(contracts)) for sym, data in contracts.items(): contract = ContractData( symbol=data["symbol"], exchange=Exchange(data["exchange"]), name=data["name"], product=PRODUCT_CTP2VT[str(data["product"])], size=data["size"], pricetick=data["pricetick"], net_position=True if str( data["positiontype"]) == THOST_FTDC_PT_Net else False, long_margin_ratio=data["long_margin_ratio"], short_margin_ratio=data["short_margin_ratio"], full_symbol=data["full_symbol"] ) # For option only if contract.product == Product.OPTION: contract.option_underlying = data["option_underlying"], contract.option_type = OPTIONTYPE_CTP2VT.get( str(data["option_type"]), None), contract.option_strike = data["option_strike"], contract.option_expiry = datetime.strptime( str(data["option_expiry"]), "%Y%m%d"), self.contracts[contract.full_symbol] = contract def register_event(self): """""" self.event_engine.register(EventType.TICK, self.process_tick_event) self.event_engine.register( EventType.ORDERSTATUS, self.process_orderstatus_event) self.event_engine.register(EventType.FILL, self.process_trade_event) self.event_engine.register( EventType.POSITION, self.process_position_event) self.event_engine.register( EventType.ACCOUNT, self.process_account_event) self.event_engine.register( EventType.CONTRACT, self.process_contract_event) self.event_engine.register( EventType.STRATEGY_CONTROL, self.process_strategycontrol_event) self.event_engine.register( EventType.HEADER, self.process_general_event) self.event_engine.register(EventType.TIMER, self.process_timer_event) # event handler def process_timer_event(self, event): # auto init and start strategy at 8:57, 20:57 nowtime = datetime.now().time() dayinitflag = (nowtime > time(hour=8, minute=55)) and ( nowtime < time(hour=8, minute=56)) daystartflag = (nowtime > time(hour=8, minute=57)) and ( nowtime < time(hour=8, minute=58)) nightinitflag = (nowtime > time(hour=20, minute=55)) and ( nowtime < time(hour=20, minute=56)) nightstartflag = (nowtime > time(hour=20, minute=57)) and ( nowtime < time(hour=20, minute=58)) if (dayinitflag or nightinitflag) and (not self.autoinited): for name, strategy in self.strategies.items(): if strategy.autostart: self.init_strategy(name) self.dayswitched = False self.autoinited = True if (daystartflag or nightstartflag) and (not self.autostarted): for name, strategy in self.strategies.items(): if strategy.autostart: self.start_strategy(name) self.autostarted = True self.dayswitched = False # auto stop strategy at 16:00 and 3:00 if (nowtime > time(hour=16, minute=0)) and (nowtime < time(hour=16, minute=1)) and (not self.dayswitched): for name, strategy in self.strategies.items(): if strategy.autostart: self.reset_strategy(name) self.dayswitched = True self.autostarted = False self.autoinited = False if (nowtime > time(hour=3, minute=0)) and (nowtime < time(hour=3, minute=1)) and (not self.dayswitched): for name, strategy in self.strategies.items(): if strategy.autostart: self.reset_strategy(name) self.dayswitched = True self.autostarted = False self.autoinited = False def process_general_event(self, event): for name, strategy in self.strategies.items(): self.call_strategy_func(strategy, strategy.on_headermsg, event) pass def process_tick_event(self, event: Event): """""" tick = event.data strategies = self.symbol_strategy_map[tick.full_symbol] if not strategies: return # self.check_stop_order(tick) for strategy in strategies: if strategy.inited: self.call_strategy_func(strategy, strategy.on_tick, tick) self.ticks[tick.full_symbol] = tick def process_orderstatus_event(self, event: Event): """""" order = event.data self.offset_converter.update_order(order) # 重新计算冻结 if order.clientID != self.id: return self.orders[order.client_order_id] = order # If order is active, then update data in dict. if order.is_active(): print('order is active') self.active_orders[order.client_order_id] = order # Otherwise, pop inactive order from in dict elif order.client_order_id in self.active_orders: self.active_orders.pop(order.client_order_id) strategy = self.orderid_strategy_map.get(order.client_order_id, None) if not strategy: print(order.client_order_id, 'dont find strategy') return # Remove client_order_id if order is no longer active. client_order_ids = self.strategy_orderid_map[strategy.strategy_name] if (order.client_order_id in client_order_ids) and (not order.is_active()): print('rm inactive order in strategy order map') client_order_ids.remove(order.client_order_id) # For server stop order, call strategy on_stop_order function # if order.type == OrderType.STOP: # so = StopOrder( # full_symbol=order.full_symbol, # direction=order.direction, # offset=order.offset, # price=order.price, # volume=order.volume, # stop_orderid=order.vt_orderid, # strategy_name=strategy.strategy_name, # status=STOP_STATUS_MAP[order.status], # vt_orderid=order.vt_orderid, # ) # self.call_strategy_func(strategy, strategy.on_stop_order, so) # Call strategy on_order function self.call_strategy_func(strategy, strategy.on_order, order) def process_trade_event(self, event: Event): """""" trade = event.data self.offset_converter.update_trade(trade) if trade.clientID != self.id: return strategy = self.orderid_strategy_map.get(trade.client_order_id, None) if not strategy: return # if trade.direction == Direction.LONG: # strategy.pos += trade.volume # else: # strategy.pos -= trade.volume self.call_strategy_func(strategy, strategy.on_trade, trade) self.put_strategy_event(strategy) self.trades[trade.vt_tradeid] = trade # send qry pos to update position m = Event(type=EventType.QRY, des=event.source, src=str(self.id), msgtype=MSG_TYPE.MSG_TYPE_QRY_POS) self._send_sock.send(m.serialize()) # self.put(m) def process_position_event(self, event: Event): """""" position = event.data self.offset_converter.update_position(position) self.positions[position.key] = position def process_account_event(self, event: Event): """""" account = event.data self.accounts[account.accountid] = account def process_contract_event(self, event: Event): """""" contract = event.data self.contracts[contract.full_symbol] = contract def process_strategycontrol_event(self, event: Event): msgtype = event.msg_type deslist = ['@*', str(self.id), '@' + str(self.id)] if (event.destination not in deslist): return elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_STATUS): m = Event(type=EventType.STRATEGY_CONTROL, des='@0', src=str(self.id), msgtype=MSG_TYPE.MSG_TYPE_STRATEGY_STATUS ) self._send_sock.send(m.serialize()) # elif (event.destination not in deslist ) : # return elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_ADD): v = event.data.split('|') classname = v[0] strname = v[1] fulsym = v[2] setting = json.loads(v[3]) self.add_strategy(classname, strname, fulsym, setting) elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_INIT): self.init_strategy(event.data) elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_INIT_ALL): self.init_all_strategies() elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_START): self.start_strategy(event.data) elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_START_ALL): self.start_all_strategies() elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_STOP): self.stop_strategy(event.data) elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_STOP_ALL): self.stop_all_strategies() elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_RELOAD): self.classes.clear() self.load_strategy_class(True) elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_RESET): self.reset_strategy(event.data) elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_RESET_ALL): self.reset_all_strategies() elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_EDIT): v = event.data.split('|') setting = json.loads(v[1]) self.edit_strategy(v[0], setting) elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_REMOVE): if self.remove_strategy(event.data): m = Event(type=EventType.STRATEGY_CONTROL, data=event.data, des='@0', src=str(self.id), msgtype=MSG_TYPE.MSG_TYPE_STRATEGY_RTN_REMOVE ) self._send_sock.send(m.serialize()) elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_REMOVE_DUPLICATE): self.remove_strategy(event.data, True) elif (msgtype == MSG_TYPE.MSG_TYPE_STRATEGY_GET_DATA): # print('begin get data') if event.data: strategy = self.strategies.get(event.data, None) if strategy: self.put_strategy_event(strategy) else: # get all strategy data for strategy in self.strategies.values(): self.put_strategy_event(strategy) def call_strategy_func( self, strategy: StrategyBase, func: Callable, params: Any = None ): """ Call function of a strategy and catch any exception raised. """ try: if params: func(params) else: func() except Exception: strategy.trading = False strategy.inited = False msg = f"触发异常已停止\n{traceback.format_exc()}" self.write_log(msg, strategy) # strategy manage def add_strategy( self, class_name: str, strategy_name: str, full_symbol: str, setting: dict ): """ Add a new strategy. """ print("begin add strategy") if strategy_name in self.strategies: self.write_log(f"创建策略失败,存在重名{strategy_name}") return if class_name not in self.classes: self.write_log( f'strategy class[{class_name}] not exist, please check') return strategy_class = self.classes[class_name] strategy = strategy_class(self, strategy_name, full_symbol, setting) self.strategies[strategy_name] = strategy # Add full_symbol to strategy map. strategies = self.symbol_strategy_map[full_symbol] strategies.append(strategy) # print("335 add strategy") # Update to setting file. self.update_strategy_setting(strategy_name, setting) self.put_strategy_event(strategy) print("end add strategy") def init_strategy(self, strategy_name: str): """ Init a strategy. """ self.init_queue.put(strategy_name) if not self.init_thread: self.init_thread = Thread(target=self._init_strategy) self.init_thread.start() def _init_strategy(self): """ Init strategies in queue. """ while not self.init_queue.empty(): strategy_name = self.init_queue.get() strategy = self.strategies[strategy_name] if strategy.inited: self.write_log(f"{strategy_name}已经完成初始化,禁止重复操作") continue self.write_log(f"{strategy_name}开始执行初始化") # Call on_init function of strategy self.call_strategy_func(strategy, strategy.on_init) # Restore strategy data(variables) data = self.strategy_data.get(strategy_name, None) if data: for name in strategy.variables: value = data.get(name, None) if value: setattr(strategy, name, value) # Subscribe market data contract = self.get_contract(strategy.full_symbol) if contract: m = Event(type=EventType.SUBSCRIBE, msgtype=MSG_TYPE.MSG_TYPE_SUBSCRIBE_MARKET_DATA) m.destination = "CTP.MD" m.source = str(self.id) req = SubscribeRequest() req.sym_type = SYMBOL_TYPE.CTP req.content = contract.symbol m.data = req self._send_sock.send(m.serialize()) else: self.write_log(f"行情订阅失败,找不到合约{strategy.full_symbol}", strategy) # qry pos and acc m = Event(type=EventType.QRY, msgtype=MSG_TYPE.MSG_TYPE_QRY_POS) m.destination = strategy.api + '.' + strategy.account m.source = str(self.id) self._send_sock.send(m.serialize()) m = Event(type=EventType.QRY, msgtype=MSG_TYPE.MSG_TYPE_QRY_ACCOUNT) m.destination = strategy.api + '.' + strategy.account m.source = str(self.id) self._send_sock.send(m.serialize()) # Put event to update init completed status. strategy.inited = True self.put_strategy_event(strategy) self.write_log(f"{strategy_name}初始化完成") self.init_thread = None def start_strategy(self, strategy_name: str): """ Start a strategy. """ strategy = self.strategies[strategy_name] if not strategy.inited: self.write_log(f"策略{strategy.strategy_name}启动失败,请先初始化") return if strategy.trading: self.write_log(f"{strategy_name}已经启动,请勿重复操作") return # qry pos and acc m = Event(type=EventType.QRY, msgtype=MSG_TYPE.MSG_TYPE_QRY_POS) m.destination = strategy.api + '.' + strategy.account m.source = str(self.id) self._send_sock.send(m.serialize()) m = Event(type=EventType.QRY, msgtype=MSG_TYPE.MSG_TYPE_QRY_ACCOUNT) m.destination = strategy.api + '.' + strategy.account m.source = str(self.id) self._send_sock.send(m.serialize()) self.call_strategy_func(strategy, strategy.on_start) strategy.trading = True self.put_strategy_event(strategy) def stop_strategy(self, strategy_name: str): """ Stop a strategy. """ strategy = self.strategies[strategy_name] if not strategy.trading: return # Call on_stop function of the strategy self.call_strategy_func(strategy, strategy.on_stop) # Change trading status of strategy to False strategy.trading = False # Cancel all orders of the strategy self.cancel_all(strategy) # Update GUI self.put_strategy_event(strategy) def reset_strategy(self, strategy_name: str): "Reset a strategy" strategy = self.strategies[strategy_name] if not strategy.inited: return # stop first self.call_strategy_func(strategy, strategy.on_stop) strategy.trading = False self.cancel_all(strategy) # reset self.call_strategy_func(strategy, strategy.on_reset) strategy.inited = False self.put_strategy_event(strategy) def edit_strategy(self, strategy_name: str, setting: dict): """ Edit parameters of a strategy. """ strategy = self.strategies[strategy_name] strategy.update_setting(setting) self.update_strategy_setting(strategy_name, setting) self.put_strategy_event(strategy) def remove_strategy(self, strategy_name: str, duplicate: bool = False): """ Remove a strategy. """ print("begin remove") strategy = self.strategies[strategy_name] if strategy.trading: self.write_log(f"策略{strategy.strategy_name}移除失败,请先停止") return # Remove setting if not duplicate: self.remove_strategy_setting(strategy_name) # Remove from symbol strategy map strategies = self.symbol_strategy_map[strategy.full_symbol] strategies.remove(strategy) # Remove from active orderid map if strategy_name in self.strategy_orderid_map: orderids = self.strategy_orderid_map.pop(strategy_name) # Remove vt_orderid strategy map for _orderid in orderids: if _orderid in self.orderid_strategy_map: self.orderid_strategy_map.pop(_orderid) # Remove from strategies self.strategies.pop(strategy_name) print("end remove") return True def load_strategy_class(self, reload: bool = False): """ Load strategy class from source code. """ # app_path = Path(__file__).parent.parent # path1 = app_path.joinpath("cta_strategy", "strategies") # self.load_strategy_class_from_folder( # path1, "vnpy.app.cta_strategy.strategies") path2 = Path.cwd().joinpath("mystrategy") self.load_strategy_class_from_folder(path2, "", reload) def load_strategy_class_from_folder(self, path: Path, module_name: str = "", reload: bool = False): """ Load strategy class from certain folder. """ for dirpath, dirnames, filenames in os.walk(path): for filename in filenames: if filename.endswith(".py"): strategy_module_name = "mystrategy.".join( [module_name, filename.replace(".py", "")]) self.load_strategy_class_from_module( strategy_module_name, reload) def load_strategy_class_from_module(self, module_name: str, reload: bool = False): """ Load strategy class from module file. """ try: module = importlib.import_module(module_name) # if reload delete old attribute if reload: for attr in dir(module): if attr not in ('__name__', '__file__'): delattr(module, attr) importlib.reload(module) for name in dir(module): value = getattr(module, name) if (isinstance(value, type) and issubclass(value, CtaTemplate) and value is not CtaTemplate): self.classes[value.__name__] = value except: # noqa msg = f"策略文件{module_name}加载失败,触发异常:\n{traceback.format_exc()}" self.write_log(msg) def load_strategy_data(self): """ Load strategy data from json file. """ self.strategy_data = load_json(self.data_filename) def sync_strategy_data(self, strategy: StrategyBase): """ Sync strategy data into json file. """ data = strategy.get_variables() # Strategy status (inited, trading) should not be synced. data.pop("inited") data.pop("trading") self.strategy_data = load_json(self.data_filename) self.strategy_data[strategy.strategy_name] = data save_json(self.data_filename, self.strategy_data) def get_all_strategy_class_names(self): """ Return names of strategy classes loaded. """ return list(self.classes.keys()) def get_strategy_class_parameters(self, class_name: str): """ Get default parameters of a strategy class. """ strategy_class = self.classes[class_name] parameters = {} for name in strategy_class.parameters: parameters[name] = getattr(strategy_class, name) return parameters def get_strategy_parameters(self, strategy_name): """ Get parameters of a strategy. """ strategy = self.strategies[strategy_name] return strategy.get_parameters() def init_all_strategies(self): """ """ for strategy_name in self.strategies.keys(): self.init_strategy(strategy_name) def start_all_strategies(self): """ """ for strategy_name in self.strategies.keys(): self.start_strategy(strategy_name) def stop_all_strategies(self): """ """ for strategy_name in self.strategies.keys(): self.stop_strategy(strategy_name) def reset_all_strategies(self): for strategy_name in self.strategies.keys(): self.reset_strategy(strategy_name) def load_strategy_setting(self): """ Load setting file. """ self.strategy_setting = load_json(self.setting_filename) for strategy_name, strategy_config in self.strategy_setting.items(): self.add_strategy( strategy_config["class_name"], strategy_name, strategy_config["full_symbol"], strategy_config["setting"] ) def update_strategy_setting(self, strategy_name: str, setting: dict): """ Update setting file. """ strategy = self.strategies[strategy_name] # in order to save other engine's setting, should load again self.strategy_setting = load_json(self.setting_filename) self.strategy_setting[strategy_name] = { "class_name": strategy.__class__.__name__, "full_symbol": strategy.full_symbol, "setting": setting, } save_json(self.setting_filename, self.strategy_setting) def remove_strategy_setting(self, strategy_name: str): """ Update setting file. """ if strategy_name not in self.strategy_setting: return # in order to save other engine's setting, should load again self.strategy_setting = load_json(self.setting_filename) self.strategy_setting.pop(strategy_name) save_json(self.setting_filename, self.strategy_setting) # def put_stop_order_event(self, stop_order: StopOrder): # """ # Put an event to update stop order status. # """ # event = Event(EVENT_CTA_STOPORDER, stop_order) # self.event_engine.put(event) def put_strategy_event(self, strategy: StrategyBase): """ Put an event to update strategy status. """ data = strategy.get_data() sdata = {} sdata[strategy.strategy_name] = data # event = Event(EVENT_CTA_STRATEGY, data) # self.event_engine.put(event) msg = json.dumps(sdata) m = Event(type=EventType.STRATEGY_CONTROL, data=msg, des='@0', src=str( self.id), msgtype=MSG_TYPE.MSG_TYPE_STRATEGY_RTN_DATA) self._send_sock.send(m.serialize()) #save_json(self.data_filename, sdata) # strategy functions #get ,qry def query_bar_from_rq( self, symbol: str, exchange: Exchange, interval: Interval, start: datetime, end: datetime ): """ Query bar data from RQData. """ data = rqdata_client.query_bar( symbol, exchange, interval, start, end ) return data def load_bar( self, full_symbol: str, days: int, interval: Interval, callback: Callable[[BarData], None], datasource: str = 'DataBase' ): """""" tradedays = abs(days) weekday = datetime.now().weekday() adddays = 2 if (days - weekday > 0) else 0 if weekday == 6: tradedays = days + 1 else: tradedays = days + adddays symbol, exchange = extract_full_symbol(full_symbol) end = datetime.now() start = end - timedelta(days=tradedays) # Query bars from RQData by default, if not found, load from database. bars = self.query_bar_from_rq(symbol, exchange, interval, start, end) if not bars: bars = database_manager.load_bar_data( symbol=symbol, exchange=exchange, interval=interval, start=start, end=end, ) for bar in bars: callback(bar) def load_tick(self, full_symbol: str, days: int, callback: Callable, datasource: str = 'DataBase'): tradedays = abs(days) weekday = datetime.now().weekday() adddays = 2 if (days - weekday > 0) else 0 if weekday == 6: tradedays = days + 1 else: tradedays = days + adddays symbol, exchange = extract_full_symbol(full_symbol) end = datetime.now() start = end - timedelta(tradedays) ticks = database_manager.load_tick_data(symbol, exchange, start, end) for tick in ticks: callback(tick) def get_tick(self, full_symbol): """ Get latest market tick data by full_symbol. """ return self.ticks.get(full_symbol, None) def get_order(self, orderid: int): """ Get latest order data by orderid. """ return self.orders.get(orderid, None) def get_trade(self, vt_tradeid): """ Get trade data by vt_tradeid. """ return self.trades.get(vt_tradeid, None) def get_position(self, key): """ Get latest position data by vt_positionid. """ return self.positions.get(key, None) def get_account(self, accountid): """ Get latest account data by accountid. """ return self.accounts.get(accountid, None) def get_contract(self, full_symbol): """ Get contract data by full_symbol. """ return self.contracts.get(full_symbol, None) def get_all_ticks(self): """ Get all tick data. """ return list(self.ticks.values()) def get_all_orders(self): """ Get all order data. """ return list(self.orders.values()) def get_all_trades(self): """ Get all trade data. """ return list(self.trades.values()) def get_all_positions(self): """ Get all position data. """ return list(self.positions.values()) def get_all_accounts(self): """ Get all account data. """ return list(self.accounts.values()) def get_all_contracts(self): """ Get all contract data. """ return list(self.contracts.values()) def get_all_active_orders(self, full_symbol: str = ""): """ Get all active orders by full_symbol. If full_symbol is empty, return all active orders. """ if not full_symbol: return list(self.active_orders.values()) else: active_orders = [ order for order in self.active_orders.values() if order.full_symbol == full_symbol ] return active_orders def get_position_holding(self, acc: str, full_symbol: str): return self.offset_converter.get_position_holding(acc, full_symbol) def get_strategy_active_orderids(self, strategy_name: str): oidset = self.strategy_orderid_map[strategy_name] return oidset #order, cancel def send_order( self, strategy: StrategyBase, original_req: OrderRequest, lock: bool = False ): """ Send a new order to server. """ # Convert with offset converter req_list = self.offset_converter.convert_order_request( original_req, lock) # Send Orders orderids = [] for req in req_list: req.clientID = self.id req.client_order_id = self.ordercount self.ordercount += 1 m = Event(type=EventType.ORDER, data=req, des=req.api + '.' + req.account, src=str(self.id) ) if req.api == "CTP.TD": m.msg_type = MSG_TYPE.MSG_TYPE_ORDER_CTP elif req.api == "PAPER.TD": m.msg_type = MSG_TYPE.MSG_TYPE_ORDER_PAPER else: print("error:api not support!") return [] msg = m.serialize() print(f'tradeclient {self.id} send msg: {msg}') self._send_sock.send(msg) orderids.append(req.client_order_id) self.offset_converter.update_order_request(req) # Save relationship between orderid and strategy. self.orderid_strategy_map[req.client_order_id] = strategy self.strategy_orderid_map[strategy.strategy_name].add( req.client_order_id) return orderids def cancel_order(self, strategy: StrategyBase, orderid: int): """ Cancel existing order by orderid. """ order = self.get_order(orderid) if not order: self.write_log(f"撤单失败,找不到委托{orderid}", strategy) return req = order.create_cancel_request() m = Event(type=EventType.CANCEL, data=req, des=order.api + '.' + order.account, src=str(self.id), msgtype=MSG_TYPE.MSG_TYPE_ORDER_ACTION ) msg = m.serialize() print(f'tradeclient {self.id} send msg: {msg}') self._send_sock.send(msg) def cancel_all(self, strategy: StrategyBase): """ Cancel all active orders of a strategy. """ orderids = self.strategy_orderid_map[strategy.strategy_name] if not orderids: print(strategy.strategy_name, 'has no active order') return for orderid in copy(orderids): print('cancel oid:', orderid) self.cancel_order(strategy, orderid) def send_testmsg(self): m = Event(des='CTP.MD', src=str(self.id), msgtype=MSG_TYPE.MSG_TYPE_TEST) msg = m.serialize() self._send_sock.send(msg) print(f'tradeclient {self.id} send msg: {msg}') # start and stop def start(self, timer=True): """ start the dispatcher thread and begin to recv msg through nng """ self.event_engine.start() print('tradeclient started ,pid = %d ' % os.getpid()) self.__active = True while self.__active: try: msgin = self._recv_sock.recv(flags=0) msgin = msgin.decode("utf-8") if msgin is not None and msgin.index('|') > 0: if msgin[0] == '@': print('tradeclient(pid = %d) rec @ msg:' % (self.id), msgin, 'at ', datetime.now()) if msgin[-1] == '\0': msgin = msgin[:-1] if msgin[-1] == '\x00': msgin = msgin[:-1] m = Event() m.deserialize(msgin) self.event_engine.put(m) except Exception as e: pass #print("TradeEngineError {0}".format(str(e.args[0])).encode("utf-8")) def stop(self): """ stop """ self.__active = False self.event_engine.stop() def put(self, event): """ send event msg,TODO:check the event """ # self._send_sock.send(event.serialize(), flags=1) def register_handler(self, type_, handler): """ register handler/subscriber """ # self.event_engine.register(type_,handler) # handlerList = self._handlers[type_] # if handler not in handlerList: # self._handlers[type_].append(handler) # #handlerList.append(handler) pass def unregister_handler(self, type_, handler): """ unregister handler/subscriber """ # handlerList = self._handlers[type_] # if handler in handlerList: # self._handlers.remove(handler) # if not handlerList: # del self._handlers[type_] pass def write_log(self, msg: str, strategy: StrategyBase = None): """ Create engine log event. """ # if strategy: # msg = f"{strategy.strategy_name}: {msg}" # log = LogData(msg=msg, gateway_name="CtaStrategy") # event = Event(type=EVENT_CTA_LOG, data=log) # self.event_engine.put(event) print(msg)
class TradeEngine(BaseEngine): """ Send to and receive from msg server ,used for strategy """ setting_filename = "cta_strategy_setting.json" data_filename = "cta_strategy_data.json" def __init__(self,config:dict): super(TradeEngine, self).__init__() """ two sockets to send and recv msg """ self.__active = False self.id = 0 self.engine_type = EngineType.LIVE self._recv_sock = Socket(SUB) self._send_sock = Socket(PUSH) self._config = config self._handlers = defaultdict(list) # stragegy manage self.strategy_setting = {} # strategy_name: dict self.strategy_data = {} # strategy_name: dict self.classes = {} # class_name: stategy_class self.strategies = {} # strategy_name: strategy self.symbol_strategy_map = defaultdict( list) # vt_symbol: strategy list self.orderid_strategy_map = {} # vt_orderid: strategy self.strategy_orderid_map = defaultdict( set) # strategy_name: orderid list self.stop_order_count = 0 # for generating stop_orderid self.stop_orders = {} # stop_orderid: stop_order self.init_thread = None self.init_queue = Queue() # order,tick,position ,etc manage self.ticks = {} self.orders = {} self.trades = {} self.positions = {} self.accounts = {} self.contracts = {} self.active_orders = {} self.rq_client = None self.rq_symbols = set() self.offset_converter = OffsetConverter(self) self.init_engine() def init_engine(self): self.init_nng() self.init_rqdata() self.load_strategy_class() self.load_strategy_setting() self.load_strategy_data() self.register_event() def init_rqdata(self): result = rqdata_client.init() if result: self.write_log("RQData数据接口初始化成功") def register_event(self): """""" self.event_engine.register(EventType.TICK, self.process_tick_event) self.event_engine.register(EventType.ORDERSTATUS, self.process_order_event) self.event_engine.register(EventType.FILL, self.process_trade_event) self.event_engine.register(EventType.POSITION, self.process_position_event) self.event_engine.register(EventType.ACCOUNT, self.process_account_event) self.event_engine.register(EventType.CONTRACT, self.process_contract_event) def process_tick_event(self, event: Event): """""" tick = event strategies = self.symbol_strategy_map[tick.full_symbol] if not strategies: return # self.check_stop_order(tick) for strategy in strategies: if strategy.inited: self.call_strategy_func(strategy, strategy.on_tick, tick) self.ticks[tick.full_symbol] = tick def process_order_event(self, event: Event): """""" order = event self.offset_converter.update_order(order) strategy = self.orderid_strategy_map.get(order.vt_orderid, None) if not strategy: return # Remove vt_orderid if order is no longer active. vt_orderids = self.strategy_orderid_map[strategy.strategy_name] if order.vt_orderid in vt_orderids and not order.is_active(): vt_orderids.remove(order.vt_orderid) # For server stop order, call strategy on_stop_order function # if order.type == OrderType.STOP: # so = StopOrder( # vt_symbol=order.vt_symbol, # direction=order.direction, # offset=order.offset, # price=order.price, # volume=order.volume, # stop_orderid=order.vt_orderid, # strategy_name=strategy.strategy_name, # status=STOP_STATUS_MAP[order.status], # vt_orderid=order.vt_orderid, # ) # self.call_strategy_func(strategy, strategy.on_stop_order, so) # Call strategy on_order function self.call_strategy_func(strategy, strategy.on_order, order) self.orders[order.vt_orderid] = order # If order is active, then update data in dict. if order.is_active(): self.active_orders[order.vt_orderid] = order # Otherwise, pop inactive order from in dict elif order.vt_orderid in self.active_orders: self.active_orders.pop(order.vt_orderid) def process_trade_event(self, event: Event): """""" trade = event self.offset_converter.update_trade(trade) strategy = self.orderid_strategy_map.get(trade.vt_orderid, None) if not strategy: return # if trade.direction == Direction.LONG: # strategy.pos += trade.volume # else: # strategy.pos -= trade.volume self.call_strategy_func(strategy, strategy.on_trade, trade) self.put_strategy_event(strategy) self.trades[trade.vt_tradeid] = trade def process_position_event(self, event: Event): """""" position = event self.offset_converter.update_position(position) self.positions[position.vt_positionid] = position def process_account_event(self, event: Event): """""" account = event self.accounts[account.vt_accountid] = account def process_contract_event(self, event: Event): """""" contract = event self.contracts[contract.vt_symbol] = contract def call_strategy_func( self, strategy: StrategyBase, func: Callable, params: Any = None ): """ Call function of a strategy and catch any exception raised. """ try: if params: func(params) else: func() except Exception: strategy.trading = False strategy.inited = False msg = f"触发异常已停止\n{traceback.format_exc()}" self.write_log(msg, strategy) def add_strategy( self, class_name: str, strategy_name: str, vt_symbol: str, setting: dict ): """ Add a new strategy. """ if strategy_name in self.strategies: self.write_log(f"创建策略失败,存在重名{strategy_name}") return strategy_class = self.classes[class_name] strategy = strategy_class(self, strategy_name, vt_symbol, setting) self.strategies[strategy_name] = strategy # Add vt_symbol to strategy map. strategies = self.symbol_strategy_map[vt_symbol] strategies.append(strategy) # Update to setting file. self.update_strategy_setting(strategy_name, setting) self.put_strategy_event(strategy) def init_strategy(self, strategy_name: str): """ Init a strategy. """ self.init_queue.put(strategy_name) if not self.init_thread: self.init_thread = Thread(target=self._init_strategy) self.init_thread.start() def _init_strategy(self): """ Init strategies in queue. """ while not self.init_queue.empty(): strategy_name = self.init_queue.get() strategy = self.strategies[strategy_name] if strategy.inited: self.write_log(f"{strategy_name}已经完成初始化,禁止重复操作") continue self.write_log(f"{strategy_name}开始执行初始化") # Call on_init function of strategy self.call_strategy_func(strategy, strategy.on_init) # Restore strategy data(variables) data = self.strategy_data.get(strategy_name, None) if data: for name in strategy.variables: value = data.get(name, None) if value: setattr(strategy, name, value) # Subscribe market data contract = self.get_contract(strategy.vt_symbol) if contract: req = SubscribeEvent() req.destination = contract.gateway_name req.source = "0" req.content = strategy.symbol self.put(req) else: self.write_log(f"行情订阅失败,找不到合约{strategy.vt_symbol}", strategy) # Put event to update init completed status. strategy.inited = True self.put_strategy_event(strategy) self.write_log(f"{strategy_name}初始化完成") self.init_thread = None def start_strategy(self, strategy_name: str): """ Start a strategy. """ strategy = self.strategies[strategy_name] if not strategy.inited: self.write_log(f"策略{strategy.strategy_name}启动失败,请先初始化") return if strategy.trading: self.write_log(f"{strategy_name}已经启动,请勿重复操作") return self.call_strategy_func(strategy, strategy.on_start) strategy.trading = True self.put_strategy_event(strategy) def stop_strategy(self, strategy_name: str): """ Stop a strategy. """ strategy = self.strategies[strategy_name] if not strategy.trading: return # Call on_stop function of the strategy self.call_strategy_func(strategy, strategy.on_stop) # Change trading status of strategy to False strategy.trading = False # Cancel all orders of the strategy # self.cancel_all(strategy) # Update GUI self.put_strategy_event(strategy) def edit_strategy(self, strategy_name: str, setting: dict): """ Edit parameters of a strategy. """ strategy = self.strategies[strategy_name] strategy.update_setting(setting) self.update_strategy_setting(strategy_name, setting) self.put_strategy_event(strategy) def remove_strategy(self, strategy_name: str): """ Remove a strategy. """ strategy = self.strategies[strategy_name] if strategy.trading: self.write_log(f"策略{strategy.strategy_name}移除失败,请先停止") return # Remove setting self.remove_strategy_setting(strategy_name) # Remove from symbol strategy map strategies = self.symbol_strategy_map[strategy.vt_symbol] strategies.remove(strategy) # Remove from active orderid map if strategy_name in self.strategy_orderid_map: vt_orderids = self.strategy_orderid_map.pop(strategy_name) # Remove vt_orderid strategy map for vt_orderid in vt_orderids: if vt_orderid in self.orderid_strategy_map: self.orderid_strategy_map.pop(vt_orderid) # Remove from strategies self.strategies.pop(strategy_name) return True def load_strategy_class(self): """ Load strategy class from source code. """ path1 = Path(__file__).parent.joinpath("") self.load_strategy_class_from_folder( path1, "mystrategy") path2 = Path.cwd().joinpath("") self.load_strategy_class_from_folder(path2, "mystrategy") def load_strategy_class_from_folder(self, path: Path, module_name: str = ""): """ Load strategy class from certain folder. """ for dirpath, dirnames, filenames in os.walk(str(path)): for filename in filenames: if filename.endswith(".py"): strategy_module_name = ".".join( [module_name, filename.replace(".py", "")]) self.load_strategy_class_from_module(strategy_module_name) def load_strategy_class_from_module(self, module_name: str): """ Load strategy class from module file. """ try: module = importlib.import_module(module_name) for name in dir(module): value = getattr(module, name) if (isinstance(value, type) and issubclass(value, StrategyBase) and value is not StrategyBase): self.classes[value.__name__] = value except: # noqa msg = f"策略文件{module_name}加载失败,触发异常:\n{traceback.format_exc()}" self.write_log(msg) def load_strategy_data(self): """ Load strategy data from json file. """ self.strategy_data = load_json(self.data_filename) def sync_strategy_data(self, strategy: StrategyBase): """ Sync strategy data into json file. """ data = strategy.get_variables() data.pop("inited") # Strategy status (inited, trading) should not be synced. data.pop("trading") self.strategy_data[strategy.strategy_name] = data save_json(self.data_filename, self.strategy_data) def get_all_strategy_class_names(self): """ Return names of strategy classes loaded. """ return list(self.classes.keys()) def get_strategy_class_parameters(self, class_name: str): """ Get default parameters of a strategy class. """ strategy_class = self.classes[class_name] parameters = {} for name in strategy_class.parameters: parameters[name] = getattr(strategy_class, name) return parameters def get_strategy_parameters(self, strategy_name): """ Get parameters of a strategy. """ strategy = self.strategies[strategy_name] return strategy.get_parameters() def init_all_strategies(self): """ """ for strategy_name in self.strategies.keys(): self.init_strategy(strategy_name) def start_all_strategies(self): """ """ for strategy_name in self.strategies.keys(): self.start_strategy(strategy_name) def stop_all_strategies(self): """ """ for strategy_name in self.strategies.keys(): self.stop_strategy(strategy_name) def load_strategy_setting(self): """ Load setting file. """ self.strategy_setting = load_json(self.setting_filename) for strategy_name, strategy_config in self.strategy_setting.items(): self.add_strategy( strategy_config["class_name"], strategy_name, strategy_config["vt_symbol"], strategy_config["setting"] ) def update_strategy_setting(self, strategy_name: str, setting: dict): """ Update setting file. """ strategy = self.strategies[strategy_name] self.strategy_setting[strategy_name] = { "class_name": strategy.__class__.__name__, "vt_symbol": strategy.vt_symbol, "setting": setting, } save_json(self.setting_filename, self.strategy_setting) def remove_strategy_setting(self, strategy_name: str): """ Update setting file. """ if strategy_name not in self.strategy_setting: return self.strategy_setting.pop(strategy_name) save_json(self.setting_filename, self.strategy_setting) # def put_stop_order_event(self, stop_order: StopOrder): # """ # Put an event to update stop order status. # """ # event = Event(EVENT_CTA_STOPORDER, stop_order) # self.event_engine.put(event) def put_strategy_event(self, strategy: StrategyBase): """ Put an event to update strategy status. """ data = strategy.get_data() # event = Event(EVENT_CTA_STRATEGY, data) # self.event_engine.put(event) pass def init_nng(self): self._recv_sock.set_string_option(SUB, SUB_SUBSCRIBE, '') # receive msg start with all self._recv_sock.set_int_option(SOL_SOCKET,RCVTIMEO,100) self._recv_sock.connect(self._config['serverpub_url']) self._send_sock.connect(self._config['serverpull_url']) #------------------------------------ public functions -----------------------------# def query_bar_from_rq( self, symbol: str, exchange: Exchange, interval: Interval, start: datetime, end: datetime ): """ Query bar data from RQData. """ data = rqdata_client.query_bar( symbol, exchange, interval, start, end ) return data def load_bar( self, vt_symbol: str, days: int, interval: Interval, callback: Callable[[BarData], None] ): """""" symbol, exchange = extract_vt_symbol(vt_symbol) end = datetime.now() start = end - timedelta(days) # Query bars from RQData by default, if not found, load from database. bars = self.query_bar_from_rq(symbol, exchange, interval, start, end) if not bars: bars = database_manager.load_bar_data( symbol=symbol, exchange=exchange, interval=interval, start=start, end=end, ) for bar in bars: callback(bar) def get_tick(self, vt_symbol): """ Get latest market tick data by vt_symbol. """ return self.ticks.get(vt_symbol, None) def get_order(self, vt_orderid): """ Get latest order data by vt_orderid. """ return self.orders.get(vt_orderid, None) def get_trade(self, vt_tradeid): """ Get trade data by vt_tradeid. """ return self.trades.get(vt_tradeid, None) def get_position(self, vt_positionid): """ Get latest position data by vt_positionid. """ return self.positions.get(vt_positionid, None) def get_account(self, vt_accountid): """ Get latest account data by vt_accountid. """ return self.accounts.get(vt_accountid, None) def get_contract(self, vt_symbol): """ Get contract data by vt_symbol. """ return self.contracts.get(vt_symbol, None) def get_all_ticks(self): """ Get all tick data. """ return list(self.ticks.values()) def get_all_orders(self): """ Get all order data. """ return list(self.orders.values()) def get_all_trades(self): """ Get all trade data. """ return list(self.trades.values()) def get_all_positions(self): """ Get all position data. """ return list(self.positions.values()) def get_all_accounts(self): """ Get all account data. """ return list(self.accounts.values()) def get_all_contracts(self): """ Get all contract data. """ return list(self.contracts.values()) def get_all_active_orders(self, vt_symbol: str = ""): """ Get all active orders by vt_symbol. If vt_symbol is empty, return all active orders. """ if not vt_symbol: return list(self.active_orders.values()) else: active_orders = [ order for order in self.active_orders.values() if order.vt_symbol == vt_symbol ] return active_orders def start(self, timer=True): """ start the dispatcher thread and begin to recv msg through nng """ self.event_engine.start() print('tradeclient started ,pid = %d ' % os.getpid()) self.__active = True # print(self._config['serverpub_url']) while self.__active: try: msgin = self._recv_sock.recv(flags=0) msgin = msgin.decode("utf-8") if msgin is not None and msgin.index('|') > 0: print('tradeclient(id = %d) rec server msg:'%(self.id), msgin,'at ', datetime.now()) if msgin[-1] == '\0': msgin = msgin[:-1] if msgin[-1] == '\x00': msgin = msgin[:-1] v = msgin.split('|') msg2type = MSG_TYPE(int(v[2])) if msg2type == MSG_TYPE.MSG_TYPE_TICK_L1: m = TickEvent() m.deserialize(msgin) elif msg2type == MSG_TYPE.MSG_TYPE_RTN_ORDER: m = OrderStatusEvent() m.deserialize(msgin) elif msg2type == MSG_TYPE.MSG_TYPE_RTN_TRADE: m = FillEvent() m.deserialize(msgin) elif msg2type == MSG_TYPE.MSG_TYPE_RSP_POS: m = PositionEvent() m.deserialize(msgin) elif msg2type == MSG_TYPE.MSG_TYPE_Hist: m = HistoricalEvent() m.deserialize(msgin) elif msg2type == MSG_TYPE.MSG_TYPE_RSP_ACCOUNT: m = AccountEvent() m.deserialize(msgin) elif msg2type == MSG_TYPE.MSG_TYPE_RSP_CONTRACT: m = ContractEvent() m.deserialize(msgin) elif msg2type == MSG_TYPE.MSG_TYPE_INFO: m = InfoEvent() m.deserialize(msgin) else: m = GeneralReqEvent() m.deserialize(msgin) pass self.event_engine.put(m) # if m.event_type in self._handlers: # [handler(m) for handler in self._handlers[m.event_type]] except Exception as e: pass #print("TradeEngineError {0}".format(str(e.args[0])).encode("utf-8")) def stop(self): """ stop """ self.__active = False self.event_engine.stop() def put(self, event): """ send event msg,TODO:check the event """ # self._send_sock.send(event.serialize(),flags=1) def register_handler(self, type_, handler): """ register handler/subscriber """ # handlerList = self._handlers[type_] # if handler not in handlerList: # self._handlers[type_].append(handler) # #handlerList.append(handler) pass def unregister_handler(self, type_, handler): """ unregister handler/subscriber """ # handlerList = self._handlers[type_] # if handler in handlerList: # self._handlers.remove(handler) # if not handlerList: # del self._handlers[type_] pass def write_log(self, msg: str, strategy: StrategyBase = None): """ Create engine log event. """ # if strategy: # msg = f"{strategy.strategy_name}: {msg}" # log = LogData(msg=msg, gateway_name="CtaStrategy") # event = Event(type=EVENT_CTA_LOG, data=log) # self.event_engine.put(event) print(msg) # -------------------------------- end of public functions -----------------------------#
class ClientMq(object): def __init__(self, config, ui_event_engine, outgoing_quue): self._ui_event_engine = ui_event_engine self._outgoing_quue = outgoing_quue self._config = config self._active = False self._thread = Thread(target=self._run) def _run(self): # os.system("taskset -cp 5 %d " % os.getpid()) while self._active: try: # response msg from server msgin = self._recv_sock.recv(flags=0) msgin = msgin.decode("utf-8") if msgin is not None and msgin.index('|') > 0: print('client rec broker msg:', msgin, 'at ', datetime.now()) if msgin[-1] == '\0': msgin = msgin[:-1] if msgin[-1] == '\x00': msgin = msgin[:-1] v = msgin.split('|') msg2type = MSG_TYPE(int(v[2])) if msg2type == MSG_TYPE.MSG_TYPE_TICK_L1: m = TickEvent() m.deserialize(msgin) self._ui_event_engine.put(m) elif msg2type == MSG_TYPE.MSG_TYPE_RTN_ORDER: m = OrderStatusEvent() m.deserialize(msgin) self._ui_event_engine.put(m) elif msg2type == MSG_TYPE.MSG_TYPE_RTN_TRADE: m = FillEvent() m.deserialize(msgin) self._ui_event_engine.put(m) elif msg2type == MSG_TYPE.MSG_TYPE_RSP_POS: m = PositionEvent() m.deserialize(msgin) self._ui_event_engine.put(m) elif msg2type == MSG_TYPE.MSG_TYPE_Hist: m = HistoricalEvent() m.deserialize(msgin) self._ui_event_engine.put(m) elif msg2type == MSG_TYPE.MSG_TYPE_RSP_ACCOUNT: m = AccountEvent() m.deserialize(msgin) self._ui_event_engine.put(m) elif msg2type == MSG_TYPE.MSG_TYPE_RSP_CONTRACT: m = ContractEvent() m.deserialize(msgin) self._ui_event_engine.put(m) elif v[2].startswith( '3'): #msg2type == MSG_TYPE.MSG_TYPE_INFO: m = InfoEvent() m.deserialize(msgin) self._ui_event_engine.put(m) pass except Exception as e: pass try: # request, qry msg to server msgout = self._outgoing_quue.get(False) print('outgoing get msg,begin send', msgout, datetime.now()) # self._send_sock.send(bytes(msgout,"ascii"), flags=0) self._send_sock.send(msgout, flags=1) print('outgoing end send', msgout, datetime.now()) except Exception as e: pass def start(self, timer=True): """ start the mq thread """ self._recv_sock = Socket(SUB) self._send_sock = Socket(PUSH) self._monitor_sock = Socket(SUB) # print(os.getpid()) self._recv_sock.connect(self._config['serverpub_url']) self._recv_sock.set_string_option(SUB, SUB_SUBSCRIBE, '') # receive msg start with all self._recv_sock.set_int_option(SOL_SOCKET, RCVTIMEO, 100) self._send_sock.connect(self._config['serverpull_url']) self._monitor_sock.connect(self._config['serversub_url']) self._active = True if not self._thread.isAlive(): self._thread.start() def stop(self): """ stop the mq thread """ self._active = False if self._thread.isAlive(): self._thread.join()