Replace 期期 equal-split with long/short bias sizing and env ratio controls.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-19 15:25:10 +08:00
parent bc797cb1db
commit e3cd2a75de
13 changed files with 330 additions and 67 deletions
+73 -13
View File
@@ -29,6 +29,8 @@
legB: null,
market: null,
ooSheetsMode: "same_sheets",
ooBiasSplitBy: "budget",
ooBiasRatio: 0.7,
ooCloseModeEnabled: root.getAttribute("data-oo-close-mode-enabled") !== "0",
ooCloseMode: "close_all",
direction: "long",
@@ -335,6 +337,20 @@
return Math.min(out, Math.floor(d + 1e-12));
}
function normalizeOptCP(ot) {
const u = String(ot || "").toUpperCase();
if (u.indexOf("C") === 0) return "C";
if (u.indexOf("P") === 0) return "P";
return "";
}
function ooSizeModeLabel(mode) {
if (mode === "long_bias") return "做多";
if (mode === "short_bias") return "做空";
if (mode === "split_budget") return "均分";
return "同张数";
}
function suggestOoSheetsLocal(budget, mode) {
const costA = unitCost(state.legA);
const costB = unitCost(state.legB);
@@ -344,24 +360,61 @@
if (!(costA > 0) || !(costB > 0)) {
return { sheetsA: 0, sheetsB: 0, premium: 0, ok: false, msg: "缺少有效卖一价,无法建议张数" };
}
let ratio = Number(state.ooBiasRatio);
if (!(ratio > 0) || !(ratio < 1)) ratio = 0.7;
const splitBy = state.ooBiasSplitBy === "sheets" ? "sheets" : "budget";
const pair = costA + costB;
let nSame = pair > 0 ? Math.floor(budget / pair + 1e-12) : 0;
nSame = capByDepth(nSame, state.legA && state.legA.ask_sz);
nSame = capByDepth(nSame, state.legB && state.legB.ask_sz);
let nA = 0;
let nB = 0;
if (mode === "split_budget") {
if (mode === "long_bias" || mode === "short_bias") {
const aCP = normalizeOptCP(state.legA && state.legA.opt_type);
const bCP = normalizeOptCP(state.legB && state.legB.opt_type);
if (!((aCP === "C" && bCP === "P") || (aCP === "P" && bCP === "C"))) {
return { sheetsA: 0, sheetsB: 0, premium: 0, ok: false, msg: "做多/做空需一腿 Call、一腿 Put" };
}
const callIsA = aCP === "C";
const majorIsCall = mode === "long_bias";
let nCall = 0;
let nPut = 0;
if (splitBy === "sheets") {
if (nSame < 2) {
return { sheetsA: 0, sheetsB: 0, premium: 0, ok: false, msg: "同张数规模不足 2,无法按比例拆分" };
}
let majorN = Math.round(nSame * ratio);
majorN = Math.max(1, Math.min(majorN, nSame - 1));
const minorN = nSame - majorN;
nCall = majorIsCall ? majorN : minorN;
nPut = majorIsCall ? minorN : majorN;
} else {
const majBudget = budget * ratio;
const minBudget = budget * (1 - ratio);
const costCall = callIsA ? costA : costB;
const costPut = callIsA ? costB : costA;
if (majorIsCall) {
nCall = Math.floor(majBudget / costCall + 1e-12);
nPut = Math.floor(minBudget / costPut + 1e-12);
} else {
nPut = Math.floor(majBudget / costPut + 1e-12);
nCall = Math.floor(minBudget / costCall + 1e-12);
}
}
nA = callIsA ? nCall : nPut;
nB = callIsA ? nPut : nCall;
nA = capByDepth(nA, state.legA && state.legA.ask_sz);
nB = capByDepth(nB, state.legB && state.legB.ask_sz);
} else if (mode === "split_budget") {
const half = budget / 2;
nA = Math.floor(half / costA + 1e-12);
nB = Math.floor(half / costB + 1e-12);
nA = capByDepth(nA, state.legA && state.legA.ask_sz);
nB = capByDepth(nB, state.legB && state.legB.ask_sz);
} else {
const pair = costA + costB;
const n = pair > 0 ? Math.floor(budget / pair + 1e-12) : 0;
nA = n;
nB = n;
}
nA = capByDepth(nA, state.legA && state.legA.ask_sz);
nB = capByDepth(nB, state.legB && state.legB.ask_sz);
if (mode !== "split_budget") {
const n = Math.min(nA, nB);
nA = n;
nB = n;
nA = nSame;
nB = nSame;
}
const premium = costA * nA + costB * nB;
const ok = nA >= 1 && nB >= 1;
@@ -400,7 +453,7 @@
const line = $("hp-oo-budget-line");
if (!line) return;
const b = resolveOoBudget();
const sizeLabel = state.ooSheetsMode === "split_budget" ? "均分" : "同张数";
const sizeLabel = ooSizeModeLabel(state.ooSheetsMode);
const closeLabel = !state.ooCloseModeEnabled
? ""
: state.ooCloseMode === "hold_expiry"
@@ -447,8 +500,15 @@
} else if (d.oo_close_mode_default && !state._ooCloseModeTouched) {
state.ooCloseMode = d.oo_close_mode_default === "hold_expiry" ? "hold_expiry" : "close_all";
}
if (d.oo_bias_split_by != null) {
state.ooBiasSplitBy = d.oo_bias_split_by === "sheets" ? "sheets" : "budget";
}
if (d.oo_bias_ratio != null && Number(d.oo_bias_ratio) > 0 && Number(d.oo_bias_ratio) < 1) {
state.ooBiasRatio = Number(d.oo_bias_ratio);
}
syncOoCloseModeUI();
setGateLine(d);
if (state.mode === "options_options") autoFillOoSheets();
} catch (e) {
setGateLine({ can_preview: false, can_start: false, reasons: [e.message], is_full_margin: false });
}