问题描述
解决方案
强力指数 (Force Index) 交易策略
强力指数(Force Index,简称 FI)是由亚历山大·埃尔德(Alexander Elder)博士开发的指标,用于衡量价格变动背后的力量。它结合了价格变动的方向、幅度和成交量三个要素。
策略原理
强力指数的基本公式为:
$$FI = (今日收盘价 - 昨日收盘价) \times 今日成交量$$
在实际交易中,通常会对原始的 FI 进行平滑处理(通常使用 EMA)。
本策略逻辑如下:
- 计算指标:计算 13 周期指数移动平均(EMA)的强力指数。
- 买入信号:当 13日 EMA Force Index 从下向上穿过 0 轴时,表明买方力量占据主导,趋势可能转强,执行买入。
- 卖出信号:当 13日 EMA Force Index 从上向下穿过 0 轴时,表明卖方力量占据主导,趋势可能转弱,执行卖出(清仓)。
QMT 策略代码实现
以下是基于 QMT Python API 的完整策略代码。代码包含了数据获取、指标计算和交易信号执行。
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
def init(ContextInfo):
"""
初始化函数,设定策略参数和账户
"""
# 设定交易账号,请替换为您的真实资金账号
ContextInfo.account_id = '6000000000'
ContextInfo.set_account(ContextInfo.account_id)
# 设定标的股票,这里以浦发银行为例
ContextInfo.stock_code = '600000.SH'
# 设定强力指数的平滑周期,通常短线用2,中线用13
ContextInfo.fi_period = 13
# 设定取数数量,保证有足够的数据计算EMA
ContextInfo.data_count = 100
# 设定股票池
ContextInfo.set_universe([ContextInfo.stock_code])
def handlebar(ContextInfo):
"""
K线周期运行函数
"""
# 获取当前周期
period = ContextInfo.period
# 获取当前正在处理的K线索引
index = ContextInfo.barpos
# 获取历史行情数据
# 使用 get_market_data_ex 获取数据,返回字典 {code: dataframe}
# 字段需要 'close' (收盘价) 和 'volume' (成交量)
market_data = ContextInfo.get_market_data_ex(
['close', 'volume'],
[ContextInfo.stock_code],
period=period,
count=ContextInfo.data_count,
dividend_type='front', # 前复权
subscribe=True
)
if ContextInfo.stock_code not in market_data:
return
df = market_data[ContextInfo.stock_code]
# 如果数据量不足以计算指标,则返回
if len(df) < ContextInfo.fi_period + 2:
return
# --- 计算强力指数 (Force Index) ---
# 1. 计算价格变化: 今日收盘 - 昨日收盘
# diff() 函数计算当前行与上一行的差值
df['price_change'] = df['close'].diff()
# 2. 计算原始 Force Index: 价格变化 * 成交量
df['raw_fi'] = df['price_change'] * df['volume']
# 3. 计算平滑后的 Force Index (EMA)
# 使用 pandas 的 ewm 方法计算指数加权移动平均
df['ema_fi'] = df['raw_fi'].ewm(span=ContextInfo.fi_period, adjust=False).mean()
# 获取最新和上一个周期的指标值
# iloc[-1] 是当前K线的值,iloc[-2] 是上一根K线的值
current_fi = df['ema_fi'].iloc[-1]
prev_fi = df['ema_fi'].iloc[-2]
# 打印调试信息 (可选)
# print(f"Time: {df.index[-1]}, FI: {current_fi:.2f}, Prev_FI: {prev_fi:.2f}")
# --- 交易逻辑 ---
# 获取当前持仓
positions = get_trade_detail_data(ContextInfo.account_id, 'stock', 'position')
current_holding = 0
for pos in positions:
if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == ContextInfo.stock_code:
current_holding = pos.m_nVolume
break
# 信号判断
# 1. 买入信号: 上一根 FI < 0 且 当前 FI > 0 (上穿0轴)
if prev_fi < 0 and current_fi > 0:
# 如果当前没有持仓,则全仓买入
# order_target_percent 需要在回测模式或实盘中配合 set_account 使用
# 这里设置为目标仓位 100% (1.0)
order_target_percent(ContextInfo.stock_code, 1.0, ContextInfo, ContextInfo.account_id)
print(f"买入信号触发: {ContextInfo.stock_code}, FI上穿0轴")
# 2. 卖出信号: 上一根 FI > 0 且 当前 FI < 0 (下穿0轴)
elif prev_fi > 0 and current_fi < 0:
# 如果有持仓,则清仓
# 设置目标仓位为 0%
order_target_percent(ContextInfo.stock_code, 0.0, ContextInfo, ContextInfo.account_id)
print(f"卖出信号触发: {ContextInfo.stock_code}, FI下穿0轴")
代码关键点解析
-
数据获取 (
get_market_data_ex):- 我们使用了
get_market_data_ex接口,这是 QMT 推荐的高效数据接口。 - 我们需要
close和volume两个字段来计算 FI。 dividend_type='front'确保使用前复权数据,避免分红派息导致的价格跳空影响指标计算。
- 我们使用了
-
指标计算 (Pandas):
df['close'].diff(): 快速计算 $C_t - C_{t-1}$。ewm(span=N): Pandas 提供的指数加权移动平均函数,用于平滑原始的强力指数,使其更适合判断趋势。
-
交易函数 (
order_target_percent):- 使用
order_target_percent可以简化仓位管理逻辑。 1.0代表调整仓位至总资产的 100%(全仓)。0.0代表调整仓位至 0%(清仓)。- 注意: 在实盘中,请确保已通过
ContextInfo.set_account绑定了正确的资金账号。
- 使用
-
编码格式:
- 文件首行
# -*- coding: gbk -*-是必须的,以防止中文注释在 QMT 编辑器中出现乱码。
- 文件首行
Q&A
Q1: 强力指数的周期参数(N)应该如何选择?
A1: 埃尔德博士建议:
- N=2: 用于捕捉短期的买卖机会。当趋势向上时,2日 EMA FI 跌破0轴通常是买入时机(回调买入)。
- N=13: 用于识别中期趋势。如本策略所示,上穿0轴看多,下穿0轴看空。
Q2: 为什么使用 get_market_data_ex 而不是 get_market_data?
A2: get_market_data 是旧版接口,官方已不建议使用。get_market_data_ex 返回标准的 Pandas DataFrame 格式,处理数据更加方便且性能更好。
Q3: 这个策略可以直接用于实盘吗?
A3: 代码逻辑是完整的,但实盘前建议:
- 在 QMT 回测模块中进行历史回测,验证收益风险比。
- 修改
ContextInfo.account_id为您的真实账号。 - 考虑添加风控逻辑(如止损、最大持仓限制等),因为单一指标策略通常存在假突破风险。