Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion docs/binance-orderbook-trade-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,44 @@ The chart alert is timeframe-agnostic and evaluates closed bars only. It support

A bearish setup requires the Bollinger middle line to cross down through SMA60 while the band center is declining. A bullish setup is generated by the exact price-axis mirror: OHLC values are transformed as `open=-open`, `high=-low`, `low=-high`, `close=-close`, and the indicator axes are transformed as `middle=-middle`, `upper=-lower`, `lower=-upper`, `ma60=-ma60`. This yields a middle-line cross up through SMA60, pre-cross closes in the middle/upper channel, and post-cross middle-line support without maintaining a second drifting detector. Both directions use the same warning/confirmation/reversal lifecycle. Bearish warning dots are red and remain above the candle high; bullish warning dots are green and remain below the candle low. Bearish confirmation is a red down arrow and bullish confirmation is a green up arrow. A reversal uses the opposite colored/directional arrow. Mirrored marker prices remain on the corresponding side of the candle (bullish confirmation below the low, bearish confirmation above the high). If overlapping setups in one direction reverse on the same candle, the newest setup owns that direction's visual reversal; opposite-direction signals retain distinct IDs and are both rendered.

The current Binance `trading-platform-30` chart runtime exposes `exportData()` as row-major numeric-keyed OHLC objects. The parser deliberately enforces that observed contract and fails if the schema changes. Every asynchronous export and marker creation revalidates the active chart instance, route symbol, and resolution so a stale task cannot annotate a newly selected chart.
The current Binance `trading-platform-30` chart runtime exposes `exportData()` as row-major numeric-keyed OHLC objects. The parser deliberately enforces that observed contract and fails if the schema changes. It validates the entire export before filtering closed bars: intraday timestamps must lie on the UTC interval grid, D/W timestamps must be at UTC midnight, weekly timestamps must be Mondays, and positive timestamp deltas must be multiples of the bar duration. Missing bars remain valid; multi-day and multi-week feeds do not have to share the Unix epoch's phase. Off-grid or incompatible-spacing snapshots are recoverable and never reach detection.

`dataReady()` alone is insufficient: Binance's chart implementation checks whether data is nonempty, not whether an interval switch has completed. A chart-owned interval session subscribes to `onIntervalChanged()` and `onDataLoaded()`. An interval change increments a revision and blocks exports until data completion; callbacks never export or mutate drawings. Every asynchronous export and marker creation revalidates the session identity/revision as well as chart instance, route symbol, and resolution. This rejects stale work even after a rapid `1S -> 1 -> 1S` switch. Stop, page hiding, teardown, and chart replacement dispose the session independently of deferred drawing removal.

Each interval session uses a private subscription owner token. The observed Binance chart integration calls `unsubscribeAll(null)` on both data-loaded and interval-changed channels when binding its own callbacks. Sharing the null owner lets that native initialization silently remove our callbacks, leaving a running monitor stuck waiting for data that has already arrived. Cleanup uses the same private token and exact callbacks; it never clears native or other-script subscriptions.

The exposed chart API can exist before its internal model during initial loading. Target discovery and current-target validation use the observed Trading Platform 30 `hasModel()` contract before reading `resolution()`. A missing model is an expected not-ready state, not a fatal error; the existing poll resumes when the model exists. Model readiness does not replace the interval/data session guard.

Indicator calculation traverses each fixed window directly instead of allocating sliced/mapped close arrays for every bar. Summation order is preserved exactly, including population variance, to avoid changing threshold decisions through floating-point drift. Stable marker audits read each shape handle once while retaining the full point/property checks. The asynchronous render loop yields a browser task after 32 signals or 8 ms of batch work; each resumed batch refreshes native shape ownership and revalidates generation, chart session, and drawing-mutation ownership. This is a cooperative budget checked between native calls, not a hard limit on an individual native API call. It adds no recurring timer and does not reduce history coverage or audit frequency. Context cleanup and obsolete-marker deletion remain synchronous; host chart loading/rendering and those removal phases are not covered by the batch budget.

`window.__TM_CLOSE_LONG_DEBUG__.bollingerAlertState` is an on-demand diagnostic snapshot of timer/task presence, context/session readiness, cached/rendered signal counts, and boolean drawing-mutation owners. It contains no order details, does not export candles or audit drawings, and adds no periodic work. Native model/data readiness is reported separately from session readiness so a waiting session is not mistaken for expensive calculation or a zero-signal window.

Alert markers use TradingView's drawing API. Every detected signal in the loaded window is rendered; there is no recent-signal truncation. Each direction allows up to 1,000 simultaneous signals, for a shared maximum of 2,000, and an over-limit window is rejected before any partial marker mutation. Marker ownership is tracked by signal ID, but the live shape list remains authoritative: externally evicted marker IDs are discarded from the registry and recreated without removing or changing foreign drawings. A typed OHLC/time-order snapshot race is treated as recoverable: existing markers and cached signals remain in place and the next poll retries. Schema, nonnumeric data, chart API, band-width, and time-alignment contract failures remain fail-closed and clear the alert layer. Current live evidence shows that removing even a `disableSave` marker emits `drawing_event` and `saveChart`, so alert reconciliation pauses during every existing order-line drawing/save owner. Symbol changes, non-trading routes, hidden documents, and page teardown stop or clear the alert lifecycle.

Existing owned markers are also checked for timestamp, resolved price, current signal price/type/direction, native shape name, color/icon and interval visibility. A changed marker is recreated; an unchanged marker is not rewritten. The price read back at creation is retained separately from the detector's requested price to tolerate host price normalization without perpetual recreation. Native `intervalsVisibilities` overrides restrict each marker to its originating interval bucket, so `1S` drawings cannot appear on a minute chart while physical cleanup is blocked. TradingView groups 60+ minute resolutions into integer-hour buckets: this matches Binance's standard hour intervals but does not provide distinct native visibility for nonstandard intervals such as 60 and 90 minutes. The session revision still invalidates computation on every interval change. Unsupported `fixedSize` overrides are not sent to arrow drawings; the current live arrow API does not expose that property.

