Show OO amplitude, Call/Put size, and min leverage in control.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -300,9 +300,15 @@ async def fleet_status(_tok: Annotated[str, Depends(require_fleet_token)]) -> di
|
|||||||
"perp_margin_mode": st.get("perp_margin_mode"),
|
"perp_margin_mode": st.get("perp_margin_mode"),
|
||||||
"perp_qty_eth": st.get("perp_qty_eth"),
|
"perp_qty_eth": st.get("perp_qty_eth"),
|
||||||
"option_qty_eth": st.get("option_qty_eth"),
|
"option_qty_eth": st.get("option_qty_eth"),
|
||||||
|
"oo_put_qty_eth": (
|
||||||
|
st.get("oo_put_qty_eth")
|
||||||
|
if st.get("oo_put_qty_eth") is not None
|
||||||
|
else _sf("oo_put_qty_eth", 0.0) or None
|
||||||
|
),
|
||||||
"sizing_mode": st.get("sizing_mode"),
|
"sizing_mode": st.get("sizing_mode"),
|
||||||
"risk_last_k": st.get("risk_last_k"),
|
"risk_last_k": st.get("risk_last_k"),
|
||||||
"risk_sizing_locked": st.get("risk_sizing_locked"),
|
"risk_sizing_locked": st.get("risk_sizing_locked"),
|
||||||
|
"risk_sizing_preview": st.get("risk_sizing_preview"),
|
||||||
"risk_loss_pct": _pick("risk_loss_pct", 1.0),
|
"risk_loss_pct": _pick("risk_loss_pct", 1.0),
|
||||||
"risk_perp_unit": _pick("risk_perp_unit", 1.0),
|
"risk_perp_unit": _pick("risk_perp_unit", 1.0),
|
||||||
"risk_option_unit": _pick("risk_option_unit", 2.0),
|
"risk_option_unit": _pick("risk_option_unit", 2.0),
|
||||||
@@ -310,6 +316,21 @@ async def fleet_status(_tok: Annotated[str, Depends(require_fleet_token)]) -> di
|
|||||||
"martingale_enabled": st.get("martingale_enabled"),
|
"martingale_enabled": st.get("martingale_enabled"),
|
||||||
"martingale_doubles": st.get("martingale_doubles"),
|
"martingale_doubles": st.get("martingale_doubles"),
|
||||||
"risk_effective_loss_pct": st.get("risk_effective_loss_pct"),
|
"risk_effective_loss_pct": st.get("risk_effective_loss_pct"),
|
||||||
|
"oo_amplitude_pct": _pick(
|
||||||
|
"oo_amplitude_pct", float(settings.oo_amplitude_pct)
|
||||||
|
),
|
||||||
|
"oo_amplitude_hours": _pick(
|
||||||
|
"oo_amplitude_hours", float(settings.oo_amplitude_hours)
|
||||||
|
),
|
||||||
|
"oo_min_option_hours": _pick(
|
||||||
|
"oo_min_option_hours", float(settings.oo_min_option_hours)
|
||||||
|
),
|
||||||
|
"oo_min_leverage": _pick(
|
||||||
|
"oo_min_leverage", float(settings.oo_min_leverage)
|
||||||
|
),
|
||||||
|
"oo_reward_ratio": _pick(
|
||||||
|
"oo_reward_ratio", float(settings.oo_reward_ratio)
|
||||||
|
),
|
||||||
"hedge_mode": (
|
"hedge_mode": (
|
||||||
hm
|
hm
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -175,24 +175,93 @@ function closeReasonZh(v: string): string {
|
|||||||
|
|
||||||
type RiskLines = {
|
type RiskLines = {
|
||||||
riskBased: boolean;
|
riskBased: boolean;
|
||||||
|
isOo: boolean;
|
||||||
sizing: string;
|
sizing: string;
|
||||||
sizingShort: string;
|
sizingShort: string;
|
||||||
lossPct: string | null;
|
lossPct: string | null;
|
||||||
exit: string;
|
exit: string;
|
||||||
openRatio: string | null;
|
openRatio: string | null;
|
||||||
/** 表格「风险/开仓」列:如 5%/1:2/12;手动为 —/名义比/时长 */
|
/** 表格「风险/开仓」列:永期 5%/1:2/12;期期 ≤2%·12h/C:P/≥24 */
|
||||||
riskOrOpen: string;
|
riskOrOpen: string;
|
||||||
|
riskOrOpenTitle: string;
|
||||||
minHours: string;
|
minHours: string;
|
||||||
leverage: string;
|
leverage: string;
|
||||||
|
leverageTitle: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isOptionOption(strat: Record<string, unknown>): boolean {
|
||||||
|
return (
|
||||||
|
String(strat.hedge_mode || "")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase() === "option_option"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function riskLines(strat: Record<string, unknown>): RiskLines {
|
function riskLines(strat: Record<string, unknown>): RiskLines {
|
||||||
|
const isOo = isOptionOption(strat);
|
||||||
const riskBased =
|
const riskBased =
|
||||||
strat.sizing_mode === "risk_based" || strat.risk_based === true;
|
strat.sizing_mode === "risk_based" || strat.risk_based === true;
|
||||||
|
|
||||||
|
if (isOo) {
|
||||||
|
const ampN = Number(strat.oo_amplitude_pct);
|
||||||
|
const ampH = Number(strat.oo_amplitude_hours);
|
||||||
|
const minH = Number(
|
||||||
|
strat.oo_min_option_hours ?? strat.min_option_hours,
|
||||||
|
);
|
||||||
|
const amp =
|
||||||
|
Number.isFinite(ampN) && Number.isFinite(ampH)
|
||||||
|
? `≤${fmt(ampN, ampN % 1 === 0 ? 0 : 1)}%·${fmt(ampH, 0)}h`
|
||||||
|
: "≤—";
|
||||||
|
const callQ =
|
||||||
|
strat.risk_sizing_preview &&
|
||||||
|
typeof strat.risk_sizing_preview === "object" &&
|
||||||
|
(strat.risk_sizing_preview as Record<string, unknown>).call_qty_eth !=
|
||||||
|
null
|
||||||
|
? (strat.risk_sizing_preview as Record<string, unknown>).call_qty_eth
|
||||||
|
: strat.option_qty_eth;
|
||||||
|
const putQ =
|
||||||
|
strat.risk_sizing_preview &&
|
||||||
|
typeof strat.risk_sizing_preview === "object" &&
|
||||||
|
(strat.risk_sizing_preview as Record<string, unknown>).put_qty_eth != null
|
||||||
|
? (strat.risk_sizing_preview as Record<string, unknown>).put_qty_eth
|
||||||
|
: strat.oo_put_qty_eth ?? strat.option_qty_eth;
|
||||||
|
const openRatio = `C${unitLabel(callQ)}:P${unitLabel(putQ)}`;
|
||||||
|
const minHours = Number.isFinite(minH) ? `≥${unitLabel(minH)}` : "≥—";
|
||||||
|
const ratioN = Number(strat.oo_reward_ratio);
|
||||||
|
const target = strat.exit_target_usdt ?? strat.net_profit_target;
|
||||||
|
let exit: string;
|
||||||
|
if (target != null && Number.isFinite(Number(target))) {
|
||||||
|
exit = `目标${fmt(target, 2)}U`;
|
||||||
|
} else if (Number.isFinite(ratioN)) {
|
||||||
|
exit = `预算×${fmt(ratioN, ratioN % 1 === 0 ? 0 : 2)}`;
|
||||||
|
} else {
|
||||||
|
exit = "预算×—";
|
||||||
|
}
|
||||||
|
const ooLev = Number(strat.oo_min_leverage);
|
||||||
|
const leverage = Number.isFinite(ooLev)
|
||||||
|
? `≥${Math.round(ooLev)}x`
|
||||||
|
: "≥—x";
|
||||||
|
return {
|
||||||
|
riskBased,
|
||||||
|
isOo: true,
|
||||||
|
sizing: riskBased ? "以损定仓(A)" : "手动开仓(B)",
|
||||||
|
sizingShort: riskBased ? "A" : "B",
|
||||||
|
lossPct: Number.isFinite(ampN)
|
||||||
|
? `≤${fmt(ampN, ampN % 1 === 0 ? 0 : 1)}%`
|
||||||
|
: null,
|
||||||
|
exit,
|
||||||
|
openRatio,
|
||||||
|
riskOrOpen: `${amp}/${openRatio}/${minHours}`,
|
||||||
|
riskOrOpenTitle: "振幅上限·回看 / Call:Put 数量 / 最短剩余(h)",
|
||||||
|
minHours,
|
||||||
|
leverage,
|
||||||
|
leverageTitle: "期期单腿最低杠杆",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const exitMode = String(strat.exit_mode || "fixed_usdt");
|
const exitMode = String(strat.exit_mode || "fixed_usdt");
|
||||||
let exit: string;
|
let exit: string;
|
||||||
if (riskBased) {
|
if (riskBased) {
|
||||||
// 必须读策略机 risk_exit_unit,禁止写死 15
|
|
||||||
exit =
|
exit =
|
||||||
strat.risk_exit_unit != null && Number.isFinite(Number(strat.risk_exit_unit))
|
strat.risk_exit_unit != null && Number.isFinite(Number(strat.risk_exit_unit))
|
||||||
? `基数${unitLabel(strat.risk_exit_unit)}`
|
? `基数${unitLabel(strat.risk_exit_unit)}`
|
||||||
@@ -224,14 +293,17 @@ function riskLines(strat: Record<string, unknown>): RiskLines {
|
|||||||
const riskOrOpen = `${lossPct || "—"}/${openRatio}/${minHours}`;
|
const riskOrOpen = `${lossPct || "—"}/${openRatio}/${minHours}`;
|
||||||
return {
|
return {
|
||||||
riskBased,
|
riskBased,
|
||||||
|
isOo: false,
|
||||||
sizing: riskBased ? "以损定仓(A)" : "手动开仓(B)",
|
sizing: riskBased ? "以损定仓(A)" : "手动开仓(B)",
|
||||||
sizingShort: riskBased ? "A" : "B",
|
sizingShort: riskBased ? "A" : "B",
|
||||||
lossPct,
|
lossPct,
|
||||||
exit,
|
exit,
|
||||||
openRatio,
|
openRatio,
|
||||||
riskOrOpen,
|
riskOrOpen,
|
||||||
|
riskOrOpenTitle: "风险比例 / 开仓比例 / 最小剩余时长(h)",
|
||||||
minHours,
|
minHours,
|
||||||
leverage: leveragePair(strat),
|
leverage: leveragePair(strat),
|
||||||
|
leverageTitle: "永续杠杆 / 期权最低杠杆",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,25 +388,44 @@ function RiskParamsBox({ strat }: { strat: Record<string, unknown> }) {
|
|||||||
<dt>定仓</dt>
|
<dt>定仓</dt>
|
||||||
<dd>{r.sizing}</dd>
|
<dd>{r.sizing}</dd>
|
||||||
</div>
|
</div>
|
||||||
|
{r.isOo ? (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<dt>振幅</dt>
|
||||||
|
<dd>{r.lossPct || "—"}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>Call:Put</dt>
|
||||||
|
<dd>{r.openRatio || "—"}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>剩余</dt>
|
||||||
|
<dd>{r.minHours}</dd>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
{r.lossPct != null ? (
|
{r.lossPct != null ? (
|
||||||
<div>
|
<div>
|
||||||
<dt>风险比例</dt>
|
<dt>风险比例</dt>
|
||||||
<dd>{r.lossPct}</dd>
|
<dd>{r.lossPct}</dd>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div>
|
|
||||||
<dt>出场</dt>
|
|
||||||
<dd>{r.exit}</dd>
|
|
||||||
</div>
|
|
||||||
{r.openRatio ? (
|
{r.openRatio ? (
|
||||||
<div>
|
<div>
|
||||||
<dt>开仓比例</dt>
|
<dt>开仓比例</dt>
|
||||||
<dd>{r.openRatio}</dd>
|
<dd>{r.openRatio}</dd>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<div>
|
<div>
|
||||||
<dt>杠杆</dt>
|
<dt>出场</dt>
|
||||||
<dd>{r.leverage}</dd>
|
<dd>{r.exit}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>{r.isOo ? "期权杠杆" : "杠杆"}</dt>
|
||||||
|
<dd title={r.leverageTitle}>{r.leverage}</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
@@ -832,12 +923,14 @@ export default function MonitorPage() {
|
|||||||
<td className="mono">{s.rounds ?? "—"}</td>
|
<td className="mono">{s.rounds ?? "—"}</td>
|
||||||
<td
|
<td
|
||||||
className="mono"
|
className="mono"
|
||||||
title="风险比例 / 开仓比例 / 最小剩余时长(h)"
|
title={risk.riskOrOpenTitle}
|
||||||
>
|
>
|
||||||
{risk.riskOrOpen}
|
{risk.riskOrOpen}
|
||||||
</td>
|
</td>
|
||||||
<td className="mono">{risk.exit}</td>
|
<td className="mono">{risk.exit}</td>
|
||||||
<td className="mono">{risk.leverage}</td>
|
<td className="mono" title={risk.leverageTitle}>
|
||||||
|
{risk.leverage}
|
||||||
|
</td>
|
||||||
<td
|
<td
|
||||||
className="col-actions"
|
className="col-actions"
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
@@ -996,16 +1089,20 @@ export default function MonitorPage() {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>定仓</th>
|
<th>定仓</th>
|
||||||
{r.lossPct != null ? <th>风险比例</th> : null}
|
{r.lossPct != null ? (
|
||||||
|
<th>{r.isOo ? "振幅" : "风险比例"}</th>
|
||||||
|
) : null}
|
||||||
<th>出场</th>
|
<th>出场</th>
|
||||||
{r.openRatio ? <th>开仓比例</th> : null}
|
{r.openRatio ? (
|
||||||
<th>杠杆</th>
|
<th>{r.isOo ? "Call:Put" : "开仓比例"}</th>
|
||||||
|
) : null}
|
||||||
|
<th>{r.isOo ? "期权杠杆" : "杠杆"}</th>
|
||||||
<th>状态</th>
|
<th>状态</th>
|
||||||
<th>模式/交易所</th>
|
<th>模式/交易所</th>
|
||||||
<th>轮次</th>
|
<th>轮次</th>
|
||||||
<th>组 ID</th>
|
<th>组 ID</th>
|
||||||
<th>保证金</th>
|
{!r.isOo ? <th>保证金</th> : null}
|
||||||
<th>名义永续/期权</th>
|
<th>{r.isOo ? "数量 Call/Put" : "名义永续/期权"}</th>
|
||||||
<th>出场目标</th>
|
<th>出场目标</th>
|
||||||
<th>指数</th>
|
<th>指数</th>
|
||||||
<th>合约对</th>
|
<th>合约对</th>
|
||||||
@@ -1017,7 +1114,7 @@ export default function MonitorPage() {
|
|||||||
{r.lossPct != null ? <td>{r.lossPct}</td> : null}
|
{r.lossPct != null ? <td>{r.lossPct}</td> : null}
|
||||||
<td>{r.exit}</td>
|
<td>{r.exit}</td>
|
||||||
{r.openRatio ? <td>{r.openRatio}</td> : null}
|
{r.openRatio ? <td>{r.openRatio}</td> : null}
|
||||||
<td>{r.leverage}</td>
|
<td title={r.leverageTitle}>{r.leverage}</td>
|
||||||
<td>
|
<td>
|
||||||
{detailRunning ? "运行中" : "已停"} · {detail.phase}
|
{detailRunning ? "运行中" : "已停"} · {detail.phase}
|
||||||
</td>
|
</td>
|
||||||
@@ -1036,13 +1133,27 @@ export default function MonitorPage() {
|
|||||||
"—",
|
"—",
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td>{String(detail.strat.perp_margin_mode || "—")}</td>
|
{!r.isOo ? (
|
||||||
<td>
|
<td>
|
||||||
{fmt(detail.strat.perp_qty_eth, 4)} /{" "}
|
{String(detail.strat.perp_margin_mode || "—")}
|
||||||
{fmt(detail.strat.option_qty_eth, 4)} ETH
|
</td>
|
||||||
|
) : null}
|
||||||
|
<td>
|
||||||
|
{r.isOo
|
||||||
|
? `${fmt(detail.strat.option_qty_eth, 2)} / ${fmt(
|
||||||
|
detail.strat.oo_put_qty_eth ??
|
||||||
|
detail.strat.option_qty_eth,
|
||||||
|
2,
|
||||||
|
)} ETH`
|
||||||
|
: `${fmt(detail.strat.perp_qty_eth, 4)} / ${fmt(
|
||||||
|
detail.strat.option_qty_eth,
|
||||||
|
4,
|
||||||
|
)} ETH`}
|
||||||
</td>
|
</td>
|
||||||
<td>{fmt(detail.strat.exit_target_usdt, 2)} USDT</td>
|
<td>{fmt(detail.strat.exit_target_usdt, 2)} USDT</td>
|
||||||
<td className="mono">{fmtExPx("perp", detail.index_px)}</td>
|
<td className="mono">
|
||||||
|
{fmtExPx("perp", detail.index_px)}
|
||||||
|
</td>
|
||||||
<td className="mono">
|
<td className="mono">
|
||||||
{detail.pair
|
{detail.pair
|
||||||
? `${detail.pair.expiry_ymd || "?"} @ ${
|
? `${detail.pair.expiry_ymd || "?"} @ ${
|
||||||
|
|||||||
Reference in New Issue
Block a user