Skip to content
All strategies

Strategies

Myquant SkyPark: DOT First-Minute Range Breakout with Literal Direction Flags

Outcome: Abandoned

MyquantSkyParkDotFirstMinuteBreakout

Outcome Summary

The Research Lead adapted Myquant's SkyPark first-minute range breakout to DOT perpetuals with UTC daily sessions, keeping the source's literal flag comparisons. After 5 iterations, including a fix that fell back to bar closes when tick data was missing so the full history was evaluated, the backtest produced 2,036 short-only trades. They returned -97.3% with Sharpe -1.83, losing even while DOT fell heavily. The analyst abandoned it at backtest review for negative expectancy, noting that the verdict rejects only the DOT/UTC adaptation, not the original strategy on session-based futures.

Hypothesis

Derived by the Research Lead from "SkyPark" by Myquant (myquant) https://github.com/myquant/strategy/blob/17595e6bf4a118e1fa87c90bfb0fd78afa69a60b/SkyPark/python/SkyPark.py: Preserved Myquant's literal signal and exit rules; completed unavailable INI settings and adapted instrument, daily initialization, sizing and execution. Distilled translation of SkyPark by Myquant, Apache-2.0, from the supplied commit. Preserve the executable comparisons, first-minute high/low boundaries, flat-only entry and timed……Show moreShow less

Derived by the Research Lead from "SkyPark" by Myquant (myquant) https://github.com/myquant/strategy/blob/17595e6bf4a118e1fa87c90bfb0fd78afa69a60b/SkyPark/python/SkyPark.py: Preserved Myquant's literal signal and exit rules; completed unavailable INI settings and adapted instrument, daily initialization, sizing and execution. Distilled translation of SkyPark by Myquant, Apache-2.0, from the supplied commit. Preserve the executable comparisons, first-minute high/low boundaries, flat-only entry and timed liquidation. The Python file specifies OPEN_VOL=5 but reads the instrument, clock times and gap thresholds from SkyPark.ini without defaults. That companion file could not be retrieved; the values below are explicitly platform-selected completions, not claimed source defaults. Use DOTUSDT.BINANCE one-minute bars and trade ticks, UTC daily research sessions, a first-minute interval of 00:00–00:01 and an exit threshold of 23:55. Replace five source contracts with capital-relative exposure. Reinitialize once per UTC day, corresponding to restarting the source for each trading day; the original code itself has no daily reset. Decisions remain bar-triggered, using the latest observed trade price. Market orders execute at the decision bar's close. No price stop or profit target is added. The requested outbox file could not be written because this workspace is read-only.

The supplied concrete strategy merits testing independently of historical performance. Long and short eligibility reduces the portfolio's directional concentration. Binance is selected despite its overrepresentation because it supplies long-history one-minute bars and trade ticks needed to preserve this source's event semantics; Hyperliquid's short minute history would materially weaken validation. Corpus concentration is empty, so DOT's individual research share cannot be verified. Options or a cross-venue hedge would change the submitted mechanism.

negative_expectancy: over 2,036 closed trades from 2020-08 to 2026-09 (full history now evaluated after the tick-fallback fix), the Research Lead's DOT operationalization of Myquant's SkyPark loses money after costs. avg_trade_return_pct is -0.69% against a 0.15% BINANCE futures floor. Profit factor is 0.66, Sharpe -1.83 (95% CI -2.63 to -1.08), total return -97.3% and max drawdown 97.5% against a pre-registered 15% limit. Every calendar year is negative. Fees are only 6.7% of gross, so the gross edge itself is……Show moreShow less

