feat: Specialist Desks — per asset-class fundamental configs + report catalogue
- 7 pre-seeded desks (Forex, Metals, Agri, Energy, Indices, Crypto, Bonds) each with default fundamental drivers, macro regime sensitivities and price delta thresholds - Global report catalogue (specialist_reports) fully manual — add any report including non-calendar ones (e.g. Cocoa Grinding Report, ICCO) - Many-to-many report ↔ desk linking (report_desk_links table) - 12 default reports pre-seeded (COT, EIA, WASDE, FOMC, ECB, CPI, NFP…) - AI scorer injects SPECIALIST DESK context block for asset classes present in each scoring batch (upcoming reports, key drivers, regime sensitivity) - /specialist-desks page: desk sidebar + fundamentals editor + macro sensitivity tag editor + reports tab + global reports catalogue + modal to create/edit any report with desk assignment Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1101,3 +1101,106 @@ export const useEvaluateInstrumentScan = () => {
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['pattern-lab-runs'] }),
|
||||
})
|
||||
}
|
||||
|
||||
// ── Specialist Desks ───────────────────────────────────────────────────────────
|
||||
|
||||
export type MacroSensitivity = { regime: string; effect: string }
|
||||
export type PriceDeltaThresholds = {
|
||||
significant_day: number; extreme_day: number
|
||||
significant_week: number; extreme_week: number
|
||||
}
|
||||
export type SpecialistReport = {
|
||||
id: string; name: string; source: string; cadence: string
|
||||
next_date: string | null; last_date: string | null
|
||||
importance: number; notes: string; url: string
|
||||
desks?: string[]
|
||||
created_at: string; updated_at: string
|
||||
}
|
||||
export type DeskConfig = {
|
||||
asset_class: string; display_name: string; icon: string
|
||||
fundamentals: string; macro_sensitivity: MacroSensitivity[]
|
||||
price_delta_thresholds: PriceDeltaThresholds; notes: string
|
||||
reports: SpecialistReport[]; updated_at: string
|
||||
}
|
||||
|
||||
export const useAllDeskConfigs = () =>
|
||||
useQuery<DeskConfig[]>({
|
||||
queryKey: ['desk-configs'],
|
||||
queryFn: () => api.get('/specialist-desks/configs').then(r => r.data),
|
||||
staleTime: 60_000,
|
||||
})
|
||||
|
||||
export const useUpdateDeskConfig = () => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ asset_class, body }: { asset_class: string; body: Partial<DeskConfig> }) =>
|
||||
api.put(`/specialist-desks/configs/${asset_class}`, body).then(r => r.data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['desk-configs'] }),
|
||||
})
|
||||
}
|
||||
|
||||
export const useAllSpecialistReports = () =>
|
||||
useQuery<SpecialistReport[]>({
|
||||
queryKey: ['specialist-reports'],
|
||||
queryFn: () => api.get('/specialist-desks/reports').then(r => r.data),
|
||||
staleTime: 60_000,
|
||||
})
|
||||
|
||||
export const useCreateSpecialistReport = () => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (body: Partial<SpecialistReport> & { desks?: string[] }) =>
|
||||
api.post('/specialist-desks/reports', body).then(r => r.data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['specialist-reports'] })
|
||||
qc.invalidateQueries({ queryKey: ['desk-configs'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const useUpdateSpecialistReport = () => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ id, body }: { id: string; body: Partial<SpecialistReport> & { desks?: string[] } }) =>
|
||||
api.put(`/specialist-desks/reports/${id}`, body).then(r => r.data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['specialist-reports'] })
|
||||
qc.invalidateQueries({ queryKey: ['desk-configs'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const useDeleteSpecialistReport = () => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => api.delete(`/specialist-desks/reports/${id}`).then(r => r.data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['specialist-reports'] })
|
||||
qc.invalidateQueries({ queryKey: ['desk-configs'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const useLinkReportDesk = () => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ report_id, asset_class }: { report_id: string; asset_class: string }) =>
|
||||
api.post(`/specialist-desks/reports/${report_id}/link/${asset_class}`).then(r => r.data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['specialist-reports'] })
|
||||
qc.invalidateQueries({ queryKey: ['desk-configs'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const useUnlinkReportDesk = () => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ report_id, asset_class }: { report_id: string; asset_class: string }) =>
|
||||
api.delete(`/specialist-desks/reports/${report_id}/link/${asset_class}`).then(r => r.data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['specialist-reports'] })
|
||||
qc.invalidateQueries({ queryKey: ['desk-configs'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user