WorldQuant101因子集构建完成,重构Qlib Alpha158因子(代码+数据)

继续讲因子挖掘:目前有三个方向

一、“传统的” 以gplearn为代表的遗传算法;

二、强化学习驱动的深度学习框架;

图片

三是gpt驱动的LLM生成因子框架。

这三类咱们都写过代码,不过接下来要做的事情,需要把三者整合到一个统一的框架下,并且与Quantlab回测引擎打通。因子可以直接形成策略。

图片

表达式由token组成,比如+-,*,/这样的运算符,

还有自定义函数,比如ts_rank, ts_argmin,我们在wordquant101里实现了

很多这样的函数

强化学习每次生成一个token,如果遇到结束符号就中止。

形成一个表达式。

我们定义一系列的Token,包含开始,结束符,然后是函数集,运算符以及常数等。

from enum import IntEnum
from typing import Type


class FeatureType(IntEnum):
    OPEN = 0
    CLOSE = 1
    HIGH = 2
    LOW = 3
    VOLUME = 4
    VWAP = 5


class SequenceIndicatorType(IntEnum):
    BEG = 0
    SEP = 1


class Token:
    def __repr__(self):
        return str(self)


class ConstantToken(Token):
    def __init__(self, constant: float) -> None:
        self.constant = constant

    def __str__(self): return str(self.constant)


class DeltaTimeToken(Token):
    def __init__(self, delta_time: int) -> None:
        self.delta_time = delta_time

    def __str__(self): return str(self.delta_time)


class FeatureToken(Token):
    def __init__(self, feature: FeatureType) -> None:
        self.feature = feature

    def __str__(self): return self.feature.name.lower()


class OperatorToken(Token):
    def __init__(self, operator) -> None:
        self.operator = operator

    # 直接返回函数名(这里的operator就是函数名), 在函灵敏集的基础上,需要加上Add, Sub, Mul, Div加减乘出。
    def __str__(self): return self.operator.__name__


class UnaryOperator(OperatorToken):
    def __init__(self, operator):
        super(UnaryOperator, self).__init__(operator)
        pass
    @classmethod
    def n_args(cls) -> int: return 1


class UnaryRollingOperator(OperatorToken):
    def __init__(self, operator):
        super(UnaryRollingOperator, self).__init__(operator)

    @classmethod
    def n_args(cls) -> int: return 2


class BinaryOperator(OperatorToken):
    def __init__(self, operator):
        super(BinaryOperator, self).__init__(operator)

    @classmethod
    def n_args(cls) -> int: return 2



class BinaryRollingOperator(OperatorToken):
    def __init__(self, operator):
        super(BinaryRollingOperator, self).__init__(operator)

    @classmethod
    def n_args(cls) -> int: return 3


class DeltaTime(Token):
    def __init__(self, delta: int):
        self.delta = delta


class SequenceIndicatorToken(Token):
    def __init__(self, indicator: SequenceIndicatorType) -> None:
        self.indicator = indicator

    def __str__(self): return self.indicator.name


BEG_TOKEN = SequenceIndicatorToken(SequenceIndicatorType.BEG)
SEP_TOKEN = SequenceIndicatorToken(SequenceIndicatorType.SEP)

一棵逻辑树:(一棵“逆波兰”token构成的表达式树),对于构建有意义的表达式。

#from alphagen.data.expression import *
from typing import List

from datafeed.mining.tokens import *


