Drive semi quote ladder by min remaining hours input.
Picks nearest expiry with hours >= threshold and refreshes the list as the field changes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+29
-13
@@ -131,6 +131,8 @@ export default function PlanPage() {
|
||||
side?: string;
|
||||
index_px?: number | null;
|
||||
expiry_ymd?: string;
|
||||
hours_left?: number | null;
|
||||
min_hours?: number;
|
||||
atm_strike?: number;
|
||||
rows?: Array<{
|
||||
strike: number;
|
||||
@@ -141,6 +143,7 @@ export default function PlanPage() {
|
||||
lev: number | null;
|
||||
}>;
|
||||
} | null>(null);
|
||||
const semiMinHRef = useRef(30);
|
||||
|
||||
const semiParamsBody = () => ({
|
||||
semi_view_side: semiView,
|
||||
@@ -186,9 +189,10 @@ export default function PlanPage() {
|
||||
? "short"
|
||||
: "long";
|
||||
const optSide = viewSide === "short" ? "put" : "call";
|
||||
const minH = semiMinHRef.current;
|
||||
setLadder(
|
||||
await apiFetch<NonNullable<typeof ladder>>(
|
||||
`/api/market/option-ladder?wings=5&side=${optSide}`,
|
||||
`/api/market/option-ladder?wings=5&side=${optSide}&min_hours=${minH}`,
|
||||
),
|
||||
);
|
||||
} catch {
|
||||
@@ -249,6 +253,10 @@ export default function PlanPage() {
|
||||
semiViewRef.current = semiView;
|
||||
}, [semiView]);
|
||||
|
||||
useEffect(() => {
|
||||
semiMinHRef.current = Number(semiMinH) > 0 ? Number(semiMinH) : 30;
|
||||
}, [semiMinH]);
|
||||
|
||||
useEffect(() => {
|
||||
refresh();
|
||||
const t = window.setInterval(refresh, 1500);
|
||||
@@ -327,25 +335,29 @@ export default function PlanPage() {
|
||||
const semiOn = !!plan?.semi_auto_enabled && !isOo;
|
||||
const showAmpCard = plan?.oo_amplitude_filter_enabled === true;
|
||||
|
||||
// 看法切换时立刻拉 Call/Put 列表
|
||||
// 看法 / 最短小时变化时立刻刷新报价链(小时输入防抖)
|
||||
useEffect(() => {
|
||||
if (!semiOn || planTab !== "semi") return;
|
||||
const optSide = semiView === "short" ? "put" : "call";
|
||||
const minH = Number(semiMinH) > 0 ? Number(semiMinH) : 30;
|
||||
let cancelled = false;
|
||||
void (async () => {
|
||||
try {
|
||||
const lad = await apiFetch<NonNullable<typeof ladder>>(
|
||||
`/api/market/option-ladder?wings=5&side=${optSide}`,
|
||||
);
|
||||
if (!cancelled) setLadder(lad);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
})();
|
||||
const t = window.setTimeout(() => {
|
||||
void (async () => {
|
||||
try {
|
||||
const lad = await apiFetch<NonNullable<typeof ladder>>(
|
||||
`/api/market/option-ladder?wings=5&side=${optSide}&min_hours=${minH}`,
|
||||
);
|
||||
if (!cancelled) setLadder(lad);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
})();
|
||||
}, 350);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
window.clearTimeout(t);
|
||||
};
|
||||
}, [semiView, semiOn, planTab]);
|
||||
}, [semiView, semiMinH, semiOn, planTab]);
|
||||
const exitMode = plan?.exit_mode ?? "fixed_usdt";
|
||||
const riskBased =
|
||||
plan?.risk_based === true || plan?.sizing_mode === "risk_based";
|
||||
@@ -893,6 +905,10 @@ export default function PlanPage() {
|
||||
{ladder?.expiry_ymd
|
||||
? `到期 ${ladder.expiry_ymd}`
|
||||
: "—"}
|
||||
{ladder?.hours_left != null
|
||||
? ` · 剩余≈${fmt(ladder.hours_left, 1)}h`
|
||||
: ""}
|
||||
{` · 门槛≥${fmt(Number(semiMinH) || 30, 0)}h`}
|
||||
{ladder?.atm_strike != null
|
||||
? ` · ATM ${Math.round(ladder.atm_strike)}`
|
||||
: ""}
|
||||
|
||||
Reference in New Issue
Block a user