From d1aa2c825a8c426d89521c31701371068796cf6e Mon Sep 17 00:00:00 2001 From: LiZhenhai-MBP14 <5935568+jackhai9@users.noreply.github.com> Date: Tue, 1 Sep 2026 19:56:54 +0800 Subject: [PATCH] fix: align Strategy 27 trigger boundary --- docs/binance-strategy27-events-development.md | 5 ++++- scripts/binance-strategy27-events.user.js | 4 ++-- .../core/live-event-contract.js | 2 +- src/binance-strategy27-events/index.user.js | 2 +- .../live-event-contract.test.js | 20 +++++++++++++++---- test/unit/userscript-release-contract.test.js | 2 +- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/docs/binance-strategy27-events-development.md b/docs/binance-strategy27-events-development.md index c805c0a..c491e72 100644 --- a/docs/binance-strategy27-events-development.md +++ b/docs/binance-strategy27-events-development.md @@ -40,6 +40,10 @@ Every created entity uses the second obtained by flooring the projection's update. If TradingView shifts the point to a different bar, the entity is removed and visualization stops with an alignment error. +`triggered_at_ms` is the trigger bucket's start boundary. The browser requires +`trigger_snapshot.bucket_start_ms` to equal it; the bucket end remains the +exclusive end of that same measurement interval. + The script stores only its own returned entity IDs. Route, symbol, interval, epoch, cursor, or sequence discontinuities abort the request and remove only those transient entities. Event count and age are bounded in memory and on the @@ -62,4 +66,3 @@ Live validation must confirm the current Binance main-world chart API, exact `1S` resolution, exact route symbol, successful create/readback/remove behavior, Tampermonkey source readback, and the actually loaded source after a hard reload. A source-level method name alone is not end-to-end proof. - diff --git a/scripts/binance-strategy27-events.user.js b/scripts/binance-strategy27-events.user.js index bc30ff9..55a6697 100644 --- a/scripts/binance-strategy27-events.user.js +++ b/scripts/binance-strategy27-events.user.js @@ -3,7 +3,7 @@ // @namespace binance.strategy27.events // @icon data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2064%2064%22%3E%3Crect%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2214%22%20fill%3D%22%23f0b90b%22%2F%3E%3Ctext%20x%3D%2232%22%20y%3D%2249%22%20text-anchor%3D%22middle%22%20font-family%3D%22Arial%2C%20sans-serif%22%20font-size%3D%2242%22%20font-weight%3D%22800%22%20fill%3D%22%23111827%22%3EJ%3C%2Ftext%3E%3C%2Fsvg%3E // @icon64 data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2064%2064%22%3E%3Crect%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2214%22%20fill%3D%22%23f0b90b%22%2F%3E%3Ctext%20x%3D%2232%22%20y%3D%2249%22%20text-anchor%3D%22middle%22%20font-family%3D%22Arial%2C%20sans-serif%22%20font-size%3D%2242%22%20font-weight%3D%22800%22%20fill%3D%22%23111827%22%3EJ%3C%2Ftext%3E%3C%2Fsvg%3E -// @version 0.1.0 +// @version 0.1.1 // @author jackhai9 // @description 在 Binance 一秒图表标注 VPS Strategy 27 的实时订单流事件和后续结果 // @match https://www.binance.com/*/futures/* @@ -152,7 +152,7 @@ assertSortedUniqueStrings(value.trigger_reasons, "payload.event.trigger_reasons", { nonEmpty: true }); validateSnapshot(value.trigger_snapshot, "payload.event.trigger_snapshot"); validateSnapshot(value.latest_snapshot, "payload.event.latest_snapshot"); - assertCondition(value.trigger_snapshot.bucket_end_ms === value.triggered_at_ms, "trigger snapshot must end at triggered_at_ms"); + assertCondition(value.trigger_snapshot.bucket_start_ms === value.triggered_at_ms, "trigger snapshot must start at triggered_at_ms"); if (value.event_status === "active") { assertCondition(value.active_end_at_ms === null && value.close_reason === null, "active event close fields must be null"); } else { diff --git a/src/binance-strategy27-events/core/live-event-contract.js b/src/binance-strategy27-events/core/live-event-contract.js index c7d4aa3..872bc5f 100644 --- a/src/binance-strategy27-events/core/live-event-contract.js +++ b/src/binance-strategy27-events/core/live-event-contract.js @@ -141,7 +141,7 @@ function validateEvent(value) { assertSortedUniqueStrings(value.trigger_reasons, 'payload.event.trigger_reasons', { nonEmpty: true }); validateSnapshot(value.trigger_snapshot, 'payload.event.trigger_snapshot'); validateSnapshot(value.latest_snapshot, 'payload.event.latest_snapshot'); - assertCondition(value.trigger_snapshot.bucket_end_ms === value.triggered_at_ms, 'trigger snapshot must end at triggered_at_ms'); + assertCondition(value.trigger_snapshot.bucket_start_ms === value.triggered_at_ms, 'trigger snapshot must start at triggered_at_ms'); if (value.event_status === 'active') { assertCondition(value.active_end_at_ms === null && value.close_reason === null, 'active event close fields must be null'); } else { diff --git a/src/binance-strategy27-events/index.user.js b/src/binance-strategy27-events/index.user.js index 5aeba06..b25941c 100644 --- a/src/binance-strategy27-events/index.user.js +++ b/src/binance-strategy27-events/index.user.js @@ -3,7 +3,7 @@ // @namespace binance.strategy27.events // @icon data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2064%2064%22%3E%3Crect%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2214%22%20fill%3D%22%23f0b90b%22%2F%3E%3Ctext%20x%3D%2232%22%20y%3D%2249%22%20text-anchor%3D%22middle%22%20font-family%3D%22Arial%2C%20sans-serif%22%20font-size%3D%2242%22%20font-weight%3D%22800%22%20fill%3D%22%23111827%22%3EJ%3C%2Ftext%3E%3C%2Fsvg%3E // @icon64 data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2064%2064%22%3E%3Crect%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2214%22%20fill%3D%22%23f0b90b%22%2F%3E%3Ctext%20x%3D%2232%22%20y%3D%2249%22%20text-anchor%3D%22middle%22%20font-family%3D%22Arial%2C%20sans-serif%22%20font-size%3D%2242%22%20font-weight%3D%22800%22%20fill%3D%22%23111827%22%3EJ%3C%2Ftext%3E%3C%2Fsvg%3E -// @version 0.1.0 +// @version 0.1.1 // @author jackhai9 // @description 在 Binance 一秒图表标注 VPS Strategy 27 的实时订单流事件和后续结果 // @match https://www.binance.com/*/futures/* diff --git a/test/unit/binance-strategy27-events/live-event-contract.test.js b/test/unit/binance-strategy27-events/live-event-contract.test.js index cffd4f8..ae23393 100644 --- a/test/unit/binance-strategy27-events/live-event-contract.test.js +++ b/test/unit/binance-strategy27-events/live-event-contract.test.js @@ -14,7 +14,7 @@ const epoch = '0123456789abcdef0123456789abcdef'; const eventId = 'a'.repeat(64); const lifecycleOptions = { maxEvents: 80, maxAgeMs: 2 * 60 * 60 * 1_000 }; -function snapshot({ start = 750, end = 1_000, mid = '1.25' } = {}) { +function snapshot({ start = 1_000, end = 1_250, mid = '1.25' } = {}) { return { bucket_start_ms: start, bucket_end_ms: end, @@ -153,6 +153,18 @@ test('validates live envelopes without coercing decimal strings or extra keys', }), /canonical decimal string/, ); + assert.throws( + () => validateLiveEnvelope({ + ...opened, + payload: { + event: { + ...opened.payload.event, + trigger_snapshot: snapshot({ start: 750, end: 1_000 }), + }, + }, + }), + /trigger snapshot must start at triggered_at_ms/, + ); }); test('tracks exact sequence and event lifecycle while allowing reset rehydration', () => { @@ -215,7 +227,7 @@ test('tracks exact sequence and event lifecycle while allowing reset rehydration test('rejects unknown lifecycle transitions without a reset', () => { const lifecycle = new LiveEventLifecycle('BTR/USDT:USDT', lifecycleOptions); lifecycle.apply(envelope()); - const lateSnapshot = snapshot({ start: 2_750, end: 3_000 }); + const lateSnapshot = snapshot({ start: 3_000, end: 3_250 }); const lateEvent = { ...event(), triggered_at_ms: 3_000, @@ -228,7 +240,7 @@ test('rejects unknown lifecycle transitions without a reset', () => { id: 'b'.repeat(64), kind: 'event_updated', payload: { event: lateEvent }, - eventTime: 3_000, + eventTime: 3_250, })), /unknown event/, ); @@ -268,7 +280,7 @@ test('bounds retained lifecycle events and reports exact count and age evictions id: eventB, sequence: 4, kind: 'event_updated', - eventTime: 1_000, + eventTime: 1_250, })); assert.equal(ignored.type, 'event_evicted'); assert.equal(lifecycle.size, 0); diff --git a/test/unit/userscript-release-contract.test.js b/test/unit/userscript-release-contract.test.js index fb03cfc..96aed57 100644 --- a/test/unit/userscript-release-contract.test.js +++ b/test/unit/userscript-release-contract.test.js @@ -37,7 +37,7 @@ test('release contract identifies the generated Strategy 27 annotation artifact' assert.equal(contract.name, '【自写】Binance Strategy 27 事件标注'); assert.equal(contract.namespace, 'binance.strategy27.events'); - assert.equal(contract.version, '0.1.0'); + assert.equal(contract.version, '0.1.1'); assert.equal(contract.runAt, 'document-idle'); assert.equal(contract.updateURL, contract.downloadURL); assert.deepEqual(contract.matches, [