class ExpressionBuilder:
    stack: List[Token]

    def __init__(self):
        self.stack = []

    def get_tree(self):
        if len(self.stack) == 1:
            return self.stack[0]
        else:
            raise InvalidExpressionException(f"Expected only one tree, got {len(self.stack)}")

    def add_token(self, token: Token):
        if not self.validate(token):
            raise InvalidExpressionException(f"Token {token} not allowed here, stack: {self.stack}.")
        if isinstance(token, OperatorToken):
            n_args: int = token.n_args()
            children = []
            for _ in range(n_args):
                children.append(self.stack.pop())
            self.stack.append(token(*reversed(children)))  # type: ignore
        elif isinstance(token, ConstantToken):
            self.stack.append(ConstantToken(token.constant))
        elif isinstance(token, DeltaTimeToken):
            self.stack.append(DeltaTime(token.delta_time))
        elif isinstance(token, FeatureToken):
            self.stack.append(FeatureToken(token.feature))
        else:
            assert False

    def is_valid(self) -> bool:
        return len(self.stack) == 1 and self.stack[0].is_featured

    def validate(self, token: Token) -> bool:
        if isinstance(token, OperatorToken):
            return self.validate_op(token)
        elif isinstance(token, DeltaTimeToken):
            return self.validate_dt()
        elif isinstance(token, ConstantToken):
            return self.validate_const()
        elif isinstance(token, FeatureToken):
            return self.validate_feature()
        else:
            assert False

    def validate_op(self, op) -> bool:
        if len(self.stack) < op.n_args():
            return False
        #print(isinstance(op, UnaryOperator))
        if isinstance(op, UnaryOperator):

            if not isinstance(self.stack[-1], FeatureToken):
                return False
        elif isinstance(op, BinaryOperator):
            if not self.stack[-1].is_featured and not self.stack[-2].is_featured:
                return False
            if (isinstance(self.stack[-1], DeltaTime) or
                    isinstance(self.stack[-2], DeltaTime)):
                return False
        elif isinstance(op, UnaryRollingOperator):
            if not isinstance(self.stack[-1], DeltaTime):
                return False
            if not self.stack[-2].is_featured:
                return False
        elif isinstance(op, BinaryRollingOperator):
            if not isinstance(self.stack[-1], DeltaTime):
                return False
            if not self.stack[-2].is_featured or not self.stack[-3].is_featured:
                return False
        else:
            assert False
        return True

    def validate_dt(self) -> bool:
        return len(self.stack) > 0 and self.stack[-1].is_featured

    def validate_const(self) -> bool:
        return len(self.stack) == 0 or self.stack[-1].is_featured

    def validate_feature(self) -> bool:
        return not (len(self.stack) >= 1 and isinstance(self.stack[-1], DeltaTime))


class InvalidExpressionException(ValueError):
    pass


if __name__ == '__main__':
    from datafeed.expr_functions import *
    tokens = [
        FeatureToken(FeatureType.LOW),
        UnaryOperator(sign),
        DeltaTimeToken(-10),
        #OperatorToken(Ref),
        FeatureToken(FeatureType.HIGH),
        FeatureToken(FeatureType.CLOSE),
        OperatorToken(Div),
        OperatorToken(Add),
    ]

    builder = ExpressionBuilder()
    for token in tokens:
        print(token)
        builder.add_token(token)

    print(f'res: {str(builder.get_tree())}')
    print(f'ref: Add(Ref(Abs($low),-10),Div($high,$close))')

代码在如下位置:

图片

这个表达式树就可以应用于强化学习生成一个个token,然后形成一个有效的表达式,计算ic值等

AI量化实验室——2024量化投资的星辰大海

大模型落地场景之AI量化投资

大横型如火如荼,与AI量化的关系也非常紧密,除了咱们之前聊天AlphaGPT之外,Quantlab3.8源码发布:整合AlphaGPT大模型自动因子挖掘以及zvt股票数据框架,这种智能问答在量化投研上应用也非常广。

AI智能投顾,第一性原理与kensho,这个被高盛和标普500收购的智能引擎,在现在大模型时代看来,就是个小儿科了。

我在星球同步了一篇最新的论文(后续会提供代码)

FinReport: 结合新闻语义信息的多因子模型显著提升预测准确性。

图片

大模型相当于“大脑”。

很多时候,并不需要再训练一个“金融大脑”。因为它的知识储备已经非常充充分。我们需要的是场景,流程分解,prompts指令,提出精确的问题。

 

吾日三省吾身 之  “一人企业”与“超级个体”

十年前,我们聊“大众创业”,现在更多人聊“超级个体”。

