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
48 changes: 22 additions & 26 deletions docs/binance-strategy27-events-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,21 @@ V10 live projection. The VPS remains the only market-data and event-analysis
authority. The userscript opens no Binance market-data WebSocket, uses no
Binance API key, and does not recalculate the four force groups.

Version 0.5.0 uses the existing installation as the CorsairQuant signal client.
Strategy29 remote ownership starts only after the local-only Strategy29 companion
publishes a valid page-scoped readiness record. Without it, the host reports
`waiting_for_companion` and leaves any legacy Strategy29 remote owner untouched.
This handshake is separate from one-time preference migration, so later reloads
continue to use the host's saved choices. Update both existing installations and
reload to complete a staged upgrade.
The historical installed script name is retained to preserve update identity.
Its private gateway URL and secret are shared by the Strategy27 consumers and
the independently controlled Strategy29 remote summary. The installation namespace
and update URLs remain unchanged; update the existing installation in place.
The retained `strategy27GatewayOrigin` and `strategy27GatewayAuthSecret` storage
keys are the single credential source, avoiding a second setup during migration.
The standalone Strategy29 script continues to own its local detector and has no
gateway request or configuration permissions. New remote modules must receive
the host's private settings adapter rather than creating another credential store.
Version 0.6.0 retains this installation's private gateway configuration and
provides a shared read-only transport. Strategy29 owns its own summary panel,
lifecycle and panel position. The existing `strategy27GatewayOrigin` and
`strategy27GatewayAuthSecret` storage keys remain the single credential source;
no second setup or credential transfer is required. Installation identity and
update URLs remain unchanged. A one-time nonsecret coordinate handoff preserves
the last Strategy29 panel position saved by the former host; this does not give
Strategy27 ownership of the new panel. Invalid old coordinates are reported to
Strategy29 without preventing the shared provider or Strategy27 from starting.

The page-visible `jh-userscripts.signal-gateway` capability exposes only fixed
Strategy29 status and event reads. It is intentionally a public-data capability:
page code can request the allowed observation data, but cannot obtain the secret,
select another origin, set request headers or send a write. Future strategy
routes require an explicit allowlist extension and contract review.

The script reads an authenticated, loopback-only long-poll endpoint through an
SSH local forward. It draws transient entities only when the Binance route,
Expand All @@ -47,17 +46,14 @@ symbol.

The secret prompt is captured from the Tampermonkey sandbox before page code can
replace the page prompt. No credential is passed through page globals, events,
localStorage or diagnostics. Strategy29's only public migration record contains
enabled state and panel coordinates, validates an exact schema and is copied once
without overwriting destination preferences. Its summary follows the route symbol
on any chart interval and remains visible while locally disabled. The host's
existing context timer samples it; visibility and pagehide abort pending summary
requests without resetting the retained cursor. A summary failure is contained at
the module boundary and does not stop the independent Strategy27 consumers.
localStorage or diagnostics. Strategy29 owns its default-on summary on every
chart interval. Its independent lifecycle handles visibility, cursor recovery and
panel persistence. The shared provider bounds requests to four in flight and a
10-second deadline, rejects redirects and restricts response size. Reconfiguring
the gateway invalidates pending reads before consumers resume.

Use the existing forward for `/v1/strategy29/status` and `/v1/strategy29/events`.
The unified gateway's disabled module state is intentional and does not require
another secret. Server monitoring and notification activation remain separate.
Server monitoring and dedicated notification delivery remain separate controls.

The macOS operator machine keeps this forward under a `launchd` user agent so
the SSH process is restarted after sleep, network changes, or a broken
Expand Down Expand Up @@ -169,7 +165,7 @@ ADR 032 in CorsairQuant owns the server-side rule and transport contract. The
browser does not reconstruct candidates from ordinary events or recalculate
market evidence. The client, lifecycle, panel, native chart layer and optional-job
controller are wired into the entrypoint and tested together. The source and
generated install artifact are version 0.5.1 with identical metadata headers.
generated install artifact are version 0.6.0 with identical metadata headers.
The generated artifact passes syntax, release-contract and isolated execution
checks, including candidate delivery, paired entities, clear and context stop.
Binance operator-page validation remains outstanding. Server/gateway rollout
Expand Down
74 changes: 40 additions & 34 deletions docs/binance-strategy29-bollinger-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
## Scope and Installation

