Python 量化金融基础

Python 量化金融基础全面指南

量化金融(Quantitative Finance)是金融工程的核心,结合数学、统计学和计算机科学,通过模型和算法进行投资决策。Python 因其科学计算能力和金融库生态,成为量化金融的首选工具。

1. 核心数学与统计基础

时间价值与复利计算

import numpy as np
import numpy_financial as npf

# 现值(PV)、未来值(FV)计算
principal = 10000  # 本金
rate = 0.05  # 年化利率
periods = 10  # 年数

# 未来值
fv = principal * (1 + rate) ** periods
fv_continuous = principal * np.exp(rate * periods)

# 年金现值
pmt = 1000  # 每年支付
pv_annuity = npf.pv(rate, periods, -pmt)

print(f"单利未来值: {fv:.2f}")
print(f"连续复利: {fv_continuous:.2f}")
print(f"年金现值: {pv_annuity:.2f}")

随机过程与布朗运动

import matplotlib.pyplot as plt
import numpy as np

# 几何布朗运动(股票价格模型)
def geometric_brownian_motion(S0, mu, sigma, T, dt, n_paths=1):
    """生成几何布朗运动路径"""
    N = int(T / dt)
    t = np.linspace(0, T, N)
    W = np.random.standard_normal(size=(N, n_paths))
    W = np.cumsum(W * np.sqrt(dt), axis=0)

    # 伊藤积分解
    paths = S0 * np.exp((mu - 0.5 * sigma**2) * t[:, np.newaxis] + 
                       sigma * W)
    return t, paths

# 参数设置
S0, mu, sigma, T, dt = 100, 0.1, 0.2, 1.0, 1/252
t, paths = geometric_brownian_motion(S0, mu, sigma, T, dt, n_paths=100)

plt.figure(figsize=(10, 6))
plt.plot(t, paths, alpha=0.3)
plt.plot(t, paths.mean(axis=1), 'r-', linewidth=2, label='平均路径')
plt.title('几何布朗运动 - 股票价格模拟')
plt.xlabel('时间')
plt.ylabel('价格')
plt.legend()
plt.show()

2. 资产定价基础

Black-Scholes 期权定价模型

from scipy.stats import norm
import numpy as np

def black_scholes(S, K, T, r, sigma, option_type='call'):
    """Black-Scholes 期权定价"""
    d1 = (np.log(S / K) + (r + 0.5 * sigma**2) * T) / (sigma * np.sqrt(T))
    d2 = d1 - sigma * np.sqrt(T)

    if option_type == 'call':
        price = S * norm.cdf(d1) - K * np.exp(-r * T) * norm.cdf(d2)
    else:  # put
        price = K * np.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1)

    return price

# Greeks 计算
def greeks(S, K, T, r, sigma, option_type='call'):
    d1 = (np.log(S / K) + (r + 0.5 * sigma**2) * T) / (sigma * np.sqrt(T))
    d2 = d1 - sigma * np.sqrt(T)

    delta = norm.cdf(d1) if option_type == 'call' else -norm.cdf(-d1)
    gamma = norm.pdf(d1) / (S * sigma * np.sqrt(T))
    theta = -(S * norm.pdf(d1) * sigma) / (2 * np.sqrt(T))
    if option_type == 'call':
        theta -= r * K * np.exp(-r * T) * norm.cdf(d2)
    else:
        theta -= r * K * np.exp(-r * T) * norm.cdf(-d2)
    theta /= 365  # 日Theta
    vega = S * norm.pdf(d1) * np.sqrt(T) / 100  # 1%波动率变化

    return {'Delta': delta, 'Gamma': gamma, 'Theta': theta, 'Vega': vega}

# 示例
S, K, T, r, sigma = 100, 100, 0.25, 0.05, 0.2
call_price = black_scholes(S, K, T, r, sigma, 'call')
print(f"看涨期权价格: {call_price:.2f}")
print("Greeks:", greeks(S, K, T, r, sigma))

