稳稳的年化10%,多任务时序动量策略——基于pytorch的深度学习策略(附python代码)

news2024/10/1 1:29:05

原创文章第608篇,专注“AI量化投资、世界运行的规律、个人成长与财富自由"。

做因子挖掘这段时间,有一个观感。

传统的因子挖掘,尤其是手工构造因子,到遗传算法因子挖掘。——本身也是一种”拟合“,或者说试图”解释“过往的收益率,有一种符号表达的方式。

传统机器学习,我们也是试图这么做的,有不少工程上的tricks。

但在深度学习时代,最大的一点进步就是不需要特征工程,因为特征工程本身是对现实数据的简化。深度卷积神经网络读图片,它是读入像素级数据,然后自己建模。

之前星球有同学提问说——为什么不能直接端到端建模

这其实是一个好问题。

图像识别就是端到端,AlphaGo就是端到端,深度强化学习端到端构建投资组合——从逻辑上更符合金融投资的场景——它甚至不需要label。

通过深度强化学习构建、筛选因子,然后再用深度学习来组合因子,这里确实会损失很多信息。

IC筛选出来的因子,在机器学习里组合效果并不一定好。

一个原因可能是ic是线性信息,而机器学习可以拟合高维非线性的信息。

所以,现在手工构建的因子,多数用于加权合成,而非机器学习;那么反过来,机器学习所需的特征,通过IC值来筛选还靠谱吗?

import warnings

# from collections import defaultdict
from typing import Dict, List, Tuple

import pandas as pd
import torch
from torch.nn.utils import clip_grad_norm_

# from torch.utils.data import DataLoader
from tqdm import tqdm

from .data_processor import DataProcessor
from .general import corrcoef_loss, get_strategy_returns, share_loss
from .module import Multi_Task_Model  # CustomDataset
from .utils import all_nan, plot_pred_nan_num


