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

@@ -14,6 +14,7 @@ import (
"github.com/shopspring/decimal"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/communications/base"
"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/asset"
@@ -24,7 +25,7 @@ import (
)
// SetupOrderManager will boot up the OrderManager
func SetupOrderManager(exchangeManager iExchangeManager, communicationsManager iCommsManager, wg *sync.WaitGroup, verbose, activelyTrackFuturesPositions bool, futuresTrackingSeekDuration time.Duration) (*OrderManager, error) {
func SetupOrderManager(exchangeManager iExchangeManager, communicationsManager iCommsManager, wg *sync.WaitGroup, cfg *config.OrderManager) (*OrderManager, error) {
if exchangeManager == nil {
return nil, errNilExchangeManager
}
@@ -34,10 +35,18 @@ func SetupOrderManager(exchangeManager iExchangeManager, communicationsManager i
if wg == nil {
return nil, errNilWaitGroup
}
if cfg == nil {
return nil, fmt.Errorf("%w OrderManager", errNilConfig)
}
var respectOrderHistoryLimits bool
if cfg.RespectOrderHistoryLimits != nil {
respectOrderHistoryLimits = *cfg.RespectOrderHistoryLimits
}
om := &OrderManager{
shutdown: make(chan struct{}),
activelyTrackFuturesPositions: activelyTrackFuturesPositions,
activelyTrackFuturesPositions: cfg.ActivelyTrackFuturesPositions,
respectOrderHistoryLimits: respectOrderHistoryLimits,
orderStore: store{
Orders: make(map[string][]*order.Detail),
exchangeManager: exchangeManager,
@@ -45,16 +54,19 @@ func SetupOrderManager(exchangeManager iExchangeManager, communicationsManager i
wg: wg,
futuresPositionController: order.SetupPositionController(),
},
verbose: verbose,
verbose: cfg.Verbose,
cfg: orderManagerConfig{
CancelOrdersOnShutdown: cfg.CancelOrdersOnShutdown,
},
}
if activelyTrackFuturesPositions {
if futuresTrackingSeekDuration > 0 {
futuresTrackingSeekDuration = -futuresTrackingSeekDuration
if cfg.ActivelyTrackFuturesPositions {
if cfg.FuturesTrackingSeekDuration > 0 {
cfg.FuturesTrackingSeekDuration *= -1
}
if futuresTrackingSeekDuration == 0 {
futuresTrackingSeekDuration = defaultOrderSeekTime
if cfg.FuturesTrackingSeekDuration == 0 {
cfg.FuturesTrackingSeekDuration = defaultOrderSeekTime
}
om.futuresPositionSeekDuration = futuresTrackingSeekDuration
om.futuresPositionSeekDuration = cfg.FuturesTrackingSeekDuration
}
return om, nil
}
@@ -699,9 +711,9 @@ func (m *OrderManager) processOrders() {
wg.Add(1)
go m.processMatchingOrders(exchanges[x], orders, &wg)
}
if m.activelyTrackFuturesPositions && enabledAssets[y].IsFutures() {
var positions []order.PositionDetails
supportedFeatures := exchanges[x].GetSupportedFeatures()
if m.activelyTrackFuturesPositions && enabledAssets[y].IsFutures() && supportedFeatures.FuturesCapabilities.OrderManagerPositionTracking {
var positions []order.PositionResponse
var sd time.Time
sd, err = m.orderStore.futuresPositionController.LastUpdated()
if err != nil {
@@ -711,10 +723,11 @@ func (m *OrderManager) processOrders() {
if sd.IsZero() {
sd = time.Now().Add(m.futuresPositionSeekDuration)
}
positions, err = exchanges[x].GetFuturesPositions(context.TODO(), &order.PositionsRequest{
Asset: enabledAssets[y],
Pairs: pairs,
StartDate: sd,
positions, err = exchanges[x].GetFuturesPositionOrders(context.TODO(), &order.PositionsRequest{
Asset: enabledAssets[y],
Pairs: pairs,
StartDate: sd,
RespectOrderHistoryLimits: m.respectOrderHistoryLimits,
})
if err != nil {
if !errors.Is(err, common.ErrNotYetImplemented) {
@@ -728,7 +741,7 @@ func (m *OrderManager) processOrders() {
}
err = m.processFuturesPositions(exchanges[x], &positions[z])
if err != nil {
log.Errorf(log.OrderMgr, "unable to process future positions for %v %v %v. err: %v", positions[z].Exchange, positions[z].Asset, positions[z].Pair, err)
log.Errorf(log.OrderMgr, "unable to process future positions for %v %v %v. err: %v", exchanges[x].GetName(), positions[z].Asset, positions[z].Pair, err)
}
}
}
@@ -741,7 +754,7 @@ func (m *OrderManager) processOrders() {
}
// processFuturesPositions ensures any open position found is kept up to date in the order manager
func (m *OrderManager) processFuturesPositions(exch exchange.IBotExchange, position *order.PositionDetails) error {
func (m *OrderManager) processFuturesPositions(exch exchange.IBotExchange, position *order.PositionResponse) error {
if !m.activelyTrackFuturesPositions {
return errFuturesTrackingDisabled
}
@@ -749,14 +762,15 @@ func (m *OrderManager) processFuturesPositions(exch exchange.IBotExchange, posit
return fmt.Errorf("%w IBotExchange", common.ErrNilPointer)
}
if position == nil {
return fmt.Errorf("%w PositionDetails", common.ErrNilPointer)
return fmt.Errorf("%w PositionResponse", common.ErrNilPointer)
}
if len(position.Orders) == 0 {
return fmt.Errorf("%w position for '%v' '%v' '%v' has no orders", errNilOrder, position.Exchange, position.Asset, position.Pair)
return fmt.Errorf("%w position for '%v' '%v' '%v' has no orders", errNilOrder, exch.GetName(), position.Asset, position.Pair)
}
sort.Slice(position.Orders, func(i, j int) bool {
return position.Orders[i].Date.Before(position.Orders[j].Date)
})
feat := exch.GetSupportedFeatures()
var err error
for i := range position.Orders {
err = m.orderStore.futuresPositionController.TrackNewOrder(&position.Orders[i])
@@ -764,7 +778,7 @@ func (m *OrderManager) processFuturesPositions(exch exchange.IBotExchange, posit
return err
}
}
_, err = m.orderStore.futuresPositionController.GetOpenPosition(position.Exchange, position.Asset, position.Pair)
_, err = m.orderStore.futuresPositionController.GetOpenPosition(exch.GetName(), position.Asset, position.Pair)
if err != nil {
if errors.Is(err, order.ErrPositionNotFound) {
return nil
@@ -773,11 +787,14 @@ func (m *OrderManager) processFuturesPositions(exch exchange.IBotExchange, posit
}
tick, err := exch.FetchTicker(context.TODO(), position.Pair, position.Asset)
if err != nil {
return fmt.Errorf("%w when fetching ticker data for %v %v %v", err, position.Exchange, position.Asset, position.Pair)
return fmt.Errorf("%w when fetching ticker data for %v %v %v", err, exch.GetName(), position.Asset, position.Pair)
}
_, err = m.UpdateOpenPositionUnrealisedPNL(position.Exchange, position.Asset, position.Pair, tick.Last, tick.LastUpdated)
_, err = m.UpdateOpenPositionUnrealisedPNL(exch.GetName(), position.Asset, position.Pair, tick.Last, tick.LastUpdated)
if err != nil {
return fmt.Errorf("%w when updating unrealised PNL for %v %v %v", err, position.Exchange, position.Asset, position.Pair)
return fmt.Errorf("%w when updating unrealised PNL for %v %v %v", err, exch.GetName(), position.Asset, position.Pair)
}
if !feat.FuturesCapabilities.FundingRates {
return nil
}
isPerp, err := exch.IsPerpetualFutureCurrency(position.Asset, position.Pair)
if err != nil {