diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dc2027..f44ce56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## [Unreleased] + +### Added + +- **Attached order (take-profit / stop-loss) support** for `SubmitOrder` and `ReplaceOrder`: + - New types: `AttachedOrderType` (`AttachedOrderTypeProfitTaker` / `AttachedOrderTypeStopLoss` / `AttachedOrderTypeBracket`), `AttachedOrderDetail`, `SubmitAttachedParams`, `ReplaceAttachedParams` + - `SubmitOrder` / `ReplaceOrder`: new `AttachedParams` field + - `Order` / `OrderDetail`: new `AttachedOrders []AttachedOrderDetail` field + - New `TradeContext.OrderDetailAttached(orderId)` and `TradeContext.CancelOrderAttached(orderId)` methods — query/cancel an attached sub-order by its own order ID + - `GetTodayOrders`: new `OrderId` and `IsAttached` fields — when combined, treats `OrderId` as an attached sub-order ID and returns that sub-order as an `Order` entry (not the parent order) + +### Breaking changes + +- `OrderDetail.ChargeDetail` is now `*OrderChargeDetail` (previously non-pointer). Attached orders return `nil` for this field; callers must handle the absent case. + ## [v0.26.0] - 2026-07-20 ### Added diff --git a/examples/submit_order_with_attached/main.go b/examples/submit_order_with_attached/main.go new file mode 100644 index 0000000..9e09772 --- /dev/null +++ b/examples/submit_order_with_attached/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + "os/signal" + "syscall" + + "github.com/longbridge/openapi-go/config" + "github.com/longbridge/openapi-go/oauth" + "github.com/longbridge/openapi-go/trade" + "github.com/shopspring/decimal" +) + +func main() { + o := oauth.New("your-client-id"). + OnOpenURL(func(url string) { fmt.Println("Open this URL to authorize:", url) }) + if err := o.Build(context.Background()); err != nil { + log.Fatal(err) + } + conf, err := config.New(config.WithOAuthClient(o)) + if err != nil { + log.Fatal(err) + } + tradeContext, err := trade.NewFromCfg(conf) + if err != nil { + log.Fatal(err) + return + } + defer tradeContext.Close() + ctx := context.Background() + + tradeContext.OnTrade(func(ev *trade.PushEvent) { + log.Printf("order event: %+v\n", ev) + }) + + // Submit a bracket order: buy 700.HK at 12.00, with a take-profit at 13.00 + // and a stop-loss at 11.00 attached to it. + order := &trade.SubmitOrder{ + Symbol: "700.HK", + OrderType: trade.OrderTypeLO, + Side: trade.OrderSideBuy, + SubmittedQuantity: 200, + TimeInForce: trade.TimeTypeDay, + SubmittedPrice: decimal.NewFromFloat(12), + AttachedParams: &trade.SubmitAttachedParams{ + AttachedOrderType: trade.AttachedOrderTypeBracket, + ProfitTakerPrice: decimal.NewFromFloat(13), + StopLossPrice: decimal.NewFromFloat(11), + }, + } + orderId, err := tradeContext.SubmitOrder(ctx, order) + if err != nil { + log.Fatal(err) + return + } + fmt.Printf("orderId: %v\n", orderId) + + detail, err := tradeContext.OrderDetail(ctx, orderId) + if err != nil { + log.Fatal(err) + return + } + for _, attached := range detail.AttachedOrders { + fmt.Printf("attached order: %+v\n", attached) + + // The attached sub-order has its own order ID; query/cancel it directly + // via the *Attached variants instead of the parent order's ID. + attachedDetail, err := tradeContext.OrderDetailAttached(ctx, attached.OrderId) + if err != nil { + log.Fatal(err) + return + } + fmt.Printf("attached order detail: %+v\n", attachedDetail) + } + + quitChannel := make(chan os.Signal, 1) + signal.Notify(quitChannel, syscall.SIGINT, syscall.SIGTERM) + <-quitChannel +} diff --git a/trade/context.go b/trade/context.go index 15b5016..4954fb2 100644 --- a/trade/context.go +++ b/trade/context.go @@ -232,6 +232,22 @@ func (c *TradeContext) CancelOrder(ctx context.Context, orderId string) (err err return } +// CancelOrderAttached cancels an attached (take-profit / stop-loss) sub-order by its own order ID, +// instead of the parent order's ID. +// Reference: https://open.longbridge.com/en/docs/trade/order/withdraw +// Example: +// +// conf, err := config.NewFromEnv() +// tctx, err := trade.NewFromCfg(conf) +// err = tctx.CancelOrderAttached(context.Background(), "706388312699592705") +func (c *TradeContext) CancelOrderAttached(ctx context.Context, orderId string) (err error) { + values := url.Values{} + values.Add("order_id", orderId) + values.Add("is_attached", "true") + err = c.opts.httpClient.Delete(ctx, "/v1/trade/order", values, nil) + return +} + // AccountBalance to obtain the available, desirable, frozen, to-be-settled, and in-transit funds (fund purchase and redemption) information for each currency of the user. // Reference: https://open.longbridge.com/en/docs/trade/asset/account // Example: @@ -350,6 +366,27 @@ func (c *TradeContext) OrderDetail(ctx context.Context, orderId string) (orderDe return } +// OrderDetailAttached queries detail for an attached (take-profit / stop-loss) sub-order by its +// own order ID, instead of the parent order's ID. +// Reference: https://open.longbridge.com/en/docs/trade/order/order_detail +// Example: +// +// conf, err := config.NewFromEnv() +// tctx, err := trade.NewFromCfg(conf) +// od, err := tctx.OrderDetailAttached(context.Background(), "706388312699592705") +func (c *TradeContext) OrderDetailAttached(ctx context.Context, orderId string) (orderDetail OrderDetail, err error) { + values := url.Values{} + values.Add("order_id", orderId) + values.Add("is_attached", "true") + var resp jsontypes.OrderDetail + err = c.opts.httpClient.Get(ctx, "/v1/trade/order", values, &resp) + if err != nil { + return + } + err = util.Copy(&orderDetail, resp) + return +} + // EstimateMaxPurchaseQuantity is used for estimating the maximum purchase quantity for Hong Kong and US stocks, warrants, and options. // Reference: https://open.longbridge.com/en/docs/trade/order/estimate_available_buy_limit // Example: diff --git a/trade/jsontypes/types.go b/trade/jsontypes/types.go index 691e5b8..e8ba19b 100644 --- a/trade/jsontypes/types.go +++ b/trade/jsontypes/types.go @@ -33,32 +33,58 @@ type Orders struct { // Order is order details type Order struct { - OrderId string `json:"order_id"` - Status string `json:"status"` - StockName string `json:"stock_name"` - Quantity string `json:"quantity"` - ExecutedQuantity string `json:"executed_quantity"` - Price string `json:"price"` - ExecutedPrice string `json:"executed_price"` - SubmittedAt string `json:"submmited_at"` - Side string `json:"side"` - Symbol string `json:"symbol"` - OrderType string `json:"order_type"` - LastDone string `json:"last_done"` - TriggerPrice string `json:"trigger_price"` - Msg string `json:"msg"` - Tag string `json:"tag"` - TimeInForce string `json:"time_in_force"` - ExpireDate string `json:"expire_date"` - UpdatedAt string `json:"updated_at"` - TriggerAt string `json:"trigger_at"` - TrailingAmount string `json:"trailing_amount"` - TrailingPercent string `json:"trailing_percent"` - LimitOffset string `json:"limit_offset"` - TriggerStatus string `json:"trigger_status"` - Currency string `json:"currency"` - OutsideRth string `json:"outside_rth"` - Remark string `json:"remark"` + OrderId string `json:"order_id"` + Status string `json:"status"` + StockName string `json:"stock_name"` + Quantity string `json:"quantity"` + ExecutedQuantity string `json:"executed_quantity"` + Price string `json:"price"` + ExecutedPrice string `json:"executed_price"` + SubmittedAt string `json:"submmited_at"` + Side string `json:"side"` + Symbol string `json:"symbol"` + OrderType string `json:"order_type"` + LastDone string `json:"last_done"` + TriggerPrice string `json:"trigger_price"` + Msg string `json:"msg"` + Tag string `json:"tag"` + TimeInForce string `json:"time_in_force"` + ExpireDate string `json:"expire_date"` + UpdatedAt string `json:"updated_at"` + TriggerAt string `json:"trigger_at"` + TrailingAmount string `json:"trailing_amount"` + TrailingPercent string `json:"trailing_percent"` + LimitOffset string `json:"limit_offset"` + TriggerStatus string `json:"trigger_status"` + Currency string `json:"currency"` + OutsideRth string `json:"outside_rth"` + Remark string `json:"remark"` + AttachedOrders []AttachedOrderDetail `json:"attached_orders"` +} + +// AttachedOrderDetail is the raw wire type for an attached (take-profit / stop-loss) sub-order. +type AttachedOrderDetail struct { + OrderId string `json:"order_id"` + AttachedTypeDisplay string `json:"attached_type_display"` + TriggerPrice string `json:"trigger_price"` + Quantity string `json:"quantity"` + ExecutedQty string `json:"executed_qty"` + Status string `json:"status"` + UpdatedAt string `json:"updated_at"` + Withdrawn bool `json:"withdrawn"` + Gtd string `json:"gtd"` + TimeInForce string `json:"time_in_force"` + CounterId string `json:"counter_id"` + TriggerStatus string `json:"trigger_status"` + ExecutedAmount string `json:"executed_amount"` + Tag string `json:"tag"` + SubmittedAt string `json:"submitted_at"` + ExecutedPrice string `json:"executed_price"` + ForceOnlyRth string `json:"force_only_rth"` + Reviewed bool `json:"reviewed"` + ActivateOrderType string `json:"activate_order_type"` + ActivateRth string `json:"activate_rth"` + SubmitPrice string `json:"submit_price"` } // AccountBalances has a AccountBalance list @@ -187,30 +213,64 @@ type PushOrderChanged struct { } type ReplaceOrder struct { - OrderId string `json:"order_id"` - Quantity uint64 `json:"quantity,string"` - Price string `json:"price"` - TriggerPrice string `json:"trigger_price,omitempty"` - LimitOffset string `json:"limit_offset,omitempty"` - TrailingAmount string `json:"trailing_ammount,omitempty"` - TrailingPercent string `json:"trailing_percent,omitempty"` - Remark string `json:"remark"` + OrderId string `json:"order_id"` + Quantity uint64 `json:"quantity,string"` + Price string `json:"price"` + TriggerPrice string `json:"trigger_price,omitempty"` + LimitOffset string `json:"limit_offset,omitempty"` + TrailingAmount string `json:"trailing_ammount,omitempty"` + TrailingPercent string `json:"trailing_percent,omitempty"` + Remark string `json:"remark"` + AttachedParams *ReplaceAttachedParams `json:"attached_params,omitempty"` } type SubmitOrder struct { - Symbol string `json:"symbol"` - OrderType string `json:"order_type"` - Side string `json:"side"` - SubmittedQuantity uint64 `json:"submitted_quantity,string"` - SubmittedPrice string `json:"submitted_price,omitempty"` - TriggerPrice string `json:"trigger_price,omitempty"` - LimitOffset string `json:"limit_offset,omitempty"` - TrailingAmount string `json:"trailing_amount,omitempty"` - TrailingPercent string `json:"trailing_percent,omitempty"` - ExpireDate string `json:"expire_date,omitempty"` - OutsideRTH string `json:"outside_rth,omitempty"` - Remark string `json:"remark,omitempty"` - TimeInForce string `json:"time_in_force"` + Symbol string `json:"symbol"` + OrderType string `json:"order_type"` + Side string `json:"side"` + SubmittedQuantity uint64 `json:"submitted_quantity,string"` + SubmittedPrice string `json:"submitted_price,omitempty"` + TriggerPrice string `json:"trigger_price,omitempty"` + LimitOffset string `json:"limit_offset,omitempty"` + TrailingAmount string `json:"trailing_amount,omitempty"` + TrailingPercent string `json:"trailing_percent,omitempty"` + ExpireDate string `json:"expire_date,omitempty"` + OutsideRTH string `json:"outside_rth,omitempty"` + Remark string `json:"remark,omitempty"` + TimeInForce string `json:"time_in_force"` + AttachedParams *SubmitAttachedParams `json:"attached_params,omitempty"` +} + +// SubmitAttachedParams is the raw wire type for attached order (take-profit / stop-loss) parameters on submit. +type SubmitAttachedParams struct { + AttachedOrderType string `json:"attached_order_type"` + ProfitTakerPrice string `json:"profit_taker_price,omitempty"` + StopLossPrice string `json:"stop_loss_price,omitempty"` + TimeInForce string `json:"time_in_force,omitempty"` + ExpireTime int64 `json:"expire_time,omitempty"` + ActivateOrderType string `json:"activate_order_type,omitempty"` + ProfitTakerSubmitPrice string `json:"profit_taker_submit_price,omitempty"` + StopLossSubmitPrice string `json:"stop_loss_submit_price,omitempty"` + ActivateRTH string `json:"activate_rth,omitempty"` +} + +// ReplaceAttachedParams is the raw wire type for attached order (take-profit / stop-loss) parameters on replace. +type ReplaceAttachedParams struct { + AttachedOrderType string `json:"attached_order_type"` + ProfitTakerPrice string `json:"profit_taker_price,omitempty"` + StopLossPrice string `json:"stop_loss_price,omitempty"` + TimeInForce string `json:"time_in_force,omitempty"` + ExpireTime int64 `json:"expire_time,omitempty"` + ProfitTakerId int64 `json:"profit_taker_id,omitempty"` + StopLossId int64 `json:"stop_loss_id,omitempty"` + CancelAllAttached bool `json:"cancel_all_attached,omitempty"` + MainId int64 `json:"main_id,omitempty"` + Quantity string `json:"quantity,omitempty"` + MarketPrice string `json:"market_price,omitempty"` + ActivateOrderType string `json:"activate_order_type,omitempty"` + ProfitTakerSubmitPrice string `json:"profit_taker_submit_price,omitempty"` + StopLossSubmitPrice string `json:"stop_loss_submit_price,omitempty"` + ActivateRTH string `json:"activate_rth,omitempty"` } type MarginRatio struct { @@ -252,43 +312,44 @@ type OrderHistoryDetail struct { // OrderDetail is for get order detail response type OrderDetail struct { - OrderId string `json:"order_id"` - Status string `json:"status"` - StockName string `json:"stock_name"` - Quantity string `json:"quantity"` // Submitted quantity - ExecutedQuantity string `json:"executed_quantity"` - Price string `json:"price"` // Submitted price - ExecutedPrice string `json:"executed_price"` - SubmittedAt string `json:"submitted_at"` - Side string `json:"side"` // Order side - Symbol string `json:"symbol"` - OrderType string `json:"order_type"` - LastDone string `json:"last_done"` - TriggerPrice string `json:"trigger_price"` - Msg string `json:"msg"` // Rejected Message or remark - Tag string `json:"tag"` - TimeInForce string `json:"time_in_force"` - ExpireDate string `json:"expire_date"` - UpdatedAt string `json:"updated_at"` - TriggerAt string `json:"trigger_at"` // Conditional order trigger time - TrailingAmount string `json:"trailing_amount"` - TrailingPercent string `json:"trailing_precent"` - LimitOffset string `json:"limit_offset"` - TriggerStatus string `json:"trigger_status"` - Currency string `json:"currency"` - OutsideRth string `json:"outside_rth"` // Enable or disable outside regular trading hours - Remark string `json:"remark"` - FreeStatus string `json:"free_status"` - FreeAmount string `json:"free_amount"` - FreeCurrency string `json:"free_currency"` - DeductionsStatus string `json:"deductions_status"` - DeductionsAmount string `json:"deductions_amount"` - DeductionsCurrency string `json:"deductions_currency"` - PlatformDeductedStatus string `json:"platform_deducted_status"` - PlatformDeductedAmount string `json:"platform_deducted_amount"` - PlatformDeductedCurrency string `json:"platform_deducted_currency"` - History []OrderHistoryDetail `json:"history"` - ChargeDetail OrderChargeDetail `json:"charge_detail"` + OrderId string `json:"order_id"` + Status string `json:"status"` + StockName string `json:"stock_name"` + Quantity string `json:"quantity"` // Submitted quantity + ExecutedQuantity string `json:"executed_quantity"` + Price string `json:"price"` // Submitted price + ExecutedPrice string `json:"executed_price"` + SubmittedAt string `json:"submitted_at"` + Side string `json:"side"` // Order side + Symbol string `json:"symbol"` + OrderType string `json:"order_type"` + LastDone string `json:"last_done"` + TriggerPrice string `json:"trigger_price"` + Msg string `json:"msg"` // Rejected Message or remark + Tag string `json:"tag"` + TimeInForce string `json:"time_in_force"` + ExpireDate string `json:"expire_date"` + UpdatedAt string `json:"updated_at"` + TriggerAt string `json:"trigger_at"` // Conditional order trigger time + TrailingAmount string `json:"trailing_amount"` + TrailingPercent string `json:"trailing_precent"` + LimitOffset string `json:"limit_offset"` + TriggerStatus string `json:"trigger_status"` + Currency string `json:"currency"` + OutsideRth string `json:"outside_rth"` // Enable or disable outside regular trading hours + Remark string `json:"remark"` + FreeStatus string `json:"free_status"` + FreeAmount string `json:"free_amount"` + FreeCurrency string `json:"free_currency"` + DeductionsStatus string `json:"deductions_status"` + DeductionsAmount string `json:"deductions_amount"` + DeductionsCurrency string `json:"deductions_currency"` + PlatformDeductedStatus string `json:"platform_deducted_status"` + PlatformDeductedAmount string `json:"platform_deducted_amount"` + PlatformDeductedCurrency string `json:"platform_deducted_currency"` + History []OrderHistoryDetail `json:"history"` + ChargeDetail *OrderChargeDetail `json:"charge_detail"` + AttachedOrders []AttachedOrderDetail `json:"attached_orders"` } // EstimateMaxPurchaseQuantity is response for estimate maximum purchase quantity diff --git a/trade/params.go b/trade/params.go index be63b4d..53aed06 100644 --- a/trade/params.go +++ b/trade/params.go @@ -61,6 +61,12 @@ func (p params) AddOptDecimal(key string, val decimal.Decimal) { } } +func (p params) AddOptBool(key string, val bool) { + if val { + p[key] = "true" + } +} + func (p params) Values() url.Values { vals := url.Values{} for k, v := range p { diff --git a/trade/request_values.go b/trade/request_values.go index 76a1a46..1aef885 100644 --- a/trade/request_values.go +++ b/trade/request_values.go @@ -107,6 +107,8 @@ func (r *GetTodayOrders) Values() url.Values { p.Add("symbol", string(r.Symbol)) p.Add("side", string(r.Side)) p.Add("market", string(r.Market)) + p.Add("order_id", r.OrderId) + p.AddOptBool("is_attached", r.IsAttached) vals := p.Values() for _, s := range r.Status { vals.Add("status", string(s)) diff --git a/trade/requests.go b/trade/requests.go index b9b5f33..b0f2113 100644 --- a/trade/requests.go +++ b/trade/requests.go @@ -36,10 +36,15 @@ type GetHistoryOrders struct { } type GetTodayOrders struct { - Symbol string // optional - Status []OrderStatus // optional - Side OrderSide // optional - Market openapi.Market // optional + Symbol string // optional + Status []OrderStatus // optional + Side OrderSide // optional + Market openapi.Market // optional + OrderId string // optional + // IsAttached, combined with OrderId, treats OrderId as an attached sub-order + // ID and returns that sub-order as an Order entry (not the parent order). + // Has no effect without OrderId. + IsAttached bool // optional } type GetFundPositions struct { @@ -68,6 +73,7 @@ type ReplaceOrder struct { TrailingAmount decimal.Decimal // TSLPAMT / TSMAMT Order Required TrailingPercent decimal.Decimal // TSLPPCT / TSMAPCT Order Required Remark string + AttachedParams *ReplaceAttachedParams // optional, take-profit / stop-loss parameters } type SubmitOrder struct { @@ -83,7 +89,44 @@ type SubmitOrder struct { ExpireDate *time.Time // required when time_in_force is GTD OutsideRTH OutsideRTH Remark string - TimeInForce TimeType // required + TimeInForce TimeType // required + AttachedParams *SubmitAttachedParams // optional, take-profit / stop-loss parameters +} + +// SubmitAttachedParams is attached order (take-profit / stop-loss) parameters for SubmitOrder. +type SubmitAttachedParams struct { + AttachedOrderType AttachedOrderType // required + ProfitTakerPrice decimal.Decimal // optional, take-profit trigger price + StopLossPrice decimal.Decimal // optional, stop-loss trigger price + TimeInForce TimeType // optional + ExpireTime int64 // optional, unix timestamp seconds, used when TimeInForce is GTD + ActivateOrderType OrderType // optional, order type to submit after trigger + // ProfitTakerSubmitPrice is required when ActivateOrderType is LIT. + ProfitTakerSubmitPrice decimal.Decimal + // StopLossSubmitPrice is required when ActivateOrderType is LIT. + StopLossSubmitPrice decimal.Decimal + ActivateRTH OutsideRTH // optional, RTH setting for the activated order +} + +// ReplaceAttachedParams is attached order (take-profit / stop-loss) parameters for ReplaceOrder. +type ReplaceAttachedParams struct { + AttachedOrderType AttachedOrderType // required + ProfitTakerPrice decimal.Decimal // optional, take-profit trigger price + StopLossPrice decimal.Decimal // optional, stop-loss trigger price + TimeInForce TimeType // optional + ExpireTime int64 // optional, unix timestamp seconds, used when TimeInForce is GTD + ProfitTakerId int64 // optional, existing take-profit order ID to modify + StopLossId int64 // optional, existing stop-loss order ID to modify + CancelAllAttached bool // optional, cancel all attached orders + MainId int64 // optional, main order ID + Quantity decimal.Decimal // optional, attached order quantity + MarketPrice decimal.Decimal // optional + ActivateOrderType OrderType // optional, order type to submit after trigger + // ProfitTakerSubmitPrice is required when ActivateOrderType is LIT. + ProfitTakerSubmitPrice decimal.Decimal + // StopLossSubmitPrice is required when ActivateOrderType is LIT. + StopLossSubmitPrice decimal.Decimal + ActivateRTH OutsideRTH // optional, RTH setting for the activated order } type GetEstimateMaxPurchaseQuantity struct { diff --git a/trade/types.go b/trade/types.go index 6cbc265..7cae523 100644 --- a/trade/types.go +++ b/trade/types.go @@ -24,6 +24,7 @@ type ( DeductionStatus string ChargeCategoryCode string Currency string + AttachedOrderType string // Attached order type (take-profit / stop-loss / bracket) ) const ( @@ -90,8 +91,8 @@ const ( // Order tag OrderTagNormal OrderTag = "Normal" // Normal Order - OrderTagLongTerm OrderTag = "Gtc" // Long term Order - OrderTagGrey OrderTag = "Grey" // Grey Order + OrderTagLongTerm OrderTag = "Gtc" // Long term Order + OrderTagGrey OrderTag = "Grey" // Grey Order // Trigger status TriggerStatusDeactive TriggerStatus = "DEACTIVE" @@ -124,6 +125,11 @@ const ( CurrencyUSD Currency = "USD" CurrencyCNH Currency = "CNH" CurrencyDefault Currency = "" + + // Attached order type + AttachedOrderTypeProfitTaker AttachedOrderType = "PROFIT_TAKER" // Take profit + AttachedOrderTypeStopLoss AttachedOrderType = "STOP_LOSS" // Stop loss + AttachedOrderTypeBracket AttachedOrderType = "BRACKET" // Bracket order (profit-taker + stop-loss) ) // Execution is execution details @@ -181,6 +187,32 @@ type Order struct { Currency string OutsideRth OutsideRTH Remark string + AttachedOrders []AttachedOrderDetail +} + +// AttachedOrderDetail is an attached (take-profit / stop-loss) sub-order of a bracket order. +type AttachedOrderDetail struct { + OrderId string + AttachedTypeDisplay AttachedOrderType // Actual type of this sub-order: ProfitTaker or StopLoss + TriggerPrice *decimal.Decimal + Quantity decimal.Decimal + ExecutedQty decimal.Decimal + Status OrderStatus + UpdatedAt string // raw unix-second string + Withdrawn bool + Gtd string // GTD expiry date, format YYYY-MM-DD + TimeInForce TimeType + CounterId string + TriggerStatus TriggerStatus + ExecutedAmount decimal.Decimal + Tag OrderTag + SubmittedAt string // raw unix-second string + ExecutedPrice *decimal.Decimal + ForceOnlyRTH OutsideRTH + Reviewed bool + ActivateOrderType OrderType // Order type to submit after trigger + ActivateRTH OutsideRTH + SubmitPrice *decimal.Decimal // Submit (limit) price for the activated order } type OrderChargeItem struct { @@ -250,7 +282,9 @@ type OrderDetail struct { PlatformDeductedAmount *decimal.Decimal PlatformDeductedCurrency string History OrderHistoryDetail - ChargeDetail OrderChargeDetail + // ChargeDetail is nil for attached (take-profit / stop-loss) sub-orders. + ChargeDetail *OrderChargeDetail + AttachedOrders []AttachedOrderDetail } // AccountBalances has a AccountBalance list