SPB Git

spb/findata

Public
3.6 KB · 73 lines python
1from __future__ import annotations23from datetime import UTC, date, datetime4from pathlib import Path56import pyarrow as pa78from findata_market_firstrate.archive import parse_bar_file, ticker_from_filename9from findata_schema import BARS_SCHEMA, Adjustment, AssetClass, Interval1011FIX = Path(__file__).parent / "fixtures" / "firstrate"12NOW = datetime(2026, 11, 1, 12, 0, tzinfo=UTC)131415def _parse(text: str, symbol="TEST", interval=Interval.m1):16    return parse_bar_file(text.encode(), symbol, AssetClass.stock, interval, Adjustment.raw, "firstrate", NOW)171819def test_ticker_from_filename_variants():20    assert ticker_from_filename("AAPL_day_1min_UNADJUSTED.txt") == "AAPL"21    assert ticker_from_filename("AAPL_1min.txt") == "AAPL"22    assert ticker_from_filename("BRK.B_full_1min_adj_split.txt") == "BRK.B"23    assert ticker_from_filename("TWTR-DELISTED_1min.txt") == "TWTR-DELISTED"24    assert ticker_from_filename("nested.zip!/ES_contin_adj_ratio_1min.txt") == "ES"252627def test_parse_real_session_bar_open_convention():28    t, raw = parse_bar_file((FIX / "AAPL_day_1min_UNADJUSTED.txt").read_bytes(), "AAPL", AssetClass.stock, Interval.m1, Adjustment.raw, "firstrate", NOW)29    assert raw == 900 and t.num_rows == 90030    assert t.schema.equals(BARS_SCHEMA)31    first = t.slice(0, 1).to_pylist()[0]32    # 2026-10-30 04:00 America/New_York (EDT, UTC-4) == 08:00 UTC ; timestamp marks the bar OPEN33    assert first["timestamp_utc"] == datetime(2026, 10, 30, 8, 0, tzinfo=UTC)34    assert first["session_date"] == date(2026, 10, 30)35    assert first["open"] == 350.76 and first["volume"] == 4802.036    assert first["instrument_id"] == "firstrate:stock:AAPL" and first["adjustment_type"] == "raw"37    last = t.slice(899, 1).to_pylist()[0]38    assert last["timestamp_utc"] == datetime(2026, 10, 30, 23, 59, tzinfo=UTC)  # 19:59 EDT394041def test_dst_spring_forward_and_fall_back():42    # 2026-03-06 (EST, UTC-5): 09:30 ET = 14:30 UTC ; 2026-03-09 (EDT, UTC-4): 09:30 ET = 13:30 UTC43    t, _ = _parse("2026-03-06 09:30:00,1,2,0.5,1.5,10\n2026-03-09 09:30:00,1,2,0.5,1.5,10\n")44    ts = t.column("timestamp_utc").to_pylist()45    assert ts == [datetime(2026, 3, 6, 14, 30, tzinfo=UTC), datetime(2026, 3, 9, 13, 30, tzinfo=UTC)]46    # fall back 2026-11-01: 2026-10-30 09:30 EDT = 13:30 UTC ; 2026-11-02 09:30 EST = 14:30 UTC47    t, _ = _parse("2026-10-30 09:30:00,1,2,0.5,1.5,10\n2026-11-02 09:30:00,1,2,0.5,1.5,10\n")48    ts = t.column("timestamp_utc").to_pylist()49    assert ts == [datetime(2026, 10, 30, 13, 30, tzinfo=UTC), datetime(2026, 11, 2, 14, 30, tzinfo=UTC)]505152def test_nonexistent_local_time_is_dropped_and_counted():53    # 02:30 on 2026-03-08 does not exist in America/New_York54    t, raw = _parse("2026-03-08 02:30:00,1,2,0.5,1.5,10\n2026-03-08 04:00:00,1,2,0.5,1.5,10\n")55    assert raw == 2 and t.num_rows == 1565758def test_crlf_header_and_fx_two_field_layout():59    t, raw = _parse("DateTime,Open,High,Low,Close,Volume\r\n2026-10-30 09:30:00,1,2,0.5,1.5,10\r\n2026-10-30 09:31:00,1,2,0.5,1.5,0\r\n")60    assert raw == 2 and t.num_rows == 261    t, raw = _parse("20261030,09:30:00,1.1,1.2,1.0,1.15,0\n")62    assert t.num_rows == 1 and t.column("timestamp_utc")[0].as_py() == datetime(2026, 10, 30, 13, 30, tzinfo=UTC)636465def test_daily_layout_with_open_interest_column():66    t, raw = _parse("2026-10-30,1,2,0.5,1.5,10,1234\n", interval=Interval.d1)67    assert t.num_rows == 1 and t.column("session_date")[0].as_py() == date(2026, 10, 30)686970def test_duplicates_are_kept_for_flagging_not_dropped():71    t, raw = _parse("2026-10-30 09:30:00,1,2,0.5,1.5,10\n2026-10-30 09:30:00,1,2,0.5,1.5,10\n")72    assert raw == 2 and t.num_rows == 273
Files · d6d35c99819515b0fe452508c04019b74b1f802d
Menu

Theme