def on_execution_report(context, execrpt): """响应委托被执行事件,委托成交或者撤单拒绝后被触发。 https://www.myquant.cn/docs/python/python_trade_event#on_execution_report%20-%20%E5%A7%94%E6%89%98%E6%89%A7%E8%A1%8C%E5%9B%9E%E6%8A%A5%E4%BA%8B%E4%BB%B6 https://www.myquant.cn/docs/python/python_object_trade#ExecRpt%20-%20%E5%9B%9E%E6%8A%A5%E5%AF%B9%E8%B1%A1 :param context: :param execrpt: :return: """ if context.now.isoweekday() > 5 or context.now.hour not in [ 9, 10, 11, 13, 14 ]: # print(f"on_execution_report: {context.now} 不是交易时间") return latest_dt = context.now.strftime(dt_fmt) logger = context.logger msg = f"委托订单被执行通知:\n{'*' * 31}\n" \ f"时间:{latest_dt}\n" \ f"标的:{execrpt.symbol}\n" \ f"名称:{context.stocks.get(execrpt.symbol, '无名')}\n" \ f"方向:{order_side_map[execrpt.side]}{pos_effect_map[execrpt.position_effect]}\n" \ f"成交量:{int(execrpt.volume)}\n" \ f"成交价:{round(execrpt.price, 2)}\n" \ f"执行回报类型:{exec_type_map[execrpt.exec_type]}" logger.info(msg.replace("\n", " - ").replace('*', "")) if context.mode != MODE_BACKTEST and execrpt.exec_type in [ 1, 5, 6, 8, 12, 19 ]: wx.push_text(content=str(msg), key=context.wx_key)
def on_backtest_finished(context, indicator): """ :param context: :param indicator: https://www.myquant.cn/docs/python/python_object_trade#bd7f5adf22081af5 :return: """ logger = context.logger logger.info(str(indicator)) logger.info("回测结束 ... ") cash = context.account().cash for k, v in indicator.items(): if isinstance(v, float): indicator[k] = round(v, 4) symbol = list(context.symbols)[0] row = OrderedDict({ "研究标的": ", ".join(list(context.symbols_map.keys())), "回测开始时间": context.backtest_start_time, "回测结束时间": context.backtest_end_time, "累计收益率": indicator['pnl_ratio'], "最大回撤": indicator['max_drawdown'], "年化收益率": indicator['pnl_ratio_annual'], "夏普比率": indicator['sharp_ratio'], "盈利次数": indicator['win_count'], "亏损次数": indicator['lose_count'], "交易胜率": indicator['win_ratio'], "累计出入金": int(cash['cum_inout']), "累计交易额": int(cash['cum_trade']), "累计手续费": int(cash['cum_commission']), "累计平仓收益": int(cash['cum_pnl']), "净收益": int(cash['pnl']), "因子版本": context.symbols_map[symbol]['trader'].version, "开多因子": context.symbols_map[symbol]['trader'].long_open_factors, "平多因子": context.symbols_map[symbol]['trader'].long_close_factors, "开空因子": context.symbols_map[symbol]['trader'].short_open_factors, "平空因子": context.symbols_map[symbol]['trader'].short_close_factors, }) df = pd.DataFrame([row]) df.to_excel(os.path.join(context.data_path, "回测结果.xlsx"), index=False) logger.info("回测结果:{}".format(row)) content = "" for k, v in row.items(): if k not in ["开多因子", '平多因子', '开空因子', '平空因子']: content += "{}: {}\n".format(k, v) push_text(content=content, key=context.wx_key) for symbol in context.symbols: # 查看买卖详情 file_bs = os.path.join(context.cache_path, "{}_bs.txt".format(symbol)) if os.path.exists(file_bs): lines = [eval(x) for x in open(file_bs, 'r', encoding="utf-8").read().strip().split("\n")] df = pd.DataFrame(lines) print(symbol, "\n", df.desc.value_counts()) print(df)
def on_error(context, code, info): if context.now.isoweekday() > 5 or context.now.hour not in [ 9, 10, 11, 13, 14 ]: # print(f"on_error: {context.now} 不是交易时间") return logger = context.logger msg = "{} - {}".format(code, info) logger.warn(msg) if context.mode != MODE_BACKTEST: wx.push_text(content=msg, key=context.wx_key)
def on_order_status(context, order): """ https://www.myquant.cn/docs/python/python_object_trade#007ae8f5c7ec5298 :param context: :param order: :return: """ if context.now.isoweekday() > 5 or context.now.hour not in [ 9, 10, 11, 13, 14 ]: # print(f"on_order_status: {context.now} 不是交易时间") return symbol = order.symbol latest_dt = context.now.strftime("%Y-%m-%d %H:%M:%S") logger = context.logger if symbol not in context.symbols_info.keys(): msg = f"订单状态更新通知:\n{'*' * 31}\n" \ f"更新时间:{latest_dt}\n" \ f"标的名称:{symbol} {context.stocks.get(symbol, '无名')}\n" \ f"操作类型:{order_side_map[order.side]}{pos_effect_map[order.position_effect]}\n" \ f"操作描述:非机器交易标的\n" \ f"下单价格:{round(order.price, 2)}\n" \ f"最新状态:{order_status_map[order.status]}\n" \ f"委托(股):{int(order.volume)}\n" \ f"已成(股):{int(order.filled_volume)}\n" \ f"均价(元):{round(order.filled_vwap, 2)}" else: trader: GmCzscTrader = context.symbols_info[symbol]['trader'] if trader.long_pos.operates: last_op_desc = trader.long_pos.operates[-1]['op_desc'] else: last_op_desc = "" msg = f"订单状态更新通知:\n{'*' * 31}\n" \ f"更新时间:{latest_dt}\n" \ f"标的名称:{symbol} {context.stocks.get(symbol, '无名')}\n" \ f"操作类型:{order_side_map[order.side]}{pos_effect_map[order.position_effect]}\n" \ f"操作描述:{last_op_desc}\n" \ f"下单价格:{round(order.price, 2)}\n" \ f"最新状态:{order_status_map[order.status]}\n" \ f"委托(股):{int(order.volume)}\n" \ f"已成(股):{int(order.filled_volume)}\n" \ f"均价(元):{round(order.filled_vwap, 2)}" logger.info(msg.replace("\n", " - ").replace('*', "")) if context.mode != MODE_BACKTEST and order.status in [1, 3, 5, 8, 9, 12]: wx.push_text(content=str(msg), key=context.wx_key)
def report_account_status(context): """报告账户持仓状态""" if context.now.isoweekday() > 5: print(f"{context.now} 不是交易时间") return logger = context.logger latest_dt = context.now.strftime(dt_fmt) account = context.account(account_id=context.account_id) cash = account.cash positions = account.positions() logger.info("=" * 30 + f" 账户状态【{latest_dt}】 " + "=" * 30) cash_report = f"净值:{int(cash.nav)},可用资金:{int(cash.available)}," \ f"浮动盈亏:{int(cash.fpnl)},标的数量:{len(positions)}" logger.info(cash_report) for p in positions: p_report = f"标的:{p.symbol},名称:{context.stocks.get(p.symbol, '无名')}," \ f"数量:{p.volume},成本:{round(p.vwap, 2)},方向:{p.side}," \ f"当前价:{round(p.price, 2)},成本市值:{int(p.volume * p.vwap)}," \ f"建仓时间:{p.created_at.strftime(dt_fmt)}" logger.info(p_report) # 实盘或仿真,推送账户信息到企业微信 if context.mode != MODE_BACKTEST: msg = f"股票账户状态报告\n{'*' * 31}\n" msg += f"账户净值:{int(cash.nav)}\n" \ f"持仓市值:{int(cash.market_value)}\n" \ f"可用资金:{int(cash.available)}\n" \ f"浮动盈亏:{int(cash.fpnl)}\n" \ f"标的数量:{len(positions)}\n" \ f"{'*' * 31}\n" for p in positions: try: msg += f'标的代码:{p.symbol}\n' \ f"标的名称:{context.stocks.get(p.symbol, '无名')}\n" \ f'持仓数量:{p.volume}\n' \ f'最新价格:{round(p.price, 2)}\n' \ f'持仓成本:{round(p.vwap, 2)}\n' \ f'盈亏比例:{int((p.price - p.vwap) / p.vwap * 10000) / 100}%\n' \ f'持仓市值:{int(p.volume * p.vwap)}\n' \ f'持仓天数:{(context.now - p.created_at).days}\n' \ f"{'*' * 31}\n" except: print(p) wx.push_text(msg.strip("\n *"), key=context.wx_key)
def check_index_status(qywx_key): """查看主要指数状态""" from czsc.utils.cache import home_path end_dt = datetime.now().strftime(dt_fmt) wx.push_text(f"{end_dt} 开始获取主要指数行情快照", qywx_key) for gm_symbol in indices.values(): try: file_html = os.path.join( home_path, f"{gm_symbol}_{datetime.now().strftime('%Y%m%d')}.html") gm_take_snapshot(gm_symbol, end_dt, file_html=file_html) wx.push_file(file_html, qywx_key) os.remove(file_html) except: traceback.print_exc() wx.push_text(f"{end_dt} 获取主要指数行情快照获取结束,请仔细观察!!!", qywx_key)
def on_execution_report(context, execrpt): """响应委托被执行事件,委托成交或者撤单拒绝后被触发。 https://www.myquant.cn/docs/python/python_trade_event#on_execution_report%20-%20%E5%A7%94%E6%89%98%E6%89%A7%E8%A1%8C%E5%9B%9E%E6%8A%A5%E4%BA%8B%E4%BB%B6 https://www.myquant.cn/docs/python/python_object_trade#ExecRpt%20-%20%E5%9B%9E%E6%8A%A5%E5%AF%B9%E8%B1%A1 :param context: :param execrpt: :return: """ latest_dt = context.now.strftime("%Y-%m-%d %H:%M:%S") logger = context.logger msg = f"委托订单被执行通知:时间:{latest_dt},标的:{execrpt.symbol},方向:{execrpt.side}," \ f"成交量: {int(execrpt.volume)},成交价:{round(execrpt.price, 2)},执行回报类型:{execrpt.exec_type}" logger.info(msg) if context.mode != MODE_BACKTEST: push_text(content=str(msg), key=context.wx_key)
def on_account_status(context, account): """响应交易账户状态更新事件,交易账户状态变化时被触发 https://www.myquant.cn/docs/python/python_trade_event#4f07d24fc4314e3c """ status = account['status'] if status['state'] == 3: return if context.now.isoweekday() > 5 or context.now.hour not in [ 9, 10, 11, 13, 14 ]: # print(f"on_account_status: {context.now} 不是交易时间") return logger = context.logger msg = f"{str(account)}" logger.warn(msg) if context.mode != MODE_BACKTEST: wx.push_text(content=msg, key=context.wx_key)
def on_order_status(context, order): """ https://www.myquant.cn/docs/python/python_object_trade#007ae8f5c7ec5298 :param context: :param order: :return: """ latest_dt = context.now.strftime("%Y-%m-%d %H:%M:%S") logger = context.logger file_orders = context.file_orders msg = f"订单状态更新通知:交易时间:{latest_dt},订单状态:{order.symbol},方向:{order.side}," \ f"价格:{round(order.price, 2)},状态:{order.status}," \ f"委托量:{int(order.volume)},已成量: {int(order.filled_volume)}," \ f"成交均价:{round(order.filled_vwap, 2)}" logger.info(msg) if context.mode == MODE_BACKTEST: with open(file_orders, 'a', encoding="utf-8") as f: f.write(str(order) + '\n') else: if order.status in [1, 3]: push_text(content=str(msg), key=context.wx_key)
def monitor(use_cache=True): push_text("自选股CZSC笔因子监控启动 @ {}".format(datetime.now().strftime("%Y-%m-%d %H:%M")), qywx_key) moni_path = os.path.join(ct_path, "monitor") # 首先清空历史快照 if os.path.exists(moni_path): shutil.rmtree(moni_path) os.makedirs(moni_path, exist_ok=True) for s in symbols: print(s) try: file_ct = os.path.join(ct_path, "{}.ct".format(s)) if os.path.exists(file_ct) and use_cache: ct: CzscTrader = read_pkl(file_ct) ct.update_factors() else: ct = CzscTrader(s, max_count=1000) save_pkl(ct, file_ct) # 每次执行,会在moni_path下面保存一份快照 file_html_ = os.path.join(moni_path, f"{ct.symbol}_{ct.kf.end_dt.strftime('%Y%m%d%H%M')}.html") ct.take_snapshot(file_html_, width="1400px", height="580px") if ct.s['日线笔因子'] != Factors.Other.value: msg = "{} - {}\n".format(s, ct.s['日线笔因子']) msg += "同花顺F10:http://basic.10jqka.com.cn/{}".format(s[:6]) push_text(msg, key=qywx_key) file_html_new = os.path.join(moni_path, f"{ct.symbol}_{ct.kf.end_dt.strftime('%Y%m%d%H%M')}.html") shutil.copyfile(file_html_, file_html_new) push_file(file_html_new, key=qywx_key) except Exception as e: traceback.print_exc() print("{} 执行失败 - {}".format(s, e)) push_text("自选股CZSC笔因子监控结束 @ {}".format(datetime.now().strftime("%Y-%m-%d %H:%M")), qywx_key)
def monitor(use_cache=True): push_text( "自选股CZSC笔因子监控启动 @ {}".format( datetime.now().strftime("%Y-%m-%d %H:%M")), qywx_key) moni_path = os.path.join(ct_path, "monitor") # 首先清空历史快照 if os.path.exists(moni_path): shutil.rmtree(moni_path) os.makedirs(moni_path, exist_ok=True) events_monitor = [ # 开多 Event(name="一买", operate=Operate.LO, factors=[ Factor(name="5分钟类一买", signals_all=[Signal("5分钟_倒1笔_类买卖点_类一买_任意_任意_0")]), Factor(name="5分钟形一买", signals_all=[Signal("5分钟_倒1笔_基础形态_类一买_任意_任意_0")]), Factor(name="15分钟类一买", signals_all=[Signal("15分钟_倒1笔_类买卖点_类一买_任意_任意_0")]), Factor(name="15分钟形一买", signals_all=[Signal("15分钟_倒1笔_基础形态_类一买_任意_任意_0")]), Factor(name="30分钟类一买", signals_all=[Signal("30分钟_倒1笔_类买卖点_类一买_任意_任意_0")]), Factor(name="30分钟形一买", signals_all=[Signal("30分钟_倒1笔_基础形态_类一买_任意_任意_0")]), ]), Event(name="二买", operate=Operate.LO, factors=[ Factor(name="5分钟类二买", signals_all=[Signal("5分钟_倒1笔_类买卖点_类二买_任意_任意_0")]), Factor(name="5分钟形二买", signals_all=[Signal("5分钟_倒1笔_基础形态_类二买_任意_任意_0")]), Factor(name="15分钟类二买", signals_all=[Signal("15分钟_倒1笔_类买卖点_类二买_任意_任意_0")]), Factor(name="15分钟形二买", signals_all=[Signal("15分钟_倒1笔_基础形态_类二买_任意_任意_0")]), Factor(name="30分钟类二买", signals_all=[Signal("30分钟_倒1笔_类买卖点_类二买_任意_任意_0")]), Factor(name="30分钟形二买", signals_all=[Signal("30分钟_倒1笔_基础形态_类二买_任意_任意_0")]), ]), Event(name="三买", operate=Operate.LO, factors=[ Factor(name="5分钟类三买", signals_all=[Signal("5分钟_倒1笔_类买卖点_类三买_任意_任意_0")]), Factor(name="5分钟形三买", signals_all=[Signal("5分钟_倒1笔_基础形态_类三买_任意_任意_0")]), Factor(name="15分钟类三买", signals_all=[Signal("15分钟_倒1笔_类买卖点_类三买_任意_任意_0")]), Factor(name="15分钟形三买", signals_all=[Signal("15分钟_倒1笔_基础形态_类三买_任意_任意_0")]), Factor(name="30分钟类三买", signals_all=[Signal("30分钟_倒1笔_类买卖点_类三买_任意_任意_0")]), Factor(name="30分钟形三买", signals_all=[Signal("30分钟_倒1笔_基础形态_类三买_任意_任意_0")]), ]), # 平多 Event(name="一卖", operate=Operate.LE, factors=[ Factor(name="5分钟类一卖", signals_all=[Signal("5分钟_倒1笔_类买卖点_类一卖_任意_任意_0")]), Factor(name="5分钟形一卖", signals_all=[Signal("5分钟_倒1笔_基础形态_类一卖_任意_任意_0")]), Factor(name="15分钟类一卖", signals_all=[Signal("15分钟_倒1笔_类买卖点_类一卖_任意_任意_0")]), Factor(name="15分钟形一卖", signals_all=[Signal("15分钟_倒1笔_基础形态_类一卖_任意_任意_0")]), Factor(name="30分钟类一卖", signals_all=[Signal("30分钟_倒1笔_类买卖点_类一卖_任意_任意_0")]), Factor(name="30分钟形一卖", signals_all=[Signal("30分钟_倒1笔_基础形态_类一卖_任意_任意_0")]), ]), Event(name="二卖", operate=Operate.LE, factors=[ Factor(name="5分钟类二卖", signals_all=[Signal("5分钟_倒1笔_类买卖点_类二卖_任意_任意_0")]), Factor(name="5分钟形二卖", signals_all=[Signal("5分钟_倒1笔_基础形态_类二卖_任意_任意_0")]), Factor(name="15分钟类二卖", signals_all=[Signal("15分钟_倒1笔_类买卖点_类二卖_任意_任意_0")]), Factor(name="15分钟形二卖", signals_all=[Signal("15分钟_倒1笔_基础形态_类二卖_任意_任意_0")]), Factor(name="30分钟类二卖", signals_all=[Signal("30分钟_倒1笔_类买卖点_类二卖_任意_任意_0")]), Factor(name="30分钟形二卖", signals_all=[Signal("30分钟_倒1笔_基础形态_类二卖_任意_任意_0")]), ]), Event(name="三卖", operate=Operate.LE, factors=[ Factor(name="5分钟类三卖", signals_all=[Signal("5分钟_倒1笔_类买卖点_类三卖_任意_任意_0")]), Factor(name="5分钟形三卖", signals_all=[Signal("5分钟_倒1笔_基础形态_类三卖_任意_任意_0")]), Factor(name="15分钟类三卖", signals_all=[Signal("15分钟_倒1笔_类买卖点_类三卖_任意_任意_0")]), Factor(name="15分钟形三卖", signals_all=[Signal("15分钟_倒1笔_基础形态_类三卖_任意_任意_0")]), Factor(name="30分钟类三卖", signals_all=[Signal("30分钟_倒1笔_类买卖点_类三卖_任意_任意_0")]), Factor(name="30分钟形三卖", signals_all=[Signal("30分钟_倒1笔_基础形态_类三卖_任意_任意_0")]), ]), ] for s in symbols: print(s) try: file_ct = os.path.join(ct_path, "{}.ct".format(s)) if os.path.exists(file_ct) and use_cache: ct: CzscTrader = read_pkl(file_ct) ct.update_factors() else: ct = CzscTrader(s, max_count=1000) save_pkl(ct, file_ct) # 每次执行,会在moni_path下面保存一份快照 file_html = os.path.join( moni_path, f"{ct.symbol}_{ct.end_dt.strftime('%Y%m%d%H%M')}.html") ct.take_snapshot(file_html, width="1400px", height="580px") msg = f"标的代码:{s}\n同花顺F10:http://basic.10jqka.com.cn/{s.split('.')[0]}\n" for event in events_monitor: m, f = event.is_match(ct.s) if m: msg += "监控提醒:{}@{}\n".format(event.name, f) if "监控提醒" in msg: push_text(msg.strip("\n"), key=qywx_key) except Exception as e: traceback.print_exc() print("{} 执行失败 - {}".format(s, e)) push_text( "自选股CZSC笔因子监控结束 @ {}".format( datetime.now().strftime("%Y-%m-%d %H:%M")), qywx_key)
def on_backtest_finished(context, indicator): """回测结束回调函数 :param context: :param indicator: https://www.myquant.cn/docs/python/python_object_trade#bd7f5adf22081af5 :return: """ wx_key = context.wx_key symbols = context.symbols data_path = context.data_path logger = context.logger logger.info(str(indicator)) logger.info("回测结束 ... ") cash = context.account().cash for k, v in indicator.items(): if isinstance(v, float): indicator[k] = round(v, 4) row = OrderedDict({ "研究标的": ", ".join(list(context.symbols_info.keys())), "回测开始时间": context.backtest_start_time, "回测结束时间": context.backtest_end_time, "累计收益率": indicator['pnl_ratio'], "最大回撤": indicator['max_drawdown'], "年化收益率": indicator['pnl_ratio_annual'], "夏普比率": indicator['sharp_ratio'], "盈利次数": indicator['win_count'], "亏损次数": indicator['lose_count'], "交易胜率": indicator['win_ratio'], "累计出入金": int(cash['cum_inout']), "累计交易额": int(cash['cum_trade']), "累计手续费": int(cash['cum_commission']), "累计平仓收益": int(cash['cum_pnl']), "净收益": int(cash['pnl']), }) sdt = pd.to_datetime(context.backtest_start_time).strftime('%Y%m%d') edt = pd.to_datetime(context.backtest_end_time).strftime('%Y%m%d') file_xlsx = os.path.join(data_path, f'{context.name}_{sdt}_{edt}.xlsx') file = pd.ExcelWriter(file_xlsx, mode='w') dfe = pd.DataFrame({"指标": list(row.keys()), "值": list(row.values())}) dfe.to_excel(file, sheet_name='回测表现', index=False) logger.info("回测结果:{}".format(row)) content = "" for k, v in row.items(): content += "{}: {}\n".format(k, v) wx.push_text(content=content, key=wx_key) trades = [] operates = [] performances = [] for symbol in symbols: trader: GmCzscTrader = context.symbols_info[symbol]['trader'] trades.extend(trader.long_pos.pairs) operates.extend(trader.long_pos.operates) performances.append(trader.long_pos.evaluate_operates()) df = pd.DataFrame(trades) df['开仓时间'] = df['开仓时间'].apply(lambda x: x.strftime("%Y-%m-%d %H:%M")) df['平仓时间'] = df['平仓时间'].apply(lambda x: x.strftime("%Y-%m-%d %H:%M")) df.to_excel(file, sheet_name='交易汇总', index=False) dfo = pd.DataFrame(operates) dfo['dt'] = dfo['dt'].apply(lambda x: x.strftime("%Y-%m-%d %H:%M")) dfo.to_excel(file, sheet_name='操作汇总', index=False) dfp = pd.DataFrame(performances) dfp.to_excel(file, sheet_name='表现汇总', index=False) file.close() wx.push_file(file_xlsx, wx_key)
def on_account_status(context, account): """响应交易账户状态更新事件,交易账户状态变化时被触发""" context.logger.info(str(account)) push_text(str(account), key=context.wx_key)
def on_error(context, code, info): logger = context.logger msg = "{} - {}".format(code, info) logger.warn(msg) if context.mode != MODE_BACKTEST: push_text(content=msg, key=context.wx_key)