问题描述
解决方案
在PTrade中,计算单笔标的(如股票)的持仓收益,主要通过获取持仓对象 Position 来实现。
你可以通过 get_position(security) 函数获取指定标的的持仓信息,然后利用其中的属性进行计算。
主要有两种方式:
- 直接获取系统计算好的浮动盈亏 (
pnl)。 - 手动计算:使用
(当前价 - 持仓成本) * 持仓数量。
以下是具体的代码实现示例和说明。
策略代码示例
def initialize(context):
# 初始化策略,设置股票池
g.security = '600570.SS'
set_universe(g.security)
# 设置一个标志,用于控制只买入一次,方便观察后续收益变化
g.bought = False
def handle_data(context, data):
# 1. 模拟买入操作(仅为了产生持仓)
if not g.bought:
order(g.security, 1000)
g.bought = True
log.info("已买入 %s" % g.security)
return # 买入当次循环暂不计算收益
# 2. 获取持仓对象
# get_position 返回的是一个 Position 对象
position = get_position(g.security)
# 3. 确保有持仓才进行计算
if position.amount > 0:
# --- 方法一:直接读取系统计算的浮动盈亏 ---
# pnl: 浮动盈亏 (Profit and Loss)
holding_pnl = position.pnl
# --- 方法二:手动计算 (用于验证或自定义逻辑) ---
# cost_basis: 持仓成本价 (通常包含手续费,取决于回测设置)
cost_price = position.cost_basis
# amount: 总持仓数量
amount = position.amount
# current_price: 当前市场价格
current_price = data[g.security]['close']
# 手动计算收益额
manual_pnl = (current_price - cost_price) * amount
# 计算收益率 (%)
if cost_price > 0:
return_rate = (current_price - cost_price) / cost_price * 100
else:
return_rate = 0.0
# 4. 打印结果
log.info("-" * 30)
log.info("标的: %s" % g.security)
log.info("当前价格: %.2f, 持仓成本: %.2f, 持仓数量: %d" % (current_price, cost_price, amount))
log.info("【系统计算】浮动盈亏(pnl): %.2f" % holding_pnl)
log.info("【手动计算】浮动盈亏: %.2f" % manual_pnl)
log.info("【收益率】: %.2f%%" % return_rate)
log.info("-" * 30)
else:
log.info("%s 当前无持仓" % g.security)
关键属性说明
在 Position 对象中(通过 get_position(security) 获取),以下属性是计算收益的关键:
| 属性名 | 类型 | 说明 |
|---|---|---|
pnl |
float | 浮动盈亏。这是最直接的收益数据,代表当前持仓相对于成本的盈亏金额。 |
cost_basis |
float | 持仓成本价。通常是加权平均成本。 |
amount |
int | 总持仓数量。 |
last_sale_price |
float | 最新价格。持仓对象中记录的最新成交价,也可用于计算。 |
注意事项
- 期货交易:如果是期货策略,
Position对象区分多头和空头。- 多头浮动盈亏:
position.long_pnl - 空头浮动盈亏:
position.short_pnl - 多头持仓成本:
position.long_cost_basis - 空头持仓成本:
position.short_cost_basis
- 多头浮动盈亏:
- 成本价 (
cost_basis):在回测中,成本价的计算方式受set_commission(佣金设置)的影响。如果设置了佣金,买入时的手续费通常会摊薄到cost_basis中,导致成本价略高于成交价。 - 数据更新频率:在
handle_data中,data[security]['close']获取的是当前周期(如分钟或日线)的收盘价。在交易模式下,持仓信息的更新可能存在极短的延迟(通常在几秒内),但在回测中是即时的。
Q&A
Q: position.pnl 和我自己算的 (现价-成本)*数量 会有误差吗?
A: 通常情况下两者是一致的。微小的误差可能来源于浮点数精度问题,或者系统使用的“现价”来源(last_sale_price)与你在 data 中获取的价格有极细微的时间差(主要在实盘tick级别)。在回测中,它们通常完全相等。
Q: 如何计算账户的总收益,而不仅仅是单只股票?
A: 账户总收益可以通过 context.portfolio 对象获取。
context.portfolio.returns: 当前相对于初始资金的收益比例。context.portfolio.pnl: 账户当前的浮动盈亏总额。context.portfolio.portfolio_value: 当前账户总资产(现金+持仓市值)。
Q: 卖出股票后的收益去哪里看?
A: get_position 获取的是当前持仓的信息。一旦股票全部卖出,amount 变为 0,pnl 也会归零。卖出后的已实现收益会体现在账户的总资金 context.portfolio.cash 和总资产 context.portfolio.portfolio_value 的变化中。如果需要统计历史已平仓订单的收益,需要自行记录交易日志或分析 get_trades() 返回的成交记录。