
为什么Python成了股票期货玩家的标配
做股票期货的人,每天都在跟数字打交道。K线图背后是价格序列,技术指标是数学公式,交易策略是条件判断。这些用Excel搞不定的时候,Python就成了那把趁手的工具。最关键的是,Python有大量现成的库,把复杂的数学运算和数据处理包装成了几行代码的事。你不用从头造轮子,调个库就能算出均线、MACD、布林带,甚至跑一个完整的策略回测。
量化交易听起来高大上,拆开看就是三件事:搞数据、算指标、做回测。每个环节都有对应的Python库来搞定。下面按实战流程走一遍,看看怎么用这些库实现复杂的函数功能。
数据获取与清洗用pandas和tushare
tushare是一个开源金融数据接口,能拿到A股、期货的日线、分钟线数据。安装后用一行代码就能拉取历史行情:
import tushare as ts
df = ts.get_k_data('600519', start='2020-01-01', end='2023-12-31')
拿到的是DataFrame结构,pandas处理起来非常顺手。缺失值用df.fillna(method='ffill')前向填充,重复行用df.drop_duplicates()去掉。计算收益率更简单:df['return'] = df['close'].pct_change()。这些操作在Excel里要拖公式,在pandas里一行搞定。

技术指标计算用ta-lib和numpy
ta-lib是技术分析库的标杆,内置了150多个指标函数。算MACD只需要:
import talib
macd, signal, hist = talib.MACD(df['close'], fastperiod=12, slowperiod=26, signalperiod=9)
返回三个数组,分别对应DIF线、DEA线和MACD柱。均线、RSI、KDJ同理,改个函数名就行。如果ta-lib装不上(Windows用户经常遇到编译问题),可以用pandas的rolling方法手写:
df['ma20'] = df['close'].rolling(window=20).mean()
复杂的函数功能靠numpy向量化实现。比如计算年化波动率,用np.std(df['return']) * np.sqrt(252),一行代码把252个交易日的数据聚合成一个标量。numpy的广播机制让不同长度的数组能直接运算,省去了写循环的麻烦。
策略逻辑用numpy和pandas组合实现
一个双均线策略的逻辑是:短期均线上穿长期均线买入,下穿卖出。用pandas实现信号生成:
df['ma5'] = df['close'].rolling(5).mean()
df['ma20'] = df['close'].rolling(20).mean()
df['signal'] = 0
df.loc[df['ma5'] > df['ma20'], 'signal'] = 1
df.loc[df['ma5'] < df['ma20'], 'signal'] = -1
df['position'] = df['signal'].shift(1)
shift(1)是关键,避免用当天收盘价产生的信号在当天成交,防止未来函数。仓位变化用df['position'].diff()能看出每次调仓的时点。
复杂一点的函数,比如计算最大回撤,需要滚动计算历史高点:
df['cummax'] = df['close'].cummax()
df['drawdown'] = (df['close'] - df['cummax']) / df['cummax']
max_drawdown = df['drawdown'].min()
这段代码在期货交易里特别实用,因为期货有杠杆,回撤控制是生命线。
参数优化用scipy
策略里的均线周期是参数,怎么找到最优组合?scipy.optimize提供了网格搜索和优化算法。写一个目标函数,输入参数返回夏普比率,然后让优化器去找最大值:
from scipy.optimize import minimize
def objective(params):
fast, slow = int(params[0]), int(params[1])
if fast >= slow:
return 1e9
df['ma_fast'] = df['close'].rolling(fast).mean()
df['ma_slow'] = df['close'].rolling(slow).mean()
df['signal'] = np.where(df['ma_fast'] > df['ma_slow'], 1, -1)
returns = df['signal'].shift(1) * df['return']
sharpe = returns.mean() / returns.std() * np.sqrt(252)
return -sharpe
result = minimize(objective, x0=[5, 20], bounds=[(2, 30), (10, 120)], method='L-BFGS-B')
minimize默认求最小值,所以目标函数返回负的夏普比率。bounds限定参数范围,避免跑出离谱的周期。这种方法比手工调参快得多,尤其在期货多品种回测时,能批量跑出最优参数。
统计检验用statsmodels
策略回测出来一个夏普比率,怎么判断它是真本事还是运气?statsmodels可以做t检验。把策略的日收益率序列拿去检验均值是否显著大于0:
import statsmodels.api as sm
t_stat, p_value, _ = sm.tsa.adfuller(df['return'].dropna())
adfuller做的是单位根检验,判断收益率序列是否平稳。p值小于0.05说明序列平稳,回测结果可信度更高。期货价格本身不平稳,但收益率通常平稳,这个检验能过滤掉那些用价格直接回归产生的伪结果。
另一个常用的是线性回归,检验策略收益和市场基准的关系。用sm.OLS跑回归,看alpha是否显著:
X = sm.add_constant(benchmark_returns)
model = sm.OLS(strategy_returns, X).fit()
print(model.summary())
输出里的alpha的p值如果小于0.05,说明策略有超额收益,不是靠暴露市场风险赚来的。
回测框架用backtrader把流程串起来
上面这些库各管一摊,backtrader把它们整合成一个完整的回测引擎。定义策略类,在next方法里写买卖逻辑:
import backtrader as bt
class DualMA(bt.Strategy):
params = (('fast', 5), ('slow', 20))
def __init__(self):
self.ma_fast = bt.indicators.SMA(self.data.close, period=self.p.fast)
self.ma_slow = bt.indicators.SMA(self.data.close, period=self.p.slow)
self.crossover = bt.indicators.CrossOver(self.ma_fast, self.ma_slow)
def next(self):
if not self.position:
if self.crossover > 0:
self.buy()
elif self.crossover < 0:
self.close()
bt.indicators里内置了SMA、CrossOver这些复杂函数,不用自己写。回测时加上手续费和滑点,结果更接近实盘。期货交易还要设置合约乘数和保证金比例,backtrader的setcommission和setmargin能精确模拟。
一个完整的复杂函数案例计算期货的ATR止损
ATR是平均真实波幅,期货交易里常用它来设止损。复杂的地方在于真实波幅要取三个值的最大值:当日最高减最低、当日最高减昨收的绝对值、当日最低减昨收的绝对值。用pandas能这样实现:
df['tr'] = np.maximum(df['high'] - df['low'],
np.maximum(abs(df['high'] - df['close'].shift(1)),
abs(df['low'] - df['close'].shift(1))))
df['atr'] = df['tr'].rolling(14).mean()
df['stop_loss'] = df['close'] - 2 * df['atr']
这段代码里np.maximum嵌套使用,把三个序列逐元素取最大。shift(1)拿昨收,rolling(14)算14日均值。最终止损位是收盘价减去两倍ATR,期货里这种动态止损比固定点数更适应波动率变化。
写在最后
Python在这些库的帮助下,把股票期货交易中的复杂函数拆解成了可组合的积木。数据用pandas洗,指标用ta-lib算,优化用scipy跑,检验用statsmodels做,整个流程串起来就是一套量化系统。关键是每一步都能单独拿出来用,不需要一次性搭完整套框架。对普通交易者来说,先把手里的指标用Python算清楚,再慢慢往策略和回测延伸,这条路比想象中好走。
