schwab-go

module
v0.4.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 9, 2026 License: Apache-2.0

README

schwab-go

CI codecov Go Report Card Go Reference OpenSSF Scorecard License

Go client library for the Schwab API. Covers Market Data and Trader API endpoints with typed responses, functional options, and structured error handling.

Features

  • Market Data - quotes, price history, option chains, instruments, market hours, movers
  • Trader - accounts, orders (create/replace/cancel/preview), transactions, user preferences
  • Auth (schwab/auth) - OAuth2 authorization code flow, token refresh, and file-based token persistence included in the core module
  • Typed quote accessors - asset-specific quote and reference types for equities, options, indices, mutual funds, forex, futures, and future options
  • Structured errors - *schwab.APIError with status code, message, and up to 1 MiB of the raw body
  • Functional options - WithToken, WithHTTPClient, WithTLSConfig, WithBaseURL, WithResponseBodyLimit, WithUserAgent, WithHeader, and WithHeaders for flexible client configuration. Invalid base URL overrides fail when a request is created instead of falling back to the production Schwab API. Sub-clients append their own API path prefixes, so custom base URLs can point at the API root. Response bodies are capped at 10 MiB by default; non-positive custom limits are ignored.
  • Context propagation - all request methods take context.Context
  • Testable - override HTTP client and base URL for httptest integration
  • No runtime dependencies - public client packages stay dependency-free; tests use stretchr/testify and kin-openapi

Installation

go get github.com/major/schwab-go

Requires Go 1.26 or later.

Quick start

Market Data
package main

import (
	"context"
	"fmt"
	"log"

	schwab "github.com/major/schwab-go/schwab"
	"github.com/major/schwab-go/schwab/marketdata"
)

func main() {
	client := marketdata.NewClient(
		schwab.WithToken("your-bearer-token"),
	)

	// Fetch quotes for multiple symbols.
	quotes, quoteErr, err := client.GetQuotes(
		context.Background(),
		[]string{"AAPL", "MSFT"},
		"quote,reference", // fields
		false,             // indicative
	)
	if err != nil {
		log.Fatal(err)
	}
	if quoteErr != nil {
		fmt.Printf("invalid symbols: %v\n", quoteErr.InvalidSymbols)
	}

	for symbol, entry := range *quotes {
		eq, err := entry.EquityQuote()
		if err != nil {
			continue
		}
		fmt.Printf("%s: $%.2f\n", symbol, eq.LastPrice)
	}
}
Trader
package main

import (
	"context"
	"fmt"
	"log"

	schwab "github.com/major/schwab-go/schwab"
	"github.com/major/schwab-go/schwab/trader"
)

func main() {
	client := trader.NewClient(
		schwab.WithToken("your-bearer-token"),
	)

	// Get encrypted account hashes (required for all account-scoped calls).
	accounts, err := client.GetAccountNumbers(context.Background())
	if err != nil {
		log.Fatal(err)
	}

	// Fetch account details with positions.
	for _, acct := range accounts {
		detail, err := client.GetAccount(
			context.Background(),
			acct.HashValue,
			"positions",
		)
		if err != nil {
			log.Fatal(err)
		}
		sa := detail.SecuritiesAccount
		fmt.Printf("Account %s: %d positions\n", sa.AccountNumber, len(sa.Positions))
	}
}
Error handling

All API errors are returned as *schwab.APIError, which includes the HTTP status code, a message, and up to 1 MiB of the raw response body:

import "errors"

_, _, err := client.GetQuotes(ctx, []string{"AAPL"}, "", false)
if apiErr, ok := errors.AsType[*schwab.APIError](err); ok {
	fmt.Printf("HTTP %d: %s\n", apiErr.StatusCode, apiErr.Message)
}

Use schwab.StatusCode(err) or schwab.IsUnauthorized(err) when callers only need generic HTTP status classification and should not depend on endpoint-specific error bodies.

Authentication

The schwab/auth package handles OAuth2 authorization code flow, token refresh, read-only token status inspection, and persistence as part of the core github.com/major/schwab-go module.

import (
    "context"
    "log"
    "os/exec"

    "github.com/major/schwab-go/schwab/auth"
    schwab "github.com/major/schwab-go/schwab"
    "github.com/major/schwab-go/schwab/marketdata"
)

cfg := auth.Config{
    ClientID:     "your-app-key",
    ClientSecret: "your-app-secret",
    CallbackURL:  "https://127.0.0.1:8443/callback",
}

store := auth.NewFileTokenStore("/path/to/tokens.json")
// For tests or short-lived applications, use auth.NewMemoryTokenStore().
ctx := context.Background()

