返回列表 发布新帖

【源码】用于A股交易的指数增强模型示例Python(回测&实时)

1506 0
发表于 2025-2-1 16:45:55 | 显示全部楼层 阅读模式

一个简单的 指数增强python模型示例。

<font color=red size=12>可回测,可实盘。</font>

基于原迅投QMT示例程序改编,原示例模型已老旧,不可运行。

其它参考请点击以下链接:

【分享】小白入门的迅投QMT股票模型开发上手指南(腾讯文档) https://www.xuntou.net/forum.php?mod=viewthread&tid=1844&user_code=PN4WZC 来自: 迅投QMT社区

【源码】一个最简单的MACD红绿柱翻转买卖的策略 https://www.xuntou.net/forum.php?mod=viewthread&tid=405&user_code=PN4WZC 来自: 迅投QMT社区

#coding:gbk
'''
指数增强型策略

以 沪深300 为股票池
1、先选出指数权重 0.0015 -- 0.003 之间的股票
2、价格在 5.00 -- 30.00 之间
3、价格大于均线 20
选出的结果大约 20支左右,订阅这些品种。

然后计算交易权重,
连续3天上涨,并成交量也上涨,m30上价格在 均线20以上。为 1.0
连续2天上涨,并成交量也上涨,m30上价格在 均线20以下。为 0.8
连续2天上涨,并成交量下降,m30上价格在 均线20以上。为 0.6
日线图,价格在 均线20之下,为 0

以 100万为总资金,持仓金额为 100万 * 指数权重 * 交易权重

运行在 m30图表上,根据金额调仓。

'''
#在指数(例如HS300)日线下运行
import numpy as np
import pandas as pd

class G(): pass
g = G()

g.Stock300 = []  #  沪深 300 的成份股
g.StockList = []  # 订阅的股票
g.DayData = {}
g.M30Data = {}
g.StockPos = {}
g.StockRatio = {}  # 品种的权重


def init(ContextInfo):
    print("\n此程序基于指数增强策略改编")
    print("code by 智能交易*姚")
    print("请提前下载好历史数据,包括1分钟K线数据!")
    print("回测时请选择 默认品种为 000300,默认周期为 30分钟")
    print("程序默认以 100万(1000000.00) 为 初始资金")

    if ContextInfo.period != '30m':
        print("K线图表的默认周期 请选择 30分钟")
        return


    #设置股票池
    g.Stock300 = ContextInfo.get_stock_list_in_sector('沪深300')

    g.DayData = ContextInfo.get_market_data_ex(
            ['close','volume'],     # 获取 收盘 和 成交额
            g.Stock300,             # 获取的品种是 000300.SH
            period = '1d',          # 获取 日K线的 周期
            start_time = '',        # 开始时间 缺省
            end_time = '',          # 结束时间 缺省
            count = -1,             # 从最右边的一根K线开始,获取 10 根K线的数据
            dividend_type='none',   # 复权方式以主图表的方式为准
            subscribe=False         # 订阅数据
           )

    g.M30Data = ContextInfo.get_market_data_ex(
            ['close','volume'],     # 获取 收盘 和 成交额
            g.Stock300,             # 获取的品种是 000300.SH
            period = '30m',         # 获取 30m线的 周期
            start_time = '',        # 开始时间 缺省
            end_time = '',          # 结束时间 缺省
            count = -1,             # 从最右边的一根K线开始,获取 10 根K线的数据
            dividend_type='none',   # 复权方式以主图表的方式为准
            subscribe=False         # 订阅数据
           )

    for symbol in g.Stock300:
        # 筛选股票
        # 保留权重大于0.35%的成份股。结果保存在字典 ContextInfo.stock300_weight中,如 '000725.SZ':0.006909999
        SymbolRatio = ContextInfo.get_weight_in_index("000300.SH", symbol) / 100
        MA20 =  g.DayData[symbol]['close'].rolling(window=20).mean()
        g.StockRatio[symbol] = 0.0

        if (g.DayData[symbol]['close'].iloc[-1] < 30.00 and  # 价格小于 30
            g.DayData[symbol]['close'].iloc[-1] > 5.00 and  # 价格大于 5
            g.DayData[symbol]['close'].iloc[-1] > MA20.iloc[-1] and  # 价格大于 均线20
            SymbolRatio > 0.0015 and                # 权重大于 0.015
            SymbolRatio < 0.0030                    # 权重小于 0.003
           ):
            g.StockList.append(symbol)
            g.StockRatio[symbol] = SymbolRatio
            # print("品种",symbol,"昨收盘价:",g.DayData[symbol]['close'].iloc[-1],"权重", SymbolRatio)

    print("选出成份股 ",len(g.StockList),"合计支。包括 ",g.StockList)
    print('所占权重总和为: ', round(sum(g.StockRatio.values()),2),) 

    ContextInfo.set_account("testS")