The standalone `binance-strategy29-bollinger.user.js` owns the local
Bollinger/SMA60 observer. The CorsairQuant signal client, delivered through the
existing Strategy27 installation, owns the read-only Strategy29 server summary.
Bollinger/SMA60 observer and its read-only server summary. The existing Strategy27
installation supplies only shared authenticated transport.
Source is `src/binance-strategy29-bollinger/`. The local observer reads only
already-loaded native chart candles. The summary reads the authenticated
unified loopback gateway; it does not call Binance market-data or account APIs,
submit orders, rotate hidden charts, or add remote events as chart drawings.

Install Strategy29 0.4.0 with orderbook 2.7.199 or later, or use it alone.
Install CorsairQuant signal client 0.5.0 for the remote summary.
Install Strategy29 0.5.0 with orderbook 2.7.199 or later, or use it alone.
Install CorsairQuant signal client 0.6.0 for the remote summary.
Do not combine it with the embedded observer in orderbook 2.7.198.
After updating/disabling the old script, reload the page. An embedded observer
is an explicit conflict: Strategy29 stops and displays an upgrade/reload notice.
Expand Down Expand Up @@ -64,32 +64,33 @@ The summary follows the existing pathname locale contract: `/zh-CN/` uses Chines
while English and other routes use English. Strategy29 imports the existing pure
orderbook locale helpers without modifying orderbook or shared runtime behavior.
Headers, connection and selection states, signal names, processing labels,
notification totals, empty states, menus and prompts have bilingual copy. Technical
notification totals and empty states have bilingual copy. Technical
identifiers and arbitrary server diagnostic details remain verbatim, with localized
labels. Chart arrows contain no textual labels.

A same-symbol locale change rerenders retained status, events and connection text
without retiring requests, rebuilding the client or resetting its cursor. Existing
Tampermonkey menu IDs are updated using the documented `GM_registerMenuCommand`
options contract (Tampermonkey 5.0 or later); no extra grant is required.
without retiring requests, rebuilding the client or resetting its cursor. Gateway
configuration menus belong to the shared provider in the Strategy27 installation.

Drag the header with the primary mouse button. Header buttons do not start a drag.
Mouse release or window blur saves only `{left, top}` under the private userscript
key `strategy29SummaryPanelPosition`; route changes and page reloads restore it.
The panel clamps its position after viewport, content, collapse and language changes.
Destroying the panel removes its document/window drag listeners. Invalid persisted
The title bar captures the primary pointer so dragging continues across the chart
iframe. Pointer release, cancellation, lost capture and window blur finish once;
destroying the panel releases capture and removes its listeners. Invalid persisted
coordinates fail explicitly. No credentials cross the page boundary.

## Optional Cross-Timeframe Server Summary
## Automatic Cross-Timeframe Server Summary

The remote summary is owned by the CorsairQuant signal client. Its single
gateway URL and secret configuration serves both Strategy27 and Strategy29,
using the existing `http://127.0.0.1:18765` local forward. The Strategy29 module
has one enable/disable menu and no separate gateway configuration. An inactive
module still displays a discoverable panel without making requests. The
secret is never stored in the page, URL, panel DOM, or debug diagnostics.
Strategy29 owns the summary panel, requests, lifecycle and private panel position.
There is no summary enable/disable menu: the summary starts automatically when
the shared gateway provider is available. Gateway origin and secret remain in
the existing Strategy27 installation's private storage. Strategy29 receives only
allowlisted public observation responses through the shared page capability;
it has no credential prompt or privileged network grant.

When enabled, the panel follows only the current Binance route symbol but shows
The panel follows only the current Binance route symbol but shows
every timeframe watched for that symbol by the server. It therefore continues to
show 1m, 1h, and other server states and recent signals regardless of the chart
interval currently open. It labels delivery counts as global because those
Expand Down Expand Up @@ -126,23 +127,28 @@ retain a complete prior-success group. Expired selection can originate from fres
or stale facts. Clock rollback does not invalidate otherwise coherent metadata.

Authentication failure affects only the remote summary, not local chart detection.
The unified client preserves the Strategy27 installation namespace, update URL and
private gateway keys. Independent automatic updates require a readiness handshake:
the host waits for the local-only companion before creating a remote panel. A
legacy companion retains its panel and polling until refresh with both scripts
updated. The new local-only runtime has a new singleton version and refuses to
reuse a legacy remote-owning runtime. If the host has not updated, the companion
shows a localized update/reload notice while local chart detection continues.
The notice disappears when the host acknowledges ownership. Existing private
host preferences win on subsequent reloads as well as initial migration.