negative_expectancy: over 2,036 closed trades from 2020-08 to 2026-09 (full history now evaluated after the tick-fallback fix), the Research Lead's DOT operationalization of Myquant's SkyPark loses money after costs. avg_trade_return_pct is -0.69% against a 0.15% BINANCE futures floor. Profit factor is 0.66, Sharpe -1.83 (95% CI -2.63 to -1.08), total return -97.3% and max drawdown 97.5% against a pre-registered 15% limit. Every calendar year is negative. Fees are only 6.7% of gross, so the gross edge itself is negative (signal IC -0.019). DOT buy-and-hold fell heavily over the window, yet this short-only book still lost. Structural note: on a 24/7 market the source's overnight-gap flags carry no information, because pre_close is the previous minute's close. The long flag never fired (0 long trades), and the literal short branch fires almost daily. Optimization has nothing to work with: tunable is empty and all values are frozen. This verdict rejects only the DOT/UTC-session adaptation, not Myquant's original strategy on session-based futures with real overnight gaps. That variant cannot be tested here without such a venue.

Implementation

Literal SkyPark first-minute range breakout on DOT futures, with UTC daily reinitialization, flat-only entry, 10% equity exposure and liquidation after the 23:55 bar-start threshold. Available trade ticks take priority; absent ticks use the one-minute LAST close.

Applied only the requested missing-tick repair to previous_code: decision_price now uses bar.close.as_double() when tick_ready is false, and should_enter no longer requires tick_ready. The LAST close represents the latest observed trade price at the bar decision. Direction-flag comparisons, first-minute upr/dwn, flat-only entry, late-entry liquidation, sizing and every fixed contract value remain unchanged. No library filters, stops or alternative triggers were added. Full-history evaluation is required before judging the mechanism.

Verification Results

The smoke window produced 10 closed short positions with -2.351% average net trade return and PF 0.166. Full-history evaluation should assess the literal short-direction rule and modeled impact; these losses do not establish a correctness defect.

Backtest Review

Sharpe
-1.83
Total return
-97.29%
Max drawdown
97.51%
Trades
2036
Win rate
43.8%
Profit factor
0.66

The iteration-2/4 fix took effect. Bars without ticks now fall back to bar.close, so the full 2020-08 to 2026-09 history is evaluated: 2,036 entries across every year, and the input audit and accounting are valid.

The sample is large and reliable (2,036 closed trades, metrics_reliable=true, end_unrealized_pct=0).

All 2,036 trades are SHORT and long_trades=0. On 24/7 DOT perps, pre_close is the 23:59 one-minute close. The 00:00 open therefore never gaps more than 1% above it, so open_long_flag never fires. The source's literal short branch (first_open > pre_close*(1-0.01)) is true almost every day, so the strategy effectively becomes 'short a break of the first minute's low, hold to 23:55' nearly every day.

Negative expectancy on a large sample: avg_trade_return_pct is -0.69% against a +0.15% BINANCE futures floor. PF is 0.66, win rate 0.44 with avg loss larger than avg win, and Sharpe -1.83 (CI -2.63 to -1.08).

Total return is -97.3% with a 97.5% max drawdown, against a pre-registered 15% limit. Every calendar year is negative (2020 through 2026), and Sharpe is negative in all three volatility regimes.

Costs are not the cause. Commission is only 6.7% of gross, and the gross per-trade edge is already negative (signal IC -0.019, t=-0.89, rejected_no_information).

Buy-and-hold comparison: correlation with DOT is -0.61 and beta -0.17. The strategy lost money even though DOT fell heavily over the window, which a short-biased book should have benefited from. Alpha is -0.50 and information ratio -0.66.

The QA edge concern (shorts losing heavily in the smoke window, PF 0.166) is confirmed by the full-history result.

Analysis

Iteration 4 reproduced iteration 2 because the code fix requested in iteration 2 was NOT applied. Apply it now. This change alters trading; a third identical run will abandon the run. DEFECT: should_enter() returns None whenever tick_ready is False. DOTUSDT trade ticks exist only from 2026-04-29 (input_audit first_ns 1777420801092000000). entry_diag shows 190,080 tick-covered bars against 2,989,019 missing. Entries were therefore blocked from 2020-08 to 2026-04, so all 22 trades fall in about 130 days of 2026.……Show moreShow less

