Skip to content
All strategies

Strategies

je-suis-tm Heikin-Ashi Backtest port: buy a growing no-upper-wick bearish Heikin-Ashi candle after a bearish one (pyramid up to 3 units), sell everything on a no-lower-wick bullish HA candle after a bullish one (NVDA.USEQ 1-DAY, long-only, next-session-open fills)

Outcome: Abandoned

NvdaHeikinAshiPyramid

Outcome Summary

NvdaHeikinAshiPyramid ported a published Heikin-Ashi pyramiding rule to NVDA daily bars, with sizing as a share of equity and fills at the next open. Over 4 iterations it got past an earlier problem where the strategy never entered a trade. The final backtest traded actively (338 trades) but lost 79.73% with a negative Sharpe and a drawdown near 95%. At the backtest review the analyst confirmed the code matched the contract and abandoned the strategy for negative expectancy, so it never reached optimization, analysis or risk review.

Hypothesis

Faithful port of "Heikin-Ashi backtest" by Administrator (je-suis-tm/quant-trading, Apache-2.0, commit 611b73f). The source runs on NVDA daily Yahoo bars; we keep NVDA, as NVDA.USEQ 1-DAY (split-adjusted daily history from 1999). The source's cited rule page (quantiacs.com) returned HTTP 404 and was not read; the rules below come only from the code. HEIKIN-ASHI SERIES (the source's heikin_ashi()): HA_close[n] = (Open+High+Low+Close)/4 of raw bar n. HA_open[0] = raw Open of the first bar; HA_open[n] =……Show moreShow less

Faithful port of "Heikin-Ashi backtest" by Administrator (je-suis-tm/quant-trading, Apache-2.0, commit 611b73f). The source runs on NVDA daily Yahoo bars; we keep NVDA, as NVDA.USEQ 1-DAY (split-adjusted daily history from 1999). The source's cited rule page (quantiacs.com) returned HTTP 404 and was not read; the rules below come only from the code. HEIKIN-ASHI SERIES (the source's heikin_ashi()): HA_close[n] = (Open+High+Low+Close)/4 of raw bar n. HA_open[0] = raw Open of the first bar; HA_open[n] = (HA_open[n-1] + HA_close[n-1]) / 2. HA_high[n] = max(HA_open, HA_close, raw Low, raw High). HA_low[n] = min(HA_open, HA_close, raw Low, raw High). Compute it recursively in Python floats from bar.open/high/low/close .as_double(), seeded on the first bar the strategy sees. Never round HA values to the instrument price precision. WHY THE PREVIOUS ATTEMPT GOT 0 ENTRIES, AND WHAT THIS CONTRACT CHANGES: the source tests HA_open == HA_high and HA_open == HA_low with exact float equality. Because HA_high is defined as a max that includes HA_open, the equality is the same as HA_open >= max(HA_close, raw High) (and HA_open <= min(HA_close, raw Low) for the exit). This contract writes the conditions in that inequality form, with an optional tolerance whose baseline 0 reproduces the source exactly. It does not tie them to rounded prices or Price objects. Our own count of the raw rule on NVDA.USEQ daily (1999-2026, tolerance 0) gives 822 entry triggers (about 22-46 a year, every year, including ~25 a year in 2015-2017 and 40 in 2022) and 1,895 exit triggers. So the entry fires on this instrument, and a sandbox window of about 950 sessions should show roughly 100 entry triggers. Zero entries in verification means the HA series or the comparison is implemented wrongly (e.g. HA values rounded or quantized, HA_open reseeded every bar, the bull/bear sign reversed, or raw OHLC used in place of HA). It does not mean the rule is rare. This changes no trading rule; it only states the source's equality in a float-safe form. POSITION LOGIC (the source's signal_generation(), stls=3): the source holds 0 to stls 'units'. An entry signal adds one unit only if the units held are fewer than stls. An exit signal sells ALL units held, and is ignored when flat. The entry check comes first (if/elif): a bar that met both sets of conditions would be treated as an entry, but the two cannot both hold, because entry needs a bearish HA candle and exit a bullish one. PLATFORM ADAPTATIONS: (1) Fills: the source fills at the signal bar's close. USEQ has no market-on-close order (same_close_fill unsupported), so every order decided on bar n's close fills at the next session's open (next_open_fill, the platform rule). (2) Sizing: the source buys 100 shares per unit on $10,000. On NVDA in 2015 (~$20 unadjusted) that is about 20% of capital per unit. Because split-adjusted prices make a fixed share count meaningless, each unit is 20% of current account equity (whole shares, floor) at the time of the decision, so the cap is 3 units, about 60% of equity, unlevered. (3) Fees: USEQ zero commission plus the modeled spread/impact. (4) Long-only, as in the source. The plotting, omega, sortino, calmar and mdd code is reporting only and is not ported. The source's performance claims are unverified. RISK CONTROL: route (b), signal-based exit with capped sizing (max ~60% of equity, no leverage). There is no stop-loss, because the source has none and none is invented.

