因子挖掘”流水线“的新架构(代码+数据)更新

星球下一步的计划: 针对ETF,股票,期货建立因子挖掘流水线。

今天的两件事情:

1、补充gplearn函数集,向world quant 101对齐,同时争取与咱们因子表达式复用。

2、发布3.1版本。

使用我们准备好的脚本,下载好螺纹钢的主连合编数据,后续咱们也会准备分钟级数据等,但对于咱们的因子挖掘没有本质的影响:

symbol = 'RB0'
df = ak.futures_main_sina(symbol="V0", start_date="19900101", end_date=datetime.now().strftime('%Y%m%d'))

df.rename(columns={'日期': 'date', '开盘价': 'open', '最高价': 'high', '最低价': 'low', '收盘价': 'close', '成交量': 'volume',
'持仓量': 'open_interest', '动态结算价': 'vwap'}, inplace=True)
print(df['date'])
df['date'] = df['date'].apply(lambda x: str(x).replace('-', ''))
from config import DATA_DIR

df.to_csv(DATA_DIR.joinpath('futures').joinpath(symbol + '.csv'), index=None)
print(df)

图片

导入gpquant的代码,实现在适合咱们因子挖掘的23个基础函数(四则运算,正余弦等)和23个时间序列函数(偏度,峰度,相关性以及常见的技术指标):

图片

function_map = {
    "square": square1,
    "sqrt": sqrt1,
    "cube": cube1,
    "cbrt": cbrt1,
    "sign": sign1,
    "neg": neg1,
    "inv": inv1,
    "abs": abs1,
    "sin": sin1,
    "cos": cos1,
    "tan": tan1,
    "log": log1,
    "sig": sig1,
    "add": add2,
    "sub": sub2,
    "mul": mul2,
    "div": div2,
    "max": max2,
    "min": min2,
    "mean": mean2,
    "clear by cond": clear_by_cond3,
    "if then else": if_then_else3,
    "if cond then else": if_cond_then_else4,
    "ts delay": ts_delay2,
    "ts delta": ts_delta2,
    "ts pct change": ts_pct_change2,
    "ts mean return": ts_mean_return2,
    "ts max": ts_max2,
    "ts min": ts_min2,
    "ts sum": ts_sum2,
    "ts product": ts_product2,
    "ts mean": ts_mean2,
    "ts std": ts_std2,
    "ts median": ts_median2,
    "ts midpoint": ts_midpoint2,
    "ts skew": ts_skew2,
    "ts kurt": ts_kurt2,
    "ts inverse cv": ts_inverse_cv2,
    "ts cov": ts_cov3,
    "ts corr": ts_corr3,
    "ts autocorr": ts_autocorr3,
    "ts maxmin": ts_maxmin2,
    "ts zscore": ts_zscore2,
    "ts regression beta": ts_regression_beta3,
    "ts linear slope": ts_linear_slope2,
    "ts linear intercept": ts_linear_intercept2,
    "ts argmax": ts_argmax2,
    "ts argmin": ts_argmin2,
    "ts argmaxmin": ts_argmaxmin2,
    "ts rank": ts_rank2,
    "ts ema": ts_ema2,
    "ts dema": ts_dema2,
    "ts kama": ts_kama4,
    "ts AROONOSC": ts_AROONOSC3,
    "ts WR": ts_WR4,
    "ts CCI": ts_CCI4,
    "ts ATR": ts_ATR4,
    "ts NATR": ts_NATR4,
    "ts ADX": ts_ADX4,
    "ts MFI": ts_MFI5,
}

另外,gplearn要用于因子挖掘,还有一个重要的扩展,就是fitness适应度(也就是我们的优化目标,可以是年化收益,夏普比等,大家下载代码后,也可以自行扩展):

fitness_map = {
    "annual return": ann_return,
    "sharpe ratio": sharpe_ratio,
    "mean absolute error": mean_absolute_error,
    "mean square error": mean_square_error,
    "direction accuracy": direction_accuracy,
}

gplearn的代码:

import numpy as np
import pandas as pd
from gplearn import fitness
# 数据集
from gplearn.genetic import SymbolicRegressor

train_data = pd.read_csv('../data/IC_train.csv', index_col=0, parse_dates=[0])
test_data = pd.read_csv('../data/IC_test.csv', index_col=0, parse_dates=[0])
feature_names = list(train_data.columns)
train_data.loc[:, 'y'] = np.log(train_data['Close'].shift(-4) / train_data['Close'])
train_data.dropna(inplace=True)

