3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在 QMT 中组合多个策略并进行资金分配,最佳的实践方式是采用 面向对象编程 (OOP) 的思想。
我们可以将每个独立的策略封装成一个 Python class(类)。主程序的 init 函数负责实例化这些策略并分配资金比例,主程序的 handlebar 函数负责循环调用这些策略的执行逻辑。
handlebar,handlebar 再驱动所有子策略运行。以下是一个完整的示例代码。它包含两个不同的策略:
这两个策略将同时运行,分别交易不同的股票,并按比例分配资金。
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
# ==============================================================================
# 1. 定义策略基类与子策略
# ==============================================================================
class BaseStrategy:
"""
策略基类,定义通用接口
"""
def __init__(self, name, stock_code, capital_ratio):
self.name = name # 策略名称
self.stock_code = stock_code # 策略操作的标的
self.capital_ratio = capital_ratio # 资金分配比例 (0.0 - 1.0)
def on_bar(self, context, total_asset):
"""
每根K线执行的逻辑,由主函数调用
total_asset: 当前账户总资产
"""
pass
class TrendStrategy(BaseStrategy):
"""
子策略1:双均线趋势策略
逻辑:短期均线上穿长期均线买入,下穿卖出
"""
def __init__(self, name, stock_code, capital_ratio, short_period=5, long_period=20):
super().__init__(name, stock_code, capital_ratio)
self.short_period = short_period
self.long_period = long_period
def on_bar(self, context, total_asset):
# 1. 获取数据
# 获取足够的历史数据以计算均线
data = context.get_market_data_ex(
['close'],
[self.stock_code],
period='1d',
count=self.long_period + 5,
dividend_type='front'
)
if self.stock_code not in data or data[self.stock_code].empty:
return
close_prices = data[self.stock_code]['close']
if len(close_prices) < self.long_period:
return
# 2. 计算指标
ma_short = close_prices.rolling(self.short_period).mean()
ma_long = close_prices.rolling(self.long_period).mean()
curr_short = ma_short.iloc[-1]
curr_long = ma_long.iloc[-1]
prev_short = ma_short.iloc[-2]
prev_long = ma_long.iloc[-2]
# 3. 计算本策略可用资金 (目标持仓市值)
# 策略的目标市值 = 总资产 * 分配比例
target_value = total_asset * self.capital_ratio
# 4. 交易逻辑
# 金叉:短线上穿长线 -> 买入(满仓该策略分配的额度)
if prev_short < prev_long and curr_short > curr_long:
print(f"[{self.name}] {self.stock_code} 金叉买入,目标市值: {target_value:.2f}")
order_target_value(self.stock_code, target_value, context, context.account_id)
# 死叉:短线下穿长线 -> 卖出(清仓)
elif prev_short > prev_long and curr_short < curr_long:
print(f"[{self.name}] {self.stock_code} 死叉卖出,目标市值: 0")
order_target_value(self.stock_code, 0, context, context.account_id)
class ReversalStrategy(BaseStrategy):
"""
子策略2:RSI反转策略
逻辑:RSI < 30 买入,RSI > 70 卖出
"""
def __init__(self, name, stock_code, capital_ratio, rsi_period=14):
super().__init__(name, stock_code, capital_ratio)
self.rsi_period = rsi_period
def calculate_rsi(self, series, period):
delta = series.diff()
up = delta.clip(lower=0)
down = -1 * delta.clip(upper=0)
ma_up = up.rolling(window=period).mean()
ma_down = down.rolling(window=period).mean()
rsi = ma_up / (ma_up + ma_down) * 100
return rsi
def on_bar(self, context, total_asset):
# 1. 获取数据
data = context.get_market_data_ex(
['close'],
[self.stock_code],
period='1d',
count=self.rsi_period + 20,
dividend_type='front'
)
if self.stock_code not in data or data[self.stock_code].empty:
return
close_prices = data[self.stock_code]['close']
if len(close_prices) < self.rsi_period:
return
# 2. 计算指标
rsi_series = self.calculate_rsi(close_prices, self.rsi_period)
current_rsi = rsi_series.iloc[-1]
# 3. 计算本策略可用资金
target_value = total_asset * self.capital_ratio
# 4. 交易逻辑
# 超卖:RSI < 30 -> 抄底买入
if current_rsi < 30:
print(f"[{self.name}] {self.stock_code} RSI超卖({current_rsi:.2f}),买入")
order_target_value(self.stock_code, target_value, context, context.account_id)
# 超买:RSI > 70 -> 止盈卖出
elif current_rsi > 70:
print(f"[{self.name}] {self.stock_code} RSI超买({current_rsi:.2f}),卖出")
order_target_value(self.stock_code, 0, context, context.account_id)
# ==============================================================================
# 2. QMT 主程序入口
# ==============================================================================
def init(ContextInfo):
# 设置账号 (请替换为您的真实账号)
ContextInfo.account_id = '600000248'
ContextInfo.set_account(ContextInfo.account_id)
# 初始化策略列表
ContextInfo.strategies = []
# --- 配置策略组合 ---
# 策略A:趋势策略,操作 平安银行,分配 40% 资金
s1 = TrendStrategy(
name="趋势策略A",
stock_code="000001.SZ",
capital_ratio=0.4,
short_period=5,
long_period=10
)
ContextInfo.strategies.append(s1)
# 策略B:反转策略,操作 贵州茅台,分配 60% 资金
s2 = ReversalStrategy(
name="反转策略B",
stock_code="600519.SH",
capital_ratio=0.6,
rsi_period=14
)
ContextInfo.strategies.append(s2)
# 设置股票池(将所有策略用到的股票加入,以便回测或行情订阅)
all_stocks = [s.stock_code for s in ContextInfo.strategies]
ContextInfo.set_universe(all_stocks)
# 回测参数设置
ContextInfo.start = '20220101'
ContextInfo.end = '20230101'
ContextInfo.benchmark = '000300.SH'
ContextInfo.capital = 1000000 # 回测初始资金
def handlebar(ContextInfo):
# 跳过未完成的K线(如果是日线策略)
if not ContextInfo.is_last_bar():
return
# 1. 获取当前账户总资产
# 回测模式下使用 ContextInfo.capital + 盈亏 (简化处理直接取净值 * 初始资金,或者模拟计算)
# 实盘模式下使用 get_trade_detail_data 获取真实资产
total_asset = 0
if ContextInfo.do_back_test:
# 回测中获取当前净值 * 初始资金作为总资产估算
# 注意:QMT回测中直接获取动态总资产比较复杂,这里用 get_net_value 近似
# 或者简单地假设资金固定,仅做演示。
# 更严谨的回测写法是根据持仓市值+现金计算。
net_value = ContextInfo.get_net_value(ContextInfo.barpos)
total_asset = ContextInfo.capital * net_value
else:
# 实盘模式:获取账户资金
acct_info = get_trade_detail_data(ContextInfo.account_id, 'stock', 'account')
if len(acct_info) > 0:
total_asset = acct_info[0].m_dBalance # 总资产
else:
print("未获取到账户信息")
return
# 2. 遍历并运行所有策略
for strategy in ContextInfo.strategies:
try:
strategy.on_bar(ContextInfo, total_asset)
except Exception as e:
print(f"策略 {strategy.name} 运行出错: {e}")
BaseStrategy 类:
name(策略名)、stock_code(操作标的)、capital_ratio(资金占比)。on_bar 方法。TrendStrategy 和 ReversalStrategy 类:
__init__ 中接收各自特有的参数(如均线周期、RSI周期)。on_bar 中,它们独立获取数据、计算指标。order_target_value 下单。目标价值计算公式为 total_asset * self.capital_ratio。这意味着如果总资产是 100 万,分配比例 0.4,策略会努力将持仓市值调整到 40 万。如果当前持仓 0,它会买入 40 万;如果当前持仓 40 万且触发卖出信号,它会卖出至 0。init 函数:
ContextInfo.strategies。handlebar 函数:
for 循环,依次调用每个策略对象的 on_bar 方法,将控制权暂时移交给子策略。init 中 append 进去即可,无需修改 handlebar 的逻辑。capital_ratio 严格控制每个策略能使用的最大资金量,防止某个策略亏损过大拖累整体,或者某个策略占用过多资金导致其他策略无法开仓。get_market_data_ex 是最高效的方式,它返回 Pandas DataFrame,方便计算指标。init 中将 ContextInfo.account_id 修改为您真实的资金账号。Q: 如果我想让策略A分配固定金额(如10万元),而不是比例,怎么改?
A: 可以在 BaseStrategy 中增加一个标记,比如 is_fixed_capital=True。在 on_bar 计算 target_value 时,判断如果是固定金额模式,直接使用该金额;如果是比例模式,则用 total_asset * ratio。
Q: 这种写法支持期货吗?
A: 框架支持,但下单函数需要修改。期货通常使用 buy_open, sell_close 等函数,且需要处理多空方向。order_target_value 主要用于股票。你需要在子策略中重写下单逻辑。
Q: 如何查看每个策略的盈亏?
A: QMT 的原生界面只显示账户总盈亏。如果需要分策略统计,你需要在 BaseStrategy 中自己记录每次交易的盈亏,或者将交易记录输出到 CSV 文件中进行盘后分析。