Compare commits

..

3 Commits

Author SHA1 Message Date
tigerenwork 5f7537a01d Refactor code structure for improved readability and maintainability 2026-06-07 22:52:48 +08:00
tigerenwork b03a8f7dda feat: add demo script for Tushare Pro data acquisition
- Implemented a comprehensive demo for acquiring financial data using Tushare Pro.
- Included sections for registration, token configuration, and data fetching for A-shares, indices, and fundamental data.
- Added data cleaning and alignment processes, along with local database construction.
- Provided a comparison between Tushare and AKShare, highlighting key differences and use cases.
2026-06-07 15:33:37 +08:00
tigerenwork a9e1399cdc Update personal investor guide and add DeepSeek AI quantification document
- Enhanced the personal investor guide with additional sections on LLM-assisted quantitative analysis and risk management strategies.
- Included detailed explanations of practical scenarios for using LLM in quantitative analysis.
- Expanded the risk management section to cover volatility targeting, hard stop-loss, and maximum drawdown triggers.
- Added appendices comparing market entry books with the demo series.
- Introduced a new document on DeepSeek and Python for quantitative trading, covering foundational concepts, tools, and practical case studies.
2026-06-07 01:36:53 +08:00
9 changed files with 10067 additions and 2 deletions

723
3rd_ref/DeepseekAIQuan.md Normal file
View File

@ -0,0 +1,723 @@
第1章 DeepSeek、Python与量化交易概述
1.1 DeepSeek介绍
1.1.1 DeepSeek模型家族
1.1.2 DeepSeek的优势
1.1.3 DeepSeek的应用领域
1.2 如何使用DeepSeek
1.2.1 使用网页版DeepSeek
1.2.2 下载DeepSeek手机App
1.3 Python编程在量化交易中的重要性和优势
1.4 DeepSeek+Python赋能量化交易
1.5 本章总结
第2章 量化交易Python语言基础
2.1 Python解释器
2.2 IDE
2.2.1 安装PyCharm
2.2.2 安装Jupyter Notebook
2.2.3 启动Jupyter Notebook
2.3 第一个Python程序
2.3.1 编写脚本文件运行第一个Python程序
2.3.2 使用PyCharm编写和运行Python程序
2.3.3 使用Jupyter Notebook编写和运行Python程序
2.4 Python语法基础
2.4.1 标识符
2.4.2 关键字
2.4.3 变量
2.4.4 语句
2.4.5 代码块
2.4.6 模块
2.5 运算符
2.5.1 算术运算符
2.5.2 关系运算符
2.5.3 逻辑运算符
2.5.4 赋值运算符
2.6 数据类型
2.6.1 数字类型
2.6.2 列表
2.6.3 元组
2.6.4 集合
2.6.5 字典
2.7 字符串
2.7.1 字符串的创建
2.7.2 字符转义
2.7.3 字符串格式化
2.7.4 数字格式化
2.8 控制语句
2.8.1 分支语句
2.8.2 循环语句
2.8.3 跳转语句
2.9 函数
2.9.1 定义函数
2.9.2 调用函数
2.9.3 带参数的函数
2.9.4 带返回值的函数
2.9.5 默认参数
2.9.6 可变参数
2.9.7 lambda函数
2.9.8 使用filter()和map()函数进行数据处理
2.10 类
2.10.1 实例变量和构造函数
2.10.2 实例方法
2.11 文件操作
2.12 异常处理
2.12.1 捕获异常
2.12.2 释放资源
2.13 多线程
2.13.1 创建线程
2.13.2 等待线程结束
2.14 本章总结
第3章 Python量化基础工具库
3.1 NumPy
3.1.1 为什么选择NumPy
3.1.2 安装NumPy
3.2 创建数组
3.2.1 从Python列表创建一维数组
3.2.2 指定数组的数据类型
3.2.3 更多创建一维数组的方式
3.2.4 arange()函数
3.2.5 等差数列与linspace()函数
3.2.6 等比数列与logspace()函数
3.3 二维数组
3.4 更多创建二维数组的方式
3.4.1 使用ones()函数
3.4.2 使用zeros()函数
3.4.3 使用empty()函数
3.4.4 使用full()函数
3.4.5 使用identity()函数
3.5 数组的属性
3.6 数组的轴
3.6.1 轴的概念
3.6.2 轴的应用
3.6.3 轴的应用示例
3.7 三维数组
3.7.1 三维数组的结构
3.7.2 创建三维数组
3.8 访问数组
3.8.1 索引访问
3.8.2 切片访问
3.8.3 布尔索引
3.8.4 花式索引
3.9 Pandas
3.9.1 为什么选择Pandas
3.9.2 安装Pandas
3.10 Series数据结构
3.10.1 理解Series数据结构
3.10.2 创建Series对象
3.10.3 访问Series数据
3.10.4 通过切片访问Series数据
3.11 DataFrame数据结构
3.12 访问DataFrame数据
3.12.1 列访问
3.12.2 行访问
3.12.3 切片访问
3.13 读写数据
3.13.1 读取CSV文件数据
3.13.2 实战案例1从CSV文件读取货币供应量数据
3.13.3 写入数据到CSV文件
3.13.4 实战案例2将银行账户交易记录写入CSV文件
3.13.5 读取Excel文件数据
3.13.6 实战案例3从Excel文件中读取货币供应量月度数据
3.13.7 读取数据库
3.13.8 实战案例4从数据库中读取银行账户交易记录数据
3.14 本章总结
第4章 量化交易Python语言基础
4.1 量化交易可视化库
4.2 使用Matplotlib绘制图表
4.2.1 安装Matplotlib
4.2.2 图表基本构成要素
4.2.3 绘制折线图
4.2.4 绘制柱状图
4.2.5 绘制饼图
4.2.6 绘制散点图
4.3 使用Seaborn绘制图表
4.3.1 Seaborn内置数据集
4.3.2 Seaborn图表主题
4.3.3 柱状图
4.3.4 直方图
4.3.5 箱线图
4.3.6 小提琴图
4.3.7 热力图
4.4 时间序列可视化
4.4.1 实战案例5使用Matplotlib绘制英伟达股票历史成交量折线图
4.4.2 实战案例6绘制英伟达股票OHLC折线图
4.4.3 K线图
4.4.4 绘制K线图
4.4.5 实战案例7绘制英伟达股票K线图
4.4.6 实战案例8使用Seaborn绘制英伟达股票历史成交量折线图
4.5 本章总结
第5章 数据采集与分析
5.1 数据采集概述
5.1.1 数据采集的基本步骤
5.1.2 数据采集技术和工具
5.2 网页数据采集
5.2.1 使用urllib爬取网页数据
5.2.2 实战案例9爬取苹果股票数据
5.2.3 解析数据
5.2.4 使用BeautifulSoup
5.2.5 实战案例10解析苹果股票数据
5.2.6 使用Selenium爬取网页数据
5.2.7 实战案例11使用Selenium爬取中国石油股票数据
5.2.8 实战案例12使用Selenium解析HTML数据
5.2.9 借助DeepSeek爬取网页数据
5.3 API调用采集数据
5.3.1 常见的金融数据API
5.3.2 使用Tushare API采集数据
5.3.3 实战案例13使用Tushare API获取中国石油股票数据
5.4 数据清洗
5.4.1 实战案例14ABC股票数据清洗
5.4.2 处理股票数据类型不一致问题
5.4.3 处理股票数据异常值
5.4.4 DeepSeek助力数据清洗
5.4.5 实战案例15使用DeepSeek清洗特斯拉股票数据
5.5 统计分析
5.5.1 DeepSeek辅助统计分析
5.5.2 相关性分析
5.5.3 实战案例16股票行业相关性分析
5.5.4 统计描述和摘要
5.5.5 实战案例17苹果股票数据统计描述和摘要分析
5.6 本章总结
第6章 量化交易基础
6.1 量化交易概述
6.2 金融市场和交易品种概述
6.3 技术分析和基本面分析基础
6.3.1 技术分析
6.3.2 基本面分析
6.4 量化交易策略概述
6.5 本章总结
第7章 DeepSeek与量化交易结合
7.1 DeepSeek辅助技术分析
7.1.1 DeepSeek 在技术分析中的主要应用
7.1.2 实战案例18利用DeepSeek对000001.SZ股票进行技术分析
7.2 DeepSeek辅助基本面分析
7.2.1 DeepSeek在基本面分析中的应用
7.2.2 实战案例19利用DeepSeek对某上市公司公告进行解析
7.3 DeepSeek在市场情报分析中的应用
7.3.1 实战案例20利用DeepSeek对“央行发布降息25个基点”消息进行分析
7.3.2 实战案例21利用DeepSeek对“重大项目获得批复股价大涨20%”消息进行分析
7.4 DeepSeek在交易决策支持中的应用
7.4.1 实战案例22某科技型上市公司获大单DeepSeek提出交易决策建议
7.4.2 实战案例23某新能源概念股获多项利好DeepSeek交易建议
7.5 使用DeepSeek进行市场预测和趋势识别
7.5.1 实战案例24DeepSeek预测某城市商业地产市场面临调整
7.5.2 实战案例25DeepSeek用于预测“新能源汽车补贴退坡”的影响
7.6 本章总结
第8章 趋势跟踪策略与DeepSeek智能增强
8.1 趋势跟踪策略概述
8.1.1 趋势跟踪和交易决策中一些主要概念
8.1.2 使用移动平均线进行分析
8.2 使用DeepSeek辅助趋势跟踪策略决策过程
8.3 实战案例26使用DeepSeek辅助移动平均线策略分析微软股票
8.3.1 步骤1数据采集和加载数据
8.3.2 步骤2计算移动平均线
8.3.3 步骤3初始策略规则的制定
8.3.4 步骤4生成买入和卖出信号
8.3.5 步骤5DeepSeek赋能模拟回测验证策略
8.3.6 步骤6绘制K线图和信号
8.3.7 步骤7DeepSeek辅助优化策略
8.4 本章总结
第9章 动量策略与DeepSeek智能辅助决策
9.1 动量策略概述
9.1.1 动量策略中的一些主要概念
9.1.2 动量策略的优缺点
9.2 相对强弱指标
9.3 使用DeepSeek辅助动量策略决策
9.4 实战案例27使用DeepSeek辅助中国铝业股票价格和RSI交易信号分析
9.4.1 步骤1数据采集与预处理
9.4.2 步骤2计算RSI
9.4.3 步骤3初始策略规则的制定
9.4.4 步骤4生成买入和卖出信号
9.4.5 步骤5绘制RSI曲线与交易信号
9.4.6 步骤6DeepSeek赋能模拟回测验证策略
9.4.7 步骤7DeepSeek辅助优化策略
9.5 本章总结
第10章 海龟交易策略
10.1 海龟交易策略的诞生与基础概念
10.1.1 海龟交易策略的起源故事
10.1.2 海龟交易策略的核心原则
10.1.3 海龟交易策略的一些主要概念
10.1.4 海龟交易策略的实施过程
10.2 使用DeepSeek辅助实施海龟交易策略
10.3 实战案例28借助DeepSeek推进海龟交易策略落地——以中国石油股票交易为例
10.3.1 步骤1数据获取和准备
10.3.2 步骤2封装海龟交易策略函数
10.3.3 步骤3回测策略
10.3.4 步骤4回测的可视化分析
10.3.5 步骤5DeepSeek辅助优化策略
10.4 本章总结
第11章 借助DeepSeek构建与优化高频交易策略
11.1 高频交易策略概述
11.1.1 高频交易的特点
11.1.2 高频交易策略中的一些主要概念
11.1.3 实施高频交易策略
11.1.4 高频交易策略中常见的策略
11.1.5 高频交易策略的技术和设施层面问题
11.2 使用DeepSeek辅助实施高频交易策略
11.3 实战案例29利用DeepSeek辅助实施高频交易策略并优化股票投资回报——以比亚
迪股票为例
11.3.1 步骤1DeepSeek辅助制定策略
11.3.2 步骤2DeepSeek辅助选择交易平台和技术手段
11.3.3 步骤3DeepSeek辅助撰写交易算法
11.4 构建高频交易框架
11.4.1 高频交易框架的核心组件
11.4.2 高频交易框架的实现步骤
11.4.3 实战案例30基本高频交易框架实现
11.5 实战案例31基于配对交易策略的高频交易实施过程
11.6 实战案例32DeepSeek辅助HTF框架下的动量策略——以苹果股票为例
11.7 DeepSeek辅助实现其他编程语言的BHTF策略
11.8 本章总结
第12章 利用DeepSeek实施套利交易策略
12.1 套利策略概述
12.1.1 套利策略的基本定义
12.1.2 套利策略的类型
12.1.3 套利策略中的一些主要概念
12.2 实施套利交易策略
12.3 使用DeepSeek辅助实施套利交易策略
12.4 套利交易策略案例分析
12.4.1 实战案例33股票A跨市场套利
12.4.2 实战案例34利用美元与欧元汇率差异套利
12.4.3 实战案例35同行业相对值套利策略
12.5 实战案例36中国石化股票和中国石油股票配对交易套利
12.5.1 步骤1清洗数据
12.5.2 步骤2读取股票数据
12.5.3 步骤3两只股票的相关性分析
12.5.4 步骤4使用DeepSeek对相关性进行分析
12.5.5 步骤5回测股票历史数据
12.5.6 步骤6使用DeepSeek对回测结果进行分析
12.5.7 步骤7使用DeepSeek优化策略
12.6 本章总结
第13章 基于机器学习与DeepSeek优化的量化交易策略
13.1 机器学习策略中的一些主要概念
13.2 机器学习策略分类
13.3 分类策略
13.3.1 Python机器学习库
13.3.2 机器学习策略实施过程
13.4 实战案例37使用分类策略预测英伟达股票走势
13.4.1 步骤1数据准备和处理
13.4.2 步骤2模型训练
13.4.3 步骤3使用DeepSeek进行模型评估
13.4.4 步骤4使用DeepSeek进行模型优化
13.4.5 步骤5预测股票走势
13.5 实战案例38使用回归策略预测英伟达股票走势
13.5.1 步骤1数据准备和处理
13.5.2 步骤2模型训练
13.5.3 步骤3预测股票走势
13.5.4 步骤4使用DeepSeek进行模型评估
13.5.5 步骤5使用DeepSeek进行模型优化
13.5.6 步骤6使用优化后的模型再次预测股票走势
13.6 实战案例39LSTM预测比特币价格趋势
13.6.1 步骤1加载和清洗数据
13.6.2 步骤2模型训练
13.6.3 步骤3可视化结果
13.6.4 步骤4使用DeepSeek进行模型评估
13.6.5 步骤5使用DeepSeek优化模型
13.6.6 步骤6比特币价格预测
13.7 本章总结
第14章 量化交易回测框架与DeepSeek优化
14.1 再谈回测
14.1.1 回测的基本流程
14.1.2 常见回测框架
14.2 Backtrader框架
14.2.1 Backtrader使用流程
14.2.2 实战案例40使用Backtrader回测苹果股票的双均线策略
14.2.3 DeepSeek辅助优化Backtrader参数双均线策略
14.3 本章总结
第15章 利用DeepSeek提高量化交易的风险管理效能
15.1 风险管理工具和方法
15.1.1 止损与止盈策略
15.1.2 实战案例41基于移动平均线的固定止损+固定止盈策略
15.1.3 实战案例42移动止损和移动止盈策略
15.1.4 头寸管理
15.1.5 实战案例43基于波动率的动态头寸管理策略——以特斯拉股票为例
15.1.6 投资组合分散
15.1.7 实战案例44股票与黄金的风险分散投资策略
15.1.8 对冲策略
15.1.9 实战案例45对冲策略——股票与债券的对冲组合
15.2 使用DeepSeek辅助量化交易风险管理
15.2.1 风险识别
15.2.2 实战案例46DeepSeek智能监控应对市场动荡
15.2.3 风险评估
15.2.4 实战案例47基于DeepSeek的科技股投资组合的风险评估
15.2.5 风险控制
15.2.6 实战案例48应对银行业危机的风险控制
15.3 本章总结
第16章 AI+量化交易的未来DeepSeek API调用与AI智能体赋能
16.1 DeepSeek API调用
16.1.1 DeepSeek RESTful API接口
16.1.2 调用DeepSeek API接口的基本流程
16.1.3 实战案例49调用DeepSeek API获取财经新闻简报
16.1.4 实战案例50使用Tushare API+DeepSeek API分析股票数据简报
16.2 智能体在量化交易中的应用
16.2.1 智能体介绍简报
16.2.2 扣子智能体平台
16.3 实战案例51实现“财经新闻快报”智能体
16.3.1 步骤1创建智能体
16.3.2 步骤2创建工作流
16.3.3 步骤3添加节点
16.3.4 步骤4试运行
16.3.5 步骤5发布
16.3.6 步骤6实时测试
16.4 智能体与量化交易现状和未来发展
16.4.1 当前状况
16.4.2 未来展望
16.5 本章总结