from examples.backtest import BackTester

class SymbolicTestor(BackTester):  # 回测的设定
    def init(self):
        self.params = {'factor': pd.Series}

    @BackTester.process_strategy
    def run_(self, *args, **kwargs) -> dict[str: int]:
        factor = np.array(self.params['factor'])
        long_cond = factor > 0
        short_cond = factor < 0
        self.backtest_env['signal'] = np.where(long_cond, 1, np.where(short_cond, -1, np.nan))
        self.construct_position_(keep_raw=True, max_holding_period=1200, take_profit=None, stop_loss=None)

# 回测环境(适应度函数)
comm = [0 / 10000, 0 / 10000]  # 买卖费率
bt = SymbolicTestor(train_data, transact_base='PreClose', commissions=(comm[0], comm[1]))  # 加载数据,根据Close成交,comm是买-
def score_func_basic(y, y_pred, sample_weight):  # 因子评价指标
    try:
        _ = bt.run_(factor=y_pred)
        factor_ret = _['annualized_mean']/_['max_drawdown'] if _['max_drawdown'] != 0 else 0 # 可以把max_drawdown换成annualized_std
    except:
        factor_ret = 0
    return factor_ret

def my_gplearn(function_set, my_fitness, pop_num=100, gen_num=3, tour_num=10, random_state = 42, feature_names=None):
    # pop_num, gen_num, tour_num的几个可选值:500, 5, 50; 1000, 3, 20; 1000, 15, 100
    metric = fitness.make_fitness(function=my_fitness, # function(y, y_pred, sample_weight) that returns a floating point number.
                        greater_is_better=True,  # 上述y是输入的目标y向量,y_predgenetic program中的预测值,sample_weight是样本权重向量
                        wrap=False)  # 不保存,运行的更快 # gplearn.fitness.make_fitness(function, greater_is_better, wrap=True)
    return SymbolicRegressor(population_size=pop_num,  # 每一代公式群体中的公式数量 500100
                              generations=gen_num,  # 公式进化的世代数量 103
                              metric=metric,  # 适应度指标,这里是前述定义的通过 大于0做多,小于0做空的 累积净值/最大回撤 的评判函数
                              tournament_size=tour_num,  # 在每一代公式中选中tournament的规模,对适应度最高的公式进行变异或繁殖 50
                              function_set=function_set,
                              const_range=(-1.0, 1.0),  # 公式中包含的常数范围
                              parsimony_coefficient='auto',
                              # 对较大树的惩罚,默认0.001auto则用c = Cov(l,f)/Var( l), where Cov(l,f) is the covariance between program size l and program fitness f in the population, and Var(l) is the variance of program sizes.
                              stopping_criteria=100.0,  # 是对metric的限制(此处为收益/回撤)
                              init_depth=(2, 3),  # 公式树的初始化深度,树深度最小2层,最大6                              init_method='half and half',  # 树的形状,grow生分枝整的不对称,full长出浓密
                              p_crossover=0.8,  # 交叉变异概率 0.8
                              p_subtree_mutation=0.05,  # 子树变异概率
                              p_hoist_mutation=0.05,  # hoist变异概率 0.15
                              p_point_mutation=0.05,  # 点变异概率
                              p_point_replace=0.05,  # 点变异中每个节点进行变异进化的概率
                              max_samples=1.0,  # The fraction of samples to draw from X to evaluate each program on.
                              feature_names=feature_names, warm_start=False, low_memory=False,
                              n_jobs=1,
                              verbose=1,
                              random_state=random_state)

# 函数集
function_set=['add', 'sub', 'mul', 'div', 'sqrt', 'log',  # 用于构建和进化公式使用的函数集
                    'abs', 'neg', 'inv', 'sin', 'cos', 'tan', 'max', 'min',
                    # 'if', 'gtpn', 'andpn', 'orpn', 'ltpn', 'gtp', 'andp', 'orp', 'ltp', 'gtn', 'andn', 'orn', 'ltn', 'delayy', 'delta', 'signedpower', 'decayl', 'stdd', 'rankk'
                    ] # 最后一行是自己的函数,目前不用自己函数效果更好

my_cmodel_gp = my_gplearn(function_set, score_func_basic, random_state=0,
                          feature_names=feature_names)  # 可以通过换random_state来生成不同因子
my_cmodel_gp.fit(train_data.loc[:, :'rank_num'].values, train_data.loc[:, 'y'].values)
print(my_cmodel_gp)

