def link_tq(api): """ 处理py进程到天勤的连接 根据天勤提供的命令行参数, 决定 TqApi 工作方式 * 直接调整api的参数 TqApi运行过程中的一批信息主动发送到天勤显示 * 进程启动和停止 * set_chart_data 指令全部发往天勤绘图 * 所有 log / print 信息传递一份 * exception 发送一份 * 所有报单/成交记录抄送一份 :return: (account, backtest, md_url) """ from tqsdk.api import TqChan, TqAccount from tqsdk.sim import TqSim # 解析命令行参数 parser = argparse.ArgumentParser() # 天勤连接基本参数 parser.add_argument('--_action', type=str, required=False) parser.add_argument('--_tq_pid', type=int, required=False) parser.add_argument('--_tq_url', type=str, required=False) # action==run时需要这几个 parser.add_argument('--_broker_id', type=str, required=False) parser.add_argument('--_account_id', type=str, required=False) parser.add_argument('--_password', type=str, required=False) # action==backtest时需要这几个 parser.add_argument('--_start_dt', type=str, required=False) parser.add_argument('--_end_dt', type=str, required=False) # action==mdreplay时需要这几个 parser.add_argument('--_ins_url', type=str, required=False) parser.add_argument('--_md_url', type=str, required=False) args, unknown = parser.parse_known_args() # 非天勤启动时直接返回 if args._action is None: return None, None if args._tq_pid is None: raise Exception("_tq_pid 参数缺失") if args._tq_url is None: raise Exception("_tq_url 参数缺失") if args._action == "run" and (not args._broker_id or not args._account_id or not args._password): raise Exception("run 必要参数缺失") if args._action == "backtest" and (not args._start_dt or not args._end_dt): raise Exception("backtest 必要参数缺失") if args._action == "mdreplay" and (not args._ins_url or not args._md_url): raise Exception("mdreplay 必要参数缺失") # 监控天勤进程存活情况 TqMonitorThread(args._tq_pid).start() # 建立到天勤进程的连接 tq_send_chan, tq_recv_chan = TqChan(api), TqChan(api) # 连接到天勤的channel api.create_task(api._connect(args._tq_url, tq_send_chan, tq_recv_chan)) # 启动到天勤客户端的连接 # 根据运行模式分别执行不同的初始化任务 if args._action == "run": instance = SingleInstance(args._account_id) api._account = TqAccount(args._broker_id, args._account_id, args._password) api._backtest = None dt_func = lambda: int(datetime.datetime.now().timestamp() * 1e9) tq_send_chan.send_nowait({ "aid": "register_instance", "instance_id": instance.instance_id, "full_path": get_self_full_name(), "instance_pid": os.getpid(), "instance_type": "RUN", "broker_id": args._broker_id, "account_id": args._account_id, "password": args._password, }) elif args._action == "backtest": instance = SingleInstance("%s-%s" % (args._start_dt, args._end_dt)) if not isinstance(api._account, TqSim): api._account = TqSim() from tqsdk.backtest import TqBacktest start_date = datetime.datetime.strptime(args._start_dt, '%Y%m%d') end_date = datetime.datetime.strptime(args._end_dt, '%Y%m%d') api._backtest = TqBacktest(start_dt=start_date, end_dt=end_date) dt_func = lambda: api._account._get_current_timestamp() tq_send_chan.send_nowait({ "aid": "register_instance", "instance_id": instance.instance_id, "full_path": get_self_full_name(), "instance_pid": os.getpid(), "instance_type": "BACKTEST", "start_dt": args._start_dt, "end_dt": args._end_dt, }) elif args._action == "mdreplay": instance = SingleInstance(args._account_id) api._account = TqSim(account_id=args._account_id) api._backtest = None api._md_url = args._md_url api._ins_url = args._ins_url dt_func = lambda: api._account._get_current_timestamp() tq_send_chan.send_nowait({ "aid": "register_instance", "instance_id": instance.instance_id, "full_path": get_self_full_name(), "instance_pid": os.getpid(), "instance_type": "RUN", "account_id": "SIM", }) else: raise Exception("_action 参数异常") # print输出, exception信息转发到天勤 logger = logging.getLogger("TQ") logger.setLevel(logging.INFO) logger.addHandler(LogHandlerChan(tq_send_chan, dt_func=dt_func)) # log输出到天勤接口 sys.stdout = PrintWriterToLog(logger) # print信息转向log输出 sys.excepthook = partial(exception_handler, api, sys.excepthook) # exception信息转向log输出 # 向api注入监控任务, 将账户交易信息主动推送到天勤 api.create_task(account_watcher(api, dt_func, tq_send_chan)) return tq_send_chan, tq_recv_chan
def run(): #获取命令行参数 parser = argparse.ArgumentParser() parser.add_argument('--source_file') parser.add_argument('--instance_id') parser.add_argument('--instance_file') args = parser.parse_args() # api api = TqApi(TqAccount("", args.instance_id, ""), url="ws://127.0.0.1:7777/" + args.instance_id) with closing(api): # log logger = logging.getLogger("TQ") logger.setLevel(logging.INFO) th = TqRunLogger(api.send_chan, args.instance_id) th.setLevel(logging.INFO) logger.addHandler(th) try: #加载策略文件 file_path, file_name = os.path.split(args.source_file) sys.path.insert(0, file_path) module_name = file_name[:-3] param_list = [] # 加载或输入参数 try: # 从文件读取参数表 with open(args.instance_file, "rt") as param_file: instance = json.load(param_file) param_list = instance.get("param_list", []) except IOError: # 获取用户代码中的参数表 def _fake_api_for_param_list(*args, **kwargs): m = sys.modules[module_name] for k, v in m.__dict__.items(): if k.upper() != k: continue param_list.append([k, v]) raise Exception() tqsdk.TqApi = _fake_api_for_param_list try: importlib.import_module(module_name) except Exception: pass param_list = input_param(param_list) if param_list is None: return with open(args.instance_file, "wt") as param_file: json.dump( { "instance_id": args.instance_id, "strategy_file_name": args.source_file, "desc": json.dumps(param_list), "param_list": param_list, }, param_file) api.send_chan.send_nowait({ "aid": "status", "instance_id": args.instance_id, "status": "RUNNING", "desc": json.dumps(param_list) }) # 拉起实例并直接执行 def _fake_api_for_launch(*args, **kwargs): m = sys.modules[module_name] for k, v in param_list: m.__dict__[k] = v return api tqsdk.TqApi = _fake_api_for_launch importlib.import_module(module_name) except ModuleNotFoundError: logger.exception("加载策略文件失败") except IndentationError: logger.exception("策略文件缩进格式错误") except Exception as e: logger.exception("策略运行中遇到异常", exc_info=True)