二叉树期权定价

def binomial_tree(S, K, T, r, sigma, n, option_type='call'):
    """Cox-Ross-Rubinstein 二项树期权定价"""
    dt = T / n
    u = np.exp(sigma * np.sqrt(dt))
    d = 1 / u
    p = (np.exp(r * dt) - d) / (u - d)

    # 构建价格树
    stock_tree = np.zeros((n+1, n+1))
    for i in range(n+1):
        for j in range(i+1):
            stock_tree[j, i] = S * (u ** (i-j)) * (d ** j)

    # 期权价值向后递推
    option_tree = np.zeros((n+1, n+1))
    option_tree[:, n] = np.maximum(K - stock_tree[:, n], 0) if option_type == 'put' else \
                       np.maximum(stock_tree[:, n] - K, 0)

    for i in range(n-1, -1, -1):
        for j in range(i+1):
            option_tree[j, i] = np.exp(-r * dt) * (
                p * option_tree[j, i+1] + (1-p) * option_tree[j+1, i+1])

    return option_tree[0, 0]

# 验证与Black-Scholes对比
bs_price = black_scholes(S, K, T, r, sigma)
bin_price = binomial_tree(S, K, T, r, sigma, n=100)
print(f"Black-Scholes: {bs_price:.4f}, 二项树: {bin_price:.4f}")

3. 风险管理与VaR

历史模拟法 VaR

import pandas as pd
import numpy as np
from scipy import stats

def historical_var(returns, confidence=0.95, portfolio_value=1000000):
    """历史模拟法VaR"""
    sorted_returns = np.sort(returns)
    var_index = int((1 - confidence) * len(sorted_returns))
    var_return = sorted_returns[var_index]
    var_amount = -var_return * portfolio_value
    return var_return, var_amount

# 示例:多资产组合
tickers = ['AAPL', 'MSFT', 'SPY']
data = yf.download(tickers, start='2020-01-01')['Adj Close']
returns = data.pct_change().dropna()

# 组合权重
weights = np.array([0.4, 0.3, 0.3])
portfolio_returns = (returns * weights).sum(axis=1)

var_ret, var_amt = historical_var(portfolio_returns, confidence=0.99)
print(f"99% VaR: {var_ret:.4f} ({var_amt:,.0f})")

参数法 VaR(正态分布假设)

def parametric_var(returns, confidence=0.95, portfolio_value=1000000):
    """参数法VaR(正态分布)"""
    mu = np.mean(returns)
    sigma = np.std(returns)
    z_score = stats.norm.ppf(1 - confidence)
    var_return = mu + z_score * sigma
    var_amount = -var_return * portfolio_value
    return var_return, var_amount, z_score

# 计算并绘制VaR
var_ret, var_amt, z = parametric_var(portfolio_returns, 0.99)
print(f"参数法99% VaR: {var_ret:.4f} ({var_amt:,.0f}), Z-score: {z:.2f}")

# 可视化
plt.figure(figsize=(12, 6))
plt.hist(portfolio_returns, bins=100, density=True, alpha=0.7, label='历史收益率')
x = np.linspace(portfolio_returns.min(), portfolio_returns.max(), 100)
plt.plot(x, stats.norm.pdf(x, np.mean(portfolio_returns), 
                          np.std(portfolio_returns)), 'r-', lw=2, label='正态拟合')
plt.axvline(var_ret, color='red', linestyle='--', label=f'99% VaR: {var_ret:.4f}')
plt.title('组合VaR分析')
plt.legend()
plt.show()

4. 投资组合优化

均值-方差优化(Markowitz 模型)

from scipy.optimize import minimize
import numpy as np

