Finalize GTL and retire S/R research harness
This commit is contained in:
@@ -200,8 +200,11 @@ export interface TriggerJobResponse {
|
||||
job: string;
|
||||
status: 'triggered' | 'busy' | 'blocked' | 'not_found';
|
||||
message: string;
|
||||
target_model?: BacktestTargetModel;
|
||||
}
|
||||
|
||||
export type BacktestTargetModel = 'production_gtl' | 'structural_sr';
|
||||
|
||||
export function listJobs() {
|
||||
return apiClient.get<JobStatus[]>('admin/jobs').then((r) => r.data);
|
||||
}
|
||||
@@ -216,9 +219,9 @@ export function toggleJob(jobName: string, enabled: boolean) {
|
||||
.then((r) => r.data);
|
||||
}
|
||||
|
||||
export function triggerJob(jobName: string) {
|
||||
export function triggerJob(jobName: string, options?: { target_model?: BacktestTargetModel }) {
|
||||
return apiClient
|
||||
.post<TriggerJobResponse>(`admin/jobs/${jobName}/trigger`)
|
||||
.post<TriggerJobResponse>(`admin/jobs/${jobName}/trigger`, options)
|
||||
.then((r) => r.data);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useMemo, useState } from 'react';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { useBacktestReport } from '../../hooks/useMarketRegime';
|
||||
import { triggerJob } from '../../api/admin';
|
||||
import type { BacktestTargetModel } from '../../api/admin';
|
||||
import { Button } from '../ui/Button';
|
||||
import { Callout } from '../ui/Callout';
|
||||
import { Disclosure } from '../ui/Disclosure';
|
||||
@@ -143,6 +144,7 @@ export function BacktestPanel() {
|
||||
const toast = useToast();
|
||||
const [selectedStrategy, setSelectedStrategy] = useState('');
|
||||
const [selectedLookback, setSelectedLookback] = useState('');
|
||||
const [targetModel, setTargetModel] = useState<BacktestTargetModel>('production_gtl');
|
||||
|
||||
const monitor = report?.portfolio_monitor ?? null;
|
||||
const activeStrategy =
|
||||
@@ -159,10 +161,11 @@ export function BacktestPanel() {
|
||||
);
|
||||
|
||||
const run = useMutation({
|
||||
mutationFn: () => triggerJob('backtest'),
|
||||
mutationFn: () => triggerJob('backtest', { target_model: targetModel }),
|
||||
onSuccess: (res) => {
|
||||
if (res.status === 'triggered') {
|
||||
toast.addToast('success', 'Backtest started — results appear when it finishes (a minute or two).');
|
||||
const label = targetModel === 'production_gtl' ? 'Live GTL' : 'Structural S/R comparison';
|
||||
toast.addToast('success', `${label} backtest started — results appear when it finishes.`);
|
||||
setTimeout(() => queryClient.invalidateQueries({ queryKey: ['backtest-report'] }), 8000);
|
||||
} else {
|
||||
toast.addToast('info', res.message || 'Could not start backtest');
|
||||
@@ -184,9 +187,61 @@ export function BacktestPanel() {
|
||||
so read it as directional.
|
||||
</p>
|
||||
</Disclosure>
|
||||
<Button onClick={() => run.mutate()} loading={run.isPending} className="shrink-0">
|
||||
{run.isPending ? 'Starting…' : report ? 'Re-run backtest' : 'Run backtest'}
|
||||
</Button>
|
||||
<div className="flex w-full flex-col gap-3 sm:w-auto sm:items-end">
|
||||
<fieldset className="grid w-full grid-cols-1 gap-2 sm:w-[34rem] sm:grid-cols-2">
|
||||
<legend className="mb-1 text-[11px] font-medium uppercase tracking-wider text-gray-500">
|
||||
Target model for this run
|
||||
</legend>
|
||||
<label
|
||||
className={`cursor-pointer rounded-lg border px-3 py-2 transition-colors focus-within:ring-2 focus-within:ring-blue-400/60 ${
|
||||
targetModel === 'production_gtl'
|
||||
? 'border-blue-400/60 bg-blue-500/10'
|
||||
: 'border-white/10 bg-white/[0.03] hover:border-white/20'
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
className="sr-only"
|
||||
type="radio"
|
||||
name="backtest-target-model"
|
||||
value="production_gtl"
|
||||
checked={targetModel === 'production_gtl'}
|
||||
onChange={() => setTargetModel('production_gtl')}
|
||||
/>
|
||||
<span className="flex items-center justify-between gap-2 text-sm font-medium text-gray-100">
|
||||
Live GTL
|
||||
<span className="rounded-full border border-blue-400/40 bg-blue-400/10 px-2 py-0.5 text-[9px] font-semibold uppercase tracking-widest text-blue-300">
|
||||
Production
|
||||
</span>
|
||||
</span>
|
||||
<span className="mt-1 block text-[11px] leading-4 text-gray-500">
|
||||
Exact target path used by the live scanner and scheduled backtest.
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
className={`cursor-pointer rounded-lg border px-3 py-2 transition-colors focus-within:ring-2 focus-within:ring-amber-400/60 ${
|
||||
targetModel === 'structural_sr'
|
||||
? 'border-amber-400/50 bg-amber-500/10'
|
||||
: 'border-white/10 bg-white/[0.03] hover:border-white/20'
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
className="sr-only"
|
||||
type="radio"
|
||||
name="backtest-target-model"
|
||||
value="structural_sr"
|
||||
checked={targetModel === 'structural_sr'}
|
||||
onChange={() => setTargetModel('structural_sr')}
|
||||
/>
|
||||
<span className="text-sm font-medium text-gray-200">Structural S/R</span>
|
||||
<span className="mt-1 block text-[11px] leading-4 text-gray-500">
|
||||
Comparison only; uses chart structure as the target source.
|
||||
</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
<Button onClick={() => run.mutate()} loading={run.isPending} className="shrink-0">
|
||||
{run.isPending ? 'Starting…' : report ? 'Re-run backtest' : 'Run backtest'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isLoading && <Callout variant="empty">Loading…</Callout>}
|
||||
@@ -206,6 +261,10 @@ export function BacktestPanel() {
|
||||
{report.params.cost_per_side_pct != null && (
|
||||
<> · net of {report.params.cost_per_side_pct}%/side costs</>
|
||||
)}
|
||||
{' '}· target model:{' '}
|
||||
<span className={report.params.is_production_target_model === false ? 'text-amber-300' : 'text-blue-300'}>
|
||||
{report.params.target_model_label ?? 'Legacy report (model not recorded)'}
|
||||
</span>
|
||||
</p>
|
||||
|
||||
{monitor && monitorRun ? (
|
||||
|
||||
@@ -399,6 +399,9 @@ export interface BacktestReport {
|
||||
horizon_days: number;
|
||||
min_lookback: number;
|
||||
cost_per_side_pct?: number;
|
||||
target_model?: 'production_gtl' | 'structural_sr';
|
||||
target_model_label?: string;
|
||||
is_production_target_model?: boolean;
|
||||
};
|
||||
overall_qualified: BacktestBucket;
|
||||
overall_all: BacktestBucket;
|
||||
|
||||
Reference in New Issue
Block a user