feat: enhanced paper trading reset with capital and reserve controls
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-1 Pipeline was successful
ci/woodpecker/push/build-2 Pipeline was successful
ci/woodpecker/push/build-3 Pipeline was successful
ci/woodpecker/push/finalize Pipeline was successful
Build and Push / lint-and-test (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.adapters.broker_adapter name:broker-adapter]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.aggregation.worker name:aggregation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.extractor.worker name:extractor]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.ingestion.worker name:ingestion]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.lake_publisher.worker name:lake-publisher]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.parser.worker name:parser]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.recommendation.worker name:recommendation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.scheduler.app name:scheduler]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.api.app:app --host 0.0.0.0 --port 8000 name:query-api]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.risk.app:app --host 0.0.0.0 --port 8000 name:risk]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.symbol_registry.app:app --host 0.0.0.0 --port 8000 name:symbol-registry]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.trading.app:app --host 0.0.0.0 --port 8000 name:trading-engine]) (push) Has been cancelled
Build and Push / build-dashboard (push) Has been cancelled
Build and Push / build-superset (push) Has been cancelled
Build and Push / integration-test (push) Has been cancelled
Build and Push / beta-gate (push) Has been cancelled
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-1 Pipeline was successful
ci/woodpecker/push/build-2 Pipeline was successful
ci/woodpecker/push/build-3 Pipeline was successful
ci/woodpecker/push/finalize Pipeline was successful
Build and Push / lint-and-test (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.adapters.broker_adapter name:broker-adapter]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.aggregation.worker name:aggregation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.extractor.worker name:extractor]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.ingestion.worker name:ingestion]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.lake_publisher.worker name:lake-publisher]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.parser.worker name:parser]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.recommendation.worker name:recommendation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.scheduler.app name:scheduler]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.api.app:app --host 0.0.0.0 --port 8000 name:query-api]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.risk.app:app --host 0.0.0.0 --port 8000 name:risk]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.symbol_registry.app:app --host 0.0.0.0 --port 8000 name:symbol-registry]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.trading.app:app --host 0.0.0.0 --port 8000 name:trading-engine]) (push) Has been cancelled
Build and Push / build-dashboard (push) Has been cancelled
Build and Push / build-superset (push) Has been cancelled
Build and Push / integration-test (push) Has been cancelled
Build and Push / beta-gate (push) Has been cancelled
- Add initial capital input (toggle between broker balance or custom amount) - Add reserve/active pool split slider (0-50%, default 20%) - Backend accepts reserve_pct in reset request body - Note in UI that Alpaca balance reset requires Alpaca dashboard - Confirmation dialog shows exact capital and split being applied
This commit is contained in:
@@ -316,9 +316,12 @@ export function useBacktestLaunch() {
|
||||
export function useResetPaperTrading() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (initial_capital: number = 0) =>
|
||||
mutationFn: (params: { initial_capital?: number; reserve_pct?: number } = {}) =>
|
||||
apiPost<{ reset: boolean; initial_capital: number; active_pool: number; reserve_pool: number; broker: Record<string, number> }>(
|
||||
'trading', '/api/trading/reset', { initial_capital },
|
||||
'trading', '/api/trading/reset', {
|
||||
initial_capital: params.initial_capital ?? 0,
|
||||
reserve_pct: params.reserve_pct ?? undefined,
|
||||
},
|
||||
),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['trading-status'] });
|
||||
|
||||
@@ -116,7 +116,7 @@ export function TradingPage() {
|
||||
|
||||
{/* Paper Trading Reset */}
|
||||
<ResetCard
|
||||
onReset={() => resetTrading.mutate(0)}
|
||||
onReset={(params) => resetTrading.mutate(params)}
|
||||
isResetting={resetTrading.isPending}
|
||||
/>
|
||||
|
||||
@@ -490,26 +490,101 @@ function ApprovalRow({ approval, onReview }: {
|
||||
|
||||
|
||||
function ResetCard({ onReset, isResetting }: {
|
||||
onReset: () => void;
|
||||
onReset: (params: { initial_capital?: number; reserve_pct?: number }) => void;
|
||||
isResetting: boolean;
|
||||
}) {
|
||||
const [showConfirm, setShowConfirm] = useState(false);
|
||||
const [capitalInput, setCapitalInput] = useState('100000');
|
||||
const [reservePct, setReservePct] = useState(20);
|
||||
const [useCustomCapital, setUseCustomCapital] = useState(false);
|
||||
|
||||
const capital = parseFloat(capitalInput) || 0;
|
||||
const reserveAmount = capital * (reservePct / 100);
|
||||
const activeAmount = capital - reserveAmount;
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<h2 className="mb-3 text-sm font-medium text-gray-400">Paper Trading Account</h2>
|
||||
|
||||
{/* Capital & Reserve Configuration */}
|
||||
<div className="mb-4 space-y-3 rounded-lg border border-surface-700 bg-surface-950 p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => setUseCustomCapital(!useCustomCapital)}
|
||||
className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 focus:ring-offset-surface-900 ${
|
||||
useCustomCapital ? 'bg-brand-600' : 'bg-surface-700'
|
||||
}`}
|
||||
role="switch"
|
||||
aria-checked={useCustomCapital}
|
||||
aria-label="Set custom initial capital"
|
||||
>
|
||||
<span className={`pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow transition-transform ${
|
||||
useCustomCapital ? 'translate-x-4' : 'translate-x-0'
|
||||
}`} />
|
||||
</button>
|
||||
<span className="text-sm text-gray-300">Set initial capital</span>
|
||||
<span className="text-[10px] text-gray-600">(otherwise uses broker account balance)</span>
|
||||
</div>
|
||||
|
||||
{useCustomCapital && (
|
||||
<div className="flex items-center gap-2">
|
||||
<label htmlFor="reset-capital" className="text-xs text-gray-500">Capital $</label>
|
||||
<input
|
||||
id="reset-capital"
|
||||
type="number"
|
||||
min={0}
|
||||
step={1000}
|
||||
value={capitalInput}
|
||||
onChange={(e) => setCapitalInput(e.target.value)}
|
||||
className="w-36 rounded-md border border-surface-700 bg-surface-900 px-2 py-1 text-sm font-mono text-gray-200"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="reset-reserve" className="text-xs text-gray-500">
|
||||
Reserve pool: {reservePct}%
|
||||
</label>
|
||||
<span className="text-xs text-gray-600">
|
||||
Active: {100 - reservePct}%
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
id="reset-reserve"
|
||||
type="range"
|
||||
min={0}
|
||||
max={50}
|
||||
step={5}
|
||||
value={reservePct}
|
||||
onChange={(e) => setReservePct(Number(e.target.value))}
|
||||
className="mt-1 w-full accent-brand-600"
|
||||
/>
|
||||
{useCustomCapital && capital > 0 && (
|
||||
<div className="mt-1 flex justify-between text-[10px] text-gray-600">
|
||||
<span>Reserve: ${reserveAmount.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||
<span>Active: ${activeAmount.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Reset Button */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<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.
|
||||
and sets capital from {useCustomCapital ? 'the amount above' : 'the broker account balance'}.
|
||||
</p>
|
||||
<p className="mt-1 text-[10px] text-gray-600 italic">
|
||||
Note: To reset the Alpaca paper account balance itself, use the Alpaca dashboard.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowConfirm(true)}
|
||||
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"
|
||||
className="shrink-0 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>
|
||||
@@ -519,11 +594,20 @@ function ResetCard({ onReset, isResetting }: {
|
||||
<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's account balance.
|
||||
All broker positions will be liquidated.
|
||||
{useCustomCapital
|
||||
? ` Capital will be set to $${capital.toLocaleString()} (${reservePct}% reserve / ${100 - reservePct}% active).`
|
||||
: ` Capital will be set from the broker's account balance (${reservePct}% reserve / ${100 - reservePct}% active).`}
|
||||
</p>
|
||||
<div className="mt-3 flex gap-2">
|
||||
<button
|
||||
onClick={() => { onReset(); setShowConfirm(false); }}
|
||||
onClick={() => {
|
||||
onReset({
|
||||
initial_capital: useCustomCapital ? capital : undefined,
|
||||
reserve_pct: reservePct / 100,
|
||||
});
|
||||
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"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user