feat(financials-v2): add statement snapshot schema and shared types

This commit is contained in:
2026-03-02 09:33:44 -05:00
parent f34c4bbcee
commit bcf4c69c92
4 changed files with 183 additions and 1 deletions

View File

@@ -0,0 +1,22 @@
CREATE TABLE `filing_statement_snapshot` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`filing_id` integer NOT NULL,
`ticker` text NOT NULL,
`filing_date` text NOT NULL,
`filing_type` text NOT NULL,
`period_end` text,
`statement_bundle` text,
`standardized_bundle` text,
`dimension_bundle` text,
`parse_status` text NOT NULL,
`parse_error` text,
`source` text NOT NULL,
`created_at` text NOT NULL,
`updated_at` text NOT NULL,
FOREIGN KEY (`filing_id`) REFERENCES `filing`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `filing_stmt_filing_uidx` ON `filing_statement_snapshot` (`filing_id`);--> statement-breakpoint
CREATE INDEX `filing_stmt_ticker_date_idx` ON `filing_statement_snapshot` (`ticker`,`filing_date`);--> statement-breakpoint
CREATE INDEX `filing_stmt_date_idx` ON `filing_statement_snapshot` (`filing_date`);--> statement-breakpoint
CREATE INDEX `filing_stmt_status_idx` ON `filing_statement_snapshot` (`parse_status`);

View File

@@ -8,6 +8,13 @@
"when": 1772137733427,
"tag": "0000_cold_silver_centurion",
"breakpoints": true
},
{
"idx": 1,
"version": "6",
"when": 1772417400000,
"tag": "0001_glossy_statement_snapshots",
"breakpoints": true
}
]
}

View File

@@ -40,6 +40,63 @@ type FilingAnalysis = {
};
};
type FinancialStatementKind = 'income' | 'balance' | 'cash_flow' | 'equity' | 'comprehensive_income';
type FilingStatementPeriod = {
id: string;
filingId: number;
accessionNumber: string;
filingDate: string;
periodEnd: string | null;
filingType: '10-K' | '10-Q';
periodLabel: string;
};
type StatementValuesByPeriod = Record<string, number | null>;
type FilingFaithfulStatementSnapshotRow = {
key: string;
label: string;
concept: string | null;
order: number;
depth: number;
isSubtotal: boolean;
values: StatementValuesByPeriod;
};
type StandardizedStatementSnapshotRow = {
key: string;
label: string;
concept: string;
category: string;
sourceConcepts: string[];
values: StatementValuesByPeriod;
};
type DimensionStatementSnapshotRow = {
rowKey: string;
concept: string | null;
periodId: string;
axis: string;
member: string;
value: number | null;
unit: string | null;
};
type FilingStatementBundle = {
periods: FilingStatementPeriod[];
statements: Record<FinancialStatementKind, FilingFaithfulStatementSnapshotRow[]>;
};
type StandardizedStatementBundle = {
periods: FilingStatementPeriod[];
statements: Record<FinancialStatementKind, StandardizedStatementSnapshotRow[]>;
};
type DimensionStatementBundle = {
statements: Record<FinancialStatementKind, DimensionStatementSnapshotRow[]>;
};
const authDateColumn = {
mode: 'timestamp_ms'
} as const;
@@ -192,6 +249,28 @@ export const filing = sqliteTable('filing', {
filingDateIndex: index('filing_date_idx').on(table.filing_date)
}));
export const filingStatementSnapshot = sqliteTable('filing_statement_snapshot', {
id: integer('id').primaryKey({ autoIncrement: true }),
filing_id: integer('filing_id').notNull().references(() => filing.id, { onDelete: 'cascade' }),
ticker: text('ticker').notNull(),
filing_date: text('filing_date').notNull(),
filing_type: text('filing_type').$type<'10-K' | '10-Q'>().notNull(),
period_end: text('period_end'),
statement_bundle: text('statement_bundle', { mode: 'json' }).$type<FilingStatementBundle | null>(),
standardized_bundle: text('standardized_bundle', { mode: 'json' }).$type<StandardizedStatementBundle | null>(),
dimension_bundle: text('dimension_bundle', { mode: 'json' }).$type<DimensionStatementBundle | null>(),
parse_status: text('parse_status').$type<'ready' | 'partial' | 'failed'>().notNull(),
parse_error: text('parse_error'),
source: text('source').$type<'sec_filing_summary' | 'xbrl_instance' | 'companyfacts_fallback'>().notNull(),
created_at: text('created_at').notNull(),
updated_at: text('updated_at').notNull()
}, (table) => ({
filingStatementFilingUnique: uniqueIndex('filing_stmt_filing_uidx').on(table.filing_id),
filingStatementTickerDateIndex: index('filing_stmt_ticker_date_idx').on(table.ticker, table.filing_date),
filingStatementDateIndex: index('filing_stmt_date_idx').on(table.filing_date),
filingStatementStatusIndex: index('filing_stmt_status_idx').on(table.parse_status)
}));
export const filingLink = sqliteTable('filing_link', {
id: integer('id').primaryKey({ autoIncrement: true }),
filing_id: integer('filing_id').notNull().references(() => filing.id, { onDelete: 'cascade' }),
@@ -250,6 +329,7 @@ export const appSchema = {
watchlistItem,
holding,
filing,
filingStatementSnapshot,
filingLink,
taskRun,
portfolioInsight

View File

@@ -127,6 +127,79 @@ export type CompanyFinancialPoint = {
debt: number | null;
};
export type FinancialStatementMode = 'standardized' | 'filing_faithful';
export type FinancialStatementKind = 'income' | 'balance' | 'cash_flow' | 'equity' | 'comprehensive_income';
export type FinancialHistoryWindow = '10y' | 'all';
export type FinancialStatementPeriod = {
id: string;
filingId: number;
accessionNumber: string;
filingDate: string;
periodEnd: string | null;
filingType: Extract<Filing['filing_type'], '10-K' | '10-Q'>;
periodLabel: string;
};
export type StandardizedStatementRow = {
key: string;
label: string;
concept: string;
category: string;
sourceConcepts: string[];
values: Record<string, number | null>;
hasDimensions: boolean;
};
export type FilingFaithfulStatementRow = {
key: string;
label: string;
concept: string | null;
order: number;
depth: number;
isSubtotal: boolean;
values: Record<string, number | null>;
hasDimensions: boolean;
};
export type DimensionBreakdownRow = {
rowKey: string;
concept: string | null;
periodId: string;
axis: string;
member: string;
value: number | null;
unit: string | null;
};
export type CompanyFinancialStatementsResponse = {
company: {
ticker: string;
companyName: string;
cik: string | null;
};
mode: FinancialStatementMode;
statement: FinancialStatementKind;
window: FinancialHistoryWindow;
periods: FinancialStatementPeriod[];
rows: StandardizedStatementRow[] | FilingFaithfulStatementRow[];
nextCursor: string | null;
coverage: {
filings: number;
rows: number;
dimensions: number;
};
dataSourceStatus: {
enabled: boolean;
hydratedFilings: number;
partialFilings: number;
failedFilings: number;
pendingFilings: number;
queuedSync: boolean;
};
dimensionBreakdown: Record<string, DimensionBreakdownRow[]> | null;
};
export type CompanyAiReport = {
accessionNumber: string;
filingDate: string;