Featured image of post LynxCrypto Kicks Off (Part 2): I Turned the Strategy Into Code, Then Falsified It Myself

LynxCrypto Kicks Off (Part 2): I Turned the Strategy Into Code, Then Falsified It Myself

I built a 15m range mean-reversion strategy into a backtestable event-driven engine, then punched myself in the face with 180 days of real ZEC data: the gross edge of naive mean reversion is roughly zero — not because risk management failed, but because fees and slippage crushed it into negative expectancy. 107 tests all green, four iron rules welded into the code.

Let me lead with the conclusion — the most valuable sentence in this entire post: using 180 days of real ZEC data, I proved that the “15-minute naive mean reversion” strategy I originally envisioned has a gross edge of roughly zero — not because risk management doesn’t work, but because fees and slippage take that tiny mean-reversion tendency and crush it straight into negative expectancy. This conclusion will save me more money than any “backtest moonshot” ever could.


1. The Ledger Before Starting: $880 and a System That Can’t Afford to Lose

First, let’s align on where things stand. At the end of Part 1, my books read: $1,080, with $200 withdrawn, leaving $880 of principal in hand. My goal is still that aggressive one — turn 800 into 1,000 every day and withdraw 200. But Part 1’s deep research already told me this goal is mathematically unsustainable.

So Part 2’s job is not “hurry up and spin up the grid to make money.” It’s this: turn the strategy from a slogan into a backtestable, falsifiable, reproducible system — let it die on historical data first, then decide whether to let it die for me in live trading.

That’s what the LynxCrypto project does. The code is on [GitHub (locally /home/li/lynxcrypto)], and the tech stack follows the choices from Part 1:

  • Python 3.12 — at 15-minute bars, latency is not the bottleneck, and the ecosystem is the richest.
  • CCXT for data — public endpoints pull ZECUSDT candles and funding rates, no API key needed.
  • A self-written event-driven backtest engine — no backtrader (unmaintained since 2023); I guarantee no look-ahead bias myself.
  • Pure pandas for indicators — no TA-Lib; every indicator can be hand-verified in unit tests.

The whole system has 48 unit tests, all green, and it runs end-to-end on 17,280 real 15-minute candles. Below I’ll walk through how I built it module by module, and the traps I hit at each step.


2. Four Iron Rules, Welded Into the Code

Part 1 defined three iron rules. I added a fourth in code, making it:

  1. Never martingale — position size can only follow the Kelly fraction; there is no code path for “add more after a loss.”
  2. Never trade without a stop-loss — every entry carries an ATR stop and a liquidation-price precheck.
  3. No live trading until the backtest passes — triple validation via WFO + MCPT + DSR/PBO, any single veto kills it.
  4. Never look ahead (new) — the easiest rule to fool yourself on: signals must be computed at bar close and filled at the next bar’s open. Any “use the current close to decide the current fill” logic is stealing money from the future.

Rule 4 sounds technical, but it’s the root cause of most backtest fraud. I wrote a dedicated unit test to nail it down: in a market that jumps 100 → 120, if the strategy emits a signal at bar 0, the engine must fill at bar 1’s open of 120, not bar 0’s close of 100. If this test fails, the entire engine is garbage.


3. Phase 1: Build the “Friction of the Real World” First

Too many people get rich in backtests and go broke in live trading; the difference is the cost model. So LynxCrypto’s first phase was not writing the strategy — it was building the friction of the real world first, including:

  • Fees: taker 0.05%, maker 0.02% (measured on ZECUSDT).
  • Slippage: the Almgren-Chriss square-root market impact model, impact ∝ σ·√(order size / average daily volume).
  • Funding: perpetuals charge every 8 hours; a neutral floor of +0.01%/8h ≈ 10.95% annualized. Funding on volatile coins like ZEC often skews positive, and longs get bled dry over time.
  • Liquidation price: the isolated-margin liquidation formula; the liquidation price must fall outside the grid/range.

One point many people get wrong: all fees are charged on notional value (margin × leverage), not on margin. So the break-even spread as a “price percentage” is actually leverage-independent; but expressed as a “margin percentage” you multiply by leverage. At 8x leverage, taker fees in and out plus slippage eat roughly 0.1% of price movement — on a 15-minute timeframe, that’s no small number.

The cost model’s math was verified by 10 unit tests, and the liquidation formula was checked against the exchange’s official documentation. Get this step solid, and the backtest numbers that follow are trustworthy.


4. Phase 2: Translating “Range + Flip” Into a State Machine

This was the core of my original idea — and the part that needed the most correction.

4.1 The flip: flagged as a red flag in Part 1, disabled by default in this version

