import { useEffect, useState } from 'react'; import type { ScheduleConfig } from '../../lib/types'; import { useScheduleSettings, useUpdateScheduleSettings } from '../../hooks/useAdmin'; import { SkeletonTable } from '../ui/Skeleton'; const DEFAULTS: ScheduleConfig = { schedule_timezone: 'America/New_York', schedule_daily_pipeline_cron: '0 2 * * *', schedule_dolt_earnings_cron: '30 2 * * *', schedule_sec_fundamentals_cron: '0 4 * * *', schedule_near_close_pipeline_cron: '30 15 * * mon-fri', schedule_after_close_pipeline_cron: '45 16 * * mon-fri', schedule_intraday_pipeline_cron: '0 10-15 * * mon-fri', schedule_backtest_cron: '0 3 * * sun', schedule_ticker_universe_cron: '0 1 * * *', }; const FIELDS: { key: keyof ScheduleConfig; label: string; hint: string; mono?: boolean }[] = [ { key: 'schedule_timezone', label: 'Timezone', hint: 'IANA name. Prefer America/New_York so the near-close scan tracks the US cash close through DST.', }, { key: 'schedule_daily_pipeline_cron', label: 'Morning pipeline', hint: 'OHLCV → benchmark → sentiment → trend/risk → alerts (no R:R scan). Default 02:00 ET so risk-quadrant changes hit Telegram in the morning.', mono: true, }, { key: 'schedule_dolt_earnings_cron', label: 'Dolt earnings', hint: 'Pull and import earnings dates/results daily at 02:30 ET. The fundamentals cache refresh uses these local events.', mono: true, }, { key: 'schedule_sec_fundamentals_cron', label: 'SEC fundamentals', hint: 'Import tracked-universe SEC facts daily at 04:00 ET, then refresh the fundamentals cache scoring reads. Disabling the job stops the SEC fetch only — the local cache refresh still runs.', mono: true, }, { key: 'schedule_near_close_pipeline_cron', label: 'Near-close pipeline (scan + alert)', hint: 'OHLCV fetch → R:R scan → Telegram. Default 15:30 ET Mon–Fri so manual MOC fills can still hit ~15:50/15:55.', mono: true, }, { key: 'schedule_after_close_pipeline_cron', label: 'After-close pipeline (outcome)', hint: 'OHLCV fetch (final bar) → outcome eval. Default 16:45 ET Mon–Fri — not chained to the partial near-close bar.', mono: true, }, { key: 'schedule_intraday_pipeline_cron', label: 'Intraday pipeline (light)', hint: 'Refresh prices + resolve outcomes mid-session. Default hourly 10:00–15:00 ET weekdays.', mono: true, }, { key: 'schedule_backtest_cron', label: 'Backtest', hint: 'Replay history and refresh the Track Record report. Default Sunday 03:00 ET. Was a 168h interval, which restarted on every deploy and so could defer indefinitely.', mono: true, }, { key: 'schedule_ticker_universe_cron', label: 'Ticker universe sync', hint: 'Refresh the tracked-symbol universe. Default 01:00 ET daily, before the morning pipeline.', mono: true, }, ]; export function ScheduleSettings() { const { data, isLoading, isError, error } = useScheduleSettings(); const update = useUpdateScheduleSettings(); const [form, setForm] = useState(DEFAULTS); useEffect(() => { if (data) setForm({ ...DEFAULTS, ...data }); }, [data]); if (isLoading) return ; if (isError) return

{(error as Error)?.message || 'Failed to load schedule'}

; return (

Pipeline Schedule

When the jobs run, as 5-field cron (min hour day month weekday). Saved changes apply to the running scheduler immediately — no redeploy. One qualifying R:R scan per day is the near-close job; alerts fire immediately after that scan.

{FIELDS.map((f) => ( ))}
); }