trade

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 5, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package trade provides access to Webull's Trading API: accounts, balances, positions, cash activities, reference data for tradable instruments, and orders across every asset class Webull offers.

Obtain a Client from the root package rather than constructing one here:

client, err := webull.NewClient(webull.Config{...})
accounts, err := client.Trade.Accounts(ctx)

All methods take a context.Context and are safe for concurrent use.

Orders

An Order describes an order to preview or place; PreviewOrder and PlaceOrder take a pointer because they fill in defaults and generated values that the caller needs afterwards. Only the fields a plain order needs must be set; the rest of Webull's required boilerplate is supplied.

Orders are validated locally before any request is sent, and a malformed order fails with ErrInvalidOrder without touching the network. The rules are Webull's: which sides, order types, times in force and quantity precision each asset class permits, which fields each order type needs, and how groups and multi-leg strategies may be shaped. Where Webull's written documentation and its API disagree, the API wins; each rule here was checked against the sandbox where that was possible.

Order safety

Every order carries a ClientOrderID, which is the key for lookup, replace and cancel. If the caller leaves it empty one is generated and written back to the Order before the request is sent, so the caller holds it even when no response arrives. That matters because PlaceOrder never retries: in this API a replayed placement is a duplicated order. After a transport error the outcome is unknown, and the right move is to look the order up by its ClientOrderID rather than to place it again.

A ClientOrderID is consumed once Webull accepts the order. Placing the same Order value a second time returns ErrDuplicateOrder. A rejected placement does not consume the ID, so a corrected Order may be resent as is.

Asset classes

Set Order.InstrumentType to trade futures, crypto or event contracts; equities are the default and options are inferred from Legs. Futures, crypto and event contracts each have their own account, with the matching AccountClass, and an order must be placed against the right one.

Some rules differ from Webull's prose and follow what the API does: options accept market orders and GTC sells, which the FAQ says they do not; event contracts accept GTC, which the guide says they do not; and a trailing stop on an option is rejected, which both agree on.

Groups and strategies

Bracket, OTO, OCO and OTOCO build a Combo of related orders that PlaceCombo submits as one call. Multi-leg option strategies are a single Order with an OptionStrategy and several Legs; LegFromSymbol builds a leg from the OCC symbols that OptionContracts returns.

Sandbox

Webull's sandbox enforces regular trading hours for DAY orders, so tests and tooling that must run at any hour should use GTC. It also refuses OTO, OCO and OTOCO groups, batch placement, and every crypto preview, all of which the documentation describes as supported. Those are recorded in the repository's compatibility matrix and the affected features are implemented to the documentation but unverified.

Numbers

Webull transmits every price, quantity and monetary amount as a decimal string. This package preserves that exactness with the SDK's Decimal type for fields that are always present and NullDecimal where a field may be absent, so that "not reported" is distinguishable from zero. Both embed shopspring's decimal types, so all their methods are available. No field is ever a float. Price builds a set NullDecimal from a literal.

Pagination

Webull uses two mechanisms, and this package exposes each as it is rather than hiding the difference. Instrument listings return a PaginationKey that is passed back to fetch the next page and is empty on the last one. Cash activities and order listings page by the ID of the last item seen. No method fetches more than one page on its own.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrDuplicateOrder = errors.New("trade: client order id already used")

ErrDuplicateOrder is returned when Webull rejects a placement because the ClientOrderID was already accepted for another order. The underlying *webull.APIError remains reachable with errors.As.

View Source
var ErrInvalidOrder = errors.New("trade: invalid order")

ErrInvalidOrder wraps local validation failures, which are detected before any request is sent.

Functions

This section is empty.

Types

type Account

type Account struct {
	AccountID     string       `json:"account_id"`
	AccountNumber string       `json:"account_number"`
	AccountType   AccountType  `json:"account_type"`
	AccountLabel  string       `json:"account_label"`
	AccountClass  AccountClass `json:"account_class"`
	UserID        string       `json:"user_id"`
}

Account is a brokerage account accessible to the API key.

type AccountClass

type AccountClass string

AccountClass is the regulatory and product classification of an account.

const (
	AccountClassIndividualCash        AccountClass = "INDIVIDUAL_CASH"
	AccountClassIndividualMargin      AccountClass = "INDIVIDUAL_MARGIN"
	AccountClassRothIRA               AccountClass = "ROTH_IRA"
	AccountClassTraditionalIRA        AccountClass = "TRADITIONAL_IRA"
	AccountClassRolloverIRA           AccountClass = "ROLLOVER_IRA"
	AccountClassManagedRothIRA        AccountClass = "MANAGED_ROTH_IRA"
	AccountClassManagedTraditionalIRA AccountClass = "MANAGED_TRADITIONAL_IRA"
	AccountClassCrypto                AccountClass = "CRYPTO"
	AccountClassFutures               AccountClass = "FUTURES"
	AccountClassEventsCash            AccountClass = "EVENTS_CASH"
)

Account classes.

type AccountType

type AccountType string

AccountType distinguishes cash and margin accounts.

const (
	AccountTypeCash   AccountType = "CASH"
	AccountTypeMargin AccountType = "MARGIN"
)

Account types.

type ActivitySubType

type ActivitySubType string

ActivitySubType refines an ActivityType.

const (
	SubTypeBuyToOpen    ActivitySubType = "BTO"
	SubTypeSellToClose  ActivitySubType = "STC"
	SubTypeWire         ActivitySubType = "WIRE"
	SubTypeACH          ActivitySubType = "ACH"
	SubTypeWireReverse  ActivitySubType = "WIRE_REVERSE"
	SubTypeACHReverse   ActivitySubType = "ACH_REVERSE"
	SubTypeWireDeposit  ActivitySubType = "WIRE_DEPOSIT"
	SubTypeWireWithdraw ActivitySubType = "WIRE_WITHDRAW"
	SubTypeACHReversal  ActivitySubType = "ACH_REVERSAL"
	SubTypeWireReversal ActivitySubType = "WIRE_REVERSAL"
	SubTypeCashJournal  ActivitySubType = "CASH_JOURNAL"
	SubTypeCredit       ActivitySubType = "CREDIT"
	SubTypeCreditCash   ActivitySubType = "CREDIT_CASH"
	SubTypeECExpiration ActivitySubType = "EC_EXPIRATION"
	SubTypeECPayout     ActivitySubType = "EC_PAYOUT"
	SubTypeOther        ActivitySubType = "OTHER"
)

Activity sub-types.

type ActivityType

type ActivityType string

ActivityType is the kind of a cash activity.

const (
	ActivityTrade       ActivityType = "TRADE"
	ActivityDeposit     ActivityType = "DEPOSIT"
	ActivityWithdraw    ActivityType = "WITHDRAW"
	ActivityFees        ActivityType = "FEES"
	ActivityJournal     ActivityType = "JOURNAL"
	ActivityInterests   ActivityType = "INTERESTS"
	ActivityECStatement ActivityType = "EC_STATEMENT"
)

Activity types.

type AlgoType

type AlgoType string

AlgoType selects an algorithmic execution strategy.

const (
	TWAP AlgoType = "TWAP"
	VWAP AlgoType = "VWAP"
	POV  AlgoType = "POV"
)

Algorithm types.

type Balance

type Balance struct {
	Currency                  string  `json:"total_asset_currency"`
	TotalCashBalance          Decimal `json:"total_cash_balance"`
	TotalMarketValue          Decimal `json:"total_market_value"`
	TotalUnrealizedProfitLoss Decimal `json:"total_unrealized_profit_loss"`
	TotalNetLiquidationValue  Decimal `json:"total_net_liquidation_value"`
	TotalDayProfitLoss        Decimal `json:"total_day_profit_loss"`

	// DayTradesLeft is a count, or "UNLIMITED". It is left as a string because
	// the sentinel makes it neither a number nor an enumeration.
	DayTradesLeft string `json:"day_trades_left"`

	MaintenanceMargin      NullDecimal `json:"maintenance_margin"`
	UsedMargin             NullDecimal `json:"used_margin"`
	UsedMarginForOpenOrder NullDecimal `json:"used_margin_for_open_order"`
	InitMargin             NullDecimal `json:"init_margin"`
	IntradayMargin         NullDecimal `json:"intraday_margin"`
	MarginExcess           NullDecimal `json:"margin_excess"`
	MarginRatio            NullDecimal `json:"margin_ratio"`

	// OpenMarginCalls lists outstanding margin calls. Webull's documentation
	// describes this as a single value; the API returns a list.
	OpenMarginCalls []MarginCall `json:"open_margin_calls"`

	CurrencyAssets []CurrencyAssets `json:"account_currency_assets"`
}

Balance is the financial summary of an account.

Fields documented as always present are Decimal. The rest are NullDecimal: Webull omits them for some account classes, and an omitted margin figure is not the same thing as a margin figure of zero.

