Honor custom S/R tolerance as a transient detect, refresh levels after OHLCV mutations without failing committed price writes, report per-ticker S/R rebuild failures from admin cleanup, and warn in the admin UI when refresh is partial.
295 lines
7.5 KiB
TypeScript
295 lines
7.5 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 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;
|
|
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 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);
|
|
}
|
|
|
|
// 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);
|
|
}
|