现在大家都劝人吃饱饭,别创业,以稳为主。把炒股和创业列为高风险行当。

其实,都是脸谱化的误解。

从本质上看,超级个体与创业的逻辑一致。

创业并不是大家理解中的“All in”。大家对创业的刻板印象是,自己辞职,找钱,找人,租一个办公室,然后开会,讨论战略,开发产品,提供服务,建立商业模式。

这些只是形式罢了。

创业的本质就是给市场提供一种更多的产品或服务。以什么样的方式来组织生产,公司只是一种形式。

而超级个体是在当前技术,市场条件下,把自己“活成”一家公司。

你是自己的CEO,有战略部;你又是研发,有研发部;有市场部,运营部。

你说精力上顾不过来怎么办?

你拆解一下:一种是基础设施,已经不需要自己构建了。比如云服务,大模型,支付服务,流量平台,方便接入且成本不高。二是一些开发产品必须的技能,比如工程师——这种硬技能没有办法,如果你恰好是栈工程师,这就再好不过了,这种可以外包给另外的超级个体——以松散的方式组织生产。

完成比完美更加重要,方向比努力重要。

选择做对的事,有限的的事,专注而持续,先完成,形成反馈闭环。

简言之:作为超级个体,你专注你的专长,把以前需要以公司化来组织的事情,交给其的组织或超级个体。

这会带来什么来的好处呢?

灵活。——“一人企业”,就是以成本小到无法倒闭的方式来组织生产,创造价值。——本质还是“创业”。

不需要风投,不需要各种沟通,汇报,会议,管理。

前两天的文章,我们开始构建WorldQuant101因子集:

WorldQuant101因子集整合Quantlab表达式引擎

在Quantlab里复现WorldQuant101因子表达式引擎(代码+数据)

WorldQuant101因子库

有前两天的函数积累,我们直接添加因子即可:

大家仔细看会发现,WorldQuant101的因子,思路上“雷同”的居多。

就是价量关系,排序后取相关性,也就是“价量背离”。

names.append('alpha012')
features.append('(sign(delta(volume, 1)) * (-1 * delta(close, 1)))')

names.append('alpha013')
features.append('(-1 * rank(covariance(rank(close), rank(volume), 5))) ')

names.append('alpha014')
features.append('((-1 * rank(delta(returns, 3))) * correlation(open, volume, 10))')

names.append('alpha015')
features.append('(-1 * sum(rank(correlation(rank(high), rank(volume), 3)), 3))')

比如,29和30号因子,这么长:

Alpha#29: (min(product(rank(rank(scale(log(sum(ts_min(rank(rank((-1 * rank(delta((close - 1), 5))))), 2), 1))))), 1), 5) + ts_rank(delay((-1 * returns), 6), 5)) 
Alpha#30: (((1.0 - rank(((sign((close - delay(close, 1))) + sign((delay(close, 1) - delay(close, 2)))) + sign((delay(close, 2) - delay(close, 3)))))) * sum(volume, 5)) / sum(volume, 20))

咱们的表达式引擎计算起来也毫无压力。

图片

第31号因子,涉及到一个新函数 decay_linear,我们需要实现即可:

图片

Alpha#31: ((rank(rank(rank(decay_linear((-1 * rank(rank(delta(close, 10)))), 10)))) + rank((-1 * delta(close, 3)))) + sign(scale(correlation(adv20, low, 12))))

把WorldQuant 101刷了一轮,补充了所有的运算函数

from datafeed.expr_functions import *


class AlphaBase:
    pass