type BatchOrderResult

type BatchOrderResult struct {
	ClientOrderID string `json:"client_order_id"`
	OrderID       string `json:"order_id"`
	Code          string `json:"error_code"`
	Message       string `json:"message"`
}

BatchOrderResult is the outcome for one order in a batch. Code and Message are set when the order was rejected.

type BatchResult

type BatchResult struct {
	Total   int                `json:"total"`
	Success int                `json:"success"`
	Failed  int                `json:"failed"`
	Orders  []BatchOrderResult `json:"batch_orders"`
}

BatchResult reports the outcome of PlaceOrders.

type CashActivitiesRequest

type CashActivitiesRequest struct {
	AccountID string
	// ActivityTypes restricts results to the given kinds. Empty means all.
	ActivityTypes []ActivityType
	// StartTime and EndTime bound the query. When both are zero Webull returns
	// the last seven days.
	StartTime time.Time
	EndTime   time.Time
	// LastActivityID is the ID of the final item from the previous page. Leave
	// empty for the first page.
	LastActivityID string
	// PageSize is between 1 and 200. Zero lets Webull apply its default of 10.
	PageSize int
}

CashActivitiesRequest filters a cash activity query.

type CashActivity

type CashActivity struct {
	ID              string          `json:"id"`
	AccountID       string          `json:"account_id"`
	ActivityType    ActivityType    `json:"activity_type"`
	ActivitySubType ActivitySubType `json:"activity_sub_type"`
	Currency        string          `json:"currency"`
	Market          string          `json:"market"`
	Symbol          string          `json:"symbol"`
	// TradeDate is the accounting date, in yyyy-MM-dd form.
	TradeDate string `json:"trade_date"`
	// NetAmount is positive for a credit and negative for a debit.
	NetAmount Decimal   `json:"net_amount"`
	BizTime   time.Time `json:"biz_time"`
}

CashActivity is a cash movement on an account: a trade, transfer, fee or similar.

type Category

type Category string

Category identifies a market and asset class in instrument lookups.

const (
	CategoryUSStock   Category = "US_STOCK"
	CategoryUSOption  Category = "US_OPTION"
	CategoryUSFutures Category = "US_FUTURES"
	CategoryUSCrypto  Category = "US_CRYPTO"
)

Categories.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client calls the Trading API. It is safe for concurrent use.

func New

func New(doer *transport.Doer, host string) *Client

New returns a Client that sends requests through doer to host.

It exists so the root webull package can compose a Client; callers of the SDK should use webull.NewClient, which resolves the host for the configured environment and wires authentication.

func (*Client) Accounts

func (c *Client) Accounts(ctx context.Context) ([]Account, error)

Accounts lists the accounts this API key can access.

func (*Client) Balance

func (c *Client) Balance(ctx context.Context, accountID string) (*Balance, error)

Balance returns the balance of an account.

func (*Client) CancelCombo

func (c *Client) CancelCombo(ctx context.Context, accountID string, combo *Combo) error

CancelCombo cancels every order in a group that can still be cancelled.

Cancelling an unfilled master cancels its exits too, but once the master has filled the exits are working orders in their own right, so every order is attempted. Orders that are already gone — cancelled along with a sibling, filled, or otherwise not cancellable — are not errors: the group's goal is that nothing is left working. Any other failure is returned, joined, after every order has been attempted.

func (*Client) CancelOrder

func (c *Client) CancelOrder(ctx context.Context, accountID, clientOrderID string) (*OrderReceipt, error)

CancelOrder cancels a working order by its client order ID.

Example

ExampleClient_CancelOrder cancels a working order by its client order ID — the same value the SDK wrote onto the Order at placement.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/sfreiberg/webull"
	"github.com/sfreiberg/webull/trade"
)

// exampleClient is a small helper the examples share. In real code, build the
// client once and reuse it.
func exampleClient() (*webull.Client, string) {
	client, err := webull.NewClient(webull.Config{
		AppKey:      os.Getenv("WEBULL_APP_KEY"),
		AppSecret:   os.Getenv("WEBULL_APP_SECRET"),
		Environment: webull.Sandbox,
	})
	if err != nil {
		log.Fatal(err)
	}
	accounts, err := client.Trade.Accounts(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	return client, accounts[0].AccountID
}

func main() {
	client, accountID := exampleClient()

	order := &trade.Order{
		Symbol:      "AAPL",
		Side:        trade.Buy,
		Type:        trade.Limit,
		TimeInForce: trade.GTC,
		Quantity:    trade.Price("1"),
		LimitPrice:  trade.Price("1.00"), // far from market, so it rests
	}
	if _, err := client.Trade.PlaceOrder(context.Background(), accountID, order); err != nil {
		log.Fatal(err)
	}

	if _, err := client.Trade.CancelOrder(context.Background(), accountID, order.ClientOrderID); err != nil {
		log.Fatal(err)
	}
	fmt.Println("cancelled", order.ClientOrderID)
}

func (*Client) CashActivities

func (c *Client) CashActivities(ctx context.Context, req CashActivitiesRequest) ([]CashActivity, error)

CashActivities returns one page of cash activities. To fetch the next page, set LastActivityID to the ID of the last item returned.

func (*Client) CryptoProfiles

func (c *Client) CryptoProfiles(ctx context.Context, req CryptoProfilesRequest) (*CryptoProfilesPage, error)

CryptoProfiles looks up crypto reference data.

func (*Client) EventCategories

func (c *Client) EventCategories(ctx context.Context) ([]EventCategory, error)

EventCategories lists all event contract categories.

func (*Client) EventMarkets

func (c *Client) EventMarkets(ctx context.Context, req EventMarketsRequest) (*EventMarketsPage, error)

EventMarkets lists tradable event contract markets.

func (*Client) EventSeries

func (c *Client) EventSeries(ctx context.Context, req EventSeriesRequest) (*EventSeriesPage, error)

EventSeries lists event contract series.

func (*Client) Events

func (c *Client) Events(ctx context.Context, req EventsRequest) ([]Event, error)

Events lists the events in a series. Webull does not paginate this endpoint.

func (*Client) FuturesContracts

func (c *Client) FuturesContracts(ctx context.Context, req FuturesContractsRequest) ([]FuturesContract, error)

FuturesContracts looks up futures contract definitions.

func (*Client) FuturesProductClasses

func (c *Client) FuturesProductClasses(ctx context.Context) ([]FuturesProductClass, error)

FuturesProductClasses lists the classification groups for futures.

func (*Client) FuturesProducts

func (c *Client) FuturesProducts(ctx context.Context, productClassID int) ([]FuturesProduct, error)

FuturesProducts lists futures products, optionally within one class. A productClassID of zero means all classes.

func (*Client) OpenOrders

func (c *Client) OpenOrders(ctx context.Context, req OrdersRequest) ([]OrderGroup, error)

OpenOrders returns one page of working orders.

func (*Client) OptionContracts

func (c *Client) OptionContracts(ctx context.Context, req OptionContractsRequest) (*OptionContractsPage, error)

OptionContracts looks up option contracts.

func (*Client) Order

func (c *Client) Order(ctx context.Context, accountID, clientOrderID string) (*OrderGroup, error)

Order retrieves an order by its client order ID.

func (*Client) OrderHistory

func (c *Client) OrderHistory(ctx context.Context, req OrdersRequest) ([]OrderGroup, error)

OrderHistory returns one page of past orders. Its status for a recently changed order can lag behind Order; an order reported CANCELLED by Order was observed as PENDING here moments later.

func (*Client) PlaceCombo

func (c *Client) PlaceCombo(ctx context.Context, accountID string, combo *Combo) (*OrderReceipt, error)

PlaceCombo submits a group. The receipt carries the group's ComboOrderID and ClientComboOrderID; the individual orders are identified by their own ClientOrderIDs, which are generated and written back before sending. Like PlaceOrder it never retries.

Example

ExampleClient_PlaceCombo places a bracket: an entry order with a take-profit and a stop-loss that are activated once it fills, and are mutually cancelling. Bracket, OTO, OCO and OTOCO build a Combo from plain Orders; PlaceCombo submits it and CancelCombo cancels the whole group.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/sfreiberg/webull"
	"github.com/sfreiberg/webull/trade"
)

