🚀 AI 一键生成 ptrade 策略代码
立即体验
👉 AI 生成代码 👈

ptrade 平台如何实现模拟与实盘交易的资金和持仓隔离?

ptrade | 发布时间: 2026/5/29
以下内容由 EasyQuant 生成。

问题描述

模拟交易和实盘交易的资金、持仓无法隔离怎么办?

/

解决方案

在 ptrade 量化交易平台中,由于券商底层的资金和持仓通常是统一计算的,如果在一个账户中同时运行模拟策略(或多个不同的实盘子策略),直接使用 context.portfolio 获取的资金和持仓数据会发生混淆。为了实现资金和持仓的有效隔离,我们需要在策略内部构建**“虚拟子账户”,并结合持久化技术(pickle)**来管理状态。

核心解决思路

  1. 放弃全局真实持仓:不再依赖 context.portfolio.cashcontext.portfolio.positions 作为交易决策的唯一依据。
  2. 构建虚拟子账户:在全局变量 g 中定义字典来记录当前策略专属的虚拟资金(g.virtual_cash)和虚拟持仓(g.virtual_positions)。
  3. 利用主推事件更新状态:通过 on_trade_response(成交主推回调)来精准更新虚拟资金和持仓,确保与实际成交一致。
  4. 数据持久化:使用 pickle 模块将虚拟账户数据落地保存,防止策略重启或服务器升级导致数据丢失。

Python 实现源码示例

以下是一个完整的代码框架,展示如何实现资金与持仓的隔离管理:

import pickle
import os

def initialize(context):
    g.security = '600570.SS'
    set_universe(g.security)
    
    # 定义持久化文件路径
    g.save_path = get_research_path() + 'virtual_account.pkl'
    
    # 尝试加载历史虚拟账户数据
    if os.path.exists(g.save_path):
        try:
            with open(g.save_path, 'rb') as f:
                account_data = pickle.load(f)
                g.virtual_cash = account_data.get('cash', 100000.0) # 默认初始资金10万
                g.virtual_positions = account_data.get('positions', {})
                log.info("成功加载虚拟账户数据")
        except Exception as e:
            log.error("加载虚拟账户失败: %s" % e)
            init_virtual_account()
    else:
        init_virtual_account()

def init_virtual_account():
    # 初始化虚拟账户
    g.virtual_cash = 100000.0
    g.virtual_positions = {}
    save_virtual_account()

def save_virtual_account():
    # 保存虚拟账户数据到本地
    account_data = {
        'cash': g.virtual_cash,
        'positions': g.virtual_positions
    }
    try:
        with open(g.save_path, 'wb') as f:
            pickle.dump(account_data, f, -1)
    except Exception as e:
        log.error("保存虚拟账户失败: %s" % e)

def handle_data(context, data):
    security = g.security
    current_price = data[security]['close']
    
    # 示例买入逻辑:如果虚拟资金充足,买入100股
    cost = current_price * 100
    if g.virtual_cash >= cost:
        order(security, 100)
        log.info("触发买入委托")
        
    # 示例卖出逻辑:如果虚拟持仓中有该股票,全部卖出
    virtual_amount = g.virtual_positions.get(security, 0)
    if virtual_amount >= 100:
        order(security, -100)
        log.info("触发卖出委托")

def on_trade_response(context, trade_list):
    # 接收成交主推,精准更新虚拟账户
    for trade in trade_list:
        # 仅处理已成状态 (status == '8')
        if trade.get('status') == '8':
            stock = trade['stock_code']
            amount = trade['business_amount']
            balance = trade['business_balance']
            direction = trade['entrust_bs'] # 1-买,2-卖
            
            if direction == '1': # 买入成交
                g.virtual_cash -= balance
                g.virtual_positions[stock] = g.virtual_positions.get(stock, 0) + amount
            elif direction == '2': # 卖出成交
                g.virtual_cash += balance
                g.virtual_positions[stock] = g.virtual_positions.get(stock, 0) - amount
                if g.virtual_positions[stock] <= 0:
                    del g.virtual_positions[stock]
                    
            log.info("虚拟账户更新 -> 资金: %.2f, 持仓: %s" % (g.virtual_cash, g.virtual_positions))
            # 每次成交后持久化保存
            save_virtual_account()

注意事项与优化建议

  1. 手续费扣除:上述示例中未包含手续费计算。在实际应用中,需要在 on_trade_response 中根据券商费率(如万分之三佣金+印花税等)额外扣除 g.virtual_cash 中的手续费。
  2. 订单状态管理:除了成交主推,建议结合 on_order_response 监控废单或撤单情况,避免虚拟资金被长期“冻结”。
  3. 多策略标识:如果在一个账户中运行多个实盘策略,可以在下单时利用 order 接口返回的 order_id,在全局维护一个 g.my_order_ids 列表。在主推回调中,只处理属于本策略的 order_id,从而实现多策略间的完美隔离。