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 ¶
- Variables
- type Account
- type AccountClass
- type AccountType
- type ActivitySubType
- type ActivityType
- type AlgoType
- type Balance
- type BatchOrderResult
- type BatchResult
- type CashActivitiesRequest
- type CashActivity
- type Category
- type Client
- func (c *Client) Accounts(ctx context.Context) ([]Account, error)
- func (c *Client) Balance(ctx context.Context, accountID string) (*Balance, error)
- func (c *Client) CancelCombo(ctx context.Context, accountID string, combo *Combo) error
- func (c *Client) CancelOrder(ctx context.Context, accountID, clientOrderID string) (*OrderReceipt, error)
- func (c *Client) CashActivities(ctx context.Context, req CashActivitiesRequest) ([]CashActivity, error)
- func (c *Client) CryptoProfiles(ctx context.Context, req CryptoProfilesRequest) (*CryptoProfilesPage, error)
- func (c *Client) EventCategories(ctx context.Context) ([]EventCategory, error)
- func (c *Client) EventMarkets(ctx context.Context, req EventMarketsRequest) (*EventMarketsPage, error)
- func (c *Client) EventSeries(ctx context.Context, req EventSeriesRequest) (*EventSeriesPage, error)
- func (c *Client) Events(ctx context.Context, req EventsRequest) ([]Event, error)
- func (c *Client) FuturesContracts(ctx context.Context, req FuturesContractsRequest) ([]FuturesContract, error)
- func (c *Client) FuturesProductClasses(ctx context.Context) ([]FuturesProductClass, error)
- func (c *Client) FuturesProducts(ctx context.Context, productClassID int) ([]FuturesProduct, error)
- func (c *Client) OpenOrders(ctx context.Context, req OrdersRequest) ([]OrderGroup, error)
- func (c *Client) OptionContracts(ctx context.Context, req OptionContractsRequest) (*OptionContractsPage, error)
- func (c *Client) Order(ctx context.Context, accountID, clientOrderID string) (*OrderGroup, error)
- func (c *Client) OrderHistory(ctx context.Context, req OrdersRequest) ([]OrderGroup, error)
- func (c *Client) PlaceCombo(ctx context.Context, accountID string, combo *Combo) (*OrderReceipt, error)
- func (c *Client) PlaceOrder(ctx context.Context, accountID string, order *Order) (*OrderReceipt, error)
- func (c *Client) PlaceOrders(ctx context.Context, accountID string, orders []*Order) (*BatchResult, error)
- func (c *Client) Positions(ctx context.Context, accountID string) ([]Position, error)
- func (c *Client) PreviewCombo(ctx context.Context, accountID string, combo *Combo) (*OrderPreview, error)
- func (c *Client) PreviewOrder(ctx context.Context, accountID string, order *Order) (*OrderPreview, error)
- func (c *Client) ReplaceOrder(ctx context.Context, accountID string, mod OrderModification) (*OrderReceipt, error)
- func (c *Client) StockProfiles(ctx context.Context, req StockProfilesRequest) (*StockProfilesPage, error)
- type Combo
- type ComboType
- type Commission
- type CryptoProfile
- type CryptoProfilesPage
- type CryptoProfilesRequest
- type CurrencyAssets
- type Decimal
- type EntrustType
- type Event
- type EventCategory
- type EventCategoryCode
- type EventFrequency
- type EventMarket
- type EventMarketStatus
- type EventMarketsPage
- type EventMarketsRequest
- type EventOutcome
- type EventSeries
- type EventSeriesPage
- type EventSeriesRequest
- type EventStatus
- type EventTradeMode
- type EventsRequest
- type Fee
- type FuturesContract
- type FuturesContractType
- type FuturesContractsRequest
- type FuturesProduct
- type FuturesProductClass
- type FuturesUnit
- type InstrumentType
- type LegDirection
- type LegInStrategy
- type LegModification
- type ListingStatus
- type MarginCall
- type NullDecimal
- type OptionContract
- type OptionContractsPage
- type OptionContractsRequest
- type OptionDeliverable
- type OptionStrategy
- type OptionStyle
- type OptionType
- type Order
- type OrderGroup
- type OrderInfo
- type OrderInfoLeg
- type OrderLeg
- type OrderModification
- type OrderPreview
- type OrderReceipt
- type OrderStatus
- type OrderType
- type OrdersRequest
- type Position
- type PositionIntent
- type PositionLeg
- type PriceRange
- type Settlement
- type Side
- type StockProfile
- type StockProfilesPage
- type StockProfilesRequest
- type StockSubCategory
- type TimeInForce
- type TradableStatus
- type TradingSession
- type TrailingType
Examples ¶
Constants ¶
This section is empty.
Variables ¶
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.
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 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.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client calls the Trading API. It is safe for concurrent use.
func New ¶
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) CancelCombo ¶
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)
}
Output:
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 ¶
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 ¶
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) 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)
}
}
Output:
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)
}
Output:
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)
}
Output:
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)
}
Output:
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 ¶
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)
}
Output:
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 ¶
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 ¶
OCO is a one-cancels-other group of two to six orders: when one fills, the rest are cancelled.
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 ¶
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 ¶
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 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.