refactor(ui): name the two regime jobs for what they actually do
"Market Regime" and "Regime Monitor" sat next to each other in Admin -> Jobs
(pipeline steps 4 and 5) reading as the same job. They are unrelated, and the
names had it backwards: "Market Regime" is the SPY 50/200 guard that drives the
TopBar trend dot and the counter-trend warning on setups, so it changes what a
setup shows; "Regime Monitor" is the observational AI/Tech thermometer that
explicitly feeds no trades. The more consequential job had the vaguer name.
market_regime "Market Regime" -> "Market Trend (SPY)"
regime_monitor "Regime Monitor" -> "AI/Tech Risk Monitor"
Display strings only. The job *ids* are persisted -- they key the pipeline step
list, cron config, runtime tracking and run history -- so they are untouched,
as is the /regime route, which keeps existing links working.
The label the admin UI renders comes from JOB_LABELS in admin_service (via
routers/jobs.py), not from the scheduler's APScheduler `name=`. Both are updated;
only the former is user-visible.
Carries the vocabulary through the rest of the surface so it does not half-land:
page title, nav ("Regime" -> "Risk"), the empty-state instruction that names the
job to run, the quadrant alert toggle, the morning-pipeline hint, and the
Telegram alert headline ("Regime quadrant change" -> "AI/Tech risk quadrant
change"). No test asserts any of these strings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+6
-2
@@ -1561,8 +1561,12 @@ def configure_scheduler(schedule_config: dict[str, str] | None = None) -> None:
|
|||||||
(scan_rr, "rr_scanner", "R:R Scanner"),
|
(scan_rr, "rr_scanner", "R:R Scanner"),
|
||||||
(run_shadow_book, "shadow_book", "Shadow Book (auto-traded strategy)"),
|
(run_shadow_book, "shadow_book", "Shadow Book (auto-traded strategy)"),
|
||||||
(evaluate_outcomes, "outcome_evaluator", "Outcome Evaluator"),
|
(evaluate_outcomes, "outcome_evaluator", "Outcome Evaluator"),
|
||||||
(compute_market_regime, "market_regime", "Market Regime"),
|
# Labels only -- the ids are persisted (pipeline steps, cron config, run
|
||||||
(compute_regime_monitor, "regime_monitor", "Regime Monitor"),
|
# history), so they stay. "Market Regime"/"Regime Monitor" read as the
|
||||||
|
# same job and had it backwards besides: the SPY guard is the one that
|
||||||
|
# changes what a setup shows, while the monitor is observational.
|
||||||
|
(compute_market_regime, "market_regime", "Market Trend (SPY)"),
|
||||||
|
(compute_regime_monitor, "regime_monitor", "AI/Tech Risk Monitor"),
|
||||||
]
|
]
|
||||||
for fn, job_id, job_name in _members:
|
for fn, job_id, job_name in _members:
|
||||||
scheduler.add_job(
|
scheduler.add_job(
|
||||||
|
|||||||
@@ -639,8 +639,9 @@ JOB_LABELS = {
|
|||||||
"ticker_universe_sync": "Ticker Universe Sync",
|
"ticker_universe_sync": "Ticker Universe Sync",
|
||||||
"outcome_evaluator": "Outcome Evaluator",
|
"outcome_evaluator": "Outcome Evaluator",
|
||||||
"alerts": "Alerts Dispatcher",
|
"alerts": "Alerts Dispatcher",
|
||||||
"market_regime": "Market Regime",
|
# Keys are persisted job ids and must not change; these are display only.
|
||||||
"regime_monitor": "Regime Monitor",
|
"market_regime": "Market Trend (SPY)",
|
||||||
|
"regime_monitor": "AI/Tech Risk Monitor",
|
||||||
"event_study": "Event Study",
|
"event_study": "Event Study",
|
||||||
"backtest": "Backtest",
|
"backtest": "Backtest",
|
||||||
"daily_pipeline": "Morning Pipeline",
|
"daily_pipeline": "Morning Pipeline",
|
||||||
|
|||||||
@@ -860,7 +860,7 @@ async def _collect_regime_quadrant(db: AsyncSession) -> list[tuple[str, str]]:
|
|||||||
else:
|
else:
|
||||||
metrics = f"State {x:.0f} · Warning {y:.0f}"
|
metrics = f"State {x:.0f} · Warning {y:.0f}"
|
||||||
text = (
|
text = (
|
||||||
f"🧭 <b>Regime quadrant change</b>\n"
|
f"🧭 <b>AI/Tech risk quadrant change</b>\n"
|
||||||
f"{QUAD_LABELS.get(prev, prev)} → {QUAD_LABELS.get(new_q, new_q)}\n"
|
f"{QUAD_LABELS.get(prev, prev)} → {QUAD_LABELS.get(new_q, new_q)}\n"
|
||||||
f"{metrics}\n"
|
f"{metrics}\n"
|
||||||
f"coverage: state {state.get('coverage'):.0f}% / warning {warning.get('coverage'):.0f}%\n"
|
f"coverage: state {state.get('coverage'):.0f}% / warning {warning.get('coverage'):.0f}%\n"
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const TRIGGERS: { key: TriggerKey; label: string; hint: string }[] = [
|
|||||||
{ key: 'sr_proximity_enabled', label: 'Watchlist S/R proximity', hint: 'a watched ticker nears a strong support/resistance' },
|
{ key: 'sr_proximity_enabled', label: 'Watchlist S/R proximity', hint: 'a watched ticker nears a strong support/resistance' },
|
||||||
{ key: 'score_drop_enabled', label: 'Score deterioration', hint: 'a watched ticker’s composite drops sharply' },
|
{ key: 'score_drop_enabled', label: 'Score deterioration', hint: 'a watched ticker’s composite drops sharply' },
|
||||||
{ key: 'digest_enabled', label: 'Daily digest', hint: 'end-of-day summary incl. open trades + trailing stops' },
|
{ key: 'digest_enabled', label: 'Daily digest', hint: 'end-of-day summary incl. open trades + trailing stops' },
|
||||||
{ key: 'regime_quadrant_enabled', label: 'Regime quadrant change', hint: 'the regime monitor shifts quadrant (hysteresis + cooldown)' },
|
{ key: 'regime_quadrant_enabled', label: 'Risk quadrant change', hint: 'the AI/Tech risk monitor shifts quadrant (hysteresis + cooldown)' },
|
||||||
{ key: 'trade_closed_enabled', label: 'Trade closed', hint: 'a paper trade auto-closes (trailing/target/stop) — incl. losses' },
|
{ key: 'trade_closed_enabled', label: 'Trade closed', hint: 'a paper trade auto-closes (trailing/target/stop) — incl. losses' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const FIELDS: { key: keyof ScheduleConfig; label: string; hint: string; mono?: b
|
|||||||
{
|
{
|
||||||
key: 'schedule_daily_pipeline_cron',
|
key: 'schedule_daily_pipeline_cron',
|
||||||
label: 'Morning pipeline',
|
label: 'Morning pipeline',
|
||||||
hint: 'OHLCV → benchmark → sentiment → regime → alerts (no R:R scan). Default 02:00 ET so regime-quadrant changes hit Telegram in the morning.',
|
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,
|
mono: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const navItems = [
|
|||||||
{ to: '/', label: 'Overview', end: true },
|
{ to: '/', label: 'Overview', end: true },
|
||||||
{ to: '/market', label: 'Market', end: false },
|
{ to: '/market', label: 'Market', end: false },
|
||||||
{ to: '/signals', label: 'Signals', end: false },
|
{ to: '/signals', label: 'Signals', end: false },
|
||||||
{ to: '/regime', label: 'Regime', end: false },
|
{ to: '/regime', label: 'Risk', end: false },
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function MobileNav() {
|
export default function MobileNav() {
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ const navItems = [
|
|||||||
{ to: '/', label: 'Overview', end: true },
|
{ to: '/', label: 'Overview', end: true },
|
||||||
{ to: '/market', label: 'Market', end: false },
|
{ to: '/market', label: 'Market', end: false },
|
||||||
{ to: '/signals', label: 'Signals', end: false },
|
{ to: '/signals', label: 'Signals', end: false },
|
||||||
{ to: '/regime', label: 'Regime', end: false },
|
// Route stays /regime so existing links keep working; only the label changes.
|
||||||
|
{ to: '/regime', label: 'Risk', end: false },
|
||||||
];
|
];
|
||||||
|
|
||||||
const linkClasses = (isActive: boolean) =>
|
const linkClasses = (isActive: boolean) =>
|
||||||
|
|||||||
@@ -507,13 +507,13 @@ export default function RegimePage() {
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-6 animate-slide-up">
|
<div className="space-y-6 animate-slide-up">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="Regime Monitor"
|
title="AI/Tech Risk Monitor"
|
||||||
subtitle="AI/Tech risk thermometer — observational only, feeds no entry, exit, or sizing decision"
|
subtitle="AI/Tech risk thermometer — observational only, feeds no entry, exit, or sizing decision"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{monitor.isLoading && <><SkeletonCard className="h-44" /><SkeletonTable rows={6} cols={4} /></>}
|
{monitor.isLoading && <><SkeletonCard className="h-44" /><SkeletonTable rows={6} cols={4} /></>}
|
||||||
{monitor.isError && <Callout variant="error" onRetry={() => monitor.refetch()}>Failed to load: {(monitor.error as Error).message}</Callout>}
|
{monitor.isError && <Callout variant="error" onRetry={() => monitor.refetch()}>Failed to load: {(monitor.error as Error).message}</Callout>}
|
||||||
{data && !data.available && <Callout variant="empty">Not computed yet — run “Regime Monitor” from Admin → Jobs or wait for the daily pipeline.</Callout>}
|
{data && !data.available && <Callout variant="empty">Not computed yet — run “AI/Tech Risk Monitor” from Admin → Jobs or wait for the daily pipeline.</Callout>}
|
||||||
|
|
||||||
{data?.available && data.state && data.warning && (
|
{data?.available && data.state && data.warning && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user