Retry after a no_trades verification loop. Our measurement shows the source rule fires about 22-46 times a year on NVDA.USEQ daily (822 entries, 1999-2026), so the failure was in the implementation (exact-equality or rounded HA comparisons), not in the premise. We keep the user's market and rules, and rewrite the equality tests as float-safe inequalities that are mathematically identical. USEQ single-stock is not an over-represented bucket (USEQ 9.7%).

negative_expectancy: This NVDA daily HA port loses on an adequate development sample: 264 closed positions, -$88,900 realized PnL, PF 0.571, and average trade -0.2002% against the USEQ 0.05% floor. The code's growing bearish-body entry and bullish sell-all conditions match the contract; the ledger shows long pyramided positions rather than the former zero-entry defect. QA's economic concern is borne out as outright negative net expectancy. No specific implementation correction warrants an optimization cycle. This verdict covers this equity-relative, next-open port, not the author's same-close source results. Drawdown alone is not the rejection basis.

Implementation

Heikin-Ashi port of the source's NVDA rules, on NVDA.USEQ daily bars. The HA series is computed recursively in floats, starting from the first bar's open. Entry buys one unit (20% of equity, whole shares) when all of these hold: the HA candle is bearish; it has no upper wick (HA_open >= max(HA_close, High)); its body is larger than body_growth_ratio x the previous body; and the previous HA candle was bearish. Units add up to a cap of 3. Exit sells all units when a bullish HA candle has no lower wick (HA_open <= min(HA_close, Low)) and follows a bullish one. Long-only. Orders fill at the next session open.

Iteration 4 fixes the zero entries. The previous code kept the source's hard-coded 2015-04-01..2018-02-15 date window and its 700-row slicer. Every bar in the recent sandbox window fell outside that window, so the code never generated an order. Following the current hypothesis contract, this version removes the date window, the slicer and the 100-share sizing, and makes these changes: (1) the HA series is computed on every bar the strategy sees; (2) the source's HA_open==HA_high and HA_open==HA_low equalities are……Show moreShow less

Iteration 4 fixes the zero entries. The previous code kept the source's hard-coded 2015-04-01..2018-02-15 date window and its 700-row slicer. Every bar in the recent sandbox window fell outside that window, so the code never generated an order. Following the current hypothesis contract, this version removes the date window, the slicer and the 100-share sizing, and makes these changes: (1) the HA series is computed on every bar the strategy sees; (2) the source's HA_open==HA_high and HA_open==HA_low equalities are written as inequalities that mean the same thing, with an optional wick_tolerance_pct whose baseline of 0 reproduces the source exactly; (3) body_growth_ratio has a baseline of 1.0, which reproduces the source's strict body-growth test; (4) each unit is 20% of equity, capped at 3 units (about 60% of equity, unlevered); (5) the entry branch is checked before the exit branch, as in the source's if/elif; (6) the exit sells all units and is ignored when flat. Platform adaptation: orders fill at the next session open, because USEQ has no market-on-close order. Risk control is route (b): a signal exit plus capped sizing, with no stop because the source has none. The parameters are renamed to match the hypothesis's v2 optimization plan, because the old frozen-window plan no longer matches the hypothesis.

Verification Results

When a position is open and units_held == 0, re-derive units_held from the position quantity (ceil(qty / approximate unit size)), or allow the exit whenever a position is open.

units_held is in-memory state. After a paper/live restart with a position open it restarts at 0, so should_exit (which requires units_held > 0) ignores exit triggers until the position is closed some other way. Pyramid counting is also lost. The backtest never restarts, so the backtest is not affected.

