用 Python 三剑客玩转量化交易:从数据获取到策略回测,一篇通关实战指南

目录

引言

一、为什么这三个库是量化交易的 “黄金组合”?

二、环境搭建:3 分钟配齐量化工具包

2.1 安装命令

2.2 验证安装

2.3 准备工作:了解股票代码格式

三、数据获取:用 yfinance 批量下载股票数据

3.1 获取单只股票的历史价格

3.2 批量获取多只股票数据

3.3 获取关键财务数据(辅助基本面分析)

四、数据处理:用 Pandas 计算量化指标

4.1 数据清洗(处理缺失值和异常值)

4.2 计算核心技术指标

4.2.1 均线(MA):判断趋势

4.2.2 日收益率:衡量波动

4.2.3 相对强弱指数(RSI):判断超买超卖

4.2.4 移动平均收敛散度(MACD):判断趋势强度

4.3 保存处理后的数据(方便复用)

五、可视化分析:用 Matplotlib 展示数据与指标

5.1 绘制价格与均线走势

5.2 绘制 RSI 指标(判断超买超卖)

5.3 绘制多股票收益率对比

六、策略构建与回测:用三剑客实现双均线策略

6.1 策略逻辑

6.2 生成交易信号

6.3 回测策略收益

6.4 可视化回测结果

6.5 策略评价指标

七、实战案例:A 股股票筛选与策略回测

7.1 筛选股票(结合基本面和技术面)

7.2 回测选中股票的双均线策略

八、避坑指南:量化新手常踩的 6 个坑

九、进阶方向:从入门到实战的升级路径

十、总结:量化交易的核心不是代码,而是逻辑


 

class 卑微码农:
    def __init__(self):
        self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
        self.发量 = 100  # 初始发量
        self.咖啡因耐受度 = '极限'
        
    def 修Bug(self, bug):
        try:
            # 试图用玄学解决问题
            if bug.严重程度 == '离谱':
                print("这一定是环境问题!")
            else:
                print("让我看看是谁又没写注释...哦,是我自己。")
        except Exception as e:
            # 如果try块都救不了,那就...
            print("重启一下试试?")
            self.发量 -= 1  # 每解决一个bug,头发-1
 
 
# 实例化一个我
我 = 卑微码农()

引言

如果你是股票投资者,可能曾有过这样的困惑:

  • 手动分析几十只股票的历史走势,耗时又容易遗漏关键信号;
  • 听说 “均线交叉”“动量策略” 能赚钱,却不知道怎么验证效果;
  • 想自动化跟踪股价、计算指标,却被复杂的编程门槛劝退。

其实,用 Python 的三个 “神器”——yfinance(数据获取)、Pandas(数据处理)、Matplotlib(可视化),就能从零搭建一套量化交易系统:免费获取股票数据、自动计算技术指标、回测交易策略、可视化分析结果,全程代码可复用,新手也能快速上手。

上述三个工具说明,请参考之前的博客。

Python pandas 实战:从数据处理到分析可视化,搞定数据分析核心技能-优快云博客

用 Python yfinance 玩转金融数据:从股票爬取到分析可视化的实战指南-优快云博客

Python Matplotlib 可视化全攻略:从基础图表到交互可视化,搞定数据呈现核心技能-优快云博客

本文从实际场景出发,通过 “数据获取→指标计算→策略构建→回测分析” 的完整流程,手把手教你用这三个库实现量化交易,附可直接运行的代码和详细注释,帮你避开 90% 的新手坑。

一、为什么这三个库是量化交易的 “黄金组合”?

量化交易的核心流程是 “数据→分析→决策→验证”,而 Pandas、yfinance、Matplotlib 恰好分别对应这一流程的关键环节:

  • yfinance:解决 “数据从哪来” 的问题。免费获取全球股票、指数、加密货币的历史价格、财务数据,无需 API 密钥,一行代码就能下载 10 年日线数据,比付费接口更适合个人用户。
  • Pandas:解决 “数据怎么处理” 的问题。作为 Python 数据处理 “瑞士军刀”,能轻松清洗数据、计算技术指标(均线、MACD、RSI)、生成交易信号,让复杂的时间序列分析变得简单。
  • Matplotlib:解决 “结果怎么展示” 的问题。将枯燥的数字转化为直观的图表(价格走势、策略信号、回测收益),帮你快速理解策略效果,发现潜在问题。