View File

@ -0,0 +1,37 @@
============================================================
数据质量报告 / Data Quality Report
生成时间: 2026-06-07T16:05:29.175368
数据源: AKShare (fund_etf_hist_em)
============================================================
159995 (芯片ETF):
日期范围: 2021-02-04 ~ 2025-05-30 (1043 天)
原始缺失: 265 天 (17.1%)
日均收益率: +0.000172
日波动率: 0.021145
最大日涨幅: +10.01%
最大日跌幅: -9.05%
159819 (人工智能ETF):
日期范围: 2021-02-04 ~ 2025-05-30 (1043 天)
原始缺失: 421 天 (27.1%)
日均收益率: +0.000163
日波动率: 0.019471
最大日涨幅: +10.07%
最大日跌幅: -9.95%
516160 (新能源ETF):
日期范围: 2021-02-04 ~ 2025-05-30 (1043 天)
原始缺失: 510 天 (32.8%)
日均收益率: -0.000343
日波动率: 0.020161
最大日涨幅: +10.07%
最大日跌幅: -9.98%
159928 (消费ETF):
日期范围: 2021-02-04 ~ 2025-05-30 (1043 天)
原始缺失: 0 天 (0.0%)
日均收益率: -0.000380
日波动率: 0.015514
最大日涨幅: +10.01%
最大日跌幅: -9.94%

File diff suppressed because it is too large Load Diff

1044
data/etf_returns_panel.csv Normal file

File diff suppressed because it is too large Load Diff

5923
data/index_prices.csv Normal file

File diff suppressed because it is too large Load Diff

465
demo_akshare_data.py Normal file
View File

