diff --git a/equity/read.go b/equity/read.go index 9e11f48..ce99029 100644 --- a/equity/read.go +++ b/equity/read.go @@ -594,9 +594,13 @@ func appendQuotes(dst []Quote, rows []quoteRow) ([]Quote, error) { type histPointJSON struct { BeginsAt string `json:"begins_at"` Open any `json:"open"` + OpenPrice any `json:"open_price"` High any `json:"high"` + HighPrice any `json:"high_price"` Low any `json:"low"` + LowPrice any `json:"low_price"` Close any `json:"close"` + ClosePrice any `json:"close_price"` Volume any `json:"volume"` Interpolated bool `json:"interpolated"` } @@ -604,38 +608,43 @@ type histPointJSON struct { type histSeriesJSON struct { Symbol string `json:"symbol"` DataPoints []histPointJSON `json:"data_points"` + Bars []histPointJSON `json:"bars"` } func parseHistoricals(raw json.RawMessage) ([]Bar, error) { - var wrap struct { - Historicals []histSeriesJSON `json:"historicals"` - } - if err := json.Unmarshal(wire.Unwrap(raw), &wrap); err != nil { + // Live Agentic wire (CT113): {"data":{"results":[{"symbol","bars":[{open_price,...}]}]}}. + // Fixture / older shapes: {"historicals":[{"symbol","data_points":[{open,...}]}]}. + rows, _, err := wire.UnmarshalRows[histSeriesJSON](raw, "results", "historicals") + if err != nil { return nil, err } var out []Bar - for _, series := range wrap.Historicals { + for _, series := range rows { if series.Symbol == "" { continue } - for _, p := range series.DataPoints { + points := series.Bars + if len(points) == 0 { + points = series.DataPoints + } + for _, p := range points { ts, err := time.Parse(time.RFC3339, p.BeginsAt) if err != nil { continue } - o, err := firstDec(p.Open) + o, err := firstDec(p.OpenPrice, p.Open) if err != nil { return nil, err } - h, err := firstDec(p.High) + h, err := firstDec(p.HighPrice, p.High) if err != nil { return nil, err } - l, err := firstDec(p.Low) + l, err := firstDec(p.LowPrice, p.Low) if err != nil { return nil, err } - cl, err := firstDec(p.Close) + cl, err := firstDec(p.ClosePrice, p.Close) if err != nil { return nil, err } diff --git a/equity/read_test.go b/equity/read_test.go index 4dbf8a4..087b270 100644 --- a/equity/read_test.go +++ b/equity/read_test.go @@ -306,10 +306,11 @@ func TestQuotes_rhntestResultsEnvelope(t *testing.T) { func TestHistoricals_rhntest(t *testing.T) { t.Parallel() s := rhntest.New(t) - s.Set("get_equity_historicals", json.RawMessage(`{"historicals":[{"symbol":"MU","data_points":[ - {"begins_at":"2026-08-18T13:30:00Z","open":"10","high":"11","low":"9","close":"10","volume":"100","interpolated":false}, - {"begins_at":"2026-08-18T13:31:00Z","open":"10","high":"10","low":"10","close":"10","volume":"1","interpolated":true} - ]}]}`)) + // Live Agentic shape (Jerry CT113): data.results[].bars[] with *_price fields. + s.Set("get_equity_historicals", json.RawMessage(`{"data":{"results":[{"symbol":"MU","bars":[ + {"begins_at":"2026-08-18T13:30:00Z","open_price":"10","high_price":"11","low_price":"9","close_price":"10","volume":"100","session":"reg","interpolated":false}, + {"begins_at":"2026-08-18T13:31:00Z","open_price":"10","high_price":"10","low_price":"10","close_price":"10","volume":"1","session":"reg","interpolated":true} + ]}]}}`)) c := equity.New(&client.Client{URL: s.URL}) got, err := c.Historicals(context.Background(), equity.HistoricalsRequest{ Symbols: []string{"MU"}, @@ -335,3 +336,27 @@ func TestHistoricals_rhntest(t *testing.T) { t.Fatalf("%+v", got.Bars[1]) } } + +func TestHistoricals_legacyShortNames(t *testing.T) { + t.Parallel() + s := rhntest.New(t) + s.Set("get_equity_historicals", json.RawMessage(`{"historicals":[{"symbol":"MU","data_points":[ + {"begins_at":"2026-08-18T13:30:00Z","open":"10","high":"11","low":"9","close":"10","volume":"100","interpolated":false}, + {"begins_at":"2026-08-18T13:31:00Z","open":"10","high":"10","low":"10","close":"10","volume":"1","interpolated":true} + ]}]}`)) + c := equity.New(&client.Client{URL: s.URL}) + got, err := c.Historicals(context.Background(), equity.HistoricalsRequest{ + Symbols: []string{"MU"}, + StartTime: time.Date(2026, 8, 18, 13, 30, 0, 0, time.UTC), + }) + if err != nil { + t.Fatal(err) + } + if len(got.Bars) != 2 { + t.Fatalf("bars %d", len(got.Bars)) + } + b0 := got.Bars[0] + if !b0.Open.Equal(decimal.RequireFromString("10")) || !b0.High.Equal(decimal.RequireFromString("11")) || !b0.Volume.Equal(decimal.RequireFromString("100")) { + t.Fatalf("%+v", b0) + } +}