package market import ( "encoding/json" "time" decimal "github.com/alpacahq/alpacadecimal" "s1d3sw1ped/robinhood-agentic-mcp/internal/wire" ) func firstDec(vs ...any) (decimal.Decimal, error) { for _, v := range vs { if v == nil { continue } if s, ok := v.(string); ok && s == "" { continue } return wire.Dec(v) } return decimal.Zero, nil } func (r *IndexesResult) UnmarshalJSON(b []byte) error { type row struct { ID string `json:"id"` Symbol string `json:"symbol"` Name string `json:"name"` } rows, _, err := wire.UnmarshalRows[row](b, "indexes", "results") if err != nil { return err } r.Indexes = make([]Index, 0, len(rows)) for _, row := range rows { r.Indexes = append(r.Indexes, Index{ID: row.ID, Symbol: row.Symbol, Name: row.Name}) } return nil } func (r *IndexQuotesResult) UnmarshalJSON(b []byte) error { type row struct { InstrumentID string `json:"instrument_id"` ID string `json:"id"` Value any `json:"value"` Last any `json:"last"` State string `json:"state"` } rows, _, err := wire.UnmarshalRows[row](b, "quotes", "results") if err != nil { return err } r.Quotes = make([]IndexQuote, 0, len(rows)) for _, row := range rows { id := row.InstrumentID if id == "" { id = row.ID } val, err := firstDec(row.Value, row.Last) if err != nil { return err } r.Quotes = append(r.Quotes, IndexQuote{InstrumentID: id, Value: val, State: row.State}) } return nil } func (r *IndexHistoricalsResult) UnmarshalJSON(b []byte) error { type pt struct { BeginsAt string `json:"begins_at"` Open any `json:"open"` High any `json:"high"` Low any `json:"low"` Close any `json:"close"` Interpolated bool `json:"interpolated"` } type series struct { InstrumentID string `json:"instrument_id"` ID string `json:"id"` DataPoints []pt `json:"data_points"` } rows, _, err := wire.UnmarshalRows[series](b, "historicals", "results") if err != nil { return err } for _, s := range rows { id := s.InstrumentID if id == "" { id = s.ID } for _, p := range s.DataPoints { ts, err := time.Parse(time.RFC3339, p.BeginsAt) if err != nil { continue } o, err := firstDec(p.Open) if err != nil { return err } h, err := firstDec(p.High) if err != nil { return err } l, err := firstDec(p.Low) if err != nil { return err } cl, err := firstDec(p.Close) if err != nil { return err } r.Bars = append(r.Bars, IndexBar{InstrumentID: id, Time: ts, Open: o, High: h, Low: l, Close: cl, Interpolated: p.Interpolated}) } } return nil } func (r *FinancialsResult) UnmarshalJSON(b []byte) error { type row struct { Symbol string `json:"symbol"` Period string `json:"period"` Revenue any `json:"revenue"` GrossProfit any `json:"gross_profit"` NetIncome any `json:"net_income"` NetMargin any `json:"net_margin"` } rows, _, err := wire.UnmarshalRows[row](b, "financials", "results") if err != nil { return err } r.Periods = make([]FinancialPeriod, 0, len(rows)) for _, row := range rows { rev, err := firstDec(row.Revenue) if err != nil { return err } gp, err := firstDec(row.GrossProfit) if err != nil { return err } ni, err := firstDec(row.NetIncome) if err != nil { return err } nm, err := firstDec(row.NetMargin) if err != nil { return err } r.Periods = append(r.Periods, FinancialPeriod{ Symbol: row.Symbol, Period: row.Period, Revenue: rev, GrossProfit: gp, NetIncome: ni, NetMargin: nm, }) } return nil } func (r *EarningsResultsResult) UnmarshalJSON(b []byte) error { var wrap struct { NextReportDate string `json:"next_report_date"` ReportDate string `json:"report_date"` Results []struct { Report struct { Date string `json:"date"` } `json:"report"` } `json:"results"` } if err := json.Unmarshal(wire.Unwrap(b), &wrap); err != nil { return err } r.NextReportDate = wrap.NextReportDate r.ReportDate = wrap.ReportDate if r.ReportDate == "" && len(wrap.Results) > 0 { r.ReportDate = wrap.Results[0].Report.Date } if r.NextReportDate == "" { r.NextReportDate = r.ReportDate } return nil } func (r *EarningsCalendarResult) UnmarshalJSON(b []byte) error { type row struct { Symbol string `json:"symbol"` ReportDate string `json:"report_date"` Date string `json:"date"` } rows, _, err := wire.UnmarshalRows[row](b, "results", "earnings") if err != nil { return err } r.Events = make([]EarningsEvent, 0, len(rows)) for _, row := range rows { d := row.ReportDate if d == "" { d = row.Date } r.Events = append(r.Events, EarningsEvent{Symbol: row.Symbol, ReportDate: d}) } return nil } func (r *SECFilingIndexResult) UnmarshalJSON(b []byte) error { type row struct { FilingID string `json:"filing_id"` ID string `json:"id"` FormType string `json:"form_type"` FiledAt string `json:"filed_at"` Date string `json:"date"` } rows, next, err := wire.UnmarshalRows[row](b, "filings", "results") if err != nil { return err } r.NextCursor = next r.Filings = make([]SECFilingRef, 0, len(rows)) for _, row := range rows { id := row.FilingID if id == "" { id = row.ID } when := row.FiledAt if when == "" { when = row.Date } r.Filings = append(r.Filings, SECFilingRef{FilingID: id, FormType: row.FormType, FiledAt: when}) } return nil } func (r *SECFilingResult) UnmarshalJSON(b []byte) error { var wrap struct { FilingID string `json:"filing_id"` ID string `json:"id"` Text string `json:"text"` Sections []struct { ID string `json:"id"` Title string `json:"title"` Text string `json:"text"` } `json:"sections"` TOC []struct { ID string `json:"id"` Title string `json:"title"` } `json:"table_of_contents"` } if err := json.Unmarshal(wire.Unwrap(b), &wrap); err != nil { return err } r.FilingID = wrap.FilingID if r.FilingID == "" { r.FilingID = wrap.ID } r.Text = wrap.Text for _, s := range wrap.Sections { r.Sections = append(r.Sections, SECSection{ID: s.ID, Title: s.Title, Text: s.Text}) } if len(r.Sections) == 0 { for _, s := range wrap.TOC { r.Sections = append(r.Sections, SECSection{ID: s.ID, Title: s.Title}) } } return nil } func (r *SECFilingFactsResult) UnmarshalJSON(b []byte) error { type row struct { Concept string `json:"concept"` Value string `json:"value"` Unit string `json:"unit"` } rows, _, err := wire.UnmarshalRows[row](b, "facts", "results") if err != nil { return err } r.Facts = make([]SECFact, 0, len(rows)) for _, row := range rows { r.Facts = append(r.Facts, SECFact{Concept: row.Concept, Value: row.Value, Unit: row.Unit}) } return nil } func (r *SECFilingFactsCatalogResult) UnmarshalJSON(b []byte) error { type row struct { Name string `json:"name"` Concept string `json:"concept"` } rows, _, err := wire.UnmarshalRows[row](b, "concepts", "results") if err != nil { return err } r.Concepts = make([]SECConcept, 0, len(rows)) for _, row := range rows { n := row.Name if n == "" { n = row.Concept } r.Concepts = append(r.Concepts, SECConcept{Name: n}) } return nil }