class MTL_TSMOM:
    def __init__(
        self,
        dataset: DataProcessor,
        input_size: int,
        lstm_hidden_size: int,
        mlp_hidden_size: int,
        lstm_layers: int,
        mlp_layers: int,
        optimizer_name: str,
        transcation_cost: float,
        target_vol: float,
        lstm_dropout: float,
        mlp_dropout: float,
        max_grad_norm: float,
        # batch_size: int,
        num_epochs: int,
        opt_kwargs: Dict = None,
        early_stopping: int = 50,
        log_step: int = 100,
        verbose: bool = False,
        save_path: str = None,
    ) -> None:
        self.epoch_loss = []  # 储存每一次的损失
        # self.all_loss = defaultdict(list)  # 储存每一次的损失 1-train 2-valid
        self.dataset = dataset
        self.transcation_cost = transcation_cost
        self.target_vol = target_vol
        self.max_grad_norm = max_grad_norm
        # self.batch_size = batch_size
        self.num_epochs = num_epochs
        self.early_stopping = early_stopping
        self.log_step = log_step
        self.verbose = verbose
        self.save_path = save_path

        # 初始化模型
        self.model = Multi_Task_Model(
            input_size,
            lstm_hidden_size,
            mlp_hidden_size,
            lstm_layers,
            mlp_layers,
            lstm_dropout,
            mlp_dropout,
        ).cuda()

        if opt_kwargs is None:
            opt_kwargs = {}

        self.optimizer = getattr(torch.optim, optimizer_name)(
            self.model.parameters(), **opt_kwargs
        )

    def log(self, arg, verbose=True) -> None:
        if verbose:
            print(arg)

    def train_model(self, train_datase: List, gloabal_step: int = None) -> float:
        self.model.train()
        # train_dataset = CustomDataset(train_datase)
        # train_loader = DataLoader(
        #     train_dataset, batch_size=self.batch_size, shuffle=False
        # )
        features, next_returns, forward_vol = train_datase
        total_loss = 0.0
        # loss = 0.0
        # for batch, (features, next_returns, forward_vol) in enumerate(train_loader):
        pred_sigma, weight = self.model(features)
        auxiliary_loss: float = corrcoef_loss(pred_sigma, forward_vol)
        main_loss: float = share_loss(
            weight, next_returns, self.target_vol, self.transcation_cost
        )
        total_loss = (auxiliary_loss + main_loss) * 0.5
        self.optimizer.zero_grad()
        total_loss.backward()
        # 为了防止梯度爆炸,我们对梯度进行裁剪
        if self.max_grad_norm is not None:
            clip_grad_norm_(self.model.parameters(), self.max_grad_norm)

        self.optimizer.step()
        # if gloabal_step is not None:
        #     self.all_loss[gloabal_step].append(
        #         (1, batch, auxiliary_loss, main_loss, total_loss)
        #     )
        # loss += total_loss
        return total_loss  # loss / len(train_loader)

    def validation_model(
        self, validation_dataset: List, gloabal_step: int = None
    ) -> float:
        # valid_dataset = CustomDataset(validation_dataset)
        # valid_loader = DataLoader(
        #     valid_dataset, batch_size=self.batch_size, shuffle=False
        # )
        total_loss = 0.0

        # loss = 0.0
        self.model.eval()
        features, next_returns, forward_vol = validation_dataset
        with torch.no_grad():
            # for batch, (features, next_returns, forward_vol) in enumerate(valid_loader):
            pred_sigma, weight = self.model(features)

            auxiliary_loss = corrcoef_loss(pred_sigma, forward_vol)
            main_loss = share_loss(
                weight, next_returns, self.target_vol, self.transcation_cost
            )

            total_loss = (auxiliary_loss + main_loss) * 0.5
            # loss += total_loss
            # if gloabal_step is not None:
            #     self.all_loss[gloabal_step].append(
            #         (2, batch, auxiliary_loss, main_loss, total_loss)
            #     )
        return total_loss  # loss / len(valid_loader)

    def predict_data(self, test_part: List) -> Tuple[torch.Tensor, torch.Tensor]:
        features, next_returns, _ = test_part
        with torch.no_grad():
            _, weight = self.model(features)
        return weight, next_returns

    def loop(
        self, train_part: List, valid_part: List, global_step: int = None
    ) -> float:
        best_valid_loss: float = float("inf")  # 用于记录最好的验证集损失
        epochs_without_improvement: int = 0  # 用于记录连续验证集损失没有改善的轮数
        for epoch in range(self.num_epochs):
            train_loss: float = self.train_model(train_part)
            valid_loss: float = self.validation_model(valid_part)

            if (self.log_step is not None) and (epoch % self.log_step == 0):
                self.log(
                    f"Epoch {epoch or epoch+1}, Train Loss: {train_loss:.4f}, Valid Loss: {valid_loss:.4f}",
                    self.verbose,
                )

            # 判断是否有性能提升,如果没有则计数器加 1
            # NOTE:这样是最小化适用的,如果是最大化,需要改成 valid_loss > best_valid_loss
            if valid_loss < best_valid_loss:
                best_valid_loss = valid_loss
                epochs_without_improvement: int = 0
            else:
                epochs_without_improvement += 1

            # 保存每一次的损失
            self.epoch_loss.append((global_step, train_loss, valid_loss))
            # 判断是否满足 early stopping 条件
            if (self.early_stopping is not None) and (
                epochs_without_improvement >= self.early_stopping
            ):
                self.log(f"Early stopping at epoch {epoch + 1}...", self.verbose)
                break

        return valid_loss

    def fit(self):
        ls: List = [] # 储存每一次的权重和收益
        size: int = len(self.dataset.train_dataset)
        for i, (train_part, valid_part, test_part) in enumerate(
            tqdm(
                zip(
                    self.dataset.train_dataset,
                    self.dataset.valid_dataset,
                    self.dataset.test_dataset,
                ),
                total=size,
                desc="train",
            )
        ):
            self.loop(train_part, valid_part, i)
            weight, next_returns = self.predict_data(test_part)
            ls.append((weight, next_returns))
            if all_nan(weight):
                warnings.warn(f"下标{i}次时:All nan in weight,已经跳过")
                # raise ValueError(f"下标{i}次时:All nan in weight")
                break

        weights_tensor: torch.Tensor = torch.cat([t[0] for t in ls], dim=0)
        returns_tensor: torch.Tensor = torch.cat([t[1] for t in ls], dim=0)

        self.weight = weights_tensor
        self.next_returns = returns_tensor
        if self.save_path is not None:
            torch.save(self.model.state_dict(), self.save_path)
        # return weights_tensor, returns_tensor

    def get_backtest_returns(self) -> pd.DataFrame:
        try:
            self.weight
        except NameError as e:
            raise NameError("请先调用fit方法") from e
        strategy_frame: pd.DataFrame = get_strategy_returns(
            self.weight, self.next_returns, self.dataset.test_idx
        )
        return strategy_frame

    def get_loss_score(self) -> pd.DataFrame:
        if self.epoch_loss == []:
            raise ValueError("请先调用fit方法")
        return pd.DataFrame(
            [(j.item(), k.item()) for _, j, k in self.epoch_loss],
            columns=["train", "valid"],
        )

    def plot_pred_nan_num(self):
        try:
            self.weight
        except NameError as e:
            raise NameError("请先调用fit方法") from e
        return plot_pred_nan_num(self.weight)


