问题描述
解决方案
CMF (Chaikin Money Flow) 资金流向指标交易策略
策略原理
蔡金资金流向指标 (Chaikin Money Flow, CMF) 是由 Marc Chaikin 开发的一种技术分析指标,用于衡量一段时间内资金的累积流入或流出情况。它结合了价格和成交量,通过比较收盘价与当日价格区间的关系来判断买卖压力。
计算公式
- 资金流量乘数 (Money Flow Multiplier, MFM):
$$ MFM = \frac{(Close - Low) - (High - Close)}{High - Low} $$
或者简化为:
$$ MFM = \frac{2 \times Close - High - Low}{High - Low} $$ - 资金流量成交量 (Money Flow Volume, MFV):
$$ MFV = MFM \times Volume $$ - CMF (N日):
$$ CMF = \frac{\sum_{i=1}^{N} MFV_i}{\sum_{i=1}^{N} Volume_i} $$
交易逻辑
本策略采用经典的 CMF 零轴交叉策略:
- 买入信号:当 CMF 指标由下向上突破 0 轴时,视为资金流入,市场转强,执行买入。
- 卖出信号:当 CMF 指标由上向下跌破 0 轴时,视为资金流出,市场转弱,执行卖出(清仓)。
策略代码实现
以下是基于聚宽 (JoinQuant) API 实现的完整策略代码。该策略以平安银行 (000001.XSHE) 为例,计算 20 日 CMF 指标进行择时交易。
# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd
def initialize(context):
"""
初始化函数,设定基准、股票池、费用等
"""
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
log.set_level('order', 'error')
# 设定股票类每笔交易时的手续费
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
# 定义全局变量
g.security = '000001.XSHE' # 操作标的:平安银行
g.N = 20 # CMF计算周期,通常为20或21天
# 每天开盘时运行策略
run_daily(market_open, time='09:30')
def calculate_cmf(security, n_days):
"""
计算 CMF 指标
:param security: 股票代码
:param n_days: 计算周期
:return: CMF 值
"""
# 获取过去 n_days 的行情数据
# 包含最高价、最低价、收盘价、成交量
df = attribute_history(security, n_days, '1d', ['high', 'low', 'close', 'volume'])
# 数据清洗:防止 High - Low 为 0 导致除零错误
# 如果 High == Low (如一字涨跌停),将分母设为一个极小值或处理 MFM 为 0
high_low_diff = df['high'] - df['low']
high_low_diff[high_low_diff == 0] = 0.0001 # 避免除以0
# 1. 计算资金流量乘数 (Money Flow Multiplier, MFM)
# 公式: [(Close - Low) - (High - Close)] / (High - Low)
# 简化公式: (2 * Close - High - Low) / (High - Low)
mfm = (2 * df['close'] - df['high'] - df['low']) / high_low_diff
# 2. 计算资金流量成交量 (Money Flow Volume, MFV)
mfv = mfm * df['volume']
# 3. 计算 CMF
# CMF = Sum(MFV, n) / Sum(Volume, n)
sum_mfv = mfv.sum()
sum_vol = df['volume'].sum()
if sum_vol == 0:
return 0
cmf = sum_mfv / sum_vol
return cmf
def market_open(context):
"""
每日开盘运行逻辑
"""
security = g.security
# 获取当前的 CMF 值
current_cmf = calculate_cmf(security, g.N)
# 获取当前持仓
position = context.portfolio.positions[security]
# 获取当前可用资金
cash = context.portfolio.available_cash
# 记录日志以便调试
# log.info(f"日期: {context.current_dt.date()}, CMF: {current_cmf:.4f}")
# 绘制 CMF 曲线到回测图表
record(CMF=current_cmf)
# === 交易逻辑 ===
# 买入信号:CMF > 0 且当前无持仓
# 含义:资金呈现净流入状态,买方力量占优
if current_cmf > 0 and position.closeable_amount == 0:
# 全仓买入
order_value(security, cash)
log.info(f"买入信号触发 (CMF={current_cmf:.4f}):买入 {security}")
# 卖出信号:CMF < 0 且当前有持仓
# 含义:资金呈现净流出状态,卖方力量占优
elif current_cmf < 0 and position.closeable_amount > 0:
# 清仓卖出
order_target(security, 0)
log.info(f"卖出信号触发 (CMF={current_cmf:.4f}):卖出 {security}")
常见问题 (Q&A)
Q: CMF 指标的 N 值一般设置多少?
A: 标准的 CMF 设置通常为 20 天或 21 天,这代表了一个月的交易日。较短的周期(如 10 天)对价格变化更敏感,但假信号更多;较长的周期(如 60 天)更能反映长期趋势,但信号会有滞后。
Q: 为什么代码中要处理 High - Low == 0 的情况?
A: 在股票出现“一字板”(涨停或跌停开盘且全天价格未变)时,最高价等于最低价。在计算 MFM 公式时,分母 (High - Low) 会变成 0,导致程序报错。代码中将其替换为一个极小值或进行特殊处理是为了保证程序的健壮性。
Q: 这个策略可以用于多只股票吗?
A: 可以。目前的示例代码针对单只股票 (g.security)。若要操作多只股票,可以将股票列表存入 g.stocks,然后在 market_open 函数中使用 for 循环遍历每只股票,分别计算 CMF 并独立判断买卖逻辑。
Q: CMF 指标有什么局限性?
A: CMF 是一个震荡指标,在强劲的趋势行情中可能出现背离(例如价格上涨但 CMF 下降)。此外,如果开盘跳空高开但收盘价低于开盘价(假阴线),CMF 可能会将其误判为卖压。通常建议结合均线(MA)或相对强弱指标(RSI)一起使用以过滤假信号。