import { CHINA_ACTIVITY_PROXY_FAMILIES, CHINA_ACTIVITY_PROXY_REGISTRY, type ChinaActivityProxyDefinition, type ChinaActivityProxyFamily, } from './china-activity-nowcast-registry'; export const CHINA_ACTIVITY_NOWCAST_METHOD_VERSION = 'china-activity-nowcast/v1' as const; export const CHINA_ACTIVITY_COMPARISON_STATES = [ 'agreement', 'proxy_leading_divergence', 'official_leading_divergence', 'mixed_signals', 'insufficient_data', ] as const; export const CHINA_ACTIVITY_DIRECTIONS = [ 'strengthening', 'weakening', 'unchanged', ] as const; export type ChinaActivityComparisonState = (typeof CHINA_ACTIVITY_COMPARISON_STATES)[number]; export type ChinaActivityDirection = (typeof CHINA_ACTIVITY_DIRECTIONS)[number]; export { CHINA_ACTIVITY_PROXY_FAMILIES, CHINA_ACTIVITY_PROXY_REGISTRY, type ChinaActivityProxyDefinition, type ChinaActivityProxyFamily, } from './china-activity-nowcast-registry'; export interface ChinaActivityOfficialObservation { seriesId: string; label: string; vintageId: string; observationPeriod: string; periodEnd: string; releaseTime: string; retrievalTime: string; direction: ChinaActivityDirection; value: number; unit: string; available: boolean; stale: boolean; provenance: unknown; } export interface ChinaActivityProxyObservation { seriesId: string; observationId: string; observedAt: string; releasedAt: string; retrievedAt: string; value: number | null; priorValue: number | null; available: boolean; stale: boolean; structuralBreak: boolean; provenance: unknown; } export interface ChinaActivityContribution { family: ChinaActivityProxyFamily; seriesId: string; registry: Readonly; observationId: string | null; observedAt: string | null; alignedAt: string | null; rawValue: number | null; priorValue: number | null; transformedValue: number | null; direction: ChinaActivityDirection | null; included: boolean; exclusionReason: string | null; provenance: unknown; } export interface ChinaActivitySensitivity { family: ChinaActivityProxyFamily; contributionCount: number; contributionShare: number; stateWithoutFamily: ChinaActivityComparisonState; changesConclusion: boolean; } export interface ChinaActivityNowcastResponse { methodVersion: typeof CHINA_ACTIVITY_NOWCAST_METHOD_VERSION; evaluatedAt: string; comparisonWindow: { days: number; startsAt: string; endsAt: string; forwardFill: false; interpolate: false; }; state: ChinaActivityComparisonState; official: ChinaActivityOfficialObservation | null; contributions: ChinaActivityContribution[]; missingInputs: Array<{ family: ChinaActivityProxyFamily; seriesId: string; reason: string; }>; confidence: { level: 'high' | 'medium' | 'low' | 'insufficient'; reason: string; eligibleFamilies: number; totalFamilies: number; }; sensitivity: ChinaActivitySensitivity[]; historicalEvaluation: { available: false; reason: string; noLookahead: true; attempted: 0; evaluated: 0; coverage: 0; directionalAgreement: null; }; limitations: string[]; audit: { deterministic: true; llmNumericComputation: false; noLookahead: true; excludedFutureObservationIds: string[]; officialCandidatesConsidered: number; }; } export interface EvaluateChinaActivityNowcastInput { evaluatedAt: string; comparisonWindowDays?: number; minimumProxyFamilies?: number; officialObservations: readonly ChinaActivityOfficialObservation[]; proxyObservations: readonly ChinaActivityProxyObservation[]; } const registryById = new Map( CHINA_ACTIVITY_PROXY_REGISTRY.map((definition) => [definition.id, definition]), ); const validDirections = new Set(CHINA_ACTIVITY_DIRECTIONS); const validStates = new Set(CHINA_ACTIVITY_COMPARISON_STATES); const MILLISECONDS_PER_DAY = 86_400_000; function instant(value: string): number | null { const parsed = Date.parse(value); return Number.isFinite(parsed) ? parsed : null; } function finite(value: unknown): value is number { return typeof value === 'number' && Number.isFinite(value); } function hasProvenance(value: unknown): boolean { return value !== null && typeof value === 'object' && !Array.isArray(value); } function selectOfficial( observations: readonly ChinaActivityOfficialObservation[], evaluatedAtMs: number, ): ChinaActivityOfficialObservation | null { return observations .filter((observation) => { const periodEnd = instant(observation.periodEnd); const releaseTime = instant(observation.releaseTime); const retrievalTime = instant(observation.retrievalTime); return periodEnd !== null && releaseTime !== null && retrievalTime !== null && periodEnd <= releaseTime && releaseTime <= retrievalTime && retrievalTime <= evaluatedAtMs && observation.available && !observation.stale && finite(observation.value) && validDirections.has(observation.direction) && hasProvenance(observation.provenance); }) .sort((left, right) => { const period = (instant(right.periodEnd) ?? 0) - (instant(left.periodEnd) ?? 0); if (period !== 0) return period; return (instant(right.retrievalTime) ?? 0) - (instant(left.retrievalTime) ?? 0); })[0] ?? null; } function transformedValue( definition: Readonly, observation: ChinaActivityProxyObservation, ): number | null { if (!finite(observation.value)) return null; const value = definition.transformation.kind === 'signed_value' ? observation.value : finite(observation.priorValue) && observation.priorValue !== 0 ? ((observation.value - observation.priorValue) / Math.abs(observation.priorValue)) * 100 : null; if (value === null) return null; return definition.transformation.direction === 'inverse' ? -value : value; } function directionFromValue(value: number): ChinaActivityDirection { if (value > 1e-9) return 'strengthening'; if (value < -1e-9) return 'weakening'; return 'unchanged'; } function excludedContribution( definition: Readonly, reason: string, observation: ChinaActivityProxyObservation | null = null, ): ChinaActivityContribution { return { family: definition.family, seriesId: definition.id, registry: definition, observationId: observation?.observationId ?? null, observedAt: observation?.observedAt ?? null, alignedAt: null, rawValue: observation?.value ?? null, priorValue: observation?.priorValue ?? null, transformedValue: null, direction: null, included: false, exclusionReason: reason, provenance: observation?.provenance ?? null, }; } function contributionFor( definition: Readonly, observations: readonly ChinaActivityProxyObservation[], evaluatedAtMs: number, windowStartsAtMs: number, ): ChinaActivityContribution { const latest = observations .filter((observation) => observation.seriesId === definition.id) .filter((observation) => { const observedAt = instant(observation.observedAt); const releasedAt = instant(observation.releasedAt); const retrievedAt = instant(observation.retrievedAt); return observedAt !== null && releasedAt !== null && retrievedAt !== null && observedAt <= releasedAt && releasedAt <= retrievedAt && retrievedAt <= evaluatedAtMs; }) .sort((left, right) => (instant(right.observedAt) ?? 0) - (instant(left.observedAt) ?? 0))[0] ?? null; if (latest === null) return excludedContribution(definition, 'no_observation'); const observedAtMs = instant(latest.observedAt)!; if (latest.structuralBreak) { return excludedContribution(definition, 'structural_break', latest); } if (!latest.available) { return excludedContribution(definition, 'unavailable', latest); } if (latest.stale) { return excludedContribution(definition, 'marked_stale', latest); } if (!hasProvenance(latest.provenance)) { return excludedContribution(definition, 'missing_provenance', latest); } const alignedAtMs = observedAtMs + definition.lagRule.days * MILLISECONDS_PER_DAY; if (alignedAtMs > evaluatedAtMs) { return excludedContribution(definition, 'lag_not_elapsed', latest); } if (alignedAtMs < windowStartsAtMs) { return excludedContribution(definition, 'outside_comparison_window_no_fill', latest); } if ( evaluatedAtMs - observedAtMs > definition.freshnessBudgetMinutes * 60_000 ) { return excludedContribution(definition, 'freshness_budget_exceeded', latest); } const transformed = transformedValue(definition, latest); if (transformed === null) { return excludedContribution( definition, definition.transformation.kind === 'percentage_change' ? 'missing_comparable_prior' : 'missing_directional_value', latest, ); } return { family: definition.family, seriesId: definition.id, registry: definition, observationId: latest.observationId, observedAt: latest.observedAt, alignedAt: new Date(alignedAtMs).toISOString(), rawValue: latest.value, priorValue: latest.priorValue, transformedValue: transformed, direction: directionFromValue(transformed), included: true, exclusionReason: null, provenance: latest.provenance, }; } function median(values: number[]): number | null { if (values.length === 0) return null; const sorted = [...values].sort((left, right) => left - right); const middle = Math.floor(sorted.length / 2); return sorted.length % 2 === 0 ? ((sorted[middle - 1] ?? 0) + (sorted[middle] ?? 0)) / 2 : sorted[middle] ?? null; } function classify( official: ChinaActivityOfficialObservation | null, contributions: readonly ChinaActivityContribution[], minimumProxyFamilies: number, ): ChinaActivityComparisonState { if (official === null || official.direction === 'unchanged') return 'insufficient_data'; const directional = contributions.filter((contribution) => contribution.included && contribution.direction !== 'unchanged'); const families = new Set(directional.map((contribution) => contribution.family)); if (families.size < minimumProxyFamilies) return 'insufficient_data'; const strengthening = directional.filter((item) => item.direction === 'strengthening').length; const weakening = directional.filter((item) => item.direction === 'weakening').length; const total = strengthening + weakening; const leading = Math.max(strengthening, weakening); if (total === 0 || leading / total < 2 / 3) return 'mixed_signals'; const proxyDirection: Exclude = strengthening > weakening ? 'strengthening' : 'weakening'; if (proxyDirection === official.direction) return 'agreement'; const proxyMedian = median(directional .map((item) => item.alignedAt === null ? null : instant(item.alignedAt)) .filter((value): value is number => value !== null)); const officialPeriodEnd = instant(official.periodEnd); return ( proxyMedian !== null && officialPeriodEnd !== null && proxyMedian > officialPeriodEnd ) ? 'proxy_leading_divergence' : 'official_leading_divergence'; } function confidenceFor( state: ChinaActivityComparisonState, official: ChinaActivityOfficialObservation | null, contributions: readonly ChinaActivityContribution[], sensitivity: readonly ChinaActivitySensitivity[], minimumProxyFamilies: number, ): ChinaActivityNowcastResponse['confidence'] { const eligibleFamilies = new Set( contributions.filter((item) => item.included).map((item) => item.family), ).size; if (state === 'insufficient_data') { const directionalFamilies = new Set( contributions .filter((item) => item.included && item.direction !== 'unchanged') .map((item) => item.family), ).size; const reason = official === null ? 'One eligible official vintage is required.' : official.direction === 'unchanged' ? 'The eligible official vintage is unchanged; a directional comparison requires strengthening or weakening.' : `At least ${minimumProxyFamilies} non-flat proxy families are required; ${directionalFamilies} are eligible.`; return { level: 'insufficient', reason, eligibleFamilies, totalFamilies: CHINA_ACTIVITY_PROXY_FAMILIES.length, }; } const conclusionSensitive = sensitivity.some((item) => item.changesConclusion); const coverage = eligibleFamilies / CHINA_ACTIVITY_PROXY_FAMILIES.length; const level = !conclusionSensitive && coverage >= 0.75 ? 'high' : coverage >= 0.5 ? 'medium' : 'low'; return { level, reason: conclusionSensitive ? 'At least one proxy family changes the leave-one-family-out conclusion.' : `Eligible proxy-family coverage is ${eligibleFamilies}/${CHINA_ACTIVITY_PROXY_FAMILIES.length}.`, eligibleFamilies, totalFamilies: CHINA_ACTIVITY_PROXY_FAMILIES.length, }; } function sensitivityFor( official: ChinaActivityOfficialObservation | null, contributions: readonly ChinaActivityContribution[], state: ChinaActivityComparisonState, minimumProxyFamilies: number, ): ChinaActivitySensitivity[] { const includedCount = contributions.filter((item) => item.included).length; return CHINA_ACTIVITY_PROXY_FAMILIES.map((family) => { const withoutFamily = contributions.filter((item) => item.family !== family); const contributionCount = contributions.filter((item) => item.family === family && item.included).length; const stateWithoutFamily = classify(official, withoutFamily, minimumProxyFamilies); return { family, contributionCount, contributionShare: includedCount === 0 ? 0 : contributionCount / includedCount, stateWithoutFamily, changesConclusion: contributionCount > 0 && stateWithoutFamily !== state, }; }); } export function evaluateChinaActivityNowcast( input: EvaluateChinaActivityNowcastInput, ): ChinaActivityNowcastResponse { const evaluatedAtMs = instant(input.evaluatedAt); if (evaluatedAtMs === null) throw new Error('Invalid China activity nowcast evaluation time'); const comparisonWindowDays = Math.max(1, Math.trunc(input.comparisonWindowDays ?? 90)); const minimumProxyFamilies = Math.max(1, Math.trunc(input.minimumProxyFamilies ?? 3)); const windowStartsAtMs = evaluatedAtMs - comparisonWindowDays * MILLISECONDS_PER_DAY; const official = selectOfficial(input.officialObservations, evaluatedAtMs); const contributions = CHINA_ACTIVITY_PROXY_REGISTRY.map((definition) => contributionFor( definition, input.proxyObservations, evaluatedAtMs, windowStartsAtMs, )); const state = classify(official, contributions, minimumProxyFamilies); const sensitivity = sensitivityFor( official, contributions, state, minimumProxyFamilies, ); const excludedFutureObservationIds = input.proxyObservations .filter((observation) => { const observed = instant(observation.observedAt); const released = instant(observation.releasedAt); const retrieved = instant(observation.retrievedAt); return observed === null || released === null || retrieved === null || observed > evaluatedAtMs || released > evaluatedAtMs || retrieved > evaluatedAtMs; }) .map((observation) => observation.observationId) .sort(); return { methodVersion: CHINA_ACTIVITY_NOWCAST_METHOD_VERSION, evaluatedAt: new Date(evaluatedAtMs).toISOString(), comparisonWindow: { days: comparisonWindowDays, startsAt: new Date(windowStartsAtMs).toISOString(), endsAt: new Date(evaluatedAtMs).toISOString(), forwardFill: false, interpolate: false, }, state, official, contributions, missingInputs: contributions .filter((item) => !item.included) .map((item) => ({ family: item.family, seriesId: item.seriesId, reason: item.exclusionReason ?? 'unavailable', })), confidence: confidenceFor( state, official, contributions, sensitivity, minimumProxyFamilies, ), sensitivity, historicalEvaluation: { available: false, reason: 'The live cache does not yet contain a bounded historical proxy ledger; no backtest is inferred from current-only snapshots.', noLookahead: true, attempted: 0, evaluated: 0, coverage: 0, directionalAgreement: null, }, limitations: [ 'This is a directional comparison, not a replacement GDP estimate or a hidden-activity verification.', 'Market prices and freight rates can move for global supply, capacity, and risk reasons unrelated to China activity.', 'Missing, stale, structurally changed, or provenance-free inputs are excluded rather than treated as neutral.', ], audit: { deterministic: true, llmNumericComputation: false, noLookahead: true, excludedFutureObservationIds, officialCandidatesConsidered: input.officialObservations.length, }, }; } export interface BacktestChinaActivityNowcastInput { evaluationTimes: readonly string[]; comparisonWindowDays?: number; minimumProxyFamilies?: number; officialObservations: readonly ChinaActivityOfficialObservation[]; proxyObservations: readonly ChinaActivityProxyObservation[]; } export interface ChinaActivityNowcastBacktest { methodVersion: typeof CHINA_ACTIVITY_NOWCAST_METHOD_VERSION; noLookahead: true; attempted: number; evaluated: number; coverage: number; directionalAgreement: number | null; rows: Array<{ evaluatedAt: string; state: ChinaActivityComparisonState; officialVintageId: string | null; directionalAgreement: boolean | null; eligibleFamilies: number; }>; } export function backtestChinaActivityNowcast( input: BacktestChinaActivityNowcastInput, ): ChinaActivityNowcastBacktest { const rows = input.evaluationTimes.map((evaluatedAt) => { const result = evaluateChinaActivityNowcast({ evaluatedAt, comparisonWindowDays: input.comparisonWindowDays, minimumProxyFamilies: input.minimumProxyFamilies, officialObservations: input.officialObservations, proxyObservations: input.proxyObservations, }); const directionalAgreement = result.state === 'agreement' ? true : result.state === 'proxy_leading_divergence' || result.state === 'official_leading_divergence' ? false : null; return { evaluatedAt: result.evaluatedAt, state: result.state, officialVintageId: result.official?.vintageId ?? null, directionalAgreement, eligibleFamilies: result.confidence.eligibleFamilies, }; }); const evaluatedRows = rows.filter((row) => row.directionalAgreement !== null); const agreements = evaluatedRows.filter((row) => row.directionalAgreement).length; return { methodVersion: CHINA_ACTIVITY_NOWCAST_METHOD_VERSION, noLookahead: true, attempted: rows.length, evaluated: evaluatedRows.length, coverage: rows.length === 0 ? 0 : evaluatedRows.length / rows.length, directionalAgreement: evaluatedRows.length === 0 ? null : agreements / evaluatedRows.length, rows, }; } function isRecord(value: unknown): value is Record { return value !== null && typeof value === 'object' && !Array.isArray(value); } function isStringOrNull(value: unknown): value is string | null { return value === null || typeof value === 'string'; } function isFiniteOrNull(value: unknown): value is number | null { return value === null || finite(value); } function isInstantOrNull(value: unknown): value is string | null { return value === null || (typeof value === 'string' && instant(value) !== null); } function isOfficialObservation( value: unknown, ): value is ChinaActivityOfficialObservation { return isRecord(value) && typeof value.seriesId === 'string' && typeof value.label === 'string' && typeof value.vintageId === 'string' && typeof value.observationPeriod === 'string' && typeof value.periodEnd === 'string' && instant(value.periodEnd) !== null && typeof value.releaseTime === 'string' && instant(value.releaseTime) !== null && typeof value.retrievalTime === 'string' && instant(value.retrievalTime) !== null && validDirections.has(String(value.direction)) && finite(value.value) && typeof value.unit === 'string' && typeof value.available === 'boolean' && typeof value.stale === 'boolean' && hasProvenance(value.provenance); } function isContribution(value: unknown): value is ChinaActivityContribution { if (!isRecord(value)) return false; const definition = registryById.get(String(value.seriesId)); if ( definition === undefined || value.family !== definition.family || !isRecord(value.registry) || value.registry.id !== definition.id || !isStringOrNull(value.observationId) || !isInstantOrNull(value.observedAt) || !isInstantOrNull(value.alignedAt) || !isFiniteOrNull(value.rawValue) || !isFiniteOrNull(value.priorValue) || !isFiniteOrNull(value.transformedValue) || typeof value.included !== 'boolean' || !isStringOrNull(value.exclusionReason) ) return false; if (value.included) { return typeof value.observationId === 'string' && typeof value.observedAt === 'string' && typeof value.alignedAt === 'string' && finite(value.transformedValue) && validDirections.has(String(value.direction)) && value.exclusionReason === null && hasProvenance(value.provenance); } return value.direction === null && value.transformedValue === null && typeof value.exclusionReason === 'string'; } function isMissingInput(value: unknown): boolean { if (!isRecord(value)) return false; const definition = registryById.get(String(value.seriesId)); return definition !== undefined && value.family === definition.family && typeof value.reason === 'string'; } function isSensitivity(value: unknown): boolean { return isRecord(value) && CHINA_ACTIVITY_PROXY_FAMILIES.includes(value.family as ChinaActivityProxyFamily) && Number.isInteger(value.contributionCount) && Number(value.contributionCount) >= 0 && finite(value.contributionShare) && value.contributionShare >= 0 && value.contributionShare <= 1 && validStates.has(String(value.stateWithoutFamily)) && typeof value.changesConclusion === 'boolean'; } function equalSensitivity( left: readonly ChinaActivitySensitivity[], right: readonly ChinaActivitySensitivity[], ): boolean { return left.length === right.length && left.every((item, index) => { const expected = right[index]; return expected !== undefined && item.family === expected.family && item.contributionCount === expected.contributionCount && item.contributionShare === expected.contributionShare && item.stateWithoutFamily === expected.stateWithoutFamily && item.changesConclusion === expected.changesConclusion; }); } export function isChinaActivityNowcastUpstreamUnavailable( response: ChinaActivityNowcastResponse, ): boolean { return response.official === null && response.contributions.every((contribution) => !contribution.included); } function isChinaActivityNowcastResponse(value: unknown): value is ChinaActivityNowcastResponse { if (!isRecord(value)) return false; if ( value.methodVersion !== CHINA_ACTIVITY_NOWCAST_METHOD_VERSION || !validStates.has(String(value.state)) || instant(String(value.evaluatedAt)) === null || !isRecord(value.comparisonWindow) || !Number.isInteger(value.comparisonWindow.days) || Number(value.comparisonWindow.days) < 1 || typeof value.comparisonWindow.startsAt !== 'string' || instant(value.comparisonWindow.startsAt) === null || typeof value.comparisonWindow.endsAt !== 'string' || instant(value.comparisonWindow.endsAt) === null || value.comparisonWindow.endsAt !== value.evaluatedAt || value.comparisonWindow.forwardFill !== false || value.comparisonWindow.interpolate !== false || !Array.isArray(value.contributions) || value.contributions.length !== CHINA_ACTIVITY_PROXY_REGISTRY.length || !value.contributions.every((item, index) => isContribution(item) && item.seriesId === CHINA_ACTIVITY_PROXY_REGISTRY[index]?.id) || !Array.isArray(value.missingInputs) || !value.missingInputs.every(isMissingInput) || !isRecord(value.confidence) || !['high', 'medium', 'low', 'insufficient'].includes(String(value.confidence.level)) || typeof value.confidence.reason !== 'string' || !Number.isInteger(value.confidence.eligibleFamilies) || Number(value.confidence.eligibleFamilies) < 0 || Number(value.confidence.eligibleFamilies) > CHINA_ACTIVITY_PROXY_FAMILIES.length || value.confidence.totalFamilies !== CHINA_ACTIVITY_PROXY_FAMILIES.length || !Array.isArray(value.sensitivity) || value.sensitivity.length !== CHINA_ACTIVITY_PROXY_FAMILIES.length || !value.sensitivity.every(isSensitivity) || new Set(value.sensitivity.map((item) => (item as Record).family)).size !== CHINA_ACTIVITY_PROXY_FAMILIES.length || !isRecord(value.historicalEvaluation) || value.historicalEvaluation.available !== false || typeof value.historicalEvaluation.reason !== 'string' || value.historicalEvaluation.noLookahead !== true || value.historicalEvaluation.attempted !== 0 || value.historicalEvaluation.evaluated !== 0 || value.historicalEvaluation.coverage !== 0 || value.historicalEvaluation.directionalAgreement !== null || !Array.isArray(value.limitations) || !value.limitations.every((item) => typeof item === 'string') || !isRecord(value.audit) || value.audit.deterministic !== true || value.audit.llmNumericComputation !== false || value.audit.noLookahead !== true || !Array.isArray(value.audit.excludedFutureObservationIds) || !value.audit.excludedFutureObservationIds.every((item) => typeof item === 'string') || !Number.isInteger(value.audit.officialCandidatesConsidered) || Number(value.audit.officialCandidatesConsidered) < 0 ) return false; if (value.official !== null && !isOfficialObservation(value.official)) return false; const candidate = value as unknown as ChinaActivityNowcastResponse; const evaluatedAtMs = instant(candidate.evaluatedAt)!; const expectedWindowStart = new Date( evaluatedAtMs - candidate.comparisonWindow.days * MILLISECONDS_PER_DAY, ).toISOString(); if (candidate.comparisonWindow.startsAt !== expectedWindowStart) return false; if (candidate.official !== null) { const periodEnd = instant(candidate.official.periodEnd)!; const releaseTime = instant(candidate.official.releaseTime)!; const retrievalTime = instant(candidate.official.retrievalTime)!; if ( !candidate.official.available || candidate.official.stale || periodEnd > releaseTime || releaseTime > retrievalTime || retrievalTime > evaluatedAtMs ) return false; } if (candidate.contributions.some((contribution) => (contribution.observedAt !== null && instant(contribution.observedAt)! > evaluatedAtMs) || (contribution.alignedAt !== null && instant(contribution.alignedAt)! > evaluatedAtMs) )) return false; if (classify(candidate.official, candidate.contributions, 3) !== candidate.state) return false; const excluded = candidate.contributions.filter((item) => !item.included); if ( candidate.missingInputs.length !== excluded.length || candidate.missingInputs.some((item, index) => { const contribution = excluded[index]; return contribution === undefined || item.family !== contribution.family || item.seriesId !== contribution.seriesId || item.reason !== contribution.exclusionReason; }) ) return false; const eligibleFamilies = new Set( candidate.contributions.filter((item) => item.included).map((item) => item.family), ).size; if (candidate.confidence.eligibleFamilies !== eligibleFamilies) return false; const expectedSensitivity = sensitivityFor( candidate.official, candidate.contributions, candidate.state, 3, ); if (!equalSensitivity(candidate.sensitivity, expectedSensitivity)) return false; const expectedConfidence = confidenceFor( candidate.state, candidate.official, candidate.contributions, expectedSensitivity, 3, ); return candidate.confidence.level === expectedConfidence.level && candidate.confidence.reason === expectedConfidence.reason && candidate.confidence.eligibleFamilies === expectedConfidence.eligibleFamilies && candidate.confidence.totalFamilies === expectedConfidence.totalFamilies; } export function parseChinaActivityNowcastWirePayload( payloadJson: string, ): ChinaActivityNowcastResponse { let parsed: unknown; try { parsed = JSON.parse(payloadJson); } catch { throw new Error('Invalid China activity nowcast response'); } if (!isChinaActivityNowcastResponse(parsed)) { throw new Error('Invalid China activity nowcast response'); } return { ...parsed, contributions: parsed.contributions.map((contribution) => ({ ...contribution, registry: registryById.get(contribution.seriesId)!, })), }; }