Add weekend open skip, expiry force-close, and Chinese trade labels.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -120,6 +120,7 @@ export type PlanState = {
|
||||
window_key: string | null;
|
||||
rest_left_sec: number;
|
||||
rest_seconds: number;
|
||||
skip_weekends?: boolean;
|
||||
exit_move_pct?: number;
|
||||
exit_mode: "fixed_usdt" | "premium_multiple";
|
||||
net_profit_target: number;
|
||||
@@ -171,6 +172,7 @@ export type StrategySettings = {
|
||||
net_profit_target: number;
|
||||
premium_exit_multiple: number;
|
||||
rest_seconds: number;
|
||||
skip_weekends: boolean;
|
||||
initial_equity: number;
|
||||
leverage: number;
|
||||
min_option_hours: number;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/** 交易相关英文码 → 中文展示(库内仍存英文)。 */
|
||||
|
||||
const STATUS_ZH: Record<string, string> = {
|
||||
open: "持仓中",
|
||||
closed: "已平仓",
|
||||
};
|
||||
|
||||
const SIDE_ZH: Record<string, string> = {
|
||||
long: "多",
|
||||
short: "空",
|
||||
call: "看涨",
|
||||
put: "看跌",
|
||||
};
|
||||
|
||||
const LEG_ZH: Record<string, string> = {
|
||||
perp: "永续",
|
||||
option: "期权",
|
||||
};
|
||||
|
||||
const ACTION_ZH: Record<string, string> = {
|
||||
open: "开仓",
|
||||
close: "平仓",
|
||||
};
|
||||
|
||||
const CLOSE_REASON_ZH: Record<string, string> = {
|
||||
fixed_usdt: "固定净盈利达标",
|
||||
premium_multiple: "权利金倍数达标",
|
||||
expiry: "到期自动全平",
|
||||
emergency: "紧急全平",
|
||||
manual: "手动平仓",
|
||||
liquidity_retry: "流动性等待后续平仓",
|
||||
unknown: "未知",
|
||||
};
|
||||
|
||||
export function statusZh(v: string | null | undefined): string {
|
||||
if (!v) return "—";
|
||||
return STATUS_ZH[v] || v;
|
||||
}
|
||||
|
||||
export function sideZh(v: string | null | undefined): string {
|
||||
if (!v) return "—";
|
||||
return SIDE_ZH[v] || v;
|
||||
}
|
||||
|
||||
/** 永续方向 / 期权方向,如「多/看跌」 */
|
||||
export function positionSidesZh(
|
||||
perp: string | null | undefined,
|
||||
option: string | null | undefined,
|
||||
): string {
|
||||
return `${sideZh(perp)}/${sideZh(option)}`;
|
||||
}
|
||||
|
||||
export function fillDescZh(leg: string, action: string, side: string): string {
|
||||
const l = LEG_ZH[leg] || leg;
|
||||
const a = ACTION_ZH[action] || action;
|
||||
const s = SIDE_ZH[side] || side;
|
||||
// 期权买入开仓:「期权开多」;永续:「永续开多/开空」
|
||||
if (leg === "option" && action === "open") return "期权开多";
|
||||
if (leg === "option" && action === "close") return "期权平多";
|
||||
if (leg === "perp" && action === "open") return s === "多" ? "永续开多" : "永续开空";
|
||||
if (leg === "perp" && action === "close") return s === "多" ? "永续平多" : "永续平空";
|
||||
return `${l}${a}${s}`;
|
||||
}
|
||||
|
||||
export function closeReasonZh(v: string | null | undefined): string {
|
||||
if (!v) return "—";
|
||||
return CLOSE_REASON_ZH[v] || v;
|
||||
}
|
||||
@@ -44,6 +44,7 @@ const PHASE_ZH: Record<string, string> = {
|
||||
stopped: "已停开",
|
||||
outside_window: "窗外",
|
||||
liquidity_wait: "流动性等待",
|
||||
weekend_skip: "周末跳过",
|
||||
};
|
||||
|
||||
export default function PlanPage() {
|
||||
@@ -123,7 +124,7 @@ export default function PlanPage() {
|
||||
<div>
|
||||
<h2 style={{ marginTop: 0 }}>自动对冲计划</h2>
|
||||
<p style={{ color: "var(--muted)", marginTop: -8 }}>
|
||||
SIM 本地撮合 · 期权只买 · 剩余时长选到期 · 净盈利达标全平
|
||||
SIM 本地撮合 · 期权只买 · 净盈利达标或到期全平
|
||||
</p>
|
||||
{err ? <div className="err">{err}</div> : null}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ export default function SettingsPage() {
|
||||
const [netTarget, setNetTarget] = useState(15);
|
||||
const [premMult, setPremMult] = useState(1);
|
||||
const [rest, setRest] = useState(300);
|
||||
const [skipWeekends, setSkipWeekends] = useState(true);
|
||||
const [leverage, setLeverage] = useState(3);
|
||||
const [minHours, setMinHours] = useState(12);
|
||||
const [minOptLev, setMinOptLev] = useState(100);
|
||||
@@ -43,6 +44,7 @@ export default function SettingsPage() {
|
||||
setNetTarget(s.net_profit_target ?? 15);
|
||||
setPremMult(s.premium_exit_multiple ?? 1);
|
||||
setRest(s.rest_seconds);
|
||||
setSkipWeekends(s.skip_weekends !== false);
|
||||
setLeverage(s.leverage ?? 3);
|
||||
setMinHours(s.min_option_hours ?? 12);
|
||||
setMinOptLev(s.min_option_leverage ?? 100);
|
||||
@@ -97,6 +99,7 @@ export default function SettingsPage() {
|
||||
net_profit_target: netTarget,
|
||||
premium_exit_multiple: premMult,
|
||||
rest_seconds: rest,
|
||||
skip_weekends: skipWeekends,
|
||||
leverage,
|
||||
min_option_hours: minHours,
|
||||
min_option_leverage: minOptLev,
|
||||
@@ -134,7 +137,7 @@ export default function SettingsPage() {
|
||||
{tab === "strategy" ? (
|
||||
<div className="card settings-card">
|
||||
<p className="settings-lead">
|
||||
无开仓时间窗;期权按剩余时长选到期 → 平值 → 校验杠杆;出场可选固定金额或权利金倍数。
|
||||
无开仓时间窗;可选周六日跳过开仓;期权按剩余时长选到期 → 平值 → 校验杠杆;出场可选固定金额或权利金倍数。
|
||||
</p>
|
||||
{stratOk ? <div className="settings-ok">{stratOk}</div> : null}
|
||||
{err && tab === "strategy" ? <div className="err">{err}</div> : null}
|
||||
@@ -282,6 +285,18 @@ export default function SettingsPage() {
|
||||
onChange={(e) => setRest(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="skipWe">周六日跳过开仓(上海时区)</label>
|
||||
<select
|
||||
id="skipWe"
|
||||
className="mono"
|
||||
value={skipWeekends ? "1" : "0"}
|
||||
onChange={(e) => setSkipWeekends(e.target.value === "1")}
|
||||
>
|
||||
<option value="1">开启(推荐)</option>
|
||||
<option value="0">关闭</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label htmlFor="fee">手续费率</label>
|
||||
<input
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { apiFetch } from "../api/client";
|
||||
import { closeReasonZh } from "../labels";
|
||||
|
||||
type Summary = {
|
||||
groups: number;
|
||||
@@ -22,6 +23,10 @@ export default function StatsPage() {
|
||||
.catch((e) => setErr(e instanceof Error ? e.message : String(e)));
|
||||
}, []);
|
||||
|
||||
const reasonEntries = s
|
||||
? Object.entries(s.close_reasons).sort((a, b) => b[1] - a[1])
|
||||
: [];
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<h2 style={{ marginTop: 0 }}>统计</h2>
|
||||
@@ -46,9 +51,17 @@ export default function StatsPage() {
|
||||
{s.total_fees.toFixed(2)} / {s.total_slip.toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="kv">
|
||||
<div className="kv" style={{ alignItems: "flex-start" }}>
|
||||
<span>平仓原因</span>
|
||||
<span className="mono">{JSON.stringify(s.close_reasons)}</span>
|
||||
<span className="mono" style={{ textAlign: "right" }}>
|
||||
{reasonEntries.length === 0
|
||||
? "—"
|
||||
: reasonEntries.map(([k, n]) => (
|
||||
<div key={k}>
|
||||
{closeReasonZh(k)} × {n}
|
||||
</div>
|
||||
))}
|
||||
</span>
|
||||
</div>
|
||||
<h3>按组盈亏</h3>
|
||||
{s.equity_curve.map((x) => (
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { apiFetch } from "../api/client";
|
||||
import {
|
||||
closeReasonZh,
|
||||
fillDescZh,
|
||||
positionSidesZh,
|
||||
statusZh,
|
||||
} from "../labels";
|
||||
|
||||
type Group = {
|
||||
group_id: string;
|
||||
@@ -62,10 +68,12 @@ export default function TradesPage() {
|
||||
onClick={() => openGroup(g.group_id)}
|
||||
>
|
||||
<span className="mono">
|
||||
{g.group_id} · {g.status} · {g.perp_side}/{g.option_side}
|
||||
{g.group_id} · {statusZh(g.status)} ·{" "}
|
||||
{positionSidesZh(g.perp_side, g.option_side)}
|
||||
</span>
|
||||
<span className="mono">
|
||||
PnL {Number(g.realized_pnl || 0).toFixed(2)} · {g.close_reason || "—"}
|
||||
盈亏 {Number(g.realized_pnl || 0).toFixed(2)} ·{" "}
|
||||
{closeReasonZh(g.close_reason)}
|
||||
</span>
|
||||
</div>
|
||||
))
|
||||
@@ -77,10 +85,11 @@ export default function TradesPage() {
|
||||
{fills.map((f) => (
|
||||
<div key={f.id} className="kv">
|
||||
<span className="mono">
|
||||
{f.leg} {f.action} {f.side}
|
||||
{fillDescZh(f.leg, f.action, f.side)}
|
||||
</span>
|
||||
<span className="mono">
|
||||
px {f.fill_px.toFixed(4)} · qty {f.qty_eth} · fee {f.fee.toFixed(4)}
|
||||
价 {f.fill_px.toFixed(4)} · 数量 {f.qty_eth} · 手续费{" "}
|
||||
{f.fee.toFixed(4)}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user