Skip to content

Strategy PnL output for futures contracts is in points, not USD — pointvalue not applied #50

@Zombajo

Description

@Zombajo

Summary

For futures contracts, PyneCore's strategy.equity / strategy.netprofit / per-trade pnl output is in POINTS instead of USD. The contract's pointvalue (e.g., NQ = $20/point, ES = $50/point) is not multiplied through. This makes reported PnL ~20× too small for NQ and proportionally wrong for any other futures contract.

Filing for verification — would appreciate confirmation that this is unintended and that PnL output should be in USD when pointvalue metadata is available.

Symptom

Run any futures strategy through PyneCore. Compare reported PnL to TradingView strategy tester output for the same Pine source on the same data. PyneCore's reported per-trade PnL is the raw point delta (exit_price - entry_price) * qty without the pointvalue multiplier.

For NQ at $20/point, PyneCore reports (exit - entry) * 1 instead of (exit - entry) * 1 * 20. A 10-point winning trade reports as $10 instead of $200.

Suspected location

pynecore/lib/strategy/__init__.py around line 638 (PnL computation in fill-handling path).

Reproduction

Minimal script on NQ futures (or any futures contract with pointvalue != 1):

from pynecore.lib import strategy

@strategy("pointvalue_repro",
          default_qty_value=1,
          commission_type=strategy.commission.cash_per_contract,
          commission_value=1.70)
def main():
    if bar_index == 100:
        strategy.entry("L", strategy.long)
    if bar_index == 200:
        strategy.close("L")

Run on NQ 1m. If bar 100 entry is at 18000 and bar 200 exit is at 18010 (10-point profit), expected reported PnL = 10 * 1 * 20 = $200 (NQ pointvalue $20). PyneCore reports $10.

Proposed fix direction

The PnL computation should multiply by the contract's pointvalue from syminfo.pointvalue (or equivalent). Suggested: detect futures via syminfo.type == 'futures' (or by pointvalue != 1.0) and apply the multiplier in strategy.equity, strategy.netprofit, and per-trade strategy.closedtrades.profit paths.

Workaround currently in use

We compute PnL post-trade from raw entry/exit prices using a known pointvalue constant. Reusable Python helper:

NQ_POINT_VALUE = 20.0
def recompute_futures_pnl(trades_df, point_value):
    points_pnl = trades_df["exit_price"] - trades_df["entry_price"]
    size = trades_df["size"]
    gross_usd = points_pnl * size * point_value
    return gross_usd

Validation evidence

Discovered during NQ 1m strategy validation. PyneCore-reported PnL was ~20× smaller than TradingView strategy tester output for identical Pine source on identical data range. Post-trade pointvalue correction brings PyneCore output into agreement with TV.

Verification request

Could you confirm this is a bug rather than intended "raw points" output that consumers must convert themselves? If intended, please point us at where the convention is documented so we can update our integration.

Environment

  • PyneCore version: [check with pip show pynecore]
  • Python version: [check with python --version]
  • OS: Windows 11

Related

Discovered alongside #48 (percent-commission, fixed in v6.4.5) and a third issue on cash_per_contract exit-leg charging filed separately.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions