SoarSoar
Methodology · Track record

Track record methodology

How every number on the public Trading History page is computed. Counting rules, exclusions, statistical formulas, and disclosed incidents. Verifiable and reproducible.

← Live track record⬇ Raw CSV (14d)
Foundation

Data sources

The Trading History page reads from three Postgres tables in our database:

  • trade_cards — every signal Soar has ever published (asset, direction, entry zone, T1, T2, stop, conviction, timestamps).
  • card_outcomes — per-card resolution rows (T1 hit, T2 hit, stop hit, expired) written by the resolution cron.
  • sessions — the publishing session each signal belongs to.

The aggregation logic lives in src/app/api/recent-trades/route.ts; statistical formulas in src/lib/trackRecordStats.ts. Both are open in the repo.

Filters applied at query time:

  • card_type = 'trade' — excludes watching/context cards.
  • sort_order >= 0 — excludes hidden / quarantined cards.
  • clerk_user_id IS NULL — excludes per-user on-demand reports (only site-wide pipeline cards).
  • session_number > 0 — excludes ad-hoc user-research sessions.
  • status = 'published' — only published sessions.
Resolution

Counting rules — what is a win, loss, expiry

A signal's outcome is determined by which level price touches first, in this priority order:

  1. target2_hit — price touched the second target. Counted as a WIN.
  2. target1_hit — price touched the first target but not the second. Counted as a WIN.
  3. stopped_after_t1 — price hit T1 then retraced through the stop. Counted as a WIN (T1 was reached and could have been monetized; we don't simulate selling, so the running-sum gain is the T1-level gain).
  4. stop_hit — price hit the stop without ever touching T1. Counted as a LOSS.
  5. expired — neither targets nor stop hit before the timeframe horizon elapsed. Counted as NEITHER (excluded from win/loss math, included in trade count).
  6. open — still live. Excluded from this page entirely (only the dashboard shows live signals).

Per-signal gain is computed direction-aware:

gain_pct = ((exit_price − entry_mid) / entry_mid) × 100 × direction_sign

where direction_sign = +1 for long, −1 for short
      entry_mid = midpoint of the published entry_zone
      exit_price = T2 / T1 / stop level depending on outcome
De-duplication

Carry-forward + lineage dedup

Soar publishes a signal in a session, then carries it forward to subsequent sessions until it resolves or retires. Each session re-publish creates a new trade_cards row, but they share an outcome timestamp because the resolution cron stamps the entire lineage at once when price crosses a level.

To avoid double-counting carry-forward clones, we dedup on the lineage key:

lineage_key = (asset, direction, timeframe, resolved_at)

Cards with identical resolved_at collapse to a single trade for stats. A re-entry on the same asset after a stop-out has a different resolved_at and counts separately.

The full per-session list shown on the page (vs. the deduped stats) is intentional — admins can see every session appearance for audit purposes; stats use the deduped lineage.

Formulas

Statistical metrics

Every metric on the page has a single canonical implementation in src/lib/trackRecordStats.ts. Formulas:

Hit rate + Wilson 95% CI

Hit rate = wins / (wins + losses). Confidence interval uses the Wilson score interval (Wilson 1927), the industry standard for small-sample binomial proportions:

p = wins / n
center = (p + z²/(2n)) / (1 + z²/n)
margin = (z × √(p(1−p)/n + z²/(4n²))) / (1 + z²/n)
CI95 = [center − margin, center + margin]
where z = 1.96 for 95% confidence

Wilson is asymmetric and stays inside [0,1] even at p≈0 or p≈1, where the naive Wald interval breaks.

Mean per signal + standard error

mean = Σ gain_pct / n
σ = √(Σ(gain_pct − mean)² / (n − 1))   ← Bessel correction for unbiased estimator
SEM = σ / √n   ← standard error of the mean

Profit factor

PF = Σ positive_returns / |Σ negative_returns|
PF > 1     → net profitable
PF > 1.5   → healthy
PF > 2     → strong
PF < 1     → losing

Expectancy per signal

Expectancy = (winRate × avgWin) − (lossRate × avgLoss)
Positive → system is +EV per signal even before sizing.

Sharpe ratio (annualized)

Per-signal Sharpe annualized at 1825 trades/year (≈ 5 closes/day × 365 days). Risk-free rate = 0% because Soar is a signal-only product with no capital deployed:

Sharpe = (mean / σ) × √(trades_per_year)
trades_per_year = 1825   ← Soar publishing cadence

Caveat: this is a per-signal Sharpe, not a portfolio-time-weighted Sharpe. Comparable to itself across windows; not directly comparable to fund Sharpe figures.

Sortino ratio (annualized)

Like Sharpe but uses downside-only deviation (penalizes losses, ignores upside vol). Better for the asymmetric distributions Soar's track record shows:

σ_downside = √(Σ negative_returns² / count_negative)
Sortino = (mean / σ_downside) × √(trades_per_year)

Max drawdown

Largest peak-to-trough decline of the running-sum cumulative index over the window. Captures the worst stretch.

Cumulative running-sum vs SPY

The headline cumulative number is a running sum, not a compounded portfolio return. We start at 100 and add each closed signal's gain_pct in resolution-time order:

index₀ = 100
indexᵢ = indexᵢ₋₁ + gain_pctᵢ

Why running sum and not compounding: Soar publishes signals; users decide capital deployment. Compounding would imply serial reinvestment into one rolling pot, which is not what the product is. Running sum is the honest "if every signal got 1 unit of capital, here's the per-signal additive total."

SPY benchmark is fetched from Yahoo Finance daily closes over the same window and indexed to 100 at the first signal close. Alpha = Soar cumulative − SPY cumulative.

Transparency

Engine-version timeline + caveats

Soar's engine has gone through significant versions. The default 14-day window reflects the current engine. The all-time view includes earlier versions whose behavior is not representative of today's engine. Per-version performance:

pipeline_versionClosedWin rateSum return
1.0 (oldest)687.4%−79.19%
3.1 (mid)5324.5%−16.09%
6.28.3.x3170.0%+224.37%
v7.x (current)buildingbuilding

Why we show the older versions at all:hiding them would be cherry-picking. The all-time toggle exists for full transparency, but is labeled clearly so visitors don't mistake the all-time number for the current engine's expected output. The 14-day default is the right framing for "what does today's engine do."

Incident disclosure

Disclosed: DASH price-feed incident

Incident — Disclosed 2026-05-09

On 2026-05-08 we identified two paper-trade rows on the asset DASH (Dash crypto, ticker DASH) that booked +354.06% and +352.33% gains. Audit confirmed the exit prices ($168.50, $168.81) came from DoorDash stock (also ticker DASH), not Dash crypto. Dash crypto traded between $34–$50 over the relevant window and never touched $168.

Root cause: the trade-closure cron at src/app/api/cron/check-trades/route.ts:24-106 had an incomplete crypto-ticker map. DASH was missing, so the price lookup fell through to the Nasdaq stocks API, which returned DoorDash. The bug pattern is the same one that caused the v6.30.9.23 logo issue (DoorDash logo shown on Dash crypto card).

Fix: the two affected rows are quarantined (is_test=true) and excluded from the canonical engine_comparison_running_totals rollup. Phantom impact on Soar's all-time running sum: +$7,063.90 of fake gains removed. Adjusted all-time net P&L: +$10,231.80 → +$3,167.90 on the $25K reference balance (from +40.93% → +12.67%).

The Trading History page on this site reads from trade_cards.hit_price_t2 (the published target level), not from the corrupted exit-price feed, so the page's computed gain for those DASH rows was always the legitimate ~+15% T2 hit, not the +354% phantom. The phantom only affected the engine-comparison admin canonical rollup. Both surfaces now reconcile.

Disclosure rationale: showing the scar publicly is the credibility play. We found a bug, we're telling you about it, here's the fix and the impact.

Scope

Other exclusions

  • Live (open) signals — excluded from this page entirely. Only on the dashboard.
  • On-demand user research — per-user signals from the report builder. Not site-wide pipeline output.
  • Watching / context cards — not full trade signals, no entry/T1/T2/stop levels.
  • Hidden / quarantined cards (sort_order < 0) — operator-flagged for data-quality review.
  • Test rows (is_test = true) — synthetic data + the disclosed DASH incident rows.
Proof

Verifiability + on-chain anchor

Three layers of independent verification:

  1. Raw CSV — every signal + outcome row is downloadable at /api/recent-trades/csv. Rerun any computation; we don't aggregate-and-hide.
  2. Cryptographic anchor on Solana mainnet — every hour, the new signals + outcomes are hashed into a Merkle tree, signed (Ed25519), and the root is published on Solana mainnet via the official Memo Program (MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr). Once anchored, the root cannot be modified without leaving cryptographic evidence. Anyone can call /api/verify/:signal_id to get an inclusion proof and independently confirm a signal existed at a given time with given parameters.
  3. Third-party audit (planned) — engagement of an independent quantitative consultant to validate methodology + sample data. Their report will be published in full, including any findings.

Live anchor history

The most recent 20 Merkle anchors written by the Soar engine. 20 of 20 are committed on Solana mainnet (the rest are local-only, awaiting their next chain tick).

Anchor wallet (verifiable on Solscan): 9yWYok7GmHHYk6VQiJGn3ddhifsPDh6HPVfUU6FjWaXN · View on Solscan ↗

AnchoredLeavesRoot (sha256)Chain
May 24, 6:05 PM550b72b23344e85bc46d0⛓ Solana ↗
May 24, 3:05 PM37067519c71438d89eff4⛓ Solana ↗
May 24, 12:05 PM3a61316c05ea01352cd36⛓ Solana ↗
May 24, 4:05 AM16a70cbfc87b621ea0838⛓ Solana ↗
May 23, 9:05 PM13e9b1913c875ea1dc6ce⛓ Solana ↗
May 23, 6:05 PM3bd581e6b6934a40d749e⛓ Solana ↗
May 23, 3:05 PM89ae50b8607efb9d54d4f⛓ Solana ↗
May 23, 12:05 PM39318c05147ad621227ae⛓ Solana ↗
May 23, 10:05 AM161902e7c952c342b91b8⛓ Solana ↗
May 23, 9:05 AM1173a34275814d52b91b2⛓ Solana ↗
May 22, 9:05 PM131d17683df2988b17d3f⛓ Solana ↗
May 22, 6:05 PM1793ceddaf548b3301c70⛓ Solana ↗
May 22, 1:05 PM1cbdf4bdee148d4ccebd2⛓ Solana ↗
May 22, 12:05 PM5ff097ba573c4056dd9c2⛓ Solana ↗
May 22, 2:05 AM1d7a3b2cf15936db105e1⛓ Solana ↗
May 21, 9:05 PM538b86a318ee65e47d6b8⛓ Solana ↗
May 21, 8:05 PM1b0ea9aac9b2e7388feba⛓ Solana ↗
May 21, 7:05 PM152b5cbe1975ee57b1e10⛓ Solana ↗
May 21, 6:05 PM10d47aa5c04c515b30fc6⛓ Solana ↗
May 21, 5:05 AM2968bafc719985809d501⛓ Solana ↗

How to verify yourself: take any signal's leaf_hash from /api/verify/:signal_id, walk the Merkle siblings on that response up to the root_hex, then read the on-chain memo at the matching Solscan link above. The memo content (soar:trc:<root_hex>) is the same root your inclusion proof rolls up to. If they match, the signal existed at the timestamp shown — anyone can prove this without trusting Soar.

Required reading

Legal disclaimer

Hypothetical / simulated performance. The track record measures signal-quality outcomes against the prices and levels Soar published. It is not a record of actual trades placed by Soar or any user. No actual capital is or has been deployed against this record. Real-world results depend on capital deployment, sizing, slippage, and fees that this page does not model.

Soar is not a registered investment adviser. Nothing on this page is investment advice, an offer to buy or sell securities, or a recommendation tailored to any individual. Past performance is not indicative of future results.

Methodology version: v7.5.22. Last updated: 2026-05-14. On-chain anchoring went live on Solana mainnet 2026-05-09 — every hourly Merkle root is now publicly verifiable.

← Back to live track record