昨天在星球里发布的论文,以上是核心代码。

图片

通过时序动量和波动率对投资组合目标波动率建模。

图片

图片

图片

代码下载:

图片

吾日三省吾身

01

财富自由小目标——七年赚到500万实现财富自由,这是我的计划,也适合大多数普通人——这是我三年前写的文章了,这个时间点的认知,基本已经成型。财富自由的三个层次,三条路径吧。

第二层次相信并持续践行中。

努力开展第三层次。——做生产者,创造有价值的东西,走财富自由快车道。

更新了一下小目标: 按进度5年的阶段小目标,如果你有勇气把目标提升至10倍,那么5年内就可以实现大目标。

图片

2000个W——普通人基本可以退休且无后顾之忧了。

怎么做呢?投资、创业、技能和知识付费。。

我问kimi怎么做,它的回答:

普通人在5年内赚取2000万是一个具有挑战性的目标,但并非不可能。以下是一些可能的途径和策略,但请注意,这些方法都涉及不同程度的风险,并且成功并不是保证的。

02 

“对宏观保持耐心,对微观保持效率”。

今天读到这句话挺受启发。

多数人对宏观缺乏耐心,无论是投资还是经营自己的人生。

其实就是“但行好事,莫问前程”,又同长期主义,延迟满足相关联。

好的事情发生,需要一点时间,有时候来得比你想象中要更久。我们可以努力的时间,只是一点一滴的当下。

“种一棵树最好的时间是十年前,其次是现在“。

03

吐槽两句——有一种讲量化的书,竟然只讲一堆理论、公式,数学推导。

然后竟然没有一行代码。

金融是一个偏实战的行业,它与物理、数学这种严格的科学不同。

好比马可维茨获得诺奖的MVO,并不能用于投资一样,因为参数敏感度太高,收益率无法预估且不稳定等因素。

理论当然重要,但金融的艺术性决定理论与实战会有出入。

所以,作为量化的书,不结合实战,连数据分析都不做,就光讲理论,洋洋洒洒这么厚的一本书,实在是。

之后我若是写书,一定会规避这种风格。要么不写,要么大家一定会拿到可以直接跑的代码。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1979764.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

6 定时器

6 定时器 1、基本概念2、使用库函数实现 1、基本概念 定时器的基本工作框架 STM32F103共支持8个定时器 假如分频器PSC的值0,则PSC分频系数1,表示输入1个时钟信号,CNT计数器加1,1S加1&#xff0c;当CNT计数器的值3&#xff0c;跟ARR自动重载寄存器的值21相等时&#xff0c;就…