def handlebar(ContextInfo):

    AccountID = "testS"
    money  = 0
    UseMoney = 100000.00
    Symbol = ""
    trader_vol = 0
    StockRatio = {}
    NewPrice = 0.0  # 获取 最新价格

    timetag_1 = ContextInfo.get_bar_timetag(ContextInfo.barpos)
    timetag_2 = ContextInfo.get_bar_timetag(ContextInfo.barpos+1)
    timetag_3 = ContextInfo.get_bar_timetag(ContextInfo.barpos+2)
    timetag_4 = ContextInfo.get_bar_timetag(ContextInfo.barpos+3)

    bar_1stime = timetag_to_datetime(timetag_1, '%Y%m%d%H%M%S')
    bar_2stime = timetag_to_datetime(timetag_2, '%Y%m%d%H%M%S')
    bar_3stime = timetag_to_datetime(timetag_3, '%Y%m%d%H%M%S')
    bar_4stime = timetag_to_datetime(timetag_4, '%Y%m%d%H%M%S')



    g.Stock300 = ContextInfo.get_stock_list_in_sector('沪深300')

    for s in g.Stock300:
        g.StockPos[s] = {"持股总数量":0,
                         "可用数量":0,
                         "盈亏比例":0,
                         "持仓成本":0
                        }

    if str(bar_1stime[-6:-1])  == "093000":
        for s in g.Stock300:
            g.StockPos[s] = {"今日交易次数":0}

    account_list = get_trade_detail_data(AccountID,'stock','account')
    for acc in account_list:
        money = acc.m_dAvailable  # 帐户可用资金
        # UseMoney = acc.m_dBalance

    PosList = get_trade_detail_data(AccountID,'stock','position')

    for pos in PosList:
        Symbol = pos.m_strInstrumentID + "." + pos.m_strExchangeID # 组合成 600025。SH 这样的字符串
        g.StockList.append(Symbol)

        if Symbol not in g.Stock300:
            continue

        g.StockPos[Symbol]["持股总数量"] = pos.m_nVolume
        g.StockPos[Symbol]["可用数量"] = pos.m_nCanUseVolume
        g.StockPos[Symbol]["盈亏比例"] = pos.m_dProfitRate
        g.StockPos[Symbol]["持仓成本"] = pos.m_dPositionCost



        if g.StockPos[Symbol]["盈亏比例"] < - 0.03:  # 止损
            # print("亏损达到:",round(pos.m_dProfitRate,2),"全部卖出!")
            passorder(24, 1101, AccountID, Symbol, 5, 0, pos.m_nCanUseVolume, "止损卖出",1,"1", ContextInfo)
            g.StockPos[Symbol]["今日交易次数"] = 1 
            # print("-----------------------")


        if g.StockPos[Symbol]["盈亏比例"] >  0.1:  # 止盈
            # print("浮盈达到:",round(pos.m_dProfitRate,2),"全部卖出!")
            passorder(24, 1101, AccountID, Symbol, 5, 0, pos.m_nCanUseVolume, "止盈卖出",1,"1", ContextInfo)
            g.StockPos[Symbol]["今日交易次数"] = 1 
            # print("-----------------------")


