首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >miniqmt、backtesting实现双均线策略例子

miniqmt、backtesting实现双均线策略例子

作者头像
子晓聊技术
发布2026-04-23 16:39:57
发布2026-04-23 16:39:57
1350
举报
文章被收录于专栏:子晓AI量化子晓AI量化

有同学问我有没有miniqmt结合策略回测的完整例子。 这里用miniqmt、backtesting写一个双均线策略例子来演示效果。

为什么回测用backtesting框架, 之前回测推荐过backtrader, 但对于新手来说可能理解起来相对麻烦。 而backtesting是一款简洁而强大的Python回测框架,让我们能用非常直观的方式定义策略、执行回测并分析结果。

这里以经典的双均线策略举例, 例子分为几个部分:

一、通过xtquant获取数据

开启miniqmt获取数据,包括下载数据、查询数据、数据格式转换成 backtesting需要的数据格式。

二、初始化backtesting回测环境, 其实维护Strategy 就好了。

如果我们想实现其他的策略,比如小市值策略什么的,只需要修改 策略类就好了。

了解过backtesting的同学可知道, 维护init、next方法。 核心是next方法,回测引擎每推进一根新的K线(比如每一天),就会调用一次next方法。我们所有的交易决策都在这里做出。

三、 backtesting执行回测

四、分析回测结果,包括总收益率、夏普比率、 最大回测

五、性能优化、参数优化

这里贴一下完整代码,参考下思路, 具体根据自己的实际情况改造。 备注:如果发现格式有多余的特殊字符,用普通浏览器打开复制应该没问题。 希望我的分享对大家有所帮助

代码语言:javascript
复制
# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
import time
from backtesting import Backtest, Strategy
from backtesting.lib import crossover
from xtquant import xtdata


class DualMovingAverageStrategy(Strategy):
    """
    双均线交叉策略回测类
    基于MiniQMT的xtdata数据源
    """

    # 策略参数:短期均线窗口和长期均线窗口
    short_window = 5
    long_window = 20

    def init(self):
        """
        初始化函数:在回测开始前计算技术指标
        """
        # 计算短期移动平均线
        self.short_ma = self.I(
            lambda x: pd.Series(x).rolling(self.short_window).mean(),
            self.data.Close
        )

        # 计算长期移动平均线
        self.long_ma = self.I(
            lambda x: pd.Series(x).rolling(self.long_window).mean(),
            self.data.Close
        )

    def next(self):
        """
        每个交易日执行的主要交易逻辑
        """
        # 确保有足够的数据计算均线
        if len(self.data.Close) < self.long_window:
            return

        # 双均线金叉信号:短期均线上穿长期均线,买入
        if crossover(self.short_ma, self.long_ma) and not self.position:
            self.buy(size=0.95)  # 使用95%资金买入

        # 双均线死叉信号:短期均线下穿长期均线,卖出
        elif crossover(self.long_ma, self.short_ma) and self.position:
            self.position.close()


def get_miniqmt_data(stock_code, start_time, end_time, period='1d'):
    """
    使用miniQMT的xtdata接口获取股票历史数据

    参数:
        stock_code: 股票代码,如 '000001.SZ'
        start_time: 开始时间,如 '20240101'
        end_time: 结束时间,如 '20240930'
        period: K线周期,默认为日线 '1d'

    返回:
        data_df: 格式化后的DataFrame,包含OHLCV数据
    """
    print(f"正在获取 {stock_code} 从 {start_time} 到 {end_time} 的{period}数据...")

    try:
        # 1. 下载历史数据到本地缓存
        print("步骤1: 下载历史数据...")
        xtdata.download_history_data2(
            stock_list=[stock_code],
            period=period,
            start_time=start_time,
            end_time=end_time
        )

        # 等待数据下载完成
        time.sleep(3)

        # 2. 获取数据
        print("步骤2: 从本地缓存读取数据...")
        raw_data =  xtdata.get_market_data_ex(
            field_list=['open', 'high', 'low', 'close', 'volume', 'amount', 'time'],  # 明确指定需要的字段
            stock_list=[stock_code],
            period=period,
            start_time=start_time,
            end_time=end_time
        )

        if not raw_data or stock_code not in raw_data:
            print(f"获取数据失败,请检查股票代码 {stock_code} 是否正确或数据是否存在")
            return None

        # 3. 数据格式转换
        stock_data = raw_data[stock_code]

        # 确保数据是DataFrame格式
        if not isinstance(stock_data, pd.DataFrame):
            print("获取的数据格式不符合预期")
            return None

        # 重命名列以符合backtesting.py的要求
        column_mapping = {
            'open': 'Open',
            'high': 'High',
            'low': 'Low',
            'close': 'Close',
            'volume': 'Volume'
        }

        # 应用列名映射
        stock_data = stock_data.rename(columns=column_mapping)

        # 确保包含所有必需的列
        required_columns = ['Open', 'High', 'Low', 'Close', 'Volume']
        missing_columns = [col for col in required_columns if col not in stock_data.columns]

        if missing_columns:
            print(f"数据缺少必要的列: {missing_columns}")
            print(f"可用的列: {list(stock_data.columns)}")
            return None

        # 选择所需的列并确保数据类型正确
        data_df = stock_data[required_columns].copy()
        data_df = data_df.apply(pd.to_numeric, errors='coerce')
        data_df = data_df.dropna()

        # 确保索引是日期时间类型
        if not isinstance(data_df.index, pd.DatetimeIndex):
            try:
                data_df.index = pd.to_datetime(data_df.index)
            except:
                print("无法将索引转换为日期时间格式,将使用默认索引")

        print(f"数据获取成功!共获取 {len(data_df)} 条记录")
        print(data_df.head())

        return data_df

    except Exception as e:
        print(f"获取数据过程中发生错误: {e}")
        return None


