Every strategy I've published in this journal so far had a human idea at its core: a breakout window, a channel fade, a session range. AG QUANT PROB V4 is different. It doesn't start from a chart pattern — it starts from a question borrowed from how institutional desks think: what is the probability this trade wins, what does it pay when it does, and what does it cost to find out?

If the answer — computed live, on every bar — isn't clearly positive after commissions and slippage, the bot does nothing. No trade. It will happily sit through an entire session without clicking once. That single design decision is, I believe, the most important thing in this article, and it's why I'm publishing the full source code below.


What "math-first" actually means

"Quant" gets thrown around loosely. Here it means something specific: every input to the entry decision is a statistical estimate with a known interpretation, not an indicator with magic numbers. V4 computes, on every bar:

None of these are exotic. They're the standard toolkit of time-series econometrics. What's unusual is finding them in a retail NinjaScript file instead of moving-average crossovers.

The architecture: two engines, one gate

V4 runs two independent signal engines — mean reversion (fade a stretched z-score when the series is anti-persistent and the OU half-life is short) and momentum (join a trend when the slope t-stat is significant and VR confirms persistence). The variance ratio decides which engine has the right to exist on any given bar.

Here's the design philosophy, straight from the file header: hard filters only define direction and regime. Quality is decided by a single probabilistic gate. Each candidate trade gets a score built from the evidence (z-score excess, reversion quality, VR distance from 1.0, trend efficiency, volume anomaly, candle confirmation), which is mapped through a logistic function into a probability estimate p. Then the gate runs, in order:

  1. Reward/risk check — the geometric target/stop ratio must clear a minimum.
  2. Probability checkp must clear the probability threshold.
  3. Sizing check — risk-based position sizing with a fractional-Kelly cap; if the risk per contract exceeds the budget, quantity is zero and the trade dies here.
  4. Cost gate — if commission + slippage exceed a set percentage of the trade's dollar risk, the trade is structurally unviable (this is what kills most 1-minute noise trades).
  5. Net expected value — the final boss.

That last check is the heart of the bot, and it's five lines of arithmetic:

// Gross EV in R units: p·RR − (1−p)
private double GrossEV(double probability, double rewardRisk) {
    return probability * rewardRisk - (1.0 - probability);
}

// Net EV: subtract (commission + 2-sided slippage) as a fraction of $ risk
private double NetExpectedValueR(double probability, double rewardRisk, double stopDistance) {
    double riskDollars = stopDistance * Instrument.MasterInstrument.PointValue;
    double slippageDollars = 2.0 * SlippageTicksPerSide * TickSize
                             * Instrument.MasterInstrument.PointValue;
    double costR = (CommissionPerRoundTurn + slippageDollars) / riskDollars;
    return GrossEV(probability, rewardRisk) - costR;
}

EVR = p·RR − (1−p) − costs/risk. If that number isn't above the threshold, there is no trade — regardless of how pretty the setup looks. Commissions and slippage aren't an afterthought in the backtest report; they're inside the entry decision itself.

An entry signal is an opinion. Expected value net of costs is an argument. V4 only trades when it can make the argument.

The part I'm proudest of: the block counter

Every rejected signal is logged with its cause — probability too low, EV negative, reward/risk short, sizing zero, cost ratio too high, cooldown active, session limits hit — and a summary prints at the end of each session. When the bot doesn't trade, I know exactly why. Most retail bots fail silently; this one keeps receipts. If you've ever stared at a flat equity curve wondering whether your bot is broken or just picky, you understand why this matters.

And the math that had to be fixed to get here

V4 exists because V1 was quietly, mathematically wrong in ways that produced zero trades — and I documented every fix in the file header. The AR(1) regression ran on raw prices instead of log-prices, so φ ≈ 0.999 and reversion quality was always zero. The slope statistic was scaled roughly 10× too small, so momentum never triggered. Risk sizing could return zero contracts on NQ with a small risk budget, so even valid signals died at the sizing step. Three subtle bugs, three engines silently dead. If there's one lesson from months of building this: when a quant bot doesn't trade, it's rarely the market — audit your formulas first.


The backtest: every number shown

This is a Strategy Analyzer run on NQ SEP26, May 1 through July 5, 2026 — two months of the exact evening window I actually trade (18:00–23:00 ET), on Kagi 5 bars built from 1-minute data, commissions included, one entry per direction, everything flat at session close.

+$22,461Net profit
1.77Profit factor
−$2,714Max drawdown
56.4%Win rate
195Total trades
1.03Sharpe ratio
MetricAll tradesLongShort
Net profit+$22,461.80+$8,199.64+$14,262.16
Profit factor1.771.601.91
Trades19586109
Win rate56.41%52.33%59.63%
Avg trade+$115.19+$95.34+$130.85
Avg win / avg loss1.371.461.30
Max drawdown−$2,714.32−$2,182.92−$2,929.00
Largest loss−$505.76−$505.76−$505.76
Commission paid$1,123.20$495.36$627.84

A few things worth noticing, because they're the fingerprint of the design rather than luck:

Now the honest part: what this backtest is not

This journal has one rule: publish the caveats with the same font size as the profits. Here they are.

A backtest is not a promise. It's a hypothesis that survived one interrogation. The market gets to cross-examine it every night after that.

Why a bot like this could actually matter

Here's the argument for why the math-first approach is worth your time — not this specific parameter set, but the framework.