# 策略结果
factor = my_cmodel_gp.predict(test_data.values)
bt_test = SymbolicTestor(test_data, transact_base='PreClose', commissions=(comm[0], comm[1]))  # 加载数据,根据Close成交,comm是买-bt_test.run_(factor=factor)
md = bt_test.summary()
print(md.out_stats)
print(bt.fees_factor)
md.plot_(comm=comm, show_bool=True)

分钟线,如果不加费率,那么效果还是非常好的:年化81%,最大回撤10%。

图片

图片

代码已经发布,大家前往星球下载:【星球优惠券】AI量化实验室——量化投资的星辰大海

图片

直接运行gpquant_demo.py即可。

图片

吾日三省吾身

这几天,除了思考AI量化实验室2024年的方向,额外思考2024年的规划。

回溯了2021-2022年,基本就是定了一个数字,一个收益率。

可以说是美好的愿景,但目标无法转化为系统,意义是有限的。

Z计划=投资计划,500万,10%的长期年化收益。10%是投资能力的代表,使用基金做大类资产配置就可以“轻松”做到。这个需要拉长周期来看,而且是被动收入,建立好投资体系,等待美好自然发生即可。

更重要的是本金的积累。

对于多数普通人而言,如何建立B计划,发展另一条收入管道非常关键,你的收入结构如何优化,如何赚到更多工资以外、理财以外的收入(一开始不必刻意强调被动收入)。

因子挖掘的流水线,一定是量化投资的未来。

分解下来,

一是数据,最常用的量价数据,无论是高频还是日频,对于我们处理没什么影响。

二是挖掘流程。gplearn或者深度强化学习,这也是通过框架。

三、扩展函数集,这个是可以积累的,比如ts_rank,甚至各种技术指标。

四、因子合成:加权,线性,或者树模型,深度学习。

五、回测,止损,投资体系。

六、实盘对接

后续我们会围绕这些主题,持续完善咱们的quantlab。按星球惯例,预计明天统计更新迭代后的代码。【星球优惠券】AI量化实验室——量化投资的星辰大海

从上面的路线图来,回测系统只占其中一小部分,新手一般认为这基本就是全部。其实不是。

市场上开源的量化回测系统很多,而且你自己实现一个简单的也超级容易,星球里有好几个版本。另外,回测到实盘,或者说,所谓“无缝切换”,其实不是什么大问题,broker,获取数据的方式,本身就不一样,只是少一点代码罢了,不必为了兼容实盘而兼容。实盘系统本身比回测更简单。

我一直在考虑回测系统,希望功能要够,足够简单,这样我们才能专注在策略和因子上,而不是与框架“做斗争”。

回测系统按运行方式有三类:向量化,时间驱动,事件驱动。

一般现在市面上主流是时间驱动,就是按bar来循环。真正的事件驱动pyalgotrade显得有点多余。这个好处是与实盘的运行机制一样,vnpy早看也是,后来改成时间驱动,因为调试起来更加容易。

按如上规划:回测系统够用即可,重点策略,核心是因子。

数据自动更新之类的,也不在回测系统范围内,自行加载CSV即可。

下载期货主连合约数据

图片

