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
2 changes: 2 additions & 0 deletions e2e/binance-orderbook/fixtures/binance-futures.js
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,8 @@ export function renderBinanceFuturesFixture(scenario) {

function closeChartOrdersPopover() {
if (!chartOrdersPopover.classList.contains('active')) return;
record('chart-orders-popover-close-requested');
if (scenario.host.chartOrdersPopoverCloseMode === 'stuck') return;
chartOrdersPopover.classList.remove('active');
record('chart-orders-popover-closed');
}
Expand Down
6 changes: 6 additions & 0 deletions e2e/binance-orderbook/scenarios/cancel-current-symbol.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export function createCancelScenario(overrides = {}) {
dialogMode: 'normal',
dialogReplacementDelayMs: null,
clearMode: 'currentSymbol',
chartOrdersPopoverCloseMode: 'normal',
precisionOptions: ['0.001', '0.01', '0.1', '1'],
...overrides.host,
},
Expand Down Expand Up @@ -84,6 +85,11 @@ export function createCancelScenario(overrides = {}) {
if (!['currentSymbol', 'none'].includes(scenario.host.clearMode)) {
throw new Error(`Unsupported clear mode: ${scenario.host.clearMode}`);
}
if (!['normal', 'stuck'].includes(scenario.host.chartOrdersPopoverCloseMode)) {
throw new Error(
`Unsupported chart-orders popover close mode: ${scenario.host.chartOrdersPopoverCloseMode}`,
);
}
if (
scenario.host.dialogReplacementDelayMs !== null
&& (!Number.isInteger(scenario.host.dialogReplacementDelayMs)
Expand Down
43 changes: 43 additions & 0 deletions e2e/binance-orderbook/specs/cancel-current-symbol.pw.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,56 @@ test('a 70-order drawing burst split across tasks performs one full save per tog
.filter((event) => event.type === 'chart-orders-checked')
.map((event) => event.value),
).toEqual([false, true]);
for (const checked of [false, true]) {
const checkedIndex = state.events.findIndex(
(event) => event.type === 'chart-orders-checked' && event.value === checked,
);
const popoverClosedIndex = state.events.findIndex(
(event, index) => index > checkedIndex && event.type === 'chart-orders-popover-closed',
);
const finalSaveRequestIndex = state.events.findLastIndex(
(event) => event.type === 'chart-save-requested' && event.checked === checked,
);
const fullSaveIndex = state.events.findIndex(
(event, index) => index > finalSaveRequestIndex
&& event.type === 'chart-saved'
&& event.snapshot.checked === checked,
);
expect(popoverClosedIndex).toBeGreaterThan(checkedIndex);
expect(popoverClosedIndex).toBeLessThan(finalSaveRequestIndex);
expect(popoverClosedIndex).toBeLessThan(fullSaveIndex);
}
await expectRestoredState(page, scenario);
const probe = await finishInteractionProbe(page);
assertResponsiveInteraction(expect, probe);
assertStableGeometry(expect, probe.baseline, probe.current);
expect(errors).toEqual([]);
});

test('a popover close failure does not discard coalesced chart saves', async ({ page }) => {
const scenario = createCancelScenario({
positions: POSITION_SETS.current,
orders: ORDER_SETS.current,
ui: { hideOtherSymbols: false, accountTab: 'positions', showOrders: true },
host: { chartOrdersPopoverCloseMode: 'stuck' },
});
const { errors } = await openUserscriptScenario(page, scenario);

await page.getByRole('button', { name: '撤单' }).click();
await expect(page.getByText('未能恢复图表当前委托显示')).toBeVisible({ timeout: 6_000 });

const state = await readFixtureState(page);
expect(state.dialogOpen).toBe(false);
expect(state.orders).toEqual(ORDER_SETS.current);
expect(state.showOrders).toBe(true);
expect(state.events.filter((event) => event.type === 'chart-save-requested')).toHaveLength(2);
expect(state.events.filter((event) => event.type === 'chart-saved')).toHaveLength(2);
expect(
state.events.filter((event) => event.type === 'chart-orders-popover-close-requested'),
).toHaveLength(2);
expect(errors).toEqual([]);
});

test('confirming with mixed-symbol orders clears only the current symbol', async ({ page }) => {
const scenario = createCancelScenario({
positions: POSITION_SETS.both,
Expand Down
20 changes: 17 additions & 3 deletions scripts/binance-orderbook-trade.user.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// @namespace binance.orderbook.trade
// @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 2.7.146
// @version 2.7.147
// @author jackhai9
// @description 单击订单簿价格,按当前开仓/平仓 tab 自动填数量并执行下单,内置数量倍率面板
// @match https://www.binance.com/*/futures/*
Expand Down Expand Up @@ -5268,14 +5268,26 @@
if (typeof expectDrawingEvents !== "boolean") {
throw new Error("Chart orders drawing-event expectation is invalid");
}
const result = await coalesceTradingViewDrawingSaves(
let popoverCloseOutcomePromise = null;
const coalescingOutcome = await coalesceTradingViewDrawingSaves(
target.tradingViewApi,
async () => {
checkbox.click();
await waitForBinanceChartOrdersPopover(target, expectedChecked);
popoverCloseOutcomePromise = closeBinanceChartOrdersPopover(target).then(
() => null,
(error) => error
);
},
expectDrawingEvents ? {} : { eventDiscoveryTimeoutMs: 0 }
).then(
(result2) => ({ result: result2, error: null }),
(error) => ({ result: null, error })
);
const popoverCloseError = popoverCloseOutcomePromise ? await popoverCloseOutcomePromise : null;
if (coalescingOutcome.error) throw coalescingOutcome.error;
if (popoverCloseError) throw popoverCloseError;
const { result } = coalescingOutcome;
log("图表当前委托保存已合并", {
drawingEvents: result.drawingEventCount,
saveRequests: result.saveRequestCount,
Expand All @@ -5294,6 +5306,7 @@
false,
true
);
return;
}
await closeBinanceChartOrdersPopover(target);
}
Expand All @@ -5310,8 +5323,9 @@
state.originalChecked,
expectDrawingEvents
);
} else {
await closeBinanceChartOrdersPopover(target);
}
await closeBinanceChartOrdersPopover(target);
clearChartOrdersRecoveryRecord();
}
async function recoverChartOrdersStateAfterReload() {
Expand Down
25 changes: 22 additions & 3 deletions src/binance-orderbook-trade/index.user.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// @namespace binance.orderbook.trade
// @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 2.7.146
// @version 2.7.147
// @author jackhai9
// @description 单击订单簿价格,按当前开仓/平仓 tab 自动填数量并执行下单,内置数量倍率面板
// @match https://www.binance.com/*/futures/*
Expand Down Expand Up @@ -3765,14 +3765,31 @@ import {
if (typeof expectDrawingEvents !== 'boolean') {
throw new Error('Chart orders drawing-event expectation is invalid');
}
const result = await coalesceTradingViewDrawingSaves(
let popoverCloseOutcomePromise = null;
const coalescingOutcome = await coalesceTradingViewDrawingSaves(
target.tradingViewApi,
async () => {
checkbox.click();
await waitForBinanceChartOrdersPopover(target, expectedChecked);
// Start hiding native setup UI immediately, but keep its failure outside
// the action so the coalescer can still replay the final chart snapshot.
popoverCloseOutcomePromise = closeBinanceChartOrdersPopover(target).then(
() => null,
(error) => error,
);
},
expectDrawingEvents ? {} : { eventDiscoveryTimeoutMs: 0 },
).then(
(result) => ({ result, error: null }),
(error) => ({ result: null, error }),
);
const popoverCloseError = popoverCloseOutcomePromise
? await popoverCloseOutcomePromise
: null;
if (coalescingOutcome.error) throw coalescingOutcome.error;
if (popoverCloseError) throw popoverCloseError;

const { result } = coalescingOutcome;
log('图表当前委托保存已合并', {
drawingEvents: result.drawingEventCount,
saveRequests: result.saveRequestCount,
Expand All @@ -3792,6 +3809,7 @@ import {
false,
true,
);
return;
}
await closeBinanceChartOrdersPopover(target);
}
Expand All @@ -3813,8 +3831,9 @@ import {
state.originalChecked,
expectDrawingEvents,
);
} else {
await closeBinanceChartOrdersPopover(target);
}
await closeBinanceChartOrdersPopover(target);
clearChartOrdersRecoveryRecord();
}

Expand Down
Loading