def portfolio_optimization(returns, cov_matrix, target_return=None):
    """均值-方差优化"""
    n_assets = returns.shape[1]

    def portfolio_variance(weights):
        return np.dot(weights.T, np.dot(cov_matrix * 252, weights))

    def portfolio_return(weights):
        return np.sum(returns.mean() * 252 * weights)

    constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})  # 权重和为1

    if target_return:
        constraints.append({'type': 'eq', 'fun': lambda x: portfolio_return(x) - target_return})

    bounds = tuple((0, 1) for _ in range(n_assets))  # 无空头约束

    # 最小方差优化
    result_mv = minimize(portfolio_variance, 0.5 * np.ones(n_assets),
                        method='SLSQP', bounds=bounds, constraints=constraints)

    # 最大夏普比率
    def neg_sharpe(weights):
        r = portfolio_return(weights)
        vol = np.sqrt(portfolio_variance(weights))
        return -r / vol

    result_sr = minimize(neg_sharpe, 0.5 * np.ones(n_assets),
                        method='SLSQP', bounds=bounds, constraints=constraints)

    return result_mv, result_sr

# 示例优化
ret_annual = returns.mean() * 252
cov_annual = returns.cov() * 252
mv_weights, sr_weights = portfolio_optimization(returns, cov_annual)

print("最小方差权重:", dict(zip(tickers, mv_weights.x.round(4))))
print("最大夏普权重:", dict(zip(tickers, sr_weights.x.round(4))))

# 有效前沿
target_returns = np.linspace(ret_annual.min(), ret_annual.max(), 20)
frontier_vols = []
for tr in target_returns:
    _, result = portfolio_optimization(returns, cov_annual, tr)
    frontier_vols.append(np.sqrt(result.fun))

plt.figure(figsize=(10, 6))
plt.plot(frontier_vols, target_returns, 'b-', label='有效前沿')
plt.scatter(np.sqrt(cov_annual.diagonal()), ret_annual, c='red', label='单资产')
plt.xlabel('波动率')
plt.ylabel('预期收益')
plt.title('Markowitz 有效前沿')
plt.legend()
plt.show()

5. 因子模型与风险分解

Fama-French 三因子模型

from sklearn.linear_model import LinearRegression
import statsmodels.api as sm

# 获取因子数据(示例)
def get_fama_french_factors():
    """获取Fama-French因子数据"""
    # 实际使用时从Ken French网站下载或使用量化平台API
    # 这里模拟数据
    np.random.seed(42)
    dates = pd.date_range('2020-01-01', '2024-01-01', freq='M')
    factors = pd.DataFrame({
        'MKT': np.random.normal(0.008, 0.04, len(dates)),
        'SMB': np.random.normal(0.002, 0.03, len(dates)),
        'HML': np.random.normal(0.001, 0.025, len(dates))
    }, index=dates)
    return factors

factors = get_fama_french_factors()
stock_returns = portfolio_returns.resample('M').apply(lambda x: (1+x).prod()-1).dropna()

# 回归分析
X = sm.add_constant(factors)
model = sm.OLS(stock_returns, X).fit()
print(model.summary())

# 因子暴露
factor_exposures = model.params
print("因子Beta:", factor_exposures[1:])

6. 蒙特卡洛模拟与情景分析

投资组合蒙特卡洛模拟

def monte_carlo_portfolio(returns, weights, n_simulations=10000, periods=252):
    """投资组合蒙特卡洛模拟"""
    mu = returns.mean() * 252
    cov = returns.cov() * 252
    portfolio_mu = np.dot(weights, mu)
    portfolio_sigma = np.sqrt(np.dot(weights.T, np.dot(cov, weights)))

    sim_returns = np.random.multivariate_normal(mu, cov, (periods, n_simulations))
    portfolio_returns = np.dot(sim_returns, weights)

    # 年化指标
    final_values = (1 + portfolio_returns).prod(axis=0)
    annual_returns = final_values ** (1/periods) - 1
    annual_vol = np.std(portfolio_returns, axis=0) * np.sqrt(252)

    return annual_returns, annual_vol, final_values