Iteration 4 reproduced iteration 2 because the code fix requested in iteration 2 was NOT applied. Apply it now. This change alters trading; a third identical run will abandon the run. DEFECT: should_enter() returns None whenever tick_ready is False. DOTUSDT trade ticks exist only from 2026-04-29 (input_audit first_ns 1777420801092000000). entry_diag shows 190,080 tick-covered bars against 2,989,019 missing. Entries were therefore blocked from 2020-08 to 2026-04, so all 22 trades fall in about 130 days of 2026. That covers about 6% of the 2,207-day history. The rule was never evaluated on the full history. FIX (code only; no fixed contract value changes): 1. In calculate_signal, set self.decision_price = self.last_trade_price if self.tick_ready else bar.close.as_double(). On a 1-minute LAST bar, the bar close IS the last trade at or before the decision time, so this is the same 'latest observed trade price', not a substitution. 2. In should_enter, remove the `not self.tick_ready` condition. Keep `not self.levels_ready`. 3. position_size already uses decision_price, so it will then work without ticks. 4. Keep subscribe_trade_ticks (fixed). Ticks still take priority when present. Expected result: entry_signaled_2020..2025 should be non-zero, with roughly one entry per day on most days. DO NOT CHANGE (literal source semantics, keep as-is): the open_long/open_short flag comparisons, first-minute upr/dwn, flat-only entry and the 23:55 timed exit. NOTE FOR LATER REVIEW: pre_close is the 23:59 one-minute bar close, so first_open vs pre_close almost never gaps by 1%. The short branch's literal '>' therefore sets open_short_flag on nearly every day, and long_trades = 0. That is the literal source behaviour under the platform's UTC/1-minute completion and will be judged on the full-history result. Do not alter it in this iteration. ## Library refinements (from the knowledge library; test them, do not assume them) The library holds the SkyPark source itself, which confirms that entries are decided in on_bar against the latest price. So the analyst's fix (use the bar close when no tick exists) is faithful to the source and must come first. After a full-history run, the library's opening-range material points to three changes: a stop at the opposite edge of the opening range, a wider opening window than one minute, and a range-based trigger in place of the gap flags, which never fire correctly on 24/7 crypto. It also warns that opening-range breakouts often fail to beat costs. 1. [entry] Evaluate entries on full history (drop the tick gate): In calculate_signal set decision_price = last_trade_price if tick_ready else bar.close.as_double(). In should_enter, remove the `not self.tick_ready` condition and keep `not self.levels_ready`. Keep subscribe_trade_ticks so ticks still take priority when they exist. No parameter changes. — In the source, on_tick only stores tick.last_price into self.close. Every decision ('if self.open_short_flag and self.close < self.dwn and 0 == self.hoding') is made in on_bar against the latest price. On a 1-minute LAST bar, the bar close is that latest price. The tick gate is a translation artefact: it limited all 22 trades to about 130 days of 2026, roughly 6% of the history, so the current metrics (Sharpe -0.80, avg trade -1.43%) say nothing about the rule. (source: SkyPark (Myquant) p.1) 2. [stop] Stop at the opposite edge of the opening range: After the full-history run, add a stop in should_exit. Short: exit on a bar close > upr. Long: exit on a bar close < dwn. Then move the stop to break-even once the open profit reaches 0.5%. Keep the 23:55 timed exit and the one-entry-per-day rule. — The QuantInsti opening-range project sets 'First SL ... at the low/high of the first candlestick', then a 'Trade Insurance' move to break-even after a favourable move. SkyPark holds a losing breakout for up to 24 hours with no stop. In the partial run the profit factor was 0.28 and the win rate 36%, so losers are far larger than winners, which is the failure this stop targets. (source: Intraday Low-Frequency Trading Strategy (QuantInsti EPAT project) p.1) 3. [parameter] Widen the opening range from 1 minute to 30 minutes: Set first_kline_time=00:00:00 and first_kline_time1=00:30:00. upr/dwn become the high and low of all 1-minute bars in 00:00–00:30 UTC, and no entries are allowed before 00:30. — The FMZ primer describes 开盘突破策略 as built on 开盘半小时 (the first half hour), which 'often decides the day's direction'. The QuantInsti ORB uses a 5-minute first candle. A one-minute DOT range is a few basis points wide, so almost any noise triggers a breakout. The trade then has to cover the ~0.10% round-trip fee from a near-random entry. (source: 发明者量化交易入门--从基础到实战 p.1; Intraday Low-Frequency Trading Strategy (QuantInsti EPAT project) p.1) 4. [entry] Replace the gap flags with a Dual Thrust range trigger: In a later iteration (once the literal version has been judged), keep the 00:00 UTC open as the anchor. Range = max(HH-LC, HC-LL) over the prior N=2 UTC days. Go long when the price is above open + 0.5*Range, short when it is below open - 0.5*Range, one entry per day, flat at 23:55. — On a 24/7 market, pre_close (the 23:59 close) is almost the 00:00 open, so a 1% gap almost never happens. The literal '>' in the short branch then flags a short nearly every day, and long_trades is 0. The Dual Thrust article describes the same opening-range-breakout idea (开盘区间突破) with a volatility trigger, K1=K2=0.5 in its demo. It notes the system does better in trends and gives false signals in choppy markets. (source: 用Python实现一个Dual Thrust数字货币量化交易策略 p.1) 5. [cost] Require a minimum range width so the move can cover fees: Only arm entries on days when the opening range is wide enough: (upr - dwn) / dwn >= 0.30%, which is 3x the 0.10% round-trip cost. Judge the result by the per-year breakdown in entry_diag, and require a positive net return in at least 4 of the 6 years. — A systematic falsification study of intraday OHLCV momentum on MNQ futures found that most signal families' gross return was below the friction floor. The Opening Range Breakout long cleared costs but failed significance and year-stability (T = 0.88, year-unstable). Tiny opening ranges on DOT produce exactly those small gross moves. Filtering them out and checking stability by year targets both failure modes. (source: Structural Limits of OHLCV-Based Intraday Momentum Signals in MNQ Futures: A Systematic Falsification Study p.1)