这三个库的组合优势在于:免费开源、生态兼容、门槛低。无需懂复杂的金融工程,只要会基础 Python,就能搭建起属于自己的量化系统。

二、环境搭建:3 分钟配齐量化工具包

在开始前,需要安装三个核心库,推荐搭配 Python 3.8 及以上版本(兼容性更好)。

2.1 安装命令

# 基础安装(推荐用pip)
pip install pandas yfinance matplotlib

# 安装指定版本(避免版本兼容问题)
pip install pandas==2.1.4 yfinance==0.2.31 matplotlib==3.8.2

2.2 验证安装

打开 Python 环境(推荐用 Jupyter Notebook,方便分步运行),输入以下代码,无报错则安装成功:

import pandas as pd       # 数据处理
import yfinance as yf     # 数据获取
import matplotlib.pyplot as plt  # 可视化

# 查看版本(确保与示例兼容)
print(f"pandas版本:{pd.__version__}")
print(f"yfinance版本:{yf.__version__}")
print(f"matplotlib版本:{plt.__version__}")

2.3 准备工作:了解股票代码格式

用 yfinance 获取数据需要股票代码,不同市场的代码格式不同(可在雅虎财经官网查询):

  • 美股:直接用代码(如苹果 AAPL、微软 MSFT);
  • A 股:上交所加.SS(如贵州茅台 600519.SS),深交所加.SZ(如宁德时代 300750.SZ);
  • 指数:标普 500 是^GSPC,沪深 300 是000300.SS

三、数据获取:用 yfinance 批量下载股票数据

量化的第一步是获取高质量数据。yfinance 能轻松获取历史价格、实时报价、分红拆股等信息,我们以 “特斯拉(TSLA)” 为例,演示核心用法。

3.1 获取单只股票的历史价格

Ticker.history()方法获取历史数据,支持自定义时间范围和周期(日线、周线等):

# 1. 创建特斯拉的Ticker对象
tsla = yf.Ticker("TSLA")

# 2. 获取2020-2023年的日线数据(包含开盘价、最高价、最低价、收盘价、成交量)
# period:时间范围(1y=1年,5y=5年,max=全部历史)
# interval:周期(1d=日线,1wk=周线,1mo=月线)
tsla_hist = tsla.history(period="5y", interval="1d", auto_adjust=True)

# 3. 查看数据结构(pandas DataFrame格式,索引为日期)
print("数据形状:", tsla_hist.shape)  # 输出(1258, 5),表示1258行数据,5列指标
print("\n前5行数据:")
print(tsla_hist.head())

输出结果中,Open(开盘价)、High(最高价)、Low(最低价)、Close(收盘价)、Volume(成交量)是量化分析的核心指标。auto_adjust=True表示启用后复权(处理分红拆股导致的价格断层),推荐始终开启。

3.2 批量获取多只股票数据

分析多只股票(如对比科技巨头)时,用yf.download()更高效:

# 1. 定义要分析的股票列表(苹果、微软、谷歌、亚马逊)
tickers = ["AAPL", "MSFT", "GOOGL", "AMZN"]

# 2. 批量获取2023年数据
multi_stocks = yf.download(
    tickers=tickers,
    start="2023-01-01",
    end="2023-12-31",
    interval="1d",
    auto_adjust=True
)

# 3. 查看数据结构(多层索引:第一层是指标,第二层是股票代码)
print("数据列名:", multi_stocks.columns.names)  # 输出['Price', 'Ticker']
print("\n收盘价子表前5行:")
print(multi_stocks["Close"].head())  # 提取所有股票的收盘价

3.3 获取关键财务数据(辅助基本面分析)

除了价格数据,yfinance 还能获取市盈率、市值等基本面指标,用于筛选股票:

# 获取苹果公司的基本信息(返回字典)
aapl = yf.Ticker("AAPL")
aapl_info = aapl.info

