feat(financials-v2): add statement snapshot schema and shared types
This commit is contained in:
22
drizzle/0001_glossy_statement_snapshots.sql
Normal file
22
drizzle/0001_glossy_statement_snapshots.sql
Normal 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`);
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
|
||||
73
lib/types.ts
73
lib/types.ts
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user