Iteration History

Verification failed (Layer 4 — QA review) [class=hypothesis_mismatch]: - [edge_concern] The sandbox's 10 closed trades have negative net expectancy. Full-history evaluation should assess the literal short-direction rule after correctness is repaired; these losses do not determine this verdict. - [critical] should_enter() suppresses qualifying entries after end_time and clears both direction flags. The hypothesis explicitly preserves the source's literal entry and timed-liquidation rules. Source lines 85–103 check……Show moreShow less

Verification failed (Layer 4 — QA review) [class=hypothesis_mismatch]: - [edge_concern] The sandbox's 10 closed trades have negative net expectancy. Full-history evaluation should assess the literal short-direction rule after correctness is repaired; these losses do not determine this verdict. - [critical] should_enter() suppresses qualifying entries after end_time and clears both direction flags. The hypothesis explicitly preserves the source's literal entry and timed-liquidation rules. Source lines 85–103 check flat-only entry before timed liquidation, allowing a first qualifying breakout after the deadline to open and close in the same callback. Removing that round trip changes trades and costs; it is an additional rule absent from the hypothesis. (line 127) — fix: Preserve entry-before-liquidation sequencing, including a qualifying late entry's immediate close. Because FactoryStrategy dispatches either entry or exit per bar, implement the immediate close through an appropriate fill lifecycle hook without leaving the late position open until another bar. - [warning] When no eligible trade tick has been observed, calculate_signal() substitutes the bar close. The hypothesis calls for the latest observed trade price, so a bar-only replay can silently test a different signal input. Subscription flags are correctly enabled, but the supplied sandbox does not establish tick coverage. (line 107) — fix: Require an observed, point-in-time trade tick before evaluating entry conditions, and verify tick coverage for the tested window.

