量化交易的核心在于将交易规则和策略转化为计算机可执行的代码。其中,技术指标的计算和基于这些指标生成交易信号是策略实现的基础环节。本文将深入探讨如何从零开始开发量化交易脚本,重点聚焦于常见技术指标的计算逻辑以及如何基于这些指标设计并实现信号触发机制。我们将使用Python作为主要编程语言,因其在数据分析和量化交易领域的广泛应用和丰富的库支持。
第一章:量化交易系统概述与技术指标基础
1.1 量化交易系统架构
一个完整的量化交易系统通常包含以下几个核心模块:
- 数据获取模块: 负责从各种来源(如交易所API、金融数据服务商、本地数据库)获取市场数据(价格、成交量等)。
- 数据处理与存储模块: 对原始数据进行清洗、整理、转换,并存储到合适的数据库或文件中。
- 策略研究模块: 基于历史数据进行策略回测,评估策略性能。
- 策略执行模块: 根据实时数据和预设策略生成交易信号,并通过交易接口执行买卖操作。
- 风险管理模块: 监控账户风险,设置止损止盈,控制仓位大小。
- 绩效评估模块: 分析交易结果,计算各项绩效指标(如夏普比率、最大回撤)。
技术指标计算 主要位于数据处理和策略执行模块中。信号触发则是策略执行模块的核心功能。
1.2 技术指标简介
技术指标是基于历史市场价格和成交量数据,通过特定数学公式计算得出的数值或图形。它们旨在帮助交易者分析市场趋势、动能、波动性以及可能的反转点,为交易决策提供依据。技术指标大致可分为以下几类:
- 趋势跟踪指标: 用于识别和确认市场趋势的方向和强度。例如:
- 移动平均线 (Moving Average, MA)
- 指数移动平均线 (Exponential Moving Average, EMA)
- 平均趋向指数 (Average Directional Index, ADX)
- 动量指标: 衡量价格变动的速度和幅度,用于判断市场是否超买或超卖。例如:
- 相对强弱指数 (Relative Strength Index, RSI)
- 随机震荡指标 (Stochastic Oscillator)
- 移动平均收敛发散 (Moving Average Convergence Divergence, MACD)
- 波动率指标: 反映市场价格的波动程度。例如:
- 布林带 (Bollinger Bands, BB)
- 平均真实波幅 (Average True Range, ATR)
- 成交量指标: 结合价格分析成交量变化。例如:
- 成交量平衡指标 (On-Balance Volume, OBV)
- 成交量加权移动平均线 (Volume-Weighted Moving Average, VWMA)
理解这些指标的计算原理是开发量化脚本的基础。
1.3 量化脚本开发环境准备
在开始编写指标计算代码前,需要搭建Python开发环境并安装必要的库:
- Python (推荐3.7+): 基础编程语言。
- NumPy: 用于高效的数值计算和数组操作。
- Pandas: 提供强大的数据处理结构(DataFrame/Series),是金融数据分析的核心。
- Matplotlib/Seaborn: 用于数据可视化和绘制指标图表。
- (可选) TA-Lib: 一个广泛使用的技术分析库,提供了许多预实现的指标计算函数。但为了深入理解原理,本文将展示手动实现。
安装命令示例:
bash
pip install numpy pandas matplotlib seaborn
# 如果需要 TA-Lib,可能需要从特定源安装或编译
第二章:核心技术指标计算原理与实现
本章将详细讲解几种最常用技术指标的计算公式,并展示如何用Python和Pandas实现它们。
2.1 移动平均线 (MA)
移动平均线是最基础的趋势跟踪指标之一,它通过计算一定周期内价格的平均值来平滑价格波动,显示价格的主要趋势。
-
简单移动平均线 (SMA): 计算公式: $$ SMA_t(N) = \frac{1}{N} \sum_{i=0}^{N-1} P_{t-i} $$ 其中 SMA_t(N) 表示在时间 t 的 N 期 SMA 值,P_{t-i} 表示时间 t-i 的价格(通常使用收盘价)。
-
指数移动平均线 (EMA): EMA 给予近期价格更高的权重,对价格变化的反应比 SMA 更灵敏。 计算公式: $$ EMA_t(N) = \begin{cases} P_0 & \text{if } t = 0 \ \alpha \cdot P_t + (1 - \alpha) \cdot EMA_{t-1} & \text{if } t > 0 \end{cases} $$ 其中 \\alpha = \\frac{2}{N + 1} 称为平滑系数,P_t 是当前时间 t 的价格。
Python实现:
python
import pandas as pd
import numpy as np
def calculate_sma(data, window):
"""
计算简单移动平均线 (SMA)
:param data: pandas Series, 价格数据 (e.g., 收盘价)
:param window: int, 移动平均窗口大小
:return: pandas Series, SMA 值
"""
return data.rolling(window=window).mean()
def calculate_ema(data, window):
"""
计算指数移动平均线 (EMA)
:param data: pandas Series, 价格数据
:param window: int, EMA 窗口大小 (决定平滑系数)
:return: pandas Series, EMA 值
"""
alpha = 2 / (window + 1)
# 使用 Pandas 的 ewm 方法计算,adjust=False 使用上述递推公式
return data.ewm(alpha=alpha, adjust=False).mean()
# 示例:假设 df 是一个包含 'close' 列的 DataFrame
df['SMA_20'] = calculate_sma(df['close'], 20)
df['EMA_12'] = calculate_ema(df['close'], 12)
df['EMA_26'] = calculate_ema(df['close'], 26) # 为后续 MACD 准备
2.2 相对强弱指数 (RSI)
RSI 是一种动量指标,用于衡量近期价格变动的幅度,以评估超买或超卖状况。RSI 值在 0 到 100 之间。
计算步骤:
- 计算价格变化: \\Delta P_t = P_t - P_{t-1}
- 分离上涨和下跌:
- 上涨幅度 U_t = \\begin{cases} \\Delta P_t \& \\text{if } \\Delta P_t \> 0 \\ 0 \& \\text{otherwise} \\end{cases}
- 下跌幅度 D_t = \\begin{cases} \|\\Delta P_t\| \& \\text{if } \\Delta P_t \< 0 \\ 0 \& \\text{otherwise} \\end{cases}
- 计算平滑平均上涨和下跌 (通常使用 EMA):
- 平均上涨 AU_t(N) = EMA(U_t, N)
- 平均下跌 AD_t(N) = EMA(D_t, N)
- 计算相对强度 (RS): RS_t(N) = \\frac{AU_t(N)}{AD_t(N)}
- 计算 RSI: RSI_t(N) = 100 - \\frac{100}{1 + RS_t(N)}
Python实现:
python
def calculate_rsi(data, window=14):
"""
计算相对强弱指数 (RSI)
:param data: pandas Series, 价格数据 (通常用收盘价)
:param window: int, RSI 计算窗口期
:return: pandas Series, RSI 值
"""
# 计算价格变化
delta = data.diff()
# 分离上涨和下跌
up = delta.copy()
up[up < 0] = 0 # 将下跌变化设为0
down = delta.copy()
down[down > 0] = 0 # 将上涨变化设为0
down = down.abs() # 取绝对值
# 计算平均上涨和平均下跌 (使用 EMA)
avg_up = calculate_ema(up, window)
avg_down = calculate_ema(down, window)
# 计算相对强度 (RS)
rs = avg_up / avg_down
# 计算 RSI
rsi = 100 - (100 / (1 + rs))
return rsi
# 示例
df['RSI_14'] = calculate_rsi(df['close'], 14)
2.3 移动平均收敛发散 (MACD)
MACD 是一个结合了趋势跟踪和动量的指标,由三部分组成:MACD 线、信号线 (Signal Line) 和柱状图 (Histogram)。
计算步骤:
- 计算短期和长期 EMA:
- EMA_{short} = EMA(P_t, N_{short}) (常用 N_{short}=12)
- EMA_{long} = EMA(P_t, N_{long}) (常用 N_{long}=26)
- 计算 MACD 线 (DIF): MACD_t = EMA_{short} - EMA_{long}
- 计算信号线 (DEA): Signal_t = EMA(MACD_t, N_{signal}) (常用 N_{signal}=9)
- 计算柱状图 (Histogram): Histogram_t = MACD_t - Signal_t
Python实现:
python
def calculate_macd(data, short_window=12, long_window=26, signal_window=9):
"""
计算 MACD 指标
:param data: pandas Series, 价格数据
:param short_window: int, 短期 EMA 窗口
:param long_window: int, 长期 EMA 窗口
:param signal_window: int, 信号线 EMA 窗口
:return: tuple (macd_line, signal_line, histogram)
"""
ema_short = calculate_ema(data, short_window)
ema_long = calculate_ema(data, long_window)
macd_line = ema_short - ema_long
signal_line = calculate_ema(macd_line, signal_window)
histogram = macd_line - signal_line
return macd_line, signal_line, histogram
# 示例
df['MACD'], df['Signal'], df['Histogram'] = calculate_macd(df['close'])
2.4 布林带 (Bollinger Bands, BB)
布林带由三条线组成:中轨(通常是SMA)、上轨和下轨。上轨和下轨基于中轨和标准差计算,反映了价格的波动区间。
计算步骤:
- 计算中轨 (Middle Band): MB_t(N) = SMA(P_t, N) (常用 N=20)
- 计算标准差: \\sigma_t(N) = \\sqrt{\\frac{1}{N} \\sum_{i=0}\^{N-1} (P_{t-i} - MB_t(N))\^2}
- 实际计算中,Pandas 的
rolling方法可以直接计算标准差。
- 实际计算中,Pandas 的
- 计算上轨 (Upper Band): UB_t(N, k) = MB_t(N) + k \\cdot \\sigma_t(N) (常用 k=2)
- 计算下轨 (Lower Band): LB_t(N, k) = MB_t(N) - k \\cdot \\sigma_t(N)
Python实现:
python
def calculate_bollinger_bands(data, window=20, num_std=2):
"""
计算布林带
:param data: pandas Series, 价格数据
:param window: int, 移动平均和标准差计算的窗口期
:param num_std: int, 计算上下轨所用的标准差倍数
:return: tuple (middle_band, upper_band, lower_band)
"""
middle_band = calculate_sma(data, window)
rolling_std = data.rolling(window=window).std()
upper_band = middle_band + (num_std * rolling_std)
lower_band = middle_band - (num_std * rolling_std)
return middle_band, upper_band, lower_band
# 示例
df['BB_Middle'], df['BB_Upper'], df['BB_Lower'] = calculate_bollinger_bands(df['close'])
2.5 平均真实波幅 (Average True Range, ATR)
ATR 是一个波动率指标,衡量价格在特定时期内的平均波动范围(考虑了价格跳空)。
计算步骤:
- 计算真实波幅 (True Range, TR):
- TR_t = \\max( \\text{High}_t - \\text{Low}_t, \|\\text{High}*t - \\text{Close}* {t-1}\|, \|\\text{Low}*t - \\text{Close}*{t-1}\| )
- 即以下三者中的最大值:
- 当日最高价 - 当日最低价
- 当日最高价 - 前一日收盘价的绝对值
- 当日最低价 - 前一日收盘价的绝对值
- 计算 ATR: 通常对 TR 序列进行平滑(如 SMA 或 EMA)。常用 N=14 期。
- ATR_t(N) = \\frac{ATR_{t-1} \\times (N-1) + TR_t}{N} (SMA 方式)
- 更常用 Wilder 的平滑方式(类似 EMA,但平滑系数不同): ATR_t = \\frac{ATR_{t-1} \\times (N-1) + TR_t}{N} (初始 ATR 可用前 N 个 TR 的 SMA)
Python实现:
python
def calculate_atr(high, low, close, window=14):
"""
计算平均真实波幅 (ATR)
:param high: pandas Series, 最高价
:param low: pandas Series, 最低价
:param close: pandas Series, 收盘价
:param window: int, ATR 平滑窗口
:return: pandas Series, ATR 值
"""
# 计算 TR
tr1 = high - low
tr2 = abs(high - close.shift(1))
tr3 = abs(low - close.shift(1))
tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
# 计算 ATR (使用 SMA 方式初始化,然后使用递推公式)
# 初始化: 前 window 个 TR 的 SMA
atr = tr.copy()
atr.iloc[:window] = np.nan # 前 window-1 个值无法计算有效 ATR
atr.iloc[window-1] = tr.iloc[:window].mean() # 第 window-1 个位置 (索引从0开始) 用前 window 个 TR 的均值
# 递推计算剩余 ATR (Wilder 平滑)
for i in range(window, len(tr)):
atr.iloc[i] = (atr.iloc[i-1] * (window - 1) + tr.iloc[i]) / window
return atr
# 示例 (假设 df 有 'high', 'low', 'close' 列)
df['ATR_14'] = calculate_atr(df['high'], df['low'], df['close'])
第三章:信号触发机制设计与实现
计算技术指标只是第一步,量化策略的核心在于如何根据这些指标(结合其他条件)生成具体的买入、卖出或持仓信号。
3.1 信号类型
- 买入信号 (Buy Signal): 触发条件满足时,指示系统应该建立多头仓位(买入)。
- 卖出信号 (Sell Signal): 触发条件满足时,指示系统应该平掉多头仓位(卖出)。
- 做空信号 (Short Signal): 触发条件满足时,指示系统应该建立空头仓位(卖出开仓)。
- 平空信号 (Cover Signal): 触发条件满足时,指示系统应该平掉空头仓位(买入平仓)。
- 持仓信号 (Hold Signal): 无明确买卖动作,继续持有当前仓位。
- 空仓信号 (Cash Signal): 不持有任何仓位。
一个策略通常需要定义明确的规则来生成这些信号。
3.2 信号触发逻辑设计原则
- 清晰明确: 触发条件必须无歧义,可精确地用代码表达。
- 可量化: 条件基于数值计算或布尔判断。
- 一致性: 在整个回测或实盘交易中,相同的市场条件应产生相同的信号。
- 避免未来函数: 信号必须仅基于当前或历史数据计算得出,不能使用未来的信息。
- 考虑延迟: 实盘交易中,数据获取、计算、网络传输、交易所撮合都需要时间。信号生成逻辑可能需要考虑这种延迟,或使用稍早的数据。
3.3 常见信号触发模式
3.3.1 单指标阈值触发
最简单的模式,当指标值超过或低于某个预设阈值时触发信号。
- 示例1 (RSI 超买/超卖):
- 买入信号: RSI_t \< 30 (超卖,可能反弹)
- 卖出信号: RSI_t \> 70 (超买,可能回调)
- 示例2 (价格突破布林带):
- 买入信号: Close_t \> UB_t (突破上轨,强势)
- 卖出信号: Close_t \< LB_t (突破下轨,弱势)
- (注意:单纯突破布林带信号可能噪音较多,常需结合其他条件)
Python实现 (RSI 阈值):
python
def generate_rsi_signals(rsi, oversold=30, overbought=70):
"""
基于 RSI 阈值生成信号
:param rsi: pandas Series, RSI 值
:param oversold: float, 超卖阈值
:param overbought: float, 超买阈值
:return: pandas Series, 信号 (1: 买入, -1: 卖出, 0: 无信号)
"""
signals = pd.Series(0, index=rsi.index) # 初始化全为0 (无信号)
signals[rsi < oversold] = 1 # 买入信号
signals[rsi > overbought] = -1 # 卖出信号
return signals
df['RSI_Signal'] = generate_rsi_signals(df['RSI_14'])
3.3.2 双指标交叉触发
当两条指标线发生交叉时触发信号。这是非常常见的模式,如均线金叉/死叉、MACD线与信号线交叉。
- 示例1 (均线金叉/死叉):
- 买入信号 (金叉): EMA_{short} \> EMA_{long} 且 前一时刻 EMA_{short} \<= EMA_{long}
- 卖出信号 (死叉): EMA_{short} \< EMA_{long} 且 前一时刻 EMA_{short} \>= EMA_{long}
- 示例2 (MACD 线穿越信号线):
- 买入信号: MACD_t \> Signal_t 且 前一时刻 MACD_t \<= Signal_t
- 卖出信号: MACD_t \< Signal_t 且 前一时刻 MACD_t \>= Signal_t
- 示例3 (MACD 柱状图转正/转负):
- 买入信号: Histogram_t \> 0 且 前一时刻 Histogram_t \<= 0
- 卖出信号: Histogram_t \< 0 且 前一时刻 Histogram_t \>= 0
Python实现 (均线交叉):
python
def generate_ma_crossover_signals(short_ma, long_ma):
"""
基于短期均线与长期均线交叉生成信号
:param short_ma: pandas Series, 短期移动平均线 (e.g., EMA_12)
:param long_ma: pandas Series, 长期移动平均线 (e.g., EMA_26)
:return: pandas Series, 信号 (1: 金叉买入, -1: 死叉卖出, 0: 无信号)
"""
signals = pd.Series(0, index=short_ma.index)
# 金叉条件: 当前短>长 且 上一时刻短<=长
golden_cross = (short_ma > long_ma) & (short_ma.shift(1) <= long_ma.shift(1))
# 死叉条件: 当前短<长 且 上一时刻短>=长
death_cross = (short_ma < long_ma) & (short_ma.shift(1) >= long_ma.shift(1))
signals[golden_cross] = 1
signals[death_cross] = -1
return signals
df['MA_Cross_Signal'] = generate_ma_crossover_signals(df['EMA_12'], df['EMA_26'])
3.3.3 多指标组合条件触发
更复杂的策略往往需要多个指标同时满足条件才触发信号,以提高信号的可靠性或满足特定策略逻辑。
- 示例 (趋势跟踪 + 动量确认):
- 买入信号:
- EMA_{short} \> EMA_{long} (趋势向上)
- RSI_t \> 50 (动量向上)
- Close_t \> SMA_{50} (价格在中期均线上方)
- 卖出信号:
- EMA_{short} \< EMA_{long} (趋势向下)
- RSI_t \< 50 (动量向下)
- Close_t \< SMA_{50} (价格在中期均线下方)
- 买入信号:
Python实现:
python
def generate_complex_signals(ema_short, ema_long, rsi, close, sma_long):
"""
基于多个指标组合条件生成信号
:param ema_short: pandas Series, 短期EMA
:param ema_long: pandas Series, 长期EMA
:param rsi: pandas Series, RSI
:param close: pandas Series, 收盘价
:param sma_long: pandas Series, 长期SMA (e.g., 50)
:return: pandas Series, 信号 (1: 买入, -1: 卖出, 0: 无信号)
"""
signals = pd.Series(0, index=ema_short.index)
# 买入条件
buy_condition = (ema_short > ema_long) & (rsi > 50) & (close > sma_long)
# 卖出条件
sell_condition = (ema_short < ema_long) & (rsi < 50) & (close < sma_long)
signals[buy_condition] = 1
signals[sell_condition] = -1
return signals
df['Complex_Signal'] = generate_complex_signals(df['EMA_12'], df['EMA_26'], df['RSI_14'], df['close'], df['SMA_50'])
3.3.4 基于波动率的仓位管理信号
ATR等波动率指标常用于动态调整止损位或仓位大小。
- 示例 (基于ATR的止损):
- 初始止损位: EntryPrice - k \\times ATR_t (多头)
- 移动止损位:随着价格上涨,将止损位上移至 CurrentHigh - k \\times ATR_t
- 信号:当价格触及止损位时,生成卖出信号。
- 示例 (基于ATR的仓位大小):
- 每笔交易风险金额 = 账户总资金 \\times 风险比例 (e.g., 1%)
- 每笔交易可承受的波动 = k \\times ATR_t
- 合约/股票数量 = \\frac{\\text{每笔交易风险金额}}{\\text{每笔交易可承受的波动}}
Python实现 (简化版止损信号):
python
def generate_atr_stop_loss_signal(position, entry_price, current_low, current_high, atr, k_factor=3):
"""
生成基于ATR的止损信号 (简化版)
:param position: int, 当前持仓方向 (1: 多头, -1: 空头, 0: 无仓)
:param entry_price: float, 开仓价格
:param current_low: float, 当前K线最低价
:param current_high: float, 当前K线最高价
:param atr: float, 当前ATR值
:param k_factor: float, ATR乘数
:return: int, 信号 (1: 无操作, -1: 触发止损卖出/平仓)
"""
if position == 1: # 持有多头
stop_loss_price = entry_price - k_factor * atr # 初始止损
# 或者基于最高价的移动止损: stop_loss_price = current_high - k_factor * atr
if current_low <= stop_loss_price: # 价格触及止损位
return -1 # 触发止损,卖出平仓
elif position == -1: # 持有空头
stop_loss_price = entry_price + k_factor * atr # 初始止损
# 或者基于最低价的移动止损: stop_loss_price = current_low + k_factor * atr
if current_high >= stop_loss_price: # 价格触及止损位
return -1 # 触发止损,买入平仓
return 1 # 未触及止损,继续持有
# 注意:这是一个简化示例,实盘实现会更复杂,需要维护持仓状态和开仓价格。
3.4 信号过滤与确认
为了减少假信号和过度交易,常采用信号过滤技术:
- 时间过滤: 信号只在特定时间段有效(如避开开盘、收盘、重要数据发布时段)。
- 波动过滤: 只有当波动率(如 ATR)达到一定水平时才考虑信号,避免在平静市场中交易。
- 价格过滤: 要求价格突破某个关键水平(如前高/前低)才确认信号。
- 成交量确认: 信号产生时要求成交量放大。
- 多周期确认: 在更高时间级别(如日线)确认低级别(如小时线)产生的信号。
Python实现 (简单的时间过滤):
python
def filter_signals_by_time(signals, valid_start_time='09:30', valid_end_time='15:45'):
"""
根据时间段过滤信号
:param signals: pandas Series, 原始信号序列 (带时间索引)
:param valid_start_time: str, 有效信号开始时间 (HH:MM)
:param valid_end_time: str, 有效信号结束时间 (HH:MM)
:return: pandas Series, 过滤后的信号 (非有效时段信号置为0)
"""
# 确保索引是 DatetimeIndex
if not isinstance(signals.index, pd.DatetimeIndex):
raise ValueError("Signals index must be DatetimeIndex")
# 将时间字符串转换为时间对象
start_time = pd.to_datetime(valid_start_time).time()
end_time = pd.to_datetime(valid_end_time).time()
# 创建过滤条件: 时间在 start_time 和 end_time 之间
time_mask = (signals.index.time >= start_time) & (signals.index.time <= end_time)
# 应用过滤
filtered_signals = signals.copy()
filtered_signals[~time_mask] = 0
return filtered_signals
# 示例 (假设 df.index 是时间戳)
df['Filtered_Signal'] = filter_signals_by_time(df['Complex_Signal'])
第四章:策略回测框架基础
开发出信号生成代码后,必须进行回测来评估策略在历史数据上的表现。回测框架需要模拟交易过程。
4.1 回测核心要素
- 历史数据: 高质量、干净、包含所需字段(开盘、最高、最低、收盘、成交量)的价格数据。
- 初始资本: 设定模拟账户的初始资金。
- 手续费模型: 模拟交易成本(佣金、滑点)。
- 信号向量: 由策略模块生成的买卖信号序列。
- 持仓管理: 记录当前持有的品种、数量、开仓价格、开仓时间。
- 订单执行: 根据信号生成订单,并按照一定的规则(如收盘价)执行。
- 绩效统计: 计算最终收益、最大回撤、夏普比率、胜率等指标。
4.2 简单向量化回测示例 (Python)
以下是一个非常简化的向量化回测框架示例,它计算策略的每日收益率,忽略仓位管理、滑点和复杂订单逻辑:
python
import pandas as pd
import numpy as np
def vectorized_backtest(data, signals, initial_capital=100000, commission_rate=0.001):
"""
一个非常简化的向量化回测函数 (仅考虑多头,信号为1买入,-1卖出)
:param data: pandas DataFrame, 包含 'close' 列的历史数据
:param signals: pandas Series, 交易信号 (1: 买入, -1: 卖出, 0: 无操作), 与data索引对齐
:param initial_capital: float, 初始资本
:param commission_rate: float, 单边交易佣金率
:return: dict, 包含回测结果 (净值曲线、总收益等)
"""
# 初始化持仓和现金
position = 0 # 0 表示空仓
cash = initial_capital
# 创建一个 DataFrame 存储每日状态
portfolio = pd.DataFrame(index=data.index)
portfolio['signal'] = signals
portfolio['close'] = data['close']
portfolio['holdings'] = 0 # 持仓市值
portfolio['cash'] = cash
portfolio['total'] = cash # 总资产 = 现金 + 持仓市值
# 遍历每一天 (向量化处理会更好,这里用循环演示逻辑)
for i in range(1, len(portfolio)):
prev_i = i - 1
# 获取前一天和当天的信号、价格
prev_signal = portfolio['signal'].iloc[prev_i]
current_signal = portfolio['signal'].iloc[i]
prev_close = portfolio['close'].iloc[prev_i]
current_close = portfolio['close'].iloc[i]
# 处理持仓价值变化 (从昨天收盘到今天收盘)
if position > 0:
portfolio['holdings'].iloc[i] = position * current_close
else:
portfolio['holdings'].iloc[i] = 0
# 处理交易信号
if current_signal == 1 and position == 0: # 买入信号且空仓
# 计算能买多少股 (假设无限可分,忽略整数股)
position_value = cash # 用全部现金买入
position = position_value / current_close # 买入股数
cash = 0
# 扣除佣金
commission = position_value * commission_rate
cash -= commission # 现金减少佣金
portfolio['holdings'].iloc[i] = position * current_close # 更新持仓市值
elif current_signal == -1 and position > 0: # 卖出信号且持有多头
sell_value = position * current_close
cash = sell_value
position = 0
# 扣除佣金
commission = sell_value * commission_rate
cash -= commission
portfolio['holdings'].iloc[i] = 0
# 更新总资产和现金
portfolio['cash'].iloc[i] = cash
portfolio['total'].iloc[i] = cash + portfolio['holdings'].iloc[i]
# 计算每日收益率
portfolio['daily_return'] = portfolio['total'].pct_change()
# 计算累计净值
portfolio['cumulative_return'] = (1 + portfolio['daily_return']).cumprod()
# 计算总收益率
total_return = (portfolio['total'].iloc[-1] / initial_capital) - 1
return {
'portfolio': portfolio,
'total_return': total_return,
'cumulative_returns': portfolio['cumulative_return']
}
# 示例用法 (假设 df 包含数据和信号)
results = vectorized_backtest(df, df['Filtered_Signal'])
print(f"总收益率: {results['total_return'] * 100:.2f}%")
results['portfolio'][['total', 'cumulative_return']].plot() # 绘制净值和累计收益曲线
注意: 这是一个极其简化的示例。专业的回测框架(如 Backtrader, Zipline, QuantConnect)会处理更复杂的情况,如多资产、杠杆、保证金、滑点、不同订单类型、精确的仓位管理、时间点处理(是使用Bar收盘价还是下一个Bar开盘价执行)等。
第五章:实盘交易接口与风险管理 (简述)
将策略部署到实盘涉及更多复杂性:
- 交易接口: 通过交易所提供的API(如REST API, Websocket)连接实盘交易系统。需要处理订单提交、查询、撤单等功能。常用库有
ccxt(支持多个交易所)。 - 实时数据处理: 使用Websocket或高频轮询获取实时行情数据。
- 订单执行逻辑: 将策略生成的信号转化为具体的订单请求(市价单、限价单、止损单等),并发送给交易所。
- 风险管理:
- 止损: 如基于ATR的动态止损。
- 止盈: 设定目标价位。
- 仓位管理: 固定比例(如每次交易风险不超过账户1%),或基于波动率动态调整。
- 最大回撤控制: 当账户总回撤超过阈值时,暂停或停止交易。
- 单一品种/市场风险暴露限制。
- 监控与日志: 实时监控策略运行状态、持仓、风险指标,并记录详细日志用于事后分析。
实盘开发需要极高的稳定性和容错能力,并严格遵守交易所的API使用规则和频率限制。
第六章:总结与进阶方向
本文详细介绍了量化交易脚本开发的核心环节:技术指标计算(SMA, EMA, RSI, MACD, BB, ATR)和信号触发机制(阈值、交叉、组合、基于波动率)。我们展示了如何用Python和Pandas手动实现这些计算和信号逻辑,并提供了一个简单的回测框架示例。
关键点回顾:
- 理解指标原理: 掌握计算公式是正确实现和灵活运用的基础。
- 精确的代码实现: 使用高效的库(Pandas, NumPy),注意边界条件和计算效率。
- 清晰的信号定义: 策略成功的关键在于明确、可量化的交易规则。
- 严格的回测: 回测是验证策略可行性的必经之路,但要注意避免过拟合和未来函数。
- 风险管理至上: 实盘交易中,保护本金比追求利润更重要。
进阶方向:
- 更复杂的策略: 统计套利、配对交易、机器学习驱动的策略、高频策略。
- 更专业的回测: 使用成熟的回测框架(Backtrader等),进行参数优化、敏感性分析、蒙特卡洛模拟。
- 性能优化: 对于高频策略或处理大量数据,需要优化计算速度(向量化、并行计算、Cython)。
- 实时系统开发: 构建低延迟、高可用的实盘交易系统。
- 风险管理模型: 研究更复杂的风险价值 (VaR)、条件风险价值 (CVaR) 模型。
- 市场微观结构: 深入研究订单簿、流动性、滑点形成机制。
量化交易脚本开发是一个融合金融知识、编程技术和数学模型的领域。深入理解市场、扎实的编程功底、严谨的策略设计和强大的风险管理能力是成功的关键。希望本文提供的技术细节和示例代码能为您开启量化交易开发之旅提供有价值的参考。持续学习、实践和改进是通往成功的道路。