ChatGPT炒股实战:基于LLM的量化交易策略开发指南
ChatGPT炒股实战:基于LLM的量化交易策略开发指南
- 传统技术分析的“老毛病”
我最早做量化时,满屏都是MACD、KDJ、布林带。跑完回测发现,收益曲线看着漂亮,一上实盘就“翻车”。症结无非三点:
- 技术指标天生滞后,价格走完了才金叉死叉,滑点吃光利润。
- 财报、突发新闻、社媒情绪这类文本数据,用关键词打分太粗糙,“利好”可能被打成“利空”。
- 特征工程靠人肉,换一只票就要重新调参,策略迁移性极差。
于是我把目光投向大模型:让LLM直接读新闻、给信号,把“情绪”量化成可执行的交易指令。
- 技术方案:FinBERT vs ChatGPT
FinBERT金融领域预训练,专业术语准,但只有两个标签(positive/negative),粒度太粗;而且自己微调要标注数据,成本不低。ChatGPT zero-shot就能吐出“强烈看空”“谨慎做多”这类带程度的词,还能给出理由,方便我做五档信号(+2 +1 0 -1 -2)。
Prompt我迭代了七版,最终定型如下,token 120 左右,便宜又稳定:
你是一名资深交易员。请阅读以下财经新闻,给出交易信号(+2强烈做多,+1谨慎做多,0观望,-1谨慎做空,-2强烈做空),并一句话说明理由。输出格式:信号|理由
新闻:{text}
把新闻标题+正文拼进 {text},temperature 设 0.3,既抑制幻觉,又保留一定区分度。
- 核心代码:端到端流水线
下面代码全部跑通 Python3.10,依赖见注释。为了阅读体验,我拆成四段,每段都能单独测试。
3.1 新闻爬取与清洗(以新浪财经为例)
# pip install requests beautifulsoup4 lxml
import requests, bs4, datetime as dt, pandas as pd
HEADERS = {"User-Agent": "Mozilla/5.0"}
def crawl_sina_finance(page=1):
url = f"https://feed.sina.com.cn/api/roll/get?pageid=153&lid=2516&k=&num=50&page={page}"
data = requests.get(url, headers=HEADERS, timeout=10).json()
records = []
for item in data["result"]["data"]:
records.append({
"time": dt.datetime.fromtimestamp(int(item["ctime"])),
"title": item["title"],
"url": item["url"]
})
return pd.DataFrame(records)
def clean_text(url):
html = requests.get(url, headers=HEADERS, timeout=10).text
soup = bs4.BeautifulSoup(html, "lxml")
article = soup.find("div", class_="article") # 新浪正文容器
if not article:
return ""
return " ".join(p.text for p in article.find_all("p"))
# 示例:抓两页,清洗后落盘
df = crawl_sina_finance(page=1)
df["body"] = df["url"].apply(clean_text)
df = df[df["body"].str.len() > 40] # 过滤太短
df.to_csv("news_raw.csv", index=False)
时间复杂度:爬取 O(n),清洗正则 O(m)(m 为单篇字符数),整体可忽略。
3.2 ChatGPT API 封装(含重试与限速)
# pip install openai tenacity
import openai, tenacity, time, os
openai.api_key = os.getenv("OPENAI_API_KEY")
@tenacity.retry(stop=tenacity.stop_after_attempt(5),
wait=tenacity.wait_exponential(multiplier=1, min=4, max=60))
def call_gpt(prompt, model="gpt-3.5-turbo", temperature=0.3):
resp = openai.ChatCompletion.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=temperature,
max_tokens=80
)
return resp["choices"][0]["message"]["content"].strip()
# 批量调用,60 条/分钟 保守限流
def batch_signal(news_df, col="body"):
signals, reasons = [], []
for txt in news_df[col]:
out = call_gpt(PROMPT_TEMPLATE.format(text=txt[:1500])) # 截断防超额
sig, _, reason = out.partition("|")
signals.append(int(sig.strip()))
reasons.append(reason.strip())
time.sleep(1.05) # RPM 限制
news_df["signal"], news_df["reason"] = signals, reasons
return news_df
3.3 信号与 TA 指标融合(示例:MACD+新闻情绪)
import talib, numpy as np
def hybrid_signal(df_price, df_news, lookback=5):
# 1. 计算 MACD
macd, signal, _ = talib.MACD(df_price["close"].values)
macd_cur = macd[-1] - signal[-1]
# 2. 新闻情绪均值
news_window = df_news.tail(lookback)["signal"].mean()
# 3. 冲突解决:简单投票
if news_window > 0.5 and macd_cur > 0:
return 1 # 双多头
elif news_window < -0.5 and macd_cur < 0:
return -1 # 双空头
else:
return 0 # 观望
3.4 回测+可视化(Walk Forward 示例框架)
# 假设 price.csv 含 ['date','open','high','low','close','vol']
price = pd.read_csv("price.csv", parse_dates=["date"])
news = pd.read_csv("news_with_signal.csv", parse_dates=["time"])
results = []
for train, test in walk_split(price, train_days=250, test_days=30):
# 训练集内只做参数统计,这里无超参可略
for idx in test.index:
sub_news = news[news["time"].between(train.date.iloc[-1], test.date[idx])]
sig = hybrid_signal(price[:idx], sub_news)
results.append({
"date": price.loc[idx, "date"],
"signal": sig,
"price": price.loc[idx, "close"]
})
res = pd.DataFrame(results)
res["ret"] = res["price"].pct_change() * res["signal"].shift(1)
res["nav"] = (1 + res["ret"].fillna(0)).cumprod()
# 画图
import matplotlib.pyplot as plt
plt.figure(figsize=(8,4))
plt.plot(res["date"], res["nav"], label="LLM+MACD")
plt.plot(res["date"], (1+res["price"].pct_change()).cumprod(), label="Buy&Hold")
plt.legend(), plt.show()
- 避坑指南
- 频率控制:OpenAI 免费 tier 60 请求/分钟,超出直接 429。用 tenacity 装饰器+sleep 双保险。
- 结果验证:
– SHAP 分析:把 prompt 拆成“标题”“正文”“行业词”三段,看哪段对信号贡献最大,防止模型偷懒只读标题。
– Walk Forward:滚动 250/30 天,比单次回测更能暴露过拟合;我初期偷懒用全量,Sharpe 1.8,滚动后掉到 0.9,真相了。 - 数据合规:美股材料必须符合 Reg-FD,公开渠道抓取可以,但别碰付费终端的 raw feed;保存日志 3 年,防止 SEC 抽查。
- 实盘隔离:同一策略先跑 Paper Account 两周,确认延迟<500 ms、滑点<2 bps 再上小资金;用子账户单独放 10% 本金,爆仓也不伤筋动骨。
- 安全与伦理
- 不公开未经验证的“神单”,防止社群跟单导致闪崩。
- 在代码仓库加 DISCLAIMER,注明“仅供教育,非投资建议”。
- 定期回炉重训:模型知识半年就过期,财报季新词一堆,信号漂移及时修。
- 开放问题留给你
当模型给出强烈买入,而 MACD 刚死叉,你更愿意相信哪一边?是设计硬规则投票,还是让 LLM 连 MACD 值一起读再下结论?或者干脆用强化学习把冲突丢给智能体?欢迎在评论区交换思路。
- 写在最后
把 ChatGPT 当“情绪增量”而不是“圣杯”,是我这趟折腾的最大体会。整套流程我已经打包成可一键跑的模板,如果你也想亲手试试“让大模型帮你读新闻”,可以从这个动手实验开始——从0打造个人豆包实时通话AI(对,语音对话那套框架同样能用在实时读新闻播报上)。我这种 Python 半桶水都能跑通,相信你也可以。祝你回测曲线永远向左上角扬!
更多推荐



所有评论(0)