feat: add market data MCP methods
This commit is contained in:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user