# 提取关键基本面指标
print(f"公司名称:{aapl_info['longName']}")
print(f"市值(美元):{aapl_info['marketCap'] / 1e9:.2f}亿")  # 转换为亿
print(f"动态市盈率:{aapl_info['trailingPE']:.2f}")
print(f"股息率(%):{aapl_info['dividendYield'] * 100:.2f}")
print(f"52周最高价:{aapl_info['fiftyTwoWeekHigh']:.2f}美元")

四、数据处理:用 Pandas 计算量化指标

获取数据后,需要用 Pandas 计算技术指标(如均线、收益率),这是生成交易信号的基础。我们以特斯拉数据为例,演示核心操作。

4.1 数据清洗(处理缺失值和异常值)

原始数据可能存在缺失(如节假日休市),需要先清洗:

# 1. 检查缺失值
print("缺失值数量:\n", tsla_hist.isnull().sum())

# 2. 处理缺失值:用前一天的数据填充(适合时间序列)
tsla_clean = tsla_hist.fillna(method="ffill")

# 3. 检查异常值:收盘价不可能为负
tsla_clean = tsla_clean[tsla_clean["Close"] > 0]

print("清洗后数据形状:", tsla_clean.shape)

4.2 计算核心技术指标

技术指标是量化策略的 “信号源”,以下是最常用的 4 个指标计算方法:

4.2.1 均线(MA):判断趋势

均线是某段时间内收盘价的平均值,5 日均线反映短期趋势,20 日均线反映中期趋势:

# 计算5日均线和20日均线
tsla_clean["MA5"] = tsla_clean["Close"].rolling(window=5).mean()  # 前5天收盘价的均值
tsla_clean["MA20"] = tsla_clean["Close"].rolling(window=20).mean()

# 查看结果(只显示收盘价和均线列)
print(tsla_clean[["Close", "MA5", "MA20"]].tail())

4.2.2 日收益率:衡量波动

日收益率 =(当日收盘价 - 前一日收盘价)/ 前一日收盘价,反映单日涨跌幅度:

# 计算日收益率(shift(1)表示取前一行数据)
tsla_clean["Daily_Return"] = (tsla_clean["Close"] - tsla_clean["Close"].shift(1)) / tsla_clean["Close"].shift(1)

# 查看结果(前几行可能为NaN,因为没有前一天数据)
print(tsla_clean[["Close", "Daily_Return"]].head())

4.2.3 相对强弱指数(RSI):判断超买超卖

RSI 取值 0-100,通常 > 70 视为超买(可能下跌),<30 视为超卖(可能上涨):

def calculate_rsi(data, window=14):
    """计算RSI指标"""
    # 计算价格变动
    delta = data["Close"].diff(1)
    # 区分上涨和下跌
    gain = delta.where(delta > 0, 0)  # 上涨幅度(下跌时记为0)
    loss = -delta.where(delta < 0, 0)  # 下跌幅度(上涨时记为0)
    # 计算平均涨跌幅
    avg_gain = gain.rolling(window=window).mean()
    avg_loss = loss.rolling(window=window).mean()
    # 计算RSI
    rs = avg_gain / avg_loss
    rsi = 100 - (100 / (1 + rs))
    return rsi

# 计算14天RSI
tsla_clean["RSI14"] = calculate_rsi(tsla_clean)
print(tsla_clean[["Close", "RSI14"]].tail())

4.2.4 移动平均收敛散度(MACD):判断趋势强度

MACD 由快线(DIF)、慢线(DEA)、柱状线(MACD)组成,交叉信号常用于判断买入卖出点:

def calculate_macd(data, fast_period=12, slow_period=26, signal_period=9):
    """计算MACD指标"""
    # 计算快慢线
    ema_fast = data["Close"].ewm(span=fast_period, adjust=False).mean()  # 12日指数移动平均
    ema_slow = data["Close"].ewm(span=slow_period, adjust=False).mean()  # 26日指数移动平均
    data["MACD_DIF"] = ema_fast - ema_slow  # 快线
    data["MACD_DEA"] = data["MACD_DIF"].ewm(span=signal_period, adjust=False).mean()  # 慢线
    data["MACD_Bar"] = data["MACD_DIF"] - data["MACD_DEA"]  # 柱状线
    return data