// urlHandler receives the authorize URL. Open it in a browser or print it for SSH/headless use.
openBrowser := func(url string) error { return exec.Command("xdg-open", url).Start() }
provider, err := auth.Login(ctx, cfg, store, openBrowser)
if err != nil {
    log.Fatal(err)
}

client := marketdata.NewClient(schwab.WithTokenProvider(provider))

For headless or SSH environments, pass a urlHandler that prints the URL instead of opening a browser. Use auth.StartLogin when an application needs to display the authorization URL before blocking for the callback and token exchange.

auth.ConfigFromAPIBaseURL derives OAuth endpoints from a caller-owned Schwab API root or proxy prefix, and auth.OAuthBaseURLFromAPIBaseURL exposes the URL conversion separately for adapters with their own config structs.

auth.NewFileProvider is a convenience wrapper around auth.NewProvider(cfg, auth.NewFileTokenStore(path), httpClient). Provider.Token refreshes expired access tokens automatically and writes refreshed tokens back to the store. Use Provider.Refresh or auth.RefreshTokenFile for explicit refresh commands, and Provider.Status or auth.InspectToken for read-only status output that must not refresh or save tokens.

auth.RedactToken and auth.RedactClientID provide safe display strings for troubleshooting output. auth.NewMemoryTokenStore is available for tests, examples, and short-lived applications that do not need token durability across process restarts. Use auth.IsRequired, auth.IsExpired, and auth.IsCallback to classify auth failures at application boundaries without duplicating errors.As checks.

CLI applications that need auth login, auth status, auth refresh, a global auth gate, JSON output envelopes, or post-login default-account setup should keep that command policy in the application adapter layer. See Auth CLI adapter pattern for Cobra-oriented guidance.

If you already have a valid bearer token, skip schwab/auth and pass it directly with schwab.WithToken().

If you previously installed schwab/auth as its own module, remove the separate require github.com/major/schwab-go/schwab/auth ... entry and run go get github.com/major/schwab-go@latest && go mod tidy so auth resolves from the core module.

API coverage

Market Data (schwab/marketdata)
Method Description
GetQuotes Multi-symbol quotes with optional fields
GetQuote Single symbol quote
GetPriceHistory OHLCV candles with configurable period/frequency
SearchInstruments Search instruments by symbol or name
GetInstrumentByCUSIP Look up instrument by CUSIP
GetOptionChain Full option chain with strikes and expirations
GetExpirationChain Expiration dates for a symbol
GetMovers Market movers by index
GetMarketHours Market hours for string market IDs with date validation
GetMarketHoursTyped Market hours for typed market IDs with date validation
GetMarketHoursSingle Market hours for a single string market ID with date validation
GetMarketHoursSingleTyped Market hours for a single typed market ID with date validation

Typed market hours methods use MarketID constants such as marketdata.MarketIDEquity. All market hours methods validate supported market values and optional dates as YYYY-MM-DD values from today through one year out.

Trader (schwab/trader)
Method Description
GetAccountNumbers Encrypted account hashes (call first)
GetAccounts / GetAccount Account details with optional positions
GetAccountsRaw / GetAccountRaw Raw account JSON for adapters that need exact Schwab field presence, including omitted-vs-zero fidelity
GetOrders / GetAllOrders / GetOrder Retrieve orders
CreateOrder / ReplaceOrder / CancelOrder Order lifecycle
CreateOrderWithResponse / ReplaceOrderWithResponse Create/replace orders and return parsed order ID from Location header
PreviewOrder / PreviewOrderRequestBody Preview an order before submission
GetTransactions / GetTransaction / GetTransactionByID Transaction history
GetUserPreference User preferences

Documentation

Full API documentation is available on pkg.go.dev.

Contributing

Contributions are welcome. Please open an issue to discuss larger changes before submitting a pull request.

License

Apache License 2.0

Directories

Path Synopsis
examples
auth command
Command auth-example demonstrates the schwab/auth OAuth2 login flow.
Command auth-example demonstrates the schwab/auth OAuth2 login flow.
Package schwab provides a Go client for the Schwab API.
Package schwab provides a Go client for the Schwab API.
auth
Package auth implements OAuth2 authorization code flow for the Schwab API.
Package auth implements OAuth2 authorization code flow for the Schwab API.
internal/httpclient
Package httpclient provides a shared HTTP client for executing API requests and decoding responses used by the marketdata and trader packages.
Package httpclient provides a shared HTTP client for executing API requests and decoding responses used by the marketdata and trader packages.
internal/openapitest
Package openapitest provides OpenAPI contract assertions for package tests.
Package openapitest provides OpenAPI contract assertions for package tests.
marketdata
Package marketdata provides market data functionality for the Schwab API.
Package marketdata provides market data functionality for the Schwab API.
trader
Package trader provides trading functionality for the Schwab API.
Package trader provides trading functionality for the Schwab API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL