在瞬息万变的股市中,选股一直是投资者最头疼的难题之一。
传统方法依赖基本面分析、技术指标或“小道消息”,但面对海量数据和复杂市场情绪,普通人难免力不从心。
如今,借助DeepSeek这类AI工具,我们可以尝试用更高效、更科学的方式挖掘投资机会。毕竟DeepSeek是做量化出身的,它的模型是不是针对选股,交易会更得心应手点呢?
为什么用DeepSeek做选股策略?
-
数据驱动,告别“拍脑袋”决策
DeepSeek擅长处理多维数据(财报、行情、舆情、行业趋势等),能从历史规律中提炼有效信号,避免主观偏见。
-
动态学习,适应市场变化
通过机器学习模型,策略可实时跟踪市场风格切换,比如从“价值股”转向“成长股”时,AI能更快捕捉趋势。
-
高效回测,验证策略可行性
无需手动复盘,输入参数即可快速验证策略在不同周期、不同市场环境下的表现,降低试错成本。
如何用DeepSeek构建选股策略?
- 明确目标
-
你是追求长期价值(如ROE、现金流稳定)?
-
还是捕捉短期波动(如量价异动、事件驱动)?
目标不同,数据维度和模型设计天差地别。
下面是跟DeepSeek的交互过程:
1. 先是给出一个目标,中短期的交易,收益20%。
DeepSeek很快给出一个交易策略代码如下:
import pandas as pd``import numpy as np``import akshare as ak``from datetime import datetime, timedelta`` ``# ========== 参数设置 ==========``TRADE_DAYS = 60 # 分析周期(60个交易日)``TARGET_PROFIT = 0.20 # 目标盈利20%``STOP_LOSS = -0.05 # 止损-5%``SELECT_NUM = 5 # 选股数量`` ``# ========== 数据获取函数 ==========``def get_stock_data(code, end_date):` `start_date = (datetime.strptime(end_date, "%Y%m%d") -` `timedelta(days=TRADE_DAYS*2)).strftime("%Y%m%d")` `df = ak.stock_zh_a_hist(symbol=code, period="daily",` `start_date=start_date,` `end_date=end_date)` `return df.set_index('日期')`` ``# ========== 选股策略 ==========``def select_stocks(end_date):` `# 获取沪深A股列表` `stock_list = ak.stock_zh_a_spot_em()` `selected = []`` ` `# 指数过滤(示例用上证指数)` `index_data = ak.stock_zh_index_daily(symbol="sh000001")` `index_ma20 = index_data['close'].rolling(20).mean().iloc[-1]`` ` `# 仅当指数在20日均线上方时选股` `if index_data['close'].iloc[-1] > index_ma20:` `for code in stock_list['代码'].sample(200): # 随机抽样加速演示` `try:` `df = get_stock_data(code, end_date)` `if len(df) < TRADE_DAYS: continue`` ` `# 技术指标计算` `df['MA5'] = df['收盘'].rolling(5).mean()` `df['MA20'] = df['收盘'].rolling(20).mean()` `df['MACD'] = df['收盘'].ewm(span=12).mean() - df['收盘'].ewm(span=26).mean()` `df['RSI'] = 100 - (100 / (1 + df['收盘'].diff(1).clip(lower=0).rolling(14).mean() /` `df['收盘'].diff(1).clip(upper=0).abs().rolling(14).mean()))`` ` `last = df.iloc[-1]`` ` `# 选股条件(示例):` `# 1. 价格站上5日线` `# 2. MACD金叉` `# 3. RSI超卖回升` `# 4. 近期放量` `if (last['收盘'] > last['MA5'] and` `df['MACD'].iloc[-1] > df['MACD'].iloc[-2] and` `df['RSI'].iloc[-1] > 30 and df['RSI'].iloc[-1] > df['RSI'].iloc[-3] and` `last['成交量'] > df['成交量'].rolling(20).mean().iloc[-1]):` `selected.append(code)`` ` `if len(selected) >= SELECT_NUM: break`` ` `except Exception as e:` `continue`` ` `return selected[:SELECT_NUM]`` ``# ========== 交易策略 ==========``def trading_strategy(selected_stocks, capital=100000):` `positions = {}` `for code in selected_stocks:` `try:` `df = get_stock_data(code, datetime.now().strftime("%Y%m%d"))` `entry_price = df['收盘'].iloc[-1]` `position = {` `'entry_price': entry_price,` `'stop_loss': entry_price * (1 + STOP_LOSS),` `'take_profit': entry_price * (1 + TARGET_PROFIT),` `'shares': capital//SELECT_NUM // entry_price` `}` `positions[code] = position` `except:` `continue` `return positions`` ``# ========== 回测示例 ==========``def backtest(end_date='20230901'):` `selected = select_stocks(end_date)` `print(f"选股结果:{selected}")`` ` `positions = trading_strategy(selected)` `results = {}`` ` `for code, pos in positions.items():` `df = get_stock_data(code, end_date)` `future_data = df[df.index > datetime.strptime(end_date, "%Y%m%d")]`` ` `for idx, row in future_data.iterrows():` `if row['最低'] <= pos['stop_loss']:` `results[code] = STOP_LOSS` `break` `elif row['最高'] >= pos['take_profit']:` `results[code] = TARGET_PROFIT` `break` `else:` `results[code] = (row['收盘'] - pos['entry_price'])/pos['entry_price']`` ` `return results`` ``# ========== 执行回测 ==========``if __name__ == "__main__":` `backtest_results = backtest()` `print("\n回测结果:")` `for code, ret in backtest_results.items():` `print(f"{code}: {ret*100:.2f}%")` `print(f"平均收益率:{np.mean(list(backtest_results.values()))*100:.2f}%")
我们来理解一下这段代码,首先有一些可以调的参数:
# ========== 参数设置 ==========``TRADE_DAYS = 60 # 分析周期(60个交易日)``TARGET_PROFIT = 0.20 # 目标盈利20%``STOP_LOSS = -0.05 # 止损-5%``SELECT_NUM = 5 # 选股数量
那么脚本会根据你设置的回测时间,往前分析60个交易日,如果符合选股策略,就会被选入股票池,5个股票最多。
那么回测则是从回测时间点开始,往后交易60个交易日,如果触及了止损线就卖出,出发了止盈线也卖出,最终计算平均收益率。
但很快你会发现,这个脚本存在bug,比如日期的处理不对,跑不通,不要急。
第二步,我们让DeepSeek自己检查一下脚本有什么bug。
完整代码如下:
import pandas as pd``import numpy as np``import akshare as ak``from datetime import datetime, timedelta`` ``# ========== 参数设置 ==========``TRADE_DAYS = 60 # 分析周期``TARGET_PROFIT = 0.20 # 目标盈利``STOP_LOSS = -0.05 # 止损``SELECT_NUM = 3 # 选股数量(示例减少数量便于测试)``TEST_END_DATE = '20230701' # 确保有后续数据的时间点`` ``# ========== 数据获取(修复复权和日期问题)==========``def get_stock_data(code, end_date):` `start_date = (datetime.strptime(end_date, "%Y%m%d") -` `timedelta(days=TRADE_DAYS*2)).strftime("%Y%m%d")` `df = ak.stock_zh_a_hist(symbol=code, period="daily",` `start_date=start_date,` `end_date=end_date,` `adjust="hfq") # 关键修复:使用后复权` `df['日期'] = pd.to_datetime(df['日期']) # 转换日期类型` `return df.set_index('日期')`` ``# ========== 选股策略(修复指数数据问题)==========``def select_stocks(end_date):` `stock_list = ak.stock_zh_a_spot_em()` `selected = []`` ` `# 修复指数数据获取` `index_data = ak.stock_zh_index_daily(symbol="sh000001")` `index_data['date'] = pd.to_datetime(index_data['date']) # 转换日期列` `index_data = index_data.set_index('date').sort_index()`` ` `if len(index_data) < 20:` `print("指数数据不足")` `return []`` ` `index_ma20 = index_data['close'].rolling(20).mean().iloc[-1]` `current_close = index_data['close'].iloc[-1]`` ` `# 调试输出指数条件` `print(f"指数条件:当前收盘{current_close:.2f} vs MA20={index_ma20:.2f} | 条件{'满足' if current_close>index_ma20 else '不满足'}")`` ` `if current_close > index_ma20:` `sample_codes = stock_list['代码'].dropna().sample(min(50, len(stock_list))) # 限制抽样数量` `for code in sample_codes:` `try:` `df = get_stock_data(code, end_date)` `if len(df) < TRADE_DAYS:` `continue`` ` `# 计算技术指标` `df['MA5'] = df['收盘'].rolling(5).mean()` `df['MA20'] = df['收盘'].rolling(20).mean()`` ` `# 修复MACD计算` `exp12 = df['收盘'].ewm(span=12, adjust=False).mean()` `exp26 = df['收盘'].ewm(span=26, adjust=False).mean()` `df['MACD'] = exp12 - exp26` `df['Signal'] = df['MACD'].ewm(span=9, adjust=False).mean()`` ` `# 计算RSI` `delta = df['收盘'].diff()` `gain = delta.clip(lower=0)` `loss = -delta.clip(upper=0)` `avg_gain = gain.rolling(14).mean()` `avg_loss = loss.rolling(14).mean()` `df['RSI'] = 100 - (100 / (1 + avg_gain / avg_loss))`` ` `last = df.iloc[-1]` `prev = df.iloc[-2]`` ` `# 选股条件(放宽条件示例):` `cond1 = last['收盘'] > last['MA5'] # 价格在5日线上` `cond2 = (last['MACD'] > last['Signal']) & (prev['MACD'] < prev['Signal']) # MACD金叉` `cond3 = last['RSI'] > 30 # RSI脱离超卖` `cond4 = last['成交量'] > df['成交量'].rolling(20).mean().iloc[-1] # 放量`` ` `if cond1 and cond2 and cond3 and cond4:` `print(f"选中股票:{code} | 价格{last['收盘']:.2f} RSI={last['RSI']:.1f} 成交量比率{last['成交量']/df['成交量'].mean():.2f}")` `selected.append(code)`` ` `if len(selected) >= SELECT_NUM:` `break`` ` `except Exception as e:` `# print(f"处理{code}时出错:{str(e)}")` `continue`` ` `return selected[:SELECT_NUM]`` ``# ========== 回测模块(修复日期比较)==========``def backtest(end_date=TEST_END_DATE):` `selected = select_stocks(end_date)` `print(f"\n最终选股:{selected}")`` ` `if not selected:` `print("无符合条件股票")` `return {}`` ` `results = {}` `test_end_dt = datetime.strptime(end_date, "%Y%m%d")`` ` `for code in selected:` `try:` `# 获取回测期之后20个交易日的数据` `df = get_stock_data(code, (test_end_dt + timedelta(days=40)).strftime("%Y%m%d"))` `df = df[df.index > test_end_dt] # 选取交易日期之后的数据`` ` `if df.empty:` `print(f"{code} 无后续交易数据")` `continue`` ` `entry_price = df.iloc[0]['开盘'] # 假设以次日开盘价买入` `stop_loss = entry_price * (1 + STOP_LOSS)` `take_profit = entry_price * (1 + TARGET_PROFIT)`` ` `exit_reason = None` `for idx, row in df.iterrows():` `if row['最低'] <= stop_loss:` `exit_reason = f"止损 @ {row['最低']:.2f}"` `ret = STOP_LOSS` `break` `elif row['最高'] >= take_profit:` `exit_reason = f"止盈 @ {row['最高']:.2f}"` `ret = TARGET_PROFIT` `break` `else:` `exit_reason = f"持有到期 @ {row['收盘']:.2f}"` `ret = (row['收盘'] - entry_price) / entry_price`` ` `results[code] = ret` `print(f"{code}: {exit_reason} | 收益率{ret*100:.1f}%")`` ` `except Exception as e:` `print(f"回测{code}出错:{str(e)}")` `continue`` ` `return results`` ``# ========== 执行 ==========``if __name__ == "__main__":` `print("开始回测...")` `results = backtest()`` ` `if results:` `print("\n=== 回测结果 ===")` `total_return = np.mean(list(results.values()))` `win_rate = len([r for r in results.values() if r >0]) / len(results)` `print(f"平均收益率:{total_return*100:.2f}%")` `print(f"胜率:{win_rate*100:.2f}%")` `else:` `print("无交易记录")
这时候你拿这段脚本去跑会发现,经常会选不出股票,要么就是选股的条件太苛刻了,要么就是数据点不够。
第三步,让DeepSeek再优化一下策略。
# 修改数据获取函数,增加重试机制和异常处理``def get_stock_data(code, end_date, max_retry=3):` `for _ in range(max_retry):` `try:` `df = ak.stock_zh_a_hist(symbol=code, period="daily",` `adjust="hfq", end_date=end_date)` `df['日期'] = pd.to_datetime(df['日期'])` `# 过滤停牌数据(成交量连续为0)` `if df['成交量'].iloc[-3:].sum() == 0:` `return None` `return df.set_index('日期').sort_index()` `except:` `continue` `return None
DeepSeek把每个函数都优化了一遍。整理好的完整的脚本我贴在这里:
import pandas as pd``import numpy as np``import akshare as ak``from datetime import datetime, timedelta``# ========== 参数设置 ==========``TRADE_DAYS = 60``TARGET_PROFIT = 0.20``STOP_LOSS = -0.05``SELECT_NUM = 5``TEST_DATES = ['20230103', '20230403', '20230703'] # 修正后的测试日期``# ========== 数据获取(增强版) ==========``def get_stock_data(code, end_date):` `start_date = (datetime.strptime(end_date, "%Y%m%d") -` `timedelta(days=TRADE_DAYS*2)).strftime("%Y%m%d")` `try:` `# 优先获取后复权数据` `df = ak.stock_zh_a_hist(symbol=code, period="daily",` `start_date=start_date, end_date=end_date,` `adjust="hfq")` `if df.empty:` `df = ak.stock_zh_a_hist(symbol=code, period="daily", # fallback` `start_date=start_date, end_date=end_date)` `df['日期'] = pd.to_datetime(df['日期'])` `df = df[df['成交量'] > 0] # 过滤停牌日` `return df.set_index('日期').sort_index()` `except:` `return None``# ========== 选股策略(调试版) ==========``def select_stocks(end_date):` `print(f"\n>>> 开始选股 {end_date}")` `# 获取股票列表(过滤ST/北交所)` `stock_list = ak.stock_zh_a_spot_em()` `stock_list = stock_list[~stock_list['名称'].str.contains('ST')]` `stock_list = stock_list[~stock_list['代码'].str.startswith(('83', '43'))]` `# 获取指数数据` `index_data = ak.stock_zh_index_daily(symbol="sh000001")` `index_data['date'] = pd.to_datetime(index_data['date'])` `index_data = index_data.set_index('date').sort_index()` `if len(index_data) < 60:` `print("指数数据不足")` `return []` `# 市场状态判断` `index_ma60 = index_data['close'].rolling(60).mean().iloc[-1]` `current_close = index_data['close'].iloc[-1]` `market_status = 'bull' if current_close > index_ma60 else 'bear'` `print(f"上证指数 {current_close:.2f} vs MA60 {index_ma60:.2f} → 市场状态:{market_status}")` `selected = []` `sample_size = min(500, len(stock_list)) # 扩大抽样范围` `for code in stock_list['代码'].sample(sample_size):` `df = get_stock_data(code, end_date)` `if df is None or len(df) < 60:` `continue` `# 指标计算` `close = df['收盘']` `df['MA5'] = close.rolling(5).mean()` `df['MA20'] = close.rolling(20).mean()` `df['MA60'] = close.rolling(60).mean()` `# MACD` `exp12 = close.ewm(span=12, adjust=False).mean()` `exp26 = close.ewm(span=26, adjust=False).mean()` `df['MACD'] = exp12 - exp26` `df['MACD_Signal'] = df['MACD'].ewm(span=9, adjust=False).mean()` `# 量能条件` `df['Volume_Ratio'] = df['成交量'] / df['成交量'].rolling(20).mean()` `last = df.iloc[-1]` `prev = df.iloc[-2]` `# 动态选股条件` `cond_price = last['收盘'] > last['MA20']` `cond_macd = (last['MACD'] > last['MACD_Signal']) & (prev['MACD'] < prev['MACD_Signal'])` `cond_volume = last['Volume_Ratio'] > 1.2` `# 趋势条件(熊市放宽)` `if market_status == 'bull':` `cond_trend = last['收盘'] > last['MA60'] * 0.97 # 允许3%误差` `else:` `cond_trend = close.pct_change(20).iloc[-1] > 0 # 20日上涨` `if all([cond_price, cond_macd, cond_volume, cond_trend]):` `print(f"符合条件:{code} | 价格{last['收盘']:.2f} 量比{last['Volume_Ratio']:.1f}x")` `selected.append(code)` `if len(selected) >= SELECT_NUM:` `break` `print(f"最终选股:{selected}")` `return selected``# ========== 执行回测 ==========``if __name__ == "__main__":` `for test_date in TEST_DATES:` `print(f"\n=== 回测日期 {test_date} ===")` `selected = select_stocks(test_date)` `if not selected:` `print("无符合条件股票")` `continue` `# 简单收益率计算(持有20个交易日)` `results = {}` `for code in selected:` `df = get_stock_data(code, (datetime.strptime(test_date, "%Y%m%d") + timedelta(days=40)).strftime("%Y%m%d"))` `if df is None or len(df) < 20:` `continue` `entry = df.iloc[0]['收盘']` `exit = df.iloc[20]['收盘']` `ret = (exit - entry) / entry` `results[code] = ret` `print(f"{code}: {ret*100:.1f}%")` `if results:` `avg_ret = np.mean(list(results.values())) * 100` `print(f"平均收益率:{avg_ret:.1f}%")
运行前需要准备好环境:
pip install akshare pandas numpy --upgrade
我运行的结果如下:
感觉这个胜率似乎还可以?
除此之外,DeepSeek还建议可以使用实时监控模式,每天自动运行,选到股了再发邮件到自己的邮箱,很可以!
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。