# https://www.joinquant.com/data/dict/alpha101
class WorldQuant101(AlphaBase):
    def get_names_features(self):
        names = []
        features = []

        # names.append('alpha001')
        # features.append('(rank(ts_argmax(signed_power((stddev(returns, 20) if (returns < 0) else close), 2.), '
        #                '5)) - 0.5)')

        names.append('alpha002')
        features.append('(-1 * correlation(rank(delta(log(volume), 2)), rank(((close - open) / open)), 6))')

        names.append('alpha003')
        features.append('(-1 * correlation(rank(open), rank(volume), 10))')

        names.append('alpha004')
        features.append('(-1 * ts_rank(rank(low), 9))')

        '''
        Alpha#5: (rank((open - (sum(vwap, 10) / 10))) * (-1 * abs(rank((close - vwap))))) 
        Alpha#6: (-1 * correlation(open, volume, 10)) 
        '''

        names.append('alpha006')
        features.append('(-1 * correlation(open, volume, 10))')

        '''
        Alpha#7: ((adv20 < volume) ? ((-1 * ts_rank(abs(delta(close, 7)), 60)) * sign(delta(close, 7))) : (-1 * 1)) 
Alpha#8: (-1 * rank(((sum(open, 5) * sum(returns, 5)) - delay((sum(open, 5) * sum(returns, 5)), 10)))) 
        '''

        names.append('alpha008')
        features.append(
            '(-1 * rank(((sum(open, 5) * sum(returns, 5)) - delay((sum(open, 5) * sum(returns, 5)), 10))))')

        '''
        Alpha#11: ((rank(ts_max((vwap - close), 3)) + rank(ts_min((vwap - close), 3))) * rank(delta(volume, 3))) 
Alpha#12: (sign(delta(volume, 1)) * (-1 * delta(close, 1))) 
Alpha#13: (-1 * rank(covariance(rank(close), rank(volume), 5))) 
Alpha#14: ((-1 * rank(delta(returns, 3))) * correlation(open, volume, 10)) 
Alpha#15: (-1 * sum(rank(correlation(rank(high), rank(volume), 3)), 3)) 
        '''
        names.append('alpha012')
        features.append('(sign(delta(volume, 1)) * (-1 * delta(close, 1)))')

        names.append('alpha013')
        features.append('(-1 * rank(covariance(rank(close), rank(volume), 5))) ')

        names.append('alpha014')
        features.append('((-1 * rank(delta(returns, 3))) * correlation(open, volume, 10))')

        names.append('alpha015')
        features.append('(-1 * sum(rank(correlation(rank(high), rank(volume), 3)), 3))')

        '''
        Alpha#18: (-1 * rank(((stddev(abs((close - open)), 5) + (close - open)) + correlation(close, open, 10)))) 
Alpha#19: ((-1 * sign(((close - delay(close, 7)) + delta(close, 7)))) * (1 + rank((1 + sum(returns, 250))))) 
Alpha#20: (((-1 * rank((open - delay(high, 1)))) * rank((open - delay(close, 1)))) * rank((open - delay(low, 1)))) 
        '''
        names.append('alpha018')
        features.append('(-1 * rank(((stddev(abs((close - open)), 5) + (close - open)) + correlation(close, open, 10))))')

        names.append('alpha019')
        features.append(
            '((-1 * sign(((close - delay(close, 7)) + delta(close, 7)))) * (1 + rank((1 + sum(returns, 250)))))')

        names.append('alpha020')
        features.append(
            '(((-1 * rank((open - delay(high, 1)))) * rank((open - delay(close, 1)))) * rank((open - delay(low, 1))))')

        '''
        Alpha#22: (-1 * (delta(correlation(high, volume, 5), 5) * rank(stddev(close, 20))))
        Alpha#26: (-1 * ts_max(correlation(ts_rank(volume, 5), ts_rank(high, 5), 5), 3)) 
        Alpha#28: scale(((correlation(adv20, low, 5) + ((high + low) / 2)) - close)) 
        
        Alpha#29: (min(product(rank(rank(scale(log(sum(ts_min(rank(rank((-1 * rank(delta((close - 1), 5))))), 2), 1))))), 1), 5) + ts_rank(delay((-1 * returns), 6), 5)) 
Alpha#30: (((1.0 - rank(((sign((close - delay(close, 1))) + sign((delay(close, 1) - delay(close, 2)))) + sign((delay(close, 2) - delay(close, 3)))))) * sum(volume, 5)) / sum(volume, 20)) 

        '''
        names.append('alpha022')
        features.append(
            '(-1 * (delta(correlation(high, volume, 5), 5) * rank(stddev(close, 20))))')

        names.append('alpha026')
        features.append(
            '(-1 * ts_max(correlation(ts_rank(volume, 5), ts_rank(high, 5), 5), 3))')

        #names.append('alpha028')
        #features.append(
        #    'scale(((correlation(adv20, low, 5) + ((high + low) / 2)) - close))')

        names.append('alpha029')
        features.append(
            '(min(product(rank(rank(scale(log(sum(ts_min(rank(rank((-1 * rank(delta((close - 1), 5))))), 2), 1))))), 1), 5) + ts_rank(delay((-1 * returns), 6), 5))')

        names.append('alpha030')
        features.append('(((1.0 - rank(((sign((close - delay(close, 1))) + sign((delay(close, 1) - delay(close, 2)))) + sign((delay(close, 2) - delay(close, 3)))))) * sum(volume, 5)) / sum(volume, 20))')

        '''
        Alpha#31: ((rank(rank(rank(decay_linear((-1 * rank(rank(delta(close, 10)))), 10)))) + rank((-1 * delta(close, 3)))) + sign(scale(correlation(adv20, low, 12)))) 
Alpha#32: (scale(((sum(close, 7) / 7) - close)) + (20 * scale(correlation(vwap, delay(close, 5), 230)))) 
Alpha#33: rank((-1 * ((1 - (open / close))^1))) 
Alpha#34: rank(((1 - rank((stddev(returns, 2) / stddev(returns, 5)))) + (1 - rank(delta(close, 1))))) 
Alpha#35: ((Ts_Rank(volume, 32) * (1 - Ts_Rank(((close + high) - low), 16))) * (1 - Ts_Rank(returns, 32))) 
        '''

        names.append('alpha034')
        features.append(
            'rank(((1 - rank((stddev(returns, 2) / stddev(returns, 5)))) + (1 - rank(delta(close, 1)))))')

        names.append('alpha035')
        features.append(
            '((ts_rank(volume, 32) * (1 - ts_rank(((close + high) - low), 16))) * (1 - ts_rank(returns, 32)))')

        '''
        Alpha#36: (((((2.21 * rank(correlation((close - open), delay(volume, 1), 15))) + (0.7 * rank((open - close)))) + (0.73 * rank(Ts_Rank(delay((-1 * returns), 6), 5)))) + rank(abs(correlation(vwap, adv20, 6)))) + (0.6 * rank((((sum(close, 200) / 200) - open) * (close - open))))) 
Alpha#37: (rank(correlation(delay((open - close), 1), close, 200)) + rank((open - close))) 
Alpha#38: ((-1 * rank(Ts_Rank(close, 10))) * rank((close / open))) 
Alpha#39: ((-1 * rank((delta(close, 7) * (1 - rank(decay_linear((volume / adv20), 9)))))) * (1 + rank(sum(returns, 250)))) 
Alpha#40: ((-1 * rank(stddev(high, 10))) * correlation(high, volume, 10)) 
        '''

        names.append('alpha037')
        features.append(
            '(rank(correlation(delay((open - close), 1), close, 200)) + rank((open - close)))')

        names.append('alpha038')
        features.append(
            '((-1 * rank(ts_rank(close, 10))) * rank((close / open)))')

        names.append('alpha040')
        features.append(
            '((-1 * rank(stddev(high, 10))) * correlation(high, volume, 10))')

        '''
        Alpha#44: (-1 * correlation(high, rank(volume), 5)) 
Alpha#45: (-1 * ((rank((sum(delay(close, 5), 20) / 20)) * correlation(close, volume, 2)) * rank(correlation(sum(close, 5), sum(close, 20), 2)))) 
        '''

        names.append('alpha044')
        features.append(
            '(-1 * correlation(high, rank(volume), 5))')

        names.append('alpha045')
        features.append(
            '(-1 * ((rank((sum(delay(close, 5), 20) / 20)) * correlation(close, volume, 2)) * rank(correlation(sum(close, 5), sum(close, 20), 2))))')

        '''
        Alpha#52: ((((-1 * ts_min(low, 5)) + delay(ts_min(low, 5), 5)) * rank(((sum(returns, 240) - sum(returns, 20)) / 220))) * ts_rank(volume, 5)) 
Alpha#53: (-1 * delta((((close - low) - (high - close)) / (close - low)), 9)) 
Alpha#54: ((-1 * ((low - close) * (open^5))) / ((low - high) * (close^5))) 
Alpha#55: (-1 * correlation(rank(((close - ts_min(low, 12)) / (ts_max(high, 12) - ts_min(low, 12)))), rank(volume), 6)) 
Alpha#60: (0 - (1 * ((2 * scale(rank(((((close - low) - (high - close)) / (high - low)) * volume)))) - scale(rank(ts_argmax(close, 10)))))) 
        '''

        names.append('alpha052')
        features.append(
            '((((-1 * ts_min(low, 5)) + delay(ts_min(low, 5), 5)) * rank(((sum(returns, 240) - sum(returns, 20)) / 220))) * ts_rank(volume, 5))')

        names.append('alpha053')
        features.append(
            '(-1 * delta((((close - low) - (high - close)) / (close - low)), 9))')

        names.append('alpha055')
        features.append(
            '(-1 * correlation(rank(((close - ts_min(low, 12)) / (ts_max(high, 12) - ts_min(low, 12)))), rank(volume), 6))')

        names.append('alpha060')
        features.append('(0 - (1 * ((2 * scale(rank(((((close - low) - (high - close)) / (high - low)) * volume)))) - scale(rank(ts_argmax(close, 10))))))')

        names.append('alpha101')
        features.append('((close - open) / ((high - low) + .001))')
        return names, features

