initial: alpaclaudia paper-trading bot + dashboard
Python bot (bot/alpaclaudia): alpaca-py client, wheel strategy (CSP + covered calls) plus equity trailing stops, risk gates (cash buffer, cost-basis guard, per-symbol concentration cap), SQLite state log, Typer CLI (tick/loop/status/ report/dump-state), Discord daily report, pytest suite. Next.js 14 dashboard (dashboard/): read-only — reads the bot's SQLite directly and pulls live account/positions/orders from Alpaca. KPIs, equity chart, positions, bot-intents audit table, and orders table. Dark UI with Tailwind. systemd/: user-unit templates for the polling loop and the post-close report timer. docs/STRATEGY.md: wheel mechanics, risk invariants, later candidates. Defaults to BOT_MODE=dry — nothing is submitted to Alpaca until explicitly enabled in .env. ALPACA_ENV=paper by default; flipping to live requires an explicit second guard.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
Area,
|
||||
AreaChart,
|
||||
CartesianGrid,
|
||||
ResponsiveContainer,
|
||||
Tooltip,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from "recharts";
|
||||
|
||||
export type EquityPoint = { ts: string; equity: number; cash: number };
|
||||
|
||||
export function EquityChart({ data }: { data: EquityPoint[] }) {
|
||||
if (!data.length) {
|
||||
return (
|
||||
<div className="panel p-6 text-mute text-sm h-[300px] flex items-center justify-center">
|
||||
No ticks recorded yet — the bot hasn't run.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="panel p-4 h-[320px]">
|
||||
<div className="text-xs text-mute mb-2 uppercase tracking-wider">Equity (ticks)</div>
|
||||
<ResponsiveContainer width="100%" height="90%">
|
||||
<AreaChart data={data} margin={{ top: 8, right: 12, bottom: 0, left: 0 }}>
|
||||
<defs>
|
||||
<linearGradient id="g-equity" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#5eead4" stopOpacity={0.55} />
|
||||
<stop offset="95%" stopColor="#5eead4" stopOpacity={0.0} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<CartesianGrid stroke="#252a35" strokeDasharray="3 3" vertical={false} />
|
||||
<XAxis
|
||||
dataKey="ts"
|
||||
stroke="#8a94a6"
|
||||
fontSize={11}
|
||||
tickFormatter={(v: string) => v.slice(11, 16)}
|
||||
minTickGap={32}
|
||||
/>
|
||||
<YAxis
|
||||
stroke="#8a94a6"
|
||||
fontSize={11}
|
||||
width={64}
|
||||
tickFormatter={(v: number) => `$${(v / 1000).toFixed(1)}k`}
|
||||
domain={["auto", "auto"]}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
background: "#13161d",
|
||||
border: "1px solid #252a35",
|
||||
borderRadius: 8,
|
||||
fontSize: 12,
|
||||
}}
|
||||
labelStyle={{ color: "#8a94a6" }}
|
||||
formatter={(v: unknown) => {
|
||||
const n = Number(v);
|
||||
return [`$${n.toLocaleString("en-US", { maximumFractionDigits: 2 })}`, "Equity"];
|
||||
}}
|
||||
/>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="equity"
|
||||
stroke="#5eead4"
|
||||
strokeWidth={2}
|
||||
fill="url(#g-equity)"
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user