@@ -3,7 +3,8 @@ import { ParsedData } from './interfaces'
33
44// Column indices for Russell CSV format
55const INDEX_NAME_COLUMN = 0
6- const VALUE_COLUMN = 4
6+ const CLOSE_VALUE_COLUMN = 4
7+ const FIRST_DATA_ROW = 7
78
89/**
910 * Specific data structure for Russell Daily Values data
@@ -32,104 +33,33 @@ export class RussellDailyValuesParser extends BaseCSVParser {
3233 }
3334
3435 async parse ( csvContent : string ) : Promise < RussellDailyValuesData [ ] > {
35- const results : RussellDailyValuesData [ ] = [ ]
36-
37- if ( ! this . validateFormat ( csvContent ) ) {
38- throw new Error ( 'Invalid CSV format for Russell data' )
39- }
40-
4136 // Russell data starts after the header rows, which vary in position
4237 // Parse the entire CSV and find Russell rows dynamically
4338 const parsed = this . parseCSV ( csvContent , {
39+ from_line : FIRST_DATA_ROW , // Start parsing from line 7 (includes header)
4440 relax_column_count : true , // Allow rows with different number of columns
4541 } )
4642
47- for ( const row of parsed ) {
48- if ( ! row || row . length < 5 ) {
49- // Need at least 5 columns for index name and close value
50- continue // Skip rows with insufficient fields
51- }
52-
53- // Skip empty rows (CSV contains separator rows with multiple empty fields)
54- const hasContent = row . some ( ( field : any ) => field && String ( field ) . trim ( ) !== '' )
55- if ( ! hasContent ) {
56- continue // Skip empty rows
57- }
58-
59- const indexName = this . convertValue ( row [ INDEX_NAME_COLUMN ] , 'string' ) as string
60-
61- // Only process rows that start with "Russell" and contain the ® symbol
62- if (
63- ! indexName ||
64- ! indexName . includes ( 'Russell' ) ||
65- ( ! indexName . includes ( '®' ) && ! indexName . includes ( '�' ) )
66- ) {
67- continue
68- }
69-
70- const data : RussellDailyValuesData = {
71- indexName,
72- close : this . convertValue ( row [ VALUE_COLUMN ] , 'number' ) as number | null ,
73- }
74-
75- // Filter by instrument if specified
76- if ( this . instrument ) {
77- // Normalize both strings for comparison (remove special characters and extra spaces)
78- const normalizeString = ( str : string ) =>
79- str . replace ( / [ ® � ™ ] / g, '' ) . replace ( / \s + / g, ' ' ) . trim ( )
80- const normalizedIndexName = normalizeString ( indexName )
81- const normalizedInstrument = normalizeString ( this . instrument )
43+ const results : RussellDailyValuesData [ ] = parsed
44+ . filter ( ( row ) => row . length > CLOSE_VALUE_COLUMN ) // Keep rows with enough columns
45+ . map ( ( row ) => ( {
46+ row,
47+ indexName : this . convertValue ( row [ INDEX_NAME_COLUMN ] , 'string' ) as string ,
48+ } ) )
49+ . filter ( ( { indexName } ) => indexName && indexName === this . instrument ) // Only process rows that match the instrument
50+ . map (
51+ ( { row, indexName } ) : RussellDailyValuesData => ( {
52+ indexName,
53+ close : this . convertValue ( row [ CLOSE_VALUE_COLUMN ] , 'number' ) as number | null ,
54+ } ) ,
55+ )
8256
83- if ( normalizedIndexName === normalizedInstrument ) {
84- results . push ( data )
85- }
86- } else {
87- results . push ( data )
88- }
57+ if ( results . length === 0 ) {
58+ throw new Error ( 'No matching Russell index records found' )
59+ } else if ( results . length > 1 ) {
60+ throw new Error ( 'Multiple matching Russell index records found, expected only one' )
8961 }
9062
9163 return results
9264 }
93-
94- /**
95- * Validate that the CSV contains Russell index data
96- */
97- validateFormat ( csvContent : string ) : boolean {
98- if ( ! csvContent || csvContent . trim ( ) . length === 0 ) {
99- return false
100- }
101-
102- try {
103- // Parse the entire CSV with relaxed column count to find Russell data
104- const parsed = this . parseCSV ( csvContent , {
105- relax_column_count : true ,
106- } )
107-
108- if ( ! parsed || parsed . length === 0 ) {
109- console . error ( 'No data rows found in CSV for Russell validation' )
110- return false
111- }
112-
113- // Check if any row contains valid Russell index data
114- const hasValidRussellData = parsed . some (
115- ( row ) =>
116- row &&
117- row . length >= 5 && // Must have at least 5 columns for index name and close value
118- row [ INDEX_NAME_COLUMN ] &&
119- String ( row [ INDEX_NAME_COLUMN ] ) . includes ( 'Russell' ) &&
120- ( String ( row [ INDEX_NAME_COLUMN ] ) . includes ( '®' ) ||
121- String ( row [ INDEX_NAME_COLUMN ] ) . includes ( '�' ) ) ,
122- )
123-
124- if ( ! hasValidRussellData ) {
125- console . error ( 'No valid Russell index data found in CSV validation' )
126- return false
127- }
128-
129- return true
130- } catch ( error ) {
131- console . error ( 'Error during Russell CSV validation:' , error )
132- return false
133- }
134- }
13565}
0 commit comments