feat: competitive intelligence & historical pattern matching layer

This commit is contained in:
Celes Renata
2026-04-14 19:42:48 +00:00
parent b478022ba3
commit f7a11d14ea
203 changed files with 20155 additions and 97 deletions
+262
View File
@@ -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'] }),
});
}