@ -0,0 +1,465 @@
# =============================================================================
# Real Data Acquisition Demo — AKShare
# 真实数据获取演示 — AKShare 版
# =============================================================================
#
# AKShare 是一个完全免费的开源金融数据接口,无需注册、无需 Token。
# 覆盖 A 股/港股/美股/期货/外汇/宏观经济等数据,是目前个人投资者
# 最实用的免费数据源。
#
# Prerequisites / 安装:
# pip install akshare pandas numpy matplotlib
#
# Topics covered / 涵盖主题:
# §1 AKShare 简介与基本用法
# §2 获取 A 股行业 ETF 日线数据(轮动策略核心输入)
# §3 获取宽基指数数据沪深300 / 中证500
# §4 获取北向资金数据(情绪因子数据源)
# §5 获取融资融券数据(杠杆情绪数据源)
# §6 真实数据的清洗陷阱(与合成数据的关键差异)
# §7 数据存储:构建本地研究数据库
# §8 数据质量报告生成
# =============================================================================
from __future__ import annotations
import warnings
warnings.filterwarnings("ignore")
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
import time
import os
# ── 绕过系统代理,直连东方财富 ──
# macOS 上的 Clash/小火箭等代理工具的出口 IP 容易被东方财富反爬封禁。
# 设置环境变量让 requests 库对 eastmoney.com 不走代理。
os.environ["NO_PROXY"] = os.environ.get("NO_PROXY", "") + ",eastmoney.com,eastmoneyfutures.com,10jqka.com.cn"
os.environ["no_proxy"] = os.environ["NO_PROXY"]
# ── 检查 akshare 是否安装 ──
try:
import akshare as ak
print(f"✓ AKShare 版本: {ak.__version__}")
except ImportError:
print("❌ AKShare 未安装。请在 trading conda 环境中执行:")
print(" conda activate trading")
print(" pip install akshare")
raise
print("=" * 68)
print(" 真实数据获取演示 — AKShare 版")
print(" Real Data Acquisition Demo with AKShare")
print("=" * 68)
# ══════════════════════════════════════════════════════════════════════
# §1 AKShare 简介与基本用法
# ══════════════════════════════════════════════════════════════════════
#
# AKShare 特点:
# • 完全免费,无需注册
# • 数据来源于东方财富、新浪财经、交易所等公开接口
# • 函数名遵循 fund_xxx / stock_xxx / macro_xxx 等命名规范
# • 每次调用触发一次 HTTP 请求,批量获取需注意频率
#
# 基本用法:
# df = ak.function_name(param1=value1, param2=value2)
# → 返回 pandas DataFrame
#
# 文档: https://akshare.akfamily.xyz/
print("\n[§1] AKShare 简介")
print(f" 数据接口数量: 数千个 (覆盖股票/基金/期货/外汇/宏观/另类)")
print(f" 数据来源: 东方财富、新浪财经、交易所公开接口等")
print(f" 费用: 完全免费")
# ══════════════════════════════════════════════════════════════════════
# §2 获取 A 股行业 ETF 日线数据
# ══════════════════════════════════════════════════════════════════════
#
# 这是 demo_07 (ETF 轮动策略) 的真实数据替代方案。
# 下面获取的 ETF 代码与 demo_07 的 ETF_UNIVERSE 一一对应。
#
# AKShare 接口: fund_etf_hist_em()
# symbol — ETF 代码 (如 "159995")
# period — "daily" / "weekly" / "monthly"
# start_date / end_date — "YYYYMMDD" 格式
# adjust — "" (不复权) / "qfq" (前复权) / "hfq" (后复权)
# ETF 通常用 "" 或 "qfq"
# =============================================================================
print("\n[§2] 获取行业 ETF 日线数据")
# A 股行业 ETF 池 — 与 demo_07 对应
ETF_UNIVERSE = {
"159995": "芯片ETF",
"159819": "人工智能ETF",
"516160": "新能源ETF",
"159928": "消费ETF",
"512170": "医疗ETF",
"512880": "证券ETF",
# 宽基 ETF (新增)
"510300": "沪深300ETF",
"510500": "中证500ETF",
# 国债 ETF (避险资产)
"511010": "国债ETF",
}
START_DATE = "20190101"
END_DATE = "20250601"
# ── 安全获取单只 ETF 数据 (带重试和限速) ──
def fetch_etf_safe(code: str, start: str, end: str, max_retries: int = 3):
"""
安全获取 ETF 日线数据失败时自动重试并控制请求频率
网络抖动或东方财富接口偶发不稳定时非常必要
"""
for attempt in range(max_retries):
try:
df = ak.fund_etf_hist_em(
symbol=code,
period="daily",
start_date=start,
end_date=end,
adjust="qfq", # 前复权
)
return df
except Exception as e:
err_msg = str(e)
if "ProxyError" in err_msg or "RemoteDisconnected" in err_msg:
wait = (attempt + 1) * 5 # 代理/连接问题,等更久
print(f"\n{code} 连接被拒绝 (代理/反爬){wait}s 后重试...")
else:
wait = 2.0
print(f"\n{code}{attempt+1} 次获取失败: {e}")
if attempt < max_retries - 1:
time.sleep(wait)
print(f"\n{code} 全部 {max_retries} 次获取失败,跳过")
return None
etf_data = {}
for i, (code, name) in enumerate(ETF_UNIVERSE.items()):
if i > 0:
# 请求间隔 + 随机抖动,避免触发东方财富反爬
time.sleep(2.0 + np.random.uniform(0, 1.5))
print(f" 获取 {code} ({name})...", end=" ")
df = fetch_etf_safe(code, START_DATE, END_DATE)
if df is not None:
etf_data[code] = df
print(f"{len(df)} 行, {df['日期'].iloc[0]} ~ {df['日期'].iloc[-1]}")
else:
print("失败 ✗")
print(f"\n 成功获取: {len(etf_data)} / {len(ETF_UNIVERSE)} 只 ETF")
# ── 构建价格面板 (Multi-Asset Price Panel) ──
# 这是最关键的步骤:将多只 ETF 的收盘价对齐到统一的日期轴上。
# 真实数据中,不同 ETF 可能有不同的上市日期和停牌日期,
# 对齐方式的选择直接影响后续所有分析。
if etf_data:
price_panel = pd.DataFrame()
for code, df in etf_data.items():
# 设置日期索引,提取收盘价
df_indexed = df.set_index("日期")["收盘"].copy()
df_indexed.name = code
price_panel = pd.concat([price_panel, df_indexed], axis=1)
# 确保日期索引排序
price_panel.index = pd.to_datetime(price_panel.index)
price_panel = price_panel.sort_index()
print(f"\n 价格面板: {price_panel.shape[0]} 个交易日 × {price_panel.shape[1]} 只 ETF")
print(f" 日期范围: {price_panel.index[0].date()} ~ {price_panel.index[-1].date()}")
print(f" 各 ETF 起止日期:")
for col in price_panel.columns:
valid = price_panel[col].dropna()
if len(valid) > 0:
print(f" {col} ({ETF_UNIVERSE.get(col, '?')}): {valid.index[0].date()} ~ {valid.index[-1].date()}, {len(valid)}")
# ══════════════════════════════════════════════════════════════════════
# §3 获取宽基指数数据
# ══════════════════════════════════════════════════════════════════════
#
# AKShare 接口: stock_zh_index_daily()
# symbol — 指数代码:
# "sh000300" → 沪深300
# "sh000905" → 中证500
# "sz399006" → 创业板指
# "sh000016" → 上证50
# =============================================================================
print("\n[§3] 获取宽基指数数据")
INDEX_CODES = {
"sh000300": "沪深300",
"sh000905": "中证500",
"sz399006": "创业板指",
"sh000016": "上证50",
}
index_data = {}
for symbol, name in INDEX_CODES.items():
print(f" 获取 {symbol} ({name})...", end=" ")
try:
df = ak.stock_zh_index_daily(symbol=symbol)
index_data[symbol] = df
print(f"{len(df)} 行, {df['date'].iloc[0]} ~ {df['date'].iloc[-1]}")
time.sleep(0.3)
except Exception as e:
print(f"失败: {e}")
# ── 计算指数滚动估值参考 (PE/PB 分位数需另接接口,此处展示收益基座) ──
if index_data:
# 对齐为面板
index_panel = pd.DataFrame()
for sym, df in index_data.items():
s = df.set_index("date")["close"].copy()
s.name = sym
index_panel = pd.concat([index_panel, s], axis=1)
index_panel.index = pd.to_datetime(index_panel.index)
index_panel = index_panel.sort_index()
# 计算滚动年化收益 (252 日)
rolling_ret = index_panel.pct_change(252).dropna()
if len(rolling_ret) > 0:
print(f"\n 最新滚动年化收益 ({rolling_ret.index[-1].date()}):")
for col in rolling_ret.columns:
name = INDEX_CODES.get(col, col)
val = rolling_ret[col].iloc[-1]
print(f" {name}: {val:+.2%}")
# ══════════════════════════════════════════════════════════════════════
# §4 获取北向资金数据(关键情绪因子数据源)
# ══════════════════════════════════════════════════════════════════════
#
# 北向资金 (North-bound Capital Flow) 指通过沪深港通从香港流入 A 股
# 的外资。净买入额被视为外资对 A 股情绪的"投票机"。
#
# AKShare 接口: stock_hsgt_hist_em() → 沪深港通历史资金流向
# stock_hsgt_north_net_flow_in_em() → 北向净流入
# =============================================================================
print("\n[§4] 获取北向资金数据")
try:
# 北向资金: stock_hsgt_hist_em 获取沪股通/深股通历史资金流向
for market, label in [("沪股通", "沪股通(北向)"), ("深股通", "深股通(北向)")]:
try:
hsgt = ak.stock_hsgt_hist_em(symbol=market)
if hsgt is not None and len(hsgt) > 0:
print(f" {label}: {hsgt.shape}")
print(f" 列名: {list(hsgt.columns)}")
print(f" 最新 3 行:\n{hsgt.tail(3)}")
time.sleep(1.5) # 避免触发反爬
except Exception as e:
print(f" {label} 获取失败: {e}")
except Exception as e:
print(f" 北向资金获取失败: {e}")
print(f" 提示: AKShare 接口可能已更新,请查阅最新文档")
# ══════════════════════════════════════════════════════════════════════
# §5 获取融资融券数据(杠杆情绪数据源)
# ══════════════════════════════════════════════════════════════════════
#
# 融资余额 (Margin Balance) 反映散户借钱买股的意愿。
# 融资余额大幅上升 → 散户看多情绪高涨(注意:极端值往往是反向指标)
# 融资余额骤降 → 恐慌去杠杆,市场底部特征之一
#
# AKShare 接口: stock_margin_sz() / stock_margin_sh() → 深圳/上海融资融券明细
# stock_margin_detail_sse() → 上交所融资融券汇总
# =============================================================================
print("\n[§5] 获取融资融券数据")
try:
# 上交所融资融券汇总 (日频)
# stock_margin_sse: 历史序列 (start_date/end_date)
# stock_margin_detail_sse: 单日快照 (date='YYYYMMDD')
margin_sse = ak.stock_margin_sse(start_date="20240101", end_date="20250601")
if margin_sse is not None and len(margin_sse) > 0:
print(f" 上交所融资融券: {margin_sse.shape}")
print(f" 列名: {list(margin_sse.columns)}")
print(f" 最新数据:\n{margin_sse.tail(3)}")
else:
print(" 未获取到融资融券数据")
except Exception as e:
print(f" 融资融券获取失败: {e}")
print(f" 说明: 此接口可能需要特定参数格式,请查阅文档")
print(f" 备选: 可直接使用东方财富网页爬取")
# ══════════════════════════════════════════════════════════════════════
# §6 真实数据的清洗陷阱
# ══════════════════════════════════════════════════════════════════════
#
# 合成数据 (Demo 系列的 GBM/Factor Model) 没有这些问题:
# ① 不同标的上市日期不同 → 面板前段大量 NaN
# ② 停牌 (suspension) → 面板中间出现 NaN 缺口
# ③ 涨跌停日 → 价格"真实"但无法成交
# ④ ETF 分红/拆分 → 不复权价格存在跳跃
# ⑤ 数据源异常 → 极少数日期的 OHLC 数据错乱
# =============================================================================
print("\n[§6] 真实数据清洗演示")
if etf_data:
# ── 6-A: 缺失值分析 ──
print(f"\n 6-A 缺失值分析:")
missing_pct = price_panel.isna().mean().sort_values(ascending=False)
for code in missing_pct.index[:5]:
pct = missing_pct[code]
name = ETF_UNIVERSE.get(code, code)
print(f" {code} {name}: {pct:.1%} 缺失")
# ── 6-B: 前向填充 (仅 ffill, 禁止 bfill!) ──
# bfill 会用"未来还没发生的数据"填补今天的空值 → Look-Ahead Bias
print(f"\n 6-B 缺失值处理 (ffill only):")
missing_before = price_panel.isna().sum().sum()
clean_prices = price_panel.ffill() # 步骤1: 前向填充
# 步骤2: 丢弃早期所有标的都 NaN 的行(通常是面板最前面)
clean_prices = clean_prices.dropna(how='any')
missing_after = clean_prices.isna().sum().sum()
print(f" 填充前 NaN: {missing_before} → 填充后 NaN: {missing_after}")
print(f" 丢弃了 {len(price_panel) - len(clean_prices)} 个早期交易日")
# ── 6-C: 收益率计算与异常值标记 ──
print(f"\n 6-C 收益率与涨跌停标记:")
returns = clean_prices.pct_change()
# 涨跌停检测: A 股 ETF 理论涨跌停 ±10%
LIMIT_PCT = 0.10
limit_up = (returns >= LIMIT_PCT - 0.002).sum()
limit_down = (returns <= -LIMIT_PCT + 0.002).sum()
for code in returns.columns[:5]:
up = limit_up[code]
dn = limit_down[code]
if up > 0 or dn > 0:
name = ETF_UNIVERSE.get(code, code)
print(f" {code} {name}: 涨停 {up} 天, 跌停 {dn}")
# ── 6-D: 数据连续性检查 ──
print(f"\n 6-D 数据连续性检查:")
date_gaps = 0
for col in clean_prices.columns:
ser = clean_prices[col].dropna()
if len(ser) < 2:
continue
# 检查自然日连续性 → 非交易日(周末/假日)是正常的
trading_gaps = ser.index.to_series().diff().dt.days
long_gaps = trading_gaps[trading_gaps > 7] # 连续停牌 > 7 天
if len(long_gaps) > 0:
name = ETF_UNIVERSE.get(col, col)
print(f" {col} {name}: {len(long_gaps)} 次连续停牌 > 7 天")
date_gaps += len(long_gaps)
if date_gaps == 0:
print(f" 所有 ETF 无连续停牌 > 7 天 ✓")
# ══════════════════════════════════════════════════════════════════════
# §7 数据存储:构建本地研究数据库
# ══════════════════════════════════════════════════════════════════════
#
# 存储原则是"存原始数据 + 存清洗后数据",不要只存一个。
# 原因: 你以后可能想换一种清洗方式,而原始数据丢了就回不去了。
# =============================================================================
print("\n[§7] 数据存储")
DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
os.makedirs(DATA_DIR, exist_ok=True)
if etf_data:
# 保存清洗后的价格面板 (研究级)
clean_path = os.path.join(DATA_DIR, "etf_price_panel_clean.csv")
clean_prices.to_csv(clean_path)
print(f" ✓ 清洗后价格面板 → {clean_path}")
# 保存收益率面板
returns_path = os.path.join(DATA_DIR, "etf_returns_panel.csv")
returns.to_csv(returns_path)
print(f" ✓ 收益率面板 → {returns_path}")
if index_data and len(index_panel) > 0:
index_path = os.path.join(DATA_DIR, "index_prices.csv")
index_panel.to_csv(index_path)
print(f" ✓ 指数价格 → {index_path}")
print(f"\n 数据目录: {DATA_DIR}")
print(f" 文件列表: {os.listdir(DATA_DIR) if os.path.exists(DATA_DIR) else ''}")
# ══════════════════════════════════════════════════════════════════════
# §8 数据质量报告
# ══════════════════════════════════════════════════════════════════════
#
# 每次拉取数据后,生成一份质量报告,记录:
# - 各标的覆盖日期范围
# - 缺失值比例
# - 极端收益率事件
# - 数据拉取时间戳
# 这对后期回溯调试非常有用——"我用的数据是哪天拉的?有什么已知缺陷?"
# =============================================================================
print("\n[§8] 数据质量报告")
if etf_data:
report_lines = []
report_lines.append("=" * 60)
report_lines.append("数据质量报告 / Data Quality Report")
report_lines.append(f"生成时间: {datetime.now().isoformat()}")
report_lines.append(f"数据源: AKShare (fund_etf_hist_em)")
report_lines.append("=" * 60)
for code in clean_prices.columns:
name = ETF_UNIVERSE.get(code, code)
ser = clean_prices[code].dropna()
ret_ser = returns[code].dropna()
n = len(ser)
missing_input = price_panel[code].isna().sum()
report_lines.append(
f"\n{code} ({name}):"
f"\n 日期范围: {ser.index[0].date()} ~ {ser.index[-1].date()} ({n} 天)"
f"\n 原始缺失: {missing_input} 天 ({missing_input/len(price_panel):.1%})"
f"\n 日均收益率: {ret_ser.mean():+.6f}"
f"\n 日波动率: {ret_ser.std():.6f}"
f"\n 最大日涨幅: {ret_ser.max():+.2%}"
f"\n 最大日跌幅: {ret_ser.min():+.2%}"
)
# 打印报告
report_text = "\n".join(report_lines)
print(report_text)
# 保存报告
report_path = os.path.join(DATA_DIR, "data_quality_report.txt")
with open(report_path, "w", encoding="utf-8") as f:
f.write(report_text)
print(f"\n ✓ 质量报告已保存 → {report_path}")
print("\n" + "=" * 68)
print(" ✓ AKShare 数据获取 Demo 完成")
print("=" * 68)
print(f"""
关键收获:
1. AKShare 完全免费覆盖 ETF / 指数 / 资金流向 / 融资融券
2. 真实数据需要处理: 上市日期不一致停牌缺口涨跌停标记
3. 填充缺失值只用 ffill() bfill() 会引入 Look-Ahead Bias
4. 数据存储时同时保留"原始""清洗后"两个版本
5. 每次拉取数据都应生成质量报告记录已知缺陷
下一步: 将清洗后的 ETF 价格面板替换 demo_07 的合成数据
重新运行 ETF 轮动策略回测观察真实数据下的策略表现
""")

