QMT量化交易实战:用Python脚本一键解析股票持仓数据

每次打开交易软件手动翻看持仓明细,总有种被数据淹没的无力感。作为量化交易者,我们需要的是清晰、可编程的持仓数据视图。本文将手把手教你用QMT平台的Python接口,打造专属持仓分析工具——从API调用到盈亏计算,完整代码可直接套用。

1. 环境准备与基础配置

在开始编写持仓查询脚本前,需要确保QMT客户端已正确安装并登录交易账户。打开QMT后,点击右上角的"Python"按钮进入策略编辑界面。这里建议新建一个专门用于数据查询的文件夹,与交易策略文件分开管理。

QMT的Python环境已预装必要依赖库,但我们仍需导入几个关键模块:

import pandas as pd
from datetime import datetime
import matplotlib.pyplot as plt  # 用于后续可视化

账户信息配置技巧

  • 主账户通常以大写字母"A"开头
  • 信用账户则以大写字母"C"开头
  • 模拟账户前缀可能因券商而异,常见有"S"或"M"

注意:账户字符串必须用单引号包裹,且严格区分大小写。错误的账户格式会导致 get_trade_detail_data 返回空列表。

2. 核心API函数深度解析

get_trade_detail_data 是QMT获取持仓数据的瑞士军刀,其完整参数结构如下:

datas = get_trade_detail_data(
    account_id,    # 交易账户
    asset_type,    # 资产类型:'stock'/'fund'/'bond'等  
    query_type,    # 查询类型:'position'/'entrust'/'deal'
    stock_code=None,  # 可选参数,指定证券代码
    start_date=None,  # 可选参数,查询起始日
    end_date=None     # 可选参数,查询结束日
)

2.1 参数组合实战案例

场景一 :查询股票持仓全量数据

positions = get_trade_detail_data('A123456789', 'stock', 'position')

场景二 :查询指定股票持仓

tsla_position = get_trade_detail_data(
    'A123456789', 
    'stock', 
    'position', 
    stock_code='600519.SH'
)

字段映射表

API返回字段 业务含义 数据类型 示例值
m_strInstrumentID 证券代码 str '600519'
m_strExchangeID 交易所代码 str 'SH'
m_nVolume 当前持仓量 int 1000
m_dOpenPrice 开仓均价 float 168.50
m_dPositionProfit 浮动盈亏 float 32500.00

3. 持仓数据解析与增强

原始API返回的数据对象需要转换为更易处理的格式。以下代码将持仓列表转换为Pandas DataFrame:

def parse_position_data(positions):
    position_list = []
    for pos in positions:
        position_list.append({
            '证券代码': f"{pos.m_strInstrumentID}.{pos.m_strExchangeID}",
            '证券名称': pos.m_strInstrumentName,
            '持仓量': pos.m_nVolume,
            '可用数量': pos.m_nCanUseVolume,
            '成本价': round(pos.m_dOpenPrice, 2),
            '当前价': round(pos.m_dInstrumentValue / pos.m_nVolume, 2),
            '持仓市值': round(pos.m_dInstrumentValue, 2),
            '持仓成本': round(pos.m_dPositionCost, 2),
            '浮动盈亏': round(pos.m_dPositionProfit, 2),
            '盈亏比例': round(pos.m_dPositionProfit / pos.m_dPositionCost * 100, 2)
        })
    return pd.DataFrame(position_list)

关键计算逻辑

  • 当前价 = 市值 / 持仓量
  • 盈亏比例 = 浮动盈亏 / 持仓成本 × 100%

提示:QMT返回的成本价可能包含手续费等杂费,与交易记录中的成交均价略有差异。

4. 高级应用与可视化

4.1 持仓分析报告生成

def generate_report(df):
    total_mv = df['持仓市值'].sum()
    total_pnl = df['浮动盈亏'].sum()
    
    print(f"\n【持仓概览 {datetime.now().strftime('%Y-%m-%d')}】")
    print(f"总市值: {total_mv:,.2f} 元")
    print(f"总盈亏: {total_pnl:+,.2f} 元")
    
    # 按盈亏排序
    top_gainers = df.sort_values('盈亏比例', ascending=False).head(3)
    print("\n【最佳表现】")
    for _, row in top_gainers.iterrows():
        print(f"{row['证券代码']} {row['证券名称']}: {row['盈亏比例']:+.2f}%")

4.2 持仓分布可视化

def plot_holdings(df):
    plt.figure(figsize=(10, 6))
    df['权重'] = df['持仓市值'] / df['持仓市值'].sum()
    
    # 饼图展示持仓分布
    plt.subplot(121)
    df.plot.pie(y='权重', labels=df['证券名称'], autopct='%1.1f%%')
    plt.title('持仓市值分布')
    
    # 条形图展示盈亏
    plt.subplot(122)
    df.sort_values('盈亏比例').plot.barh(
        x='证券名称', 
        y='盈亏比例',
        color=df['盈亏比例'].apply(lambda x: 'green' if x >=0 else 'red')
    )
    plt.title('各标的盈亏比例')
    plt.tight_layout()
    plt.show()

5. 异常处理与性能优化

实际运行中可能遇到的典型问题及解决方案:

常见错误排查表

错误现象 可能原因 解决方案
返回空列表 账户格式错误 检查账户前缀大小写
缺少字段 查询类型错误 确认第二个参数为'stock'
数据延迟 未登录交易账户 先手动登录QMT客户端
连接超时 网络问题 检查客户端连接状态

性能优化建议

  1. 缓存机制:对不常变动的持仓数据设置本地缓存
  2. 批量查询:避免在循环中频繁调用API
  3. 异常重试:对网络请求添加retry逻辑
from tenacity import retry, stop_after_attempt

@retry(stop=stop_after_attempt(3))
def safe_get_position(account):
    return get_trade_detail_data(account, 'stock', 'position')

6. 完整代码实现

将上述功能整合为可直接运行的脚本:

# qmt_position_analyzer.py
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime
from tenacity import retry, stop_after_attempt

class PositionAnalyzer:
    def __init__(self, account_id):
        self.account = account_id
        
    @retry(stop=stop_after_attempt(3))
    def get_positions(self):
        return get_trade_detail_data(self.account, 'stock', 'position')
    
    def analyze(self):
        raw_data = self.get_positions()
        df = self.parse_to_dataframe(raw_data)
        
        self.generate_report(df)
        self.plot_holdings(df)
        return df
    
    @staticmethod
    def parse_to_dataframe(positions):
        # ...同上文parse_position_data实现...
        
    @staticmethod
    def generate_report(df):
        # ...同上文generate_report实现...
        
    @staticmethod 
    def plot_holdings(df):
        # ...同上文plot_holdings实现...

if __name__ == '__main__':
    analyzer = PositionAnalyzer('A123456789')  # 替换为你的账户
    position_df = analyzer.analyze()
    position_df.to_excel('持仓分析.xlsx')  # 导出Excel

实际项目中,这个脚本帮我节省了大量手动统计时间。特别是组合盈亏比例的自动计算,让调仓决策变得直观很多。记得第一次运行时,发现某个持仓的盈亏比例计算异常,检查才发现是除零错误——原来那只股票刚刚买入尚未成交,成本价为零。后来增加了异常值处理,代码就健壮多了。

更多推荐