Retiring a context always invalidates it immediately. Its layer remains in a cleanup set until owned markers and outstanding asynchronous creations have finished; late creations are owned before checking currentness. No late callback removes a drawing while a trade/save owner is busy. The existing poll drains retired layers when safe, without deleting user or other-script drawings or making additional market requests.

### Marker Save Bursts

The observed Binance Trading Platform 30 integration schedules a full `widget.save` 100 ms after every non-click/non-move drawing event. `disableSave` excludes temporary markers from the saved JSON but does not suppress these events. A native CPU profile of timeframe switching attributed the main scripting hotspot to repeated chart serialization, especially unchanged parallel-channel properties; marker audits and indicator detection were not the dominant sampled branch.

`core/chart-marker-save-controller.js` installs one stable base `saveChart` wrapper per API in a WeakMap. Only actual Bollinger marker creation, publication and deletion arm its burst; unchanged audits do not. Default callback saves during that burst share one complete serialization after 150 ms of quiet, capped at 1,000 ms per burst. Every pending callback receives a separate JSON snapshot, including all saveable user drawings. Callback failures are reported at a separate asynchronous job boundary, without skipping later callbacks or interrupting an unrelated explicit save. No drawings are deleted or excluded to accelerate serialization.

This is deliberately not a fully transparent public-API replacement: default `saveChart(callback)` callers during a marker burst receive a deferred callback and no synchronous callback return value. Binance's observed autosave caller does not use that return value, but a third-party default caller in the same window has this limitation. Idle calls, explicit options (including `includeDrawings: false`), unusual arguments and foreign receivers remain synchronous. An outer order-save wrapper remains authoritative; the base never restores over it. This optimization does not cover independent Strategy 27 or manual-drawing bursts outside a Bollinger mutation window.

Before starting a continuous-order save owner, toggling order-line visibility, or opening the native cancellation confirmation, the workflow drains pending marker mutations and their delayed-save tail. Asynchronous native creations remain counted through completion, including stale hidden results. An independent 150 ms mutation tail survives an explicit save interrupting the burst. Draining blocks new marker mutations, has a 2,000 ms timeout that refuses the chart workflow before its next action, and supports immediate continuous-task abort. The cancellation confirmation callback remains synchronous; the script does not delay, repeat or confirm a financial click. `clear()` remains synchronous and save timers settle afterward. The controller has no idle recurring timer or retained serialized snapshot; on-demand `bollingerAlertState.markerSaveStats` exposes aggregate counts only.

Native asynchronous shape creation can automatically enable the interval active when it resolves. Markers therefore start with `visible: false`. Only a current session with drawing mutation ownership may synchronously publish the marker using `setProperties`, restoring its originating interval mask and setting `visible: true`; properties are read back before registration. Stale or busy results remain hidden until safe cleanup. Native visibility normalizes second resolutions of at least 60 seconds into integer-minute buckets as well.

## Vertical Depth Profile

The optional depth profile is a userscript-owned canvas beside the TradingView price axis. It does not clone Binance's native Depth React component. A feature-gated adapter reads the active TradingView main pane height and maps each depth price through the pane's current price scale, including logarithmic and inverted modes. The divider uses Binance's latest visible trade price rather than the order-book midpoint so it follows TradingView's live-price line. If that contract is unavailable or invalid, the profile fails closed instead of falling back to an approximate scale. Binance Basic and native Depth modes do not expose the verified coordinate contract, so the profile is hidden in those modes.

The geometry adapter checks the native `hasModel()` contract before reading pane heights or panes. A missing readiness API or not-yet-created model returns `null` through the existing not-ready geometry lifecycle, avoiding first-refresh `Value is null` errors without catching pane errors or adding delays.

Every synchronous depth render creates a fresh geometry snapshot. Its 13-step inverse coordinate search memoizes native coordinate-to-price samples by the exact numeric coordinate, shared across that render's levels and initial validation samples. Search branches, precision, pixel-row ownership and cumulative quantities are unchanged. No sample survives into the next geometry, so zoom, logarithmic/inverted mode, and native scale changes are resampled. This removes repeated native calls at shared search-tree nodes without adding timers or caching depth prices/results. Invalid initial samples still reject the geometry; invalid intermediate search samples still throw.

The userscript installs `core/binance-native-depth-source.js` at `document-start` and passively observes the native `/fapi/v1/rpiDepth?limit=1000` response and `{symbol}@rpiDepth@500ms` messages. It preserves Binance's original `fetch` result and WebSocket instances and never opens a second depth connection. `core/depth-profile-book.js` applies the observed `lastUpdateId`, `U`, `u`, and `pu` sequence contract and treats quantities as absolute values; zero removes a price level. The profile keeps every active price accumulated from the native stream and defers pixel-row aggregation to rendering, so distant levels are not discarded by the snapshot limit. Rendering remains sparse: a row is painted only when at least one real price level maps to that chart pixel. `core/depth-profile-session.js` only subscribes the active symbol to that page-owned source. A sequence gap waits for Binance's native resynchronization instead of issuing a userscript-owned retry request, while a changed private RPI contract fails the profile explicitly without blocking Binance's own request.

The session must stop and invalidate old work on symbol change, non-trading routes, hidden documents, and `pagehide`. The overlay canvas uses `pointer-events: none`; only its compact collapse control may receive pointer input. Do not connect this visualization book to ladder pricing or any trading decision.
Expand Down
Loading
Loading