Implement P1 local matcher/ledger and P2 strategy engine.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-24 17:03:46 +08:00
parent 0fca5f025e
commit 51b8bb8f8a
30 changed files with 2086 additions and 150 deletions
+153 -56
View File
@@ -1,5 +1,11 @@
import { FormEvent, useState } from "react";
import { changeCredentials, getUsername, setSession } from "../api/client";
import { FormEvent, useEffect, useState } from "react";
import {
changeCredentials,
getUsername,
setSession,
apiFetch,
StrategySettings,
} from "../api/client";
export default function SettingsPage() {
const [newUsername, setNewUsername] = useState(getUsername() || "admin");
@@ -10,7 +16,24 @@ export default function SettingsPage() {
const [ok, setOk] = useState("");
const [loading, setLoading] = useState(false);
async function onSave(e: FormEvent) {
const [fee, setFee] = useState(0.0005);
const [exitPts, setExitPts] = useState(30);
const [rest, setRest] = useState(300);
const [maxRounds, setMaxRounds] = useState(3);
const [stratOk, setStratOk] = useState("");
useEffect(() => {
apiFetch<StrategySettings>("/api/settings/strategy")
.then((s) => {
setFee(s.fee_rate);
setExitPts(s.exit_move_points);
setRest(s.rest_seconds);
setMaxRounds(s.max_rounds);
})
.catch(() => undefined);
}, []);
async function onSaveCreds(e: FormEvent) {
e.preventDefault();
setErr("");
setOk("");
@@ -41,60 +64,134 @@ export default function SettingsPage() {
}
}
async function onSaveStrategy(e: FormEvent) {
e.preventDefault();
setStratOk("");
setErr("");
try {
await apiFetch("/api/settings/strategy", {
method: "PUT",
body: JSON.stringify({
fee_rate: fee,
exit_move_points: exitPts,
rest_seconds: rest,
max_rounds: maxRounds,
}),
});
setStratOk("策略参数已保存");
} catch (ex) {
setErr(ex instanceof Error ? ex.message : String(ex));
}
}
return (
<div className="card" style={{ maxWidth: 560 }}>
<h2 style={{ marginTop: 0 }}></h2>
<p style={{ color: "var(--muted)" }}> .env</p>
{err ? <div className="err">{err}</div> : null}
{ok ? <div style={{ color: "var(--up)", marginBottom: 12 }}>{ok}</div> : null}
<form onSubmit={onSave}>
<div className="field">
<label htmlFor="user"></label>
<input
id="user"
value={newUsername}
onChange={(e) => setNewUsername(e.target.value)}
autoComplete="username"
required
/>
</div>
<div className="field">
<label htmlFor="cur"></label>
<input
id="cur"
type="password"
value={currentPassword}
onChange={(e) => setCurrentPassword(e.target.value)}
autoComplete="current-password"
required
/>
</div>
<div className="field">
<label htmlFor="np"></label>
<input
id="np"
type="password"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
autoComplete="new-password"
required
/>
</div>
<div className="field">
<label htmlFor="cp"></label>
<input
id="cp"
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
autoComplete="new-password"
required
/>
</div>
<button className="btn" type="submit" disabled={loading}>
{loading ? "保存中…" : "保存"}
</button>
</form>
<div style={{ display: "grid", gap: 16, maxWidth: 560 }}>
<div className="card">
<h2 style={{ marginTop: 0 }}></h2>
<p style={{ color: "var(--muted)" }}>
N =1×
</p>
{stratOk ? <div style={{ color: "var(--up)", marginBottom: 12 }}>{stratOk}</div> : null}
<form onSubmit={onSaveStrategy}>
<div className="field">
<label htmlFor="exit">EXIT_MOVE_POINTS</label>
<input
id="exit"
className="mono"
type="number"
step="1"
value={exitPts}
onChange={(e) => setExitPts(Number(e.target.value))}
/>
</div>
<div className="field">
<label htmlFor="rest">REST_SECONDS</label>
<input
id="rest"
className="mono"
type="number"
step="1"
value={rest}
onChange={(e) => setRest(Number(e.target.value))}
/>
</div>
<div className="field">
<label htmlFor="rounds">MAX_ROUNDS</label>
<input
id="rounds"
className="mono"
type="number"
step="1"
value={maxRounds}
onChange={(e) => setMaxRounds(Number(e.target.value))}
/>
</div>
<div className="field">
<label htmlFor="fee">FEE_RATE</label>
<input
id="fee"
className="mono"
type="number"
step="0.0001"
value={fee}
onChange={(e) => setFee(Number(e.target.value))}
/>
</div>
<button className="btn" type="submit">
</button>
</form>
</div>
<div className="card">
<h2 style={{ marginTop: 0 }}></h2>
{err ? <div className="err">{err}</div> : null}
{ok ? <div style={{ color: "var(--up)", marginBottom: 12 }}>{ok}</div> : null}
<form onSubmit={onSaveCreds}>
<div className="field">
<label htmlFor="user"></label>
<input
id="user"
value={newUsername}
onChange={(e) => setNewUsername(e.target.value)}
required
/>
</div>
<div className="field">
<label htmlFor="cur"></label>
<input
id="cur"
type="password"
value={currentPassword}
onChange={(e) => setCurrentPassword(e.target.value)}
required
/>
</div>
<div className="field">
<label htmlFor="np"></label>
<input
id="np"
type="password"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
required
/>
</div>
<div className="field">
<label htmlFor="cp"></label>
<input
id="cp"
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
/>
</div>
<button className="btn" type="submit" disabled={loading}>
{loading ? "保存中…" : "保存账号"}
</button>
</form>
</div>
</div>
);
}