"""The dated price lookup: carry forward, never backward, and know when it is stale.""" from datetime import date, timedelta from decimal import Decimal from fintracker.pricing.prices import STALE_AFTER_DAYS, PriceTable, Quote D = Decimal GAZP, TBRU = 10, 11 def table() -> PriceTable: return PriceTable( { GAZP: [ Quote(D(100), "RUB", date(2025, 3, 3)), Quote(D(110), "RUB", date(2025, 3, 6), accrued_interest=D(5)), ] }, {GAZP: Quote(D(120), "RUB", date(2025, 3, 10))}, ) def at(d: date, instrument_id: int = GAZP) -> Quote: quote = table().at(instrument_id, d) assert quote is not None return quote def latest(d: date, instrument_id: int = GAZP) -> Quote: quote = table().latest(instrument_id, d) assert quote is not None return quote def test_a_quiet_day_reuses_the_last_close(): assert at(date(2025, 3, 5)).price == D(100) def test_there_is_no_price_before_the_first_quote(): assert table().at(GAZP, date(2025, 3, 2)) is None assert table().at(TBRU, date(2025, 3, 5)) is None def test_staleness_is_measured_from_the_day_the_price_was_quoted(): quoted = date(2025, 3, 6) quote = at(quoted + timedelta(days=30)) assert quote.as_of == quoted assert not quote.is_stale_on(quoted + timedelta(days=STALE_AFTER_DAYS)) assert quote.is_stale_on(quoted + timedelta(days=STALE_AFTER_DAYS + 1)) def test_total_adds_accrued_interest(): assert at(date(2025, 3, 6)).total == D(115) assert at(date(2025, 3, 3)).total == D(100) def test_the_intraday_price_wins_once_the_daily_bar_is_behind(): assert latest(date(2025, 3, 11)).price == D(120) # …but never a price from the future of the day being valued assert latest(date(2025, 3, 7)).price == D(110) def test_price_last_alone_is_enough_to_value_an_instrument(): only_last = PriceTable({}, {TBRU: Quote(D(7), "RUB", date(2025, 3, 10))}) assert only_last.at(TBRU, date(2025, 3, 10)) is None quote = only_last.latest(TBRU, date(2025, 3, 10)) assert quote is not None and quote.price == D(7)