feat: net-of-cost backtest, gate ablation + time-exit sweeps, longer tails
Phase 1 of the strategy-measurement plan — report-only, no production trading behavior changes: - Cost haircut: every bucket/sweep now reports net_avg_r/net_total_r alongside gross (COST_PER_SIDE=0.1% of notional, converted to R via each setup's stop distance); params carry cost_per_side_pct. - Gate ablation table: re-qualifies candidates at the current momentum cutoff with one floor removed per row (confidence / R:R / NEUTRAL / momentum-only) to show which floors earn their keep. - Time-based exit sweep: hold 5/10/21/30 days with the initial ATR stop, exit at the day-N close — the classic momentum implementation, to disambiguate the wide-trailing result. - TP sweep extended to +40/+50%, trailing to 25/30% so the optima are interior instead of starred at the sweep edge. - BacktestPanel: Net Avg R columns everywhere, gate-ablation and time-exit tables, stars now mark best net avg R; stale cached reports still render (all new fields optional/guarded). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,20 @@ const SIGNAL_LABELS: Record<string, string> = {
|
||||
vol_6m: '6-month realized volatility',
|
||||
};
|
||||
|
||||
const ABLATION_LABELS: Record<string, string> = {
|
||||
all_floors: 'All floors (current gate)',
|
||||
no_confidence_floor: 'Without confidence floor',
|
||||
no_rr_floor: 'Without R:R floor',
|
||||
no_neutral_exclusion: 'Without NEUTRAL exclusion',
|
||||
momentum_only: 'Momentum only (no floors)',
|
||||
};
|
||||
|
||||
// Prefer the net-of-costs number when the report carries it; older cached
|
||||
// reports (pre-cost model) fall back to gross.
|
||||
function netOrGross(r: { avg_r: number | null; net_avg_r?: number | null }): number | null {
|
||||
return r.net_avg_r ?? r.avg_r;
|
||||
}
|
||||
|
||||
// An |IC| this large, with a consistent sign, is a real (if small) edge worth
|
||||
// building on; below it, ranking on the signal sorts essentially nothing.
|
||||
const IC_EDGE_THRESHOLD = 0.03;
|
||||
@@ -76,6 +90,7 @@ function BucketRow({ label, b }: { label: string; b: BacktestBucket }) {
|
||||
<td className="num px-4 py-2.5 text-right text-gray-400">{b.expired}</td>
|
||||
<td className="num px-4 py-2.5 text-right text-gray-200">{fmtPct(b.hit_rate)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right ${rColor(b.avg_r)}`}>{fmtR(b.avg_r)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right ${rColor(b.net_avg_r ?? null)}`}>{fmtR(b.net_avg_r ?? null)}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
@@ -87,11 +102,15 @@ export function BacktestPanel() {
|
||||
|
||||
const bestTpAvgR =
|
||||
report?.take_profit_sweep && report.take_profit_sweep.length > 0
|
||||
? Math.max(...report.take_profit_sweep.map((r) => r.avg_r ?? -Infinity))
|
||||
? Math.max(...report.take_profit_sweep.map((r) => netOrGross(r) ?? -Infinity))
|
||||
: null;
|
||||
const bestTrailAvgR =
|
||||
report?.trailing_sweep && report.trailing_sweep.length > 0
|
||||
? Math.max(...report.trailing_sweep.map((r) => r.avg_r ?? -Infinity))
|
||||
? Math.max(...report.trailing_sweep.map((r) => netOrGross(r) ?? -Infinity))
|
||||
: null;
|
||||
const bestTimeAvgR =
|
||||
report?.time_exit_sweep && report.time_exit_sweep.length > 0
|
||||
? Math.max(...report.time_exit_sweep.map((r) => netOrGross(r) ?? -Infinity))
|
||||
: null;
|
||||
|
||||
const run = useMutation({
|
||||
@@ -140,6 +159,9 @@ export function BacktestPanel() {
|
||||
<p className="text-[11px] text-gray-500">
|
||||
Ran {timeAgo(report.generated_at)} · {report.tickers} tickers · {report.candidates} setups
|
||||
({report.qualified} qualified) · weekly cadence, {report.params.horizon_days}-day horizon
|
||||
{report.params.cost_per_side_pct != null && (
|
||||
<> · net assumes {report.params.cost_per_side_pct}%/side costs</>
|
||||
)}
|
||||
</p>
|
||||
|
||||
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
|
||||
@@ -179,6 +201,7 @@ export function BacktestPanel() {
|
||||
<th className="px-4 py-2.5 text-right">Expired</th>
|
||||
<th className="px-4 py-2.5 text-right">Hit Rate</th>
|
||||
<th className="px-4 py-2.5 text-right">Avg R</th>
|
||||
<th className="px-4 py-2.5 text-right">Net Avg R</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -214,6 +237,7 @@ export function BacktestPanel() {
|
||||
<th className="px-4 py-2.5 text-right">Losses</th>
|
||||
<th className="px-4 py-2.5 text-right">Hit Rate</th>
|
||||
<th className="px-4 py-2.5 text-right">Avg R</th>
|
||||
<th className="px-4 py-2.5 text-right">Net Avg R</th>
|
||||
<th className="px-4 py-2.5 text-right">Total R</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -231,6 +255,7 @@ export function BacktestPanel() {
|
||||
<td className="num px-4 py-2.5 text-right text-red-400">{row.losses}</td>
|
||||
<td className="num px-4 py-2.5 text-right text-gray-200">{fmtPct(row.hit_rate)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right font-semibold ${rColor(row.avg_r)}`}>{fmtR(row.avg_r)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right ${rColor(row.net_avg_r ?? null)}`}>{fmtR(row.net_avg_r ?? null)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right ${rColor(row.total_r)}`}>{fmtR(row.total_r)}</td>
|
||||
</tr>
|
||||
);
|
||||
@@ -241,6 +266,51 @@ export function BacktestPanel() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{report.gate_ablation && report.gate_ablation.length > 0 && (
|
||||
<div>
|
||||
<p className="mb-2 text-xs font-medium uppercase tracking-widest text-gray-500">
|
||||
Gate ablation — which floors earn their keep
|
||||
</p>
|
||||
<p className="mb-2 text-[11px] text-gray-500">
|
||||
{report.gate_ablation_note ??
|
||||
'Each row re-qualifies the same candidates at the current momentum cutoff with one floor removed (long-only throughout).'}
|
||||
</p>
|
||||
<div className="glass overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-white/[0.06] text-left text-xs uppercase tracking-wider text-gray-500">
|
||||
<th className="px-4 py-2.5">Variant</th>
|
||||
<th className="px-4 py-2.5 text-right">Setups</th>
|
||||
<th className="px-4 py-2.5 text-right">Hit Rate</th>
|
||||
<th className="px-4 py-2.5 text-right">Avg R</th>
|
||||
<th className="px-4 py-2.5 text-right">Net Avg R</th>
|
||||
<th className="px-4 py-2.5 text-right">Total R</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{report.gate_ablation.map((row) => (
|
||||
<tr
|
||||
key={row.variant}
|
||||
className={`border-b border-white/[0.04] ${row.variant === 'all_floors' ? 'bg-blue-400/10' : ''}`}
|
||||
>
|
||||
<td className="px-4 py-2.5 font-medium text-gray-200">
|
||||
{ABLATION_LABELS[row.variant] ?? row.variant}
|
||||
</td>
|
||||
<td className="num px-4 py-2.5 text-right text-gray-200">{row.total}</td>
|
||||
<td className="num px-4 py-2.5 text-right text-gray-200">{fmtPct(row.hit_rate)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right ${rColor(row.avg_r)}`}>{fmtR(row.avg_r)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right font-semibold ${rColor(row.net_avg_r ?? null)}`}>
|
||||
{fmtR(row.net_avg_r ?? null)}
|
||||
</td>
|
||||
<td className={`num px-4 py-2.5 text-right ${rColor(row.total_r)}`}>{fmtR(row.total_r)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{report.take_profit_sweep && report.take_profit_sweep.length > 0 && (
|
||||
<div>
|
||||
<p className="mb-2 text-xs font-medium uppercase tracking-widest text-gray-500">
|
||||
@@ -253,7 +323,7 @@ export function BacktestPanel() {
|
||||
target model above. <span className="text-gray-300">Hit Rate = how often you'd have banked
|
||||
+X%</span> (how far winners actually run) — no top-ticking, it's the level you'd really set.
|
||||
The setup's own S/R target is <em>not</em> used here (exiting at that target is the model
|
||||
above); this is a pure fixed-% exit. ★ = best avg R.
|
||||
above); this is a pure fixed-% exit. ★ = best net avg R.
|
||||
</p>
|
||||
<div className="glass overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
@@ -264,12 +334,13 @@ export function BacktestPanel() {
|
||||
<th className="px-4 py-2.5 text-right">Hit (banked)</th>
|
||||
<th className="px-4 py-2.5 text-right">Hit Rate</th>
|
||||
<th className="px-4 py-2.5 text-right">Avg R</th>
|
||||
<th className="px-4 py-2.5 text-right">Net Avg R</th>
|
||||
<th className="px-4 py-2.5 text-right">Total R</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{report.take_profit_sweep.map((row) => {
|
||||
const best = row.avg_r != null && row.avg_r === bestTpAvgR;
|
||||
const best = netOrGross(row) != null && netOrGross(row) === bestTpAvgR;
|
||||
return (
|
||||
<tr key={row.tp_pct} className={`border-b border-white/[0.04] ${best ? 'bg-emerald-400/[0.06]' : ''}`}>
|
||||
<td className="num px-4 py-2.5 text-gray-200">
|
||||
@@ -279,7 +350,8 @@ export function BacktestPanel() {
|
||||
<td className="num px-4 py-2.5 text-right text-gray-200">{row.total}</td>
|
||||
<td className="num px-4 py-2.5 text-right text-emerald-400">{row.wins}</td>
|
||||
<td className="num px-4 py-2.5 text-right text-gray-200">{fmtPct(row.hit_rate)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right font-semibold ${rColor(row.avg_r)}`}>{fmtR(row.avg_r)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right ${rColor(row.avg_r)}`}>{fmtR(row.avg_r)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right font-semibold ${rColor(row.net_avg_r ?? null)}`}>{fmtR(row.net_avg_r ?? null)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right ${rColor(row.total_r)}`}>{fmtR(row.total_r)}</td>
|
||||
</tr>
|
||||
);
|
||||
@@ -299,7 +371,7 @@ export function BacktestPanel() {
|
||||
Let it run, but exit when price gives back <span className="text-gray-300">X% from its
|
||||
peak</span> (the stop only ratchets up, never below the initial stop). Captures the tail
|
||||
without the fixed take-profit's all-or-nothing miss, and protects gains. In R vs the initial
|
||||
risk. <span className="text-gray-300">Win Rate = share closed in profit.</span> ★ = best avg R.
|
||||
risk. <span className="text-gray-300">Win Rate = share closed in profit.</span> ★ = best net avg R.
|
||||
</p>
|
||||
<div className="glass overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
@@ -310,12 +382,13 @@ export function BacktestPanel() {
|
||||
<th className="px-4 py-2.5 text-right">Profitable</th>
|
||||
<th className="px-4 py-2.5 text-right">Win Rate</th>
|
||||
<th className="px-4 py-2.5 text-right">Avg R</th>
|
||||
<th className="px-4 py-2.5 text-right">Net Avg R</th>
|
||||
<th className="px-4 py-2.5 text-right">Total R</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{report.trailing_sweep.map((row) => {
|
||||
const best = row.avg_r != null && row.avg_r === bestTrailAvgR;
|
||||
const best = netOrGross(row) != null && netOrGross(row) === bestTrailAvgR;
|
||||
return (
|
||||
<tr key={row.trail_pct} className={`border-b border-white/[0.04] ${best ? 'bg-emerald-400/[0.06]' : ''}`}>
|
||||
<td className="num px-4 py-2.5 text-gray-200">
|
||||
@@ -325,7 +398,56 @@ export function BacktestPanel() {
|
||||
<td className="num px-4 py-2.5 text-right text-gray-200">{row.total}</td>
|
||||
<td className="num px-4 py-2.5 text-right text-emerald-400">{row.wins}</td>
|
||||
<td className="num px-4 py-2.5 text-right text-gray-200">{fmtPct(row.win_rate)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right font-semibold ${rColor(row.avg_r)}`}>{fmtR(row.avg_r)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right ${rColor(row.avg_r)}`}>{fmtR(row.avg_r)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right font-semibold ${rColor(row.net_avg_r ?? null)}`}>{fmtR(row.net_avg_r ?? null)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right ${rColor(row.total_r)}`}>{fmtR(row.total_r)}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{report.time_exit_sweep && report.time_exit_sweep.length > 0 && (
|
||||
<div>
|
||||
<p className="mb-2 text-xs font-medium uppercase tracking-widest text-gray-500">
|
||||
Time-based exit
|
||||
</p>
|
||||
<p className="mb-2 text-[11px] text-gray-500">
|
||||
Buy at detection, keep the initial ATR stop, and exit at the{' '}
|
||||
<span className="text-gray-300">day-N close</span> — no target, no trailing. This is the
|
||||
classic cross-sectional momentum implementation (hold ~a month, re-rank).{' '}
|
||||
<span className="text-gray-300">Win Rate = share closed in profit.</span> ★ = best net avg R.
|
||||
</p>
|
||||
<div className="glass overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-white/[0.06] text-left text-xs uppercase tracking-wider text-gray-500">
|
||||
<th className="px-4 py-2.5">Hold</th>
|
||||
<th className="px-4 py-2.5 text-right">Setups</th>
|
||||
<th className="px-4 py-2.5 text-right">Profitable</th>
|
||||
<th className="px-4 py-2.5 text-right">Win Rate</th>
|
||||
<th className="px-4 py-2.5 text-right">Avg R</th>
|
||||
<th className="px-4 py-2.5 text-right">Net Avg R</th>
|
||||
<th className="px-4 py-2.5 text-right">Total R</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{report.time_exit_sweep.map((row) => {
|
||||
const best = netOrGross(row) != null && netOrGross(row) === bestTimeAvgR;
|
||||
return (
|
||||
<tr key={row.hold_days} className={`border-b border-white/[0.04] ${best ? 'bg-emerald-400/[0.06]' : ''}`}>
|
||||
<td className="num px-4 py-2.5 text-gray-200">
|
||||
{best && <span className="mr-1 text-emerald-300">★</span>}
|
||||
{row.hold_days}d
|
||||
</td>
|
||||
<td className="num px-4 py-2.5 text-right text-gray-200">{row.total}</td>
|
||||
<td className="num px-4 py-2.5 text-right text-emerald-400">{row.wins}</td>
|
||||
<td className="num px-4 py-2.5 text-right text-gray-200">{fmtPct(row.win_rate)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right ${rColor(row.avg_r)}`}>{fmtR(row.avg_r)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right font-semibold ${rColor(row.net_avg_r ?? null)}`}>{fmtR(row.net_avg_r ?? null)}</td>
|
||||
<td className={`num px-4 py-2.5 text-right ${rColor(row.total_r)}`}>{fmtR(row.total_r)}</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
@@ -229,6 +229,9 @@ export interface BacktestBucket {
|
||||
hit_rate: number | null;
|
||||
avg_r: number | null;
|
||||
total_r: number | null;
|
||||
// Net of transaction costs — optional so a stale cached report still renders.
|
||||
net_avg_r?: number | null;
|
||||
net_total_r?: number | null;
|
||||
}
|
||||
|
||||
export interface BacktestCalibrationRow {
|
||||
@@ -249,6 +252,8 @@ export interface BacktestTakeProfitRow {
|
||||
hit_rate: number | null;
|
||||
avg_r: number | null;
|
||||
total_r: number | null;
|
||||
net_avg_r?: number | null;
|
||||
net_total_r?: number | null;
|
||||
}
|
||||
|
||||
export interface BacktestTrailingRow {
|
||||
@@ -258,6 +263,23 @@ export interface BacktestTrailingRow {
|
||||
win_rate: number | null;
|
||||
avg_r: number | null;
|
||||
total_r: number | null;
|
||||
net_avg_r?: number | null;
|
||||
net_total_r?: number | null;
|
||||
}
|
||||
|
||||
export interface BacktestTimeExitRow {
|
||||
hold_days: number;
|
||||
total: number;
|
||||
wins: number;
|
||||
win_rate: number | null;
|
||||
avg_r: number | null;
|
||||
total_r: number | null;
|
||||
net_avg_r?: number | null;
|
||||
net_total_r?: number | null;
|
||||
}
|
||||
|
||||
export interface BacktestGateAblationRow extends BacktestBucket {
|
||||
variant: string;
|
||||
}
|
||||
|
||||
export interface BacktestSignalEvalRow {
|
||||
@@ -276,14 +298,22 @@ export interface BacktestReport {
|
||||
tickers: number;
|
||||
candidates: number;
|
||||
qualified: number;
|
||||
params: { step_days: number; horizon_days: number; min_lookback: number };
|
||||
params: {
|
||||
step_days: number;
|
||||
horizon_days: number;
|
||||
min_lookback: number;
|
||||
cost_per_side_pct?: number;
|
||||
};
|
||||
overall_qualified: BacktestBucket;
|
||||
overall_all: BacktestBucket;
|
||||
by_direction: Record<string, BacktestBucket>;
|
||||
min_momentum_percentile: number;
|
||||
sweep: BacktestSweepRow[];
|
||||
gate_ablation?: BacktestGateAblationRow[];
|
||||
gate_ablation_note?: string;
|
||||
take_profit_sweep?: BacktestTakeProfitRow[];
|
||||
trailing_sweep?: BacktestTrailingRow[];
|
||||
time_exit_sweep?: BacktestTimeExitRow[];
|
||||
calibration: BacktestCalibrationRow[];
|
||||
signal_eval?: BacktestSignalEvalRow[];
|
||||
signal_eval_note?: string;
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/activation.ts","./src/api/admin.ts","./src/api/auth.ts","./src/api/client.ts","./src/api/fundamentals.ts","./src/api/health.ts","./src/api/indicators.ts","./src/api/ingestion.ts","./src/api/jobs.ts","./src/api/market.ts","./src/api/ohlcv.ts","./src/api/papertrades.ts","./src/api/performance.ts","./src/api/scores.ts","./src/api/sentiment.ts","./src/api/sr-levels.ts","./src/api/tickers.ts","./src/api/trades.ts","./src/api/watchlist.ts","./src/components/admin/activationsettings.tsx","./src/components/admin/alertsettings.tsx","./src/components/admin/datacleanup.tsx","./src/components/admin/jobcontrols.tsx","./src/components/admin/pipelinereadinesspanel.tsx","./src/components/admin/recommendationsettings.tsx","./src/components/admin/schedulesettings.tsx","./src/components/admin/sentimentprovidersettings.tsx","./src/components/admin/settingsform.tsx","./src/components/admin/tickermanagement.tsx","./src/components/admin/tickeruniversebootstrap.tsx","./src/components/admin/usertable.tsx","./src/components/auth/protectedroute.tsx","./src/components/charts/candlestickchart.tsx","./src/components/dashboard/opentradespanel.tsx","./src/components/layout/appshell.tsx","./src/components/layout/mobilenav.tsx","./src/components/layout/sidebar.tsx","./src/components/rankings/rankingstable.tsx","./src/components/rankings/weightsform.tsx","./src/components/scanner/tradetable.tsx","./src/components/signals/backtestpanel.tsx","./src/components/signals/mytradespanel.tsx","./src/components/signals/setupspanel.tsx","./src/components/signals/trackrecordpanel.tsx","./src/components/ticker/dimensionbreakdownpanel.tsx","./src/components/ticker/fundamentalspanel.tsx","./src/components/ticker/indicatorselector.tsx","./src/components/ticker/recommendationpanel.tsx","./src/components/ticker/sroverlay.tsx","./src/components/ticker/sentimentpanel.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/callout.tsx","./src/components/ui/confirmdialog.tsx","./src/components/ui/disclosure.tsx","./src/components/ui/dropdown.tsx","./src/components/ui/field.tsx","./src/components/ui/pageheader.tsx","./src/components/ui/scorecard.tsx","./src/components/ui/section.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/tabs.tsx","./src/components/ui/toast.tsx","./src/components/watchlist/addtickerform.tsx","./src/components/watchlist/watchlisttable.tsx","./src/hooks/useactivation.ts","./src/hooks/useadmin.ts","./src/hooks/useauth.ts","./src/hooks/usefetchsymboldata.ts","./src/hooks/usemarketregime.ts","./src/hooks/usepapertrades.ts","./src/hooks/useperformance.ts","./src/hooks/userisksettings.ts","./src/hooks/usescores.ts","./src/hooks/usetickerdetail.ts","./src/hooks/usetickers.ts","./src/hooks/usetrades.ts","./src/hooks/usewatchlist.ts","./src/lib/format.ts","./src/lib/ingestionstatus.ts","./src/lib/papertrade.ts","./src/lib/position.ts","./src/lib/qualification.ts","./src/lib/recommendation.ts","./src/lib/regime.ts","./src/lib/types.ts","./src/pages/adminpage.tsx","./src/pages/dashboardpage.tsx","./src/pages/loginpage.tsx","./src/pages/marketpage.tsx","./src/pages/registerpage.tsx","./src/pages/signalspage.tsx","./src/pages/tickerdetailpage.tsx","./src/stores/authstore.ts"],"version":"5.6.3"}
|
||||
{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/activation.ts","./src/api/admin.ts","./src/api/auth.ts","./src/api/client.ts","./src/api/fundamentals.ts","./src/api/health.ts","./src/api/indicators.ts","./src/api/ingestion.ts","./src/api/jobs.ts","./src/api/market.ts","./src/api/ohlcv.ts","./src/api/papertrades.ts","./src/api/performance.ts","./src/api/regime.ts","./src/api/scores.ts","./src/api/sentiment.ts","./src/api/sr-levels.ts","./src/api/tickers.ts","./src/api/trades.ts","./src/api/watchlist.ts","./src/components/admin/activationsettings.tsx","./src/components/admin/alertsettings.tsx","./src/components/admin/datacleanup.tsx","./src/components/admin/exitpolicysettings.tsx","./src/components/admin/jobcontrols.tsx","./src/components/admin/pipelinereadinesspanel.tsx","./src/components/admin/recommendationsettings.tsx","./src/components/admin/schedulesettings.tsx","./src/components/admin/sentimentprovidersettings.tsx","./src/components/admin/settingsform.tsx","./src/components/admin/tickermanagement.tsx","./src/components/admin/tickeruniversebootstrap.tsx","./src/components/admin/usertable.tsx","./src/components/auth/protectedroute.tsx","./src/components/charts/candlestickchart.tsx","./src/components/dashboard/opentradespanel.tsx","./src/components/layout/appshell.tsx","./src/components/layout/mobilenav.tsx","./src/components/layout/sidebar.tsx","./src/components/layout/tickersearch.tsx","./src/components/rankings/rankingstable.tsx","./src/components/rankings/weightsform.tsx","./src/components/regime/regimequadrant.tsx","./src/components/regime/scorehistorychart.tsx","./src/components/scanner/tradetable.tsx","./src/components/signals/backtestpanel.tsx","./src/components/signals/mytradespanel.tsx","./src/components/signals/setupspanel.tsx","./src/components/signals/trackrecordpanel.tsx","./src/components/ticker/dimensionbreakdownpanel.tsx","./src/components/ticker/fundamentalspanel.tsx","./src/components/ticker/indicatorselector.tsx","./src/components/ticker/recommendationpanel.tsx","./src/components/ticker/sroverlay.tsx","./src/components/ticker/sentimentpanel.tsx","./src/components/ticker/standingmatrix.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/callout.tsx","./src/components/ui/confirmdialog.tsx","./src/components/ui/disclosure.tsx","./src/components/ui/dropdown.tsx","./src/components/ui/field.tsx","./src/components/ui/pageheader.tsx","./src/components/ui/scorecard.tsx","./src/components/ui/section.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/tabs.tsx","./src/components/ui/toast.tsx","./src/components/watchlist/addtickerform.tsx","./src/components/watchlist/watchlisttable.tsx","./src/hooks/useactivation.ts","./src/hooks/useadmin.ts","./src/hooks/useauth.ts","./src/hooks/usefetchsymboldata.ts","./src/hooks/usemarketregime.ts","./src/hooks/usepapertrades.ts","./src/hooks/useperformance.ts","./src/hooks/userisksettings.ts","./src/hooks/usescores.ts","./src/hooks/usetickerdetail.ts","./src/hooks/usetickers.ts","./src/hooks/usetrades.ts","./src/hooks/usewatchlist.ts","./src/lib/format.ts","./src/lib/ingestionstatus.ts","./src/lib/papertrade.ts","./src/lib/position.ts","./src/lib/qualification.ts","./src/lib/recommendation.ts","./src/lib/regime.ts","./src/lib/types.ts","./src/pages/adminpage.tsx","./src/pages/dashboardpage.tsx","./src/pages/loginpage.tsx","./src/pages/marketpage.tsx","./src/pages/regimepage.tsx","./src/pages/registerpage.tsx","./src/pages/signalspage.tsx","./src/pages/tickerdetailpage.tsx","./src/stores/authstore.ts"],"version":"5.6.3"}
|
||||
Reference in New Issue
Block a user