几点收获:

一、积累运算函数库,后续我们机器挖掘可以复用,给gplearn和强化学习。

二、因子构造的思路,其实这个我之前让GPT做过:

Quantlab3.9代码:内置大模型LLM因子挖掘,全A股数据源以及自带GUI界面

今天文章涉及的代码在如下位置:

图片

量化数据中心建设

昨天有同学问数据的事情,咱们公众号对应的文章,使用到的数据,都会随代码一起打包在星球发布。

但为了更方便使用,咱们可以考虑建设一个会自动更新的数据中台。

这也印证了咱们是想把AI量化当作事业持续做下去的。

大模型LLM

最近大模型领域,重磅消息是Lalla3开源。

目前看来,llama3-8B的版本,很多小团队有几张卡就可以玩,这是个令人振奋的消息。

可以做的事情有三件: 增量预训练(比如加入更多的中文数据集或者领域数据集)_如果只是小数据集,在增量预训练中如何起作用指令微调(让它更加能听懂人话),SFT(监督微调)

需要自己构建数据集,指令集,领域数据集等,同时还需要有评测手段,以评估训练的结果

另外,就是基于大模型的api,使用指令(prompts)与大模型交互。这个优点是自己要做的事情比较少,调API就好了,缺点是模型不符合预期时,没法干预,还是token还是比较贵的。

 

吾日三省吾身

最近老看到一些职场里,竞业限制引发的争端,甚至很多人赔了不少钱。

以前这种事情可能只会发生于大公司,高管圈之中。而且多数这些钱对他们来讲,小case。更多是为了专利保护,技术保密之类的诉求。

现在似乎情况发生了变化。

有些人就是普通的开发,运营。

以前的互联网,人员流动比较快。甚至很多公司在做类似项目时,就高价到对方公司挖人。

以前的互联网,开除人也比较快。现在随着新劳动法的出台,基本上没有N+1是结束不了。

企业无法高效激发活力,这是一种负担。人才不能有效流动。这也是一种负担。

不过从成熟的,契约精神的角度,平衡工作与生活的角度,也许未必是错的。

毕竟,工作都是为了更好地生活嘛。

历史文章:

WorldQuant101因子集整合Quantlab表达式引擎

AI量化实验室——2024量化投资的星辰大海

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

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

相关推荐

发表回复

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