import akshare as ak
from datetime import datetime
import pandas as pd
'''
0  V0 dce    PVC连续
1  P0 dce    棕榈油连续
2  B0 dce    豆二连续
3  M0 dce    豆粕连续
4  I0 dce    铁矿石连续
5  JD0    dce    鸡蛋连续
6  L0 dce    塑料连续
7  PP0    dce    聚丙烯连续
8  FB0    dce    纤维板连续
9  BB0    dce    胶合板连续
10 Y0 dce    豆油连续
11 C0 dce    玉米连续
12 A0 dce    豆一连续
13 J0 dce    焦炭连续
14 JM0    dce    焦煤连续
15 CS0    dce    淀粉连续
16 EG0    dce    乙二醇连续
17 RR0    dce    粳米连续
18 EB0    dce    苯乙烯连续
19 LH0    dce    生猪连续
20 TA0    czce   PTA连续
21 OI0    czce   菜油连续
22 RS0    czce   菜籽连续
23 RM0    czce   菜粕连续
24 ZC0    czce   动力煤连续
25 WH0    czce   强麦连续
26 JR0    czce   粳稻连续
27 SR0    czce   白糖连续
28 CF0    czce   棉花连续
29 RI0    czce   早籼稻连续
30 MA0    czce   甲醇连续
31 FG0    czce   玻璃连续
32 LR0    czce   晚籼稻连续
33 SF0    czce   硅铁连续
34 SM0    czce   锰硅连续
35 CY0    czce   棉纱连续
36 AP0    czce   苹果连续
37 CJ0    czce   红枣连续
38 UR0    czce   尿素连续
39 SA0    czce   纯碱连续
40 PF0    czce   短纤连续
41 PK0    czce   花生连续
42 FU0    shfe   燃料油连续
43 SC0    ine    上海原油连续
44 AL0    shfe   铝连续
45 RU0    shfe   天然橡胶连续
46 ZN0    shfe   沪锌连续
47 CU0    shfe   铜连续
48 AU0    shfe   黄金连续
49 RB0    shfe   螺纹钢连续
50 WR0    shfe   线材连续
51 PB0    shfe   铅连续
52 AG0    shfe   白银连续
53 BU0    shfe   沥青连续
54 HC0    shfe   热轧卷板连续
55 SN0    shfe   锡连续
56 NI0    shfe   镍连续
57 SP0    shfe   纸浆连续
58 NR0    ine    20号胶连续
59 SS0    shfe   不锈钢连续
60 LU0    ine    低硫燃料油连续
61 BC0    ine    国际铜连续
62 IF0    cffex  沪深300指数期货连续
63 TF0    cffex  5年期国债期货连续
64 IH0    cffex  上证50指数期货连续
65 IC0    cffex  中证500指数期货连续
66 TS0    cffex  2年期国债期货连续
'''
symbol = 'V0'
df = ak.futures_main_sina(symbol="V0", start_date="19900101", end_date=datetime.now().strftime('%Y%m%d'))
print(df)
df.rename(columns={'日期':'date','开盘价':'open', '最高价':'high', '最低价':'low','收盘价':'close', '成交量':'volume','持仓量':'open_interest','动态结算价':'vwap'}, inplace=True)

from config import DATA_DIR
df.to_csv(DATA_DIR.joinpath(symbol+'.csv'),index=None)

数据格式已经处理好了。

图片

新增CSVLoader:

from config import DATA_DIR_CACHE_H5, DATA_DIR
import os


class CSVDataloader(Dataloader):
    def __init__(self, path:WindowsPath, symbols, start_date='20100101', end_date=datetime.now().strftime('%Y%m%d')):
        super(CSVDataloader, self).__init__(path, symbols, start_date, end_date)

    def _load_dfs(self):
        dfs = []
        csvs = os.listdir(self.path.resolve())
        for csv in csvs:
            df = pd.read_csv(self.path.joinpath(csv).resolve(), index_col=None)
            df.set_index('date', inplace=True)

        dfs.append(df)
        return dfs

因子通过表达式计算:

图片

gplearn也是整合到这里,信号计算之后,进行回测。

代码和数据请前往星球下载:

【星球优惠券】AI量化实验室——量化投资的星辰大海

吾日三省吾身

昨天与一位大模型公司CEO吃饭,兄弟是前知名上市公司CTO,早就财富自由。现在是为了理想而战,

这是最佳的工作状态,不必畏首畏尾,纯粹为兴趣而战,享受过程中的快乐与心流状态。

当然,谁都有需要焦虑的事情。

比如,我们聊起的主题,孩子上初中了,教育分流的事情。

我们都是高考受益的一代人,对于好大学和学历是有执念的。

但孩子学习这个事情吧,未必都可以强求。

大家会说,未来的事,谁知道呢。尽管我们劝别人都说“儿孙自有儿孙福”,但时代的一粒沙,落在个人身上就是一座山。

记得20年前,刚出校园时,大家聊起北京户口。很多朋友说,户口的作用也就是孩子读书,20年后,没准不需要了呢,或者你把孩子送出国了呢?

20年过去了,似乎更重要的。能送出国的家庭寥寥。很多朋友陷入孩子一上初中,不得不将孩子送回老家上学的困境。

未来是不可预知的。

谁知道会发生什么?

不必去焦虑未来,但要充分做好准备。

比如多读书,书中有几乎一切问题的答案和解决方案。

专注自己的成长,但行好事,莫问前程。

发布者:股市刺客,转载请注明出处:https://www.95sca.cn/archives/103593
站内所有文章皆来自网络转载或读者投稿,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。敬请谅解!

(0)
股市刺客的头像股市刺客
上一篇 2024 年 7 月 29 日
下一篇 2024 年 7 月 29 日

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注