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,40 @@
|
||||
export function money(v: number | string | null | undefined, opts?: { sign?: boolean }): string {
|
||||
const n = typeof v === "string" ? Number(v) : v ?? 0;
|
||||
if (n === null || Number.isNaN(n)) return "—";
|
||||
const f = n.toLocaleString("en-US", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
});
|
||||
if (opts?.sign && n > 0) return `+${f}`;
|
||||
return f;
|
||||
}
|
||||
|
||||
export function pct(v: number | string | null | undefined, digits = 2): string {
|
||||
const n = typeof v === "string" ? Number(v) : v ?? 0;
|
||||
if (Number.isNaN(n)) return "—";
|
||||
return `${(n * 100).toFixed(digits)}%`;
|
||||
}
|
||||
|
||||
export function num(v: number | string | null | undefined, digits = 2): string {
|
||||
const n = typeof v === "string" ? Number(v) : v ?? 0;
|
||||
if (Number.isNaN(n)) return "—";
|
||||
return n.toLocaleString("en-US", {
|
||||
minimumFractionDigits: digits,
|
||||
maximumFractionDigits: digits,
|
||||
});
|
||||
}
|
||||
|
||||
export function timeAgo(iso: string): string {
|
||||
const t = new Date(iso).getTime();
|
||||
if (!t) return "—";
|
||||
const sec = Math.round((Date.now() - t) / 1000);
|
||||
if (sec < 60) return `${sec}s ago`;
|
||||
const min = Math.round(sec / 60);
|
||||
if (min < 60) return `${min}m ago`;
|
||||
const hr = Math.round(min / 60);
|
||||
if (hr < 24) return `${hr}h ago`;
|
||||
const day = Math.round(hr / 24);
|
||||
return `${day}d ago`;
|
||||
}
|
||||
Reference in New Issue
Block a user