找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 股票 资源 源码
查看: 82|回复: 0

自制小工具---QMT持仓导入通达信自定义版块

[复制链接]

1021

主题

89

回帖

3万

积分

管理员

积分
36120
发表于 2025-4-24 04:04:11 | 显示全部楼层 |阅读模式
本文将详细解析一个实用的量化交易工具——将QMT持仓数据导入通达信自定义板块的Python程序。该工具能够自动获取QMT交易系统中的持仓股票,并将其转换为通达信软件可识别的板块文件格式。
程序概述
这个Python脚本主要实现以下功能:
  • 1. 通过图形界面获取用户配置信息
  • 2. 连接QMT交易系统获取持仓数据
  • 3. 将持仓股票转换为通达信格式
  • 4. 生成通达信自定义板块文件
代码结构解析
  • 1. 日志系统配置
def setup_logger():
    # 创建logs目录
    if not os.path.exists('logs'):
        os.makedirs('logs')
        
    # 配置日志文件名(使用日期)
    log_file = os.path.join('logs', f'qmt_import_{datetime.now().strftime("%Y%m%d")}.log')
   
    # 创建日志记录器
    logger = logging.getLogger('QMTImport')
    logger.setLevel(logging.INFO)
   
    # 创建文件处理器(限制单个文件大小为5MB,最多保留5个文件)
    file_handler = RotatingFileHandler(log_file, maxBytes=5*1024*1024, backupCount=5, encoding='utf-8')
    file_handler.setLevel(logging.INFO)
这段代码配置了一个完善的日志系统:
• 自动创建logs目录存储日志文件
• 按日期生成日志文件名
• 使用RotatingFileHandler实现日志轮转,防止单个日志文件过大
• 同时输出到文件和终端控制台
  • 2. 图形用户界面
class ConfigGUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("仓鼠智能交易系统--QMT持仓导入通达信")
        self.root.geometry("800x600")
        
        # 默认配置
        self.qmt_path = tk.StringVar(value=r"D:\通达信\国金QMT交易端模拟\userdata_mini")
        self.tdx_path = tk.StringVar(value=r"D:\通达信\自用版")
GUI类使用tkinter库创建,主要特点包括:
• 设置默认路径,方便用户直接使用
• 提供浏览按钮选择目录
• 包含日志显示区域,实时反馈操作结果
• 输入验证功能,确保必要参数已填写
  • 3. QMT连接模块
def connect_qmt():
    try:
        # 生成会话ID
        session_id = int(time.time()) % 1000000
        
        # 创建交易对象
        xt_trader = XtQuantTrader(QMT_PATH, session_id)
        xt_trader.start()
        
        # 连接交易服务器
        if xt_trader.connect() != 0:
            log_message("错误: 连接QMT交易服务器失败")
            return None
连接QMT的核心步骤:
  • 1. 生成唯一的会话ID
  • 2. 创建XtQuantTrader交易对象
  • 3. 启动交易连接
  • 4. 订阅指定账户
  • 5. 持仓数据获取
def get_positions(xt_trader):
    account = StockAccount(QMT_ACCOUNT)
    positions = xt_trader.query_stock_positions(account)
   
    # 获取股票名称和最新价格
    stock_info = get_stock_info(stock_codes)
   
    # 计算盈亏百分比
    profit_percent = (price / cost - 1) * 100
获取持仓数据后,程序还会:
• 查询股票的实时行情数据
• 计算每只股票的盈亏比例
• 格式化输出持仓明细
  • 5. 通达信板块导出
def export_to_tdx(positions):
    # 转换代码格式为通达信格式
    if code.endswith('.SH'):
        tdx_code = '1' + code.split('.')[0]
    elif code.endswith('.SZ'):
        tdx_code = '0' + code.split('.')[0]
        
    # 写入板块文件
    with open(block_file, 'w', encoding='gbk') as f:
        f.write('\n'.join(stock_codes))