# 计算MACD
tsla_clean = calculate_macd(tsla_clean)
print(tsla_clean[["Close", "MACD_DIF", "MACD_DEA", "MACD_Bar"]].tail())

4.3 保存处理后的数据(方便复用)

处理好的数据可保存为 CSV,避免重复下载:

# 保存为CSV(索引为日期,保留)
tsla_clean.to_csv("tsla_5y_data.csv")

# 后续可直接读取
# tsla_clean = pd.read_csv("tsla_5y_data.csv", index_col=0, parse_dates=True)

五、可视化分析:用 Matplotlib 展示数据与指标

光有数据和指标不够直观,用 Matplotlib 绘制图表,能快速发现趋势和信号。

5.1 绘制价格与均线走势

# 设置中文显示(避免乱码)
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams["axes.unicode_minus"] = False  # 解决负号显示问题

# 1. 创建画布
fig, ax = plt.subplots(figsize=(12, 6))

# 2. 绘制收盘价和均线
ax.plot(tsla_clean.index, tsla_clean["Close"], label="收盘价", alpha=0.7, linewidth=1)
ax.plot(tsla_clean.index, tsla_clean["MA5"], label="5日均线", color="orange", linewidth=2)
ax.plot(tsla_clean.index, tsla_clean["MA20"], label="20日均线", color="green", linewidth=2)

# 3. 添加标题和标签
ax.set_title("特斯拉股价与均线走势(2019-2024)", fontsize=15)
ax.set_xlabel("日期")
ax.set_ylabel("价格(美元)")

# 4. 添加网格和图例
ax.grid(alpha=0.3)
ax.legend()

# 5. 自动调整日期标签(避免重叠)
fig.autofmt_xdate()

plt.show()

从图中可直观看到:当 5 日均线上穿 20 日均线(金叉),股价往往上涨;下穿(死叉)则可能下跌 —— 这就是均线策略的核心逻辑。

5.2 绘制 RSI 指标(判断超买超卖)

# 创建上下两个子图(共享x轴)
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), sharex=True, gridspec_kw={"height_ratios": [3, 1]})

# 上子图:收盘价
ax1.plot(tsla_clean.index, tsla_clean["Close"], label="收盘价", color="blue")
ax1.set_title("特斯拉股价与RSI指标", fontsize=15)
ax1.legend(loc="upper left")
ax1.grid(alpha=0.3)

# 下子图:RSI
ax2.plot(tsla_clean.index, tsla_clean["RSI14"], label="RSI(14)", color="purple")
ax2.axhline(70, color="red", linestyle="--", alpha=0.5)  # 超买线
ax2.axhline(30, color="green", linestyle="--", alpha=0.5)  # 超卖线
ax2.set_ylabel("RSI值")
ax2.legend(loc="upper left")
ax2.grid(alpha=0.3)

# 调整布局
plt.tight_layout()
plt.show()

图中红色虚线(70)和绿色虚线(30)是关键阈值:当 RSI 突破 70 后回落,可能是下跌信号;跌破 30 后回升,可能是上涨信号。

5.3 绘制多股票收益率对比

# 1. 提取2023年四只科技股的收盘价
close_prices = multi_stocks["Close"]

# 2. 计算累计收益率(以第一天为基准,标准化为100)
normalized = (close_prices / close_prices.iloc[0] * 100)

# 3. 绘制收益率曲线
plt.figure(figsize=(12, 6))
for stock in normalized.columns:
    plt.plot(normalized.index, normalized[stock], label=stock, linewidth=2)

plt.title("2023年科技股累计收益率对比(初始值=100)", fontsize=15)
plt.xlabel("日期")
plt.ylabel("累计收益(初始100)")
plt.legend()
plt.grid(alpha=0.3)
fig.autofmt_xdate()
plt.show()

通过收益率曲线,能快速看出哪只股票表现更好(如 2023 年英伟达涨幅远超其他科技股)。

