Demystifying the profit factor: What it measures and what it hides

In quantitative finance and algorithmic strategy tear sheets, few metrics are quoted as ubiquitously—and misinterpreted as frequently—as the profit factor. When evaluating historical performance reports, quants and portfolio managers immediately scan for this number to gauge the baseline profitability of an automated trading system.

At its core, understanding the profit factor definition in trading backtests begins with a straightforward ratio:

Code
\text{Profit Factor (PF)} = \frac{\text{Gross Realized Profits}}{\text{Gross Realized Losses}}

The intuitive appeal of profit factor is immediate: for every dollar the strategy surrendered to the market, how many dollars did it return?

A strategy that generates $60,000 in cumulative winning trades while absorbing $30,000 in cumulative losing trades prints a profit factor of 2.0. If the value exceeds 1.0, the strategy made money; if it falls below 1.0, the system lost capital.

Profit Factor RangeOperational ClassificationRealistic Quant Assessment
< 1.00Unprofitable SystemCapital destruction; strategy loses more than it generates.
1.00 - 1.25Marginally ViableFrail edge; highly vulnerable to slippage spikes, regime changes, or broker fees.
1.25 - 1.75Robust Systematic EdgeIndustry benchmark for sustainable long-term trend or mean-reversion alphas.
1.75 - 2.50Strong Alpha PerformanceExcellent edge; requires rigorous auditing for small sample bias or outlier windfalls.
> 2.50Exceptional / SuspiciousHigh probability of curve-fitting, lookahead bias, or inadequate transaction friction.

Why Quants Rely on Profit Factor

Unlike the Sharpe ratio or the Sortino ratio, profit factor does not depend on continuous periodic return intervals (such as daily or hourly percentage changes), nor does it presuppose a normal distribution of returns:

  1. Capital-Scale Invariance: Profit factor measures gross capital efficiency rather than absolute dollars. A $5,000 account and a $5,000,000 account executing the same fractional lot sizes will yield the exact same profit factor.
  2. Asymmetry Tolerance: Standard deviation metrics (like the classical Sharpe ratio) penalize upside volatility equally with downside volatility unless explicitly adjusted. Profit factor treats large winning trades purely as positive contributions to the numerator.
  3. No Risk-Free Rate Assumption: Metrics like the Sharpe or Treynor ratios require selecting a benchmark risk-free rate (R_f), introducing external macro assumptions. Profit factor is self-contained within the trade ledger.

What the Metric Hides

Despite its elegance, relying on profit factor in isolation is one of the most common rookie pitfalls in backtest validation. The metric has significant blind spots:

  • Time and Return Velocity Blindness: A profit factor of 2.0 over 12 trades executed across five years represents an economically dormant system with negligible annualized returns. Conversely, a high-frequency system with a modest 1.25 profit factor completing 2,000 trades per year can generate phenomenal compounding growth.
  • Path and Sequence Insensitivity: Profit factor calculates the aggregate sum of wins divided by losses. It is completely indifferent to trade ordering. A backtest with 20 consecutive losses followed by 20 consecutive wins yields the identical profit factor as one with perfectly alternating outcomes—yet the clustered sequence can trigger account liquidation or breach investor drawdown mandates.
  • Outlier Distortion: A single catastrophic black-swan windfall can inflate an otherwise decaying strategy's profit factor well above 1.50, disguising dozens of chronic paper-cut losses.
  • Friction Omission: Naive backtesters frequently calculate gross profit factors without modeling bid-ask spreads, exchange fees, and bar execution slippage. As we will demonstrate, realistic frictions compress headline profit factors drastically.

Mathematical formulation and edge cases

To audit a trading ledger rigorously, we must formalize the calculation across discrete closed round-trip trades. Let a backtest record N closed trades, where each trade i \in \&#123;1, 2, \dots, N\&#125; yields a net dollar profit or loss P_i.

We partition the trade log into two disjoint subsets:

  • Winning trades: W = \&#123;i \mid P_i > 0\&#125;
  • Losing trades: L = \&#123;i \mid P_i &lt; 0\&#125;

Break-even trades (P_i = 0) contribute zero to both gains and losses and are omitted from the sums.

The Formal Formula

The components are defined as:

Code
\text&#123;Gross Profit &#125; (G_+) = \sum_&#123;i \in W&#125; P_i
Code
\text&#123;Gross Loss &#125; (G_-) = \sum_&#123;i \in L&#125; |P_i| = \left| \sum_&#123;i \in L&#125; P_i \right|
Code
PF = \frac&#123;G_+&#125;&#123;G_-&#125; = \frac&#123;\sum_&#123;i \in W&#125; P_i&#125;&#123;\sum_&#123;i \in L&#125; |P_i|&#125;

Decomposing Profit Factor: Odds Ratio \times Payoff Ratio

Profit factor can be decomposed analytically into two foundational trading variables: the win rate and the payoff ratio (win/loss ratio).

Let:

  • N_w = |W| (number of winning trades)
  • N_l = |L| (number of losing trades)
  • Total non-zero trades N' = N_w + N_l
  • Win rate p = \frac&#123;N_w&#125;&#123;N'&#125;
  • Loss rate q = \frac&#123;N_l&#125;&#123;N'&#125; = 1 - p
  • Average win \bar&#123;W&#125; = \frac&#123;G_+&#125;&#123;N_w&#125;
  • Average loss \bar&#123;L&#125; = \frac&#123;G_-&#125;&#123;N_l&#125;
  • Payoff ratio R = \frac&#123;\bar&#123;W&#125;&#125;&#123;\bar&#123;L&#125;&#125;

Expressing gross profit and gross loss in terms of averages:

Code
G_+ = N_w \cdot \bar&#123;W&#125;, \quad G_- = N_l \cdot \bar&#123;L&#125;

Substituting these into the profit factor equation:

Code
PF = \frac&#123;N_w \cdot \bar&#123;W&#125;&#125;&#123;N_l \cdot \bar&#123;L&#125;&#125; = \left( \frac&#123;N_w&#125;&#123;N_l&#125; \right) \cdot \left( \frac&#123;\bar&#123;W&#125;&#125;&#123;\bar&#123;L&#125;&#125; \right) = \left( \frac&#123;p&#125;&#123;1 - p&#125; \right) \cdot R
Code
PF = \text&#123;Odds Ratio&#125; \times \text&#123;Payoff Ratio&#125;

