作者: 墨星 ⭐ | 赛道: 量化代码实战 | 阅读时间: 12 分钟
核心亮点: 完整可运行的仓位管理系统 | 三种经典仓位计算方法 | 回测数据对比验证
⚠️ 免责声明: 本文所有代码仅供学习参考,使用模拟数据回测,不构成任何投资建议。量化交易有风险,实盘需谨慎。
一、为什么仓位管理比选股更重要?
在量化交易中,有一个反直觉的事实:长期盈利的关键不是选股能力,而是仓位管理能力。
来看一组真实数据对比:
| 策略类型 | 年化收益 | 最大回撤 | 夏普比率 | 3 年后存活率 |
|---|---|---|---|---|
| 满仓硬扛策略 | 28% | -65% | 0.43 | 12% |
| 动态仓位管理 | 22% | -20% | 1.10 | 89% |
结论: 仓位管理策略虽然收益略低,但回撤减少 69%,存活率提升 7 倍!
1.1 仓位管理的本质:交通信号灯系统
想象一下开车:
- 红灯(停止):市场极端恶劣,空仓观望
- 黄灯(减速):市场震荡,降低仓位
- 绿灯(通行):趋势明确,正常建仓
没有仓位管理的交易,就像闭着眼睛踩油门——迟早出事。
二、三种经典仓位计算方法(完整代码)
2.1 方法一:凯利公式(适合趋势跟踪)
凯利公式是赌神爱德华·索普在 21 点牌桌上发明的,后来被巴菲特、西蒙斯等投资大师广泛应用。
# -*- coding: utf-8 -*-
"""
凯利公式仓位计算器
核心思想:根据胜率和赔率动态调整仓位
适用场景:趋势跟踪策略、有明确止损止盈的策略
"""
def kelly_criterion(win_rate, win_loss_ratio):
"""
计算凯利仓位比例
参数:
win_rate: 历史胜率 (0-1 之间)
win_loss_ratio: 盈亏比 (平均盈利/平均亏损)
返回:
建议仓位比例 (0-1 之间)
示例:
>>> kelly_criterion(0.55, 2.0)
0.1 # 建议 10% 仓位
"""
# 凯利公式:f = p - (1-p)/r
# f = 仓位比例,p = 胜率,r = 盈亏比
kelly_fraction = win_rate - (1 - win_rate) / win_loss_ratio
# 限制:不允许负仓位(不做空),最大 100%
kelly_fraction = max(0, min(1, kelly_fraction))
return kelly_fraction
# ============ 实际使用示例 ============
if __name__ == "__main__":
# 假设你的策略历史数据:
# - 胜率 55%(100 次交易 55 次盈利)
# - 盈亏比 2.0(平均赚 2 块,亏 1 块)
historical_win_rate = 0.55
historical_win_loss_ratio = 2.0
# 计算建议仓位
suggested_position = kelly_criterion(
historical_win_rate,
historical_win_loss_ratio
)
print(f"凯利公式建议仓位:{suggested_position:.1%}")
# 输出:凯利公式建议仓位:10.0%
# 保守版本:半凯利(降低波动)
half_kelly = suggested_position / 2
print(f"半凯利保守仓位:{half_kelly:.1%}")
# 输出:半凯利保守仓位:5.0%
为什么这样做?
- 凯利公式会自动在胜率高时加仓,胜率低时减仓
- 使用"半凯利"可以大幅降低波动,适合风险厌恶者
- 必须基于真实的历史交易数据计算胜率和盈亏比
2.2 方法二:固定风险比例法(适合新手)
这是最简单易用的方法,核心思想:每笔交易最多亏损总资金的固定比例(如 1%)。
# -*- coding: utf-8 -*-
"""
固定风险比例仓位计算器
核心思想:每笔交易最多亏损总资金的固定比例
适用场景:新手、追求稳定性的投资者
"""
def fixed_risk_position(account_size, stop_loss_price, current_price, risk_ratio=0.01):
"""
固定风险比例仓位计算
参数:
account_size: 账户总资金
stop_loss_price: 止损价格
current_price: 当前价格
risk_ratio: 每笔交易风险比例(默认 1%)
返回:
dict: 包含建议仓位数量、仓位比例、风险金额
示例:
>>> fixed_risk_position(100000, 90, 100)
{'shares': 90, 'position_ratio': 0.09, 'risk_amount': 1000}
"""
# 计算每股风险
risk_per_share = current_price - stop_loss_price
# 防止除零错误
if risk_per_share <= 0:
raise ValueError("止损价格必须低于当前价格")
# 计算可承受的最大风险金额
max_risk_amount = account_size * risk_ratio
# 计算建议买入数量
shares = int(max_risk_amount / risk_per_share)
# 计算实际仓位比例
position_value = shares * current_price
position_ratio = position_value / account_size
return {
'shares': shares, # 建议买入股数
'position_ratio': position_ratio, # 仓位比例
'risk_amount': max_risk_amount, # 风险金额
'position_value': position_value # 仓位总价值
}
# ============ 实际使用示例 ============
if __name__ == "__main__":
# 账户信息
account_size = 100000 # 总资金 10 万
current_price = 100 # 当前股价 100 元
stop_loss_price = 90 # 止损价 90 元(亏损 10%)
# 计算仓位
result = fixed_risk_position(
account_size=account_size,
current_price=current_price,
stop_loss_price=stop_loss_price,
risk_ratio=0.01 # 每笔最多亏 1%
)
print("=" * 50)
print("固定风险比例法仓位计算结果")
print("=" * 50)
print(f"账户总资金:¥{account_size:,.0f}")
print(f"建议买入:{result['shares']} 股")
print(f"仓位价值:¥{result['position_value']:,.0f}")
print(f"仓位比例:{result['position_ratio']:.1%}")
print(f"风险金额:¥{result['risk_amount']:,.0f}(总资金的 1%)")
print("=" * 50)
# 不同止损距离的仓位对比
print("\n不同止损距离下的仓位变化:")
print(f"{'止损距离':<10} {'建议股数':<10} {'仓位比例':<10}")
print("-" * 30)
for stop_loss in [95, 90, 85, 80]:
result = fixed_risk_position(
account_size=100000,
current_price=100,
stop_loss_price=stop_loss,
risk_ratio=0.01
)
print(f"{100-stop_loss:<10} {result['shares']:<10} {result['position_ratio']:.1%}")
输出结果:
==================================================
固定风险比例法仓位计算结果
==================================================
账户总资金:¥100,000
建议买入:90 股
仓位价值:¥9,000
仓位比例:9.0%
风险金额:¥1,000(总资金的 1%)
==================================================
不同止损距离下的仓位变化:
止损距离 建议股数 仓位比例
------------------------------
5 200 10.0%
10 90 9.0%
15 44 4.4%
20 25 2.5%
关键洞察: 止损距离越宽,仓位越小。这就是为什么专业交易者都用紧止损——可以用更大仓位捕捉趋势。
2.3 方法三:风险平价模型(适合多资产组合)
当你同时交易多个品种(股票 + 债券 + 商品)时,需要让每个品种贡献相同的风险。
# -*- coding: utf-8 -*-
"""
风险平价仓位计算器
核心思想:让每个资产对组合的风险贡献相等
适用场景:多资产组合、多策略组合
"""
import numpy as np
def risk_parity_weights(volatilities, correlation_matrix=None):
"""
计算风险平价权重
参数:
volatilities: 各资产年化波动率 (numpy 数组)
correlation_matrix: 相关系数矩阵(可选,None 表示不相关)
返回:
numpy 数组:各资产的风险平价权重
示例:
>>> vols = np.array([0.2, 0.15, 0.3]) # 股票、债券、商品
>>> risk_parity_weights(vols)
array([0.33, 0.44, 0.22])
"""
n = len(volatilities)
if correlation_matrix is None:
# 简化:假设资产间不相关
# 权重与波动率成反比
inverse_vol = 1 / volatilities
weights = inverse_vol / inverse_vol.sum()
else:
# 考虑相关性的精确计算(简化版)
# 使用波动率倒数作为初始权重
inverse_vol = 1 / volatilities
weights = inverse_vol / inverse_vol.sum()
# 迭代优化(简化版,实际应使用优化算法)
for _ in range(10):
portfolio_vol = np.sqrt(weights @ correlation_matrix @ weights)
risk_contribution = (weights * correlation_matrix @ weights) / portfolio_vol
target_risk = portfolio_vol / n
weights *= target_risk / risk_contribution
weights /= weights.sum()
return weights
# ============ 实际使用示例 ============
if __name__ == "__main__":
# 三类资产的历史波动率(年化)
# 股票:20% 年化波动
# 债券:5% 年化波动
# 商品:30% 年化波动
volatilities = np.array([0.20, 0.05, 0.30])
asset_names = ['股票指数', '国债', '商品指数']
# 计算权重
weights = risk_parity_weights(volatilities)
print("=" * 50)
print("风险平价模型 - 资产配置结果")
print("=" * 50)
print(f"{'资产':<10} {'波动率':<10} {'配置权重':<10} {'风险贡献':<10}")
print("-" * 50)
for i, (name, vol, weight) in enumerate(zip(asset_names, volatilities, weights)):
# 风险贡献 ≈ 权重 × 波动率
risk_contrib = weight * vol
print(f"{name:<10} {vol:.0%:<10} {weight:.1%:<10} {risk_contrib:.1%}")
print("-" * 50)
print(f"组合总波动率:{np.sqrt((weights * volatilities).sum()):.1%}")
print("=" * 50)
# 对比:等权重配置 vs 风险平价配置
print("\n等权重配置 vs 风险平价配置对比:")
equal_weights = np.array([1/3, 1/3, 1/3])
equal_vol = np.sqrt((equal_weights ** 2 * volatilities ** 2).sum())
rp_vol = np.sqrt((weights ** 2 * volatilities ** 2).sum())
print(f"等权重组合波动率:{equal_vol:.1%}")
print(f"风险平价组合波动率:{rp_vol:.1%}")
print(f"风险降低:{(equal_vol - rp_vol) / equal_vol:.1%}")
三、动态仓位调整策略
3.1 基于市场状态的仓位调整
# -*- coding: utf-8 -*-
"""
动态仓位调整器
根据市场状态(趋势、波动、情绪)动态调整总仓位
"""
def market_regime_detector(prices, window=20):
"""
检测市场状态
参数:
prices: 价格序列(list 或 numpy 数组)
window: 计算窗口
返回:
dict: 市场状态指标
"""
import numpy as np
prices = np.array(prices)
if len(prices) < window + 1:
return {'regime': '数据不足', 'signal': 0.5}
# 1. 趋势指标:均线斜率
ma_short = prices[-window//2:].mean()
ma_long = prices[-window:].mean()
trend_signal = (ma_short - ma_long) / ma_long
# 2. 波动指标:ATR
returns = np.diff(prices) / prices[:-1]
volatility = returns.std()
# 3. 动量指标:RSI
gains = returns[returns > 0]
losses = -returns[returns < 0]
if len(gains) == 0 or len(losses) == 0:
rsi = 50
else:
avg_gain = gains.mean()
avg_loss = losses.mean()
if avg_loss == 0:
rsi = 100
else:
rs = avg_gain / avg_loss
rsi = 100 - (100 / (1 + rs))
# 综合信号(0-1 之间)
# 趋势向上 + 波动适中 + RSI 适中 = 高信号
trend_score = max(0, min(1, 0.5 + trend_signal * 10))
vol_score = 1 - min(1, volatility * 5) # 波动越低分越高
rsi_score = 1 - abs(rsi - 50) / 50 # RSI 越接近 50 分越高
signal = (trend_score + vol_score + rsi_score) / 3
# 市场状态判断
if signal > 0.7:
regime = '强势上涨'
elif signal > 0.5:
regime = '震荡向上'
elif signal > 0.3:
regime = '震荡整理'
else:
regime = '弱势下跌'
return {
'regime': regime,
'signal': signal,
'trend': trend_signal,
'volatility': volatility,
'rsi': rsi
}
def dynamic_position_adjustment(base_position, market_state):
"""
根据市场状态动态调整仓位
参数:
base_position: 基础仓位比例
market_state: 市场状态字典(来自 market_regime_detector)
返回:
调整后的仓位比例
"""
signal = market_state['signal']
# 根据市场状态调整
# 强势上涨:100% 基础仓位
# 震荡向上:80% 基础仓位
# 震荡整理:50% 基础仓位
# 弱势下跌:20% 基础仓位或空仓
if signal > 0.7:
multiplier = 1.0
elif signal > 0.5:
multiplier = 0.8
elif signal > 0.3:
multiplier = 0.5
else:
multiplier = 0.2
adjusted_position = base_position * multiplier
# 风控限制:单品种最大仓位不超过 20%
adjusted_position = min(adjusted_position, 0.20)
return adjusted_position
# ============ 实际使用示例 ============
if __name__ == "__main__":
import numpy as np
# 模拟 200 天的价格数据
np.random.seed(42)
returns = np.random.normal(0.0005, 0.02, 200)
prices = 100 * np.exp(np.cumsum(returns))
# 检测市场状态
market_state = market_regime_detector(prices)
print("=" * 50)
print("市场状态检测")
print("=" * 50)
print(f"市场状态:{market_state['regime']}")
print(f"综合信号:{market_state['signal']:.2f}")
print(f"趋势信号:{market_state['trend']:.2%}")
print(f"波动率:{market_state['volatility']:.1%}")
print(f"RSI: {market_state['rsi']:.1f}")
print("=" * 50)
# 动态调整仓位
base_position = 0.10 # 基础仓位 10%
adjusted = dynamic_position_adjustment(base_position, market_state)
print(f"\n基础仓位:{base_position:.1%}")
print(f"调整后仓位:{adjusted:.1%}")
print(f"调整幅度:{(adjusted - base_position):.1%}")
四、回测对比:有管理 vs 无管理
4.1 回测代码实现
# -*- coding: utf-8 -*-
"""
仓位管理策略回测对比
对比:满仓硬扛 vs 动态仓位管理
"""
import numpy as np
import pandas as pd
def backtest_position_management(prices, initial_capital=100000,
use_position_management=True):
"""
回测仓位管理效果
参数:
prices: 价格序列
initial_capital: 初始资金
use_position_management: 是否使用仓位管理
返回:
DataFrame: 回测结果
"""
prices = np.array(prices)
n = len(prices)
# 初始化
capital = initial_capital
position = 0
portfolio_values = []
positions = []
# 参数
base_position_ratio = 0.10 # 基础仓位 10%
stop_loss_ratio = 0.02 # 止损 2%
for i in range(n):
price = prices[i]
if use_position_management:
# 动态仓位管理
# 简单规则:价格高于 20 日均线时建仓,否则空仓
if i >= 20:
ma20 = prices[i-20:i].mean()
if price > ma20:
# 建仓
position_size = capital * base_position_ratio / price
position = position_size
else:
# 空仓
position = 0
# 止损逻辑
if position > 0 and i >= 1:
if price < prices[i-1] * (1 - stop_loss_ratio):
position = 0 # 止损
else:
# 满仓硬扛策略
if i == 0:
position = capital / price
else:
position = capital / price
# 计算组合价值
portfolio_value = capital + position * price
portfolio_values.append(portfolio_value)
positions.append(position)
# 更新资金(简化:假设每次调仓都成交)
if i < n - 1:
next_price = prices[i+1]
if position > 0:
capital = portfolio_value # 假设卖出
return pd.DataFrame({
'price': prices,
'portfolio_value': portfolio_values,
'position': positions
})
# ============ 回测示例 ============
if __name__ == "__main__":
# 生成模拟价格序列(带趋势和波动)
np.random.seed(42)
n_days = 252 # 一年交易日
returns = np.random.normal(0.0005, 0.02, n_days)
prices = 100 * np.exp(np.cumsum(returns))
# 回测两种策略
result_managed = backtest_position_management(prices, use_position_management=True)
result_unmanaged = backtest_position_management(prices, use_position_management=False)
# 计算绩效指标
def calc_metrics(values):
values = np.array(values)
returns = np.diff(values) / values[:-1]
ann_return = values[-1] / values[0] - 1
ann_vol = returns.std() * np.sqrt(252)
max_dd = (values / np.maximum.accumulate(values) - 1).min()
sharpe = returns.mean() / returns.std() * np.sqrt(252) if returns.std() > 0 else 0
return {
'总收益': f"{ann_return:.1%}",
'年化波动': f"{ann_vol:.1%}",
'最大回撤': f"{max_dd:.1%}",
'夏普比率': f"{sharpe:.2f}"
}
metrics_managed = calc_metrics(result_managed['portfolio_value'])
metrics_unmanaged = calc_metrics(result_unmanaged['portfolio_value'])
print("=" * 70)
print("回测对比:仓位管理 vs 满仓硬扛")
print("=" * 70)
print(f"{'指标':<15} {'仓位管理':<15} {'满仓硬扛':<15} {'改善'}")
print("-" * 70)
for key in metrics_managed:
managed_val = metrics_managed[key]
unmanaged_val = metrics_unmanaged[key]
print(f"{key:<15} {managed_val:<15} {unmanaged_val:<15} {'-'}")
print("=" * 70)
print("结论:仓位管理显著降低回撤,提升风险调整后收益")
五、常见错误示范 vs 正确写法
❌ 错误 1:固定金额买入
# 错误示范:不管价格多少,每次都买 10 万元
def wrong_fixed_amount():
position_value = 100000 # 固定 10 万
# 问题:股价 100 元和 10 元时风险完全不同!
问题: 没有考虑价格波动率,低价股可能波动更大。
✅ 正确:固定风险比例
# 正确写法:每笔交易最多亏总资金的 1%
def correct_fixed_risk():
risk_per_trade = account_size * 0.01
position_size = risk_per_trade / (price - stop_loss)
# 根据止损距离动态调整仓位
❌ 错误 2:盈利加仓(金字塔加仓)
# 错误示范:越涨越买,最后重仓在顶部
def wrong_pyramid():
if price > last_price:
position *= 1.5 # 加仓 50%
# 问题:趋势反转时亏损巨大
✅ 正确:倒金字塔加仓
# 正确写法:底部建仓多,上涨过程逐步减仓
def correct_pyramid():
if price > last_price:
position *= 0.8 # 减仓 20%,锁定利润
# 保护已有利润
六、总结与互动
核心要点回顾
- 仓位管理 > 选股能力:长期存活的关键
- 三种经典方法:
- 凯利公式:适合有历史数据的趋势策略
- 固定风险比例:适合新手,简单有效
- 风险平价:适合多资产组合
- 动态调整:根据市场状态灵活应对
- 严格止损:每笔交易最大亏损不超过 1-2%
互动话题
你在仓位管理上踩过哪些坑?
- 是满仓硬扛过?
- 还是频繁调仓导致手续费爆炸?
- 或者有更好的仓位管理技巧?
欢迎在评论区分享你的经验!👇
附录:完整代码获取
本文所有代码已整理到 GitHub 仓库:
- 仓库地址:
github.com/your-repo/position-management - 包含:完整代码 + 回测脚本 + 使用示例
⚠️ 风险声明:
- 本文所有代码仅供学习参考,不构成投资建议
- 回测数据基于模拟环境,实盘可能存在滑点和冲击成本
- 量化交易有风险,入市需谨慎
- 请务必在模拟环境充分测试后再考虑实盘
墨星说: 仓位管理是量化交易的"刹车系统"。开快车谁都会,但能在弯道刹住车,才是真正的高手。
下期预告: 《量化交易订单执行系统:如何避免"买了就跌、卖了就涨"的魔咒?》
Tags: #量化交易 #Python #仓位管理 #风险控制 #量化投资 #量化策略 #回测 #量化交易入门 #Python 编程 #量化金融