六、策略构建与回测:用三剑客实现双均线策略

有了数据、指标和可视化基础,我们来构建一个经典的 “双均线策略”,并回测其效果。

6.1 策略逻辑

双均线策略是量化入门的经典策略,逻辑简单且有效:

  • 买入信号:短期均线(如 5 日均线)上穿长期均线(如 20 日均线)—— 金叉;
  • 卖出信号:短期均线下穿长期均线 —— 死叉;
  • 持仓规则:发出买入信号时全仓买入,卖出信号时清仓,不考虑止损。

6.2 生成交易信号

用 Pandas 标记买入(1)和卖出(-1)信号:

# 1. 复制数据(避免修改原始数据)
strategy_data = tsla_clean[["Close", "MA5", "MA20"]].copy()

# 2. 生成信号:金叉=买入(1),死叉=卖出(-1)
# 先判断前一天的均线位置关系(避免未来函数)
strategy_data["MA5_prev"] = strategy_data["MA5"].shift(1)
strategy_data["MA20_prev"] = strategy_data["MA20"].shift(1)

# 金叉:今天MA5 > MA20,且昨天MA5 <= MA20
strategy_data["Buy_Signal"] = ((strategy_data["MA5"] > strategy_data["MA20"]) & 
                              (strategy_data["MA5_prev"] <= strategy_data["MA20_prev"])).astype(int)

# 死叉:今天MA5 < MA20,且昨天MA5 >= MA20
strategy_data["Sell_Signal"] = ((strategy_data["MA5"] < strategy_data["MA20"]) & 
                               (strategy_data["MA5_prev"] >= strategy_data["MA20_prev"])).astype(int)

# 合并为交易信号(1=买入,-1=卖出,0=无操作)
strategy_data["Signal"] = strategy_data["Buy_Signal"] - strategy_data["Sell_Signal"]

# 查看信号示例(筛选有信号的行)
print(strategy_data[strategy_data["Signal"] != 0][["Close", "MA5", "MA20", "Signal"]].tail())

6.3 回测策略收益

计算策略的累计收益,并与 “买入持有” 策略对比:

# 1. 初始化持仓和资金
strategy_data["Position"] = 0  # 持仓状态:0=空仓,1=满仓
strategy_data["Cash"] = 100000  # 初始资金10万
strategy_data["Shares"] = 0  # 持有股数
strategy_data["Total_Asset"] = 100000  # 总资产(现金+股票市值)

# 2. 遍历每一天,执行交易
for i in range(1, len(strategy_data)):
    # 获取当前和前一天的数据
    prev = strategy_data.iloc[i-1]
    current = strategy_data.iloc[i]
    
    # 继承前一天的持仓和资产
    strategy_data.at[strategy_data.index[i], "Position"] = prev["Position"]
    strategy_data.at[strategy_data.index[i], "Cash"] = prev["Cash"]
    strategy_data.at[strategy_data.index[i], "Shares"] = prev["Shares"]
    
    # 买入信号且空仓:用全部现金买入
    if current["Signal"] == 1 and prev["Position"] == 0:
        # 计算可买股数(取整数,不考虑手续费)
        shares = int(prev["Cash"] / current["Close"])
        cost = shares * current["Close"]
        # 更新状态
        strategy_data.at[strategy_data.index[i], "Shares"] = shares
        strategy_data.at[strategy_data.index[i], "Cash"] = prev["Cash"] - cost
        strategy_data.at[strategy_data.index[i], "Position"] = 1
    
    # 卖出信号且满仓:清仓
    elif current["Signal"] == -1 and prev["Position"] == 1:
        # 计算卖出收益
        cash = prev["Cash"] + prev["Shares"] * current["Close"]
        # 更新状态
        strategy_data.at[strategy_data.index[i], "Shares"] = 0
        strategy_data.at[strategy_data.index[i], "Cash"] = cash
        strategy_data.at[strategy_data.index[i], "Position"] = 0
    
    # 计算总资产
    total_asset = strategy_data.at[strategy_data.index[i], "Cash"] + \
                  strategy_data.at[strategy_data.index[i], "Shares"] * current["Close"]
    strategy_data.at[strategy_data.index[i], "Total_Asset"] = total_asset