// exampleClient is a small helper the examples share. In real code, build the
// client once and reuse it.
func exampleClient() (*webull.Client, string) {
	client, err := webull.NewClient(webull.Config{
		AppKey:      os.Getenv("WEBULL_APP_KEY"),
		AppSecret:   os.Getenv("WEBULL_APP_SECRET"),
		Environment: webull.Sandbox,
	})
	if err != nil {
		log.Fatal(err)
	}
	accounts, err := client.Trade.Accounts(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	return client, accounts[0].AccountID
}

func main() {
	client, accountID := exampleClient()

	combo := trade.Bracket(
		&trade.Order{Symbol: "AAPL", Side: trade.Buy, Type: trade.Limit, TimeInForce: trade.GTC, Quantity: trade.Price("1"), LimitPrice: trade.Price("180.00")},
		&trade.Order{Symbol: "AAPL", Side: trade.Sell, Type: trade.Limit, TimeInForce: trade.GTC, Quantity: trade.Price("1"), LimitPrice: trade.Price("200.00")},   // take profit
		&trade.Order{Symbol: "AAPL", Side: trade.Sell, Type: trade.StopLoss, TimeInForce: trade.GTC, Quantity: trade.Price("1"), StopPrice: trade.Price("170.00")}, // stop loss
	)

	receipt, err := client.Trade.PlaceCombo(context.Background(), accountID, combo)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("placed group", receipt.ComboOrderID)

	// CancelCombo cancels every order in the group still working; an
	// unfilled master takes its exits with it, while a filled master
	// leaves independent exit orders that this call still cancels.
	if err := client.Trade.CancelCombo(context.Background(), accountID, combo); err != nil {
		log.Fatal(err)
	}
}

func (*Client) PlaceOrder

func (c *Client) PlaceOrder(ctx context.Context, accountID string, order *Order) (*OrderReceipt, error)

PlaceOrder submits an order.

If order.ClientOrderID is empty a value is generated and written back before the request is sent. Should the call fail with a transport error, the outcome is unknown: the order may have been accepted. Use Order with that ClientOrderID to find out before retrying — PlaceOrder itself never retries.

Example

ExampleClient_PlaceOrder places a limit order to buy ten shares. The SDK fills in what Webull requires but a caller should not have to know: the market, combo type, entrust type, trading session, and a client order ID.

package main

import (
	"context"
	"errors"
	"fmt"
	"log"
	"os"

	"github.com/sfreiberg/webull"
	"github.com/sfreiberg/webull/trade"
)

// exampleClient is a small helper the examples share. In real code, build the
// client once and reuse it.
func exampleClient() (*webull.Client, string) {
	client, err := webull.NewClient(webull.Config{
		AppKey:      os.Getenv("WEBULL_APP_KEY"),
		AppSecret:   os.Getenv("WEBULL_APP_SECRET"),
		Environment: webull.Sandbox,
	})
	if err != nil {
		log.Fatal(err)
	}
	accounts, err := client.Trade.Accounts(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	return client, accounts[0].AccountID
}

func main() {
	client, accountID := exampleClient()

	order := &trade.Order{
		Symbol:     "AAPL",
		Side:       trade.Buy,
		Type:       trade.Limit,
		Quantity:   trade.Price("10"),
		LimitPrice: trade.Price("180.00"),
	}

	receipt, err := client.Trade.PlaceOrder(context.Background(), accountID, order)
	if err != nil {
		var apiErr *webull.APIError
		if errors.As(err, &apiErr) {
			log.Fatalf("rejected: %s (%s)", apiErr.Message, apiErr.Code)
		}
		// A transport failure leaves the outcome unknown. The SDK wrote
		// order.ClientOrderID before sending, so the order can be looked up
		// rather than blindly resent.
		log.Fatalf("outcome unknown for %s: %v", order.ClientOrderID, err)
	}
	fmt.Println("placed", receipt.OrderID)
}
Example (Option)

ExampleClient_PlaceOrder_option buys a single option contract. LegFromSymbol parses the strike, expiry and right from an OCC symbol; the caller sets the side and quantity on the order.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/sfreiberg/webull"
	"github.com/sfreiberg/webull/trade"
)

// exampleClient is a small helper the examples share. In real code, build the
// client once and reuse it.
func exampleClient() (*webull.Client, string) {
	client, err := webull.NewClient(webull.Config{
		AppKey:      os.Getenv("WEBULL_APP_KEY"),
		AppSecret:   os.Getenv("WEBULL_APP_SECRET"),
		Environment: webull.Sandbox,
	})
	if err != nil {
		log.Fatal(err)
	}
	accounts, err := client.Trade.Accounts(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	return client, accounts[0].AccountID
}

func main() {
	client, accountID := exampleClient()

	leg, err := trade.LegFromSymbol("AAPL261218C00240000")
	if err != nil {
		log.Fatal(err)
	}
	order := &trade.Order{
		Symbol:         "AAPL",
		Side:           trade.Buy,
		Type:           trade.Limit,
		TimeInForce:    trade.Day,
		Quantity:       trade.Price("1"),
		LimitPrice:     trade.Price("5.00"),
		PositionIntent: trade.BuyToOpen,
		Legs:           []trade.OrderLeg{leg},
	}
	receipt, err := client.Trade.PlaceOrder(context.Background(), accountID, order)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("placed", receipt.OrderID)
}
Example (VerticalSpread)

ExampleClient_PlaceOrder_verticalSpread places a two-leg vertical spread. A multi-leg strategy is one Order carrying an OptionStrategy and its legs; the price is the net debit or credit for the spread.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/sfreiberg/webull"
	"github.com/sfreiberg/webull/trade"
)

// exampleClient is a small helper the examples share. In real code, build the
// client once and reuse it.
func exampleClient() (*webull.Client, string) {
	client, err := webull.NewClient(webull.Config{
		AppKey:      os.Getenv("WEBULL_APP_KEY"),
		AppSecret:   os.Getenv("WEBULL_APP_SECRET"),
		Environment: webull.Sandbox,
	})
	if err != nil {
		log.Fatal(err)
	}
	accounts, err := client.Trade.Accounts(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	return client, accounts[0].AccountID
}

func main() {
	client, accountID := exampleClient()

	long, err := trade.LegFromSymbol("AAPL261218C00240000")
	if err != nil {
		log.Fatal(err)
	}
	short, err := trade.LegFromSymbol("AAPL261218C00250000")
	if err != nil {
		log.Fatal(err)
	}
	long.Side, short.Side = trade.Buy, trade.Sell

	receipt, err := client.Trade.PlaceOrder(context.Background(), accountID, &trade.Order{
		Symbol:         "AAPL",
		Side:           trade.Buy,
		Type:           trade.Limit,
		TimeInForce:    trade.Day,
		Quantity:       trade.Price("1"),    // one spread
		LimitPrice:     trade.Price("3.50"), // net debit
		PositionIntent: trade.BuyToOpen,
		OptionStrategy: trade.StrategyVertical,
		Legs:           []trade.OrderLeg{long, short},
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("placed", receipt.OrderID)
}

func (*Client) PlaceOrders

func (c *Client) PlaceOrders(ctx context.Context, accountID string, orders []*Order) (*BatchResult, error)

PlaceOrders submits several equity orders at once. Webull accepts only Normal combo, Market or Limit, Day orders by quantity in a batch; each order is validated as for PlaceOrder and those constraints are enforced locally.

func (*Client) Positions

func (c *Client) Positions(ctx context.Context, accountID string) ([]Position, error)

Positions lists the open positions in an account. Webull does not paginate this endpoint.

func (*Client) PreviewCombo

func (c *Client) PreviewCombo(ctx context.Context, accountID string, combo *Combo) (*OrderPreview, error)

PreviewCombo estimates the cost of a group without placing it.

func (*Client) PreviewOrder

func (c *Client) PreviewOrder(ctx context.Context, accountID string, order *Order) (*OrderPreview, error)

PreviewOrder estimates cost and fees for an order without placing it. It applies the same defaults and validation as PlaceOrder.

Example

ExampleClient_PreviewOrder estimates cost and fees without placing anything.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/sfreiberg/webull"
	"github.com/sfreiberg/webull/trade"
)

// exampleClient is a small helper the examples share. In real code, build the
// client once and reuse it.
func exampleClient() (*webull.Client, string) {
	client, err := webull.NewClient(webull.Config{
		AppKey:      os.Getenv("WEBULL_APP_KEY"),
		AppSecret:   os.Getenv("WEBULL_APP_SECRET"),
		Environment: webull.Sandbox,
	})
	if err != nil {
		log.Fatal(err)
	}
	accounts, err := client.Trade.Accounts(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	return client, accounts[0].AccountID
}

func main() {
	client, accountID := exampleClient()

	preview, err := client.Trade.PreviewOrder(context.Background(), accountID, &trade.Order{
		Symbol:     "AAPL",
		Side:       trade.Buy,
		Type:       trade.Limit,
		Quantity:   trade.Price("10"),
		LimitPrice: trade.Price("180.00"),
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(preview.EstimatedCost)
}

func (*Client) ReplaceOrder

func (c *Client) ReplaceOrder(ctx context.Context, accountID string, mod OrderModification) (*OrderReceipt, error)

ReplaceOrder modifies a working order.

func (*Client) StockProfiles

func (c *Client) StockProfiles(ctx context.Context, req StockProfilesRequest) (*StockProfilesPage, error)

StockProfiles looks up equity reference data.

type Combo

type Combo struct {
	// ClientComboOrderID identifies the group. If empty, PlaceCombo generates
	// one and writes it back before sending.
	ClientComboOrderID string
	Orders             []*Order
}

Combo is a group of orders placed together: a bracket (an order with take-profit and stop-loss orders attached), an OTO, an OCO or an OTOCO. Build one with Bracket, OTO, OCO or OTOCO, which assign each order's role, and place it with PlaceCombo.

Every order in a group is a complete Order in its own right: it carries its own symbol, side, quantity and ClientOrderID, and can be looked up individually with Order. Cancelling the RoleMaster cancels the whole group.

Webull's rules, enforced locally before any request:

  • OTO, OCO and OTOCO groups are equity-only.
  • Brackets on options require a single-leg order; multi-leg strategies cannot be grouped at all.
  • Each role admits only certain order types and counts; see the constructors.

The sandbox accepts brackets but rejects OTO, OCO and OTOCO groups with "invalid combo_type", contrary to Webull's documentation. Those three are implemented to the documentation and are unverified.

func Bracket

func Bracket(master, takeProfit, stopLoss *Order) *Combo

Bracket attaches a take-profit and/or stop-loss order to a master order.

The master is a Market or Limit order. takeProfit is a Limit order and stopLoss a RoleStopLoss order, each on the opposite side; either may be nil but not both. To attach exits to an existing position, pass a nil master and give the exits the closing side.

func OCO

func OCO(orders ...*Order) *Combo

OCO is a one-cancels-other group of two to six orders: when one fills, the rest are cancelled.

func OTO

func OTO(master *Order, triggered ...*Order) *Combo

OTO is a one-triggers-other group: when master fills, the triggered orders (one to six) are submitted.

func OTOCO

func OTOCO(master *Order, oco ...*Order) *Combo

OTOCO is a one-triggers-OCO group: when master fills, the given orders (one to six) are submitted as an OCO set.

type ComboType

type ComboType string

ComboType is an order's role within a group. Normal is a standalone order; every other value only makes sense inside a Combo.

const (
	Normal ComboType = "NORMAL"
	// RoleMaster is the primary order of a bracket, OTO or OTOCO group.
	RoleMaster ComboType = "MASTER"
	// RoleStopProfit and RoleStopLoss are the take-profit and stop-loss
	// orders of a bracket.
	RoleStopProfit ComboType = "STOP_PROFIT"
	RoleStopLoss   ComboType = "STOP_LOSS"
	// RoleOTO, RoleOCO and RoleOTOCO mark the dependent orders of those
	// groups.
	RoleOTO   ComboType = "OTO"
	RoleOCO   ComboType = "OCO"
	RoleOTOCO ComboType = "OTOCO"
)

Combo roles. See Bracket, OTO, OCO and OTOCO for the groups they form.

type Commission

type Commission struct {
	Actual     NullDecimal `json:"actual_commission"`
	Receivable NullDecimal `json:"receivable_commission"`
}

Commission is the commission charged on an order.

type CryptoProfile

type CryptoProfile struct {
	Name         string         `json:"name"`
	InstrumentID string         `json:"instrument_id"`
	ExchangeCode string         `json:"exchange_code"`
	Category     Category       `json:"category"`
	Symbol       string         `json:"symbol"`
	Status       TradableStatus `json:"status"`
	Currency     string         `json:"currency"`

	MinTradeAmount   NullDecimal `json:"min_trade_amt"`
	MaxTradeAmount   NullDecimal `json:"max_trade_amt"`
	MinTradeQuantity NullDecimal `json:"min_trade_qty"`
	MaxTradeQuantity NullDecimal `json:"max_trade_qty"`
	PriceStep        NullDecimal `json:"price_step"`
	LotSize          NullDecimal `json:"lot_size"`
}

CryptoProfile is reference data for a crypto trading pair.

type CryptoProfilesPage

type CryptoProfilesPage struct {
	Profiles      []CryptoProfile `json:"data"`
	PaginationKey string          `json:"pagination_key"`
}

CryptoProfilesPage is one page of crypto profiles.

type CryptoProfilesRequest

type CryptoProfilesRequest struct {
	// Symbols holds up to 100 pairs such as "BTCUSD".
	Symbols       []string
	Status        TradableStatus
	PaginationKey string
}

CryptoProfilesRequest filters a crypto lookup.

type CurrencyAssets

type CurrencyAssets struct {
	Currency             string  `json:"currency"`
	CashBalance          Decimal `json:"cash_balance"`
	MarketValue          Decimal `json:"market_value"`
	UnrealizedProfitLoss Decimal `json:"unrealized_profit_loss"`

	SettledCash             NullDecimal `json:"settled_cash"`
	UnsettledCash           NullDecimal `json:"unsettled_cash"`
	HeldAmount              NullDecimal `json:"held_amount"`
	FrozenAmount            NullDecimal `json:"frozen_amount"`
	BuyingPower             NullDecimal `json:"buying_power"`
	AvailableWithdrawal     NullDecimal `json:"available_withdrawal"`
	InterestsUnpaid         NullDecimal `json:"interests_unpaid"`
	NetLiquidationValue     NullDecimal `json:"net_liquidation_value"`
	OptionBuyingPower       NullDecimal `json:"option_buying_power"`
	DayBuyingPower          NullDecimal `json:"day_buying_power"`
	OvernightBuyingPower    NullDecimal `json:"overnight_buying_power"`
	NightTradingBuyingPower NullDecimal `json:"night_trading_buying_power"`
	DayProfitLoss           NullDecimal `json:"day_profit_loss"`
	UsedMargin              NullDecimal `json:"used_margin"`
	UsedMarginForOpenOrder  NullDecimal `json:"used_margin_for_open_order"`
	InitMargin              NullDecimal `json:"init_margin"`
	MaintenanceMargin       NullDecimal `json:"maintenance_margin"`
	IntradayMargin          NullDecimal `json:"intraday_margin"`
	MarginExcess            NullDecimal `json:"margin_excess"`
	MarginRatio             NullDecimal `json:"margin_ratio"`
}

CurrencyAssets is the per-currency breakdown of a Balance.

type Decimal

type Decimal = wire.Decimal

Decimal is the SDK's fixed-point decimal type. It embeds github.com/shopspring/decimal.Decimal, so all of that type's methods are available, and additionally decodes Webull's absent forms — null and an empty string — where shopspring's own type rejects "".

type EntrustType

type EntrustType string

EntrustType is whether quantity is expressed in units or in cash. ByAmount is accepted for equities (fractional shares) and crypto only.

const (
	ByQuantity EntrustType = "QTY"
	ByAmount   EntrustType = "AMOUNT"
)

Entrust types.

type Event

type Event struct {
	SeriesID  string      `json:"series_id"`
	Symbol    string      `json:"symbol"`
	Name      string      `json:"name"`
	ShortName string      `json:"short_name"`
	Status    EventStatus `json:"status"`
	// StrikeDate is yyyy-MM-dd; StrikePeriod is free text such as "Q4 2026".
	StrikeDate        string `json:"strike_date"`
	StrikePeriod      string `json:"strike_period"`
	MutuallyExclusive bool   `json:"mutually_exclusive"`
}

Event is one occurrence within a series, such as a particular Fed meeting.

type EventCategory

type EventCategory struct {
	// ID is numeric on the wire although Webull documents it as a string.
	ID   int               `json:"category_id"`
	Code EventCategoryCode `json:"category_code"`
	Name string            `json:"category_name"`
}

EventCategory groups event contract series, such as Economics or Sports.

type EventCategoryCode

type EventCategoryCode string

EventCategoryCode identifies a category of event contracts.

const (
	EventClimateWeather EventCategoryCode = "CLIMATE_WEATHER"
	EventCommodities    EventCategoryCode = "COMMODITIES"
	EventCrypto         EventCategoryCode = "CRYPTO"
	EventEconomics      EventCategoryCode = "ECONOMICS"
	EventElections      EventCategoryCode = "ELECTIONS"
	EventEntertainment  EventCategoryCode = "ENTERTAINMENT"
	EventFinancials     EventCategoryCode = "FINANCIALS"
	EventScienceTech    EventCategoryCode = "SCIENCE_TECHNOLOGY"
	EventSports         EventCategoryCode = "SPORTS"
)

Event contract categories, as returned by the categories endpoint. Webull's documentation lists POLITICS and TRANSPORTATION instead of ELECTIONS and COMMODITIES; the API returns the latter.

type EventFrequency

type EventFrequency string

EventFrequency is how often a series recurs.

const (
	FrequencyHourly  EventFrequency = "HOURLY"
	FrequencyDaily   EventFrequency = "DAILY"
	FrequencyWeekly  EventFrequency = "WEEKLY"
	FrequencyMonthly EventFrequency = "MONTHLY"
	FrequencyAnnual  EventFrequency = "ANNUAL"
	FrequencyOneOff  EventFrequency = "ONE_OFF"
	FrequencyCustom  EventFrequency = "CUSTOM"
)

Event series frequencies.

type EventMarket

type EventMarket struct {
	SeriesID     string `json:"series_id"`
	SeriesSymbol string `json:"series_symbol"`
	SeriesName   string `json:"series_name"`
	EventSymbol  string `json:"event_symbol"`
	EventName    string `json:"event_name"`
	InstrumentID string `json:"instrument_id"`
	Symbol       string `json:"symbol"`
	Name         string `json:"name"`
	YesCondition string `json:"yes_condition"`

	Status         EventMarketStatus `json:"status"`
	TradableStatus TradableStatus    `json:"tradable_status"`
	CanCloseEarly  bool              `json:"can_close_early"`
	Fractionable   bool              `json:"fractionable"`

	// Dates are yyyy-MM-dd.
	LastTradingDate string `json:"last_trading_date"`
	ExpectedExpDate string `json:"expected_exp_date"`
	LatestExpDate   string `json:"latest_exp_date"`
	PayoutDate      string `json:"payout_date"`

	PriceRanges []PriceRange `json:"price_ranges"`
}

EventMarket is a tradable yes/no contract on an event.

type EventMarketStatus

type EventMarketStatus string

EventMarketStatus is the listing status of an event market.

const (
	EventMarketNotSet       EventMarketStatus = "NOT_SET"
	EventMarketListing      EventMarketStatus = "LISTING"
	EventMarketDelisting    EventMarketStatus = "DELISTING"
	EventMarketOther        EventMarketStatus = "OTHER"
	EventMarketUnrecognized EventMarketStatus = "UNRECOGNIZED"
)

Event market statuses.

type EventMarketsPage

type EventMarketsPage struct {
	Markets       []EventMarket `json:"data"`
	PaginationKey string        `json:"pagination_key"`
}

EventMarketsPage is one page of event markets.

type EventMarketsRequest

type EventMarketsRequest struct {
	SeriesSymbol string
	EventSymbol  string
	// Symbols holds up to 100 market symbols.
	Symbols []string
	// ExpirationDateAfter is yyyy-MM-dd.
	ExpirationDateAfter string
	PaginationKey       string
}

EventMarketsRequest filters an event market lookup.

type EventOutcome

type EventOutcome string

EventOutcome is the side of an event contract: which binary outcome is being bought or sold. It is required on every event contract order and must not be set on any other kind.

const (
	OutcomeYes EventOutcome = "yes"
	OutcomeNo  EventOutcome = "no"
)

Event outcomes.

type EventSeries

type EventSeries struct {
	SeriesID  string            `json:"series_id"`
	Symbol    string            `json:"symbol"`
	Name      string            `json:"name"`
	Category  EventCategoryCode `json:"category"`
	Frequency EventFrequency    `json:"frequency"`
}

EventSeries is a template for recurring events, such as "Fed Meeting".

type EventSeriesPage

type EventSeriesPage struct {
	Series        []EventSeries `json:"data"`
	PaginationKey string        `json:"pagination_key"`
}

EventSeriesPage is one page of event series.

type EventSeriesRequest

type EventSeriesRequest struct {
	Category EventCategoryCode
	// Symbols holds up to 100 series symbols.
	Symbols       []string
	PaginationKey string
}

EventSeriesRequest filters a series lookup.

type EventStatus

type EventStatus string

EventStatus is whether an event is open.

const (
	EventActive   EventStatus = "ACTIVE"
	EventInactive EventStatus = "INACTIVE"
)

Event statuses.

type EventTradeMode

type EventTradeMode string

EventTradeMode is how an event contract order's size is expressed.

const (
	TradeInAmount   EventTradeMode = "TRADE_IN_AMOUNT"
	TradeInContract EventTradeMode = "TRADE_IN_CONTRACT"
)

Event trade modes.

type EventsRequest

type EventsRequest struct {
	SeriesSymbol string
	// Symbols holds up to 100 event symbols.
	Symbols []string
	Status  EventStatus
}

EventsRequest selects events within a series.

type Fee

type Fee struct {
	Type       string      `json:"type"`
	Actual     NullDecimal `json:"actual_value"`
	Receivable NullDecimal `json:"receivable_value"`
}

Fee is one regulatory or exchange fee on an order.

type FuturesContract

type FuturesContract struct {
	Symbol           string              `json:"symbol"`
	InstrumentID     string              `json:"instrument_id"`
	ExchangeCode     string              `json:"exchange_code"`
	Code             string              `json:"code"`
	Name             string              `json:"name"`
	ProductClassID   int                 `json:"product_class_id"`
	ProductClassName string              `json:"product_class_name"`
	Status           TradableStatus      `json:"status"`
	Currency         string              `json:"currency"`
	ContractType     FuturesContractType `json:"contract_type"`
	Settlement       Settlement          `json:"settlement"`
	Unit             FuturesUnit         `json:"unit"`

	// ContractMonth is yyyyMM. The date fields are yyyy-MM-dd.
	ContractMonth    string `json:"contract_month"`
	SettlementDate   string `json:"settlement_date"`
	FirstNoticeDate  string `json:"first_notice_date"`
	LastNoticeDate   string `json:"last_notice_date"`
	FirstTradingDate string `json:"first_trading_date"`
	LastTradingDate  string `json:"last_trading_date"`

	// Size is the contract multiplier; MinTick the minimum price increment.
	Size    Decimal `json:"size"`
	MinTick Decimal `json:"min_tick"`
}

FuturesContract is a dated or continuous futures contract.

type FuturesContractType

type FuturesContractType string

FuturesContractType distinguishes dated contracts from the continuous one.

const (
	ContractMonthly FuturesContractType = "MONTHLY"
	ContractMain    FuturesContractType = "MAIN"
)

Futures contract types.

type FuturesContractsRequest

type FuturesContractsRequest struct {
	// Symbols holds contract symbols such as "ESZ5".
	Symbols []string
	// Code selects every contract for a product, such as "ES".
	Code   string
	Status TradableStatus
}

FuturesContractsRequest selects contracts by symbol or by product code. One of Symbols or Code must be set.

type FuturesProduct

type FuturesProduct struct {
	Name             string `json:"name"`
	Code             string `json:"code"`
	ProductClassID   int    `json:"product_class_id"`
	ProductClassName string `json:"product_class_name"`
	ExchangeCode     string `json:"exchange_code"`
}

FuturesProduct is an underlying product such as "E-Mini S&P 500" (code ES).

type FuturesProductClass

type FuturesProductClass struct {
	ID   int    `json:"product_class_id"`
	Name string `json:"product_class_name"`
}

FuturesProductClass groups futures products, such as "Equities" or "Energy".

type FuturesUnit

type FuturesUnit int

FuturesUnit is the pricing unit of a futures contract, as a numeric code. Webull's documentation describes this field as a string enumeration such as "1 - Index points"; the API transmits the integer. String returns the description.

func (FuturesUnit) String

func (u FuturesUnit) String() string

String returns the unit's description, or its number if unknown.

type InstrumentType

type InstrumentType string

InstrumentType is the asset class of a position or order.

const (
	InstrumentEquity  InstrumentType = "EQUITY"
	InstrumentOption  InstrumentType = "OPTION"
	InstrumentFutures InstrumentType = "FUTURES"
	InstrumentCrypto  InstrumentType = "CRYPTO"
	InstrumentEvent   InstrumentType = "EVENT"
)

Instrument types.

type LegDirection

type LegDirection string

LegDirection is whether an option order legs into or out of a position.

const (
	LegIn  LegDirection = "LEG_IN"
	LegOut LegDirection = "LEG_OUT"
)

Leg directions.

type LegInStrategy

type LegInStrategy string

LegInStrategy is the multi-leg strategy a position becomes after legging in.

const (
	Vertical   LegInStrategy = "VERTICAL"
	Calendar   LegInStrategy = "CALENDAR"
	Strangle   LegInStrategy = "STRANGLE"
	Straddle   LegInStrategy = "STRADDLE"
	IronCondor LegInStrategy = "IRON_CONDOR"
	Butterfly  LegInStrategy = "BUTTERFLY"
	Custom     LegInStrategy = "CUSTOM"
)

Leg-in strategies.

type LegModification

type LegModification struct {
	ID       string      `json:"id"`
	Quantity NullDecimal `json:"quantity,omitzero"`
}

LegModification changes the quantity of one option leg. Both fields are required.

type ListingStatus

type ListingStatus string

ListingStatus is whether an option contract is currently listed.

const (
	Listing   ListingStatus = "LISTING"
	Delisting ListingStatus = "DELISTING"
)

Listing statuses.

type MarginCall

type MarginCall string

MarginCall is a kind of open margin call on an account.

const (
	MarginCallEM MarginCall = "EM"
	MarginCallRM MarginCall = "RM"
	MarginCallRT MarginCall = "RT"
	MarginCallDT MarginCall = "DT"
)

Margin call kinds.

type NullDecimal

type NullDecimal = wire.NullDecimal

NullDecimal is the SDK's nullable fixed-point decimal type, the counterpart of Decimal for fields that may be absent.

func Price

func Price(s string) NullDecimal

Price returns a set NullDecimal from a literal, for optional price fields. It panics on a malformed string, like regexp.MustCompile; use decimal.NewFromString for untrusted input.

Example

ExamplePrice builds a decimal quantity or price from a string, avoiding the precision loss of a float literal.

package main

import (
	"fmt"

	"github.com/sfreiberg/webull/trade"
)

func main() {
	q := trade.Price("10.5")
	fmt.Println(q.Decimal)
}
Output:
10.5

type OptionContract

type OptionContract struct {
	InstrumentID   string         `json:"instrument_id"`
	Symbol         string         `json:"symbol"`
	Status         ListingStatus  `json:"status"`
	TradableStatus TradableStatus `json:"tradable_status"`
	// ExpirationDate is in yyyy-MM-dd form, after any corporate-action
	// adjustment. InitExpirationDate is the original date and is returned by
	// the API but not documented.
	ExpirationDate     string `json:"expiration_date"`
	InitExpirationDate string `json:"init_expiration_date"`

	RootSymbol             string `json:"root_symbol"`
	UnderlyingSymbol       string `json:"underlying_symbol"`
	UnderlyingInstrumentID string `json:"underlying_instrument_id"`
	UnderlyingType         string `json:"underlying_type"`

	OptionType       OptionType  `json:"option_type"`
	Style            OptionStyle `json:"style"`
	StrikePrice      Decimal     `json:"strike_price"`
	Multiplier       Decimal     `json:"multiplier"`
	SettlementMethod string      `json:"settlement_method"`
	ExpiredCycle     string      `json:"expired_cycle"`
	// PPInd is the Penny Program indicator.
	PPInd           bool     `json:"ppind"`
	Currency        string   `json:"currency"`
	DefType         string   `json:"def_type"`
	ListedExchanges []string `json:"listed_exchanges"`

	// Deliverables is populated only when requested with ShowDeliverables.
	Deliverables []OptionDeliverable `json:"deliverables"`
}

OptionContract is the static definition of a listed option.

type OptionContractsPage

type OptionContractsPage struct {
	Contracts     []OptionContract `json:"data"`
	PaginationKey string           `json:"pagination_key"`
}

OptionContractsPage is one page of option contracts.

type OptionContractsRequest

type OptionContractsRequest struct {
	// OptionSymbols selects specific OCC symbols.
	OptionSymbols []string
	// UnderlyingSymbols selects chains by underlying.
	UnderlyingSymbols []string
	// Status defaults to Listing when empty.
	Status ListingStatus
	// StartDate and EndDate are yyyy-MM-dd expiration bounds.
	StartDate string
	EndDate   string
	// RootSymbol filters by series, such as "SPXW".
	RootSymbol string
	OptionType OptionType
	Style      OptionStyle
	// StrikePriceGTE and StrikePriceLTE bound the strike, inclusive. An
	// invalid (zero-value) NullDecimal leaves that bound unset.
	StrikePriceGTE NullDecimal
	StrikePriceLTE NullDecimal
	// PPInd filters on the Penny Program indicator; nil means either.
	PPInd *bool
	// ShowDeliverables includes the Deliverables array in each contract.
	ShowDeliverables bool
	PaginationKey    string
}

OptionContractsRequest filters an option chain lookup.

type OptionDeliverable

type OptionDeliverable struct {
	AssetType            string  `json:"asset_type"`
	Symbol               string  `json:"symbol"`
	InstrumentID         string  `json:"instrument_id"`
	Amount               Decimal `json:"amount"`
	AllocationPercentage Decimal `json:"allocation_percentage"`
	SettlementType       string  `json:"settlement_type"`
	SettlementMethod     string  `json:"settlement_method"`
	SettlementStatus     string  `json:"settlement_status"`
}

OptionDeliverable is what one contract delivers on exercise. After a corporate action a single contract may deliver several things.

type OptionStrategy

type OptionStrategy string

OptionStrategy classifies an option order or position. StrategySingle is one leg; every other value is a multi-leg order that needs at least two Legs and cannot be part of a Combo. Covered and collar strategies include a stock leg, and may be market orders.

const (
	StrategySingle          OptionStrategy = "SINGLE"
	StrategyCoveredStock    OptionStrategy = "COVERED_STOCK"
	StrategyStraddle        OptionStrategy = "STRADDLE"
	StrategyStrangle        OptionStrategy = "STRANGLE"
	StrategyVertical        OptionStrategy = "VERTICAL"
	StrategyCalendar        OptionStrategy = "CALENDAR"
	StrategyButterfly       OptionStrategy = "BUTTERFLY"
	StrategyCondor          OptionStrategy = "CONDOR"
	StrategyCollarWithStock OptionStrategy = "COLLAR_WITH_STOCK"
	StrategyIronButterfly   OptionStrategy = "IRON_BUTTERFLY"
	StrategyIronCondor      OptionStrategy = "IRON_CONDOR"
	StrategyDiagonal        OptionStrategy = "DIAGONAL"
)

Option strategies. Any value other than StrategySingle is a multi-leg order and requires at least two legs and a Normal combo type.

type OptionStyle

type OptionStyle string

OptionStyle is the exercise style of an option contract.

const (
	American OptionStyle = "AMERICAN"
	European OptionStyle = "EUROPEAN"
)

Option styles.

type OptionType

type OptionType string

OptionType is a call or a put.

const (
	Call OptionType = "CALL"
	Put  OptionType = "PUT"
)

Option types.

type Order

type Order struct {
	// ClientOrderID identifies the order to Webull and is the key for cancel,
	// replace and lookup. Webull requires 10 to 40 characters, unique per
	// account. If empty, PlaceOrder and PreviewOrder generate one and write it
	// back here before sending, so the caller holds it even if no response
	// arrives.
	//
	// An ID is consumed once Webull accepts the order, so an Order value
	// describes one placement: placing it a second time returns
	// ErrDuplicateOrder. A rejected placement does not consume the ID, so a
	// corrected Order may be resent as is.
	ClientOrderID string `json:"client_order_id"`

	// InstrumentType defaults to InstrumentEquity, or to InstrumentOption when
	// Legs is set.
	InstrumentType InstrumentType `json:"instrument_type,omitzero"`
	// ComboType defaults to Normal.
	ComboType ComboType `json:"combo_type,omitzero"`
	// EntrustType defaults to ByQuantity.
	EntrustType EntrustType `json:"entrust_type,omitzero"`
	// Market defaults to "US".
	Market string `json:"market,omitzero"`
	// TradingSession applies to US equities and defaults to SessionCore; Webull
	// rejects equity orders without one even though its documentation marks
	// the field optional.
	TradingSession TradingSession `json:"support_trading_session,omitzero"`

	Symbol      string      `json:"symbol"`
	Side        Side        `json:"side"`
	Type        OrderType   `json:"order_type"`
	TimeInForce TimeInForce `json:"time_in_force,omitzero"`
	// ExpireDate is yyyy-MM-dd and is required when TimeInForce is GTD.
	ExpireDate string `json:"expire_date,omitzero"`

	Quantity NullDecimal `json:"quantity,omitzero"`
	// TotalCashAmount places a fractional equity order by dollar value; set
	// EntrustType to ByAmount.
	TotalCashAmount NullDecimal `json:"total_cash_amount,omitzero"`
	LimitPrice      NullDecimal `json:"limit_price,omitzero"`
	StopPrice       NullDecimal `json:"stop_price,omitzero"`

	TrailingType     TrailingType `json:"trailing_type,omitzero"`
	TrailingStopStep NullDecimal  `json:"trailing_stop_step,omitzero"`

	// CurrentAsk and CurrentBid are accepted by preview only, for stock and
	// option orders.
	CurrentAsk NullDecimal `json:"current_ask,omitzero"`
	CurrentBid NullDecimal `json:"current_bid,omitzero"`

	AlgoType         AlgoType    `json:"algo_type,omitzero"`
	TargetVolPercent NullDecimal `json:"target_vol_percent,omitzero"`
	MaxTargetPercent NullDecimal `json:"max_target_percent,omitzero"`
	// AlgoStartTime and AlgoEndTime are HH:mm:ss in US Eastern time.
	AlgoStartTime string `json:"algo_start_time,omitzero"`
	AlgoEndTime   string `json:"algo_end_time,omitzero"`

	EventOutcome   EventOutcome   `json:"event_outcome,omitzero"`
	EventTradeMode EventTradeMode `json:"event_trade_mode,omitzero"`

	// Option fields. OptionStrategy defaults to StrategySingle when there is
	// exactly one leg.
	OptionStrategy OptionStrategy `json:"option_strategy,omitzero"`
	PositionIntent PositionIntent `json:"position_intent,omitzero"`
	LegDirection   LegDirection   `json:"leg_in_or_out,omitzero"`
	PositionID     string         `json:"position_id,omitzero"`
	LegInStrategy  LegInStrategy  `json:"leg_in_strategy,omitzero"`
	Legs           []OrderLeg     `json:"legs,omitempty"`
}

Order describes an order to preview or place.

Only Symbol, Side, Type, Quantity and (for limit and stop-limit orders) LimitPrice are needed for a plain equity order; the SDK supplies Webull's required boilerplate. Optional decimal fields are NullDecimal and are omitted from the request when unset, so a zero price remains expressible.

Set InstrumentType for futures, crypto and event contracts. Each asset class permits different sides, order types, times in force and quantity precision; those rules are checked before any request is sent, and an event contract order additionally needs EventOutcome.

type OrderGroup

type OrderGroup struct {
	ClientOrderID string      `json:"client_order_id"`
	ComboType     ComboType   `json:"combo_type"`
	ComboOrderID  string      `json:"combo_order_id"`
	Orders        []OrderInfo `json:"orders"`
}

OrderGroup is how Webull reports orders: a combo envelope containing one order for a Normal order and several for OCO, OTO and OTOCO groups.

type OrderInfo

type OrderInfo struct {
	ClientOrderID  string         `json:"client_order_id"`
	OrderID        string         `json:"order_id"`
	Symbol         string         `json:"symbol"`
	Side           Side           `json:"side"`
	Status         OrderStatus    `json:"status"`
	Type           OrderType      `json:"order_type"`
	InstrumentType InstrumentType `json:"instrument_type"`
	TradingSession TradingSession `json:"support_trading_session"`
	EntrustType    EntrustType    `json:"entrust_type"`
	TimeInForce    TimeInForce    `json:"time_in_force"`
	ExpireDate     string         `json:"expire_date"`

	TotalQuantity  Decimal     `json:"total_quantity"`
	FilledQuantity NullDecimal `json:"filled_quantity"`
	// FilledPrice is the average fill price and is unset until a fill.
	FilledPrice      NullDecimal  `json:"filled_price"`
	LimitPrice       NullDecimal  `json:"limit_price"`
	StopPrice        NullDecimal  `json:"stop_price"`
	TrailingType     TrailingType `json:"trailing_type"`
	TrailingStopStep NullDecimal  `json:"trailing_stop_step"`

	// PlaceTime and FilledTime are UTC. FilledTime is zero until a fill.
	PlaceTime  time.Time `json:"place_time_at"`
	FilledTime time.Time `json:"filled_time_at,omitzero"`

	AlgoType         AlgoType    `json:"algo_type"`
	TargetVolPercent NullDecimal `json:"target_vol_percent"`
	MaxTargetPercent NullDecimal `json:"max_target_percent"`
	AlgoStartTime    string      `json:"algo_start_time"`
	AlgoEndTime      string      `json:"algo_end_time"`

	EventOutcome   EventOutcome   `json:"event_outcome"`
	EventTradeMode EventTradeMode `json:"event_trade_mode"`

	OptionStrategy OptionStrategy `json:"option_strategy"`
	PositionIntent PositionIntent `json:"position_intent"`
	LegDirection   LegDirection   `json:"leg_in_or_out"`
	PositionID     string         `json:"position_id"`
	LegInStrategy  LegInStrategy  `json:"leg_in_strategy"`
	Legs           []OrderInfoLeg `json:"legs"`

	Commission Commission `json:"commission"`
	Fees       []Fee      `json:"fees"`
}

OrderInfo is the state of one order.

type OrderInfoLeg

type OrderInfoLeg struct {
	ID       string  `json:"id"`
	Symbol   string  `json:"symbol"`
	Side     Side    `json:"side"`
	Quantity Decimal `json:"quantity"`

	OptionType          OptionType  `json:"option_type"`
	Style               OptionStyle `json:"option_category"`
	StrikePrice         NullDecimal `json:"strike_price"`
	ExpireDate          string      `json:"option_expire_date"`
	ContractMultiplier  NullDecimal `json:"option_contract_multiplier"`
	ContractDeliverable NullDecimal `json:"option_contract_deliverable"`
}

OrderInfoLeg is the state of one leg of an option order.

type OrderLeg

type OrderLeg struct {
	Side     Side        `json:"side"`
	Quantity NullDecimal `json:"quantity,omitzero"`
	// Market defaults to "US"; InstrumentType to InstrumentOption.
	Market         string         `json:"market,omitzero"`
	InstrumentType InstrumentType `json:"instrument_type,omitzero"`
	// Symbol is the option's root symbol, such as "AAPL" or "SPXW". For index
	// weeklies this differs from the underlying ("SPX"), and Webull rejects the
	// underlying; LegFromSymbol sets it correctly.
	Symbol      string      `json:"symbol"`
	StrikePrice NullDecimal `json:"strike_price,omitzero"`
	// ExpireDate is yyyy-MM-dd.
	ExpireDate string     `json:"option_expire_date,omitzero"`
	OptionType OptionType `json:"option_type,omitzero"`
}

OrderLeg is one leg of an option order. LegFromSymbol builds one from an OCC symbol. Covered and collar strategies also take a stock leg, with InstrumentType set to InstrumentEquity, no option fields, and Quantity in shares — it is never defaulted from the order's contract count.

func LegFromSymbol

func LegFromSymbol(symbol string) (OrderLeg, error)

LegFromSymbol builds a leg from an OCC option symbol such as "AAPL261218C00240000", the form returned by OptionContracts. The symbol's root becomes the leg's Symbol, which is what Webull expects. Side and Quantity are left for the caller.

Example

ExampleLegFromSymbol parses an OCC option symbol into an order leg, setting the root symbol, expiry, right and strike. This example runs.

package main

import (
	"fmt"
	"log"

	"github.com/sfreiberg/webull/trade"
)

func main() {
	leg, err := trade.LegFromSymbol("AAPL261218C00240000")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s %s %s $%s\n", leg.Symbol, leg.OptionType, leg.ExpireDate, leg.StrikePrice.Decimal)
}
Output:
AAPL CALL 2026-12-18 $240

type OrderModification

type OrderModification struct {
	ClientOrderID string      `json:"client_order_id"`
	Type          OrderType   `json:"order_type,omitzero"`
	TimeInForce   TimeInForce `json:"time_in_force,omitzero"`
	Quantity      NullDecimal `json:"quantity,omitzero"`
	LimitPrice    NullDecimal `json:"limit_price,omitzero"`
	StopPrice     NullDecimal `json:"stop_price,omitzero"`

	TrailingType     TrailingType `json:"trailing_type,omitzero"`
	TrailingStopStep NullDecimal  `json:"trailing_stop_step,omitzero"`
	TargetVolPercent NullDecimal  `json:"target_vol_percent,omitzero"`
	MaxTargetPercent NullDecimal  `json:"max_target_percent,omitzero"`
	AlgoStartTime    string       `json:"algo_start_time,omitzero"`
	AlgoEndTime      string       `json:"algo_end_time,omitzero"`

	// Legs modifies option leg quantities by leg ID, as returned by Order.
	Legs []LegModification `json:"legs,omitempty"`
}

OrderModification changes a working order, identified by ClientOrderID. Unset fields are left as they are.

type OrderPreview

type OrderPreview struct {
	EstimatedCost           Decimal `json:"estimated_cost"`
	EstimatedTransactionFee Decimal `json:"estimated_transaction_fee"`
	// Currency is returned by the API but not documented.
	Currency string `json:"currency"`
}

OrderPreview estimates the cost of an order without placing it.

type OrderReceipt

type OrderReceipt struct {
	ClientOrderID string `json:"client_order_id"`
	// OrderID is Webull's identifier. Cancel, replace and lookup are keyed by
	// ClientOrderID, not by this.
	OrderID            string `json:"order_id"`
	ClientComboOrderID string `json:"client_combo_order_id"`
	ComboOrderID       string `json:"combo_order_id"`
}

OrderReceipt identifies an order that was placed, replaced or cancelled.

type OrderStatus

type OrderStatus string

OrderStatus is the lifecycle state of an order.

const (
	StatusPending       OrderStatus = "PENDING"
	StatusSubmitted     OrderStatus = "SUBMITTED"
	StatusCancelled     OrderStatus = "CANCELLED"
	StatusFilled        OrderStatus = "FILLED"
	StatusFailed        OrderStatus = "FAILED"
	StatusPartialFilled OrderStatus = "PARTIAL_FILLED"
)

Order statuses.

type OrderType

type OrderType string

OrderType determines how an order executes.

Not every type is available to every asset class: event contracts accept Limit only, crypto accepts Market, Limit and StopLossLimit, and options do not accept TrailingStopLoss. Batch placement accepts Market and Limit. An order of a type its instrument does not permit fails locally.

const (
	Market           OrderType = "MARKET"
	Limit            OrderType = "LIMIT"
	StopLoss         OrderType = "STOP_LOSS"
	StopLossLimit    OrderType = "STOP_LOSS_LIMIT"
	TrailingStopLoss OrderType = "TRAILING_STOP_LOSS"
	MarketOnOpen     OrderType = "MARKET_ON_OPEN"
	MarketOnClose    OrderType = "MARKET_ON_CLOSE"
	LimitOnOpen      OrderType = "LIMIT_ON_OPEN"
)

Order types.

type OrdersRequest

type OrdersRequest struct {
	AccountID string
	// StartDate and EndDate are yyyy-MM-dd and apply to OrderHistory only.
	StartDate string
	EndDate   string
	// PageSize is between 10 and 100. Zero lets Webull apply its default.
	PageSize int
	// LastClientOrderID is the ClientOrderID of the final group from the
	// previous page. Leave empty for the first page.
	LastClientOrderID string
}

OrdersRequest pages through open or historical orders.

type Position

type Position struct {
	PositionID           string         `json:"position_id"`
	Currency             string         `json:"currency"`
	Symbol               string         `json:"symbol"`
	InstrumentType       InstrumentType `json:"instrument_type"`
	OptionStrategy       OptionStrategy `json:"option_strategy"`
	Quantity             Decimal        `json:"quantity"`
	LastPrice            Decimal        `json:"last_price"`
	CostPrice            Decimal        `json:"cost_price"`
	UnrealizedProfitLoss Decimal        `json:"unrealized_profit_loss"`

	// EventOutcome is set only for event contract positions.
	EventOutcome EventOutcome `json:"event_outcome"`

	// Legs is populated for option positions.
	Legs []PositionLeg `json:"legs"`
}

Position is an open holding in an account.

type PositionIntent

type PositionIntent string

PositionIntent is the effect of an option order on the position.

const (
	BuyToOpen   PositionIntent = "BUY_TO_OPEN"
	BuyToClose  PositionIntent = "BUY_TO_CLOSE"
	SellToOpen  PositionIntent = "SELL_TO_OPEN"
	SellToClose PositionIntent = "SELL_TO_CLOSE"
)

Position intents.

type PositionLeg

type PositionLeg struct {
	LegID    string      `json:"leg_id"`
	Symbol   string      `json:"symbol"`
	Quantity NullDecimal `json:"quantity"`

	OptionType OptionType `json:"option_type"`
	// ExpireDate is in yyyy-MM-dd form.
	ExpireDate          string      `json:"option_expire_date"`
	ExercisePrice       NullDecimal `json:"option_exercise_price"`
	ContractMultiplier  NullDecimal `json:"option_contract_multiplier"`
	ContractDeliverable NullDecimal `json:"option_contract_deliverable"`
	ExpirationType      string      `json:"expiration_type"`
}

PositionLeg is one leg of an option position.

type PriceRange

type PriceRange struct {
	Start Decimal `json:"start"`
	End   Decimal `json:"end"`
	Step  Decimal `json:"step"`
}

PriceRange is a band of permitted prices and the tick within it.

type Settlement

type Settlement string

Settlement is how a contract settles at expiry.

const (
	SettlementCash     Settlement = "CASH"
	SettlementPhysical Settlement = "PHYSICAL"
)

Settlement methods. Webull's documentation shows these in title case; the API returns upper case, which is what these constants hold.

type Side

type Side string

Side is the direction of an order.

const (
	Buy   Side = "BUY"
	Sell  Side = "SELL"
	Short Side = "SHORT"
)

Order sides. Short is a short sale of equities.

type StockProfile

type StockProfile struct {
	Name         string           `json:"name"`
	InstrumentID string           `json:"instrument_id"`
	ExchangeCode string           `json:"exchange_code"`
	Category     Category         `json:"category"`
	Symbol       string           `json:"symbol"`
	Status       TradableStatus   `json:"status"`
	SubCategory  StockSubCategory `json:"sub_category"`
	Currency     string           `json:"currency"`

	Shortable                 bool `json:"shortable"`
	Fractionable              bool `json:"fractionable"`
	Marginable                bool `json:"marginable"`
	OvernightTradingSupported bool `json:"overnight_trading_supported"`
	EasyToBorrow              bool `json:"easy_to_borrow"`
	CryptoETF                 bool `json:"crypto_etf"`
	// SingleStockETF and InverseETF are returned by the API but not documented.
	SingleStockETF bool `json:"single_stock_etf"`
	InverseETF     bool `json:"inverse_etf"`

	MarginRequirementLong  NullDecimal `json:"margin_requirement_long"`
	MarginRequirementShort NullDecimal `json:"margin_requirement_short"`
	IntradayMarginLong     NullDecimal `json:"intraday_margin_long"`
	IntradayMarginShort    NullDecimal `json:"intraday_margin_short"`
	MaintenanceMarginLong  NullDecimal `json:"maintenance_margin_long"`
	MaintenanceMarginShort NullDecimal `json:"maintenance_margin_short"`
	LotSize                NullDecimal `json:"lot_size"`

	// ETFLeveragedFlag is "YES" or "NO"; ETFLeveragedFactor is the multiple.
	ETFLeveragedFlag   string      `json:"etf_leveraged_flag"`
	ETFLeveragedFactor NullDecimal `json:"etf_leveraged_factor"`
}

StockProfile is reference data for an equity instrument.

type StockProfilesPage

type StockProfilesPage struct {
	Profiles []StockProfile `json:"data"`
	// PaginationKey fetches the next page, and is empty on the last one.
	PaginationKey string `json:"pagination_key"`
}

StockProfilesPage is one page of stock profiles.

type StockProfilesRequest

type StockProfilesRequest struct {
	// Symbols holds up to 100 symbols.
	Symbols     []string
	Status      TradableStatus
	SubCategory StockSubCategory
	// PaginationKey continues a previous page. Leave empty for the first.
	PaginationKey string
}

StockProfilesRequest filters an equity lookup. Either Symbols or a SubCategory browse is expected; SubCategory is ignored when Symbols is set.

type StockSubCategory

type StockSubCategory string

StockSubCategory refines an equity instrument.

const (
	CommonStock    StockSubCategory = "COMMON_STOCK"
	ETF            StockSubCategory = "ETF"
	PreferredStock StockSubCategory = "PREFERRED_STOCK"
	Warrant        StockSubCategory = "WARRANT"
	Units          StockSubCategory = "UNITS"
	Right          StockSubCategory = "RIGHT"
)

Stock sub-categories.

type TimeInForce

type TimeInForce string

TimeInForce is how long an order remains active.

Equities accept every value; options and futures accept Day and GTC; crypto accepts Day, GTC and IOC. Webull enforces regular trading hours for Day orders, rejecting one placed after the close, so anything that must run at any hour should use GTC.

const (
	Day TimeInForce = "DAY"
	GTC TimeInForce = "GTC"
	IOC TimeInForce = "IOC"
	GTD TimeInForce = "GTD"
	FOK TimeInForce = "FOK"
)

Time-in-force values. GTD requires Order.ExpireDate.

type TradableStatus

type TradableStatus string

TradableStatus reports whether an instrument can currently be traded.

const (
	// Tradable means the instrument may be bought and sold.
	Tradable TradableStatus = "OC"
	// LiquidateOnly means positions may be closed but not opened.
	LiquidateOnly TradableStatus = "CO"
	// NotTradable means no orders are accepted.
	NotTradable TradableStatus = "NT"
)

Tradable statuses.

type TradingSession

type TradingSession string

TradingSession restricts when a US equity order may execute.

const (
	SessionAll   TradingSession = "ALL"
	SessionCore  TradingSession = "CORE"
	SessionNight TradingSession = "NIGHT"
)

Trading sessions.

type TrailingType

type TrailingType string

TrailingType is how a trailing stop's offset is expressed. Both kinds are accepted for equities and futures, on either side and with GTC as well as Day. Options do not accept trailing stops at all. The wire values are case-sensitive.

const (
	TrailByAmount     TrailingType = "AMOUNT"
	TrailByPercentage TrailingType = "PERCENTAGE"
)

Trailing types.

Jump to

Keyboard shortcuts

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