feat: competitive intelligence & historical pattern matching layer
This commit is contained in:
@@ -474,3 +474,265 @@ export function useCoverageGaps() {
|
||||
export function useSymbolCoverage() {
|
||||
return useGet<unknown[]>(['symbol-coverage'], 'query', '/api/admin/companies/coverage');
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Competitors (Symbol Registry)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface CompetitorRelationship {
|
||||
id: string;
|
||||
company_a_id: string;
|
||||
company_b_id: string;
|
||||
relationship_type: string;
|
||||
strength: number;
|
||||
bidirectional: boolean;
|
||||
source: string;
|
||||
active: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
// Enriched fields from API
|
||||
ticker?: string;
|
||||
legal_name?: string;
|
||||
}
|
||||
|
||||
export function useCompanyCompetitors(companyId: string | undefined) {
|
||||
return useGet<CompetitorRelationship[]>(
|
||||
['company-competitors', companyId],
|
||||
'registry',
|
||||
`/companies/${companyId}/competitors`,
|
||||
!!companyId,
|
||||
);
|
||||
}
|
||||
|
||||
export function useInferCompetitors(companyId: string) {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: () => apiPost<CompetitorRelationship[]>('registry', `/companies/${companyId}/competitors/infer`, {}),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['company-competitors', companyId] }),
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Historical Patterns (Query API)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface HistoricalPattern {
|
||||
source_ticker: string;
|
||||
target_ticker: string;
|
||||
catalyst_type: string;
|
||||
time_horizon: string;
|
||||
sample_count: number;
|
||||
bullish_pct: number;
|
||||
bearish_pct: number;
|
||||
avg_strength: number;
|
||||
avg_time_to_resolution: number;
|
||||
pattern_confidence: number;
|
||||
data_start: string;
|
||||
data_end: string;
|
||||
tier: string;
|
||||
insufficient_data: boolean;
|
||||
}
|
||||
|
||||
export function useHistoricalPatterns(ticker: string | undefined, params?: { catalyst_type?: string; time_horizon?: string }) {
|
||||
const qs = new URLSearchParams();
|
||||
if (params?.catalyst_type) qs.set('catalyst_type', params.catalyst_type);
|
||||
if (params?.time_horizon) qs.set('time_horizon', params.time_horizon);
|
||||
const path = `/api/patterns/${ticker}${qs.toString() ? '?' + qs : ''}`;
|
||||
return useGet<HistoricalPattern[]>(['historical-patterns', ticker, params], 'query', path, !!ticker);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Competitive Signals (Query API)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface CompetitiveSignal {
|
||||
id: string;
|
||||
source_document_id: string;
|
||||
source_ticker: string;
|
||||
target_ticker: string;
|
||||
catalyst_type: string;
|
||||
pattern_confidence: number;
|
||||
signal_direction: string;
|
||||
signal_strength: number;
|
||||
relationship_strength: number;
|
||||
computed_at: string;
|
||||
}
|
||||
|
||||
export function useCompetitiveSignals(ticker: string | undefined) {
|
||||
return useGet<CompetitiveSignal[]>(
|
||||
['competitive-signals', ticker],
|
||||
'query',
|
||||
`/api/patterns/${ticker}/competitive-signals`,
|
||||
!!ticker,
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Corporate Decisions (Query API)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface CorporateDecision {
|
||||
catalyst_type: string;
|
||||
date: string;
|
||||
summary: string;
|
||||
trend_direction: string;
|
||||
trend_strength: number;
|
||||
sample_count: number;
|
||||
pattern_confidence: number;
|
||||
document_id?: string;
|
||||
}
|
||||
|
||||
export function useCorporateDecisions(ticker: string | undefined) {
|
||||
return useGet<CorporateDecision[]>(
|
||||
['corporate-decisions', ticker],
|
||||
'query',
|
||||
`/api/patterns/${ticker}/decisions`,
|
||||
!!ticker,
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Competitive Layer Toggle (Query API)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface CompetitiveStatus {
|
||||
enabled: boolean;
|
||||
toggled_at: string | null;
|
||||
toggled_by: string | null;
|
||||
}
|
||||
|
||||
export function useCompetitiveStatus() {
|
||||
return useGet<CompetitiveStatus>(['competitive-status'], 'query', '/api/admin/competitive/status');
|
||||
}
|
||||
|
||||
export function useToggleCompetitive() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (enabled: boolean) => apiPut<unknown>('query', '/api/admin/competitive/toggle', { enabled }),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['competitive-status'] }),
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Macro: Global Events (Task 17.1, 17.2)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface GlobalEvent {
|
||||
id: string;
|
||||
event_types: string[];
|
||||
severity: string;
|
||||
affected_regions: string[];
|
||||
affected_sectors: string[];
|
||||
affected_commodities: string[];
|
||||
summary: string;
|
||||
key_facts: string[];
|
||||
estimated_duration: string;
|
||||
confidence: number;
|
||||
source_document_id: string | null;
|
||||
model_provider: string | null;
|
||||
model_name: string | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface MacroImpactRecord {
|
||||
id: string;
|
||||
event_id: string;
|
||||
company_id: string;
|
||||
ticker: string;
|
||||
macro_impact_score: number;
|
||||
impact_direction: string;
|
||||
contributing_factors: string[];
|
||||
confidence: number;
|
||||
computed_at: string;
|
||||
}
|
||||
|
||||
export interface GlobalEventDetail extends GlobalEvent {
|
||||
impacts: MacroImpactRecord[];
|
||||
}
|
||||
|
||||
export interface ExposureProfile {
|
||||
id: string;
|
||||
company_id: string;
|
||||
geographic_revenue_mix: Record<string, number>;
|
||||
supply_chain_regions: string[];
|
||||
key_input_commodities: string[];
|
||||
regulatory_jurisdictions: string[];
|
||||
market_position_tier: string;
|
||||
export_dependency_pct: number;
|
||||
source: string;
|
||||
confidence: number;
|
||||
version: number;
|
||||
active: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface CompanyMacroImpacts {
|
||||
exposure_profile: ExposureProfile | null;
|
||||
impacts: MacroImpactRecord[];
|
||||
}
|
||||
|
||||
export function useGlobalEvents(params?: { severity?: string; region?: string; sector?: string; limit?: number }) {
|
||||
const qs = new URLSearchParams();
|
||||
if (params?.severity) qs.set('severity', params.severity);
|
||||
if (params?.region) qs.set('region', params.region);
|
||||
if (params?.sector) qs.set('sector', params.sector);
|
||||
if (params?.limit) qs.set('limit', String(params.limit));
|
||||
const path = `/api/macro/events${qs.toString() ? '?' + qs : ''}`;
|
||||
return useGet<GlobalEvent[]>(['global-events', params], 'query', path);
|
||||
}
|
||||
|
||||
export function useGlobalEvent(id: string | undefined) {
|
||||
return useGet<GlobalEventDetail>(['global-event', id], 'query', `/api/macro/events/${id}`, !!id);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Macro: Company Impacts (Task 17.3)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function useCompanyMacroImpacts(ticker: string | undefined) {
|
||||
return useGet<CompanyMacroImpacts>(['company-macro-impacts', ticker], 'query', `/api/macro/impacts/${ticker}`, !!ticker);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Macro: Trend Projection (Task 17.5)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface TrendProjection {
|
||||
id: string;
|
||||
trend_window_id: string;
|
||||
projected_direction: string;
|
||||
projected_strength: number;
|
||||
projected_confidence: number;
|
||||
projection_horizon: string;
|
||||
driving_factors: string[];
|
||||
macro_contribution_pct: number;
|
||||
diverges_from_current: boolean;
|
||||
computed_at: string;
|
||||
}
|
||||
|
||||
export function useTrendProjection(trendId: string | undefined) {
|
||||
return useGet<TrendProjection>(['trend-projection', trendId], 'query', `/api/trends/${trendId}/projection`, !!trendId);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Macro: Admin Toggle (Task 17.6)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface MacroStatus {
|
||||
enabled: boolean;
|
||||
toggled_at: string | null;
|
||||
toggled_by: string | null;
|
||||
}
|
||||
|
||||
export function useMacroStatus() {
|
||||
return useGet<MacroStatus>(['macro-status'], 'query', '/api/admin/macro/status');
|
||||
}
|
||||
|
||||
export function useToggleMacro() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (enabled: boolean) => apiPut<unknown>('query', `/api/admin/macro/toggle`, { enabled }),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['macro-status'] }),
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user