我发现从MyQuantBase
并不能直接到 MyQuantBaseStep2Signals
,主要是因为后者需要依靠一阶的斜率计算。这部分本来也可以放在MyQuantBase
做,但是ADBS又做了一次升级,所以干脆独立一个ADBS,与MyQuantBase
同级,都会向QuantData
取数。
之后还要解决基于同样数据,但是增添新的计算逻辑(并行)的结构。
全ADBS与半ADBS。全ADBS是最初的结构,从数据入Mongo,到Mongo到工作队列,以及工作队列到出Mongo这样一个完整的链条。一个项目至少会有一个全ADBS。
半ADBS是为了在兼顾稳定性的情况下,拓展逻辑处理的灵活性下的结构。半ADBS没有自己的MongoIn, 也没有入系统队列(也就没有app01)。
半ADBS又需要在监控中表示入系统的进度,所以在监控的数据方面,半ADBS需要“借用”某个全ADBS的缓存变量。
全ADBS通常无需管理通道(只用默认的1个),但是半ADBS会使用不同的通道。
在本次需求中,构造一个全ADBS就可以,相对比较省事;但是在下一次,例如要变换周期时,肯定就会涉及半ADBS。
在这个过程中,还有一个关于设计方面的想法。
本来还有一种方案,是修改全ADBS的结构,在里面允许多个工作队列。但这样就属于「用复杂方法解决复杂问题」,性价比是极低的。而半ADBS则是用简单方法/结构解决复杂问题,性价比就高得多。简单结构的特点总是看起来存在一些冗余,例如半ADBS会有自己的数据库,每加一个半ADBS就多一个库。但是简单结构的叠加(甚至删除)都是很灵活,可靠的。
MyQuantBase从QuantData取数,而新的ADBS(MyQuantBaseStep1Signals)也要从QuantData里取数,所以:
到QuantData项目下,导入init_projects.py
的依赖
执行一下语句,建立一个新的通道
cur_w.set_a_index(tier1 =project_name, tier2 =step1_mongo_out, idx_var='_ch002')
configs_base.py
init_projects_base.py
(挂载新的)这样流程就完毕了,接下来就进入Worker部分
修改核心部分的处理逻辑,并将结果进行标准化挂接
for T in T_list:
res_dict['close_T_%s_K' % T] = cal_K(recs_df2['close'])
worker_af = af
worker_Chain_session_list = Step1Chain_session_list
worker_Chain_session_dict = Step1Chain_session_dict
在配置文件部分,要记得修改数值型变量的声明
# 数值型转换(如果声明的变量缺少流程就会中断)
app01_toDoubleVarList = ['open', 'close', 'high', 'low', 'vol', 'amt','data_slot']
app03_toDoubleVarList = app01_toDoubleVarList +['close_T_600_K','close_T_2400_K']
启动一个 -d 服务,用于执行各种基础流转。
python3 sche.py
按次消耗的worker,通常用于冷启动,或者任务有堆积的时候。
python3 CNT_worker.py
有四个参数可以选,通常来说会修改 cnt_limit或者worker_name;有时会修改is_fetch_mode。
序号 | 参数 | 作用 |
---|---|---|
1 | cnt_limit | 执行次数,默认10000 |
2 | is_fetch_mode | 任务模式,默认yes,分发 |
3 | worker_name | worker名字,默认alice |
4 | group_name | 工作组,默认group1 |
因为现在数据库的代理通过负载均衡增强了(10倍),所以现在可以多启动一些worker。不过看起来瓶颈可能会出现在mongo,因为我当时只给了8g内存。不过至少目前来说是够用的,未来如果有更大规模的存储吞吐时可以扩充为局域网的mongo集群。
按带宽消耗worker,用于增强实时的处理能力
python3 Band_worker.py
对应的参数控制如下
序号 | 参数 | 作用 |
---|---|---|
1 | to_dt | 执行时间上限,默认’2099-01-01 00:00:00’ |
2 | pace | 休眠时间上限,默认为1,如果<=0,那么就不休眠 |
3 | is_fetch_mode | 同CNT_worker |
4 | worker_name | 默认alice |
5 | group_name | 默认group1 |
本次做到这里,就生成了第一步的特征提取。我发现每一层的ADBS对应着原来pandas里的每次rolling或者diff,所以这种时序的计算的确是比较麻烦的。
pandas虽然方便,但每次只能探索一个假设空间,而且不能持久和自动化。
例如,我在1月1日探索一个周期 T1觉得没啥用,但是在3月1日觉得说不定有点用,但是就很难立即验证,通常就会放弃。又或者我想知道此时此刻,某个空间下的信号是否会发出,如果要重新拉取数据,打开程序计算就很麻烦。
我想,ADBS的意义就在于将假设空间持久化,计算自动化,无论在什么时候,只要进行一次数据库查询就能获得可能需要准备数分钟乃至数十分钟的数据。
当然,也包括提供实时的信号。
如果退一步,假设没有pandas这样的分析工具,我发现其实时序上的处理还是比较麻烦的。最大的点在于逻辑的深度的确比较有挑战。
以本次要实现的信号组为例:
这里实现的只是一个简单的版本(尚且那么多层级),我觉得逻辑的复杂度已经达到一个人(大部分人)单次任务的极限了。
如果保持结构不变,那么每一层可拓展的维度还非常多,这样就可以在更多的空间上决策。当然也可以继续保持特征的深度探索和决策的深度探索,结构还可以不断的扩充,层级也会不断的变多。
其实仅仅是这样一个基础的结构,其能够发现的特征就已经超过人能够理解的范围了,暂时我觉得是够用的。
新的启动项只挂载了配置、初始化程序、取数程序以及核心的Worker处理逻辑。
docker run -d \
--name=Shard_MyQuantBaseStep1Signals_CNT_Worker_16 \
--rm \
-v /etc/localtime:/etc/localtime \
-v /etc/timezone:/etc/timezone\
-v /etc/hostname:/etc/hostname\
-e "LANG=C.UTF-8" \
-v /PATH/configs_base.py:/workspace/configs_base.py \
-v /PATH/init_projects_base.py:/workspace/init_projects_base.py \
-v /PATH/sniffer.py:/workspace/sniffer.py \
-v /PATH/sniffer01_query_quant_data.py:/workspace/sniffer01_query_quant_data.py \
-v /PATH/Step1Worker.py:/workspace/TheWorker.py \
registry.cn-hangzhou.aliyuncs.com/andy08008/apifunc_database_model1:v4 \
sh -c "python3 CNT_worker.py --cnt_limit=50000"
刚开始调试时,可能因为数值型变量没有转换等问题,会导致原始数据中存在天然的计算错误,所以可能要进行源的任务通道初始化。这样数据再次入库时会自动转成数值型。
数据重新入库后,在本系统内再次将数据的状态初始化,这样就会重新走任务队列的流。
最近的大语言模型证明了一件事:计算机硬件的发展,允许人们以更“傻”的方式来堆叠,达到更高的效果。
过去受限于计算机硬件资源,程序设计者总是想办法用更巧妙的方法来计算和存储。例如pandas里的rolling,可以将n个周期的数据一次载入,批次计算再输出。而现在我的方法则是利用更大、更快的存储加上分布式网络进行更“傻”的计算。只要设计好一次的计算,例如载入100个数,求平均,这显然比rolling要简单不知道多少。(rolling的背后涉及到并行计算,大概转成了矩阵之类的)
所以,数据的处理可以做的非常精细,只要考虑“做好一次”即可。效率的问题通过机器堆,在人的应用层面上感受不到差别(当然机器更累一些)。
这样,给到更多的硬件(这个假设看起来越来越成立),我们通过简单的结构就可以堆叠更大(并行),以及更复杂(存储多个空间)的项目与方法。