代码转换规则:
• 上海市场股票:前缀1+股票代码
• 深圳市场股票:前缀0+股票代码
• 使用GBK编码写入文件,确保通达信能正确识别
技术要点总结
  • 1. 多线程安全:通过生成唯一会话ID确保多实例运行安全
  • 2. 错误处理:完善的异常捕获和日志记录机制
  • 3. 性能优化:批量获取股票行情数据,减少API调用次数
  • 4. 兼容性:处理不同市场股票代码的转换规则
  • 5. 用户体验:图形界面与日志反馈相结合
实际应用价值
该工具解决了量化交易中的一个常见需求:将QMT系统中的持仓股票快速导入到通达信软件中进行分析。主要优势包括:
  • 1. 自动化:无需手动记录和输入股票代码
  • 2. 实时性:获取最新持仓和行情数据
  • 3. 可视化:直观显示盈亏情况
  • 4. 灵活性:可自定义板块名称和存储路径
对于同时使用QMT和通达信的投资者,这个工具能显著提高工作效率,实现两个平台之间的数据无缝衔接。
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-

  3. from __future__ import print_function
  4. import os
  5. import sys
  6. import json
  7. import uuid
  8. import shutil
  9. import platform
  10. import re
  11. from datetime import datetime
  12. import errno

  13. def get_storage_path():
  14.     """获取配置文件路径"""
  15.     system = platform.system().lower()
  16.     home = os.path.expanduser('~')
  17.    
  18.     if system == 'windows':
  19.         return os.path.join(os.getenv('APPDATA'), 'Cursor', 'User', 'globalStorage', 'storage.json')
  20.     elif system == 'darwin':  # macOS
  21.         return os.path.join(home, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'storage.json')
  22.     else:  # Linux
  23.         return os.path.join(home, '.config', 'Cursor', 'User', 'globalStorage', 'storage.json')

  24. def get_main_js_path():
  25.     """获取main.js文件路径"""
  26.     system = platform.system().lower()
  27.    
  28.     if system == 'darwin':  # macOS
  29.         return '/Applications/Cursor.app/Contents/Resources/app/out/main.js'
  30.     elif system == 'windows':  # Windows
  31.         user_profile = os.getenv('LOCALAPPDATA')  # 使用LOCALAPPDATA而不是USERPROFILE
  32.         if not user_profile:
  33.             return None
  34.         return os.path.join(user_profile, 'Programs', 'cursor', 'resources', 'app', 'out', 'main.js')
  35.     return None

  36. def generate_random_id():
  37.     """生成随机ID (64位十六进制)"""
  38.     return uuid.uuid4().hex + uuid.uuid4().hex

  39. def generate_uuid():
  40.     """生成UUID"""
  41.     return str(uuid.uuid4())

  42. def backup_file(file_path):
  43.     """创建文件备份"""
  44.     if os.path.exists(file_path):
  45.         backup_path = '{}.backup_{}'.format(
  46.             file_path,
  47.             datetime.now().strftime('%Y%m%d_%H%M%S')
  48.         )
  49.         shutil.copy2(file_path, backup_path)
  50.         print('已创建备份文件:', backup_path)

  51. def ensure_dir_exists(path):
  52.     """确保目录存在(兼容 Python 2/3)"""
  53.     if not os.path.exists(path):
  54.         try:
  55.             os.makedirs(path)
  56.         except OSError as e:
  57.             if e.errno != errno.EEXIST:
  58.                 raise

  59. def update_main_js(file_path):
  60.     """更新main.js文件中的UUID生成方式"""
  61.     if not os.path.exists(file_path):
  62.         print('警告: main.js 文件不存在:', file_path)
  63.         return False

  64.     # 创建备份
  65.     backup_file(file_path)

  66.     try:
  67.         # 读取文件内容
  68.         with open(file_path, 'r') as f:
  69.             content = f.read()

  70.         system = platform.system().lower()
  71.         if system == 'darwin':
  72.             # macOS: 替换 ioreg 命令
  73.             new_content = re.sub(
  74.                 r'ioreg -rd1 -c IOPlatformExpertDevice',
  75.                 'UUID=$(uuidgen | tr \'[:upper:]\' \'[:lower:]\');echo \"IOPlatformUUID = \"$UUID\";',
  76.                 content
  77.             )
  78.         elif system == 'windows':
  79.             # Windows: 替换 REG.exe 命令
  80.             # 注意:这里使用三重引号来处理复杂的转义
  81.             old_cmd = r'${v5[s$()]}\\REG.exe QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography /v MachineGuid'
  82.             new_cmd = r'powershell -Command "[guid]::NewGuid().ToString().ToLower()"'
  83.             new_content = content.replace(old_cmd, new_cmd)
  84.         else:
  85.             print('警告: 不支持的操作系统')
  86.             return False

  87.         # 写入修改后的内容
  88.         with open(file_path, 'w') as f:
  89.             f.write(new_content)

  90.         # 验证修改
  91.         success_marker = 'UUID=$(uuidgen | tr \'[:upper:]\' \'[:lower:]\');echo \"IOPlatformUUID = \"$UUID\";' if system == 'darwin' else 'powershell -Command "[guid]::NewGuid().ToString().ToLower()"'
  92.         if success_marker in new_content:
  93.             print('main.js 文件修改成功')
  94.             return True
  95.         else:
  96.             print('警告: main.js 文件可能未被正确修改,请检查文件内容')
  97.             print('你可以从备份文件恢复:', file_path + '.backup_*')
  98.             return False

  99.     except Exception as e:
  100.         print('修改 main.js 时出错:', str(e))
  101.         return False

  102. def update_storage_file(file_path):
  103.     """更新存储文件中的ID"""
  104.     # 生成新的ID
  105.     new_machine_id = generate_random_id()
  106.     new_mac_machine_id = generate_random_id()
  107.     new_dev_device_id = generate_uuid()
  108.    
  109.     # 确保目录存在
  110.     ensure_dir_exists(os.path.dirname(file_path))
  111.    
  112.     # 读取或创建配置文件
  113.     if os.path.exists(file_path):
  114.         try:
  115.             with open(file_path, 'r') as f:
  116.                 data = json.load(f)
  117.         except ValueError:
  118.             data = {}
  119.     else:
  120.         data = {}
  121.    
  122.     # 更新ID
  123.     data['telemetry.machineId'] = new_machine_id
  124.     data['telemetry.macMachineId'] = new_mac_machine_id
  125.     data['telemetry.devDeviceId'] = new_dev_device_id
  126.     data['telemetry.sqmId'] = '{' + str(uuid.uuid4()).upper() + '}'
  127.    
  128.     # 写入文件
  129.     with open(file_path, 'w') as f:
  130.         json.dump(data, f, indent=4)
  131.    
  132.     return new_machine_id, new_mac_machine_id, new_dev_device_id

  133. def main():
  134.     """主函数"""
  135.     try:
  136.         # 获取配置文件路径
  137.         storage_path = get_storage_path()
  138.         print('配置文件路径:', storage_path)
  139.         
  140.         # 备份原文件
  141.         backup_file(storage_path)
  142.         
  143.         # 更新ID
  144.         machine_id, mac_machine_id, dev_device_id = update_storage_file(storage_path)
  145.         
  146.         # 输出结果
  147.         print('\n已成功修改 ID:')
  148.         print('machineId:', machine_id)
  149.         print('macMachineId:', mac_machine_id)
  150.         print('devDeviceId:', dev_device_id)

  151.         # 处理 main.js
  152.         system = platform.system().lower()
  153.         if system in ['darwin', 'windows']:
  154.             main_js_path = get_main_js_path()
  155.             if main_js_path:
  156.                 update_main_js(main_js_path)
  157.         
  158.     except Exception as e:
  159.         print('错误:', str(e), file=sys.stderr)
  160.         sys.exit(1)

  161. if __name__ == '__main__':
  162.     main()
复制代码


1

试读已结束,请付费阅读全文。

  本文只能试读95%,付费后可阅读全文。 

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

本版积分规则

QQ|手机版|小黑屋| 股指标网 ( 渝ICP备2024026571号-1 )

GMT+8, 2025-5-13 05:40 Powered by Discuz! X3.5

股指标