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:
Scott
2023-09-26 16:16:31 +10:00
committed by GitHub
parent a2ae99ed7f
commit 5f2f6f884b
67 changed files with 11558 additions and 4475 deletions

View File

@@ -9,7 +9,9 @@ import (
"github.com/shopspring/decimal"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/collateral"
"github.com/thrasher-corp/gocryptotrader/exchanges/fundingrate"
"github.com/thrasher-corp/gocryptotrader/exchanges/margin"
)
var (
@@ -35,6 +37,8 @@ var (
ErrNoPositionsFound = errors.New("no positions found")
// ErrGetFundingDataRequired is returned when requesting funding rate data without the prerequisite
ErrGetFundingDataRequired = errors.New("getfundingdata is a prerequisite")
// ErrOrderHistoryTooLarge is returned when you lookup order history, but with too early a start date
ErrOrderHistoryTooLarge = errors.New("order history start date too long ago")
errExchangeNameEmpty = errors.New("exchange name empty")
errExchangeNameMismatch = errors.New("exchange name mismatch")
@@ -65,57 +69,12 @@ type TotalCollateralResponse struct {
TotalValueOfPositiveSpotBalances decimal.Decimal
CollateralContributedByPositiveSpotBalances decimal.Decimal
UsedCollateral decimal.Decimal
UsedBreakdown *UsedCollateralBreakdown
UsedBreakdown *collateral.UsedBreakdown
AvailableCollateral decimal.Decimal
AvailableMaintenanceCollateral decimal.Decimal
UnrealisedPNL decimal.Decimal
BreakdownByCurrency []CollateralByCurrency
BreakdownOfPositions []CollateralByPosition
}
// CollateralByPosition shows how much collateral is used
// from positions
type CollateralByPosition struct {
PositionCurrency currency.Pair
Size decimal.Decimal
OpenOrderSize decimal.Decimal
PositionSize decimal.Decimal
MarkPrice decimal.Decimal
RequiredMargin decimal.Decimal
CollateralUsed decimal.Decimal
}
// CollateralByCurrency individual collateral contribution
// along with what the potentially scaled collateral
// currency it is represented as
// eg in Bybit ScaledCurrency is USDC
type CollateralByCurrency struct {
Currency currency.Code
SkipContribution bool
TotalFunds decimal.Decimal
AvailableForUseAsCollateral decimal.Decimal
CollateralContribution decimal.Decimal
AdditionalCollateralUsed decimal.Decimal
FairMarketValue decimal.Decimal
Weighting decimal.Decimal
ScaledCurrency currency.Code
UnrealisedPNL decimal.Decimal
ScaledUsed decimal.Decimal
ScaledUsedBreakdown *UsedCollateralBreakdown
Error error
}
// UsedCollateralBreakdown provides a detailed
// breakdown of where collateral is currently being allocated
type UsedCollateralBreakdown struct {
LockedInStakes decimal.Decimal
LockedInNFTBids decimal.Decimal
LockedInFeeVoucher decimal.Decimal
LockedInSpotMarginFundingOffers decimal.Decimal
LockedInSpotOrders decimal.Decimal
LockedAsCollateral decimal.Decimal
UsedInPositions decimal.Decimal
UsedInSpotMarginBorrows decimal.Decimal
BreakdownByCurrency []collateral.ByCurrency
BreakdownOfPositions []collateral.ByPosition
}
// PositionController manages all futures orders
@@ -310,38 +269,27 @@ type Position struct {
type PositionSummaryRequest struct {
Asset asset.Item
Pair currency.Pair
// UnderlyingPair is optional if the exchange requires it for a contract like BTCUSDT-13333337
UnderlyingPair currency.Pair
// offline calculation requirements below
CalculateOffline bool
Direction Side
FreeCollateral decimal.Decimal
TotalCollateral decimal.Decimal
OpeningPrice decimal.Decimal
CurrentPrice decimal.Decimal
OpeningSize decimal.Decimal
CurrentSize decimal.Decimal
CollateralUsed decimal.Decimal
NotionalPrice decimal.Decimal
Leverage decimal.Decimal
MaxLeverageForAccount decimal.Decimal
TotalAccountValue decimal.Decimal
TotalOpenPositionNotional decimal.Decimal
}
// PositionSummary returns basic details on an open position
type PositionSummary struct {
MaintenanceMarginRequirement decimal.Decimal
InitialMarginRequirement decimal.Decimal
EstimatedLiquidationPrice decimal.Decimal
CollateralUsed decimal.Decimal
MarkPrice decimal.Decimal
CurrentSize decimal.Decimal
BreakEvenPrice decimal.Decimal
AverageOpenPrice decimal.Decimal
RecentPNL decimal.Decimal
MarginFraction decimal.Decimal
FreeCollateral decimal.Decimal
TotalCollateral decimal.Decimal
// EstimatePosition if enabled, can be used to calculate a new position
EstimatePosition bool
// These fields are also used for offline calculation
OpeningPrice decimal.Decimal
OpeningSize decimal.Decimal
Leverage decimal.Decimal
Direction Side
TotalAccountValue decimal.Decimal
}
// PositionDetails are used to track open positions
@@ -359,4 +307,56 @@ type PositionsRequest struct {
Asset asset.Item
Pairs currency.Pairs
StartDate time.Time
EndDate time.Time
// RespectOrderHistoryLimits is designed for the order manager
// it allows for orders to be tracked if the start date in the config is
// beyond the allowable limits by the API, rather than returning an error
RespectOrderHistoryLimits bool
}
// PositionResponse are used to track open positions
// in the order manager
type PositionResponse struct {
Pair currency.Pair
Asset asset.Item
Orders []Detail
}
// PositionSummary returns basic details on an open position
type PositionSummary struct {
Pair currency.Pair
Asset asset.Item
MarginType margin.Type
CollateralMode collateral.Mode
// The currency in which the values are quoted against. Isn't always pair.Quote
// eg BTC-USDC-230929's quote in GCT is 230929, but the currency should be USDC
Currency currency.Code
AvailableEquity decimal.Decimal
CashBalance decimal.Decimal
DiscountEquity decimal.Decimal
EquityUSD decimal.Decimal
IsolatedEquity decimal.Decimal
IsolatedLiabilities decimal.Decimal
IsolatedUPL decimal.Decimal
NotionalLeverage decimal.Decimal
TotalEquity decimal.Decimal
StrategyEquity decimal.Decimal
IsolatedMargin decimal.Decimal
NotionalSize decimal.Decimal
Leverage decimal.Decimal
MaintenanceMarginRequirement decimal.Decimal
InitialMarginRequirement decimal.Decimal
EstimatedLiquidationPrice decimal.Decimal
CollateralUsed decimal.Decimal
MarkPrice decimal.Decimal
CurrentSize decimal.Decimal
AverageOpenPrice decimal.Decimal
PositionPNL decimal.Decimal
MaintenanceMarginFraction decimal.Decimal
FreeCollateral decimal.Decimal
TotalCollateral decimal.Decimal
FrozenBalance decimal.Decimal
EquityOfCurrency decimal.Decimal
}

View File

@@ -820,7 +820,7 @@ func TestStringToOrderSide(t *testing.T) {
{"any", AnySide, nil},
{"ANY", AnySide, nil},
{"aNy", AnySide, nil},
{"woahMan", UnknownSide, errUnrecognisedOrderSide},
{"woahMan", UnknownSide, ErrSideIsInvalid},
}
for i := range cases {
testData := &cases[i]
@@ -1340,8 +1340,8 @@ func TestValidationOnOrderTypes(t *testing.T) {
getOrders.AssetType = asset.Spot
err = getOrders.Validate()
if !errors.Is(err, errUnrecognisedOrderSide) {
t.Fatalf("received: '%v' but expected: '%v'", err, errUnrecognisedOrderSide)
if !errors.Is(err, ErrSideIsInvalid) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrSideIsInvalid)
}
getOrders.Side = AnySide

View File

@@ -7,6 +7,7 @@ import (
"github.com/gofrs/uuid"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/margin"
)
// var error definitions
@@ -19,10 +20,12 @@ var (
ErrPairIsEmpty = errors.New("order pair is empty")
ErrAssetNotSet = errors.New("order asset type is not set")
ErrSideIsInvalid = errors.New("order side is invalid")
ErrCollateralInvalid = errors.New("collateral type is invalid")
ErrTypeIsInvalid = errors.New("order type is invalid")
ErrAmountIsInvalid = errors.New("order amount is equal or less than zero")
ErrPriceMustBeSetIfLimitOrder = errors.New("order price must be set if limit order type is desired")
ErrOrderIDNotSet = errors.New("order id or client order id is not set")
ErrSubmitLeverageNotSupported = errors.New("leverage is not supported via order submission")
ErrClientOrderIDNotSupported = errors.New("client order id not supported")
ErrUnsupportedOrderType = errors.New("unsupported order type")
// ErrNoRates is returned when no margin rates are returned when they are expected
@@ -64,6 +67,9 @@ type Submit struct {
TriggerPrice float64
ClientID string // TODO: Shift to credentials
ClientOrderID string
// MarginType such as isolated or cross margin for when an exchange
// supports margin type definition when submitting an order eg okx
MarginType margin.Type
// RetrieveFees use if an API submit order response does not return fees
// enabling this will perform additional request(s) to retrieve them
// and set it in the SubmitResponse
@@ -103,6 +109,7 @@ type SubmitResponse struct {
Fee float64
FeeAsset currency.Code
Cost float64
MarginType margin.Type
}
// Modify contains all properties of an order
@@ -191,6 +198,7 @@ type Detail struct {
CloseTime time.Time
LastUpdated time.Time
Pair currency.Pair
MarginType margin.Type
Trades []TradeHistory
}
@@ -226,6 +234,7 @@ type Cancel struct {
Side Side
AssetType asset.Item
Pair currency.Pair
MarginType margin.Type
}
// CancelAllResponse returns the status from attempting to

View File

@@ -35,7 +35,6 @@ var (
ErrOrderNotFound = errors.New("order not found")
errTimeInForceConflict = errors.New("multiple time in force options applied")
errUnrecognisedOrderSide = errors.New("unrecognised order side")
errUnrecognisedOrderType = errors.New("unrecognised order type")
errUnrecognisedOrderStatus = errors.New("unrecognised order status")
errExchangeNameUnset = errors.New("exchange name unset")
@@ -476,6 +475,7 @@ func (s *Submit) DeriveSubmitResponse(orderID string) (*SubmitResponse, error) {
TriggerPrice: s.TriggerPrice,
ClientID: s.ClientID,
ClientOrderID: s.ClientOrderID,
MarginType: s.MarginType,
LastUpdated: time.Now(),
Date: time.Now(),
@@ -1056,7 +1056,7 @@ func StringToOrderSide(side string) (Side, error) {
case AnySide.String():
return AnySide, nil
default:
return UnknownSide, fmt.Errorf("'%s' %w", side, errUnrecognisedOrderSide)
return UnknownSide, fmt.Errorf("'%s' %w", side, ErrSideIsInvalid)
}
}
@@ -1210,7 +1210,7 @@ func (g *MultiOrderRequest) Validate(opt ...validate.Checker) error {
}
if g.Side == UnknownSide {
return errUnrecognisedOrderSide
return ErrSideIsInvalid
}
if g.Type == UnknownType {