# ------------------开始回测程序---------------------------------------
    if ContextInfo.do_back_test:
        # print("\n 开始回测模式......")
        if  int(bar_1stime) > 20230318000000 and  int(bar_1stime) < 20240101000000: 
            PosList = get_trade_detail_data(AccountID,'stock','position')

            for pos in PosList:
                Symbol = pos.m_strInstrumentID + "." + pos.m_strExchangeID # 组合成 600025。SH 这样的字符串
                passorder(24, 1101, AccountID, Symbol, 5, 0, pos.m_nVolume, "卖出",1,"1", ContextInfo)
            return


        g.StockList = []

        for symbol in g.Stock300:
        # 筛选股票
        # 保留权重大于0.35%的成份股。结果保存在字典 ContextInfo.stock300_weight中,如 '000725.SZ':0.006909999
            SymbolRatio = ContextInfo.get_weight_in_index("000300.SH", symbol) / 100
            dayMA20 =  g.DayData[symbol]['close'].rolling(window=20).mean()

            if (g.DayData[symbol]['close'].iloc[-1] < 30.00   and  # 价格小于 30
                g.DayData[symbol]['close'].iloc[-1] > 5.00    and  # 价格大于 30
                g.DayData[symbol]['close'].iloc[-1] > dayMA20.iloc[-1] and  # 价格大于 均线20
                SymbolRatio > 0.0015 and                # 权重大于 0.015
                SymbolRatio < 0.0030                    # 权重小于 0.003
               ):
                g.StockList.append(symbol)
                g.StockRatio[symbol] = SymbolRatio
                # print("品种",symbol,"昨收盘价:",g.DayData[symbol]['close'].iloc[-1],"权重", SymbolRatio)

        for symbol in g.StockList:

            dayMA20 = g.DayData[symbol]['close'].rolling(window=20).mean()
            m30MA20 = g.M30Data[symbol]['close'].rolling(window=20).mean()

            day1_index = g.DayData[symbol]['close'].index.get_loc(bar_1stime[0:8])
            day2_index = day1_index -1
            day3_index = day1_index -2
            day4_index = day1_index -3


            if ( g.DayData[symbol]['close'].iloc[day3_index] < g.DayData[symbol]['close'].iloc[day2_index]  and # 前2天的收盘价都上涨
                 g.DayData[symbol]['close'].iloc[day4_index] < g.DayData[symbol]['close'].iloc[day3_index]  and   
                 g.DayData[symbol]['volume'].iloc[day3_index] < g.DayData[symbol]['volume'].iloc[day2_index]  and  # 成交量也上涨
                 g.M30Data[symbol]['close'].loc[bar_1stime] > m30MA20.loc[bar_1stime]  # m30 价格在 均线 20之上
               ):
                StockRatio[symbol] = 1.0 

            if ( g.DayData[symbol]['close'].iloc[day3_index] < g.DayData[symbol]['close'].iloc[day2_index]  and # 前2天的收盘价都上涨
                 g.DayData[symbol]['volume'].iloc[day3_index] < g.DayData[symbol]['volume'].iloc[day2_index]  and  # 成交量也上涨
                 g.M30Data[symbol]['close'].loc[bar_1stime] < m30MA20.loc[bar_1stime]  # m30 价格在 均线 20之下
               ):
                StockRatio[symbol] = 0.8


            if ( g.DayData[symbol]['close'].iloc[day3_index] < g.DayData[symbol]['close'].iloc[day2_index]  and # 前2天的收盘价都上涨
                 g.DayData[symbol]['volume'].iloc[day3_index] > g.DayData[symbol]['volume'].iloc[day2_index]  and  # 成交量下降
                 g.M30Data[symbol]['close'].loc[bar_1stime] > m30MA20.loc[bar_1stime]  # m30 价格在 均线 20之上
               ):
                StockRatio[symbol] = 0.6

            if ( g.DayData[symbol]['close'].iloc[day2_index] < dayMA20.iloc[day2_index]):
                StockRatio[symbol] = 0.0
#------------------准备调仓交易
        for symbol in g.Stock300:
            if symbol not in StockRatio: continue

            money  = UseMoney * g.StockRatio[symbol] *100 * StockRatio[symbol]
            NewPrice = g.M30Data[symbol].loc[bar_2stime,"close"]  # 获取 最新价格
            # print("品种 ",symbol,"交易权重为 ",StockRatio[symbol],"指数权重为:",g.StockRatio[symbol])
            # print("应持仓金额为: ",money)
            if  money < 0.01 and g.StockPos[symbol]["可用数量"] >= 100:  # 如果要求持仓为0,但有持仓,则卖出
                #print("品种 ",symbol,"应持仓金额为 ",money,"全出!")
                passorder(24, 1101, AccountID, symbol, 5, 0, g.StockPos[symbol]["可用数量"], "调仓卖出",1,"1", ContextInfo)
                #print("------------------------------------")

            if money > 0.01 :
                order_target_value(symbol, money, ContextInfo,AccountID)
                #print("品种 ",symbol,"调整持仓金额为 ",round(money,2))
                #print("------------------------------------")

        return # 回测结束
# ----------------------------------------------------------------------------------------

    if not ContextInfo.is_last_bar():  # 这不是最右边的那根K线上的跳价?
        return  # 那就返回,退出程序 

