Skip to content

margin_long/margin_short liquidation appears to be gated on percent_of_equity sizing only — default_qty_type=fixed strategies never get margin-called, even at 34x initial capital in losses #144

Description

@h1rot

The observation

Running the same etpl-1.0 strategy template (initial_capital=100000, margin_long=margin_short=5.0,
default_qty_type=strategy.fixed, market-only entries) through PineForge engine0.12.2-codegen0.10.1
and through PyneCore v6.7.0 on identical OHLCV input produces wildly different trade counts and P&L:

PyneCore PineForge
trades 1,683 22,426
net PnL -92,048.49 (≈ -92% of capital) -3,411,226.05 (≈ -34x capital)
last trade 2021-03-05 (then silent for 5.3 years / 82% of the data range) continues to the data end

PyneCore's trade CSV shows explicit "Margin call" exit labels once equity approaches the
~91–92% drawdown mark, after which it stops opening new positions in any meaningful size.
PineForge shows no equivalent — losses run to 34x the initial capital, which is not something
a real margin account could ever reach.

Root cause (as far as I can tell from the shipped image)

include/pineforge/engine.hpp:436-461 documents the entry-admission margin re-check as valid
only under the invariant percent_of_equity sizing && pct<=100 && margin<=100 && sizing_equity>0.
Outside that — including plain default_qty_type=fixed, which is what etpl-1.0 uses for
cross-engine parity testing — the re-check is described as out of scope and orders are admitted
regardless of equity.

I confirmed this experimentally: compiling the same strategy with margin_long=1000 (i.e. >100)
baked into the .pine source did not change trade count or qty versus margin_long=5.0 — consistent
with "margin>100 is outside the re-check range," but also meaning there does not appear to be any
configuration of margin_long/margin_short under fixed sizing that causes PineForge to actually
liquidate a losing position or stop opening new ones.

Confirmation via neutralization

Setting margin_long=margin_short=0 on both engines (which both source trees early-return out of the
margin-call path entirely) makes the two engines converge:

PyneCore (margin=0) PineForge (margin=0)
trades 15,944 15,827 (Δ 0.7%)
net PnL -593,524 -583,679 (Δ 1.7%)
trades to data end yes yes

This isolates the divergence to the margin-call path specifically, not some other engine difference.

The question

  1. Is "margin re-check applies only to percent_of_equity sizing" intentional, or is fixed-sizing
    margin enforcement simply not implemented yet?
  2. I noticed the git history (via the public repo, not the shipped image) has
    2026-07-25 fix(margin): close one whole contract on every floor-zero liquidation (#136),
    which postdates the engine0.12.2 release (2026-07-23) and isn't in any tagged
    pineforge-release build yet. Does that fix address fixed-sizing accounts at all, or is it
    scoped to percent_of_equity? Is there a release timeline for it?
  3. If fixed-sizing margin enforcement is out of scope by design, is there a recommended
    workaround for strategies that need both fixed position sizing and realistic margin-call
    behavior (short of switching to percent_of_equity, which changes the sizing semantics we
    need for cross-engine parity testing)?

Reproduction

Minimal repro available on request (strategy template + OHLCV slice + exact docker run invocation
used above). Happy to share if useful.

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