equity: Accept open_price aliases in historicals
CI / Test and build (pull_request) Successful in 16s
CI / Test and build (pull_request) Successful in 16s
Live Agentic get_equity_historicals returns data.results[].bars[] with open_price/high_price/low_price/close_price. parseHistoricals only read historicals[].data_points[] with short OHLC names, so CT113 saw bars_len=30 on the wire but 0 parsed bars and VWAP stayed 0 all day (insufficient_data mass skip). Accept results|historicals envelopes, bars|data_points arrays, and *_price aliases alongside short names. Fixture uses the live shape; legacy short-name envelope stays covered. Link: #18
This commit is contained in:
+19
-10
@@ -594,9 +594,13 @@ func appendQuotes(dst []Quote, rows []quoteRow) ([]Quote, error) {
|
|||||||
type histPointJSON struct {
|
type histPointJSON struct {
|
||||||
BeginsAt string `json:"begins_at"`
|
BeginsAt string `json:"begins_at"`
|
||||||
Open any `json:"open"`
|
Open any `json:"open"`
|
||||||
|
OpenPrice any `json:"open_price"`
|
||||||
High any `json:"high"`
|
High any `json:"high"`
|
||||||
|
HighPrice any `json:"high_price"`
|
||||||
Low any `json:"low"`
|
Low any `json:"low"`
|
||||||
|
LowPrice any `json:"low_price"`
|
||||||
Close any `json:"close"`
|
Close any `json:"close"`
|
||||||
|
ClosePrice any `json:"close_price"`
|
||||||
Volume any `json:"volume"`
|
Volume any `json:"volume"`
|
||||||
Interpolated bool `json:"interpolated"`
|
Interpolated bool `json:"interpolated"`
|
||||||
}
|
}
|
||||||
@@ -604,38 +608,43 @@ type histPointJSON struct {
|
|||||||
type histSeriesJSON struct {
|
type histSeriesJSON struct {
|
||||||
Symbol string `json:"symbol"`
|
Symbol string `json:"symbol"`
|
||||||
DataPoints []histPointJSON `json:"data_points"`
|
DataPoints []histPointJSON `json:"data_points"`
|
||||||
|
Bars []histPointJSON `json:"bars"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseHistoricals(raw json.RawMessage) ([]Bar, error) {
|
func parseHistoricals(raw json.RawMessage) ([]Bar, error) {
|
||||||
var wrap struct {
|
// Live Agentic wire (CT113): {"data":{"results":[{"symbol","bars":[{open_price,...}]}]}}.
|
||||||
Historicals []histSeriesJSON `json:"historicals"`
|
// Fixture / older shapes: {"historicals":[{"symbol","data_points":[{open,...}]}]}.
|
||||||
}
|
rows, _, err := wire.UnmarshalRows[histSeriesJSON](raw, "results", "historicals")
|
||||||
if err := json.Unmarshal(wire.Unwrap(raw), &wrap); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var out []Bar
|
var out []Bar
|
||||||
for _, series := range wrap.Historicals {
|
for _, series := range rows {
|
||||||
if series.Symbol == "" {
|
if series.Symbol == "" {
|
||||||
continue
|
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)
|
ts, err := time.Parse(time.RFC3339, p.BeginsAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
o, err := firstDec(p.Open)
|
o, err := firstDec(p.OpenPrice, p.Open)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
h, err := firstDec(p.High)
|
h, err := firstDec(p.HighPrice, p.High)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
l, err := firstDec(p.Low)
|
l, err := firstDec(p.LowPrice, p.Low)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cl, err := firstDec(p.Close)
|
cl, err := firstDec(p.ClosePrice, p.Close)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-4
@@ -306,10 +306,11 @@ func TestQuotes_rhntestResultsEnvelope(t *testing.T) {
|
|||||||
func TestHistoricals_rhntest(t *testing.T) {
|
func TestHistoricals_rhntest(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
s := rhntest.New(t)
|
s := rhntest.New(t)
|
||||||
s.Set("get_equity_historicals", json.RawMessage(`{"historicals":[{"symbol":"MU","data_points":[
|
// Live Agentic shape (Jerry CT113): data.results[].bars[] with *_price fields.
|
||||||
{"begins_at":"2026-08-18T13:30:00Z","open":"10","high":"11","low":"9","close":"10","volume":"100","interpolated":false},
|
s.Set("get_equity_historicals", json.RawMessage(`{"data":{"results":[{"symbol":"MU","bars":[
|
||||||
{"begins_at":"2026-08-18T13:31:00Z","open":"10","high":"10","low":"10","close":"10","volume":"1","interpolated":true}
|
{"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})
|
c := equity.New(&client.Client{URL: s.URL})
|
||||||
got, err := c.Historicals(context.Background(), equity.HistoricalsRequest{
|
got, err := c.Historicals(context.Background(), equity.HistoricalsRequest{
|
||||||
Symbols: []string{"MU"},
|
Symbols: []string{"MU"},
|
||||||
@@ -335,3 +336,27 @@ func TestHistoricals_rhntest(t *testing.T) {
|
|||||||
t.Fatalf("%+v", got.Bars[1])
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user