Optional: increment units_held only after a confirmed fill (on_order_filled) instead of at decision time.

should_enter increments units_held before the base class sizes the order. If position_size returned 0 or the order were dropped for min notional, the count would drift. The counter self-heals: it resets to 0 on the next flat bar, and a 20%-of-equity NVDA unit is far above $5. So the drift cannot happen in practice.

None required.

min_bars_required() returns 2, which overrides the base default of 20. This is deliberate and harmless. The first primary bar is skipped by the base class, so the HA recursion is seeded on the second bar's open. That matches the contract's 'seeded on the first bar the strategy sees' and only shifts the seed by one bar.

The strategy is a long-only NVDA book with up to 60% of equity invested. Much of any profit will be NVDA beta, not the HA timing rule. The analyst should compare it against a 60% NVDA buy-and-hold over the same window. Average trade return is above the USEQ spread floor (about 0.05%) on daily holds, so fees are unlikely to be the deciding factor.

Backtest Review

Sharpe
-0.15
Total return
-79.73%
Max drawdown
94.85%
Trades
338
Win rate
58.6%
Profit factor
0.63

264 closed positions, all long; trade timestamps and reported exposure are consistent with next-open execution and pyramiding.

Closed-trade PnL totals -$88,900, closely reconciling with the -88.85% account return.

Average trade return is -0.2002% versus the USEQ 0.05% floor; profit factor is 0.571 and Sharpe is -0.299.

Average losers ($1,607) exceed average winners ($877), despite a 51.1% win rate.

Drawdown is 94.85% against the registered 40% constraint.

Writing the required outbox file failed with PermissionError; this verdict was not saved.

Iteration History

Verification failed (Layer 1.5 — hypothesis/config consistency) [class=hypothesis_mismatch]: - optimization_plan fixes leverage at 1.0

Verification failed (Layer 1 — static analysis): - Import outside the sandbox allowlist: from zoneinfo — strategies may import __future__, abc, bisect, calendar, collections, copy, dataclasses, datetime, decimal, enum, fractions, functools, heapq, itertools, json, math, nautilus_trader, numpy, operator, re, src, statistics, string, typing (line 7)

Verification failed (Layer 3 — sandbox backtest): No trades produced Bar type used: NVDA.USEQ-1-DAY-LAST-EXTERNAL, Bars processed: 953 Diagnostics: should_enter() returned a side 0 times over 953 evaluated bars -> your ENTRY CONDITION never triggered. Loosen the entry logic / thresholds. Ensure your strategy produces trades with the given data and parameters.

Outcome Summary

Fixing the float-equality bug that had produced zero entries only showed that this Heikin-Ashi pyramid rule has negative net expectancy on NVDA daily bars with next-open fills. The signal-research check also found no forecasting information (IC 0.034, t=0.9), so a correct implementation does not mean an edge exists.

At the backtest review the analyst voted to abandon for negative expectancy before optimization. The review found that the code matched the contract and that the earlier zero-entry defect was fixed, but the strategy lost money net of costs, with average losses larger than average wins. The analyst saw no specific code fix worth an optimization cycle.

A port of je-suis-tm's Heikin-Ashi backtest on NVDA.USEQ daily bars (1999-2026). It bought one unit of 20% of equity when a growing bearish Heikin-Ashi candle with no upper wick followed another bearish candle, held up to 3 units, and sold everything on a bullish candle with no lower wick that followed a bullish one. It was long-only and filled at the next session's open.

The attached backtest made 338 long trades (339 entries submitted) and lost 79.73% in total, with a Sharpe of -0.146, a profit factor of 0.627, a 94.85% max drawdown and an expectancy of -$234.68 per trade, although avg_trade_return_pct was reported as +0.61%. The backtest-review analysis cites different figures from its own run: 264 closed positions, a -88.85% return, -$88,900 of realized PnL, a profit factor of 0.571, a Sharpe of -0.299 and an average trade of -0.2002%.

Analysis

Heikin-Ashi backtest

Analysis

Heikin-Ashi%20backtest.py

Omega ratio - Wikipedia

Sortino ratio - Wikipedia

Calmar ratio - Wikipedia

Backtest and paper results are hypothetical. Trading involves risk of loss.