33// @namespace binance.orderbook.trade
44// @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
55// @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
6- // @version 2.7.230
6+ // @version 2.7.231
77// @author jackhai9
88// @description 单击订单簿价格,按当前开仓/平仓 tab 自动填数量并执行下单,内置数量倍率面板
99// @match https://www.binance.com/*/futures/*
@@ -5837,6 +5837,28 @@ Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com>
58375837 return parts.join("");
58385838 }
58395839
5840+ // src/binance-orderbook-trade/dom/order-feedback-scan.js
5841+ var ORDER_FEEDBACK_SELECTOR = [
5842+ '[role="alert"]',
5843+ "[aria-live]",
5844+ '[class*="toast"]',
5845+ '[class*="Toast"]',
5846+ '[class*="message"]',
5847+ '[class*="Message"]',
5848+ '[class*="notification"]',
5849+ '[class*="Notification"]'
5850+ ].join(",");
5851+ var MAX_ORDER_FEEDBACK_TEXT_LENGTH = 300;
5852+ function readVisibleOrderFeedbackEntries(document2, { isVisibleElement }) {
5853+ const entries = [];
5854+ for (const el of document2.querySelectorAll(ORDER_FEEDBACK_SELECTOR)) {
5855+ const text = (el.textContent || "").replace(/\s+/g, " ").trim();
5856+ if (!text || text.length > MAX_ORDER_FEEDBACK_TEXT_LENGTH || !isPotentialOrderFeedbackText(text)) continue;
5857+ if (isVisibleElement(el)) entries.push({ el, text });
5858+ }
5859+ return entries;
5860+ }
5861+
58405862 // src/binance-orderbook-trade/dom/chart-orders.js
58415863 var ACTIVE_POPOVER_SELECTOR = ".bn-bubble.active";
58425864 var OPEN_ORDERS_LABEL_PATTERN = /^(?:当前委托|Open Orders)$/i;
@@ -7490,29 +7512,69 @@ Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com>
74907512 // src/binance-orderbook-trade/chart-storage/json.js
74917513 var encoder = new TextEncoder();
74927514 var nativeObjectSource = Function.prototype.toString.call(Object);
7515+ var nativeObjectConstructors = /* @__PURE__ */ new WeakSet();
74937516 function assert(condition, message) {
74947517 if (!condition) throw new Error(message);
74957518 }
7519+ function isNativeObjectConstructor(candidate) {
7520+ if (nativeObjectConstructors.has(candidate)) return true;
7521+ if (Function.prototype.toString.call(candidate) !== nativeObjectSource) return false;
7522+ nativeObjectConstructors.add(candidate);
7523+ return true;
7524+ }
74967525 function isPlainJsonObject(value) {
74977526 const prototype = Object.getPrototypeOf(value);
74987527 if (prototype === null || Object.getPrototypeOf(prototype) !== null) return false;
74997528 const constructor = Object.getOwnPropertyDescriptor(prototype, "constructor");
75007529 if (!constructor || !Object.hasOwn(constructor, "value") || typeof constructor.value !== "function") return false;
7501- if (Function.prototype.toString.call (constructor.value) !== nativeObjectSource ) return false;
7530+ if (!isNativeObjectConstructor (constructor.value)) return false;
75027531 const descriptor = Object.getOwnPropertyDescriptor(constructor.value, "prototype");
75037532 return !!descriptor && Object.hasOwn(descriptor, "value") && descriptor.value === prototype;
75047533 }
75057534 function byteLength(value) {
75067535 return encoder.encode(JSON.stringify(value)).byteLength;
75077536 }
7537+ function jsonStringByteLength(text) {
7538+ if (text.length >= 256) return byteLength(text);
7539+ let bytes = 2;
7540+ for (let index = 0; index < text.length; index += 1) {
7541+ const code = text.charCodeAt(index);
7542+ if (code < 128) {
7543+ if (code === 34 || code === 92) bytes += 2;
7544+ else if (code >= 32) bytes += 1;
7545+ else bytes += code === 8 || code === 9 || code === 10 || code === 12 || code === 13 ? 2 : 6;
7546+ } else if (code < 2048) {
7547+ bytes += 2;
7548+ } else if (code >= 55296 && code <= 56319) {
7549+ const next = text.charCodeAt(index + 1);
7550+ if (next >= 56320 && next <= 57343) {
7551+ bytes += 4;
7552+ index += 1;
7553+ } else {
7554+ bytes += 6;
7555+ }
7556+ } else if (code >= 56320 && code <= 57343) {
7557+ bytes += 6;
7558+ } else {
7559+ bytes += 3;
7560+ }
7561+ }
7562+ return bytes;
7563+ }
7564+ function primitiveJsonByteLength(value) {
7565+ if (value === null) return 4;
7566+ if (typeof value === "boolean") return value ? 4 : 5;
7567+ if (typeof value === "number") return String(value).length;
7568+ return jsonStringByteLength(value);
7569+ }
75087570 function snapshotJson(value, budget = { remaining: Infinity }, seen = /* @__PURE__ */ new Set()) {
75097571 function consume(bytes) {
75107572 budget.remaining -= bytes;
75117573 assert(budget.remaining >= 0, "Chart JSON byte limit exceeded");
75127574 }
75137575 if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number") {
75147576 assert(typeof value !== "number" || Number.isFinite(value), "Chart JSON numbers must be finite");
7515- consume(byteLength (value));
7577+ consume(primitiveJsonByteLength (value));
75167578 return value;
75177579 }
75187580 assert(typeof value === "object", "Chart values must be JSON");
@@ -7525,19 +7587,18 @@ Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com>
75257587 if (array) assert(keys.length === value.length && keys.every((key, index) => key === String(index)), "Chart JSON arrays must be dense");
75267588 seen.add(value);
75277589 consume(2 + Math.max(0, keys.length - 1));
7528- const result = array ? [] : {} ;
7590+ const copies = [] ;
75297591 for (const key of keys) {
75307592 const descriptor = descriptors[key];
75317593 assert(descriptor.enumerable && Object.hasOwn(descriptor, "value"), "Chart JSON requires enumerable data properties");
7532- if (!array) consume(byteLength(key) + 1);
7533- Object.defineProperty(result, key, {
7534- value: snapshotJson(descriptor.value, budget, seen),
7535- enumerable: true,
7536- writable: true,
7537- configurable: true
7538- });
7594+ if (array) {
7595+ copies.push(snapshotJson(descriptor.value, budget, seen));
7596+ continue;
7597+ }
7598+ consume(jsonStringByteLength(key) + 1);
7599+ copies.push([key, snapshotJson(descriptor.value, budget, seen)]);
75397600 }
7540- return result ;
7601+ return array ? copies : Object.fromEntries(copies) ;
75417602 }
75427603 function equalJson(first, second) {
75437604 if (Object.is(first, second)) return true;
@@ -15581,6 +15642,10 @@ Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com>
1558115642 const USDT_REBALANCE_FLAT_STABLE_MS = 2e3;
1558215643 const USDT_REBALANCE_REQUEST_TIMEOUT_MS = 5e3;
1558315644 const USDT_REBALANCE_BALANCE_POLL_MS = 1e3;
15645+ const AUTOMATIC_USDT_REBALANCE_EPISODE_PREFIX = "userscripts:automatic-usdt-rebalance:v1:";
15646+ const NON_CONSUMED_AUTOMATIC_USDT_REBALANCE_EPISODES = new Set(
15647+ ["active", "in_flight", "blocked"].map((status) => JSON.stringify({ version: 1, status }))
15648+ );
1558415649 const USDT_REBALANCE_RATIO = formatUsdtRebalanceRatio(USDT_REBALANCE_TARGET_PERCENTS);
1558515650 const USDT_REBALANCE_COPY = Object.freeze({
1558615651 allocated: formatUsdtRebalanceAllocatedStatus(USDT_REBALANCE_RATIO),
@@ -17912,30 +17977,11 @@ Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com>
1791217977 `下单按钮 ${TRADE_ACTION_BUTTON_READY_TIMEOUT_SECONDS} 秒内未达到可点击状态`
1791317978 );
1791417979 }
17915- function readVisibleOrderFeedbackEntries() {
17916- const selectors = [
17917- '[role="alert"]',
17918- "[aria-live]",
17919- '[class*="toast"]',
17920- '[class*="Toast"]',
17921- '[class*="message"]',
17922- '[class*="Message"]',
17923- '[class*="notification"]',
17924- '[class*="Notification"]'
17925- ];
17926- const seen = /* @__PURE__ */ new Set();
17927- const entries = [];
17928- for (const el of document.querySelectorAll(selectors.join(","))) {
17929- if (seen.has(el) || !isVisibleElement(el)) continue;
17930- seen.add(el);
17931- const text = (el.textContent || "").replace(/\s+/g, " ").trim();
17932- if (!text || text.length > 300) continue;
17933- if (isPotentialOrderFeedbackText(text)) entries.push({ el, text });
17934- }
17935- return entries;
17980+ function readVisibleOrderFeedbackEntries2() {
17981+ return readVisibleOrderFeedbackEntries(document, { isVisibleElement });
1793617982 }
1793717983 function takeOrderFeedbackSnapshot() {
17938- const entries = readVisibleOrderFeedbackEntries ();
17984+ const entries = readVisibleOrderFeedbackEntries2 ();
1793917985 return {
1794017986 elements: new Set(entries.map(({ el }) => el)),
1794117987 textByElement: new Map(entries.map(({ el, text }) => [el, text])),
@@ -17944,7 +17990,7 @@ Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com>
1794417990 }
1794517991 function readNewVisibleOrderFeedbackText(previousSnapshot) {
1794617992 const snapshot = previousSnapshot || { elements: /* @__PURE__ */ new Set(), textByElement: /* @__PURE__ */ new Map(), htmlByElement: /* @__PURE__ */ new Map() };
17947- for (const { el, text } of readVisibleOrderFeedbackEntries ()) {
17993+ for (const { el, text } of readVisibleOrderFeedbackEntries2 ()) {
1794817994 if (!snapshot.elements.has(el)) return text;
1794917995 if (snapshot.textByElement.get(el) !== text) return text;
1795017996 if (snapshot.htmlByElement.get(el) !== el.innerHTML) return text;
@@ -21000,7 +21046,7 @@ Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com>
2100021046 throw new Error("Unsupported or unverified account identity");
2100121047 }
2100221048 const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(payload.data.userId));
21003- return "userscripts:automatic-usdt-rebalance:v1:" + Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
21049+ return AUTOMATIC_USDT_REBALANCE_EPISODE_PREFIX + Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
2100421050 }
2100521051 function readUsdtRebalanceEpisode(key) {
2100621052 return readAutomaticRebalanceEpisode(localStorage.getItem(key));
@@ -21048,8 +21094,16 @@ Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com>
2104821094 if (next.status !== previous.status) writeUsdtRebalanceEpisode(key, next.status);
2104921095 }, null, false);
2105021096 }
21097+ function hasRearmableAutomaticUsdtRebalanceEpisode() {
21098+ for (let index = 0; index < localStorage.length; index += 1) {
21099+ const key = localStorage.key(index);
21100+ if (key.startsWith(AUTOMATIC_USDT_REBALANCE_EPISODE_PREFIX) && !NON_CONSUMED_AUTOMATIC_USDT_REBALANCE_EPISODES.has(localStorage.getItem(key))) return true;
21101+ }
21102+ return false;
21103+ }
2105121104 function queueAutomaticUsdtRebalanceActivityObservation() {
2105221105 if (automaticUsdtRebalanceObservationTask || !cachedBncHeaders) return;
21106+ if (!hasRearmableAutomaticUsdtRebalanceEpisode()) return;
2105321107 automaticUsdtRebalanceObservationTask = observeAutomaticUsdtRebalanceActivity().catch((error) => {
2105421108 log("Automatic USDT activity observation failed", error.name, error.message.slice(0, 240));
2105521109 }).finally(() => {
0 commit comments