# 3. 计算买入持有策略的收益(第一天买入,持有至最后)
first_price = strategy_data["Close"].iloc[0]
last_price = strategy_data["Close"].iloc[-1]
hold_shares = int(100000 / first_price)
hold_asset = 100000 - hold_shares * first_price + hold_shares * last_price
strategy_data["Hold_Asset"] = hold_asset  # 买入持有策略的最终资产(保持不变)

print(f"策略最终总资产:{strategy_data['Total_Asset'].iloc[-1]:.2f}美元")
print(f"买入持有最终资产:{hold_asset:.2f}美元")

6.4 可视化回测结果

plt.figure(figsize=(12, 6))

# 绘制总资产和买入持有资产
plt.plot(strategy_data.index, strategy_data["Total_Asset"], label="双均线策略", color="blue")
plt.plot(strategy_data.index, strategy_data["Hold_Asset"], label="买入持有", color="gray", linestyle="--")

# 标记买入和卖出点
buy_signals = strategy_data[strategy_data["Signal"] == 1]
sell_signals = strategy_data[strategy_data["Signal"] == -1]
plt.scatter(buy_signals.index, buy_signals["Total_Asset"], marker="^", color="green", label="买入", s=100)
plt.scatter(sell_signals.index, sell_signals["Total_Asset"], marker="v", color="red", label="卖出", s=100)

plt.title("双均线策略与买入持有收益对比", fontsize=15)
plt.xlabel("日期")
plt.ylabel("总资产(美元)")
plt.legend()
plt.grid(alpha=0.3)
fig.autofmt_xdate()
plt.show()

6.5 策略评价指标

光看总资产不够,还需计算以下指标评估策略优劣:

def evaluate_strategy(data):
    """计算策略评价指标"""
    # 1. 总收益率
    total_return = (data["Total_Asset"].iloc[-1] / data["Total_Asset"].iloc[0] - 1) * 100
    
    # 2. 年化收益率(假设一年252个交易日)
    days = len(data)
    annual_return = ((1 + total_return / 100) ** (252 / days) - 1) * 100
    
    # 3. 胜率(盈利交易次数 / 总交易次数)
    trades = data[(data["Signal"] == 1) | (data["Signal"] == -1)]
    if len(trades) == 0:
        win_rate = 0
    else:
        # 简化计算:每次买入后下次卖出为一次完整交易
        buy_points = data[data["Signal"] == 1].index
        sell_points = data[data["Signal"] == -1].index
        profitable = 0
        for b, s in zip(buy_points, sell_points):
            if data.loc[s, "Close"] > data.loc[b, "Close"]:
                profitable += 1
        win_rate = (profitable / len(buy_points)) * 100 if len(buy_points) > 0 else 0
    
    # 4. 最大回撤(历史最高点到最低点的跌幅)
    rolling_max = data["Total_Asset"].cummax()  # 累计最大值
    drawdown = (data["Total_Asset"] - rolling_max) / rolling_max  # 回撤率
    max_drawdown = drawdown.min() * 100  # 最大回撤(负的)
    
    return {
        "总收益率(%)": round(total_return, 2),
        "年化收益率(%)": round(annual_return, 2),
        "胜率(%)": round(win_rate, 2),
        "最大回撤(%)": round(max_drawdown, 2)
    }

# 计算并打印评价指标
metrics = evaluate_strategy(strategy_data)
print("策略评价指标:")
for key, value in metrics.items():
    print(f"{key}: {value}")

指标解读

  • 总收益率:策略期间的整体收益;
  • 年化收益率:将收益标准化到一年,方便跨周期对比;
  • 胜率:盈利交易占比,越高说明策略越稳定;
  • 最大回撤:历史上从最高点下跌的最大幅度,越小说明风险越低。

七、实战案例:A 股股票筛选与策略回测

前面以美股为例,现在用 A 股数据实战:筛选出符合 “低市盈率 + 高动量” 的股票,并用双均线策略回测。

7.1 筛选股票(结合基本面和技术面)

