Files
signal-platform/frontend/src/api/admin.ts
T
dennisthiessen 49bf3b140e
Deploy / lint (push) Successful in 8s
Deploy / test (push) Successful in 1m48s
Deploy / deploy (push) Successful in 40s
Add Admin control for fundamentals cutover
2026-07-24 16:15:51 +02:00

395 lines
10 KiB
TypeScript

import apiClient from './client';
import type {
ActivationConfig,
AdminUser,
AlertConfig,
AlertTestResult,
FundamentalsCutoverConfig,
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 getFundamentalsCutoverSettings() {
return apiClient
.get<FundamentalsCutoverConfig>('admin/settings/fundamentals-cutover')
.then((r) => r.data);
}
export function updateFundamentalsCutoverSettings(enabled: boolean) {
return apiClient
.put<FundamentalsCutoverConfig>('admin/settings/fundamentals-cutover', { 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 interface PerformanceConfig {
start_date: string;
}
export function getPerformanceSettings() {
return apiClient
.get<PerformanceConfig>('admin/settings/performance')
.then((r) => r.data);
}
export function updatePerformanceSettings(payload: Partial<PerformanceConfig>) {
return apiClient
.put<PerformanceConfig>('admin/settings/performance', payload)
.then((r) => r.data);
}
export interface ShadowBookConfig {
enabled: boolean;
capacity: number;
risk_pct: number;
start_equity: number;
}
export function getShadowBookSettings() {
return apiClient
.get<ShadowBookConfig>('admin/settings/shadow-book')
.then((r) => r.data);
}
export function updateShadowBookSettings(payload: Partial<ShadowBookConfig>) {
return apiClient
.put<ShadowBookConfig>('admin/settings/shadow-book', 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;
regime_quadrant_enabled?: boolean;
trade_closed_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);
}
export function backfillTickerNames() {
return apiClient
.post<{ updated: number; checked: number; unmatched: number }>('admin/tickers/backfill-names')
.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;
target_model?: BacktestTargetModel;
cadence?: BacktestCadence;
}
export interface ParityFieldStats {
legacy_available: number;
candidate_available: number;
both_available: number;
material_differences: number;
median_absolute_delta: number | null;
p95_absolute_delta: number | null;
max_absolute_delta: number | null;
}
export interface FundamentalsParityReport {
report_version: number;
generated_at: string;
as_of_date: string;
approval_status: string;
read_only: boolean;
summary: {
universe_count: number;
legacy_fundamental_score_available: number;
candidate_fundamental_score_available: number;
fundamental_scores_compared: number;
fundamental_score_material_changes: number;
fundamental_rank_changes: number;
field_stats: Record<string, ParityFieldStats>;
};
source_runs: Record<string, {
run_id: number;
status: string;
revision: string | null;
source_max_date: string | null;
completed_at: string | null;
} | null>;
}
export type BacktestTargetModel = 'production_gtl' | 'structural_sr';
export type BacktestCadence = 'weekly' | 'daily';
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,
options?: { target_model?: BacktestTargetModel; cadence?: BacktestCadence },
) {
return apiClient
.post<TriggerJobResponse>(`admin/jobs/${jobName}/trigger`, options)
.then((r) => r.data);
}
export function getFundamentalsParityReport() {
return apiClient
.get<FundamentalsParityReport | null>('admin/fundamentals-parity')
.then((r) => r.data);
}
export function getFundamentalsParityCsv() {
return apiClient
.get<{ filename: string; content: string } | null>('admin/fundamentals-parity/csv')
.then((r) => r.data);
}
export function getFundamentalsParityJson() {
return apiClient
.get<{ filename: string; content: string } | null>('admin/fundamentals-parity/json')
.then((r) => r.data);
}
// System events (operational warnings / errors)
export interface SystemEvent {
id: number;
severity: 'warning' | 'error' | string;
source: string;
code: string;
message: string;
symbol: string | null;
created_at: string | null;
acknowledged_at: string | null;
}
export interface SystemEventSummary {
days: number;
total: number;
unacknowledged: number;
unacknowledged_errors: number;
unacknowledged_warnings: number;
}
export function listSystemEvents(params?: {
days?: number;
severity?: string;
unacknowledged_only?: boolean;
}) {
return apiClient
.get<SystemEvent[]>('admin/system-events', { params })
.then((r) => r.data);
}
export function getSystemEventSummary(days = 7) {
return apiClient
.get<SystemEventSummary>('admin/system-events/summary', { params: { days } })
.then((r) => r.data);
}
export function acknowledgeSystemEvents(days = 7) {
return apiClient
.post<{ acknowledged: number }>('admin/system-events/acknowledge', null, {
params: { days },
})
.then((r) => r.data);
}
// Data cleanup
export interface CleanupResult {
ohlcv: number;
sentiment: number;
fundamentals: number;
sr_refresh_ok: number;
sr_refresh_failed: number;
sr_refresh_failures: { symbol: string; error: string }[];
}
export function cleanupData(olderThanDays: number) {
return apiClient
.post<CleanupResult>('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);
}