Files
signal-platform/frontend/src/api/admin.ts
T
dennisthiessen c34f3cb1a4
Deploy / lint (push) Successful in 9s
Deploy / test (push) Successful in 46s
Deploy / deploy (push) Successful in 28s
redesign activation gate to expected value + make pipelines cron-configurable
Diagnosing "no qualified signals for 5 days": setups were generated but none
qualified. The gate required BOTH a high min_rr (2.0) AND a high
min_target_probability (60), which became contradictory after the Jun-15
probability recalibration — probability already embeds R:R via the 1/(rr+1) ruin
term, so high-R:R targets are inherently low-probability and nothing cleared both.

Gate is now expected value (R): p*rr - (1-p) from the primary target's
probability. R:R and confidence stay as floors; high-conviction / exclude-conflicts
/ min-target-probability become optional tighteners (default off). Defaults:
min_expected_value=0.15, min_rr=1.2, min_confidence=55. EV is only enforced when
computable. Migration 009 clears stored activation_* rows so the new defaults
apply. Backtest sweeps min_expected_value instead of target probability.

Scheduling: pipelines are now cron-configurable in Admin -> Jobs. daily_pipeline
(full, default 0 7 * * *) plus a new light intraday_pipeline (OHLCV + outcome eval,
default hourly US session) that keeps prices/live-R:R current without setup churn.
Fundamentals on its own early weekly cron. Timezone configurable (default
Europe/Berlin). Moving interval->CronTrigger also fixes the restart-deferral bug
where an interval job's countdown resets on every process restart.

319 backend unit tests pass; frontend tsc clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 14:46:38 +02:00

232 lines
5.9 KiB
TypeScript

import apiClient from './client';
import type {
ActivationConfig,
AdminUser,
AlertConfig,
AlertTestResult,
PipelineReadiness,
RecommendationConfig,
ScheduleConfig,
SentimentProviderConfig,
SentimentTestResult,
SystemSetting,
TickerUniverse,
TickerUniverseBootstrapResult,
TickerUniverseSetting,
} from '../lib/types';
// Users
export function listUsers() {
return apiClient.get<AdminUser[]>('admin/users').then((r) => r.data);
}
export function createUser(data: {
username: string;
password: string;
role: string;
has_access: boolean;
}) {
return apiClient.post<AdminUser>('admin/users', data).then((r) => r.data);
}
export function updateAccess(userId: number, hasAccess: boolean) {
return apiClient
.put<{ message: string }>(`admin/users/${userId}/access`, {
has_access: hasAccess,
})
.then((r) => r.data);
}
export function resetPassword(userId: number, password: string) {
return apiClient
.put<{ message: string }>(`admin/users/${userId}/password`, { new_password: password })
.then((r) => r.data);
}
// Settings
export function listSettings() {
return apiClient
.get<SystemSetting[]>('admin/settings')
.then((r) => r.data);
}
export function updateSetting(key: string, value: string) {
return apiClient
.put<{ message: string }>(`admin/settings/${key}`, { value })
.then((r) => r.data);
}
export function updateRegistration(enabled: boolean) {
return apiClient
.put<{ message: string }>('admin/settings/registration', { enabled })
.then((r) => r.data);
}
export function getRecommendationSettings() {
return apiClient
.get<RecommendationConfig>('admin/settings/recommendations')
.then((r) => r.data);
}
export function updateRecommendationSettings(payload: Partial<RecommendationConfig>) {
return apiClient
.put<RecommendationConfig>('admin/settings/recommendations', payload)
.then((r) => r.data);
}
export function getActivationSettings() {
return apiClient
.get<ActivationConfig>('admin/settings/activation')
.then((r) => r.data);
}
export function updateActivationSettings(payload: Partial<ActivationConfig>) {
return apiClient
.put<ActivationConfig>('admin/settings/activation', payload)
.then((r) => r.data);
}
export function getScheduleSettings() {
return apiClient
.get<ScheduleConfig>('admin/settings/schedule')
.then((r) => r.data);
}
export function updateScheduleSettings(payload: Partial<ScheduleConfig>) {
return apiClient
.put<ScheduleConfig>('admin/settings/schedule', payload)
.then((r) => r.data);
}
export function getSentimentSettings() {
return apiClient
.get<SentimentProviderConfig>('admin/settings/sentiment')
.then((r) => r.data);
}
export function updateSentimentSettings(payload: {
provider?: string;
model?: string;
api_key?: string;
}) {
return apiClient
.put<SentimentProviderConfig>('admin/settings/sentiment', payload)
.then((r) => r.data);
}
export function testSentimentSettings(ticker: string) {
return apiClient
.post<SentimentTestResult>('admin/settings/sentiment/test', { ticker })
.then((r) => r.data);
}
export function getAlertSettings() {
return apiClient
.get<AlertConfig>('admin/settings/alerts')
.then((r) => r.data);
}
export function updateAlertSettings(payload: {
enabled?: boolean;
bot_token?: string;
telegram_chat_id?: string;
qualified_enabled?: boolean;
sr_proximity_enabled?: boolean;
score_drop_enabled?: boolean;
digest_enabled?: boolean;
}) {
return apiClient
.put<AlertConfig>('admin/settings/alerts', payload)
.then((r) => r.data);
}
export function testAlertSettings() {
return apiClient
.post<AlertTestResult>('admin/settings/alerts/test')
.then((r) => r.data);
}
export function getTickerUniverseSetting() {
return apiClient
.get<TickerUniverseSetting>('admin/settings/ticker-universe')
.then((r) => r.data);
}
export function updateTickerUniverseSetting(universe: TickerUniverse) {
return apiClient
.put<TickerUniverseSetting>('admin/settings/ticker-universe', { universe })
.then((r) => r.data);
}
export function bootstrapTickers(universe: TickerUniverse, pruneMissing: boolean) {
return apiClient
.post<TickerUniverseBootstrapResult>('admin/tickers/bootstrap', null, {
params: {
universe,
prune_missing: pruneMissing,
},
})
.then((r) => r.data);
}
// Jobs
export interface JobStatus {
name: string;
label: string;
enabled: boolean;
next_run_at: string | null;
via_pipeline?: boolean;
registered: boolean;
running?: boolean;
runtime_status?: string | null;
runtime_processed?: number | null;
runtime_total?: number | null;
runtime_progress_pct?: number | null;
runtime_current_ticker?: string | null;
runtime_started_at?: string | null;
runtime_finished_at?: string | null;
runtime_message?: string | null;
}
export interface TriggerJobResponse {
job: string;
status: 'triggered' | 'busy' | 'blocked' | 'not_found';
message: string;
}
export function listJobs() {
return apiClient.get<JobStatus[]>('admin/jobs').then((r) => r.data);
}
export function getPipelineReadiness() {
return apiClient.get<PipelineReadiness[]>('admin/pipeline/readiness').then((r) => r.data);
}
export function toggleJob(jobName: string, enabled: boolean) {
return apiClient
.put<{ message: string }>(`admin/jobs/${jobName}/toggle`, { enabled })
.then((r) => r.data);
}
export function triggerJob(jobName: string) {
return apiClient
.post<TriggerJobResponse>(`admin/jobs/${jobName}/trigger`)
.then((r) => r.data);
}
// Data cleanup
export function cleanupData(olderThanDays: number) {
return apiClient
.post<{ message: string }>('admin/data/cleanup', {
older_than_days: olderThanDays,
})
.then((r) => r.data);
}
// Track record
export function resetTrackRecord() {
return apiClient
.post<{ trade_setups: number }>('admin/track-record/reset')
.then((r) => r.data);
}