# 1. 定义A股股票池(茅台、宁德时代、比亚迪、招商银行)
a股_tickers = ["600519.SS", "300750.SZ", "002594.SZ", "600036.SS"]
names = ["贵州茅台", "宁德时代", "比亚迪", "招商银行"]

# 2. 批量获取基本面数据,筛选低市盈率(<30)的股票
filtered = []
for ticker, name in zip(a股_tickers, names):
    stock = yf.Ticker(ticker)
    info = stock.info
    pe = info.get("trailingPE", float("inf"))  # 动态市盈率
    if pe < 30:  # 筛选市盈率<30的股票
        filtered.append((ticker, name, pe))

print("低市盈率股票筛选结果:")
for t, n, p in filtered:
    print(f"{n}({t}):市盈率={p:.2f}")

# 3. 从筛选结果中,选择近3个月涨幅最高的股票(高动量)
best_stock = None
max_return = -float("inf")
for t, n, p in filtered:
    # 获取近3个月数据
    hist = yf.download(t, period="3mo", auto_adjust=True)
    # 计算涨幅
    return_rate = (hist["Close"].iloc[-1] / hist["Close"].iloc[0] - 1) * 100
    print(f"{n}近3个月涨幅:{return_rate:.2f}%")
    if return_rate > max_return:
        max_return = return_rate
        best_stock = (t, n)

print(f"\n最终选择:{best_stock[1]}({best_stock[0]}),近3个月涨幅{max_return:.2f}%")

7.2 回测选中股票的双均线策略

# 1. 获取选中股票的5年数据
ticker, name = best_stock
a股_data = yf.download(ticker, period="5y", auto_adjust=True)

# 2. 计算均线
a股_data["MA5"] = a股_data["Close"].rolling(5).mean()
a股_data["MA20"] = a股_data["Close"].rolling(20).mean()

# 3. 生成信号(复用前面的逻辑)
a股_data["MA5_prev"] = a股_data["MA5"].shift(1)
a股_data["MA20_prev"] = a股_data["MA20"].shift(1)
a股_data["Buy_Signal"] = ((a股_data["MA5"] > a股_data["MA20"]) & 
                         (a股_data["MA5_prev"] <= a股_data["MA20_prev"])).astype(int)
a股_data["Sell_Signal"] = ((a股_data["MA5"] < a股_data["MA20"]) & 
                          (a股_data["MA5_prev"] >= a股_data["MA20_prev"])).astype(int)
a股_data["Signal"] = a股_data["Buy_Signal"] - a股_data["Sell_Signal"]

# 4. 回测(复用前面的回测逻辑)
a股_data["Position"] = 0
a股_data["Cash"] = 100000
a股_data["Shares"] = 0
a股_data["Total_Asset"] = 100000

for i in range(1, len(a股_data)):
    prev = a股_data.iloc[i-1]
    current = a股_data.iloc[i]
    
    a股_data.at[a股_data.index[i], "Position"] = prev["Position"]
    a股_data.at[a股_data.index[i], "Cash"] = prev["Cash"]
    a股_data.at[a股_data.index[i], "Shares"] = prev["Shares"]
    
    if current["Signal"] == 1 and prev["Position"] == 0:
        shares = int(prev["Cash"] / current["Close"])
        cost = shares * current["Close"]
        a股_data.at[a股_data.index[i], "Shares"] = shares
        a股_data.at[a股_data.index[i], "Cash"] = prev["Cash"] - cost
        a股_data.at[a股_data.index[i], "Position"] = 1
    elif current["Signal"] == -1 and prev["Position"] == 1:
        cash = prev["Cash"] + prev["Shares"] * current["Close"]
        a股_data.at[a股_data.index[i], "Shares"] = 0
        a股_data.at[a股_data.index[i], "Cash"] = cash
        a股_data.at[a股_data.index[i], "Position"] = 0
    
    total_asset = a股_data.at[a股_data.index[i], "Cash"] + \
                  a股_data.at[a股_data.index[i], "Shares"] * current["Close"]
    a股_data.at[a股_data.index[i], "Total_Asset"] = total_asset