Preserve entry-before-liquidation sequencing, including a qualifying late entry's immediate close. Because FactoryStrategy dispatches either entry or exit per bar, implement the immediate close through an appropriate fill lifecycle hook without leaving the late position open until another bar.

should_enter() suppresses qualifying entries after end_time and clears both direction flags. The hypothesis explicitly preserves the source's literal entry and timed-liquidation rules. Source lines 85–103 check flat-only entry before timed liquidation, allowing a first qualifying breakout after the deadline to open and close in the same callback. Removing that round trip changes trades and costs; it is an additional rule absent from the hypothesis.

Require an observed, point-in-time trade tick before evaluating entry conditions, and verify tick coverage for the tested window.

When no eligible trade tick has been observed, calculate_signal() substitutes the bar close. The hypothesis calls for the latest observed trade price, so a bar-only replay can silently test a different signal input. Subscription flags are correctly enabled, but the supplied sandbox does not establish tick coverage.

Validate DOT trade-tick coverage independently over the evaluated window and report tick age or missing-feed intervals. Preserve the specified latest-observed-price rule rather than substituting bar closes.

tick_ready remains true indefinitely after any historical tick arrives. Consequently, tick_covered_bars measures availability of a prior tick rather than actual tick-feed coverage; missing days could silently reuse an old decision price. The brief does not establish that such gaps occurred.

Track the previous bar timestamp and validate midnight continuity, or obtain the completed previous UTC daily close through a point-in-time-safe mechanism.

Daily pre_close uses the last processed minute close without checking its timestamp. If bars immediately before midnight are missing, this may differ from the intended previous daily close.

Verification failed (Layer 4 — QA review) [class=hypothesis_mismatch]: - [edge_concern] The truncated sandbox reports 10 closed positions with -2.35% average net notional return. This is advisory; full-history performance remains unmeasured. - [critical] The hypothesis explicitly requires bar-triggered decisions using the latest observed trade price. calculate_signal instead assigns price = close_px and deliberately prevents subscribed trade ticks from altering decisions. Consequently, should_enter compares the completed bar close against the first-minute boundaries even when the latest observed tick differs. This substitutes the specified signal input; the developer's coverage-repair rationale does not revise the hypothesis. (line 103) — fix: Use the latest observed trade price available at or before the decision timestamp, with explicit handling of missing tick coverage. Alternatively, obtain a revised hypothesis explicitly authorizing completed-bar-close signals before reviewing this implementation under that specification.

Use the latest observed trade price available at or before the decision timestamp, with explicit handling of missing tick coverage. Alternatively, obtain a revised hypothesis explicitly authorizing completed-bar-close signals before reviewing this implementation under that specification.

The hypothesis explicitly requires bar-triggered decisions using the latest observed trade price. calculate_signal instead assigns price = close_px and deliberately prevents subscribed trade ticks from altering decisions. Consequently, should_enter compares the completed bar close against the first-minute boundaries even when the latest observed tick differs. This substitutes the specified signal input; the developer's coverage-repair rationale does not revise the hypothesis.

Outcome Summary

Overnight-gap signals from session-based futures carry no information on a 24/7 market where the 'previous close' is just the prior minute, so they should be redefined or tested only on venues with real session gaps.

The backtest-review verdict was abandon for negative expectancy: avg_trade_return_pct of -0.69% is far below the 0.15% BINANCE futures floor. The gross edge was already negative (fees were only 6.7% of gross, signal IC -0.019), and with no tunable parameters there was nothing for optimization to improve.

A translation of Myquant's SkyPark strategy to DOTUSDT.BINANCE 1-minute bars: set long/short flags from the first UTC minute's open versus the previous close, enter on a break of that first minute's high or low, and close out at 23:55 UTC.

Over 2,036 closed trades (all short, 0 long) from 2020-08 to 2026-09, it returned -97.3% with a 97.5% max drawdown, Sharpe -1.83 (95% CI -2.63 to -1.08), profit factor 0.66, win rate 0.44 and avg_trade_return_pct -0.69%. Every calendar year lost money.

Analysis

SkyPark

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