619
demo_tushare_data.py Normal file
View File

@ -0,0 +1,619 @@
# =============================================================================
# Real Data Acquisition Demo — Tushare Pro
# 真实数据获取演示 — Tushare Pro 版
# =============================================================================
#
# Tushare Pro 是国内最成熟的金融数据 API 之一,数据质量和稳定性
# 优于 AKShare适合策略验证通过后切换到生产级数据。
#
# 与 AKShare 的核心差异:
# • 需要注册获取 Token (免费注册,基础接口免费)
# • 积分系统: 注册送 120 分,部分接口需要更高积分
# • 股票代码格式: "000001.SZ" (而非纯数字)
# • 数据质量更高、接口更稳定
# • 支持基本面数据 (PE/PB/ROE) 和因子数据
#
# Prerequisites / 前置准备:
# 1. 注册 Tushare: https://tushare.pro/register
# 2. 获取 Token: 登录后 → 个人主页 → 接口 Token
# 3. 安装: pip install tushare pandas numpy
#
# Topics covered / 涵盖主题:
# §1 Tushare 注册与 Token 配置
# §2 获取 A 股个股日线数据
# §3 获取指数日线数据
# §4 获取股票基础信息 (上市日期/行业/市值)
# §5 获取财务数据 (PE/PB/ROE — 价值因子的数据源)
# §6 获取指数成分股权重 (组合优化的输入)
# §7 获取行业分类 (申万行业)
# §8 数据清洗与对齐
# §9 构建本地数据库
# §10 AKShare vs Tushare 对比总结
# =============================================================================
from __future__ import annotations
import warnings
warnings.filterwarnings("ignore")
import numpy as np
import pandas as pd
import os
import time
from datetime import datetime
# ── 检查 tushare 是否安装 ──
try:
import tushare as ts
print(f"✓ Tushare 版本: {ts.__version__}")
except ImportError:
print("❌ Tushare 未安装。请在 trading conda 环境中执行:")
print(" conda activate trading")
print(" pip install tushare")
raise
print("=" * 68)
print(" 真实数据获取演示 — Tushare Pro 版")
print(" Real Data Acquisition Demo with Tushare Pro")
print("=" * 68)
# ══════════════════════════════════════════════════════════════════════
# §1 Tushare 注册与 Token 配置
# ══════════════════════════════════════════════════════════════════════
#
# 使用步骤:
# 1. 访问 https://tushare.pro/register 注册 (用手机号即可)
# 2. 登录后 → 个人主页 → 接口 Token → 复制那一长串字符
# 3. 把 Token 粘贴到下面 (或设置环境变量 TUSHARE_TOKEN)
#
# 积分说明:
# 注册: 120 分 (基础接口可用)
# 完善个人信息: +20 分
# 推荐他人注册: 各 +50 分
# 捐赠: 200 RMB = 3000 分 (解锁全部接口)
#
# 日常使用建议:
# - 将 Token 存为环境变量而非硬编码在代码中
# - 每个接口都标注所需最低积分,避免超限调用
# =============================================================================
print("\n[§1] Tushare Token 配置")
TUSHARE_TOKEN = os.environ.get("TUSHARE_TOKEN", "")
if not TUSHARE_TOKEN:
print(" ⚠ 未检测到 TUSHARE_TOKEN 环境变量")
print(" ── 获取 Token 的步骤 ──")
print(" 1. 访问 https://tushare.pro/register 注册")
print(" 2. 登录后进入个人主页 → 接口 Token")
print(" 3. 复制 Token 后,设置环境变量:")
print(" export TUSHARE_TOKEN='你的token'")
print()
print(" 或者直接在下方代码中填入 (仅本地测试用):")
print(' TUSHARE_TOKEN = "你的token"')
print()
print(" ⚠ 本 Demo 将继续运行,但数据获取部分会跳过或使用模拟数据")
print(" 您仍然可以看到完整的代码结构和数据清洗逻辑。")
print()
# 尝试初始化 Pro API
PRO = None
TOKEN_VALID = False
if TUSHARE_TOKEN:
try:
PRO = ts.pro_api(TUSHARE_TOKEN)
# 用最简单接口测试 Token 是否有效
test = PRO.trade_cal(exchange='SSE', start_date='20250601', end_date='20250605')
TOKEN_VALID = len(test) > 0
print(f" ✓ Token 验证成功")
print(f" ✓ Pro API 就绪")
except Exception as e:
print(f" ✗ Token 验证失败: {e}")
else:
print(f" - Token 未设置,跳过远程数据获取")
print(f" - 代码结构完整,可阅读学习流程逻辑")
# ══════════════════════════════════════════════════════════════════════
# §2 获取 A 股个股日线数据
# ══════════════════════════════════════════════════════════════════════
#
# 接口: pro.daily()
# 所需积分: 120 分 (注册即送)
# 每日调用上限: 200 次
# 单次最多返回: 约 5000 条记录
#
# 参数:
# ts_code — 股票代码 "000001.SZ" / "600000.SH"
# trade_date— 交易日期 "YYYYMMDD"
# start_date/end_date — 日期范围
#
# 注意: Tushare 的日期格式是 "YYYYMMDD" (无连字符)
# =============================================================================
print("\n[§2] 获取个股日线数据")
# ── 构建 A 股代表性标的池 ──
STOCK_POOL = {
"000001.SZ": "平安银行",
"000002.SZ": "万科A",
"000858.SZ": "五粮液",
"600519.SH": "贵州茅台",
"600036.SH": "招商银行",
"600276.SH": "恒瑞医药",
"300750.SZ": "宁德时代",
"601318.SH": "中国平安",
}
# ── 日期范围转换 ──
# Demo 系列用 "YYYY-MM-DD"Tushare 用 "YYYYMMDD"
START_DATE = "20190101"
END_DATE = "20250601"
def fetch_daily_safe(pro, ts_code, start, end, max_retries=3):
"""
安全获取个股日线数据
Tushare 每次调用返回约 5000 单只股票 6 年约 1500 个交易日
一次调用即可覆盖如果有更多股票/更长区间需要分多次调用并拼接
"""
for attempt in range(max_retries):
try:
df = pro.daily(
ts_code=ts_code,
start_date=start,
end_date=end,
fields='ts_code,trade_date,open,high,low,close,pre_close,'
'change,pct_chg,vol,amount'
)
time.sleep(0.3) # 频率控制: Tushare 每分钟上限约 200 次
return df
except Exception as e:
print(f"{ts_code}{attempt+1} 次失败: {e}")
if attempt < max_retries - 1:
time.sleep(2.0)
return None
if TOKEN_VALID and PRO:
stock_daily_data = {}
for code, name in STOCK_POOL.items():
print(f" 获取 {code} ({name})...", end=" ")
df = fetch_daily_safe(PRO, code, START_DATE, END_DATE)
if df is not None and len(df) > 0:
stock_daily_data[code] = df
print(f"{len(df)} 行, {df['trade_date'].iloc[-1]} ~ {df['trade_date'].iloc[0]}")
else:
print("无数据")
if stock_daily_data:
# ── 构建价格面板 ──
stock_price_panel = pd.DataFrame()
for code, df in stock_daily_data.items():
# Tushare 返回的 trade_date 是 YYYYMMDD 格式
df = df.copy()
df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d')
df = df.sort_values('trade_date')
s = df.set_index('trade_date')['close'].copy()
s.name = code
stock_price_panel = pd.concat([stock_price_panel, s], axis=1)
print(f"\n 个股价格面板: {stock_price_panel.shape}")
# 展示最新 3 行
print(f" 最新 3 个交易日:")
print(stock_price_panel.tail(3).to_string())
else:
print(" (跳过 — Token 未配置)")
print(f" 演示: 如果 Token 有效,会拉取以下 {len(STOCK_POOL)} 只股票的数据")
for code, name in STOCK_POOL.items():
print(f" {code} ({name})")
# ══════════════════════════════════════════════════════════════════════
# §3 获取指数日线数据
# ══════════════════════════════════════════════════════════════════════
#
# 接口: pro.index_daily()
# 所需积分: 120 分
#
# 指数代码:
# 000300.SH — 沪深300
# 000905.SH — 中证500
# 000016.SH — 上证50
# 399006.SZ — 创业板指
# =============================================================================
print("\n[§3] 获取指数日线数据")
INDEX_CODES = {
"000300.SH": "沪深300",
"000905.SH": "中证500",
"000016.SH": "上证50",
"399006.SZ": "创业板指",
}
if TOKEN_VALID and PRO:
index_data = {}
for code, name in INDEX_CODES.items():
print(f" 获取 {code} ({name})...", end=" ")
try:
df = PRO.index_daily(
ts_code=code,
start_date=START_DATE,
end_date=END_DATE,
fields='ts_code,trade_date,close,open,high,low,vol,amount,pct_chg'
)
time.sleep(0.3)
if df is not None and len(df) > 0:
index_data[code] = df
print(f"{len(df)}")
else:
print("无数据")
except Exception as e:
print(f"失败: {e}")
if index_data:
index_panel = pd.DataFrame()
for code, df in index_data.items():
df = df.copy()
df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d')
df = df.sort_values('trade_date')
s = df.set_index('trade_date')['close']
s.name = code
index_panel = pd.concat([index_panel, s], axis=1)
print(f"\n 指数面板: {index_panel.shape}")
# 年化收益对比
ann_rets = (index_panel.iloc[-1] / index_panel.iloc[0]) ** (252.0 / len(index_panel)) - 1
print(" 年化收益率 (CAGR):")
for code, val in ann_rets.items():
name = INDEX_CODES.get(code, code)
print(f" {name}: {val:+.2%}")
else:
print(" (跳过 — Token 未配置)")
# ══════════════════════════════════════════════════════════════════════
# §4 获取股票基础信息
# ══════════════════════════════════════════════════════════════════════
#
# 接口: pro.stock_basic()
# 所需积分: 120 分
# 功能: 获取股票列表,包含股票代码、名称、上市日期、退市日期、行业、地区等
# =============================================================================
print("\n[§4] 获取股票基础信息")
if TOKEN_VALID and PRO:
try:
# 获取沪深两市全部 A 股列表
stock_basic = PRO.stock_basic(
exchange='',
list_status='L', # L=上市, D=退市, P=暂停上市
fields='ts_code,symbol,name,area,industry,market,list_date,'
'delist_date,curr_type,list_status,enname'
)
if stock_basic is not None and len(stock_basic) > 0:
print(f" 当前上市股票: {len(stock_basic)}")
# 退市股票 (用于幸存者偏差检查!)
# 如果只拉当前上市股票,回测会漏掉已退市的垃圾公司
# → 幸存者偏差 (Survivorship Bias) ← 这是实盘最大的坑之一
delisted = PRO.stock_basic(
exchange='',
list_status='D', # 退市
fields='ts_code,symbol,name,list_date,delist_date'
)
if delisted is not None and len(delisted) > 0:
print(f" 历史退市股票: {len(delisted)} 只 ← 回测必须包含!")
print(f" 退市样本 (前 5 只):")
for _, row in delisted.head(5).iterrows():
print(f" {row['ts_code']} {row['name']} "
f"{row['list_date']} ~ {row['delist_date']}")
# 行业分布
if 'industry' in stock_basic.columns:
industry_counts = stock_basic['industry'].value_counts().head(10)
print(f"\n 行业分布 Top 10:")
for ind, cnt in industry_counts.items():
print(f" {ind}: {cnt}")
except Exception as e:
print(f" 获取失败: {e}")
else:
print(" (跳过 — Token 未配置)")
print(" 提示: stock_basic() 可获取全市场股票列表和退市记录")
print(" 退市数据对消除幸存者偏差至关重要")
# ══════════════════════════════════════════════════════════════════════
# §5 获取财务数据 (价值因子的数据源)
# ══════════════════════════════════════════════════════════════════════
#
# 这是 Tushare 相比 AKShare 最大的优势领域 ——
# AKShare 也有部分财务接口,但覆盖度和稳定性不如 Tushare。
#
# 接口: pro.daily_basic() — 每日指标
# 所需积分: 300 分 (需升级)
# 字段: pe, pe_ttm, pb, ps, ps_ttm, dv_ratio, total_share, float_share,
# total_mv, circ_mv
#
# 接口: pro.fina_indicator() — 财务指标
# 所需积分: 600 分 (需进一步升级)
# 字段: roe, roa, grossprofit_margin, netprofit_margin, debt_to_assets 等
# =============================================================================
print("\n[§5] 获取财务数据")
if TOKEN_VALID and PRO:
try:
# daily_basic: 日频估值指标 (PE/PB/市值)
daily_basic = PRO.daily_basic(
ts_code='000001.SZ',
start_date='20240101',
end_date='20250601',
fields='ts_code,trade_date,close,pe,pe_ttm,pb,ps,total_mv,circ_mv'
)
if daily_basic is not None and len(daily_basic) > 0:
print(f" 平安银行 (000001.SZ) 日频估值: {len(daily_basic)}")
print(f" 字段: {list(daily_basic.columns)}")
print(f" 最新 PE_TTM / PB / 市值:")
latest = daily_basic.iloc[0]
print(f" PE(TTM): {latest.get('pe_ttm', 'N/A')}")
print(f" PB: {latest.get('pb', 'N/A')}")
print(f" 总市值: {latest.get('total_mv', 'N/A')} 万元")
print(f"\n ⚠ 注意: 如果返回的 pe_ttm 都是 NaN, 说明积分不足 (需 300 分)")
else:
print(" 未获取到数据 (积分可能不足)")
except Exception as e:
print(f" 获取失败: {e}")
print(f" 提示: daily_basic 需要 300 积分,注册仅送 120 分")
else:
print(" (跳过 — Token 未配置)")
print(" 提示: Tushare 的 daily_basic() 和 fina_indicator() 可获取")
print(" PE/PB/ROE/ROA/毛利率/资产负债率等基本面数据")
print(" 这些都是价值因子 (Value Factor) 的核心输入")
# ══════════════════════════════════════════════════════════════════════
# §6 获取指数成分股权重
# ══════════════════════════════════════════════════════════════════════
#
# 接口: pro.index_weight() — 指数成分股权重
# 所需积分: 120 分
# 用途: 组合优化 (Black-Litterman 的市场均衡权重)
# =============================================================================
print("\n[§6] 获取指数成分股权重")
if TOKEN_VALID and PRO:
try:
# 沪深300 成分股权重 (每月更新)
index_weights = PRO.index_weight(
index_code='000300.SH',
start_date='20240101',
end_date='20250601',
)
if index_weights is not None and len(index_weights) > 0:
print(f" 沪深300 成分股权重: {len(index_weights)} 条记录")
print(f" 字段: {list(index_weights.columns)}")
# 最新一期 Top 10
latest_date = index_weights['trade_date'].max() if 'trade_date' in index_weights.columns else None
if latest_date is not None:
latest_weights = index_weights[
index_weights['trade_date'] == latest_date
].nlargest(10, 'weight') if 'weight' in index_weights.columns else None
if latest_weights is not None and len(latest_weights) > 0:
print(f"\n {latest_date} 前 10 大权重股:")
for _, row in latest_weights.iterrows():
print(f" {row['con_code']} {row.get('con_name', '')}: "
f"{row['weight']:.2%}")
else:
print(" 未获取到数据")
except Exception as e:
print(f" 获取失败: {e}")
else:
print(" (跳过 — Token 未配置)")
print(" 提示: index_weight() 可获取沪深300/中证500等指数的成分股权重")
print(" 这是 Black-Litterman 模型市场均衡组合的必需输入")
# ══════════════════════════════════════════════════════════════════════
# §7 获取行业分类 (申万行业)
# ══════════════════════════════════════════════════════════════════════
#
# 申万行业分类是中国 A 股投研最常用的行业分类标准。
# 在组合优化中 (demo_05), 行业约束需要知道每只股票属于哪个行业。
#
# 接口: pro.index_classify() — 申万行业分类
# 所需积分: 120 分
# =============================================================================
print("\n[§7] 获取行业分类")
if TOKEN_VALID and PRO:
try:
# 获取申万一级行业分类 (level='L1')
sw_classify = PRO.index_classify(
level='L1',
src='SW2021' # 申万2021版行业分类
)
if sw_classify is not None and len(sw_classify) > 0:
print(f" 申万一级行业: {sw_classify['industry_name'].nunique()}")
print(f" 各行业成分股数量 Top 10:")
industry_counts = sw_classify.groupby('industry_name').size().sort_values(ascending=False)
for name, cnt in industry_counts.head(10).items():
print(f" {name}: {cnt}")
else:
print(" 未获取到数据 (接口可能已变更)")
except Exception as e:
print(f" 获取失败: {e}")
print(f" 提示: 申万行业分类接口可能已更新,建议查阅最新文档")
else:
print(" (跳过 — Token 未配置)")
# ══════════════════════════════════════════════════════════════════════
# §8 数据清洗与对齐 (Tushare 特有)
# ══════════════════════════════════════════════════════════════════════
# =============================================================================
print("\n[§8] 数据清洗与对齐")
if TOKEN_VALID and PRO and 'stock_daily_data' in dir() and stock_daily_data:
# ── 缺失值分析 ──
missing = stock_price_panel.isna().sum()
print(f" 各标的缺失天数:")
for code, cnt in missing.items():
name = STOCK_POOL.get(code, code)
print(f" {code} ({name}): {cnt} 天 ({cnt/len(stock_price_panel):.1%})")
# ── ffill + 丢弃行首 ──
clean_prices = stock_price_panel.ffill().dropna(how='any')
print(f"\n 清洗前: {stock_price_panel.shape[0]}")
print(f" 清洗后: {clean_prices.shape[0]} 天 (丢弃 {stock_price_panel.shape[0] - clean_prices.shape[0]} 天)")
# ── 收益率面板 ──
returns = clean_prices.pct_change().dropna()
print(f" 收益率面板: {returns.shape}")
# ── 相关性矩阵 ──
corr = returns.corr()
print(f"\n 收益率相关性 (仅展示对角外最高相关的 3 对):")
corr_unstack = corr.where(
~np.eye(len(corr), dtype=bool)
).unstack().dropna()
top_pairs = corr_unstack.abs().nlargest(6)
for (s1, s2), val in top_pairs.items():
print(f" {s1}{s2}: {val:+.3f}")
else:
print(" (跳过 — 无数据可清洗)")
print(" 提示: 清洗流程与 demo_akshare_data.py 相同,核心原则:")
print(" 1. 只用 ffill(), 禁止 bfill()")
print(" 2. 填充后 dropna(how='any') 丢弃仍有 NaN 的行")
print(" 3. pct_change() 后再 dropna() 得到收益率面板")
# ══════════════════════════════════════════════════════════════════════
# §9 构建本地数据库
# ══════════════════════════════════════════════════════════════════════
# =============================================================================
print("\n[§9] 构建本地数据库")
DATA_DIR = os.path.join(os.path.dirname(__file__), "data", "tushare")
os.makedirs(DATA_DIR, exist_ok=True)
if TOKEN_VALID and PRO:
saved_files = []
if 'stock_daily_data' in dir() and stock_daily_data:
# 保存清洗后价格面板
clean_path = os.path.join(DATA_DIR, "stock_price_clean.csv")
clean_prices.to_csv(clean_path)
saved_files.append(clean_path)
# 保存收益率
ret_path = os.path.join(DATA_DIR, "stock_returns.csv")
returns.to_csv(ret_path)
saved_files.append(ret_path)
if 'index_panel' in dir() and len(index_panel) > 0:
idx_path = os.path.join(DATA_DIR, "index_prices.csv")
index_panel.to_csv(idx_path)
saved_files.append(idx_path)
# 保存拉取元信息
meta_path = os.path.join(DATA_DIR, "fetch_metadata.txt")
with open(meta_path, "w", encoding="utf-8") as f:
f.write(f"数据源: Tushare Pro\n")
f.write(f"拉取时间: {datetime.now().isoformat()}\n")
f.write(f"数据范围: {START_DATE} ~ {END_DATE}\n")
f.write(f"标的数量: {len(STOCK_POOL)} 只个股, {len(INDEX_CODES)} 个指数\n")
saved_files.append(meta_path)
for f in saved_files:
print(f"{f}")
print(f"\n 数据目录: {DATA_DIR}")
print(f" 文件列表: {os.listdir(DATA_DIR) if os.path.exists(DATA_DIR) else ''}")
else:
print(" (跳过 — 无数据可保存)")
# ══════════════════════════════════════════════════════════════════════
# §10 AKShare vs Tushare 对比总结
# ══════════════════════════════════════════════════════════════════════
# =============================================================================
print("\n" + "=" * 68)
print(" §10 AKShare vs Tushare 对比总结")
print("=" * 68)
print("""
维度 AKShare Tushare Pro
费用 完全免费 基础免费(120)
高级需积分/捐赠
注册 不需要 需要手机号注册
数据覆盖 极广 (//// 聚焦 A /指数/
/另类/舆情等) 基金/财务/因子
数据稳定性 中等 (依赖爬虫, (独立数据源,
接口偶发变更) 专业维护)
基本面数据 有限 (PE/PB 基本) 丰富 (PE/PB/ROE/
财务报表等)
退市股票数据 获取较难 stock_basic() 直接
包含退市记录
因子数据 (Barra 风格因子)
股票代码格式 "000001" (纯数字) "000001.SZ" (含市场)
调用频率限制 无硬限制 (建议礼貌) (按积分等级)
最佳使用场景 研究探索快速验证 策略验证通过后的
数据覆盖第一选择 生产级数据源
推荐路径:
研究阶段 AKShare (零成本快速验证想法)
策略定型 Tushare (数据质量更高接口更稳定)
实盘运行 Tushare + 本地缓存数据库 (减少 API 依赖)
关键提醒:
AKShare 接口偶尔变更升级版本前记得检查 changelog
Tushare 积分用完会拒绝请求注意剩余调用次数
两个数据源的收盘价可能存在微小差异 (取整/复权方式)
回测结果因此会有细微不同这是正常的
长期存储请用 Parquet 格式 ( CSV 更快更省空间):
df.to_parquet("data.parquet")
df = pd.read_parquet("data.parquet")
""")
print("=" * 68)
print(" ✓ Tushare Pro 数据获取 Demo 完成")
print("=" * 68)
print(f"""
关键收获:
1. Tushare 需要 Token (免费注册)积分系统决定可用接口范围
2. 股票代码格式为 "000001.SZ" / "600000.SH" (含交易所后缀)
3. Tushare 最大优势: 财务数据 (PE/PB/ROE) + 退市数据 + 稳定性
4. daily_basic() fina_indicator() 是价值因子的核心数据源
5. index_weight() 提供 Black-Litterman 所需的市场权重
6. stock_basic(list_status='D') 是消除幸存者偏差的关键
7. 研究阶段用 AKShare定型后用 Tushare 生产化
下一步:
- Tushare 拉取的数据写入 demo_04 (Alpha 因子)
用真实 PE/PB/ROE 替代合成因子
- 用真实退市记录修正回测的幸存者偏差
""")

