fix: remove broken capital controls, reset now queries broker for real balance

- Removed PUT /api/trading/capital (set capital) — only touched in-memory state
- Removed POST /api/trading/capital/adjust (add/withdraw) — same problem
- Reset endpoint now: liquidates Alpaca positions, cancels orders, clears DB,
  then queries Alpaca for real portfolio_value to set engine capital
- Frontend: replaced CapitalCard with simple ResetCard (one button)
- Removed useSetTradingCapital and useAdjustCapital hooks
This commit is contained in:
Celes Renata
2026-04-17 04:24:10 +00:00
parent 5fb59b379c
commit fd862da29e
4 changed files with 69 additions and 284 deletions
-9
View File
@@ -426,15 +426,6 @@ export function useSetTradingMode() {
});
}
export function useSetTradingCapital() {
const qc = useQueryClient();
return useMutation({
mutationFn: (initial_capital: number) =>
apiPut<{ initial_capital: number; active_pool: number; reserve_pool: number }>('trading', '/api/trading/capital', { initial_capital }),
onSuccess: () => qc.invalidateQueries({ queryKey: ['trading-config'] }),
});
}
export function usePendingApprovals() {
return useGet<Approval[]>(['pending-approvals'], 'query', '/api/admin/trading/approvals');
}
+4 -18
View File
@@ -309,12 +309,13 @@ export function useBacktestLaunch() {
});
}
/** Full paper trading reset: clears all positions, orders, decisions, and resets capital. */
/** Full paper trading reset: liquidates broker positions, cancels orders,
* clears all local trading state, and syncs capital from the broker. */
export function useResetPaperTrading() {
const qc = useQueryClient();
return useMutation({
mutationFn: (initial_capital: number) =>
apiPost<{ reset: boolean; initial_capital: number; active_pool: number; reserve_pool: number }>(
mutationFn: (initial_capital: number = 0) =>
apiPost<{ reset: boolean; initial_capital: number; active_pool: number; reserve_pool: number; broker: Record<string, number> }>(
'trading', '/api/trading/reset', { initial_capital },
),
onSuccess: () => {
@@ -326,21 +327,6 @@ export function useResetPaperTrading() {
});
}
/** Add or subtract capital from the active pool without resetting anything else. */
export function useAdjustCapital() {
const qc = useQueryClient();
return useMutation({
mutationFn: (amount: number) =>
apiPost<{ adjusted: number; active_pool: number; reserve_pool: number; total_value: number }>(
'trading', '/api/trading/capital/adjust', { amount },
),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['trading-status'] });
qc.invalidateQueries({ queryKey: ['trading-metrics'] });
},
});
}
/** Update notification preferences. */
export function useUpdateNotificationConfig() {
const qc = useQueryClient();
+25 -138
View File
@@ -2,7 +2,6 @@ import { useState } from 'react';
import {
useTradingConfig,
useSetTradingMode,
useSetTradingCapital,
usePendingApprovals,
useReviewApproval,
useActiveLockouts,
@@ -11,7 +10,7 @@ import {
useCompetitiveStatus,
useToggleCompetitive,
} from '../api/hooks';
import { useResetPaperTrading, useAdjustCapital } from '../api/tradingHooks';
import { useResetPaperTrading } from '../api/tradingHooks';
import { StatusBadge, LoadingSpinner, Card } from '../components/ui';
export function TradingPage() {
@@ -21,9 +20,7 @@ export function TradingPage() {
const { data: macroStatus } = useMacroStatus();
const { data: competitiveStatus } = useCompetitiveStatus();
const setMode = useSetTradingMode();
const setCapital = useSetTradingCapital();
const resetTrading = useResetPaperTrading();
const adjustCapital = useAdjustCapital();
const reviewApproval = useReviewApproval();
const toggleMacro = useToggleMacro();
const toggleCompetitive = useToggleCompetitive();
@@ -92,14 +89,10 @@ export function TradingPage() {
)}
</Card>
{/* Paper Trading Capital */}
<CapitalCard
onSetCapital={(amount) => setCapital.mutate(amount)}
onReset={(amount) => resetTrading.mutate(amount)}
onAdjust={(amount) => adjustCapital.mutate(amount)}
isPending={setCapital.isPending}
{/* Paper Trading Reset */}
<ResetCard
onReset={() => resetTrading.mutate(0)}
isResetting={resetTrading.isPending}
isAdjusting={adjustCapital.isPending}
/>
{/* Macro Signal Layer Toggle */}
@@ -309,77 +302,45 @@ function ApprovalRow({ approval, onReview }: {
}
function CapitalCard({ onSetCapital, onReset, onAdjust, isPending, isResetting, isAdjusting }: {
onSetCapital: (amount: number) => void;
onReset: (amount: number) => void;
onAdjust: (amount: number) => void;
isPending: boolean;
function ResetCard({ onReset, isResetting }: {
onReset: () => void;
isResetting: boolean;
isAdjusting: boolean;
}) {
const [amount, setAmount] = useState('100000');
const [adjustAmount, setAdjustAmount] = useState('');
const [showConfirm, setShowConfirm] = useState(false);
const [showResetConfirm, setShowResetConfirm] = useState(false);
const presets = [10000, 50000, 100000, 500000];
const busy = isPending || isResetting || isAdjusting;
return (
<Card>
<h2 className="mb-3 text-sm font-medium text-gray-400">Paper Trading Capital</h2>
{/* Set Capital */}
<div className="flex flex-wrap items-end gap-3">
<h2 className="mb-3 text-sm font-medium text-gray-400">Paper Trading Account</h2>
<div className="flex items-center justify-between">
<div>
<label htmlFor="capital-input" className="mb-1 block text-xs text-gray-500">Initial Capital ($)</label>
<input
id="capital-input"
type="number"
min="100"
step="1000"
value={amount}
onChange={(e) => setAmount(e.target.value)}
className="w-40 rounded-md border border-surface-700 bg-surface-900 px-3 py-1.5 text-sm text-gray-200 focus:border-brand-500 focus:outline-none"
/>
</div>
<div className="flex gap-2">
{presets.map((p) => (
<button
key={p}
onClick={() => setAmount(String(p))}
className={`rounded-md border px-2 py-1.5 text-xs font-medium ${
amount === String(p)
? 'border-brand-500 bg-brand-600/20 text-brand-300'
: 'border-surface-700 bg-surface-900 text-gray-400 hover:bg-surface-800'
}`}
>
${(p / 1000).toFixed(0)}k
</button>
))}
<p className="text-sm text-gray-300">Full Reset</p>
<p className="text-[10px] text-gray-600">
Liquidates all broker positions, cancels open orders, wipes local trading history,
and syncs capital from the broker account.
</p>
</div>
<button
onClick={() => setShowConfirm(true)}
disabled={busy || !amount || Number(amount) <= 0}
className="rounded-md bg-brand-600 px-4 py-1.5 text-sm font-medium text-white hover:bg-brand-700 disabled:opacity-50"
disabled={isResetting}
className="rounded-md border border-red-700/50 bg-red-900/20 px-3 py-1.5 text-sm font-medium text-red-400 hover:bg-red-900/40 disabled:opacity-50"
>
Set Capital
Reset Everything
</button>
</div>
{showConfirm && (
<div className="mt-4 rounded-lg border border-orange-700/50 bg-orange-900/20 p-4">
<p className="text-sm text-orange-300">
This will reset the paper trading pools to <span className="font-semibold">${Number(amount).toLocaleString()}</span>.
Any existing pool balances will be overwritten.
<div className="mt-3 rounded-lg border border-red-700/50 bg-red-900/20 p-4">
<p className="text-sm text-red-300">
This will <span className="font-semibold">permanently delete</span> all positions, orders,
trading decisions, stop levels, portfolio snapshots, and backtest data.
All broker positions will be liquidated and capital will be set from the broker&apos;s account balance.
</p>
<div className="mt-3 flex gap-2">
<button
onClick={() => { onSetCapital(Number(amount)); setShowConfirm(false); }}
disabled={busy}
className="rounded-md bg-orange-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-orange-700 disabled:opacity-50"
onClick={() => { onReset(); setShowConfirm(false); }}
disabled={isResetting}
className="rounded-md bg-red-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-red-700 disabled:opacity-50"
>
{isPending ? 'Setting…' : 'Confirm'}
{isResetting ? 'Resetting…' : 'Yes, Reset Everything'}
</button>
<button
onClick={() => setShowConfirm(false)}
@@ -390,80 +351,6 @@ function CapitalCard({ onSetCapital, onReset, onAdjust, isPending, isResetting,
</div>
</div>
)}
{/* Add / Subtract Capital */}
<div className="mt-4 border-t border-surface-700 pt-4">
<h3 className="mb-2 text-xs font-medium text-gray-500">Adjust Capital</h3>
<div className="flex items-end gap-2">
<div>
<label htmlFor="adjust-input" className="mb-1 block text-xs text-gray-500">Amount ($)</label>
<input
id="adjust-input"
type="number"
step="100"
value={adjustAmount}
onChange={(e) => setAdjustAmount(e.target.value)}
placeholder="5000"
className="w-36 rounded-md border border-surface-700 bg-surface-900 px-3 py-1.5 text-sm text-gray-200 placeholder-gray-600 focus:border-brand-500 focus:outline-none"
/>
</div>
<button
onClick={() => { onAdjust(Math.abs(Number(adjustAmount))); setAdjustAmount(''); }}
disabled={busy || !adjustAmount || Number(adjustAmount) <= 0}
className="rounded-md bg-green-700 px-3 py-1.5 text-sm font-medium text-white hover:bg-green-600 disabled:opacity-50"
>
+ Add
</button>
<button
onClick={() => { onAdjust(-Math.abs(Number(adjustAmount))); setAdjustAmount(''); }}
disabled={busy || !adjustAmount || Number(adjustAmount) <= 0}
className="rounded-md bg-red-700 px-3 py-1.5 text-sm font-medium text-white hover:bg-red-600 disabled:opacity-50"
>
Withdraw
</button>
</div>
{isAdjusting && <p className="mt-1 text-xs text-gray-500">Adjusting</p>}
</div>
{/* Full Reset */}
<div className="mt-4 border-t border-surface-700 pt-4">
<div className="flex items-center justify-between">
<div>
<h3 className="text-xs font-medium text-gray-500">Full Reset</h3>
<p className="text-[10px] text-gray-600">Wipes all positions, orders, decisions, and history. Resets capital to the amount above.</p>
</div>
<button
onClick={() => setShowResetConfirm(true)}
disabled={busy || !amount || Number(amount) <= 0}
className="rounded-md border border-red-700/50 bg-red-900/20 px-3 py-1.5 text-sm font-medium text-red-400 hover:bg-red-900/40 disabled:opacity-50"
>
Reset Everything
</button>
</div>
{showResetConfirm && (
<div className="mt-3 rounded-lg border border-red-700/50 bg-red-900/20 p-4">
<p className="text-sm text-red-300">
This will <span className="font-semibold">permanently delete</span> all positions, orders, trading decisions, stop levels, portfolio snapshots, and backtest data.
Capital will be reset to <span className="font-semibold">${Number(amount).toLocaleString()}</span>.
</p>
<div className="mt-3 flex gap-2">
<button
onClick={() => { onReset(Number(amount)); setShowResetConfirm(false); }}
disabled={busy}
className="rounded-md bg-red-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-red-700 disabled:opacity-50"
>
{isResetting ? 'Resetting…' : 'Yes, Reset Everything'}
</button>
<button
onClick={() => setShowResetConfirm(false)}
className="rounded-md border border-surface-700 px-3 py-1.5 text-sm text-gray-400 hover:bg-surface-800"
>
Cancel
</button>
</div>
</div>
)}
</div>
</Card>
);
}