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
49 changes: 13 additions & 36 deletions e2e/binance-orderbook/specs/control-flows.pw.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ async function readRect(locator) {
return rect;
}

async function expectStandardDisabledStyle(locator) {
await expect(locator).toHaveCSS('background-color', 'rgb(245, 245, 245)');
await expect(locator).toHaveCSS('border-color', 'rgb(213, 217, 226)');
await expect(locator).toHaveCSS('color', 'rgb(183, 189, 198)');
await expect(locator).toHaveCSS('opacity', '0.65');
await expect(locator).toHaveCSS('cursor', 'not-allowed');
}

test('native open and close tabs drive one stable panel direction selector', async ({ page }) => {
const scenario = createCancelScenario({
positions: POSITION_SETS.current,
Expand Down Expand Up @@ -156,31 +164,16 @@ test('starting a ladder preserves action slots and turns only the active action
await expect(stop).toHaveCount(0);
const startLongRect = await readRect(startLong);
const startShortRect = await readRect(startShort);
const startShortTone = await startShort.evaluate((button) => {
const style = getComputedStyle(button);
return {
backgroundColor: style.backgroundColor,
borderColor: style.borderColor,
color: style.color,
};
});
const cancelRect = await readRect(cancel);
await installInteractionProbe(page, '[data-ladder-action="OPEN_LONG"]');
await startLong.click();
await expect(startLong).toHaveCount(0);
await expect(startShort).toBeDisabled();
await expect(stop).toBeEnabled();
await expectStandardDisabledStyle(startShort);
const stopRect = await readRect(stop);
expect(stopRect).toEqual(startLongRect);
expect(await readRect(startShort)).toEqual(startShortRect);
expect(await startShort.evaluate((button) => {
const style = getComputedStyle(button);
return {
backgroundColor: style.backgroundColor,
borderColor: style.borderColor,
color: style.color,
};
})).toEqual(startShortTone);
expect(await readRect(cancel)).toEqual(cancelRect);
const submissionsBeforeStop = (await readFixtureState(page)).events
.filter((event) => event.type === 'order-submitted').length;
Expand Down Expand Up @@ -213,16 +206,16 @@ test('starting close short keeps unavailable close long in the standard disabled
const closeShort = panel.getByRole('button', { name: '阶梯平空' });

await expect(closeLong).toBeDisabled();
await expect(closeLong).not.toHaveAttribute('data-ladder-preserve-tone', 'true');
await expectStandardDisabledStyle(closeLong);
await expect(closeShort).toBeEnabled();
await closeShort.click();
await expect(panel.getByRole('button', { name: '停止平空' })).toBeEnabled();
await expect(closeLong).toBeDisabled();
await expect(closeLong).not.toHaveAttribute('data-ladder-preserve-tone', 'true');
await expectStandardDisabledStyle(closeLong);
expect(errors).toEqual([]);
});

test('starting close short preserves close long tone only when a long position exists', async ({ page }) => {
test('starting close short disables close long with the standard disabled style', async ({ page }) => {
const scenario = createCancelScenario({
positions: [
{ symbol: CURRENT_SYMBOL, side: 'LONG', quantity: '0.1' },
Expand All @@ -237,26 +230,10 @@ test('starting close short preserves close long tone only when a long position e

await expect(closeLong).toBeEnabled();
await expect(closeShort).toBeEnabled();
const closeLongTone = await closeLong.evaluate((button) => {
const style = getComputedStyle(button);
return {
backgroundColor: style.backgroundColor,
borderColor: style.borderColor,
color: style.color,
};
});
await closeShort.click();
await expect(panel.getByRole('button', { name: '停止平空' })).toBeEnabled();
await expect(closeLong).toBeDisabled();
await expect(closeLong).toHaveAttribute('data-ladder-preserve-tone', 'true');
expect(await closeLong.evaluate((button) => {
const style = getComputedStyle(button);
return {
backgroundColor: style.backgroundColor,
borderColor: style.borderColor,
color: style.color,
};
})).toEqual(closeLongTone);
await expectStandardDisabledStyle(closeLong);
expect(errors).toEqual([]);
});

Expand Down
28 changes: 10 additions & 18 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.131
// @version 2.7.132
// @author jackhai9
// @description 单击订单簿价格,按当前开仓/平仓 tab 自动填数量并执行下单,内置数量倍率面板
// @match https://www.binance.com/*/futures/*
Expand Down Expand Up @@ -1973,7 +1973,7 @@
style.id = styleId;
style.textContent = `
button[${NATIVE_ACTION_DISABLED_ATTR}="true"],
#${PANEL_ID} button:disabled:not([data-ladder-preserve-tone="true"]) {
#${PANEL_ID} button:disabled {
background: ${DISABLED_CONTROL_BG} !important;
color: ${DISABLED_CONTROL_TEXT} !important;
border-color: ${DISABLED_CONTROL_BORDER} !important;
Expand Down Expand Up @@ -5648,23 +5648,17 @@
"</div>"
].join("");
}
function ladderActionButton(actionType, label, tone, disabled = false, preserveTone = false) {
function ladderActionButton(actionType, label, tone, disabled = false) {
const isBuyTone = tone === "BUY";
const borderColor = isBuyTone ? "var(--color-Buy)" : "var(--color-Sell)";
const background = isBuyTone ? "var(--color-GreenAlpha01)" : "var(--color-RedAlpha01)";
const disabledAttrs = disabled ? ` disabled aria-disabled="true"${preserveTone ? ' data-ladder-preserve-tone="true"' : ""}` : "";
const disabledAttrs = disabled ? ' disabled aria-disabled="true"' : "";
const cursor = disabled ? "not-allowed" : "pointer";
return `<button type="button" data-ladder-action="${actionType}"${disabledAttrs} style="height:${LADDER_CONTROL_BUTTON_HEIGHT}px;border:1px solid ${borderColor};border-radius:6px;background:${background};color:${borderColor};font-size:${LADDER_CONTROL_BUTTON_FONT_SIZE}px;font-weight:${CONTROL_FONT_WEIGHT};line-height:${LADDER_CONTROL_BUTTON_HEIGHT - 2}px;cursor:${cursor};opacity:1;">${label}</button>`;
}
function ladderExecutionButton(actionType, label, tone, disabled = false, preserveTone = false) {
function ladderExecutionButton(actionType, label, tone, disabled = false) {
if (activeLadderActionType !== actionType) {
return ladderActionButton(
actionType,
label,
tone,
disabled,
Boolean(activeLadderActionType) && preserveTone
);
return ladderActionButton(actionType, label, tone, disabled);
}
return `<button type="button" data-ladder-stop="true" data-ladder-action-origin="${actionType}" style="height:${LADDER_CONTROL_BUTTON_HEIGHT}px;border:1px solid var(--color-PrimaryYellow);border-radius:6px;background:var(--color-BadgeBg);color:#9a6700;font-size:${LADDER_CONTROL_BUTTON_FONT_SIZE}px;font-weight:${CONTROL_FONT_WEIGHT};line-height:${LADDER_CONTROL_BUTTON_HEIGHT - 2}px;cursor:pointer;">${PANEL_COPY.action.stopLadderByAction[actionType]}</button>`;
}
Expand All @@ -5691,8 +5685,8 @@
ladderOptionRow(PANEL_COPY.field.interval, PANEL_COPY.tooltip.interval, LADDER_STEP_OPTIONS, getLadderStep(tradeMode, symbol, precision), "step", "")
],
actionButtons: [
ladderExecutionButton("OPEN_LONG", PANEL_COPY.action.openLong, "BUY", actionDisabled, true),
ladderExecutionButton("OPEN_SHORT", PANEL_COPY.action.openShort, "SELL", actionDisabled, true)
ladderExecutionButton("OPEN_LONG", PANEL_COPY.action.openLong, "BUY", actionDisabled),
ladderExecutionButton("OPEN_SHORT", PANEL_COPY.action.openShort, "SELL", actionDisabled)
]
};
}
Expand All @@ -5717,15 +5711,13 @@
"CLOSE_LONG",
PANEL_COPY.action.closeLong,
"SELL",
closeLongDisabled,
Boolean(closeContext?.knowsLong && closeContext?.hasLong)
closeLongDisabled
),
ladderExecutionButton(
"CLOSE_SHORT",
PANEL_COPY.action.closeShort,
"BUY",
closeShortDisabled,
Boolean(closeContext?.knowsShort && closeContext?.hasShort)
closeShortDisabled
)
]
};
Expand Down
26 changes: 8 additions & 18 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.131
// @version 2.7.132
// @author jackhai9
// @description 单击订单簿价格,按当前开仓/平仓 tab 自动填数量并执行下单,内置数量倍率面板
// @match https://www.binance.com/*/futures/*
Expand Down Expand Up @@ -393,7 +393,7 @@ import {
style.id = styleId;
style.textContent = `
button[${NATIVE_ACTION_DISABLED_ATTR}="true"],
#${PANEL_ID} button:disabled:not([data-ladder-preserve-tone="true"]) {
#${PANEL_ID} button:disabled {
background: ${DISABLED_CONTROL_BG} !important;
color: ${DISABLED_CONTROL_TEXT} !important;
border-color: ${DISABLED_CONTROL_BORDER} !important;
Expand Down Expand Up @@ -4811,26 +4811,18 @@ import {
].join('');
}

function ladderActionButton(actionType, label, tone, disabled = false, preserveTone = false) {
function ladderActionButton(actionType, label, tone, disabled = false) {
const isBuyTone = tone === 'BUY';
const borderColor = isBuyTone ? 'var(--color-Buy)' : 'var(--color-Sell)';
const background = isBuyTone ? 'var(--color-GreenAlpha01)' : 'var(--color-RedAlpha01)';
const disabledAttrs = disabled
? ` disabled aria-disabled="true"${preserveTone ? ' data-ladder-preserve-tone="true"' : ''}`
: '';
const disabledAttrs = disabled ? ' disabled aria-disabled="true"' : '';
const cursor = disabled ? 'not-allowed' : 'pointer';
return `<button type="button" data-ladder-action="${actionType}"${disabledAttrs} style="height:${LADDER_CONTROL_BUTTON_HEIGHT}px;border:1px solid ${borderColor};border-radius:6px;background:${background};color:${borderColor};font-size:${LADDER_CONTROL_BUTTON_FONT_SIZE}px;font-weight:${CONTROL_FONT_WEIGHT};line-height:${LADDER_CONTROL_BUTTON_HEIGHT - 2}px;cursor:${cursor};opacity:1;">${label}</button>`;
}

function ladderExecutionButton(actionType, label, tone, disabled = false, preserveTone = false) {
function ladderExecutionButton(actionType, label, tone, disabled = false) {
if (activeLadderActionType !== actionType) {
return ladderActionButton(
actionType,
label,
tone,
disabled,
Boolean(activeLadderActionType) && preserveTone,
);
return ladderActionButton(actionType, label, tone, disabled);
}
return `<button type="button" data-ladder-stop="true" data-ladder-action-origin="${actionType}" style="height:${LADDER_CONTROL_BUTTON_HEIGHT}px;border:1px solid var(--color-PrimaryYellow);border-radius:6px;background:var(--color-BadgeBg);color:#9a6700;font-size:${LADDER_CONTROL_BUTTON_FONT_SIZE}px;font-weight:${CONTROL_FONT_WEIGHT};line-height:${LADDER_CONTROL_BUTTON_HEIGHT - 2}px;cursor:pointer;">${PANEL_COPY.action.stopLadderByAction[actionType]}</button>`;
}
Expand Down Expand Up @@ -4858,8 +4850,8 @@ import {
ladderOptionRow(PANEL_COPY.field.interval, PANEL_COPY.tooltip.interval, LADDER_STEP_OPTIONS, getLadderStep(tradeMode, symbol, precision), 'step', ''),
],
actionButtons: [
ladderExecutionButton('OPEN_LONG', PANEL_COPY.action.openLong, 'BUY', actionDisabled, true),
ladderExecutionButton('OPEN_SHORT', PANEL_COPY.action.openShort, 'SELL', actionDisabled, true),
ladderExecutionButton('OPEN_LONG', PANEL_COPY.action.openLong, 'BUY', actionDisabled),
ladderExecutionButton('OPEN_SHORT', PANEL_COPY.action.openShort, 'SELL', actionDisabled),
],
};
}
Expand All @@ -4886,14 +4878,12 @@ import {
PANEL_COPY.action.closeLong,
'SELL',
closeLongDisabled,
Boolean(closeContext?.knowsLong && closeContext?.hasLong),
),
ladderExecutionButton(
'CLOSE_SHORT',
PANEL_COPY.action.closeShort,
'BUY',
closeShortDisabled,
Boolean(closeContext?.knowsShort && closeContext?.hasShort),
),
],
};
Expand Down
4 changes: 2 additions & 2 deletions test/unit/binance-orderbook-trade/ladder-options.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,8 @@ test('ladder quantity levels and step options share one stable five-slot grid',
});

test('close ladder buttons match the Binance native long-short order', () => {
const closeLongButton = "'CLOSE_LONG',\n PANEL_COPY.action.closeLong,\n 'SELL',\n closeLongDisabled,\n Boolean(closeContext?.knowsLong && closeContext?.hasLong)";
const closeShortButton = "'CLOSE_SHORT',\n PANEL_COPY.action.closeShort,\n 'BUY',\n closeShortDisabled,\n Boolean(closeContext?.knowsShort && closeContext?.hasShort)";
const closeLongButton = "'CLOSE_LONG',\n PANEL_COPY.action.closeLong,\n 'SELL',\n closeLongDisabled,\n )";
const closeShortButton = "'CLOSE_SHORT',\n PANEL_COPY.action.closeShort,\n 'BUY',\n closeShortDisabled,\n )";

assert.notEqual(source.indexOf(closeLongButton), -1);
assert.notEqual(source.indexOf(closeShortButton), -1);
Expand Down
9 changes: 3 additions & 6 deletions test/unit/binance-orderbook-trade/source-regressions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -883,11 +883,8 @@ test('cancel current-symbol open orders are single-flight and follow the native
assert.match(panelBody, /noOrdersFeedback: cancelNoOrdersFeedbackActive/);
assert.match(source, /let activeLadderActionType = null/);
assert.match(source, /let activeLadderPanelContext = null/);
assert.match(source, /data-ladder-preserve-tone="true"/);
assert.match(source, /button:disabled:not\(\[data-ladder-preserve-tone="true"\]\)/);
assert.match(source, /Boolean\(activeLadderActionType\) && preserveTone/);
assert.match(source, /Boolean\(closeContext\?\.knowsLong && closeContext\?\.hasLong\)/);
assert.match(source, /Boolean\(closeContext\?\.knowsShort && closeContext\?\.hasShort\)/);
assert.match(source, /#\$\{PANEL_ID\} button:disabled/);
assert.doesNotMatch(source, /data-ladder-preserve-tone|preserveTone/);
assert.match(panelBody, /controlSections\.actionButtons/);
assert.doesNotMatch(panelBody, /grid-column:span 2/);
assert.match(panelBody, /grid-template-columns:repeat\(3,minmax\(0,1fr\)\)/);
Expand Down Expand Up @@ -933,7 +930,7 @@ test('cancel current-symbol open orders are single-flight and follow the native
const actionRowsBody = readFunctionBody('getLadderControlSections');
assert.match(actionRowsBody, /actionDisabled = ladderRunning \|\| cancelCurrentSymbolOpenOrdersBlocksLadderActions/);
assert.doesNotMatch(actionRowsBody, /!!cancelCurrentSymbolOpenOrdersTask/);
assert.match(actionRowsBody, /ladderExecutionButton\('OPEN_LONG',[\s\S]*actionDisabled, true\)/);
assert.match(actionRowsBody, /ladderExecutionButton\('OPEN_LONG',[\s\S]*actionDisabled\)/);
assert.match(startBody, /activeLadderActionType = actionType/);
assert.match(startBody, /activeLadderPanelContext = \{[\s\S]*mode: spec\.mode,[\s\S]*symbol: actionSymbol,[\s\S]*precision: readCurrentOrderbookPrecisionValue\(\)/);
assert.match(startBody, /finally\(\(\) => \{[\s\S]*activeLadderActionType = null/);
Expand Down
Loading