annual_ret, annual_vol, final_values = monte_carlo_portfolio(returns, weights)

# 置信区间
ci_lower = np.percentile(annual_ret, 5)
ci_upper = np.percentile(annual_ret, 95)
print(f"年化收益95%置信区间: [{ci_lower:.2%}, {ci_upper:.2%}]")

# 可视化
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.hist(annual_ret * 100, bins=50, alpha=0.7)
plt.axvline(np.mean(annual_ret)*100, color='red', linestyle='--')
plt.title('年化收益分布')
plt.xlabel('年化收益 (%)')

plt.subplot(1, 2, 2)
plt.hist(final_values, bins=50, alpha=0.7)
plt.axvline(np.percentile(final_values, [5, 95]), color='red', linestyle='--', alpha=0.7)
plt.title('最终组合价值分布')
plt.ylabel('频次')
plt.tight_layout()
plt.show()

7. 固定收益与利率模型

债券定价与久期

def bond_price(face_value, coupon_rate, ytm, years, freq=2):
    """债券定价"""
    coupon = face_value * coupon_rate / freq
    periods = years * freq
    dt = 1 / freq

    # 现金流现值
    pv_coupons = sum([coupon / (1 + ytm/freq)**(t+1) for t in range(int(periods))])
    pv_face = face_value / (1 + ytm/freq)**periods

    return pv_coupons + pv_face

def macaulay_duration(face_value, coupon_rate, ytm, years, freq=2):
    """麦考利久期"""
    price = bond_price(face_value, coupon_rate, ytm, years, freq)
    coupon = face_value * coupon_rate / freq
    periods = years * freq

    weighted_cf = 0
    for t in range(1, int(periods)+1):
        if t == periods:
            cf = coupon + face_value
        else:
            cf = coupon
        weighted_cf += cf * t / freq / (1 + ytm/freq)**t

    duration = weighted_cf / price
    return duration

# 示例:10年期债券
price = bond_price(1000, 0.05, 0.04, 10)
duration = macaulay_duration(1000, 0.05, 0.04, 10)
print(f"债券价格: {price:.2f}")
print(f"麦考利久期: {duration:.2f}年")

8. 关键库与工具

库名功能安装命令
QuantLib专业金融计算库(期权、债券、风险)pip install QuantLib
numpy-financial金融函数(NPV、IRR、债券)pip install numpy-financial
pyfolio绩效分析与风险指标pip install pyfolio
empyrical金融绩效指标计算pip install empyrical
cvxpy凸优化(组合优化)pip install cvxpy

安装专业库示例

# QuantLib 高级期权定价
import QuantLib as ql

# 设置计算日期
calculation_date = ql.Date(17, 10, 2025)
ql.Settings.instance().evaluationDate = calculation_date

# 欧洲期权
option = ql.VanillaOption(ql.PlainVanillaPayoff(ql.Option.Call, 100),
                         ql.EuropeanExercise(ql.Date(17, 10, 2026)))

9. 学习路径与实践建议

  1. 理论基础
  • 《期权、期货及其他衍生品》(Hull)
  • 《量化投资策略》(Grinold & Kahn)
  • 《金融时间序列分析》(Tsay)
  1. Python实践
  • 从简单定价模型开始
  • 实现经典策略回测
  • 构建风险管理系统
  1. 进阶应用
  • 机器学习在量化金融中的应用
  • 高频交易系统开发
  • 另类数据处理
  1. 实盘注意事项
  • 滑点与交易成本建模
  • 实时期货与保证金管理
  • 监管合规与风控

10. 常见陷阱与最佳实践

  • 模型风险:定期验证模型假设
  • 数据偏差:注意幸存者偏差、回测过度拟合
  • 计算精度:使用双精度浮点,避免数值不稳定
  • 并发处理:蒙特卡洛模拟使用向量化或并行计算

需要深入某个特定主题(如随机波动率模型、信用风险建模)或完整策略实现,请告诉我具体需求!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注