Linux基于设备树的字符设备驱动框架

掌握设备树是 Linux 驱动开发人员必备的技能&#xff01;因为在新版本的 Linux 中&#xff0c;ARM 相关的驱动全部采用了设备树(也有支持老式驱动的&#xff0c;比较少)&#xff0c;最新出的 CPU 其驱动开发也基本都是基于设备树的&#xff0c;比如 ST 新出的 STM32MP157、NXP的…

【React】详解 index.js 文件

文章目录 一、index.js文件的基本结构1. 引入必要的模块2. 渲染根组件3. 注册服务工作者&#xff08;可选&#xff09; 二、index.js文件的详细解析1. ReactDOM.render的作用2. 为什么使用React.StrictMode3. 服务工作者的注册 三、index.js文件的最佳实践1. 使用模块化引入2. …

缓存常见问题总结

目录 一&#xff1a;缓存穿透 解决方案 1.对请求增加校验机制 2.缓存空值或者特殊值 3.使用布隆过滤器 布隆过滤器原理图​编辑 哈希函数的基本特性 布隆过滤器为什么会存在误判 如何降低误判率 如何使用布隆过滤器 布隆过滤器的应用场景 如何使用布隆过滤器 第一…

QT 建立tcp服务端 TcpServer TcpSocket

基于正点原子教程&#xff0c;个人改编一点点(先写着&#xff0c;还没学客户端来验证) QTcpServer 服务端&#xff0c;下控制多个socket QTcpSocket 可以理解为一个TCP连接 使用客户端的流程为 1.包含network和include 2.声明QTcpServer信号 整个流程都要使用QTcpServer对象&a…

搭建pxe网络安装环境实现服务器自动部署

1.首先配置自动化脚本工具 根据自己的主机设置自己的IP 这张图选择红框下面的选则剩余空间 红帽7的初始网卡为ens33&#xff0c;所以部署后新机器的网卡为ens33 根据自己所要部署的版本编写合适的脚本 使用vim.ks.cfg进入脚本编写 2.配置DHCP 使用vim编写/etc/dhcp/dhcpd.conf …

深入研究Java的String常量池

文章目录 一、StringTable分析一段代码示例一示例二示例三 二、 intern1、StringTable位置2、StringTable 性能调优3、intern深入分析3.1 思考3.2 JDK6中的解释3.3 JDK7中的解释3.4 详细分析3.5 intern正确使用的例子3.6 intern使用不当的例子 一、StringTable 常量池中的字符…

清华计算几何-ElementUniqueness, MinMap, MaxGap