Back in Part 1’s research I flagged a trap: the “71.5% win-rate flip system” circulating online, when actually tested, turned out to be “close and go flat” (long-only-fade-then-flat), not a real “the moment a long ends, immediately flip short.” The evidence for true flipping is weak, and the risk of getting whipsawed to death around the mean is extremely high.

So this version of the LynxCrypto strategy defaults to a flat “flip”: close the position and stop, no unconditional reversal. Flipping is kept as an optional enhancement for Phase 6, and it only gets turned on if an independent backtest proves it has a positive marginal contribution.

4.2 Anti-one-sided-market: not an indicator, a state machine

My biggest fear in Part 1 was a one-sided trending market wiping out the grid/mean-reversion strategy in one move. The defense is not “just set a stop-loss” — it’s determining whether the market right now is ranging or trending: mean reversion is only allowed in a ranging market; in a trending market it shuts off entirely.

What I implemented is a multi-indicator confluence + hysteresis state machine:

  • Trend votes: ADX>25, CHOP<38.2, Hurst>0.55, expanding Bollinger bandwidth, expanding ATR, EMA20>50>200, price above Supertrend.
  • Range votes: ADX<20, CHOP>61.8, Hurst<0.45, contracting Bollinger bandwidth, contracting ATR.

Two key design choices:

  1. At least 3 indicators must flip simultaneously to change state — a single indicator flipping doesn’t trigger anything, so one stray spike can’t fake you out.
  2. Hysteresis debounce — a new state must be confirmed for 3 consecutive bars before it takes effect, preventing jitter back and forth on the range/trend boundary.

The state machine’s 6 unit tests cover: staying neutral with insufficient data, no switch on a single indicator flip, switching only after consecutive confirmation, and no jitter under whipsaw.

4.3 Entry: fade the extremes, ride back to the mean

In a ranging market, the strategy runs the classic two-sided fade:

  • Long: price breaks below the lower Bollinger band, closes back inside the band, RSI<25, and price is below VWAP — confirming this is “panic oversold,” enter betting on a return to the mean.
  • Short: the mirror image — break above the upper band and close back inside, RSI>75, price above VWAP.
  • Stop-loss: beyond the extreme of the piercing candle, plus 1x ATR.
  • Take-profit: back at VWAP / the Bollinger midline, or a fixed 1.25R.
  • Time stop: if price hasn’t returned to the mean within 24 hours, the fade is probably wrong — get out.

All indicators are pure pandas, with 13 unit tests verified against hand-computed values: ATR converges to the range width on a constant range, RSI=100 when everything rises, VWAP resets at day boundaries, and the Donchian channel excludes the current bar (to prevent self-reference).


5. Phase 3: Kelly Sizing + Three Fuses

Position sizing is the most fundamental split between this system and martingale. Martingale says “double down when you lose”; Kelly says “only add when you win, and how much you add is determined by expectancy”:

$$f^* = \frac{bp - q}{b}$$

p is win rate, b is the win/loss ratio, q=1−p. What you compute is full Kelly; in practice you use quarter Kelly to hedge parameter estimation error. p and b can’t be pulled out of thin air — they must come from out-of-sample backtest statistics, which is why Kelly’s inputs were left as placeholders until Phase 3’s WFO produces real OOS data to fill them.

The fuses (circuit breakers) are latching — once tripped they stay tripped, and can only be reset manually, never auto-recover:

  • Tier 1: intraday drawdown −2% → halve position size.
  • Tier 2: −3% → stop opening new positions.
  • Tier 3: −5% → kill switch: cancel all open orders, flatten all positions, shut down.
  • 4 consecutive losses → pause + force a re-evaluation of whether the regime has already flipped.

The risk layer has 11 unit tests, focused on the “latch”: after a −2% trip, even if equity recovers to a new high, position size stays halved until a manual reset(). This is the thing grid-martingale doesn’t have — a foundation that knows how to say stop.


6. The Backtest: I Falsified My Own Strategy With My Own Hands

Enough setup — let’s look at the data. 180 days of ZECUSDT, 17,280 15-minute candles, real funding rates, no look-ahead, all costs included. I ran three presets:

ConfigTradesWin rateProfit factor PFGross PnLTotal costsNet PnL
Strict (RSI25/75 + VWAP, taker)250%6.2+0.440.15+0.29
Loose (RSI40/60, taker)17250.6%0.98+9.1311.98−2.84
Loose + maker limit21249.5%0.97+10.0715.42−5.49

Reading the three rows together, the conclusion stings but is crystal clear:

1. Strict filtering → almost no trades. With the dual RSI(25/75)+VWAP filter, only 2 trades triggered in 180 days. Selectivity is maxed out, but the sample is too small to mean anything statistically — effectively no strategy at all.