# 5. 可视化结果
plt.figure(figsize=(12, 6))
plt.plot(a股_data.index, a股_data["Total_Asset"], label="双均线策略", color="blue")
buy_signals = a股_data[a股_data["Signal"] == 1]
sell_signals = a股_data[a股_data["Signal"] == -1]
plt.scatter(buy_signals.index, buy_signals["Total_Asset"], marker="^", color="green", label="买入", s=100)
plt.scatter(sell_signals.index, sell_signals["Total_Asset"], marker="v", color="red", label="卖出", s=100)
plt.title(f"{name}双均线策略回测结果", fontsize=15)
plt.xlabel("日期")
plt.ylabel("总资产(人民币)")
plt.legend()
plt.grid(alpha=0.3)
fig.autofmt_xdate()
plt.show()

# 6. 打印评价指标
a股_metrics = evaluate_strategy(a股_data)
print(f"\n{name}策略评价指标:")
for key, value in a股_metrics.items():
    print(f"{key}: {value}")

八、避坑指南:量化新手常踩的 6 个坑

  1. 数据复权问题:未复权的数据会因分红拆股出现价格断层(如股价突然从 100 元跌到 20 元),导致指标计算错误。解决:获取数据时始终设置auto_adjust=True

  2. 未来函数陷阱:用当天数据计算前一天的信号(如用今天的均线判断昨天是否买入),会导致回测收益虚高。解决:计算信号时用shift(1)取前一天数据。

  3. 过度优化参数:为了让回测结果好看,反复调整均线周期(如把 5 日和 20 日改成 7 日和 19 日),导致策略 “拟合历史” 但未来失效。解决:用样本外数据验证(如用前 3 年数据优化,后 2 年数据测试)。

  4. 忽略交易成本:回测时不考虑手续费、滑点(买卖价差),导致实际收益远低于预期。解决:在回测中加入成本(如每次交易扣除 0.1% 的手续费)。

  5. 数据频率错误:用日线数据回测日内策略,或用分钟线数据计算周线指标,逻辑矛盾。解决:确保数据频率与策略周期匹配(如日线策略用日线数据)。

  6. 单只股票回测偏见:只回测一只股票就判定策略有效,可能是运气。解决:多股票、多周期回测(如在 10 只股票上测试,且至少 3 年数据)。

九、进阶方向:从入门到实战的升级路径

掌握基础后,可从以下方向进阶:

  1. 策略升级:结合更多指标(如 MACD+RSI 组合信号)、加入止损止盈(如亏损 10% 强制止损)、尝试选股策略(如多因子模型)。

  2. 工具扩展:学习专业回测框架(如 Backtrader、VectorBT),支持更复杂的策略逻辑;用 TA-Lib 库快速计算技术指标(替代手动编写的 RSI、MACD 函数)。

  3. 自动化交易:对接券商 API(如富途、雪球),实现策略自动下单;用定时任务(如schedule库)每日自动获取数据并生成信号。

  4. 风险管理:学习仓位管理(如每只股票持仓不超过总资金的 10%)、分散投资(同时持有多只不相关股票),降低单一风险。

  5. 量化思维培养:理解 “概率思维”—— 量化策略不是 100% 赚钱,而是通过多次交易让盈利概率大于亏损概率;接受 “回测≠未来”,市场变化会导致策略失效,需持续优化。

十、总结:量化交易的核心不是代码,而是逻辑

用 Pandas、yfinance、Matplotlib 实现量化的过程,本质是 “将交易逻辑转化为可执行的代码”。这三个库只是工具,真正重要的是:

  • 你能否清晰定义策略的买入卖出规则(避免模糊的 “感觉”);
  • 你能否用数据验证策略的有效性(而非凭直觉判断);
  • 你能否接受策略的局限性(没有永远赚钱的策略,只有适应市场的策略)。

从本文的双均线策略开始,尝试修改参数(如换成 10 日和 30 日均线)、添加新指标、测试不同股票,逐步积累经验。记住:量化交易的门槛不在编程,而在 “用数据说话” 的思维方式。

注意:本文只是技术切磋,请勿盲目跟随!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值