ElementUniqueness问题(EU) 给出一组数给出一组数据,, 判断每个数都是唯一性的或者说判断是否存在重复的. 算法思路很简单, 快速排序 遍历判断: Max(O(nlogn) O(n)) O(nlogn)算法复杂度 代码实现 bool IsEelementUniqueness(const vector<float>& Elemnts) {vect…

【数学建模】 机器学习与统计模型

文章目录 机器学习与统计模型1. 统计分布与假设检验1.1 统计量与常见统计分布常见统计分布Python代码示例 1.2 正态性检验Shapiro-Wilk检验Python代码示例 1.3 独立性检验卡方检验Python代码示例 1.4 两组样本的差异性检验独立样本t检验Python代码示例 1.5 方差分析与事后多重比…

基于Django+MySQL球馆场地预约系统的设计与实现(源码+论文+部署讲解等)

博主介绍&#xff1a;✌全网粉丝10W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术栈介绍&#xff1a;我是程序员阿龙&#xff…

学习STM32(1)--Keil软件安装与基本操作和Keil 软件高级应用

目录 1 引 言 2 实验目的 3 实验内容 3.1 认识单片机和STM32 3.2 安装、认识软件Keil和硬件STM32F103开发板 3.3 学习调试工程 3.4 Keil工程软件的配置 4 深入解析 思考一 1.以项目“12-GPIO输出—使用固件库点亮LED”为例子&#xff0c;认识本地工程文件夹&#xf…

2024下《信息安全工程师》案例简答题,刷这些就够了!

距离2024下半年软考已经越来越近了&#xff0c;不知道今年备考软考信息安全工程师的同学们开始准备了吗&#xff1f; 简答题一直是信安拿分的重点区域&#xff0c;对于许多考生来说&#xff0c;也往往是最具挑战性的部分。今天我就把那些重要的案例简答题类型整理汇总给大家&am…

招聘网站的头像如何上传?

1、某招聘网站 显示的是圆形的&#xff0c;但是上传却需要正方形的300300像素。 2、某某招聘网站 仅支持jpg格式&#xff0c;不支持png 3、某某招聘网站 支持1mb以内图片&#xff0c;格式gif,jpg,png都支持。比较友好 4、某招聘网站 这里仅支持200200像素&#xff0c;图片格…

贪心系列专题篇四

​​​​​​​ 目录 整数替换 解法一 解法二 俄罗斯套娃信封问题 堆箱子 可被三整除的最大和 距离相等的条形码 重构字符串 声明&#xff1a;接下来主要使用贪心法来解决问题&#xff01;&#xff01;&#xff01; 整数替换 题目 思路 下面将使用两种方法来解决这…

【论文阅读】—RTDETR

《DETRs Beat YOLOs on Real-time Object Detection》 最近&#xff0c;基于端到端DETR取得了显著的性能。然而&#xff0c;DETR的高计算成本限制了它们的实际应用&#xff0c;并阻碍了它们充分利用无后处理&#xff08;如非最大抑制&#xff08;NMS&#xff09;&#xff09;的…

Windows下如何像linux一样查看GPU使用情况

在linux下&#xff0c;只要使用 nvidia-smi即可看到服务器中每块卡的使用情况 但是在windows下该如何查看显卡的使用情况呢 通过网上学习发现&#xff0c;windows下有一个叫nvidia-smi.exe的程序 找到它所在的路径&#xff0c;然后在命令行中进入到这个路径&#xff0c;然后…

文件上传漏洞-HackBar使用

介绍 HackBar 是一个用于浏览器的扩展插件&#xff0c;主要用于进行网络渗透测试和安全评估。它提供了一系列方便的工具和功能&#xff0c;可以帮助用户执行各种网络攻击和测试&#xff0c;包括 XSS、SQL 注入、CSRF、路径穿越等 下载地址 可以到github上面去下载&#xff0…

傅里叶级数的C语言验证

目录 概述 1 收敛性 1.1 收敛定理 1.2 理解收敛定理 2 傅里叶级数的应用 2.1 问题描述 2.2 实现方法 3 方波函数的傅里叶验证&#xff08;C语言实现&#xff09; 3.1 方波函数 3.1.1 编写方波函数 3.1.2 程序函数验证 3.2 傅里叶级数函数实现 3.2.1 编写傅里叶级…

Android 实现左侧导航栏:NavigationView是什么?NavigationView和Navigation搭配使用

目录 1&#xff09;左侧导航栏效果图 2&#xff09;NavigationView是什么&#xff1f; 3&#xff09;NavigationView和Navigation搭配使用 4&#xff09;NavigationView的其他方法 一、实现左侧导航栏 由于Android这边没有直接提供左侧导航栏的控件&#xff0c;所以我尝试了…

Frida Hook String构造函数

前言 在实际Android应用开发中&#xff0c;无论是使用多么复杂的算法对字符串进行加密&#xff0c;然而开发者常常会构造出字符串的实例。因此&#xff0c;我们可以通过使用Frida hook String类的构造函数来追踪这些实例的构造位置&#xff0c;然后可以通过构造实例的地方栈回…