#-------------------刷新数据-----------------------------------------------
    m30Data = ContextInfo.get_market_data_ex(
            ['close','volume'], 
            g.Stock300, 
            period    = '30m', 
            count     = 1, 
            dividend_type='none',   # 复权方式以主图表的方式为准
            subscribe = True
           )

    for Symbol in g.Stock300:
        g30mD_stime = g.M30Data[Symbol].index.tolist()  # 获取 初始化时 的时间戳
        m30D_stime  = m30Data[Symbol].index.tolist()   # 获取 新5m 的时间戳

        if len(m30D_stime) > 0:  # 确实获取到了新的报价数据
            if m30D_stime[0] not in g30mD_stime :  # 30m 更新了一根 K 线
                g.M30Data[Symbol] = pd.concat([g.M30Data[Symbol],m30Data[Symbol]])
            # print("品种",Symbol,"30分K线图表上出现一根新的K线,已加入到全局数据对象 g.m30Data 中。")

        g.M30Data[Symbol].loc[m30D_stime[0]] = m30Data[Symbol].loc[m30D_stime[0]]  # 直接刷新一行数据

        NewPrice = m30Data[Symbol]["close"].iloc[-1]  # 获取 最新价格

        dayMA20 = g.DayData[Symbol]['close'].rolling(window=20).mean()
        m30MA20 = g.M30Data[Symbol]['close'].rolling(window=20).mean()

        day1_index = g.DayData[Symbol]['close'].index.get_loc(bar_1stime[0:8])
        day2_index = day1_index -1
        day3_index = day1_index -2
        day4_index = day1_index -3

        if ( g.DayData[Symbol]['close'].iloc[day3_index] < g.DayData[Symbol]['close'].iloc[day2_index]  and # 前2天的收盘价都上涨
             g.DayData[Symbol]['close'].iloc[day4_index] < g.DayData[Symbol]['close'].iloc[day3_index]  and   
             g.DayData[Symbol]['volume'].iloc[day3_index] < g.DayData[Symbol]['volume'].iloc[day2_index]  and  # 成交量也上涨
             g.M30Data[Symbol]['close'].loc[bar_1stime] > m30MA20.loc[bar_1stime]  # m30 价格在 均线 20之上
           ):
            StockRatio[Symbol] = 1.0 

        if ( g.DayData[Symbol]['close'].iloc[day3_index] < g.DayData[Symbol]['close'].iloc[day2_index]  and # 前2天的收盘价都上涨
             g.DayData[Symbol]['volume'].iloc[day3_index] < g.DayData[Symbol]['volume'].iloc[day2_index]  and  # 成交量也上涨
             g.M30Data[Symbol]['close'].loc[bar_1stime] < m30MA20.loc[bar_1stime]  # m30 价格在 均线 20之下
           ):
            StockRatio[Symbol] = 0.8


        if ( g.DayData[Symbol]['close'].iloc[day3_index] < g.DayData[Symbol]['close'].iloc[day2_index]  and # 前2天的收盘价都上涨
             g.DayData[Symbol]['volume'].iloc[day3_index] > g.DayData[Symbol]['volume'].iloc[day2_index]  and  # 成交量下降
             g.M30Data[Symbol]['close'].loc[bar_1stime] > m30MA20.loc[bar_1stime]  # m30 价格在 均线 20之上
           ):
            StockRatio[Symbol] = 0.6

        if ( g.DayData[Symbol]['close'].iloc[day2_index] < dayMA20.iloc[day2_index]):
            StockRatio[Symbol] = 0.0

#------------------准备调仓交易
        for Symbol in g.Stock300:
            if Symbol not in StockRatio: continue

            money  = UseMoney * g.StockRatio[Symbol] *100 * StockRatio[Symbol]

            # print("品种 ",symbol,"交易权重为 ",StockRatio[symbol],"指数权重为:",g.StockRatio[symbol])
            # print("应持仓金额为: ",money)
            if  money < 0.01 and g.StockPos[Symbol]["可用数量"] >= 100:  # 如果要求持仓为0,但有持仓,则卖出
                #print("品种 ",Symbol,"应持仓金额为 ",money,"全出!")
                passorder(24, 1101, AccountID, Symbol, 5, 0, g.StockPos[Symbol]["可用数量"], "调仓卖出",1,"1", ContextInfo)
                #print("------------------------------------")

            if money > 0.01 :

                money = money - g.StockPos[Symbol]["持仓成本"]  # 应持有金额 与 现持有金额的差值
                trader_vol = (money / NewPrice) //100 * 100 
                trader_vol = abs(trader_vol)

                if money < 0: # 应减仓
                    passorder(24,1101,AccountID,Symbol,5,0,trader_vol,"减仓卖出",1,"1",ContextInfo)
                    print("品种 ",Symbol,"调整持仓金额为 ",round(money,2),"实际持仓金额为:",g.StockPos[Symbol]["持仓成本"],"减仓卖出")

                elif money > 0: # 应加仓
                    passorder(23,1101,AccountID,Symbol,5,0,trader_vol,"加仓买入",1,"1",ContextInfo)
                    print("品种 ",Symbol,"调整持仓金额为 ",round(money,2),"实际持仓金额为:",g.StockPos[Symbol]["持仓成本"],"加仓卖入")
                #print("------------------------------------")

    print("==========================================")
    return

回复

您需要登录后才可以回帖 登录 | 立即注册

客服专线

400-080-8112

用思考的速度交易,用真诚的态度合作,我们是认真的!
  • 关注公众号
  • 添加微信客服
Copyright © 2001-2025 迅投QMT社区 版权所有 All Rights Reserved. 蜀ICP备19002686号-2
关灯 快速发帖
扫一扫添加微信客服
QQ客服返回顶部
快速回复 返回顶部 返回列表