Update the existing installation in place. Strategy29's
standalone entry is local-only and publishes only a versioned preference record
containing enabled state and panel coordinates. The host validates its exact keys,
copies missing preferences once into its own private storage and preserves existing
host choices. This public record cannot supply gateway credentials, origins or
event state. Both script load orders are supported; update both scripts before
reloading. The host never installs or replaces the local detector singleton.
Invalid provider state stops the remote module at its job boundary, retires its
panel and request, and displays a localized failure notice. Local sampling keeps
running. The stopped module does not automatically retry invalid configuration.
The shared provider preserves the Strategy27 installation namespace, update URL
and private gateway keys. Update both existing scripts and reload. Both load
orders are supported. Without the current provider, Strategy29 displays a localized
update/reload notice while local chart detection continues. Previous host-owned
summary releases do not receive the retired readiness handshake, so they cannot
start a second panel beside the current Strategy29 runtime. Runtime version 4
refuses to reuse the legacy singleton. The final position saved by the old
Strategy27 host is handed back once as a strictly validated nonsecret coordinate
record. Strategy29 saves its own completion version and position; later reloads
preserve subsequent Strategy29 drags instead of reapplying the old host position.
An invalid handoff stops only the remote summary without copying a position or
completion marker. Credentials and the retired enable preference never migrate.

The public transport accepts only exact fixed status/event routes and validated
query fields. Requests carry no caller-selected origin, headers or body. The
provider owns authentication, redirect rejection, bounded response size, a
10-second deadline and cancellation. A settings revision retires old responses
before they can advance Strategy29's cursor. Unicode letter/number base symbols
retain their exact canonical identity, including symbols such as `牛来/USDT:USDT`.

The unified gateway reports `module_disabled` when the server module is intentionally
off, `gateway_unavailable` when an enabled fixed backend cannot be reached, and
Expand Down
46 changes: 46 additions & 0 deletions e2e/binance-orderbook/specs/strategy29-panel-drag.pw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { readFile } from 'node:fs/promises';
import { test, expect } from '@playwright/test';

const source = await readFile(new URL('../../../src/binance-strategy29-bollinger/dom/panel-position.js', import.meta.url), 'utf8');
const moduleUrl = `data:text/javascript;base64,${Buffer.from(source).toString('base64')}`;
const fixtureUrl = 'https://panel.example.test/';

async function install(page) {
await page.evaluate(async url => {
const { installPanelPosition } = await import(url);
window.savedPositions = [];
window.panelPosition = installPanelPosition(document, document.querySelector('section'), document.querySelector('header'), {
initialPosition: JSON.parse(localStorage.getItem('position') ?? '{"left":100,"top":300}'),
savePosition(position) {
window.savedPositions.push(position);
localStorage.setItem('position', JSON.stringify(position));
},
});
}, moduleUrl);
}

test('header drag crosses a chart iframe, releases capture and restores the saved position', async ({ page }) => {
await page.route(fixtureUrl, route => route.fulfill({ contentType: 'text/html', body: `
<iframe style="position:fixed;inset:0;width:100%;height:100%;border:0"
srcdoc="<button onclick='document.body.dataset.clicked=1'>Chart control</button>"></iframe>
<section style="position:fixed;width:340px;height:200px;background:white">
<header style="height:34px;background:gray">Strategy 29 <button>Collapse</button></header>
</section>` }));
await page.goto(fixtureUrl);
await install(page);
await page.mouse.move(170, 315);
await page.mouse.down();
await page.mouse.move(230, 275);
await page.mouse.up();
await expect.poll(() => page.evaluate(() => window.savedPositions)).toEqual([{ left: 160, top: 260 }]);
await expect(page.locator('section')).toHaveCSS('left', '160px');
await expect(page.locator('section')).toHaveCSS('top', '260px');
await page.frameLocator('iframe').getByRole('button', { name: 'Chart control' }).click();
await expect(page.frameLocator('iframe').locator('body')).toHaveAttribute('data-clicked', '1');
await page.reload();
await install(page);
await expect(page.locator('section')).toHaveCSS('left', '160px');
await expect(page.locator('section')).toHaveCSS('top', '260px');
await page.locator('header button').click();
expect(await page.evaluate(() => window.savedPositions)).toEqual([]);
});
Loading
Loading