def safe_get_stat(stats, key, default='N/A'):
    """
    安全获取统计值,避免KeyError
    """
    return stats.get(key, default)


def main():
    """
    主函数:执行完整的回测流程
    """
    # --- 配置参数 ---
    STOCK_CODE = '510300.SH'  # 股票代码
    START_TIME = '20250101'  # 开始时间
    END_TIME = '20250930'  # 结束时间
    INITIAL_CASH = 100000  # 初始资金
    COMMISSION = 0.002  # 手续费率

    # 短期和长期均线参数
    SHORT_WINDOW = 5
    LONG_WINDOW = 20

    print("=" * 60)
    print("MiniQMT双均线策略回测系统")
    print("=" * 60)

    # 1. 获取真实数据
    print("\n1. 从MiniQMT获取行情数据...")
    price_data = get_miniqmt_data(STOCK_CODE, START_TIME, END_TIME)

    if price_data is None or price_data.empty:
        print("数据获取失败,请检查网络连接、股票代码和时间参数")
        return

    # 2. 初始化回测环境
    print("\n2. 初始化回测环境...")
    bt = Backtest(
        price_data,
        DualMovingAverageStrategy,
        cash=INITIAL_CASH,
        commission=COMMISSION,
        exclusive_orders=True
    )

    # 3. 运行回测
    print("3. 运行双均线策略回测...")
    stats = bt.run(short_window=SHORT_WINDOW, long_window=LONG_WINDOW)

    # 4. 输出回测结果
    print("\n" + "=" * 60)
    print("回测结果统计")
    print("=" * 60)

    # 使用安全方法获取统计值
    end_value = safe_get_stat(stats, 'Equity Final [$]', 0)
    total_return = safe_get_stat(stats, 'Return [%]', 0)
    sharpe_ratio = safe_get_stat(stats, 'Sharpe Ratio', 0)
    max_drawdown = safe_get_stat(stats, 'Max. Drawdown [%]', 0)
    trades_count = safe_get_stat(stats, '# Trades', 0)
    win_rate = safe_get_stat(stats, 'Win Rate [%]', 0)

    print(f"标的股票: {STOCK_CODE}")
    print(f"回测期间: {START_TIME} 至 {END_TIME}")
    print(f"策略参数: {SHORT_WINDOW}日/{LONG_WINDOW}日均线")
    print(f"最终净值: ¥{end_value:,.2f}")
    print(f"总收益率: {total_return:.2f}%")
    print(f"夏普比率: {sharpe_ratio:.2f}")
    print(f"最大回撤: {max_drawdown:.2f}%")
    print(f"总交易次数: {trades_count}")
    print(f"胜率: {win_rate:.2f}%")

    # 5. 显示关键性能指标
    print("\n关键性能指标:")
    important_stats = [
        'Equity Final [$]', 'Return [%]', 'Sharpe Ratio',
        'Max. Drawdown [%]', '# Trades', 'Win Rate [%]',
        'Volatility (Ann.) [%]', 'Sortino Ratio', 'Calmar Ratio'
    ]

    for key in important_stats:
        if key in stats:
            print(f"{key}: {stats[key]}")

    # 6. 参数优化
    try:
        print("\n4. 进行参数优化...")
        optimization_result = bt.optimize(
            short_window=range(3, 15, 2),
            long_window=range(15, 50, 5),
            maximize='Sharpe Ratio'
        )

        print("最优参数组合:")
        print(f"短期均线: {optimization_result._strategy.short_window}")
        print(f"长期均线: {optimization_result._strategy.long_window}")

    except Exception as e:
        print(f"参数优化跳过或失败: {e}")

    # 7. 绘制回测图表
    try:
        print("\n5. 生成回测图表...")
        bt.plot()
    except Exception as e:
        print(f"图表生成失败: {e}")


if __name__ == '__main__':
    # 确保已启动MiniQMT并登录
    print("请注意: 运行前请确保已启动MiniQMT并成功登录!")
    main()

国庆假期结束了,今天大A就开盘了, 看看大A表现。

如果我的分享对你投资有所帮助,不吝啬给个点赞关注呗。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-10-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 子晓聊技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 这里贴一下完整代码,参考下思路, 具体根据自己的实际情况改造。 备注:如果发现格式有多余的特殊字符,用普通浏览器打开复制应该没问题。 希望我的分享对大家有所帮助
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档