██████╗██╗ ██╗ █████╗ ███╗ ██╗ ██████╗ ██╗ ██╗
██╔════╝██║ ██║██╔══██╗████╗ ██║ ██╔══██╗╚██╗ ██╔╝
██║ ███████║███████║██╔██╗ ██║ ██████╔╝ ╚████╔╝
██║ ██╔══██║██╔══██║██║╚██╗██║ ██╔═══╝ ╚██╔╝
╚██████╗██║ ██║██║ ██║██║ ╚████║██╗██║ ██║
╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝╚═╝ ╚═╝
特别说明①:当前公开部分代码暂时只包含基本的静态计算能力,暂未包含策略类,特征,模型,automl框架,交易引擎对接等;
完整代码22000行左右,公开版约5300行;本README对应的是完整版(可能在某些地方使用上和公开版本代码不一致),尽量参考快速上手指南;
如有使用疑惑,欢迎讨论/邮件联系。
特别说明②:依赖最低版本为python3.11;由于本项目是高度计算密集型,鉴于python3.11发布且运算速度大幅提升,实测相比于python 3.8.5计算时间缩短约16%,故后续开发均基于python3.11;
本框架从使用深度上来讲,分四种不同的级别:
.
├── 📁 Bi # 笔
│ ├── 📄 BiConfig.py: 配置类
│ ├── 📄 BiList.py: 笔列表类
│ └── 📄 Bi.py: 笔类
├── 📁 Seg: 线段类
│ ├── 📄 Eigen.py: 特征序列类
│ ├── 📄 EigenFX.py: 特征序列分形类
│ ├── 📄 SegConfig.py: 线段配置类
│ ├── 📄 SegListComm.py: 线段计算框架通用父类
│ ├── 📄 SegListChan.py: 线段计算:根据原文实现
│ ├── 📄 SegListDef.py: 线段计算:根据定义实现
│ ├── 📄 SegListDYH.py: 线段计算:根据都业华1+1突破实现
│ └── 📄 Seg.py: 线段类
├── 📁 ZS: 中枢类
│ ├── 📄 ZSConfig.py: 中枢配置
│ ├── 📄 ZSList.py: 中枢列表类
│ └── 📄 ZS.py: 中枢类
├── 📁 KLine: K线类
│ ├── 📄 KLine_List.py: K线列表类
│ ├── 📄 KLine.py: 合并K线类
│ ├── 📄 KLine_Unit.py: 单根K线类
│ └── 📄 TradeInfo.py: K线指标类(换手率,成交量,成交额等)
├── 📁 BuySellPoint: 形态学买卖点类(即bsp)
│ ├── 📄 BSPointConfig.py: 配置
│ ├── 📄 BSPointList.py: 买卖点列表类
│ └── 📄 BS_Point.py: 买卖点类
├── 📁 Combiner: K线,特征序列合并器
│ ├── 📄 Combine_Item.py: 合并元素通用父类
│ └── 📄 KLine_Combiner.py: K线合并器
├── 📁 Common: 通用函数
│ ├── 📄 cache.py: 缓存装饰器,大幅提高计算性能
│ ├── 📄 CEnum.py: 所有枚举类,K线类型/方向/笔类型/中枢类型等
│ ├── 📄 ChanException.py: 异常类
│ ├── 📄 CTime.py: 缠论时间类(可处理不同级别联立)
│ ├── 📄 func_util.py: 通用函数
│ ├── 📄 send_msg_cmd.py: 消息推送
│ ├── 📄 tools.py: 工具类
│ ├── 📄 CommonThred.py: 线程类
│ └── 📄 TradeUtil.py: 交易通用函数
├── 📁 Config: 配置
│ ├── 📄 config.sh shell脚本读取配置
│ ├── 📄 demo_config.yaml: demo配置
│ ├── 📄 EnvConfig.py: python读取配置类
├── 📁 CustomBuySellPoint: 自定义动力学买卖点类(即cbsp)
│ ├── 📄 Strategy.py: 通用抽象策略父类
│ ├── 📄 CustomStrategy.py: demo策略1
│ ├── 📄 SegBspStrategy.py: demo策略2
│ ├── 📄 ExamStrategy.py: 生成买卖点判断试题的策略
│ ├── 📄 CustomBSP.py: 自定义买卖点
│ └── 📄 Signal.py: 信号类
├── 📁 DataAPI: 数据接口
│ ├── 📄 CommonStockAPI.py: 通用数据接口抽象父类
│ ├── 📄 AkShareAPI.py: akshare数据接口
│ ├── 📄 BaoStockAPI.py: baostock数据接口
│ ├── 📄 ETFStockAPI.py: ETF数据解耦接口
│ ├── 📄 FutuAPI.py: futu数据接口
│ ├── 📄 OfflineDataAPI.py: 离线数据接口
│ ├── 📄 MarketValueFilter.py: 股票市值过滤类
│ └── 📁 SnapshotAPI: 实时股价数据接口
│ ├── 📄 StockSnapshotAPI.py: 统一调用接口
│ ├── 📄 CommSnapshot.py: snapshot通用父类
│ ├── 📄 AkShareSnapshot.py: akshare接口,支持a股,etf,港股,美股
│ ├── 📄 FutuSnapshot.py: 富途接口,支持a股,港股,美股
│ ├── 📄 PytdxSnapshot.py: pytdx,支持A股,ETF
│ └── 📄 SinaSnapshot.py: 新浪接口,支持a股,etf,港股,美股
├── 📁 Math: 计算类
│ ├── 📄 BOLL.py: 布林线计算类
│ ├── 📄 MACD.py: MACD计算类
│ ├── 📄 Demark.py: Demark指标计算类
│ ├── 📄 OutlinerDetection.py: 离群点计算类
│ ├── 📄 TrendModel.py: 趋势类(支持均线,最大值,最小值)
│ └── 📄 TrendLine.py: 趋势线
├── 📁 ModelStrategy: 模型策略
│ ├── 📄 BacktestChanConfig.py: 回测配置
│ ├── 📄 backtest.py: 回测计算框架
│ ├── 📄 FeatureReconciliation.py: 特征离线在线一致性校验
│ ├── 📄 ModelGenerator.py: 训练模型通用父类
│ ├── 📁 models: 提供模型
│ │ ├── 📁 deepModel: 深度学习模型
│ │ │ ├── 📄 MLPModelGenerator.py: 深度学习模型
│ │ │ └── 📄 train_all_model.sh 拉起全流程训练预测评估脚本
│ │ ├── 📁 lightGBM
│ │ │ ├── 📄 LGBMModelGenerator.py: LGBM模型
│ │ │ └── 📄 train_all_model.sh 拉起全流程训练预测评估脚本
│ │ └── 📁 Xgboost
│ │ ├── 📄 train_all_model.sh 拉起全流程训练预测评估脚本
│ │ ├── 📄 XGBTrainModelGenerator.py: XGB模型
│ │ └── 📄 xgb_util.py: XGB便捷调用工具
│ └── 📁 parameterEvaluate: 策略参数评估
│ ├── 📄 eval_strategy.py: 评估策略收益类
│ ├── 📄 multi_cycle_test_data.sh: 多周期策略评估数据生成脚本
│ ├── 📄 multi_cycle_test.py: 多周期策略评估
│ ├── 📄 para_automl.py: Automl计算模型超参
│ ├── 📄 automl_verify.py: 验证Automl结果小脚本
│ ├── 📄 parse_automl_result.py: 解析Automl超参生成交易配置文件
│ └── 📁 AutoML FrameWork: Automl学习框架,本项目不专门提供
├── 📁 ChanModel: 模型
│ ├── 📄 CommModel.py: 通用模型抽象父类
│ ├── 📄 FeatureDesc.py: 特征注册
│ ├── 📄 Features.py: 特征计算
│ └── 📄 XGBModel.py: XGB模型 demo
├── 📁 OfflineData: 离线数据更新
│ ├── 📄 download_all_offline_data.sh 调度下载A股,港股,美股所有数据脚本
│ ├── 📄 ak_update.py: akshare更新港股美股A股离线数据
│ ├── 📄 bao_download.py: baostock下载全量A股数据
│ ├── 📄 bao_update.py: baostock增量更新数据
│ ├── 📄 etf_download.py: 下载A股ETF数据脚本
│ ├── 📄 futu_download.py: 更新futu港股数据
│ ├── 📄 offline_data_util.py: 离线数据更新通用工具类
│ └── 📁 stockInfo: 股票指标数据
│ ├── 📄 CalTradeInfo.py: 计算股票指标数据分布,分位数
│ ├── 📄 query_marketvalue.py: 计算股票市值分位数
│ └── 📄 run_market_value_query.sh 调度脚本
├── 📁 Plot: 画图类
│ ├── 📄 AnimatePlotDriver.py: 动画画图类
│ ├── 📄 PlotDriver.py: matplotlib画图引擎
│ ├── 📄 PlotMeta.py: 图元数据
│ └── 📁 CosApi: COS文件上传类
│ ├── 📄 minio_api.py: minio上传接口
│ ├── 📄 tencent_cos_api.py: 腾讯云cos上传接口
│ └── 📄 cos_config.py: 读取项目配置里面的cos配置参数
├── 📁 Trade: 交易引擎
│ ├── 📄 db_util.py: 数据库操作类
│ ├── 📄 FutuTradeEngine.py: futu交易引擎类
│ ├── 📄 MysqlDB.py: Mysql数据库类
│ ├── 📄 SqliteDB.py: SqliteDB数据库类
│ ├── 📄 OpenQuotaGen.py: 开仓交易手数策略类(用于控制仓位)
│ ├── 📄 TradeEngine.py: 交易引擎核心类
│ └── 📁 Script: 核心交易脚本
│ ├── 📄 update_data_signal.sh: 离线数据更新,信号计算调度脚本
│ ├── 📄 CheckOpenScore.py: 后验检查开仓是否准确
│ ├── 📄 ClosePreErrorOpen.py: 修复错误开仓
│ ├── 📄 MakeOpenTrade.py: 开仓
│ ├── 📄 OpenConfig_demo.yaml: 开仓参数配置
│ ├── 📄 OpenConfig.py: 开仓参数配置类
│ ├── 📄 RealTimeTracker.py: 实时跟踪是否遇到止损,止盈点
│ ├── 📄 RetradeCoverOrder.py: 修复未成功交易平仓单
│ ├── 📄 SignalMonitor.py: 信号计算
│ ├── 📄 StaticsChanConfig.py: 缠论计算配置
│ └── 📄 UpdatePeakPrice.py: 峰值股价更新(用于做动态止损)
├── 📁 Debug: debug工具
│ ├── 📁 cprofile_analysis: 性能分析
│ │ └── 📄 cprofile_analysis.sh 性能分析脚本
│ └── 📁 Notebook
│ └── 📄 xxx.ipynb 各种notebook
├── 📁 Script: 脚本汇总
│ ├── 📄 InitDB.py: 数据库初始化
│ ├── 📄 Install.sh 安装本框架脚本
│ ├── 📄 requirements.txt: pip requirements文件
│ ├── 📄 pip_upgrade.sh: pip更新股票数据相关的库
│ ├── 📄 run_backtest.sh 运行回测计算
│ ├── 📄 run_train_pipeline.sh 运行回测,指定模型训练预测评估,校验,全pipeline脚本
│ ├── 📁 cprofile_analysis: 性能分析
│ │ └── 📄 cprofile_analysis.sh 性能分析脚本
│ └── 📁 Notion: Notion数据表同步脚本
│ ├── 📄 DB_sync_Notion.py 交易数据库同步Notion脚本
│ └── 📁 notion: Notion API
│ ├── 📄 notion_api.py: Notion统一API接口
│ ├── 📄 block_driver.py: Notion块操作类
│ ├── 📄 prop_driver.py.py: Notion数据表属性操作类
│ ├── 📄 text.py: Notion 富文本操作类
│ └── 📄 secret.py: notion读取配置文件里面的参数
├── 📄 main.py: demo main函数
├── 📄 Chan.py: 缠论主类
├── 📄 ChanConfig.py: 缠论配置
├── 📄 ExamGenerator.py: 测试题生成API
├── 📄 LICENSE
└── 📄 README.md: 本文件
Config/config.yaml
Script/Install.sh
,会执行:
Common/tools.py
配置文件所需填写内容如下:
Env:
python: /usr/bin/python3.11 # python命令
Data:
offline_data_path: xxx # 离线数据存储位置
model_path: xxx # 模型数据存储位置
log_path: xxx # 日志存储位置
stock_info_path: xxx # 股票信息存储位置
pickle_data_path: xxx # 股票pickle文件存储位置(可不填)
automl_result_path: xxx # automl结果输出路径
send_offline_data_update_info: True # 是否推送每日数据更新情况
send_kl_missing_msg: False # 是否推送K线丢失情况(下载的离线数据可能会缺失本来已有的K线)
DB:
TYPE: mysql # 数据库类型,可选mysql / sqlite
HOST: 127.0.0.1 # mysql host(TYPE=mysql时填写)
PORT: 3306 # mysql 端口(TYPE=mysql时填写)
USER: xxx # mysql user(TYPE=mysql时填写)
PASSWD: xxx # mysql 密码(TYPE=mysql时填写)
DATABASE: xxx # mysql数据库(TYPE=mysql时填写)
SQLITE_PATH: xxx/xxx/xxx.db # sqlite路径(TYPE=sqlite时填写)
TABLE: xxx # sqlite表名(TYPE=sqlite时填写)
Futu:
PASSWORD_MD5: xxx # futu交易密码
HOST: 127.0.0.1 # futu后端 host
PORT: 11111 # futu后端端口
RSA_PATH: "" # futu RSA鉴权文件
ENV: SIMULATE # futu交易环境,SIMULATE模拟盘/REAL实盘
Trade:
log_file: xxx # 交易日志文件路径
trade_model_path: xxx # 交易模型路径
area: cn,hk,us # 交易哪些地区股票,A股cn,港股hk,美股us,逗号分割
open_price_tolerance: 0.01 # 开仓后验允许价格误差,相对值
open_score_tolerance: 0.03 # 开仓后验允许分数误差,绝对值
chan_begin_date: 2015-01-01 # 缠论计算开始K线日期
latest_ipo_date: 2021-01-01 # 股票最晚上市时间(太近的话,K线数据不足,计算不准)
touch_sl_cnt: 1 # 多少根分钟K线连续触达止损线才发起止损单
touch_sw_cnt: 1 # 多少根分钟K线连续触达止盈线才发起止盈单
allow_break_sw_bound: True # 止盈提单后如果没成交且价格跌破止盈价,是否允许调整下单价格低于止盈价
dynamic_sl_include_tody: False # 动态止盈是否考虑当天峰值
DST: True # True是夏令时,False是冬令时,影响美股交易时间
open_thred_cnt: 10 # 开仓时计算缠论信息的线程数
trade_reconciliation_begin_t: '20220101' # 交易一致性检测开始时间
snapshot_eigine: # 股价快照引擎,默认值为下面配置
us: sina
hk: futu
cn: futu
Model: # 模型配置,可自定义
model_tag: bsp_label-scale # 模型标签
model_type: normal:normal/area/bs_type # 模型分数来源
backtest_begin_date: '2000-01-01' # 回测数据开始时间
sample_set_split_date: '20220101' # 训练集,测试集划分时间
automl_begin_t: '20220101' # automl数据集开始时间,也就是predict_all的参数
automl_klu_end_date: '2022/12/31' # automl K线开始时间,可以不填,则为无穷久之后
automl_begin_open_date: '2021/01/01' # automl 最早允许开仓时间
automl_end_open_date: '2022/09/01' # automl 最晚允许开仓时间
win_loss_ratio_smooth: 50.0 # 盈亏比平滑系数
Chan:
debug: false # 是否开启debug模式
Notion: # Notion同步配置
cn_id: xxx
hk_id: xxx
us_id: xxx
token: xxx
COS: # COS上传图片配置
cos_type: xxx # tencent/minio
tencent_cos:
secretid: xxx
secretkey: xxx
region: ap-xxx
bucket: chan-xxx
minio:
endpoint: xxxx.com
access_key: xxx
secret_key: xxx
bucket: xxx
需要根据自身服务器环境提供三个函数在 Common/tools.py
文件中,当然默认情况下可以只定义不实现;
def send_msg(title, content, lv='INFO'):
...
def _metric_report(id, key, value):
...
def _log_trade(title, *msg):
...
send_msg
:发送消息函数,比如运行失败,离线计算结果,交易订单相关的信息等等,均会调用这个函数给用户发送消息;可以对接自己的 gotify/chanify/server 酱/邮件等_metric_report
:指标上报,可以对接自己的grafana或者其他监控系统,比如可以查看到数据更新是否稳定,每天新增多少个交易信号等;_log_trade
:线上交易时会将调该接口将交易相关的日志写入文件,方便排查;如果 Config/config.yaml
打开了 debug 开关,也会将 debug 调该函数写入文件;比如下图表述的就是 1,2,3 类 bsp:
而下图则是由某简易策略计算出来的 cbsp,本框架中一般用虚线表示,如果前面有个 √
,表示最后回过头来看,这个 cbsp 是找对了;
from Chan import CChan
from ChanConfig import CChanConfig
from ChanModel.XGBModel import CXGBModel
from Common.CEnum import AUTYPE, KL_TYPE
from Config.EnvConfig import Env
from CustomBuySellPoint.CustomStrategy import CCustomStrategy
from Plot.AnimatePlotDriver import CAnimateDriver
from Plot.PlotDriver import CPlotDriver
config = CChanConfig({}) # 缠论计算配置,见后文
chan = CChan(
code="HK.00700",
begin_time="2012-01-01",
end_time=None,
data_src=DATA_SRC.FUTU, # 数据来源,
lv_list=[KL_TYPE.K_DAY], # 多级别可以从大到小传入
config=config,
autype=AUTYPE.QFQ,
extra_kl=None,
)
plot_config = {
"plot_kline": True,
"plot_kline_combine": True,
"plot_bi": True,
"plot_seg": True,
"plot_eigen": False,
"plot_zs": True,
"plot_macd": False,
"plot_mean": False,
"plot_channel": False,
"plot_bsp": True,
"plot_cbsp": True,
"plot_extrainfo": False,
} # 绘图元素开关,详见后文
plot_para = {
"seg": {
"plot_trendline": True, # 绘制趋势线
},
"bi": {
"show_num": True, # 笔是否显示序号
"disp_end": True, # 是否显示首尾价格
},
"figure": {
"width": 24,
},
"cbsp": {
"plot_cover": True, # 绘制平仓操作
},
} # 空格绘图元素详细配置,详见后文
if not config.trigger_step: # 绘制静态图
plot_driver = CPlotDriver(
chan,
plot_config=plot_config,
plot_para=plot_para,
)
else: # 绘制动画
CAnimateDriver(
chan,
plot_config=plot_config,
plot_para=plot_para,
)
需要计算缠论相关数据,仅需 CChan 调用那一行; 如果需要画图:
CPlotDriver
CAnimateDriver
核心缠论信息计算类 CChan 接受参数包括:
[KL_TYPE.K_DAY, KL_TYPE.K_60M]
,可选:
AUTYPE.QFQ
,即前复权,可选
CChanConfig
类,缠论元素计算参数配置,参见下文 CChanConfig
data_src
的数据,比如离线 data_src
只有到昨天为止的数据,今天开仓需要加上今天实时获得的部分K线数据;默认为 None;
CKLine_Unit
类;此时如果 lv_list
参数有多个级别,则会报错lv_list
参数里面的每个级别,value 是数组,每个元素是 CKLine_Unit
类如果需要部署成服务对外提供接口,调用
CChan.toJson()
可返回所有相关信息。
运行后,可通过 CChan[KL_TYPE]
的 bi_list,seg_list,bs_point_lst,cbsp_strategy 等属性获得笔,线段,bsp,cbsp 信息;
如果只有一个级别,可以省去 KL_TYPE,直接使用
CChan[0].bi_list
这种调用方法
该参数主要用于配置计算逻辑,通过字典初始化 CChanConfig
即可,支持配置参数如下:
setup_bias
根K线前的close,如果不是,下跌setup对比的是low,上升对比的是close,默认为Truecountdown_bias
根K线前的close,如果不是,下跌setup对比的是low,上升对比的是close,默认为Truekl_data_check
为 True 时生效kl_data_check
为 True 时生效model
配置时生效,默认为 Nonemodel
或者 cbsp_strategy
会被强制设置成 Truemin_zs_cnt
计算的中枢至少 3 笔(少于 3 笔是因为开启了 one_bi_zs
参数),默认为 True;max_bs2_rate
最大回测比例条件没生成),默认为 FalseCCustomStrategy
/CSegBspStrategy
CCustomStrategy
/CSegBspStrategy
,支持配置参数包括:
stock_no_active_day
根K线内一字线超过阈值则定义为不活跃;(涨跌停除外),默认为 3stock_no_active_day
根K线内股价多样性低于多少则定义为不活跃,默认为 25divergence_rate
,min_zs_cnt
,bsp1_only_multibi_zs
,max_bs2_rate
,macd_algo
,bs1_peak
,strategy_para
,bs_type
,bsp2_follow_1
,bsp3_follow_1
,bsp2s_follow_2
,strict_bsp3
,score_thred
,bsp3_peak
这几个指标可以分别对买卖点/线段买卖点各自设置:
-buy
或 -sell
,比如 min_zs_cnt-buy=2,min_zs_cnt-sell=1:对『笔』的买点/卖点生效-segbuy
或 -segsell
:对『线段』的买点/卖点生效-seg
:对『线段』的买卖点同时生效config = CChanConfig({
"zs_combine": True,
"zs_combine_mode": "zs",
"bi_strict": True,
"mean_metrics": [],
"trigger_step": False,
"skip_step": 0,
"seg_algo": "chan",
"divergence_rate": 0.9,
"min_zs_cnt": 1,
"max_bs2_rate": 0.618,
"bs1_peak": True,
"macd_algo": "peak",
"bs_type": '1,2,3a,3b,2s,1p',
"cbsp_strategy": CCustomStrategy,
"only_judge_last": False,
"strategy_para": {
"strict_open": True,
"use_qjt": True,
"short_shelling": True,
}
})
CPlotDriver 和 CAnimateDriver 参数,用于控制绘制哪些元素
其中这个参数有几种写法:
{"plot_bi": True, "plot_seg": True}
["plot_bi", "plot_seg"]
,即出现在数组中的为Tue"plot_bi,plot_seg"
{KL_TYPE.K_DAY: ${CONFIG}, KL_TYPE.K_30M: ${CONFIG}}
,其中${CONFIG}
为上面三种写法之一;另外,前缀
plot_
是可以不填的
用于具体画图细节控制,具有两级,每个配置都有二级参数,传入一个二级字典实现修改需要更改的参数;
YYYY/MM/DD
,为 '0' 表示不生效,绘制全部YYYY/MM/DD
,为 '0' 表示不生效,绘制全部bi:笔(虚线表示还没确定的笔)
seg: 线段(虚线表示还没确定的笔)
CChanConfig
中 seg_algo
设置为 chan
时有效)
CChanConfig
中 cal_cover
)boll_n
)
CChanConfig.trend_metrics
中出现,如果为 None,则为 CChanConfig.trend_metrics
的最大值default_color
rsi: RSI指标
kdj: KDJ指标
中枢算法主要有zs_algo
参数决定,有两种取值:
中枢满足:
中枢满足:
本框架可以通过机器学习方法来提高买卖点判断的准确率,在计算动力学买卖点 cbsp 时,会同时计算默认提供的数百个特征(一直持续增加中)和五种不同的标签;
可以通过运行 ModelStrategy/backtest.py
特征回测脚本,计算所有感兴趣的股票的特征和 label,并落地为本地文件;
然后开发一个类继承自 ModelStrategy/ModelGenerator.py
(详情见下文『模型开发』),训练出模型文件,并评估离线指标;
【可选】有了模型,什么阈值买点收益更高,模型对哪一类买卖点效果更好,买卖点具备什么属性更适合这个模型;我们可以将模型类注册进 ModelStrategy/parameterEvaluate/para_automl.py
的 AUTOML 框架中,通过对每种参数评估出盈亏比,交易次数,最大回撤,平均收益等诸多指标,然后自己实现一个 CalScore(eval_res)
函数,计算出该策略的分值;automl 框架会自动启发式的搜索出最优参数组合;(这个可能需要专门写一篇长文来解释。。。)
有了模型,参考下文『模型接入』实现一个类继承自 CCommModel
,并设置为配置 CChanConfig.model
,即可实现对每个 cbsp 进行打分;
【可选】如果担心接入后线上模型和离线模型特征不一致带来的差异(比如 backtest.py 中笔段的计算起始时间和实盘中的不一样,可能会导致某些特征不相等),可以运行 ModelStrategy/FeatureReconciliation.py
来进行特征一致性检查;
同时,如果为了例行更新模型,可以配置后直接运行 Script/run_train_pipeline.sh
,会自动完成以下所有流程:
Trade/Script/OpenConfig.yaml
本框架中所有的特征计算均在 ChanModel/Features.py
中实现,而特征的描述 meta 均注册在 ChanModel/FeaturesDesc.py
(运行完回测脚本 backtest.py
后运行 FeaturesDesc.py
即可获得那些特征描述还没注册);
特征最终是为了描述 cbsp 而设计的,而 cbsp 生成过程中一定对应某个 bsp,bsp 计算过程中也会产生描述 bsp 的特征,这些特征会被同步附加到 cbsp 的特征列表里面;如果要获取某级别某个特征的值,可以通过 CChan[lv].cbsp_strategy.features[feat_name]
获得;
CFeatures
通过回测脚本 backtest.py
可以生成特征文件供模型训练研究用;也可以对接到 CCommModel
类,在实盘中对 cbsp 打分;
框架默认提供 400+特征:
本框架暂时只实现了对接 futu 交易系统;原因也比较简单,接入门槛低,而且提供非常完善的 API,可以一键切换模拟盘 & 实盘;
其中交易系统包含的主要模块为:
数据库支持 sqlite 和 Mysql 两种,可以在安装时在配置文件 config.yaml 中指定;
数据库结构为(以 mysql 为例):
create table if not exists {table_name}(
id int(11) NOT NULL AUTO_INCREMENT,
add_date datetime(6), --- 计算信号时
stock_code varchar(20) NOT NULL, --- 计算信号时
stock_name varchar(64) NOT NULL, --- 计算信号时
status varchar(20) NOT NULL,
lv char(5) NOT NULL, --- 计算信号时
bstype char(10) NOT NULL, --- 计算信号时
is_buy boolean default true, --- 计算信号时
open_thred float, --- 计算信号时
sl_thred float, --- 计算信号时
target_klu_time varchar(10), --- 计算信号时
watching boolean default true, --- 默认为True, 信号不再生效时/修复错误信号时为False
unwatch_reason varchar(256), --- 计算信号时不再生效时设置
signal_last_modify datetime(6), --- 计算信号时不再生效时设置
model_version varchar(256), --- 开仓时
model_score_before float, --- 开仓时
snapshot_before varchar(256), --- 开仓时
model_score_after float, --- 分数复查时
snapshot_after varchar(256), --- 分数复查时
is_open BOOLEAN default false, --- 开仓信号突破时
open_price float, --- 成交时设置
quota int default 0, --- 提交订单时
open_date datetime(6), --- 开仓信号突破时
open_order_id varchar(32), --- 提交订单时
open_image_url varchar(64),--- 开仓信号突破时
cover_image_url varchar(64),--- 平仓时
peak_price_after_open float, --- 更新peak_price时
cover_avg_price float, -- 平仓成交时
cover_quota int default 0, --- 平仓提交订单时
cover_date datetime(6), --- 平仓提交订单时
cover_reason varchar(256), --- or 平仓提交时设置
cover_order_id varchar(256), --- 平仓提交订单时
open_err boolean default false, --- 默认为FALSE, recheck时发现错误信号设置为True
close_err boolean default false,
open_err_reason varchar(256), --- recheck时发现错误信号设置为True
close_err_reason varchar(256),
relate_cover_id int, --- 平仓信号突破时设置
is_cover_record bool default false, --- 开仓时设置
PRIMARY KEY (id)
);
字段主要描述了一个信号从产生,到开仓,最后到因为种种原因平仓的环境记录;
另外,在 db_util.py
中已经实现了 CChanDB()
,根据缠论的不同操作(开仓,平仓,止损,获取信号灯)对数据库增删改查封装了函数接口,可以在外部或者交易引擎中直接使用,而无需关注实现细节;
直接使用 db = CChanDB()
即可,无需初始化,会自动从配置文件中读取数据库类型和连接参数;
信号计算的实现在 SignalMonitor.py
中实现,按照下文『cbsp 买卖点策略』中需要实现一个类继承自 CStrategy,并且实现产生信号数据的接口 bsp_signal
,那么因为配置文件中设置了该 cbsp_strategy
,所以可以不用做修改每周期例行化运行,即可实现:
交易引擎类实现在 TradeEngine.py
中,初始化方法为:CTradeEngine(market: TrdMarket, chan_db: CChanDB)
该引擎实现了:
CTradeEngine.wait4MarketOpen()
add_trade(trade_info: CTradeInfo, price)
,传入信号和开仓价格COpenQuotaGen
实现一个仓位控制类,输入当前价格和一手多少股,返回需要开仓多少手一个典型的交易流程(即本框架 Trade/Script
下实现的)包括以下步骤:
DataAPI/OfflineDataAPI.py
而不是网络接口,可以提高线上交易处理速度)加入自己本地数据源或增加对其他网络数据源的解析,可以参考 DataAPI/*API.py
文件,默认提供了读取 akshare,baostock,etf 指数数据,futu 和本地离线文件的 demo;
方法是实现一个类,继承自 CCommonStockApi
,接受输入参数为 code, k_type, begin_date, end_date;
并在该类里面实现两个方法:
get_kl_data(self)
:该方法为一个生成器,yield 返回每一根K线信息 CKLine_Unit(idx, k_type, item_dict)
,其中 item 为:
{
DATA_FIELD.FIELD_TIME: time, # 必须是框架实现的CTime类
DATA_FIELD.FIELD_OPEN: float(_open),
DATA_FIELD.FIELD_CLOSE: float(_close),
DATA_FIELD.FIELD_LOW: float(_low),
DATA_FIELD.FIELD_HIGH: float(_high),
DATA_FIELD.FIELD_VOLUME: float(volume),
DATA_FIELD.FIELD_TURNOVER: float(amount),
DATA_FIELD.FIELD_TURNRATE: float(turn),
}
SetBasciInfo()
:用于设置股票名字和其他需要用到的信息
当使用本框架用于实盘交易时,往往需要使用实时的K线数据,本框架已经实现了 akshare,futu,sina,pytdx 等几种实时数据类;如果要实现其他实时数据接入,仅需参考 DataAPI/SnapshotAPI/
目录下相应脚本的实现即可;
方法是实现一个类,包含一个query的类方法:
code_list
: List[str],代码列表return_klu
: bool,是否返回K线类code_list
里面的各个code,各个value分别为:CKLine_Unit
类Dict[str, float]
,为是包含 name,price,low,high,open,yesterdayClose 五个 key 的字典,其中price,low,high必须有,其他选填class CCustomSnapshot:
@classmethod
def query(cls, code_list: List[str], return_klu: bool) -> Dict[str, Optional[CKLine_Unit | Dict[str, float]]]:
...
然后再在DataAPI/SnapshotAPI/StockSnapshotAPI.py
的priceQuery
注册:
def priceQuery(codelist: List[str], engine: str, return_klu: bool = False):
_class_dict = {
'sina': CSinaApi,
'futu': CFutuSnapshot,
'pytdx': CPytdxSnapshot,
'ak': CAKShareSnapshot,
}
if engine in _class_dict:
return _class_dict[engine].query(codelist, return_klu=return_klu)
else:
raise Exception(f"eigen={engine} not found")
最后在config.yaml
中配置修改snapshot_engine
信息即可;
笔模型由于比较简单,如果要增加自己的逻辑,建议在读懂代码情况下直接修改 Bi/BiList.py
和 Bi/Bi.py
即可;
后续可能会考虑将完全自由的开发类的方法开放出来;
线段的计算坊间也有多种不同的计算方法,框架提供基于特征序列,基于笔破坏,都业华课程中所谓的 1+1 终结等几个实现;
如需实现自己的算法,实现一个类,继承自 CSegListComm
,实现一个函数 update(self, bi_list: CBiList)
即可,bi_list 包含了所有已知笔的信息;
其中 CSegListComm
有两个属性:
CSegListComm.lst: List[CSeg]
存储所有计算出来的线段,必须按顺序存储;CSegListComm.config: CSegConfig
线段配置类,如果需要传入自己实现的配置参数,可通过这个类实现必须要说明一下的是,
CSegListComm
提供了大量对还没确定K线计算虚线段的通用处理函数,这不仅是整个项目里面逻辑最复杂最难的部分,也是代码最难以维护的部分。。之前这个文件代码撸了好几天,里面加了大量的检测断言,验收标准是对全量 A股港股美股 20000+股票计算不出错,当前已经完全不敢改这个文件了,但是由于已经例行运行了差不多 7 个月没出过任何错了,所以,应该问题不大。。
形态学买卖点如果需要开发自己设计的买卖点,可以参考 BuySellPoint/BSPointList.py
开发一个类,对外暴露以下方法:
def cal(self, bi_list: CBiList, seg_list: CSegListComm) -> None:
...
其中 bi_list
和 seg_list
是上游已经计算好的所有笔列表和线段列表;
bi_list
:包含所有笔的信息seg_list
:包含所有线段的信息(内部可以通过 seg_list[n].zs_lst[i]
来获取第 n 个线段的第 i 个中枢)cal
方法实现的就是将计算出来的买卖点类 CBS_Point
加入到 self.lst: List[CBS_Point]
中;
本框架支持方便地开发用户自己买卖点策略,比如一买底分型确定时买入这种;
实现方法也很简单,开发一个类继承自 CStrategy
,赋值给 CChanConfig.cbsp_strategy
即可;一旦设置,那么每新增一根K线都会调用该类的 update
函数来计算当下是否是买卖点,其中 update
函数会调用用户开发的 try_open
(开仓)和 try_close
(平仓)函数,接受的参数都是 CChan 类(包含所有级别的信息)和 lv(当前级别,chan[lv]
为当前级别信息)。
之所以需要传入 CChan 和本级别 lv,是为了可以方便实现类似区间套的策略,计算本级别买卖点时接口可以通过
chan[lv+1]
拿到次级别的所有数据
class CCustomStrategy(CStrategy):
def __init__(self, conf: CChanConfig):
super(CCustomStrategy, self).__init__(conf=conf)
@abc.abstractmethod
def try_open(self, chan: CChan, lv: int) -> Optional[CCustomBSP]:
...
@abc.abstractmethod
def try_close(self, chan: CChan, lv: int) -> None:
...
@abc.abstractmethod
def bsp_signal(self, data: CKLine_List) -> List[CSignal]:
...
需要实现的函数解释如下:
def try_open(self, chan: CChan, lv: int)
: 判断当下最后一根出现时是否是买卖时机,如果是则返回 CCustomBSP 来设置买卖点相关信息,否则返回 None
def try_close(self, chan: CChan, lv: int)
: 判断当下对之前已经开仓且未平仓的买卖点决定是否平仓,如果需要平仓,调 CCustomBSP.do_close(price: float, close_klu: CKLine_Unit, reason: str, quota=None)即可。
bsp_signal(self, chan: CChan, lv: int)
-> List[CSignal]: 如果需要上线实盘交易时需要实现,返回当前数据哪些股票可能在第二天如果满足自定义的突破条件时就会变成真正的 cbsp,算出信号后框架会自动落库,第二天实盘交易时只会跟踪产生信号的股票;
在 CStrategy
这个框架下就很容易实现区间套的策略:因为 CChan
里面包含了所有级别的数据,所以利用 chan[lv+1]
就可以得到次级别的买卖点数据,而每根次级别K线也可以通过 self.sup_kl
获得其对应的父级别 klu 变量,所以区间套策略可以用下面 20 行左右代码实现:
def try_open(self, chan: CChan, lv) -> Optional[CCustomBSP]:
data = chan[lv]
if lv != len(chan.lv_list)-1 and data.bi_list: # 当前级别不是最低级别,且至少有一笔
if qjt_bsp := self.cal_qjt_bsp(data, chan[lv + 1]): # 计算区间套
return qjt_bsp
def cal_qjt_bsp(self, data: CKLine_List, sub_lv_data: CKLine_List) -> Optional[CCustomBSP]:
last_klu = data[-1][-1]
last_bsp_lst = data.bs_point_lst.getLastestBspList()
if len(last_bsp_lst) == 0:
return None
last_bsp = last_bsp_lst[0]
if last_bsp.klu.idx != last_klu.idx: # 当前K线是父级别的买卖点
return None
for sub_bsp in sub_lv_data.cbsp_strategy: # 对于次级别的买卖点
if sub_bsp.klu.sup_kl.idx == last_klu.idx and \ # 如果是父级别K线下的次级别K线
sub_bsp.type2str().find("1") >= 0: # 且是一类买卖点
return CCustomBSP(
bsp=last_bsp,
klu=last_klu,
bs_type=last_bsp.qjt_type(), # 返回区间套买卖点
is_buy=last_bsp.is_buy,
target_klc=sub_bsp.target_klc[-1].sup_kl.klc,
price=sub_bsp.open_price,
)
return None
下图中次级别是父级别绿线部分的走势,可以看得出来次级别已经逐渐背驰了,所以在最后一个背驰点产生了父级别的买卖点:
为了方便进行不同的机器学习模型的开发和评估,本框架提供了抽象模型生成类 CModelGenerator
和抽象数据类 CDataSet
,基于这两个类分别实现响应的抽象方法,即可调用 CModelGenerator
中的 trainProcess
,PredictProcess
来实现对所有数据的训练,预测和评估;
比如 XGB 模型,CModelGenerator 继承类需要描述 xgb.Booster,CDataSet 的继承类需要实现 DMatrix 的一些读写方法,而对于深度学习,CModelGenerator 继承类需要描述网络结构,而 CDataSet 的继承类则对应于 TFRecord 的操作;
CModelGenerator 的子类需要实现以下 6 个方法:
@abc.abstractmethod
def train(self, train_set: CDataSet, test_set: CDataSet) -> None:
# 训练后将模型赋值给self.model_info.model
...
@abc.abstractmethod
def create_train_test_set(self, sample_iter) -> Tuple[CDataSet, CDataSet]:
# 自定义逻辑实现如何划分训练集和测试集,返回训练集和测试集的CDataSet
...
@abc.abstractmethod
def save_model(self):
# 实现如何将模型self.model_info.model保存到self.GetMetaPath()这个路径下
...
@abc.abstractmethod
def load_model(self) -> int:
# 加载模型,并返回所需特征维度
...
@abc.abstractmethod
def predict(self, dataSet: CDataSet) -> List[float]:
# 返回预测结果
...
@abc.abstractmethod
def create_data_set(self, feature_arr: List[List[float]]) -> CDataSet:
# 实现如何从描述N个样本M个特征的二维数组生成CDataSet
...
比如 XGB 可以实现:
class CXGBTrainModelGenerator(CModelGenerator):
def __init__(
self,
model_tag,
is_buy=None,
market=None,
bsp_type=None,
folder_thred=None,
xgb_params=None,
):
super(CXGBTrainModelGenerator, self).__init__(
model_type='xgb',
model_tag=model_tag,
is_buy=is_buy,
market=market,
bsp_type=bsp_type,
folder_thred=folder_thred
)
self.xgb_params = xgb_params
def train(self, train_set: CDataSet, test_set: CDataSet) -> None:
... # 自己实现,读取self.xgb_params
def create_train_test_set(self, sample_iter) -> tuple(CDataSet, CDataSet):
# 自己实现
...
def save_model(self):
self.model_info.model.save_model(self.GetModelPath())
def load_model(self) -> int:
bst = xgb.Booster(model_file=self.GetModelPath())
self.model_info.model = bst
return bst.num_features()
def predict(self, dataSet: CDataSet) -> List[float]:
return self.model_info.model.predict(dataSet.data)
def create_data_set(self, feature_arr: List[List[float]]) -> CDataSet:
return CDataSet(xgb.DMatrix(feature_arr))
CDataSet 的定义如下:
class CDataSet:
def __init__(self, data, tag='tmp'):
self.data = data
self.tag = tag
@abc.abstractclassmethod
def get_count(self):
...
@abc.abstractclassmethod
def get_pos_count(self):
...
@abc.abstractclassmethod
def get_label(self):
...
需要实现三个方法,分别是获得样本数量,获得正样本数量,获取 label;其中 self.tag
可以用来标识训练集,测试集;而 self.data
则是该模型所可以直接读取的数据本身;
比如 XGB 可以实现如下,其中 self.data 应该是 xgb.DMatrix
类
class CXGB_DataSet(CDataSet):
def __init__(self, data, tag='tmp'):
super(CXGB_DataSet, self).__init__(data, tag)
def get_count(self):
return len(self.get_label())
def get_pos_count(self):
return sum(self.get_label())
def get_label(self):
return self.data.get_label()
实现了上面两个类之后:
CModelGenerator.trainProcess()
即可实现对买卖类型,不同地区股票不同买卖类型,不同买卖点进行训练生成模型和评估结果;CModelGenerator.PredictProcess()
即可实现对回测特征文件全部或部分进行预测CModelGenerator.predictAllProcess()
即可用所有模型对回测特征文件进行打分,并生成所需要的分数,特征文件,可以直接对接到本框架提供的 automl 方法中;为了可以在缠论框架中读取生成的模型,并在计算过程中对 cbsp 进行实时打分,需要实现一个类继承自 CCommModel
,并设置为配置 CChanConfig.model
;
抽象类如下:
class CCommModel:
def __init__(self, path):
self.load(path)
@abc.abstractmethod
def load(self, path):
...
@abc.abstractmethod
def predict(self, cbsp: CCustomBSP) -> float:
# 取出cbsp.features,构造成模型所需要的输入,返回预测值即可
...
注意,往往加载模型并不仅仅是加载模型文件本身,模型生成时应该还有对应的 meta 文件,用于描述特征名和特征 index 的关系,这样就可以根据 cbsp.features
这个字典构造出模型文件 predict 接口所需要的输入 vector 了;
虽然 CCommModel
的输入只有 path 一个变量,但是下游实现具体方法的时候可以任意复杂,比如管理多个模型文件,cbsp.code 根据是 A股,港股,美股,指数的不同去调用不同的模型来打分;
对于一个模型,虽然训练时可以对评估集计算一些诸如 AUC 的机器学习指标,但是仍无法让我们知道诸如以下这些问题的答案:
其根本原因在于:仅仅对 cbsp 打分,是无法关注到策略的平仓行为的,对 cbsp 预测得再准,如果止损或者止盈点设置不同,也会获得不同的结局;
所以本框架首先实现了一个策略在离线数据中的评估类 eval_strategy.py
,输入所需要的策略配置(比如买卖点阈值,模型策略,对不同类别的买卖店的不同特征值进行过滤,止盈止损配置,评估股票集合,评估时间段等),即可返回评估结果,包含盈亏比,交易次数,回撤,所需成本,最大持股数,收益,各个股票获利情况等信息;
有了上述的框架类,再实现一个 Automl 的框架 para_automl.py
中实现,配置所需要的AutoML算法(提供了贝叶斯优化,PBT,暴力搜索),需要搜索的参数和范围,初始值,以及一个打分函数;
那么随着AutoML轮数的迭代,会返回一个打分函数尽可能高的参数配置;
对于 para_automl.py
的返回结果(包含所有探索过的配置和结果),可以借助 parse_automl_result.py
生成线上交易引擎可以直接使用的配置 yaml;
交易引擎在开仓时会推送股票,止损点,价格,分数之类的信息;为了方便在推送时附带上缠论绘制的图片,所以需要一个可以上传图片的地方,所以本项目实现了两个基于cos的上传接口,即项目中Plot/CosApi
下的实现;
首选需要在config.yaml
中配置上COS相关的信息(设计账号密码,桶名称之类的),自己开发使用时,可以直接调用CPlotDriver.Upload2COS(path=None)
实现自动上传并获得url;
chan = CChan(
code=code,
begin_time=begin_time,
end_time=end_time,
data_src=data_src,
lv_list=lv_list,
config=config,
autype=AUTYPE.QFQ,
extra_kl=None,
)
plot_driver = CPlotDriver(
chan,
plot_config=plot_config,
plot_para=plot_para,
)
print(plot_driver.Upload2COS())
交易引擎已内嵌实现了该调用:
如果在config.yaml
中配置了Notion相关的信息,例行脚本中会调用Script/Notion/DB_sync_Notion.py
将已开仓过的股票的操作数据同步至Notion制定的表中;并且会把开仓图片嵌入对应的页面中;
此功能源于网上大家提到的一个问题:走势为什么往往是走出来之后才恍然大悟;
所以基于此脑洞了一个功能,根据用户输入的条件(什么地区的股票,什么类型买卖点,买还是卖,背驰率等等等等),随机生成一道测试题;
使用方法如下:
from ExamGenerator import CQuestion
config = { # 即CChanConfig所使用的配置文件规则
"min_zs_cnt": 1,
"bs_type": '1,1p',
}
Q = CQuestion(area='cn', begin_time='2010-01-01', kl_type=KL_TYPE.K_DAY, _config=config) # 试题引擎
if Q.QuestionGenerator(is_buy=True): # 尝试随机获取一只股票,生成符合条件的买卖点
Q.PlotTestFigure(kl_type_lst=None) # 绘制题目,其中kl_type_lst默认为试题引擎的kl_type,也可以指定绘制多个级别
else:
print("not cbsp found")
# 给出判断后可以查看答案绘图:
Q.PlotAnswerFigure()
其原理就是几行代码实现了个如果cbsp分形完成,就输出的策略(参见CustomBuySellPoint/ExamStrategy.py
);
绘制的题目效果如下:
答案效果如下:
如果有自定义绘制开发需求,可以直接修改
ExamGenerator.py
文件,就三个函数。。。如果有人对这个感兴趣,回头可以做成一个APP/小程序/网页供大家玩耍。
除去上面所说的,还有很多细节的东西没有展开讲,比如
第一,我并不觉得缠论一定有用;之所以选择缠论,仅仅是因为觉得它最基础的笔段划分的方法非常适合代码实现拆解走势而已;
第二,我并不觉得有办法证明缠论没用(没人能证明自己使用缠论的方法是对的);
第三,【非常关键】这可能实现的不是真正的缠论,只是我自己理解下的缠论,因为我原著都没看过(如果有和真正缠论理解相悖的,我也不一定会往那边靠拢);
第四,其实这个 chan.py 已经是第三版了,正在实现第四版中。。
这个框架的起因是因为对某只股票走势研究后发现了某种规律,然后来回捡了几波钱,然后就在想,能否把所有股票里面符合这种规律的通过编程找出来(看下面的文档也可以发现其实很多优化和设计思路都是朝着对海量股票进行计算筛选方向搞的);既然需要编程,就需要某种量化手段;搜索了一番之后,发现缠论可能是找到的资料里面最接近可以编程实现那个规律的了(虽然并不完全符合);
于是乎,简单找了一下教程和课程后,就开始着手搞了;但是搞了许久发现,其实因为我囫囵吞枣的缘故,很多概念一开始并没弄懂,比如走势可以递归;还有就是一开始设计的只想计算分形,笔,线段之类的,代码难以扩展;
然后,第二版应运而生,就是重零开始从头开发;继而实现了这个框架里的大部分功能,除了缠论最基本的笔段,买卖点,自定义动力学策略,甚至到了特征,Automl,模型,并对接了富途的交易 API,上线了一个自己开发的策略后在模拟盘纯自动地跑了两个月;
V2 就是最早发上 github 的这一版,这一版有个很严重的问题,因为 V2 堆了太多功能,且不说代码 hardcode 了太多东西(比如各种文件路径啥的),可扩展性比较差,上一个新策略都要改动和调试很多文件(这也就是 V2 github 上很多文件没公开的原因);而且交易系统那里更是完全对接了自己服务器的各种环境,无法服用;
于是乎,整整花了三四个月,几乎把所有文件都重构了一遍,支持全局配置项,提供安装脚本,统一汇总管理所有特征,开放各个模块自定义能力,一键完成整个 pipeline 部署和模型训练等等等等;
整个项目回过头来看还是很有成就感的,由于个人原因,不太喜欢直接用太多外部的库,所以这个项目里面很多很基础的东西都是手动实现的,比如 MACD,布林线,趋势线,所有画图元素和逐帧动画,回测评估, automl等;
至今为止,总代码行19000+行,下图是至今为止的开发时间热力图,显示花了615个小时,但估计总体应该超过700个小时了,因为花在 jupyterlab 上画图调试的时间无法算在里面;V3的重构也差不多花了300个小时了;
总共提了差不多196个 issue,1229+次commit:
最近这两周一边上模拟盘跑(A股美股港股全部股票都是候选股票,纯自动化,下图是接入自己的推送系统显示的消息),一边优化+修bug,离最终形态大概已经完成了99%了,总体上使用体验符合预期;
不过我还有一个很想实现但是完全没法开工的远景,就是上面这些绘图的功能全部搬上web端,并且实现可交互,无奈我并不懂这些东西,时间上也不太允许;
总之文档写了很多,估计也没几个人能读的完,具体的详细讲解可以看B站的交流会视频;
10月14日开始进行了模拟盘&实盘同步运行,因为使用的是富途,所以跑的是美股和港股;
没有专门的仓位控制策略,所以采用策略如下:
class COpenQuotaGen:
@classmethod
def bench_price_func(cls, bench_price):
def f(price, lot_size) -> int:
quota = lot_size
while quota*price < bench_price:
quota += lot_size
return quota
return f
即买的手数为让开仓金额大于bench_price
的最小值,模拟盘采用的是10000,实盘是2000;(富途评估收益率的分母是提供的总模拟金额100万,所以在仓位不高的情况下收益率显示总是很低;)
全程两个月除了bug修复外,全程零工人干预;实验效果如下:
效果看起来还行,不过我并不太确定是大行情问题还是策略问题,所以还需要再观察观察;
以上!
如果你觉得这个项目对你有帮助或有启发,可以请我喝一杯。。额。。咖啡和牛奶以外的东西,毕竟我喝这两种会拉肚子。。