First: it converts trading from prediction into inventory management. The bot doesn't claim to know where NQ is going. It claims something much smaller and more defensible: "over many trades where p, RR, and net EV clear these thresholds, the sum should be positive." That's the same claim a casino makes, and it's the only claim that survives contact with randomness. Every input to that claim is measurable and auditable.

Second: it makes discipline structural instead of emotional. I've written before about the override problem — the 2 AM urge to interfere. V4's design attacks it from both sides: the EV gate refuses the trades I'd take out of boredom, and the session rails (max daily loss, consecutive-loss halt, cooldowns) refuse the revenge trades I'd take out of frustration. The math doesn't get tired at the end of a hospital shift. I do.

Third: it fails loudly instead of silently. Between the block counters and the on-chart diagnostic panel, every non-trade is explained. When the edge decays — and every edge decays — the diagnostics will show the probability estimates degrading before the account does. That's the difference between retiring a strategy and being retired by it.

"Used correctly" is carrying a lot of weight in this article's subtitle, so let me define it: validate on data the optimizer never saw, size so the worst month is survivable, respect the session rails as law, re-audit monthly, and treat any bot — including this one — as an employee on permanent probation. Used that way, a probability-gated engine is the most honest tool I've found for trading while holding a day job. Used as a money printer with the risk limits deleted, it will find your maximum pain tolerance with impressive efficiency.


The full source code

As promised: the complete strategy, not a teaser. 1,682 lines of NinjaScript for NinjaTrader 8 — both engines, the probability gate, the OU half-life and variance ratio implementations, the Kelly-capped sizing, the session risk rails, and the block-counter diagnostics. Import it via Tools → Import → NinjaScript Add-On or drop the file into Documents\NinjaTrader 8\bin\Custom\Strategies and compile.

AGQUANTPROBV4.cs
NinjaTrader 8 · C# · 1,682 lines · free download
↓ Download the strategy

Two of the mathematical cores, so you know what you're importing. The Ornstein-Uhlenbeck half-life — the test that decides whether mean reversion is even allowed to trade:

// OU half-life: dy_t = a + b·y_{t−1} + e, on log-price.
// HL = −ln(2)/ln(1+b). If b ≥ 0 there is no reversion (HL infinite).
private double OrnsteinUhlenbeckHalfLife(int period) {
    // ... regression of Δlog(price) on lagged log(price) ...
    double b = (n * sumXY - sumX * sumY) / denom;
    if (b >= 0) return double.PositiveInfinity;
    return -Math.Log(2.0) / Math.Log(1.0 + b);
}

And the regime detector — the Lo-MacKinlay variance ratio that arbitrates between the two engines:

// Lo-MacKinlay Variance Ratio with overlapping returns:
// VR(q) = Var(r_q) / (q · Var(r_1)).
// VR < 1 → anti-persistent (mean reversion allowed)
// VR > 1 → persistent (momentum allowed)
private double VarianceRatio(int q, int window) {
    // ... 1-period return variance vs q-period return variance ...
    return Clamp(varQ / (q * var1), 0.05, 5.0);
}

// Hurst approximation from VR: H ≈ 0.5 + ln(VR) / (2·ln q)

Everything else — the logistic scoring, the candidate builder, the sizing, the rails — is in the file, commented in detail. Nothing is obfuscated. If you find a bug, email me; the block counters exist because I assume there's always one more.


Frequently asked questions

What is a probability gate in a trading bot?

It's a final filter that estimates the probability of a trade winning — via a logistic model over statistical evidence like z-scores, t-statistics and variance ratios — computes the expected value net of commissions and slippage, and blocks any trade whose net EV isn't clearly positive. The bot only trades when p·RR − (1−p) − costs/risk clears its threshold.

Is AG QUANT PROB V4 free to download?

Yes. The complete 1,682-line NinjaScript source for NinjaTrader 8 is linked above — both engines, the gate, the sizing, and the risk rails. It's published for educational purposes; validate it yourself before risking a dollar.

What math does it use?

An EWMA volatility z-score (RiskMetrics λ = 0.94), an OLS t-statistic on the log-price slope, the Ornstein-Uhlenbeck half-life, the Lo-MacKinlay variance ratio with an approximate Hurst exponent, a log-volume z-score, net expected value in R units, and fractional-Kelly position sizing with a cap.

Can I run it on a prop firm funded account?

Bulenox and TradeDay both allow NinjaTrader automated strategies, and the session rails were designed with prop firm drawdown rules in mind. But the correct path is data → Market Replay → sim → micros → evaluation. Skipping steps is how evaluations get failed.


Run it on funded capital (after you validate it)

The correct order is: import → backtest on your own data → Market Replay → sim → micro contracts → then, and only then, an evaluation account. Both firms below allow NinjaTrader automated strategies and are compatible with the 18:00–23:00 ET session this bot trades. The session rails (max daily loss, consecutive-loss halt) were designed with prop firm drawdown rules in mind.

Affiliate Partner

Bulenox

No minimum trading days, 100% profit split on the first $10K, native NinjaTrader + Rithmic. My pick for algo strategies.

Start Evaluation →
Affiliate Partner

TradeDay

Day-one payouts, 24-hour processing, up to 95% profit split. Best payout policy in the industry.

Get Funded →

Disclosure: affiliate links — commission earned at no cost to you. Risk disclosure: all content is educational, nothing here is financial advice. Backtest results are hypothetical, do not account for all real-market conditions, and do not guarantee future performance. Trading futures involves substantial risk of loss and is not suitable for every investor.