View File

@ -3,7 +3,8 @@
> **定位**:本文档基于对整个 Demo 系列的代码审计和策略评估,为个人投资者回答三个核心问题——做什么策略、怎么做策略、用什么平台做。 > **定位**:本文档基于对整个 Demo 系列的代码审计和策略评估,为个人投资者回答三个核心问题——做什么策略、怎么做策略、用什么平台做。
> **前置阅读**:建议已通读完 7 篇 Demo 后阅读本文。 > **前置阅读**:建议已通读完 7 篇 Demo 后阅读本文。
> **目标读者**:有 Python 基础的量化入门者,关注 A 股实操。 > **目标读者**:有 Python 基础的量化入门者,关注 A 股实操。
> **附注**:文末附录 B 对比了市面上典型量化入门书如《DeepSeek+Python 量化交易》)与本 Demo 系列的差异,帮读者理解"为什么有些书看起来厚但学不到东西"。
--- ---
@ -14,8 +15,13 @@
3. [个人投资者的现实策略频谱](#3-个人投资者的现实策略频谱) 3. [个人投资者的现实策略频谱](#3-个人投资者的现实策略频谱)
4. [中低频策略的核心范式](#4-中低频策略的核心范式) 4. [中低频策略的核心范式](#4-中低频策略的核心范式)
5. [情绪因子与突发事件量化](#5-情绪因子与突发事件量化) 5. [情绪因子与突发事件量化](#5-情绪因子与突发事件量化)
- 5.3 [LLM 辅助量化分析:两个务实场景](#53-llm-辅助量化分析两个务实场景)
6. [从 Demo 到实盘:平台与工具链](#6-从-demo-到实盘平台与工具链) 6. [从 Demo 到实盘:平台与工具链](#6-从-demo-到实盘平台与工具链)
- 6.3 [真实数据接入](#63-真实数据接入合成数据的第一个暴击)
- 6.4 [风控层详解Demo 系列最大的盲区](#64-风控层详解demo-系列最大的盲区)
7. [推荐学习与实践路径](#7-推荐学习与实践路径) 7. [推荐学习与实践路径](#7-推荐学习与实践路径)
附录 A[关键术语速查](#附录-a关键术语速查)
附录 B[市面入门书 vs 本 Demo 系列](#附录-b市面入门书-vs-本-demo-系列以deepseekpython-量化交易为例)
--- ---
@ -174,6 +180,64 @@ Demo 中用的价格/成交量/波动率都是"硬数据",但"软数据"在很
3. **文本情绪监控**:用大模型对财经新闻标题做情感分析,构建日频情绪指数。当前有大模型后门槛大幅降低——可以用本地模型批量打分。属于"另类数据因子"的范畴,初期可选。 3. **文本情绪监控**:用大模型对财经新闻标题做情感分析,构建日频情绪指数。当前有大模型后门槛大幅降低——可以用本地模型批量打分。属于"另类数据因子"的范畴,初期可选。
### 5.3 LLM 辅助量化分析:两个务实场景
现有 Demo 系列完全没有触及 LLM大语言模型在量化中的应用。这不是让你用 ChatGPT 预测股价——那是玄学。以下两个场景是 LLM 在量化中真正有实用价值的方向,也是市面 AI 量化书籍如《DeepSeek+Python 量化交易》)中为数不多的值得关注的内容。
**场景一:非结构化信息 → 结构化情绪因子**
传统量价因子(动量、波动率、流动性)是 Demo 4 的核心但市场情绪信息大多藏在文本里——财经新闻、公告、研报、社交媒体。LLM 让个人投资者也能批量处理这类非结构化数据:
```
工作流:
每日收盘后
→ 用 AKShare/Tushare 获取当日财经新闻标题列表
→ 批量送 LLM
prompt: "以下是一条财经新闻请判断它对A股市场
的情绪影响:看多/中性/看空。只回复一个词。"
→ 汇总当日 "看多率 看空率" 作为情绪因子原始值
→ 做滚动标准化Z-score得到日频情绪因子
```
注意事项:
- 用本地模型Ollama + Qwen/Llama 等 7B 级模型)可以零成本跑,速度足够覆盖日频
- 情感分类只用标题即可(延迟低、成本低),全文分析留到需要深度解读的场景
- 情绪因子的 IC 通常较低0.02-0.04),但与传统量价因子相关性低,合成后能提升 ICIR
- 需要小心 Look-Ahead如果你用 LLM 解读盘后新闻,信号只能在次日使用
**场景二:回测报告的自动解读**
你跑完回测得到一串绩效数字Sharpe 1.2, MaxDD -18%, Calmar 0.8...这些数字真正意味着什么LLM 可以做有价值的"翻译"
```
Prompt 示例:
"以下是一个ETF轮动策略的回测绩效
- 夏普比率1.15
- 最大回撤:-22.3%,发生在 2022年3月
- 月度胜率58%
- 年化收益率13.2%
- 换手率:月均 35%
请分析:
1. 这些指标的整体评价
2. 最大回撤发生在什么市场环境下提示2022年3月 A股处于什么阶段
3. 这个策略最可能在什么市场环境下失效
4. 实盘中需要额外关注的风险点"
```
这比你自己盯着一串数字琢磨半小时有效得多。关键是——LLM 不是替你决策,而是帮你更全面地**理解**你的策略在历史上的行为特征。
**LLM 在量化中的定位边界**
| 能用 LLM 做的 | 不该用 LLM 做的 |
|--------------|----------------|
| 文本情感 → 因子值 | "帮我写一个稳赚的策略" |
| 回测报告解读 | 直接预测股价涨跌方向 |
| 代码辅助与调试 | 替代回测LLM 不知道数据里的陷阱) |
| 参数优化建议(需结合回测验证) | 直接给调仓决策(你才是最终负责人) |
> **一句话**LLM 是研究助理,不是交易员。它可以帮你从文本中提取信号、帮你理解回测结果、帮你写代码——但最终的策略逻辑、风险控制和执行纪律,必须是你自己理解并负责的。
--- ---
## 6. 从 Demo 到实盘:平台与工具链 ## 6. 从 Demo 到实盘:平台与工具链
@ -242,6 +306,94 @@ Demo 代码在本地跑跑看图表是"研究阶段"。实盘涉及三个层次
| 8. 参数稳健性检验 | 对 top_k、回望期、再平衡频率做敏感性分析 | 建议 | | 8. 参数稳健性检验 | 对 top_k、回望期、再平衡频率做敏感性分析 | 建议 |
| 9. 绩效归因 | Brinson 归因 / 因子归因,确认收益来源 | 进阶 | | 9. 绩效归因 | Brinson 归因 / 因子归因,确认收益来源 | 进阶 |
以下对其中两项(数据接入 + 风控层)展开详细说明,因为它们是 Demo 到实盘跨度最大的两步也是市面量化入门书如《DeepSeek+Python 量化交易》)几乎不涉及的内容。
### 6.3 真实数据接入:合成数据的第一个"暴击"
现有 Demo 全部使用合成数据GBM / 因子模型生成),好处是零门槛运行,但切换到真实数据时会遇到以下几类合成数据没有的问题:
**问题 1停牌导致的日期错位**
真实 A 股数据中,不同标的停牌日期不同。直接 `pd.concat` 多个标的的收盘价会得到参差不齐的日期索引。关键是只做 ffill向前填充**禁止 bfill**——bfill 会用"复牌后的未来价格"填补停牌前的 NaN这是典型的 Look-Ahead Bias。
```
正确做法:
price_panel = price_panel.ffill() # 只看向历史
price_panel = price_panel.dropna(how='any') # 仍有 NaN 的行丢弃
```
**问题 2ETF 净值 vs 市价的差异**
ETF 有两个价格——IOPV实时估算净值和市价。Demo 7 的轮动策略用收盘价回测是合理的,但实盘中 ETF 可能出现折溢价(市价显著偏离净值),这在极端行情下会影响实际成交价格。实盘代码应该用市价做信号计算和回测,但需要理解 ETF 的折溢价风险。
**问题 3数据源的选择**
| 数据源 | 优势 | 劣势 |
|--------|------|------|
| AKShare | 免费、覆盖面广、更新及时 | API 偶尔不稳定,部分接口可能变更 |
| Tushare Pro | 稳定、文档全、数据质量高 | 需要积分(部分接口收费) |
| Baostock | 免费、无需注册 | 更新慢、覆盖面有限 |
| 聚宽/米筐数据 | 清洗过、质量最高 | 绑定平台,本地使用受限 |
推荐初次尝试用 AKShare零成本策略验证通过后切到 Tushare Pro稳定性更好
### 6.4 风控层详解Demo 系列最大的盲区
现有 7 篇 Demo 的风险管理几乎只有"最大回撤"这个**事后统计指标**。实盘中,风控必须是一个**事前和事中的执行层**——在亏损发生之前或初期就介入。以下三种风控手段是个人投资者最容易落地且效果最显著的。
**手段一波动率缩放仓位Volatility Targeting**
这是最简单、最有效、但对策略绩效改善最大的手段。核心思想:当市场最近很"颠簸"(波动率放大),自动减小仓位;当市场平稳,正常仓位。
```
核心公式:
目标仓位 = 基础仓位 × (目标波动率 / 近期已实现波动率)
示例:
基础仓位 = 100%(满仓)
目标波动率 = 15%(年化,你愿意承受的风险水平)
近期已实现波动率 = 30%(过去 20 日年化) → 仓位 = 100% × 15/30 = 50%
近期已实现波动率 = 10% → 仓位 = 100% × 15/10 = 150%(需设上限)
实现要点:
- 波动率窗口20-60 个交易日,太短不稳定,太长反应慢
- 仓位上下限:通常设 [30%, 150%] 或 [50%, 100%](不做杠杆)
- 更新频率:每日计算但每周调一次即可(避免过度交易)
- A 股限制:融资账户最高杠杆 1:1普通账户无法做空
```
这个手段的妙处在于:它不预测市场方向,只根据"当前有多危险"来调整暴露——是纯粹的防守动作,不会引入新的预测误差。
**手段二硬止损Hard Stop-Loss**
现有 Demo 的策略出场完全依赖信号(金叉进/死叉出),这在实盘中是不够的。信号可能迟迟不来,而你的亏损已经超出了可承受范围。
```
简单止损规则:
- 单笔交易止损:持仓亏损超过 -X%(如 -8%或 -15%)→ 无条件平仓
- 月度最大亏损:当月累计亏损超过 -Y% → 暂停交易,下月再评估
- 连续亏损暂停:连续 Z 笔交易亏损 → 暂停,人工审查策略是否失效
参数建议(个人投资者):
- 趋势策略(如动量轮动): X=12-15%Y=8-10%Z=4
- 均值回归策略(如 RSI: X=5-8% Y=5-8% Z=5
```
**手段三:最大回撤熔断**
这是一个组合层面的保护:如果账户从历史最高点回撤超过一定幅度,自动进入"防御模式"。
```
实现逻辑:
if 当前回撤(从历史最高净值算起) < -25%:
强制减仓至 30%
暂停新开仓
发送告警 → "策略触发最大回撤熔断,净值从峰值回撤 xx%,已自动减仓"
# 回撤恢复到 -15% 以内再恢复交易
```
这三种手段的组合使用,能把你从"策略失效了才发现"的被动状态,变成"有明确规则控制损失"的主动状态。Demo 系列没有覆盖这些,但它们在实盘中比多找到 0.1 的夏普更重要。
--- ---
## 7. 推荐学习与实践路径 ## 7. 推荐学习与实践路径
@ -282,7 +434,7 @@ Demo 给你的是知识地图,不是提款机密码。实盘中最重要的是
--- ---
## 附录:关键术语速查 ## 附录 A:关键术语速查
| 术语 | 英文 | 含义 | | 术语 | 英文 | 含义 |
|------|------|------| |------|------|------|
@ -294,3 +446,58 @@ Demo 给你的是知识地图,不是提款机密码。实盘中最重要的是
| 前视偏差 | Look-Ahead Bias | 用"未来数据"做回测决策导致的虚假高收益 | | 前视偏差 | Look-Ahead Bias | 用"未来数据"做回测决策导致的虚假高收益 |
| 幸存者偏差 | Survivorship Bias | 只用当前存活的股票回测,忽略了已退市的股票 | | 幸存者偏差 | Survivorship Bias | 只用当前存活的股票回测,忽略了已退市的股票 |
| 风格漂移 | Style Drift | 策略实际持仓偏离了其宣称的投资风格 | | 风格漂移 | Style Drift | 策略实际持仓偏离了其宣称的投资风格 |
---
## 附录 B市面入门书 vs 本 Demo 系列——以《DeepSeek+Python 量化交易》为例
市面上很多量化交易入门书看起来很厚(三四百页、几十个案例),但读完往往发现"好像学了很多,又好像什么都没学到"。这里以《DeepSeek+Python 量化交易》16 章、51 个实战案例)作为典型标本,拆解为什么会有这种感觉,以及它和本 Demo 系列的真实差距。
### 全书结构
| 板块 | 章节 | 内容 |
|------|------|------|
| Python 基础 | 第 1-5 章 | Python 语法、NumPy/Pandas/Matplotlib、网页爬虫 + Tushare API |
| 策略入门 | 第 6-10 章 | 量化概念、DeepSeek 辅助分析(技术/基本面/消息)、双均线/RSI/海龟策略 |
| 进阶策略 | 第 11-13 章 | 高频交易(概念层)、套利策略、机器学习预测(分类/回归/LSTM |
| 工程与风控 | 第 14-16 章 | Backtrader 回测、止损止盈/头寸管理/对冲、DeepSeek API + 智能体 |
### 它的定位
一本**面向绝对零基础读者的量化交易科普 + DeepSeek 使用指南**。目标读者是"刚学会 Python 甚至还没学 Python 的人"。全书 51 个案例覆盖面极广,但每个案例的深度有限——典型模式是:"拉数据 → 套一个经典公式 → 画图 → 把结果扔给 DeepSeek 解读 → DeepSeek 给优化建议"。
### 它有价值的部分
书中真正值得关注的内容集中在三个 Demo 系列没有覆盖的方向:
1. **真实数据接入(第 5 章)**:用 Tushare API 拉取 A 股真实数据的流程,虽然讲得浅,但方向对——这是 Demo 到实盘的第一道坎
2. **风险管理工具(第 15 章)**:止损止盈、波动率头寸管理、对冲组合——这些是 Demo 系列最大盲区,详见本文 §6.4
3. **LLM 辅助分析(第 7、16 章)**:用 LLM 处理非结构化信息(公告解读、新闻情绪、策略回测解读)——这是这本书唯一真正的差异化内容,详见本文 §5.3
### 它的问题
| 问题 | 说明 |
|------|------|
| 策略覆盖面过时 | 双均线 + RSI + 海龟是 1980-1990 年代的经典。现代量化核心——因子模型、截面选股、组合优化——完全没有涉及 |
| 高频章节名不副实 | 第 11 章名为"高频交易",实际是概念科普 + 一个比亚迪案例。真正的高频涉及 colocation/FPGA/市场微观结构,书中完全无法覆盖——这会误导读者以为"写个 Python 脚本就是高频交易" |
| 深度学习案例浮于表面 | LSTM 预测股价只用一个案例,无时间序列交叉验证、无样本外泛化讨论——容易让初学者产生"跑个模型就能预测股价"的危险错觉 |
| DeepSeek 角色被过度包装 | 书中几乎每个案例都以"DeepSeek 辅助优化"结尾。但 LLM 对量化策略的优化能力完全取决于 prompt 质量和用户自身的判断力——书中没有讨论这个根本局限 |
| 回测严谨性近乎为零 | 不涉及过拟合检测、前视偏差、幸存者偏差、参数敏感性——而本 Demo 系列的事件驱动回测和滚动前向验证专门解决了这些问题 |
| 无 A 股特有规则的系统讨论 | 涨跌停、T+1、融券限制、印花税——这些在本 Demo 系列的 `doc_06``doc_07` 中有专门覆盖,书中几乎没有 |
### 两种学习路径的对比
| 维度 | 《DeepSeek+Python量化交易》 | 本 Demo 系列 |
|------|---------------------------|-------------|
| 目标读者 | 零基础(从 Python 安装开始) | 有 Python 基础,缺少量化方法论 |
| 学习方式 | 广度优先 — 51 个案例快速扫一遍 | 深度优先 — 5-7 个主题层层递进 |
| 策略类型 | 经典技术指标策略(均线/RSI/海龟) | 因子模型 + 截面选股 + 组合优化 |
| 回测方法论 | 仅 Backtrader 基本使用 | 向量化 → 事件驱动 → 前向验证,完整链条 |
| 学术严谨性 | 较低 — 不涉及 IC/ICIR/因子预处理 | 较高 — IC 分析、去极值/Z-score/中性化、Ledoit-Wolf |
| A 股实操 | 无系统讨论 | 实盘指南doc_06+ ETF 轮动策略doc_07 |
| AI 角色 | DeepSeek 贯穿全书,定位为"策略顾问" | 无 AI 依赖,本文 §5.3 补充了 LLM 的务实用法 |
| 代码复杂度 | 简单脚本级别 | 类 + 函数封装,模拟生产级结构 |
### 一句话总结
这本书能让你从"完全不会 Python"跑到"能跑通第一个策略脚本",但从"跑通脚本"到"独立做量化交易"所需的系统知识——因子研究、稳健回测、组合构建、风险控制、A 股实盘陷阱——它几乎没有涉及。而这些东西恰好是本 Demo 系列所覆盖的。两套内容存在一定的互补性:**用那本书学会 Python 基础,用这套 Demo 学会量化方法论。**

View File

@ -2,3 +2,6 @@ numpy
pandas pandas
matplotlib matplotlib
scipy scipy
scikit-learn
akshare
tushare