mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-07 23:16:53 +00:00
Binance,Okx: Add Leverage, MarginType, Positions and CollateralMode support (#1220)
* init * surprise train commit * basic distinctions * the terms of binance are confusing * renames and introduction of allocatedMargin * add new margin funcs * pulling out wires * implement proper getposition stuff * bad coding day * investigate order manager next * a broken mess, but a progressing one * finally completes some usdtmargined stuff * coinMfutures eludes me * expand to okx * imports fix * completes okx wrapper implementations * cleans and polishes before rpc implementations * rpc setup, order manager features, exch features * more rpc, collateral and margin things * mini test * looking at rpc response, expansion of features * reorganising before the storm * changing how futures requests work * cleanup and tests of cli usage * remove silly client side logic * cleanup * collateral package, typo fix, margin err, rpc derive * uses convert.StringToFloat ONLY ON STRUCTS FROM THIS PR * fix binance order history bug * niteroos * adds new funcs to exchange standards testing * more post merge fixes * fix binance * replace simepletimeformat * fix for merge * merge fixes * micro fixes * order side now required for leverage * fix up the rest * global -> portfolio collateral * Update exchanges/collateral/collateral_test.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * adds fields and todos * rm field redundancy * lint fix oopsie daisy * fixes panic, expands error and cli explanations (sorry shaz) * ensures casing is appropriate for underlying * Adds a shiny TODO --------- Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
This commit is contained in:
@@ -284,6 +284,7 @@ var (
|
||||
errMissingValidGreeksType = errors.New("missing valid greeks type")
|
||||
errMissingIsolatedMarginTradingSetting = errors.New("missing isolated margin trading setting, isolated margin trading settings automatic:Auto transfers autonomy:Manual transfers")
|
||||
errInvalidOrderSide = errors.New("invalid order side")
|
||||
errOrderSideRequired = errors.New("order side required")
|
||||
errInvalidCounterParties = errors.New("missing counter parties")
|
||||
errInvalidLegs = errors.New("no legs are provided")
|
||||
errMissingRFQIDANDClientSuppliedRFQID = errors.New("missing rfq id or client supplied rfq id")
|
||||
@@ -338,6 +339,7 @@ var (
|
||||
errInvalidProtocolType = errors.New("invalid protocol type, only 'staking' and 'defi' allowed")
|
||||
errExceedLimit = errors.New("limit exceeded")
|
||||
errOnlyThreeMonthsSupported = errors.New("only three months of trade data retrieval supported")
|
||||
errOnlyOneResponseExpected = errors.New("one response item expected")
|
||||
errNoInstrumentFound = errors.New("no instrument found")
|
||||
)
|
||||
|
||||
@@ -1859,9 +1861,9 @@ func (ok *Okx) GetConvertHistory(ctx context.Context, before, after time.Time, l
|
||||
|
||||
/********************************** Account endpoints ***************************************************/
|
||||
|
||||
// GetNonZeroBalances retrieves a list of assets (with non-zero balance), remaining balance, and available amount in the trading account.
|
||||
// AccountBalance retrieves a list of assets (with non-zero balance), remaining balance, and available amount in the trading account.
|
||||
// Interest-free quota and discount rates are public data and not displayed on the account interface.
|
||||
func (ok *Okx) GetNonZeroBalances(ctx context.Context, currency string) ([]Account, error) {
|
||||
func (ok *Okx) AccountBalance(ctx context.Context, currency string) ([]Account, error) {
|
||||
var resp []Account
|
||||
params := url.Values{}
|
||||
if currency != "" {
|
||||
@@ -2010,26 +2012,12 @@ func (ok *Okx) SetPositionMode(ctx context.Context, positionMode string) (string
|
||||
return "", errNoValidResponseFromServer
|
||||
}
|
||||
|
||||
// SetLeverage sets a leverage setting for instrument id.
|
||||
func (ok *Okx) SetLeverage(ctx context.Context, arg SetLeverageInput) (*SetLeverageResponse, error) {
|
||||
// SetLeverageRate sets a leverage setting for instrument id.
|
||||
func (ok *Okx) SetLeverageRate(ctx context.Context, arg SetLeverageInput) (*SetLeverageResponse, error) {
|
||||
if arg.InstrumentID == "" && arg.Currency == "" {
|
||||
return nil, errors.New("either instrument id or currency is required")
|
||||
}
|
||||
if arg.Leverage < 0 {
|
||||
return nil, errors.New("missing leverage")
|
||||
}
|
||||
if arg.InstrumentID == "" && arg.MarginMode == TradeModeIsolated {
|
||||
return nil, errors.New("only can be cross if ccy is passed")
|
||||
}
|
||||
if arg.MarginMode != TradeModeCross && arg.MarginMode != TradeModeIsolated {
|
||||
return nil, errors.New("only applicable to \"isolated\" margin mode of FUTURES/SWAP allowed")
|
||||
}
|
||||
arg.PositionSide = strings.ToLower(arg.PositionSide)
|
||||
if arg.PositionSide != positionSideLong &&
|
||||
arg.PositionSide != positionSideShort &&
|
||||
arg.MarginMode == "isolated" {
|
||||
return nil, errors.New("\"long\" \"short\" Only applicable to isolated margin mode of FUTURES/SWAP")
|
||||
}
|
||||
var resp []SetLeverageResponse
|
||||
err := ok.SendHTTPRequest(ctx, exchange.RestSpot, setLeverageEPL, http.MethodPost, accountSetLeverage, &arg, &resp, true)
|
||||
if err != nil {
|
||||
@@ -2107,7 +2095,7 @@ func (ok *Okx) IncreaseDecreaseMargin(ctx context.Context, arg IncreaseDecreaseM
|
||||
return nil, errors.New("missing valid amount")
|
||||
}
|
||||
var resp []IncreaseDecreaseMargin
|
||||
err := ok.SendHTTPRequest(ctx, exchange.RestSpot, increaseOrDecreaseMarginEPL, http.MethodGet, accountPositionMarginBalance, &arg, &resp, true)
|
||||
err := ok.SendHTTPRequest(ctx, exchange.RestSpot, increaseOrDecreaseMarginEPL, http.MethodPost, accountPositionMarginBalance, &arg, &resp, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -2117,8 +2105,8 @@ func (ok *Okx) IncreaseDecreaseMargin(ctx context.Context, arg IncreaseDecreaseM
|
||||
return nil, errNoValidResponseFromServer
|
||||
}
|
||||
|
||||
// GetLeverage retrieves leverage data for different instrument id or margin mode.
|
||||
func (ok *Okx) GetLeverage(ctx context.Context, instrumentID, marginMode string) ([]LeverageResponse, error) {
|
||||
// GetLeverageRate retrieves leverage data for different instrument id or margin mode.
|
||||
func (ok *Okx) GetLeverageRate(ctx context.Context, instrumentID, marginMode string) ([]LeverageResponse, error) {
|
||||
params := url.Values{}
|
||||
if instrumentID != "" {
|
||||
params.Set("instId", instrumentID)
|
||||
@@ -3461,10 +3449,8 @@ func (ok *Okx) GetFundingRateHistory(ctx context.Context, instrumentID string, b
|
||||
if !after.IsZero() {
|
||||
params.Set("after", strconv.FormatInt(after.UnixMilli(), 10))
|
||||
}
|
||||
if limit > 0 && limit <= 100 {
|
||||
if limit > 0 {
|
||||
params.Set("limit", strconv.FormatInt(limit, 10))
|
||||
} else if limit > 100 {
|
||||
return nil, errLimitValueExceedsMaxOf100
|
||||
}
|
||||
var resp []FundingRateResponse
|
||||
return resp, ok.SendHTTPRequest(ctx, exchange.RestSpot, getFundingRateHistoryEPL, http.MethodGet, common.EncodeURLValues(publicFundingRateHistory, params), nil, &resp, false)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1237,72 +1237,72 @@ type Account struct {
|
||||
|
||||
// AccountDetail account detail information.
|
||||
type AccountDetail struct {
|
||||
AvailableBalance okxNumericalValue `json:"availBal"`
|
||||
AvailableEquity okxNumericalValue `json:"availEq"`
|
||||
CashBalance okxNumericalValue `json:"cashBal"` // Cash Balance
|
||||
Currency string `json:"ccy"`
|
||||
CrossLiab okxNumericalValue `json:"crossLiab"`
|
||||
DiscountEquity okxNumericalValue `json:"disEq"`
|
||||
EquityOfCurrency okxNumericalValue `json:"eq"`
|
||||
EquityUsd okxNumericalValue `json:"eqUsd"`
|
||||
FrozenBalance okxNumericalValue `json:"frozenBal"`
|
||||
Interest okxNumericalValue `json:"interest"`
|
||||
IsoEquity okxNumericalValue `json:"isoEq"`
|
||||
IsolatedLiabilities okxNumericalValue `json:"isoLiab"`
|
||||
IsoUpl okxNumericalValue `json:"isoUpl"` // Isolated unrealized profit and loss of the currency applicable to Single-currency margin and Multi-currency margin and Portfolio margin
|
||||
LiabilitiesOfCurrency okxNumericalValue `json:"liab"`
|
||||
MaxLoan okxNumericalValue `json:"maxLoan"`
|
||||
MarginRatio okxNumericalValue `json:"mgnRatio"` // Equity of the currency
|
||||
NotionalLever okxNumericalValue `json:"notionalLever"` // Leverage of the currency applicable to Single-currency margin
|
||||
OpenOrdersMarginFrozen okxNumericalValue `json:"ordFrozen"`
|
||||
Twap okxNumericalValue `json:"twap"`
|
||||
UpdateTime okxUnixMilliTime `json:"uTime"`
|
||||
UnrealizedProfit okxNumericalValue `json:"upl"`
|
||||
UnrealizedCurrencyLiabilities okxNumericalValue `json:"uplLiab"`
|
||||
StrategyEquity okxNumericalValue `json:"stgyEq"` // strategy equity
|
||||
TotalEquity okxNumericalValue `json:"totalEq"` // Total equity in USD level
|
||||
AvailableBalance convert.StringToFloat64 `json:"availBal"`
|
||||
AvailableEquity convert.StringToFloat64 `json:"availEq"`
|
||||
CashBalance convert.StringToFloat64 `json:"cashBal"` // Cash Balance
|
||||
Currency string `json:"ccy"`
|
||||
CrossLiab convert.StringToFloat64 `json:"crossLiab"`
|
||||
DiscountEquity convert.StringToFloat64 `json:"disEq"`
|
||||
EquityOfCurrency convert.StringToFloat64 `json:"eq"`
|
||||
EquityUsd convert.StringToFloat64 `json:"eqUsd"`
|
||||
FrozenBalance convert.StringToFloat64 `json:"frozenBal"`
|
||||
Interest convert.StringToFloat64 `json:"interest"`
|
||||
IsoEquity convert.StringToFloat64 `json:"isoEq"`
|
||||
IsolatedLiabilities convert.StringToFloat64 `json:"isoLiab"`
|
||||
IsoUpl convert.StringToFloat64 `json:"isoUpl"` // Isolated unrealized profit and loss of the currency applicable to Single-currency margin and Multi-currency margin and Portfolio margin
|
||||
LiabilitiesOfCurrency convert.StringToFloat64 `json:"liab"`
|
||||
MaxLoan convert.StringToFloat64 `json:"maxLoan"`
|
||||
MarginRatio convert.StringToFloat64 `json:"mgnRatio"` // Equity of the currency
|
||||
NotionalLever convert.StringToFloat64 `json:"notionalLever"` // Leverage of the currency applicable to Single-currency margin
|
||||
OpenOrdersMarginFrozen convert.StringToFloat64 `json:"ordFrozen"`
|
||||
Twap convert.StringToFloat64 `json:"twap"`
|
||||
UpdateTime okxUnixMilliTime `json:"uTime"`
|
||||
UnrealizedProfit convert.StringToFloat64 `json:"upl"`
|
||||
UnrealizedCurrencyLiabilities convert.StringToFloat64 `json:"uplLiab"`
|
||||
StrategyEquity convert.StringToFloat64 `json:"stgyEq"` // strategy equity
|
||||
TotalEquity convert.StringToFloat64 `json:"totalEq"` // Total equity in USD level. Appears unused
|
||||
}
|
||||
|
||||
// AccountPosition account position.
|
||||
type AccountPosition struct {
|
||||
AutoDeleveraging string `json:"adl"` // Auto-deleveraging (ADL) indicator Divided into 5 levels, from 1 to 5, the smaller the number, the weaker the adl intensity.
|
||||
AvailablePosition string `json:"availPos"` // Position that can be closed Only applicable to MARGIN, FUTURES/SWAP in the long-short mode, OPTION in Simple and isolated OPTION in margin Account.
|
||||
AveragePrice string `json:"avgPx"`
|
||||
CreationTime okxUnixMilliTime `json:"cTime"`
|
||||
Currency string `json:"ccy"`
|
||||
DeltaBS string `json:"deltaBS"` // delta:Black-Scholes Greeks in dollars,only applicable to OPTION
|
||||
DeltaPA string `json:"deltaPA"` // delta:Greeks in coins,only applicable to OPTION
|
||||
GammaBS string `json:"gammaBS"` // gamma:Black-Scholes Greeks in dollars,only applicable to OPTION
|
||||
GammaPA string `json:"gammaPA"` // gamma:Greeks in coins,only applicable to OPTION
|
||||
InitialMarginRequirement string `json:"imr"` // Initial margin requirement, only applicable to cross.
|
||||
InstrumentID string `json:"instId"`
|
||||
InstrumentType string `json:"instType"`
|
||||
Interest string `json:"interest"`
|
||||
USDPrice string `json:"usdPx"`
|
||||
LastTradePrice string `json:"last"`
|
||||
Leverage string `json:"lever"` // Leverage, not applicable to OPTION seller
|
||||
Liabilities string `json:"liab"` // Liabilities, only applicable to MARGIN.
|
||||
LiabilitiesCurrency string `json:"liabCcy"` // Liabilities currency, only applicable to MARGIN.
|
||||
LiquidationPrice string `json:"liqPx"` // Estimated liquidation price Not applicable to OPTION
|
||||
MarkPx string `json:"markPx"`
|
||||
Margin string `json:"margin"`
|
||||
MgnMode string `json:"mgnMode"`
|
||||
MgnRatio string `json:"mgnRatio"`
|
||||
MaintenanceMarginRequirement string `json:"mmr"` // Maintenance margin requirement in USD level Applicable to Multi-currency margin and Portfolio margin
|
||||
NotionalUsd string `json:"notionalUsd"` // Quality of Positions -- usd
|
||||
OptionValue string `json:"optVal"` // Option Value, only application to position.
|
||||
QuantityOfPosition string `json:"pos"` // Quantity of positions,In the mode of autonomous transfer from position to position, after the deposit is transferred, a position with pos of 0 will be generated
|
||||
PositionCurrency string `json:"posCcy"`
|
||||
PositionID string `json:"posId"`
|
||||
PositionSide string `json:"posSide"`
|
||||
ThetaBS string `json:"thetaBS"` // theta:Black-Scholes Greeks in dollars,only applicable to OPTION
|
||||
ThetaPA string `json:"thetaPA"` // theta:Greeks in coins,only applicable to OPTION
|
||||
TradeID string `json:"tradeId"`
|
||||
UpdatedTime okxUnixMilliTime `json:"uTime"` // Latest time position was adjusted,
|
||||
Upl float64 `json:"upl,string,omitempty"` // Unrealized profit and loss
|
||||
UPLRatio float64 `json:"uplRatio,string,omitempty"` // Unrealized profit and loss ratio
|
||||
VegaBS string `json:"vegaBS"` // vega:Black-Scholes Greeks in dollars,only applicable to OPTION
|
||||
VegaPA string `json:"vegaPA"` // vega:Greeks in coins,only applicable to OPTION
|
||||
AutoDeleveraging string `json:"adl"` // Auto-deleveraging (ADL) indicator Divided into 5 levels, from 1 to 5, the smaller the number, the weaker the adl intensity.
|
||||
AvailablePosition string `json:"availPos"` // Position that can be closed Only applicable to MARGIN, FUTURES/SWAP in the long-short mode, OPTION in Simple and isolated OPTION in margin Account.
|
||||
AveragePrice convert.StringToFloat64 `json:"avgPx"`
|
||||
CreationTime okxUnixMilliTime `json:"cTime"`
|
||||
Currency string `json:"ccy"`
|
||||
DeltaBS string `json:"deltaBS"` // delta:Black-Scholes Greeks in dollars,only applicable to OPTION
|
||||
DeltaPA string `json:"deltaPA"` // delta:Greeks in coins,only applicable to OPTION
|
||||
GammaBS string `json:"gammaBS"` // gamma:Black-Scholes Greeks in dollars,only applicable to OPTION
|
||||
GammaPA string `json:"gammaPA"` // gamma:Greeks in coins,only applicable to OPTION
|
||||
InitialMarginRequirement convert.StringToFloat64 `json:"imr"` // Initial margin requirement, only applicable to cross.
|
||||
InstrumentID string `json:"instId"`
|
||||
InstrumentType asset.Item `json:"instType"`
|
||||
Interest convert.StringToFloat64 `json:"interest"`
|
||||
USDPrice convert.StringToFloat64 `json:"usdPx"`
|
||||
LastTradePrice convert.StringToFloat64 `json:"last"`
|
||||
Leverage convert.StringToFloat64 `json:"lever"` // Leverage, not applicable to OPTION seller
|
||||
Liabilities string `json:"liab"` // Liabilities, only applicable to MARGIN.
|
||||
LiabilitiesCurrency string `json:"liabCcy"` // Liabilities currency, only applicable to MARGIN.
|
||||
LiquidationPrice convert.StringToFloat64 `json:"liqPx"` // Estimated liquidation price Not applicable to OPTION
|
||||
MarkPrice convert.StringToFloat64 `json:"markPx"`
|
||||
Margin convert.StringToFloat64 `json:"margin"`
|
||||
MarginMode string `json:"mgnMode"`
|
||||
MarginRatio convert.StringToFloat64 `json:"mgnRatio"`
|
||||
MaintenanceMarginRequirement convert.StringToFloat64 `json:"mmr"` // Maintenance margin requirement in USD level Applicable to Multi-currency margin and Portfolio margin
|
||||
NotionalUsd convert.StringToFloat64 `json:"notionalUsd"` // Quality of Positions -- usd
|
||||
OptionValue convert.StringToFloat64 `json:"optVal"` // Option Value, only application to position.
|
||||
QuantityOfPosition convert.StringToFloat64 `json:"pos"` // Quantity of positions,In the mode of autonomous transfer from position to position, after the deposit is transferred, a position with pos of 0 will be generated
|
||||
PositionCurrency string `json:"posCcy"`
|
||||
PositionID string `json:"posId"`
|
||||
PositionSide string `json:"posSide"`
|
||||
ThetaBS string `json:"thetaBS"` // theta:Black-Scholes Greeks in dollars,only applicable to OPTION
|
||||
ThetaPA string `json:"thetaPA"` // theta:Greeks in coins,only applicable to OPTION
|
||||
TradeID string `json:"tradeId"`
|
||||
UpdatedTime okxUnixMilliTime `json:"uTime"` // Latest time position was adjusted,
|
||||
UPNL convert.StringToFloat64 `json:"upl"` // Unrealized profit and loss
|
||||
UPLRatio convert.StringToFloat64 `json:"uplRatio"` // Unrealized profit and loss ratio
|
||||
VegaBS string `json:"vegaBS"` // vega:Black-Scholes Greeks in dollars,only applicable to OPTION
|
||||
VegaPA string `json:"vegaPA"` // vega:Greeks in coins,only applicable to OPTION
|
||||
|
||||
// PushTime added feature in the websocket push data.
|
||||
|
||||
@@ -1421,19 +1421,19 @@ type PositionMode struct {
|
||||
|
||||
// SetLeverageInput represents set leverage request input
|
||||
type SetLeverageInput struct {
|
||||
Leverage int `json:"lever,string"` // set leverage for isolated
|
||||
MarginMode string `json:"mgnMode"` // Margin Mode "cross" and "isolated"
|
||||
InstrumentID string `json:"instId,omitempty"` // Optional:
|
||||
Currency string `json:"ccy,omitempty"` // Optional:
|
||||
PositionSide string `json:"posSide,omitempty"`
|
||||
Leverage float64 `json:"lever,string"` // set leverage for isolated
|
||||
MarginMode string `json:"mgnMode"` // Margin Mode "cross" and "isolated"
|
||||
InstrumentID string `json:"instId,omitempty"` // Optional:
|
||||
Currency string `json:"ccy,omitempty"` // Optional:
|
||||
PositionSide string `json:"posSide,omitempty"`
|
||||
}
|
||||
|
||||
// SetLeverageResponse represents set leverage response
|
||||
type SetLeverageResponse struct {
|
||||
Leverage string `json:"lever"`
|
||||
MarginMode string `json:"mgnMode"` // Margin Mode "cross" and "isolated"
|
||||
InstrumentID string `json:"instId"`
|
||||
PositionSide string `json:"posSide"` // "long", "short", and "net"
|
||||
Leverage okxNumericalValue `json:"lever"`
|
||||
MarginMode string `json:"mgnMode"` // Margin Mode "cross" and "isolated"
|
||||
InstrumentID string `json:"instId"`
|
||||
PositionSide string `json:"posSide"` // "long", "short", and "net"
|
||||
}
|
||||
|
||||
// MaximumBuyAndSell get maximum buy , sell amount or open amount
|
||||
@@ -1464,20 +1464,20 @@ type IncreaseDecreaseMarginInput struct {
|
||||
|
||||
// IncreaseDecreaseMargin represents increase or decrease the margin of the isolated position response
|
||||
type IncreaseDecreaseMargin struct {
|
||||
Amt string `json:"amt"`
|
||||
Ccy string `json:"ccy"`
|
||||
InstrumentID string `json:"instId"`
|
||||
Leverage string `json:"leverage"`
|
||||
PosSide string `json:"posSide"`
|
||||
Type string `json:"type"`
|
||||
Amount okxNumericalValue `json:"amt"`
|
||||
Ccy string `json:"ccy"`
|
||||
InstrumentID string `json:"instId"`
|
||||
Leverage okxNumericalValue `json:"leverage"`
|
||||
PosSide string `json:"posSide"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// LeverageResponse instrument id leverage response.
|
||||
type LeverageResponse struct {
|
||||
InstrumentID string `json:"instId"`
|
||||
MarginMode string `json:"mgnMode"`
|
||||
PositionSide string `json:"posSide"`
|
||||
Leverage uint `json:"lever,string"`
|
||||
InstrumentID string `json:"instId"`
|
||||
MarginMode string `json:"mgnMode"`
|
||||
PositionSide string `json:"posSide"`
|
||||
Leverage okxNumericalValue `json:"lever"`
|
||||
}
|
||||
|
||||
// MaximumLoanInstrument represents maximum loan of an instrument id.
|
||||
|
||||
@@ -11,15 +11,18 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/collateral"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/deposit"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/fundingrate"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/margin"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/protocol"
|
||||
@@ -68,11 +71,13 @@ func (ok *Okx) SetDefaults() {
|
||||
ok.API.CredentialsValidator.RequiresKey = true
|
||||
ok.API.CredentialsValidator.RequiresSecret = true
|
||||
ok.API.CredentialsValidator.RequiresClientID = true
|
||||
pairFormat := ¤cy.PairFormat{
|
||||
|
||||
cpf := ¤cy.PairFormat{
|
||||
Delimiter: currency.DashDelimiter,
|
||||
Uppercase: true,
|
||||
}
|
||||
err := ok.SetGlobalPairsManager(pairFormat, pairFormat, asset.Spot, asset.Futures, asset.PerpetualSwap, asset.Options, asset.Margin)
|
||||
|
||||
err := ok.SetGlobalPairsManager(cpf, cpf, asset.Spot, asset.Futures, asset.PerpetualSwap, asset.Options, asset.Margin)
|
||||
if err != nil {
|
||||
log.Errorln(log.ExchangeSys, err)
|
||||
}
|
||||
@@ -80,8 +85,9 @@ func (ok *Okx) SetDefaults() {
|
||||
// Fill out the capabilities/features that the exchange supports
|
||||
ok.Features = exchange.Features{
|
||||
Supports: exchange.FeaturesSupported{
|
||||
REST: true,
|
||||
Websocket: true,
|
||||
REST: true,
|
||||
Websocket: true,
|
||||
MaximumOrderHistory: kline.OneDay.Duration() * 90,
|
||||
RESTCapabilities: protocol.Features{
|
||||
TickerFetching: true,
|
||||
OrderbookFetching: true,
|
||||
@@ -123,6 +129,9 @@ func (ok *Okx) SetDefaults() {
|
||||
},
|
||||
WithdrawPermissions: exchange.AutoWithdrawCrypto,
|
||||
FuturesCapabilities: exchange.FuturesCapabilities{
|
||||
Positions: true,
|
||||
Leverage: true,
|
||||
CollateralMode: true,
|
||||
FundingRates: true,
|
||||
MaximumFundingRateHistory: kline.ThreeMonth.Duration(),
|
||||
FundingRateFrequency: kline.EightHour.Duration(),
|
||||
@@ -360,14 +369,14 @@ func (ok *Okx) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) err
|
||||
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (ok *Okx) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) {
|
||||
format, err := ok.GetPairFormat(a, false)
|
||||
pairFormat, err := ok.GetPairFormat(a, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p.IsEmpty() {
|
||||
return nil, currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
instrumentID := format.Format(p)
|
||||
instrumentID := pairFormat.Format(p)
|
||||
if !ok.SupportsAsset(a) {
|
||||
return nil, fmt.Errorf("%w: %v", asset.ErrNotSupported, a)
|
||||
}
|
||||
@@ -502,14 +511,14 @@ func (ok *Okx) UpdateOrderbook(ctx context.Context, pair currency.Pair, assetTyp
|
||||
return nil, err
|
||||
}
|
||||
var instrumentID string
|
||||
format, err := ok.GetPairFormat(assetType, false)
|
||||
pairFormat, err := ok.GetPairFormat(assetType, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !pair.IsPopulated() {
|
||||
return nil, errIncompleteCurrencyPair
|
||||
}
|
||||
instrumentID = format.Format(pair)
|
||||
instrumentID = pairFormat.Format(pair)
|
||||
orderbookNew, err = ok.GetOrderBookDepth(ctx, instrumentID, 400)
|
||||
if err != nil {
|
||||
return book, err
|
||||
@@ -552,7 +561,7 @@ func (ok *Okx) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (acc
|
||||
if !ok.SupportsAsset(assetType) {
|
||||
return info, fmt.Errorf("%w: %v", asset.ErrNotSupported, assetType)
|
||||
}
|
||||
accountBalances, err := ok.GetNonZeroBalances(ctx, "")
|
||||
accountBalances, err := ok.AccountBalance(ctx, "")
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
@@ -660,15 +669,14 @@ func (ok *Okx) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ ass
|
||||
|
||||
// GetRecentTrades returns the most recent trades for a currency and asset
|
||||
func (ok *Okx) GetRecentTrades(ctx context.Context, p currency.Pair, assetType asset.Item) ([]trade.Data, error) {
|
||||
format, err := ok.GetPairFormat(assetType, false)
|
||||
pairFormat, err := ok.GetPairFormat(assetType, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p.IsEmpty() {
|
||||
return nil, currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
|
||||
instrumentID := format.Format(p)
|
||||
instrumentID := pairFormat.Format(p)
|
||||
tradeData, err := ok.GetTrades(ctx, instrumentID, 1000)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -708,7 +716,7 @@ func (ok *Okx) GetHistoricTrades(ctx context.Context, p currency.Pair, assetType
|
||||
return nil, errOnlyThreeMonthsSupported
|
||||
}
|
||||
const limit = 100
|
||||
format, err := ok.GetPairFormat(assetType, false)
|
||||
pairFormat, err := ok.GetPairFormat(assetType, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -716,7 +724,7 @@ func (ok *Okx) GetHistoricTrades(ctx context.Context, p currency.Pair, assetType
|
||||
return nil, currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
var resp []trade.Data
|
||||
instrumentID := format.Format(p)
|
||||
instrumentID := pairFormat.Format(p)
|
||||
tradeIDEnd := ""
|
||||
allTrades:
|
||||
for {
|
||||
@@ -774,17 +782,17 @@ func (ok *Okx) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitR
|
||||
if s.Amount <= 0 {
|
||||
return nil, fmt.Errorf("amount, or size (sz) of quantity to buy or sell hast to be greater than zero ")
|
||||
}
|
||||
format, err := ok.GetPairFormat(s.AssetType, false)
|
||||
pairFormat, err := ok.GetPairFormat(s.AssetType, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.Pair.IsEmpty() {
|
||||
return nil, currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
instrumentID := format.Format(s.Pair)
|
||||
var tradeMode string
|
||||
if s.AssetType != asset.Margin {
|
||||
tradeMode = "cash"
|
||||
instrumentID := pairFormat.Format(s.Pair)
|
||||
tradeMode := ok.marginTypeToString(s.MarginType)
|
||||
if s.Leverage != 0 && s.Leverage != 1 {
|
||||
return nil, fmt.Errorf("%w received '%v'", order.ErrSubmitLeverageNotSupported, s.Leverage)
|
||||
}
|
||||
var sideType string
|
||||
if s.Side.IsLong() {
|
||||
@@ -846,6 +854,17 @@ func (ok *Okx) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitR
|
||||
return s.DeriveSubmitResponse(placeOrderResponse.OrderID)
|
||||
}
|
||||
|
||||
func (ok *Okx) marginTypeToString(m margin.Type) string {
|
||||
switch m {
|
||||
case margin.Isolated:
|
||||
return "isolated"
|
||||
case margin.Multi:
|
||||
return "cross"
|
||||
default:
|
||||
return "cash"
|
||||
}
|
||||
}
|
||||
|
||||
// ModifyOrder will allow of changing orderbook placement and limit to market conversion
|
||||
func (ok *Okx) ModifyOrder(ctx context.Context, action *order.Modify) (*order.ModifyResponse, error) {
|
||||
if err := action.Validate(); err != nil {
|
||||
@@ -855,14 +874,14 @@ func (ok *Okx) ModifyOrder(ctx context.Context, action *order.Modify) (*order.Mo
|
||||
if math.Trunc(action.Amount) != action.Amount {
|
||||
return nil, errors.New("okx contract amount can not be decimal")
|
||||
}
|
||||
format, err := ok.GetPairFormat(action.AssetType, false)
|
||||
pairFormat, err := ok.GetPairFormat(action.AssetType, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if action.Pair.IsEmpty() {
|
||||
return nil, currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
instrumentID := format.Format(action.Pair)
|
||||
instrumentID := pairFormat.Format(action.Pair)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -891,14 +910,14 @@ func (ok *Okx) CancelOrder(ctx context.Context, ord *order.Cancel) error {
|
||||
if !ok.SupportsAsset(ord.AssetType) {
|
||||
return fmt.Errorf("%w: %v", asset.ErrNotSupported, ord.AssetType)
|
||||
}
|
||||
format, err := ok.GetPairFormat(ord.AssetType, false)
|
||||
pairFormat, err := ok.GetPairFormat(ord.AssetType, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ord.Pair.IsEmpty() {
|
||||
return currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
instrumentID := format.Format(ord.Pair)
|
||||
instrumentID := pairFormat.Format(ord.Pair)
|
||||
req := CancelOrderRequestParam{
|
||||
InstrumentID: instrumentID,
|
||||
OrderID: ord.OrderID,
|
||||
@@ -921,7 +940,6 @@ func (ok *Okx) CancelBatchOrders(ctx context.Context, o []order.Cancel) (*order.
|
||||
}
|
||||
cancelOrderParams := make([]CancelOrderRequestParam, len(o))
|
||||
var err error
|
||||
var format currency.PairFormat
|
||||
for x := range o {
|
||||
ord := o[x]
|
||||
err = ord.Validate(ord.StandardCancel())
|
||||
@@ -931,16 +949,17 @@ func (ok *Okx) CancelBatchOrders(ctx context.Context, o []order.Cancel) (*order.
|
||||
if !ok.SupportsAsset(ord.AssetType) {
|
||||
return nil, fmt.Errorf("%w: %v", asset.ErrNotSupported, ord.AssetType)
|
||||
}
|
||||
format, err = ok.GetPairFormat(ord.AssetType, true)
|
||||
|
||||
var instrumentID string
|
||||
var pairFormat currency.PairFormat
|
||||
pairFormat, err = ok.GetPairFormat(ord.AssetType, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ord.Pair.IsPopulated() {
|
||||
return nil, errIncompleteCurrencyPair
|
||||
}
|
||||
instrumentID = format.Format(ord.Pair)
|
||||
instrumentID = pairFormat.Format(ord.Pair)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1078,12 +1097,17 @@ func (ok *Okx) GetOrderInfo(ctx context.Context, orderID string, pair currency.P
|
||||
return nil, err
|
||||
}
|
||||
|
||||
format, err := ok.GetPairFormat(assetType, false)
|
||||
pairFormat, err := ok.GetPairFormat(assetType, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instrumentID := format.Format(pair)
|
||||
if !pair.IsPopulated() {
|
||||
return nil, errIncompleteCurrencyPair
|
||||
}
|
||||
instrumentID := pairFormat.Format(pair)
|
||||
if !ok.SupportsAsset(assetType) {
|
||||
return nil, fmt.Errorf("%w: %v", asset.ErrNotSupported, assetType)
|
||||
}
|
||||
orderDetail, err := ok.GetOrderDetail(ctx, &OrderDetailRequestParam{
|
||||
InstrumentID: instrumentID,
|
||||
OrderID: orderID,
|
||||
@@ -1712,3 +1736,386 @@ func (ok *Okx) GetFundingRates(ctx context.Context, r *fundingrate.RatesRequest)
|
||||
func (ok *Okx) IsPerpetualFutureCurrency(a asset.Item, _ currency.Pair) (bool, error) {
|
||||
return a == asset.PerpetualSwap, nil
|
||||
}
|
||||
|
||||
// SetMarginType sets the default margin type for when opening a new position
|
||||
// okx allows this to be set with an order, however this sets a default
|
||||
func (ok *Okx) SetMarginType(_ context.Context, _ asset.Item, _ currency.Pair, _ margin.Type) error {
|
||||
return fmt.Errorf("%w margin type is set per order", common.ErrFunctionNotSupported)
|
||||
}
|
||||
|
||||
// SetCollateralMode sets the collateral type for your account
|
||||
func (ok *Okx) SetCollateralMode(_ context.Context, _ asset.Item, _ collateral.Mode) error {
|
||||
return fmt.Errorf("%w must be set via website", common.ErrFunctionNotSupported)
|
||||
}
|
||||
|
||||
// GetCollateralMode returns the collateral type for your account
|
||||
func (ok *Okx) GetCollateralMode(ctx context.Context, item asset.Item) (collateral.Mode, error) {
|
||||
if !ok.SupportsAsset(item) {
|
||||
return 0, fmt.Errorf("%w: %v", asset.ErrNotSupported, item)
|
||||
}
|
||||
cfg, err := ok.GetAccountConfiguration(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch cfg[0].AccountLevel {
|
||||
case 1:
|
||||
if item != asset.Spot {
|
||||
return 0, fmt.Errorf("%w %v", asset.ErrNotSupported, item)
|
||||
}
|
||||
fallthrough
|
||||
case 2:
|
||||
return collateral.SingleMode, nil
|
||||
case 3:
|
||||
return collateral.MultiMode, nil
|
||||
case 4:
|
||||
return collateral.PortfolioMode, nil
|
||||
default:
|
||||
return collateral.UnknownMode, fmt.Errorf("%w %v", order.ErrCollateralInvalid, cfg[0].AccountLevel)
|
||||
}
|
||||
}
|
||||
|
||||
// ChangePositionMargin will modify a position/currencies margin parameters
|
||||
func (ok *Okx) ChangePositionMargin(ctx context.Context, req *margin.PositionChangeRequest) (*margin.PositionChangeResponse, error) {
|
||||
if req == nil {
|
||||
return nil, fmt.Errorf("%w PositionChangeRequest", common.ErrNilPointer)
|
||||
}
|
||||
if !ok.SupportsAsset(req.Asset) {
|
||||
return nil, fmt.Errorf("%w: %v", asset.ErrNotSupported, req.Asset)
|
||||
}
|
||||
if req.NewAllocatedMargin == 0 {
|
||||
return nil, fmt.Errorf("%w %v %v", margin.ErrNewAllocatedMarginRequired, req.Asset, req.Pair)
|
||||
}
|
||||
if req.OriginalAllocatedMargin == 0 {
|
||||
return nil, margin.ErrOriginalPositionMarginRequired
|
||||
}
|
||||
if req.MarginType != margin.Isolated {
|
||||
return nil, fmt.Errorf("%w %v", margin.ErrMarginTypeUnsupported, req.MarginType)
|
||||
}
|
||||
pairFormat, err := ok.GetPairFormat(req.Asset, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fPair := req.Pair.Format(pairFormat)
|
||||
marginType := "add"
|
||||
amt := req.NewAllocatedMargin - req.OriginalAllocatedMargin
|
||||
if req.NewAllocatedMargin < req.OriginalAllocatedMargin {
|
||||
marginType = "reduce"
|
||||
amt = req.OriginalAllocatedMargin - req.NewAllocatedMargin
|
||||
}
|
||||
if req.MarginSide == "" {
|
||||
req.MarginSide = "net"
|
||||
}
|
||||
r := IncreaseDecreaseMarginInput{
|
||||
InstrumentID: fPair.String(),
|
||||
PositionSide: req.MarginSide,
|
||||
Type: marginType,
|
||||
Amount: amt,
|
||||
}
|
||||
|
||||
if req.Asset == asset.Margin {
|
||||
r.Currency = req.Pair.Base.Item.Symbol
|
||||
}
|
||||
|
||||
resp, err := ok.IncreaseDecreaseMargin(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &margin.PositionChangeResponse{
|
||||
Exchange: ok.Name,
|
||||
Pair: req.Pair,
|
||||
Asset: req.Asset,
|
||||
AllocatedMargin: resp.Amount.Float64(),
|
||||
MarginType: req.MarginType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetFuturesPositionSummary returns position summary details for an active position
|
||||
func (ok *Okx) GetFuturesPositionSummary(ctx context.Context, req *order.PositionSummaryRequest) (*order.PositionSummary, error) {
|
||||
if req == nil {
|
||||
return nil, fmt.Errorf("%w PositionSummaryRequest", common.ErrNilPointer)
|
||||
}
|
||||
if req.CalculateOffline {
|
||||
return nil, common.ErrCannotCalculateOffline
|
||||
}
|
||||
if !ok.SupportsAsset(req.Asset) || !req.Asset.IsFutures() {
|
||||
return nil, fmt.Errorf("%w %v", asset.ErrNotSupported, req.Asset)
|
||||
}
|
||||
fPair, err := ok.FormatExchangeCurrency(req.Pair, req.Asset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instrumentType := ok.GetInstrumentTypeFromAssetItem(req.Asset)
|
||||
positionSummaries, err := ok.GetPositions(ctx, instrumentType, fPair.String(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var positionSummary *AccountPosition
|
||||
for i := range positionSummaries {
|
||||
if positionSummaries[i].QuantityOfPosition.Float64() <= 0 {
|
||||
continue
|
||||
}
|
||||
positionSummary = &positionSummaries[i]
|
||||
break
|
||||
}
|
||||
if positionSummary == nil {
|
||||
return nil, fmt.Errorf("%w, received '%v', no positions found", errOnlyOneResponseExpected, len(positionSummaries))
|
||||
}
|
||||
marginMode := margin.Isolated
|
||||
if positionSummary.MarginMode == "cross" {
|
||||
marginMode = margin.Multi
|
||||
}
|
||||
|
||||
acc, err := ok.AccountBalance(ctx, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(acc) != 1 {
|
||||
return nil, fmt.Errorf("%w, received '%v'", errOnlyOneResponseExpected, len(acc))
|
||||
}
|
||||
var (
|
||||
freeCollateral, totalCollateral, equityOfCurrency, frozenBalance,
|
||||
availableEquity, cashBalance, discountEquity,
|
||||
equityUSD, totalEquity, isolatedEquity, isolatedLiabilities,
|
||||
isolatedUnrealisedProfit, notionalLeverage,
|
||||
strategyEquity decimal.Decimal
|
||||
)
|
||||
|
||||
for i := range acc[0].Details {
|
||||
if acc[0].Details[i].Currency != positionSummary.Currency {
|
||||
continue
|
||||
}
|
||||
freeCollateral = acc[0].Details[i].AvailableBalance.Decimal()
|
||||
frozenBalance = acc[0].Details[i].FrozenBalance.Decimal()
|
||||
totalCollateral = freeCollateral.Add(frozenBalance)
|
||||
equityOfCurrency = acc[0].Details[i].EquityOfCurrency.Decimal()
|
||||
availableEquity = acc[0].Details[i].AvailableEquity.Decimal()
|
||||
cashBalance = acc[0].Details[i].CashBalance.Decimal()
|
||||
discountEquity = acc[0].Details[i].DiscountEquity.Decimal()
|
||||
equityUSD = acc[0].Details[i].EquityUsd.Decimal()
|
||||
totalEquity = acc[0].Details[i].TotalEquity.Decimal()
|
||||
isolatedEquity = acc[0].Details[i].IsoEquity.Decimal()
|
||||
isolatedLiabilities = acc[0].Details[i].IsolatedLiabilities.Decimal()
|
||||
isolatedUnrealisedProfit = acc[0].Details[i].IsoUpl.Decimal()
|
||||
notionalLeverage = acc[0].Details[i].NotionalLever.Decimal()
|
||||
strategyEquity = acc[0].Details[i].StrategyEquity.Decimal()
|
||||
|
||||
break
|
||||
}
|
||||
collateralMode, err := ok.GetCollateralMode(ctx, req.Asset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &order.PositionSummary{
|
||||
Pair: req.Pair,
|
||||
Asset: req.Asset,
|
||||
MarginType: marginMode,
|
||||
CollateralMode: collateralMode,
|
||||
Currency: currency.NewCode(positionSummary.Currency),
|
||||
AvailableEquity: availableEquity,
|
||||
CashBalance: cashBalance,
|
||||
DiscountEquity: discountEquity,
|
||||
EquityUSD: equityUSD,
|
||||
IsolatedEquity: isolatedEquity,
|
||||
IsolatedLiabilities: isolatedLiabilities,
|
||||
IsolatedUPL: isolatedUnrealisedProfit,
|
||||
NotionalLeverage: notionalLeverage,
|
||||
TotalEquity: totalEquity,
|
||||
StrategyEquity: strategyEquity,
|
||||
IsolatedMargin: positionSummary.Margin.Decimal(),
|
||||
NotionalSize: positionSummary.NotionalUsd.Decimal(),
|
||||
Leverage: positionSummary.Leverage.Decimal(),
|
||||
MaintenanceMarginRequirement: positionSummary.MaintenanceMarginRequirement.Decimal(),
|
||||
InitialMarginRequirement: positionSummary.InitialMarginRequirement.Decimal(),
|
||||
EstimatedLiquidationPrice: positionSummary.LiquidationPrice.Decimal(),
|
||||
CollateralUsed: positionSummary.Margin.Decimal(),
|
||||
MarkPrice: positionSummary.MarkPrice.Decimal(),
|
||||
CurrentSize: positionSummary.QuantityOfPosition.Decimal(), // TODO: add field(s) for contract amount vs quote amount
|
||||
AverageOpenPrice: positionSummary.AveragePrice.Decimal(),
|
||||
PositionPNL: positionSummary.UPNL.Decimal(),
|
||||
MaintenanceMarginFraction: positionSummary.MarginRatio.Decimal(),
|
||||
FreeCollateral: freeCollateral,
|
||||
TotalCollateral: totalCollateral,
|
||||
FrozenBalance: frozenBalance,
|
||||
EquityOfCurrency: equityOfCurrency,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetFuturesPositionOrders returns the orders for futures positions
|
||||
func (ok *Okx) GetFuturesPositionOrders(ctx context.Context, req *order.PositionsRequest) ([]order.PositionResponse, error) {
|
||||
if req == nil {
|
||||
return nil, fmt.Errorf("%w PositionSummaryRequest", common.ErrNilPointer)
|
||||
}
|
||||
if !ok.SupportsAsset(req.Asset) || !req.Asset.IsFutures() {
|
||||
return nil, fmt.Errorf("%w %v", asset.ErrNotSupported, req.Asset)
|
||||
}
|
||||
if time.Since(req.StartDate) > ok.Features.Supports.MaximumOrderHistory {
|
||||
if req.RespectOrderHistoryLimits {
|
||||
req.StartDate = time.Now().Add(-ok.Features.Supports.MaximumOrderHistory)
|
||||
} else {
|
||||
return nil, fmt.Errorf("%w max lookup %v", order.ErrOrderHistoryTooLarge, time.Now().Add(-ok.Features.Supports.MaximumOrderHistory))
|
||||
}
|
||||
}
|
||||
if err := common.StartEndTimeCheck(req.StartDate, req.EndDate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp := make([]order.PositionResponse, len(req.Pairs))
|
||||
for i := range req.Pairs {
|
||||
fPair, err := ok.FormatExchangeCurrency(req.Pairs[i], req.Asset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instrumentType := ok.GetInstrumentTypeFromAssetItem(req.Asset)
|
||||
resp[i] = order.PositionResponse{
|
||||
Pair: req.Pairs[i],
|
||||
Asset: req.Asset,
|
||||
}
|
||||
|
||||
var positions []OrderDetail
|
||||
historyRequest := &OrderHistoryRequestParams{
|
||||
OrderListRequestParams: OrderListRequestParams{
|
||||
InstrumentType: instrumentType,
|
||||
InstrumentID: fPair.String(),
|
||||
Start: req.StartDate,
|
||||
End: req.EndDate,
|
||||
},
|
||||
}
|
||||
if time.Since(req.StartDate) <= time.Hour*24*7 {
|
||||
positions, err = ok.Get7DayOrderHistory(ctx, historyRequest)
|
||||
} else {
|
||||
positions, err = ok.Get3MonthOrderHistory(ctx, historyRequest)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for j := range positions {
|
||||
if req.Pairs[i].String() != positions[j].InstrumentID {
|
||||
continue
|
||||
}
|
||||
var orderStatus order.Status
|
||||
orderStatus, err = order.StringToOrderStatus(strings.ToUpper(positions[j].State))
|
||||
if err != nil {
|
||||
log.Errorf(log.ExchangeSys, "%s %v", ok.Name, err)
|
||||
}
|
||||
orderSide := positions[j].Side
|
||||
var oType order.Type
|
||||
oType, err = ok.OrderTypeFromString(positions[j].OrderType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
orderAmount := positions[j].Size
|
||||
if positions[j].QuantityType == "quote_ccy" {
|
||||
// Size is quote amount.
|
||||
orderAmount /= positions[j].AveragePrice
|
||||
}
|
||||
|
||||
remainingAmount := float64(0)
|
||||
if orderStatus != order.Filled {
|
||||
remainingAmount = orderAmount.Float64() - positions[j].AccumulatedFillSize.Float64()
|
||||
}
|
||||
resp[i].Orders = append(resp[i].Orders, order.Detail{
|
||||
Price: positions[j].Price.Float64(),
|
||||
AverageExecutedPrice: positions[j].AveragePrice.Float64(),
|
||||
Amount: orderAmount.Float64(), // TODO: add field(s) for contract amount vs quote amount
|
||||
ExecutedAmount: positions[j].AccumulatedFillSize.Float64(),
|
||||
RemainingAmount: remainingAmount,
|
||||
Fee: positions[j].TransactionFee.Float64(),
|
||||
FeeAsset: currency.NewCode(positions[j].FeeCurrency),
|
||||
Exchange: ok.Name,
|
||||
OrderID: positions[j].OrderID,
|
||||
ClientOrderID: positions[j].ClientSupplierOrderID,
|
||||
Type: oType,
|
||||
Side: orderSide,
|
||||
Status: orderStatus,
|
||||
AssetType: req.Asset,
|
||||
Date: positions[j].CreationTime,
|
||||
LastUpdated: positions[j].UpdateTime,
|
||||
Pair: req.Pairs[i],
|
||||
Cost: positions[j].AveragePrice.Float64() * positions[j].AccumulatedFillSize.Float64(),
|
||||
CostAsset: currency.NewCode(positions[j].RebateCurrency),
|
||||
})
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// SetLeverage sets the account's initial leverage for the asset type and pair
|
||||
func (ok *Okx) SetLeverage(ctx context.Context, item asset.Item, pair currency.Pair, marginType margin.Type, amount float64, orderSide order.Side) error {
|
||||
posSide := "net"
|
||||
switch item {
|
||||
case asset.Futures, asset.PerpetualSwap:
|
||||
if marginType == margin.Isolated {
|
||||
switch {
|
||||
case orderSide == order.UnknownSide:
|
||||
return errOrderSideRequired
|
||||
case orderSide.IsLong():
|
||||
posSide = "long"
|
||||
case orderSide.IsShort():
|
||||
posSide = "short"
|
||||
default:
|
||||
return fmt.Errorf("%w %v requires long/short", errInvalidOrderSide, orderSide)
|
||||
}
|
||||
}
|
||||
fallthrough
|
||||
case asset.Margin, asset.Options:
|
||||
instrumentID, err := ok.FormatSymbol(pair, item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
marginMode := ok.marginTypeToString(marginType)
|
||||
_, err = ok.SetLeverageRate(ctx, SetLeverageInput{
|
||||
Leverage: amount,
|
||||
MarginMode: marginMode,
|
||||
InstrumentID: instrumentID,
|
||||
PositionSide: posSide,
|
||||
})
|
||||
return err
|
||||
default:
|
||||
return fmt.Errorf("%w %v", asset.ErrNotSupported, item)
|
||||
}
|
||||
}
|
||||
|
||||
// GetLeverage gets the account's initial leverage for the asset type and pair
|
||||
func (ok *Okx) GetLeverage(ctx context.Context, item asset.Item, pair currency.Pair, marginType margin.Type, orderSide order.Side) (float64, error) {
|
||||
var inspectLeverage bool
|
||||
switch item {
|
||||
case asset.Futures, asset.PerpetualSwap:
|
||||
if marginType == margin.Isolated {
|
||||
switch {
|
||||
case orderSide == order.UnknownSide:
|
||||
return 0, errOrderSideRequired
|
||||
case orderSide.IsLong(), orderSide.IsShort():
|
||||
inspectLeverage = true
|
||||
default:
|
||||
return 0, fmt.Errorf("%w %v requires long/short", errInvalidOrderSide, orderSide)
|
||||
}
|
||||
}
|
||||
fallthrough
|
||||
case asset.Margin, asset.Options:
|
||||
instrumentID, err := ok.FormatSymbol(pair, item)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
marginMode := ok.marginTypeToString(marginType)
|
||||
lev, err := ok.GetLeverageRate(ctx, instrumentID, marginMode)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if len(lev) == 0 {
|
||||
return -1, fmt.Errorf("%w %v %v %s", order.ErrPositionNotFound, item, pair, marginType)
|
||||
}
|
||||
if inspectLeverage {
|
||||
for i := range lev {
|
||||
if lev[i].PositionSide == orderSide.Lower() {
|
||||
return lev[i].Leverage.Float64(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// leverage is the same across positions
|
||||
return lev[0].Leverage.Float64(), nil
|
||||
default:
|
||||
return -1, fmt.Errorf("%w %v", asset.ErrNotSupported, item)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user