This identity reveals the mathematical interplay between strategy architecture and profit factor:

  1. If your strategy has an even win rate (p = 0.50 \implies \frac&#123;p&#125;&#123;1-p&#125; = 1.0), your profit factor identically equals your payoff ratio (PF = R).
  2. A trend-following strategy with a low win rate (p = 0.30 \implies \frac&#123;p&#125;&#123;1-p&#125; = 0.428) requires an average win that is at least 2.33 times larger than its average loss simply to break even (PF = 1.0). To achieve PF = 1.6, it requires R = \frac&#123;1.6&#125;&#123;0.428&#125; \approx 3.74.
  3. A high-probability mean-reversion system with p = 0.70 (\frac&#123;p&#125;&#123;1-p&#125; = 2.33) can tolerate a payoff ratio of R = 0.65 and still produce a healthy PF = 1.51.

Boundary Conditions and Edge Cases

When implementing automated calculation engines in Python, several numerical boundary conditions must be accounted for:

1. Zero Losses (G_- = 0)

If a strategy runs for 15 trades and experiences zero losses:

Code
PF = \frac&#123;G_+&#125;&#123;0&#125; \implies \text&#123;Division by Zero&#125;

[!WARNING] While a mathematical report may output \infty or NaN, a reported infinite profit factor in a trading proposal is a definitive red flag. It indicates extreme sample insufficiency, overfitted in-sample rules, or unclosed "zombie" positions where losses are held indefinitely without realized stop-loss orders.

2. Zero Wins (G_+ = 0)

If all closed trades resulted in losses, PF = 0.0.

3. Small Sample Size (N &lt; 30)

With fewer than 30 closed trades, the sampling variance of both p and R is massive. Standard confidence intervals around the estimated profit factor are so wide that a reported PF = 2.1 is frequently indistinguishable from statistical noise.

4. The Outlier Windfall Trap

Consider a strategy with 50 recorded trades:

  • 49 losing trades at -200each (G_- = $9,800$)
  • 1 massive windfall trade resulting from an unforeseen corporate takeover gap: +20,000(G_+ = $20,000$)
Code
PF = \frac&#123;\$20,000&#125;&#123;\$9,800&#125; = 2.04

A superficial scan shows a stellar PF = 2.04. Yet 98% of all executions lost money. If we trim that single outlier, the profit factor collapses to 0.0. Systematic quants evaluate trimmed profit factor (e.g., removing the top 2% of trades) to detect windfall dependency.


The devastating impact of realistic frictions on Profit Factor

The most pervasive error in amateur quantitative backtesting is ignoring or underestimating trade execution frictions. In liquid financial markets, every transaction crosses a bid-ask spread, incurs broker commissions or clearing fees, and experiences execution slippage against the order book.

The Asymmetric "Double-Tax" of Friction

Let c_i > 0 denote the total round-trip friction cost (entry plus exit) for trade i. When moving from frictionless gross accounting to real-world net execution:

  • Winning Trade (P_i^&#123;\text&#123;gross&#125;&#125; > 0): P_i^&#123;\text&#123;net&#125;&#125; = P_i^&#123;\text&#123;gross&#125;&#125; - c_i (Realized gain shrinks)
  • Losing Trade (P_i^&#123;\text&#123;gross&#125;&#125; &lt; 0): |P_i^&#123;\text&#123;net&#125;&#125;| = |P_i^&#123;\text&#123;gross&#125;&#125;| + c_i (Realized loss expands)

Transaction friction attacks the profit factor simultaneously from both sides:

Code
PF_&#123;\text&#123;net&#125;&#125; = \frac&#123;\sum_&#123;i \in W^*&#125; (P_i^&#123;\text&#123;gross&#125;&#125; - c_i)&#125;&#123;\sum_&#123;i \in L^*&#125; (|P_i^&#123;\text&#123;gross&#125;&#125;| + c_i)&#125;

Furthermore, marginal winning trades where 0 &lt; P_i^&#123;\text&#123;gross&#125;&#125; &lt; c_i are converted into losing trades. They are subtracted from the numerator and added to the denominator, accelerating the collapse of the ratio.

Numerical Walkthrough: The Erosion of a 2.50 Profit Factor

Consider an algorithmic trading strategy that executes 100 round-trip trades over an annual period:

  • Total Trades: 100 (50 wins, 50 losses, p = 50\%)
  • Average Gross Win: $1,000 (Total Gross Profit: $50,000)
  • Average Gross Loss: $400 (Total Gross Loss: $20,000)
  • Frictionless Gross Profit Factor: \frac&#123;50,000}{$20,000} = \mathbf{2.50}$

Now let us examine what happens across escalating levels of realistic round-trip frictions (combining commissions, exchange fees, and tick slippage):

Friction ScenarioRound-Trip Cost / Trade (c_i)Realized Avg WinRealized Avg LossRealized Gross ProfitRealized Gross LossRealized Net PnLNet Profit FactorDegradation vs Gross
0. Pure Gross (Theoretical)$0$1,000$400$50,000$20,000+$30,0002.50Baseline
1. Ultra-Low Fee Institutional$50$950$450$47,500$22,500+$25,0002.11-15.6%
2. Standard Equity / Liquid ETF$150$850$550$42,500$27,500+$15,0001.55-38.0%
3. Illiquid Stock / High Spread$250$750$650$37,500$32,500+$5,0001.15-54.0%
4. Adverse Slippage / Volatility$320$680$720$34,000$36,000-$2,0000.94Collapse into Ruin

In Scenario 4, an apparently magnificent gross profit factor of 2.50 implodes into an account-draining PF = 0.94 simply due to realistic slippage and trading costs.

[!IMPORTANT] This is why deterministic backtesting frameworks like OpenBacktesting prioritize exact execution modeling. By applying user-defined per-share commission structures and configurable tick or percentage slippage models directly to each simulated order fill, you eliminate phantom alpha before risking capital.


Python implementation: Vectorized calculation and trade log audit

The following production-ready Python module implements vectorized profit factor calculation, handles numerical division-by-zero edge cases, computes outlier-trimmed metrics, and conducts friction sensitivity analysis.

audit_profit_factor.py
import numpy as np
import pandas as pd


def audit_profit_factor(
    trades: pd.DataFrame,
    pnl_col: str = "pnl",
    round_trip_cost: float = 0.0,
    trim_outlier_pct: float = 0.0,
) -> dict[str, float]:
    """
    Compute comprehensive profit factor, expectancy, and payoff metrics
    from a closed-trade ledger.

    Parameters
    ----------
    trades : pd.DataFrame
        DataFrame containing closed trade records.
    pnl_col : str
        Column name representing trade PnL before or after base costs.
    round_trip_cost : float
        Additional per-trade friction (commission + slippage) to deduct.
    trim_outlier_pct : float
        Top percentile of winning trades to trim (e.g., 0.02 trims top 2%).

    Returns
    -------
    dict[str, float]
        Dictionary of audited quantitative performance metrics.
    """
    if trades.empty or pnl_col not in trades.columns:
        raise ValueError(f"Trades DataFrame must contain column '{pnl_col}'")

    # Apply per-trade execution friction
    pnl = trades[pnl_col].astype(float).copy() - round_trip_cost

    # Optional outlier trimming to test windfall resilience
    if trim_outlier_pct > 0.0:
        cutoff = pnl.quantile(1.0 - trim_outlier_pct)
        pnl = pnl[pnl <= cutoff]

    # Partition winners and losers
    wins = pnl[pnl > 0.0]
    losses = pnl[pnl < 0.0]
    breakeven_count = int((pnl == 0.0).sum())

    gross_profit = float(wins.sum())
    gross_loss = float(abs(losses.sum()))
    net_profit = float(pnl.sum())

    total_trades = len(pnl)
    win_count = len(wins)
    loss_count = len(losses)

    # Win rate and Loss rate
    win_rate = (win_count / total_trades) if total_trades > 0 else 0.0
    loss_rate = (loss_count / total_trades) if total_trades > 0 else 0.0

    # Average payoffs
    avg_win = float(wins.mean()) if win_count > 0 else 0.0
    avg_loss = float(abs(losses.mean())) if loss_count > 0 else 0.0
    payoff_ratio = (avg_win / avg_loss) if avg_loss > 0 else np.nan

    # Robust Profit Factor with division-by-zero handling
    if gross_loss == 0.0:
        profit_factor = np.inf if gross_profit > 0.0 else 0.0
    else:
        profit_factor = gross_profit / gross_loss

    # Expected dollar value per trade: (p * avg_win) - (q * avg_loss)
    expectancy = (win_rate * avg_win) - (loss_rate * avg_loss)

    return {
        "total_trades": float(total_trades),
        "win_count": float(win_count),
        "loss_count": float(loss_count),
        "breakeven_count": float(breakeven_count),
        "win_rate": win_rate,
        "avg_win": avg_win,
        "avg_loss": avg_loss,
        "payoff_ratio": payoff_ratio,
        "gross_profit": gross_profit,
        "gross_loss": gross_loss,
        "net_profit": net_profit,
        "profit_factor": profit_factor,
        "expectancy_per_trade": expectancy,
    }


if __name__ == "__main__":
    # Generate realistic synthetic trade distribution:
    # 120 trades with modest edge + 2 large outlier windfalls
    np.random.seed(42)
    base_pnls = np.random.normal(loc=40.0, scale=350.0, size=120)
    
    # Inject two artificial windfall outliers
    base_pnls[15] = 4800.0
    base_pnls[88] = 5200.0

    df_trades = pd.DataFrame({"pnl": base_pnls})

    # 1. Frictionless Gross Audit
    gross_metrics = audit_profit_factor(df_trades, round_trip_cost=0.0)

    # 2. Realistic Friction Audit ($75 per trade commission + slippage)
    net_metrics = audit_profit_factor(df_trades, round_trip_cost=75.0)

    # 3. Friction + Outlier-Trimmed Audit (drop top 2% of trades)
    robust_metrics = audit_profit_factor(
        df_trades, round_trip_cost=75.0, trim_outlier_pct=0.02
    )

    print("--- 1. GROSS METRICS (Zero Friction) ---")
    print(f"Profit Factor : {gross_metrics['profit_factor']:.2f}")
    print(f"Net Profit    : ${gross_metrics['net_profit']:,.2f}")
    print(f"Win Rate      : {gross_metrics['win_rate'] * 100:.1f}%")
    print(f"Payoff Ratio  : {gross_metrics['payoff_ratio']:.2f}")

    print("\n--- 2. REALISTIC NET METRICS ($75 Friction / Trade) ---")
    print(f"Profit Factor : {net_metrics['profit_factor']:.2f}")
    print(f"Net Profit    : ${net_metrics['net_profit']:,.2f}")
    print(f"Expectancy    : ${net_metrics['expectancy_per_trade']:.2f} / trade")

    print("\n--- 3. TRIMMED ROBUST METRICS (Friction + Top 2% Trimmed) ---")
    print(f"Profit Factor : {robust_metrics['profit_factor']:.2f}")
    print(f"Net Profit    : ${robust_metrics['net_profit']:,.2f}")
    print(f"Expectancy    : ${robust_metrics['expectancy_per_trade']:.2f} / trade")

Script Output Analysis

Executing this audit against the synthetic dataset demonstrates how fragile headline metrics can be:

Code
--- 1. GROSS METRICS (Zero Friction) ---
Profit Factor : 1.84
Net Profit    : $11,777.12
Win Rate      : 52.5%
Payoff Ratio  : 1.67

--- 2. REALISTIC NET METRICS ($75 Friction / Trade) ---
Profit Factor : 1.15
Net Profit    : $2,777.12
Expectancy    : $23.14 / trade

--- 3. TRIMMED ROBUST METRICS (Friction + Top 2% Trimmed) ---
Profit Factor : 0.58
Net Profit    : -$7,900.02
Expectancy    : -$67.52 / trade

Notice the progression:

  1. The raw, frictionless report indicates a seemingly attractive strategy (PF = 1.84, $11,777 net profit, 52.5% win rate).
  2. Incorporating standard transaction friction shrinks the profit factor to 1.15 and slashes net profit to $2,777.
  3. Trimming just two windfall outlier trades causes the system to drop into deep negative expectancy (PF = 0.58, loss of -$7,900). The system possessed no structural alpha; it was simply a carrier for two lucky trades.

Triangulating metrics: Why Profit Factor alone is dangerous

Because profit factor aggregates gross gains and gross losses into a single scalar value, quantitative developers must triangulate it with complementary risk and structural metrics.

The Sustainability Matrix: Win Rate vs. Payoff Ratio

Using our analytical derivation PF = \left(\frac&#123;p&#125;&#123;1 - p&#125;\right) \cdot R, we can solve for the required payoff ratio (R^*) needed to achieve a target profit factor at any given win rate:

Code
R^*(p, PF) = PF \cdot \left( \frac&#123;1 - p&#125;&#123;p&#125; \right)

The following matrix illustrates sustainable versus unsustainable trade profiles across varying win rates:

Win Rate (p)Required R for Breakeven (PF = 1.0)Required R for Viable (PF = 1.3)Required R for Strong (PF = 1.6)Strategy ArchetypePractical Viability Assessment
20.0%4.005.206.40Ultra-Long Trend FollowingVery high psychological barrier; severe drawdown streaks.
30.0%2.333.033.73Standard Trend FollowingClassic managed futures model; requires large right-tail runners.
40.0%1.501.952.40Swing MomentumWell-balanced profile; attainable across equities and commodities.
50.0%1.001.301.60Co-integrated Pairs / BreakoutHighly achievable; equal win/loss frequency requires moderate payoff.
60.0%0.670.871.07Mean ReversionHigh win rate allows taking smaller gains than losses.
70.0%0.430.560.69Credit Spreads / Short VolatilityAsymmetric left-tail vulnerability; catastrophic risk if unhedged.
80.0%0.250.330.40Scalping / Grid SystemsHigh risk of ruin; single tail loss erases dozens of wins.

Triangulation Dimensions

When auditing strategy performance, pair Profit Factor with these three vital dimensions:

Code
                    ┌──────────────────────────────┐
                    │        Profit Factor         │
                    │   (Gross Capital Efficiency) │
                    └──────────────┬───────────────┘
                                   │
         ┌─────────────────────────┼─────────────────────────┐
         │                         │                         │
┌────────▼────────┐       ┌────────▼────────┐       ┌────────▼────────┐
│ Structural Edge │       │ Tail Risk Depth │       │ Return Velocity │
│   - Win Rate    │       │  - Maximum DD   │       │  - Calmar Ratio │
│   - Payoff (R)  │       │  - Ulcer Index  │       │  - Trade Count  │
│   - Expectancy  │       │  - Recovery Time│       │  - Annual CAGR  │
└─────────────────┘       └─────────────────┘       └─────────────────┘

1. Maximum Drawdown (MDD) and Underwater Duration

Profit factor does not penalize consecutive losses. A strategy can have a healthy PF = 1.65 while plunging into a devastating 42% drawdown during a prolonged chop regime. Always audit the equity curve alongside the trade ledger. For comprehensive formulas and underwater curve calculations, explore our detailed guide on Maximum Drawdown in Python.

2. Calmar Ratio and Return Velocity

Profit factor is static; it does not measure annual return velocity relative to tail risk. The Calmar Ratio (\text&#123;CAGR&#125; / |MDD|) contextualizes whether a strategy's capital efficiency translates into compelling, risk-adjusted annual compounding. A strategy with PF = 1.8 that generates only 2% CAGR due to low trade frequency is vastly inferior to one with PF = 1.4 generating 18% CAGR with manageable downside.

3. Trade Count (N) and Statistical Significance

To determine whether an observed profit factor is statistically distinguishable from pure luck, quants calculate the t-statistic of the trade expectancy:

Code
t = \frac&#123;\bar&#123;P&#125;&#125;&#123;\frac&#123;s_P&#125;&#123;\sqrt&#123;N&#125;&#125;&#125;

Where \bar&#123;P&#125; is average trade PnL, s_P is the standard deviation of trade returns, and N is sample size. If t &lt; 2.0, your backtest lacks sufficient statistical confidence, regardless of how impressive the headline profit factor appears.


Audit checklist: How to evaluate a reported Profit Factor

Before putting systematic capital at risk or taking an optimization tear sheet at face value, run through this quantitative audit checklist:

  • Sample Size Gate (N \ge 100 closed trades): Reject or heavily discount any strategy with fewer than 30 trades. Target N \ge 100 trades spanning at least one full market cycle (bull, bear, high volatility, low volatility).
  • Outlier Robustness (Trimmed Profit Factor): Recalculate profit factor after trimming the top 1% to 2% most profitable trades. If the trimmed profit factor drops below 1.10, the strategy relies on lottery-like windfalls rather than persistent structural edge.
  • Comprehensive Friction Audit: Verify that realistic commissions, exchange fees, borrowing costs (for shorts), and execution slippage are deducted from every simulated order. Frictionless backtests must be rejected outright.
  • Zero-Loss / Infinite PF Disqualification: Discard any strategy reporting PF = \infty or zero losses. This indicates curve-fitting, lookahead bias, or failure to close losing positions.
  • Sub-Period Consistency: Measure profit factor across rolling 6-month or 1-year windows. Ensure the metric remains consistently above 1.15 rather than relying on a single explosive quarter.
  • Trade Duration and Expectancy Margin: Verify that the average trade expectancy (\bar&#123;P&#125;) exceeds average round-trip friction by at least 3x to 5x. If expectancy is $15 and round-trip costs are $12, the strategy will fail in live execution.
  • Sequence Independence Check: Run Monte Carlo trade-log permutations to verify that reshuffling the order of trades does not cause unmanageable peak-to-trough drawdowns (see our guide on Monte Carlo Backtesting in Python).

Conclusion and resources

Profit factor is one of the most intuitive, capital-scale-invariant metrics available to systematic traders. It provides a direct reading of gross capital efficiency by measuring how many dollars of profit are produced per dollar lost.

However, treating profit factor as a standalone validation score is a critical flaw. A headline profit factor of 2.0 can easily mask severe transaction cost drag, single-trade windfall dependency, devastating drawdown clusters, or insufficient sample sizes. Robust quantitative research requires decomposing profit factor into its underlying win rate and payoff ratio, stress-testing against realistic execution frictions, and triangulating performance against drawdown depth and return velocity.

Next Steps and Further Reading

To deepen your systematic backtesting and risk modeling framework, explore these foundational guides:

  • How to Backtest a Trading Strategy: A complete 10-step architectural blueprint covering data hygiene, causal execution, walk-forward splits, and execution friction.
  • Maximum Drawdown in Python: Learn how to track high-water marks, compute underwater durations, and audit the tail risks that profit factor conceals.
  • Monte Carlo Backtesting in Python: Stress-test your trade log sequences against path dependency and sequence-of-returns risk.
  • OpenBacktesting Documentation: Review deterministic bar-by-bar execution modeling, order types (market, limit, stop, trailing stop, OCO), and reproducible portfolio export schemas in the OpenBacktesting ecosystem.