From 21170bcf15a22e659e8c905c992a66835454955c Mon Sep 17 00:00:00 2001 From: Justin Harms Date: Tue, 1 Sep 2026 12:39:00 -0500 Subject: [PATCH] fix: parse quotes results envelope and JSON-number num_std --- equity/read.go | 28 +++++++++++++++++++++------- equity/read_test.go | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/equity/read.go b/equity/read.go index f9145c2..0a21041 100644 --- a/equity/read.go +++ b/equity/read.go @@ -347,7 +347,7 @@ func (c *Client) TechnicalIndicators(ctx context.Context, req TechnicalIndicator args["period"] = *req.Period } if req.NumStd != nil { - args["num_std"] = wire.Encode(*req.NumStd) + args["num_std"] = json.Number(wire.Encode(*req.NumStd)) } if req.FastPeriod != nil { args["fast_period"] = *req.FastPeriod @@ -359,7 +359,7 @@ func (c *Client) TechnicalIndicators(ctx context.Context, req TechnicalIndicator args["signal_period"] = *req.SignalPeriod } if req.Multiplier != nil { - args["multiplier"] = wire.Encode(*req.Multiplier) + args["multiplier"] = json.Number(wire.Encode(*req.Multiplier)) } if req.Method != "" { args["method"] = req.Method @@ -471,13 +471,27 @@ func (r quoteRow) asQuote() (Quote, bool, error) { func parseQuotes(raw json.RawMessage) ([]Quote, error) { var wrap struct { - Quotes []quoteRow `json:"quotes"` + Quotes []quoteRow `json:"quotes"` + Results []quoteRow `json:"results"` } if err := json.Unmarshal(wire.Unwrap(raw), &wrap); err != nil { return nil, err } - out := make([]Quote, 0, len(wrap.Quotes)) - for _, row := range wrap.Quotes { + out := make([]Quote, 0, len(wrap.Quotes)+len(wrap.Results)) + var err error + out, err = appendQuotes(out, wrap.Quotes) + if err != nil { + return nil, err + } + out, err = appendQuotes(out, wrap.Results) + if err != nil { + return nil, err + } + return out, nil +} + +func appendQuotes(dst []Quote, rows []quoteRow) ([]Quote, error) { + for _, row := range rows { q, ok, err := row.asQuote() if err != nil { return nil, err @@ -485,9 +499,9 @@ func parseQuotes(raw json.RawMessage) ([]Quote, error) { if !ok { continue } - out = append(out, q) + dst = append(dst, q) } - return out, nil + return dst, nil } type histPointJSON struct { diff --git a/equity/read_test.go b/equity/read_test.go index 7245987..8416c1a 100644 --- a/equity/read_test.go +++ b/equity/read_test.go @@ -170,11 +170,11 @@ func TestEquity_toolNames(t *testing.T) { "adjustment_type": "split", "output": "latest", "period": 14, - "num_std": "2", + "num_std": json.Number("2"), "fast_period": 12, "slow_period": 26, "signal_period": 9, - "multiplier": "3", + "multiplier": json.Number("3"), "method": "classic", }, }, @@ -270,6 +270,36 @@ func TestQuotes_rhntest(t *testing.T) { } } +func TestQuotes_rhntestResultsEnvelope(t *testing.T) { + t.Parallel() + s := rhntest.New(t) + s.Set("get_equity_quotes", json.RawMessage(`{"data":{"results":[{"quote":{"symbol":"SPY","last_trade_price":"763.470000","bid_price":"763.760000","ask_price":"764.100000"},"close":{"symbol":"SPY","price":"765.72"}}]}}`)) + c := equity.New(&client.Client{URL: s.URL}) + got, err := c.Quotes(context.Background(), equity.QuotesRequest{Symbols: []string{"SPY"}}) + if err != nil { + t.Fatal(err) + } + if len(got.Quotes) != 1 { + t.Fatalf("%+v", got) + } + q := got.Quotes[0] + if q.Symbol != "SPY" { + t.Fatalf("%+v", q) + } + if !q.Last.Equal(decimal.RequireFromString("763.470000")) { + t.Fatalf("last %s", q.Last) + } + if !q.Bid.Equal(decimal.RequireFromString("763.760000")) { + t.Fatalf("bid %s", q.Bid) + } + if !q.Ask.Equal(decimal.RequireFromString("764.100000")) { + t.Fatalf("ask %s", q.Ask) + } + if !q.PrevClose.Equal(decimal.RequireFromString("765.72")) { + t.Fatalf("prev close %s", q.PrevClose) + } +} + func TestHistoricals_rhntest(t *testing.T) { t.Parallel() s := rhntest.New(t)