a6bf8632ce
Typed result structs replace empty envelopes. Equity place sends ref_id only so live additionalProperties:false schemas accept the call.
175 lines
3.9 KiB
Go
175 lines
3.9 KiB
Go
package crypto
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
decimal "github.com/alpacahq/alpacadecimal"
|
|
"s1d3sw1ped/robinhood-agentic-mcp/internal/wire"
|
|
)
|
|
|
|
func (o *Order) UnmarshalJSON(b []byte) error {
|
|
var row struct {
|
|
ID string `json:"id"`
|
|
Symbol string `json:"symbol"`
|
|
Side string `json:"side"`
|
|
State string `json:"state"`
|
|
Status string `json:"status"`
|
|
Quantity any `json:"quantity"`
|
|
}
|
|
if err := json.Unmarshal(b, &row); err != nil {
|
|
return err
|
|
}
|
|
o.ID, o.Symbol, o.Side = row.ID, row.Symbol, row.Side
|
|
o.State = row.State
|
|
if o.State == "" {
|
|
o.State = row.Status
|
|
}
|
|
var err error
|
|
o.Qty, err = firstDec(row.Quantity)
|
|
return err
|
|
}
|
|
|
|
func parseOrders(raw json.RawMessage) ([]Order, error) {
|
|
rows, _, err := wire.UnmarshalRows[Order](raw, "results", "orders")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(rows) > 0 {
|
|
return rows, nil
|
|
}
|
|
var one Order
|
|
if err := json.Unmarshal(wire.Unwrap(raw), &one); err != nil {
|
|
return nil, err
|
|
}
|
|
if one.ID != "" {
|
|
return []Order{one}, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (r *PairsResult) UnmarshalJSON(b []byte) error {
|
|
type row struct {
|
|
ID string `json:"id"`
|
|
Symbol string `json:"symbol"`
|
|
}
|
|
rows, next, err := wire.UnmarshalRows[row](b, "results", "pairs")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.NextCursor = next
|
|
r.Pairs = make([]CurrencyPair, 0, len(rows))
|
|
for _, row := range rows {
|
|
r.Pairs = append(r.Pairs, CurrencyPair{ID: row.ID, Symbol: row.Symbol})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *QuotesResult) UnmarshalJSON(b []byte) error {
|
|
type row struct {
|
|
Symbol string `json:"symbol"`
|
|
Bid any `json:"bid"`
|
|
Ask any `json:"ask"`
|
|
Mark any `json:"mark"`
|
|
PrevClose any `json:"open_price"`
|
|
Close any `json:"previous_close"`
|
|
}
|
|
rows, _, err := wire.UnmarshalRows[row](b, "results", "quotes")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.Quotes = make([]CryptoQuote, 0, len(rows))
|
|
for _, row := range rows {
|
|
bid, err := firstDec(row.Bid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ask, err := firstDec(row.Ask)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
mark, err := firstDec(row.Mark)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
prev, err := firstDec(row.PrevClose, row.Close)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.Quotes = append(r.Quotes, CryptoQuote{Symbol: row.Symbol, Bid: bid, Ask: ask, Mark: mark, PrevClose: prev})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *PositionsResult) UnmarshalJSON(b []byte) error {
|
|
type row struct {
|
|
Symbol string `json:"symbol"`
|
|
Asset string `json:"asset"`
|
|
Quantity any `json:"quantity"`
|
|
CostBasis any `json:"cost_basis"`
|
|
}
|
|
rows, next, err := wire.UnmarshalRows[row](b, "results", "positions")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.NextCursor = next
|
|
r.Positions = make([]CryptoPosition, 0, len(rows))
|
|
for _, row := range rows {
|
|
sym := row.Symbol
|
|
if sym == "" {
|
|
sym = row.Asset
|
|
}
|
|
qty, err := firstDec(row.Quantity)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cost, err := firstDec(row.CostBasis)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.Positions = append(r.Positions, CryptoPosition{Symbol: sym, Quantity: qty, CostBasis: cost})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *OrdersResult) UnmarshalJSON(b []byte) error {
|
|
rows, next, err := wire.UnmarshalRows[Order](b, "results", "orders")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.Orders = rows
|
|
r.NextCursor = next
|
|
return nil
|
|
}
|
|
|
|
func (r *PreviewResult) UnmarshalJSON(b []byte) error {
|
|
var wrap struct {
|
|
Errors []string `json:"errors"`
|
|
Warnings []string `json:"warnings"`
|
|
Quantity any `json:"quantity"`
|
|
Price any `json:"price"`
|
|
}
|
|
if err := json.Unmarshal(wire.Unwrap(b), &wrap); err != nil {
|
|
return err
|
|
}
|
|
r.Errors, r.Warnings = wrap.Errors, wrap.Warnings
|
|
var err error
|
|
if r.Quantity, err = firstDec(wrap.Quantity); err != nil {
|
|
return err
|
|
}
|
|
r.Price, err = firstDec(wrap.Price)
|
|
return err
|
|
}
|
|
|
|
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
|
|
}
|