2. Loose filtering → you get trade frequency, but the gross edge is near zero. 172 trades, 50.6% win rate, +9.13 gross — looks okay? But costs are 11.98, and net is −2.84. Profit factor 0.98, below 1 — this is a negative-expectancy strategy.

3. Switching to maker limit orders doesn’t save it either. I thought swapping taker (0.05%) for maker (0.02%) would turn things around. Instead: 212 trades, costs rose to 15.42 (because there were more trades), net −5.49.

The most valuable finding from this set of experiments: the problem isn’t the fee tier (maker/taker), it’s the gross edge itself. On the 15-minute timeframe, ZEC’s mean-reversion tendency is too weak — weak enough that even maker’s low fee rate can’t cover it. In other words — my original naive idea of “find the range, fade the extremes, flip” has no alpha in the face of real costs and real volatility.

This is why my grid made $100 yesterday while today’s “smarter” strategy loses money: what the grid earns isn’t directional money — it’s the money from maker limit orders pushing costs to near zero, with each grid level’s profit above the break-even threshold. My fade strategy has neither the maker cost advantage nor restraint in trade frequency — it just feeds the principal to fees and slippage, little by little.


7. Four Things This “Backtest Loss” Taught Me

Losing money bought me understanding, and these four lessons are worth more than the $880:

1. The first job of a backtest is to falsify, not to confirm. If my goal were “produce a pretty curve,” I could totally tune parameters until it moons (like trotting out the trend-market SMA strategy’s 3.83 Sharpe to brag about). But that’s lying to myself. Letting the strategy die on historical data first is how it saves me money in live trading.

2. Cost is the life-or-death line for 15-minute strategies. The higher the frequency and the smaller the timeframe, the more fees and slippage grow exponentially as a share. For any 15m strategy, first compute the break-even threshold ΔP_be clearly; if each grid level / each trade’s profit can’t cover it, just don’t do it.

3. “High win rate” and “positive expectancy” are two different things. Yesterday’s grid had a high win rate; today’s fade strategy also has a 50% win rate — but one makes money and the other loses. The difference is per-trade expectancy = win rate × average win − loss rate × average loss − costs; win rate is only one term.

4. Regime filtering helps, but it can’t save a signal with no gross edge. The state machine really did block my losses in trending markets (the trend preset barely trades), but it can only “avoid being wrong,” not “manufacture being right.” Alpha still has to come from the signal itself.


8. Next Steps: Where the Real Alpha Lives

Having falsified naive mean reversion, my direction is actually clearer now. LynxCrypto’s next steps:

  1. Restructure the cost side: switch entries from taker market orders to maker limit orders, pushing costs toward zero — the one thing the grid did right, and worth inheriting.
  2. Higher selectivity: better zero trades in a day than negative-expectancy trades. Tighten the regime confluence further, and only strike when “high-quality range + extreme + multi-indicator confirmation” all hold at once.
  3. Land the backtest discipline: finish the triple validation — WFO (walk-forward) + MCPT (Monte Carlo permutation test, p<0.01) + DSR/PBO. Any strategy that can’t pass gets a veto, and absolutely does not enter Phase 4 live trading.
  4. That $880 keeps sitting there: until a new strategy passes validation, the live principal stays untouched. I withdrew the $100 I made yesterday; the 880 stays as seed — but seeds get planted in validated strategies, not in illusions.

9. Closing: To Everyone Else Who Wants “Daily Withdrawals”

If, like me, you came in with a goal like “make 200 a day, withdraw steadily,” I want to hand you the hardest sentence this system taught me:

“Steady daily withdrawals” is not a strategy goal — it’s marketing copy. Real quant trading is: long stretches of research, lots of falsification, strict drawdown control, and then accepting that returns are unevenly distributed — maybe no withdrawal for a week, then one big withdrawal in a month. Martingale can give you the illusion of “small gains every day,” at the price of postponing one big, account-zeroing loss to some future day.

LynxCrypto is not a money-making system yet — it’s an honest system: it tells me “this idea doesn’t make money” before I put money in, not after. For my $880 of principal, that matters more than anything.

See you in the next one. Once I finish WFO + MCPT, if I find a signal that truly passes validation, I’ll come back and write “Part 3: the first live trade that passed validation.” If I don’t find one, I’ll write that too — a story of “I still haven’t found alpha, but my money is still here.” The latter is probably the norm.


Code: /home/li/lynxcrypto, 48 unit tests all green; data is real ZECUSDT perpetuals from 2026-03-09 to 2026-09-05. All backtest numbers include fees, slippage, and funding, with no look-ahead. This post is not investment advice.