feat: add market data MCP methods
This commit is contained in:
@@ -0,0 +1,29 @@
|
|||||||
|
package market
|
||||||
|
|
||||||
|
import "s1d3sw1ped/robinhood-agentic-mcp/client"
|
||||||
|
|
||||||
|
// Client wraps Robinhood market-data MCP tools.
|
||||||
|
type Client struct {
|
||||||
|
c client.Caller
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a market client that invokes tools through c.
|
||||||
|
func New(c client.Caller) *Client {
|
||||||
|
return &Client{c: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tools returns the MCP names this package implements.
|
||||||
|
func Tools() []string {
|
||||||
|
return []string{
|
||||||
|
toolIndexes,
|
||||||
|
toolIndexQuotes,
|
||||||
|
toolIndexHistoricals,
|
||||||
|
toolFinancials,
|
||||||
|
toolEarningsResults,
|
||||||
|
toolEarningsCalendar,
|
||||||
|
toolSECFilingIndex,
|
||||||
|
toolSECFiling,
|
||||||
|
toolSECFilingFacts,
|
||||||
|
toolSECFilingFactsCatalog,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,308 @@
|
|||||||
|
package market
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/client"
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/internal/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
toolIndexes = "get_indexes"
|
||||||
|
toolIndexQuotes = "get_index_quotes"
|
||||||
|
toolIndexHistoricals = "get_index_historicals"
|
||||||
|
toolFinancials = "get_financials"
|
||||||
|
toolEarningsResults = "get_earnings_results"
|
||||||
|
toolEarningsCalendar = "get_earnings_calendar"
|
||||||
|
toolSECFilingIndex = "get_sec_filing_index"
|
||||||
|
toolSECFiling = "get_sec_filing"
|
||||||
|
toolSECFilingFacts = "get_sec_filing_facts"
|
||||||
|
toolSECFilingFactsCatalog = "get_sec_filing_facts_catalog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IndexesRequest is the argument set for get_indexes.
|
||||||
|
type IndexesRequest struct {
|
||||||
|
Symbols string // comma-separated; live schema is a string, not an array
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexesResult is the parsed get_indexes payload.
|
||||||
|
type IndexesResult struct{}
|
||||||
|
|
||||||
|
// IndexQuotesRequest is the argument set for get_index_quotes.
|
||||||
|
type IndexQuotesRequest struct {
|
||||||
|
InstrumentIDs []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexQuotesResult is the parsed get_index_quotes payload.
|
||||||
|
type IndexQuotesResult struct{}
|
||||||
|
|
||||||
|
// IndexHistoricalsRequest is the argument set for get_index_historicals.
|
||||||
|
type IndexHistoricalsRequest struct {
|
||||||
|
InstrumentIDs []string
|
||||||
|
StartTime time.Time
|
||||||
|
EndTime time.Time
|
||||||
|
Interval string // required — no hidden default
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexHistoricalsResult is the parsed get_index_historicals payload.
|
||||||
|
type IndexHistoricalsResult struct{}
|
||||||
|
|
||||||
|
// FinancialsRequest is the argument set for get_financials.
|
||||||
|
type FinancialsRequest struct {
|
||||||
|
Symbols []string
|
||||||
|
Period string
|
||||||
|
Limit int
|
||||||
|
}
|
||||||
|
|
||||||
|
// FinancialsResult is the parsed get_financials payload.
|
||||||
|
type FinancialsResult struct{}
|
||||||
|
|
||||||
|
// EarningsResultsRequest is the argument set for get_earnings_results.
|
||||||
|
type EarningsResultsRequest struct {
|
||||||
|
Symbol string
|
||||||
|
}
|
||||||
|
|
||||||
|
// EarningsResultsResult is the parsed get_earnings_results payload.
|
||||||
|
type EarningsResultsResult struct {
|
||||||
|
NextReportDate string `json:"next_report_date"`
|
||||||
|
ReportDate string `json:"report_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EarningsCalendarRequest is the argument set for get_earnings_calendar.
|
||||||
|
type EarningsCalendarRequest struct {
|
||||||
|
StartDate string
|
||||||
|
Days int
|
||||||
|
Filter string
|
||||||
|
}
|
||||||
|
|
||||||
|
// EarningsCalendarResult is the parsed get_earnings_calendar payload.
|
||||||
|
type EarningsCalendarResult struct{}
|
||||||
|
|
||||||
|
// SECFilingIndexRequest is the argument set for get_sec_filing_index.
|
||||||
|
type SECFilingIndexRequest struct {
|
||||||
|
Symbol string
|
||||||
|
FormType []string
|
||||||
|
Since string
|
||||||
|
Until string
|
||||||
|
Cursor string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SECFilingIndexResult is the parsed get_sec_filing_index payload.
|
||||||
|
type SECFilingIndexResult struct{}
|
||||||
|
|
||||||
|
// SECFilingRequest is the argument set for get_sec_filing.
|
||||||
|
type SECFilingRequest struct {
|
||||||
|
FilingID string
|
||||||
|
Section string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SECFilingResult is the parsed get_sec_filing payload.
|
||||||
|
type SECFilingResult struct{}
|
||||||
|
|
||||||
|
// SECFilingFactsRequest is the argument set for get_sec_filing_facts.
|
||||||
|
type SECFilingFactsRequest struct {
|
||||||
|
FilingIDs []string
|
||||||
|
Concepts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SECFilingFactsResult is the parsed get_sec_filing_facts payload.
|
||||||
|
type SECFilingFactsResult struct{}
|
||||||
|
|
||||||
|
// SECFilingFactsCatalogRequest is the argument set for get_sec_filing_facts_catalog.
|
||||||
|
type SECFilingFactsCatalogRequest struct {
|
||||||
|
FilingID string
|
||||||
|
ConceptContains string
|
||||||
|
AxisNameIn []string
|
||||||
|
Offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
// SECFilingFactsCatalogResult is the parsed get_sec_filing_facts_catalog payload.
|
||||||
|
type SECFilingFactsCatalogResult struct{}
|
||||||
|
|
||||||
|
// Indexes calls get_indexes.
|
||||||
|
func (c *Client) Indexes(ctx context.Context, req IndexesRequest) (IndexesResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if req.Symbols != "" {
|
||||||
|
args["symbols"] = req.Symbols
|
||||||
|
}
|
||||||
|
var out IndexesResult
|
||||||
|
if err := c.parse(ctx, toolIndexes, args, &out); err != nil {
|
||||||
|
return IndexesResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexQuotes calls get_index_quotes.
|
||||||
|
func (c *Client) IndexQuotes(ctx context.Context, req IndexQuotesRequest) (IndexQuotesResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if len(req.InstrumentIDs) > 0 {
|
||||||
|
args["instrument_ids"] = req.InstrumentIDs
|
||||||
|
}
|
||||||
|
var out IndexQuotesResult
|
||||||
|
if err := c.parse(ctx, toolIndexQuotes, args, &out); err != nil {
|
||||||
|
return IndexQuotesResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexHistoricals calls get_index_historicals. start_time and interval are always sent (required; no hidden default).
|
||||||
|
func (c *Client) IndexHistoricals(ctx context.Context, req IndexHistoricalsRequest) (IndexHistoricalsResult, error) {
|
||||||
|
args := map[string]any{
|
||||||
|
"start_time": req.StartTime.UTC().Format(time.RFC3339),
|
||||||
|
"interval": req.Interval,
|
||||||
|
}
|
||||||
|
if len(req.InstrumentIDs) > 0 {
|
||||||
|
args["instrument_ids"] = req.InstrumentIDs
|
||||||
|
}
|
||||||
|
if !req.EndTime.IsZero() {
|
||||||
|
args["end_time"] = req.EndTime.UTC().Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
var out IndexHistoricalsResult
|
||||||
|
if err := c.parse(ctx, toolIndexHistoricals, args, &out); err != nil {
|
||||||
|
return IndexHistoricalsResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Financials calls get_financials.
|
||||||
|
func (c *Client) Financials(ctx context.Context, req FinancialsRequest) (FinancialsResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if len(req.Symbols) > 0 {
|
||||||
|
args["symbols"] = req.Symbols
|
||||||
|
}
|
||||||
|
if req.Period != "" {
|
||||||
|
args["period"] = req.Period
|
||||||
|
}
|
||||||
|
if req.Limit != 0 {
|
||||||
|
args["limit"] = req.Limit
|
||||||
|
}
|
||||||
|
var out FinancialsResult
|
||||||
|
if err := c.parse(ctx, toolFinancials, args, &out); err != nil {
|
||||||
|
return FinancialsResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EarningsResults calls get_earnings_results.
|
||||||
|
func (c *Client) EarningsResults(ctx context.Context, req EarningsResultsRequest) (EarningsResultsResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if req.Symbol != "" {
|
||||||
|
args["symbol"] = req.Symbol
|
||||||
|
}
|
||||||
|
var out EarningsResultsResult
|
||||||
|
if err := c.parse(ctx, toolEarningsResults, args, &out); err != nil {
|
||||||
|
return EarningsResultsResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EarningsCalendar calls get_earnings_calendar.
|
||||||
|
func (c *Client) EarningsCalendar(ctx context.Context, req EarningsCalendarRequest) (EarningsCalendarResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if req.StartDate != "" {
|
||||||
|
args["start_date"] = req.StartDate
|
||||||
|
}
|
||||||
|
if req.Days != 0 {
|
||||||
|
args["days"] = req.Days
|
||||||
|
}
|
||||||
|
if req.Filter != "" {
|
||||||
|
args["filter"] = req.Filter
|
||||||
|
}
|
||||||
|
var out EarningsCalendarResult
|
||||||
|
if err := c.parse(ctx, toolEarningsCalendar, args, &out); err != nil {
|
||||||
|
return EarningsCalendarResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SECFilingIndex calls get_sec_filing_index.
|
||||||
|
func (c *Client) SECFilingIndex(ctx context.Context, req SECFilingIndexRequest) (SECFilingIndexResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if req.Symbol != "" {
|
||||||
|
args["symbol"] = req.Symbol
|
||||||
|
}
|
||||||
|
if len(req.FormType) > 0 {
|
||||||
|
args["form_type"] = req.FormType
|
||||||
|
}
|
||||||
|
if req.Since != "" {
|
||||||
|
args["since"] = req.Since
|
||||||
|
}
|
||||||
|
if req.Until != "" {
|
||||||
|
args["until"] = req.Until
|
||||||
|
}
|
||||||
|
if req.Cursor != "" {
|
||||||
|
args["cursor"] = req.Cursor
|
||||||
|
}
|
||||||
|
var out SECFilingIndexResult
|
||||||
|
if err := c.parse(ctx, toolSECFilingIndex, args, &out); err != nil {
|
||||||
|
return SECFilingIndexResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SECFiling calls get_sec_filing.
|
||||||
|
func (c *Client) SECFiling(ctx context.Context, req SECFilingRequest) (SECFilingResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if req.FilingID != "" {
|
||||||
|
args["filing_id"] = req.FilingID
|
||||||
|
}
|
||||||
|
if req.Section != "" {
|
||||||
|
args["section"] = req.Section
|
||||||
|
}
|
||||||
|
var out SECFilingResult
|
||||||
|
if err := c.parse(ctx, toolSECFiling, args, &out); err != nil {
|
||||||
|
return SECFilingResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SECFilingFacts calls get_sec_filing_facts.
|
||||||
|
func (c *Client) SECFilingFacts(ctx context.Context, req SECFilingFactsRequest) (SECFilingFactsResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if len(req.FilingIDs) > 0 {
|
||||||
|
args["filing_ids"] = req.FilingIDs
|
||||||
|
}
|
||||||
|
if len(req.Concepts) > 0 {
|
||||||
|
args["concepts"] = req.Concepts
|
||||||
|
}
|
||||||
|
var out SECFilingFactsResult
|
||||||
|
if err := c.parse(ctx, toolSECFilingFacts, args, &out); err != nil {
|
||||||
|
return SECFilingFactsResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SECFilingFactsCatalog calls get_sec_filing_facts_catalog.
|
||||||
|
func (c *Client) SECFilingFactsCatalog(ctx context.Context, req SECFilingFactsCatalogRequest) (SECFilingFactsCatalogResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if req.FilingID != "" {
|
||||||
|
args["filing_id"] = req.FilingID
|
||||||
|
}
|
||||||
|
if req.ConceptContains != "" {
|
||||||
|
args["concept_contains"] = req.ConceptContains
|
||||||
|
}
|
||||||
|
if len(req.AxisNameIn) > 0 {
|
||||||
|
args["axis_name_in"] = req.AxisNameIn
|
||||||
|
}
|
||||||
|
if req.Offset != 0 {
|
||||||
|
args["offset"] = req.Offset
|
||||||
|
}
|
||||||
|
var out SECFilingFactsCatalogResult
|
||||||
|
if err := c.parse(ctx, toolSECFilingFactsCatalog, args, &out); err != nil {
|
||||||
|
return SECFilingFactsCatalogResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) parse(ctx context.Context, tool string, args map[string]any, dest any) error {
|
||||||
|
raw, err := c.c.Call(ctx, tool, args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(wire.Unwrap(raw), dest); err != nil {
|
||||||
|
return client.ToolErrorf(tool, "parse: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
package market_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/client"
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/internal/rhntest"
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/market"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMarket_toolNames(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
start := time.Date(2026, 8, 18, 13, 30, 0, 0, time.UTC)
|
||||||
|
end := time.Date(2026, 8, 18, 20, 0, 0, 0, time.UTC)
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
call func(*market.Client) error
|
||||||
|
wantName string
|
||||||
|
wantArgs map[string]any
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Indexes",
|
||||||
|
call: func(c *market.Client) error {
|
||||||
|
_, err := c.Indexes(context.Background(), market.IndexesRequest{Symbols: "SPX,NDX"})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_indexes",
|
||||||
|
wantArgs: map[string]any{"symbols": "SPX,NDX"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IndexQuotes",
|
||||||
|
call: func(c *market.Client) error {
|
||||||
|
_, err := c.IndexQuotes(context.Background(), market.IndexQuotesRequest{
|
||||||
|
InstrumentIDs: []string{"idx-spx", "idx-ndx"},
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_index_quotes",
|
||||||
|
wantArgs: map[string]any{"instrument_ids": []string{"idx-spx", "idx-ndx"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IndexHistoricals",
|
||||||
|
call: func(c *market.Client) error {
|
||||||
|
_, err := c.IndexHistoricals(context.Background(), market.IndexHistoricalsRequest{
|
||||||
|
InstrumentIDs: []string{"idx-spx"},
|
||||||
|
StartTime: start,
|
||||||
|
EndTime: end,
|
||||||
|
Interval: "day",
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_index_historicals",
|
||||||
|
wantArgs: map[string]any{
|
||||||
|
"instrument_ids": []string{"idx-spx"},
|
||||||
|
"start_time": "2026-08-18T13:30:00Z",
|
||||||
|
"end_time": "2026-08-18T20:00:00Z",
|
||||||
|
"interval": "day",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Financials",
|
||||||
|
call: func(c *market.Client) error {
|
||||||
|
_, err := c.Financials(context.Background(), market.FinancialsRequest{
|
||||||
|
Symbols: []string{"MU", "AAPL"},
|
||||||
|
Period: "quarterly",
|
||||||
|
Limit: 8,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_financials",
|
||||||
|
wantArgs: map[string]any{
|
||||||
|
"symbols": []string{"MU", "AAPL"},
|
||||||
|
"period": "quarterly",
|
||||||
|
"limit": 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "EarningsResults",
|
||||||
|
call: func(c *market.Client) error {
|
||||||
|
_, err := c.EarningsResults(context.Background(), market.EarningsResultsRequest{Symbol: "MU"})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_earnings_results",
|
||||||
|
wantArgs: map[string]any{"symbol": "MU"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "EarningsCalendar",
|
||||||
|
call: func(c *market.Client) error {
|
||||||
|
_, err := c.EarningsCalendar(context.Background(), market.EarningsCalendarRequest{
|
||||||
|
StartDate: "2026-08-18",
|
||||||
|
Days: 7,
|
||||||
|
Filter: "high_market_cap",
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_earnings_calendar",
|
||||||
|
wantArgs: map[string]any{
|
||||||
|
"start_date": "2026-08-18",
|
||||||
|
"days": 7,
|
||||||
|
"filter": "high_market_cap",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SECFilingIndex",
|
||||||
|
call: func(c *market.Client) error {
|
||||||
|
_, err := c.SECFilingIndex(context.Background(), market.SECFilingIndexRequest{
|
||||||
|
Symbol: "MU",
|
||||||
|
FormType: []string{"10-K", "10-Q"},
|
||||||
|
Since: "2026-01-01",
|
||||||
|
Until: "2026-08-18",
|
||||||
|
Cursor: "c1",
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_sec_filing_index",
|
||||||
|
wantArgs: map[string]any{
|
||||||
|
"symbol": "MU",
|
||||||
|
"form_type": []string{"10-K", "10-Q"},
|
||||||
|
"since": "2026-01-01",
|
||||||
|
"until": "2026-08-18",
|
||||||
|
"cursor": "c1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SECFiling",
|
||||||
|
call: func(c *market.Client) error {
|
||||||
|
_, err := c.SECFiling(context.Background(), market.SECFilingRequest{
|
||||||
|
FilingID: "f-1",
|
||||||
|
Section: "item1",
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_sec_filing",
|
||||||
|
wantArgs: map[string]any{"filing_id": "f-1", "section": "item1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SECFilingFacts",
|
||||||
|
call: func(c *market.Client) error {
|
||||||
|
_, err := c.SECFilingFacts(context.Background(), market.SECFilingFactsRequest{
|
||||||
|
FilingIDs: []string{"f-1", "f-2"},
|
||||||
|
Concepts: []string{"NetIncomeLoss", "Revenues"},
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_sec_filing_facts",
|
||||||
|
wantArgs: map[string]any{
|
||||||
|
"filing_ids": []string{"f-1", "f-2"},
|
||||||
|
"concepts": []string{"NetIncomeLoss", "Revenues"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SECFilingFactsCatalog",
|
||||||
|
call: func(c *market.Client) error {
|
||||||
|
_, err := c.SECFilingFactsCatalog(context.Background(), market.SECFilingFactsCatalogRequest{
|
||||||
|
FilingID: "f-1",
|
||||||
|
ConceptContains: "Debt",
|
||||||
|
AxisNameIn: []string{"LegalEntityAxis"},
|
||||||
|
Offset: 10,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_sec_filing_facts_catalog",
|
||||||
|
wantArgs: map[string]any{
|
||||||
|
"filing_id": "f-1",
|
||||||
|
"concept_contains": "Debt",
|
||||||
|
"axis_name_in": []string{"LegalEntityAxis"},
|
||||||
|
"offset": 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
var gotName string
|
||||||
|
var gotArgs map[string]any
|
||||||
|
c := market.New(client.Func(func(ctx context.Context, name string, args map[string]any) (json.RawMessage, error) {
|
||||||
|
gotName, gotArgs = name, args
|
||||||
|
return json.RawMessage(`{}`), nil
|
||||||
|
}))
|
||||||
|
if err := tc.call(c); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if gotName != tc.wantName {
|
||||||
|
t.Fatalf("%s %+v", gotName, gotArgs)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(tc.wantArgs, gotArgs); diff != "" {
|
||||||
|
t.Fatal(diff)
|
||||||
|
}
|
||||||
|
if tc.name == "IndexHistoricals" {
|
||||||
|
if gotArgs["interval"] != "day" {
|
||||||
|
t.Fatalf("interval %v", gotArgs["interval"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTools(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
want := []string{
|
||||||
|
"get_earnings_calendar",
|
||||||
|
"get_earnings_results",
|
||||||
|
"get_financials",
|
||||||
|
"get_index_historicals",
|
||||||
|
"get_index_quotes",
|
||||||
|
"get_indexes",
|
||||||
|
"get_sec_filing",
|
||||||
|
"get_sec_filing_facts",
|
||||||
|
"get_sec_filing_facts_catalog",
|
||||||
|
"get_sec_filing_index",
|
||||||
|
}
|
||||||
|
got := append([]string(nil), market.Tools()...)
|
||||||
|
sort.Strings(got)
|
||||||
|
if diff := cmp.Diff(want, got); diff != "" {
|
||||||
|
t.Fatal(diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEarningsResults_rhntest(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
s := rhntest.New(t)
|
||||||
|
s.Set("get_earnings_results", json.RawMessage(`{"next_report_date":"2026-10-15","report_date":"2026-07-15"}`))
|
||||||
|
c := market.New(&client.Client{URL: s.URL})
|
||||||
|
got, err := c.EarningsResults(context.Background(), market.EarningsResultsRequest{Symbol: "MU"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got.NextReportDate != "2026-10-15" || got.ReportDate != "2026-07-15" {
|
||||||
|
t.Fatalf("%+v", got)
|
||||||
|
}
|
||||||
|
if s.LastName() != "get_earnings_results" {
|
||||||
|
t.Fatalf("%s", s.LastName())
|
||||||
|
}
|
||||||
|
want := map[string]any{"symbol": "MU"}
|
||||||
|
if diff := cmp.Diff(want, s.LastArgs()); diff != "" {
|
||||||
|
t.Fatal(diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user