Files
Scott 85403fe801 exchange/order/limits: Migrate to new package and integrate with exchanges (#1860)
* move limits, transition to key gen

* rollout NewExchangePairAssetKey everywhere

* test improvements

* self-review fixes

* ok, lets go

* fix merge issue

* slower value func,assertify,drop IsValidPairString

* remove binance reference for backtesting test

* Redundant nil checks removed due to redundancy

* Update order_test.go

* Move limits back into /exchanges/

* puts limits in a different box again

* SHAZBERT SPECIAL SUGGESTIONS

* Update gateio_wrapper.go

* fixes all build issues

* Many niteroos!

* something has gone awry

* bugfix

* gk's everywhere nits

* lint

* extra lint

* re-remove IsValidPairString

* lint fix

* standardise test

* revert some bads

* dupe rm

* another revert 360 mcgee

* un-in-revertify

* Update exchange/order/limits/levels_test.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* fix

* Update exchanges/binance/binance_test.go

HERE'S HOPING GITHUB FORMATS THIS CORRECTLY!

Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com>

* update text

* rn func, same line err gk4202000

---------

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com>
2025-08-26 12:30:21 +10:00

119 lines
3.5 KiB
Go

package limits
import (
"fmt"
"time"
"github.com/thrasher-corp/gocryptotrader/common/key"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
// Load loads all limits into private limit holder
func Load(levels []MinMaxLevel) error {
return manager.load(levels)
}
// GetOrderExecutionLimits returns the order limit matching the key
func GetOrderExecutionLimits(k key.ExchangeAssetPair) (MinMaxLevel, error) {
return manager.getOrderExecutionLimits(k)
}
// CheckOrderExecutionLimits is a convenience method to check if the price and amount conforms
// to the exchange order limits
func CheckOrderExecutionLimits(k key.ExchangeAssetPair, price, amount float64, orderType order.Type) error {
return manager.checkOrderExecutionLimits(k, price, amount, orderType)
}
func (e *store) load(levels []MinMaxLevel) error {
if len(levels) == 0 {
return ErrEmptyLevels
}
e.mtx.Lock()
defer e.mtx.Unlock()
if e.epaLimits == nil {
e.epaLimits = make(map[key.ExchangeAssetPair]*MinMaxLevel)
}
for x := range levels {
if levels[x].Key.Exchange == "" {
return fmt.Errorf("cannot load levels for %q %q: %w", levels[x].Key.Asset, levels[x].Key.Pair(), errExchangeNameEmpty)
}
if !levels[x].Key.Asset.IsValid() {
return fmt.Errorf("cannot load levels for %q %q: %w", levels[x].Key.Exchange, levels[x].Key.Pair(), errAssetInvalid)
}
if levels[x].Key.Pair().IsEmpty() {
return fmt.Errorf("cannot load levels for %q %q: %w", levels[x].Key.Exchange, levels[x].Key.Asset, errPairNotSet)
}
if levels[x].MinPrice > 0 &&
levels[x].MaxPrice > 0 &&
levels[x].MinPrice > levels[x].MaxPrice {
return fmt.Errorf("%w for %q %q %q supplied min: %f max: %f",
errInvalidPriceLevels,
levels[x].Key.Exchange,
levels[x].Key.Asset,
levels[x].Key.Pair(),
levels[x].MinPrice,
levels[x].MaxPrice)
}
if levels[x].MinimumBaseAmount > 0 &&
levels[x].MaximumBaseAmount > 0 &&
levels[x].MinimumBaseAmount > levels[x].MaximumBaseAmount {
return fmt.Errorf("%w for %q %q %q supplied min: %f max: %f",
errInvalidAmountLevels,
levels[x].Key.Exchange,
levels[x].Key.Asset,
levels[x].Key.Pair(),
levels[x].MinimumBaseAmount,
levels[x].MaximumBaseAmount)
}
if levels[x].MinimumQuoteAmount > 0 &&
levels[x].MaximumQuoteAmount > 0 &&
levels[x].MinimumQuoteAmount > levels[x].MaximumQuoteAmount {
return fmt.Errorf("%w for %q %q %q supplied min: %f max: %f",
errInvalidQuoteLevels,
levels[x].Key.Exchange,
levels[x].Key.Asset,
levels[x].Key.Pair(),
levels[x].MinimumQuoteAmount,
levels[x].MaximumQuoteAmount)
}
levels[x].UpdatedAt = time.Now()
e.epaLimits[levels[x].Key] = &levels[x]
}
return nil
}
func (e *store) getOrderExecutionLimits(k key.ExchangeAssetPair) (MinMaxLevel, error) {
e.mtx.RLock()
defer e.mtx.RUnlock()
if e.epaLimits == nil {
return MinMaxLevel{}, ErrExchangeLimitNotLoaded
}
el, ok := e.epaLimits[k]
if !ok {
return MinMaxLevel{}, fmt.Errorf("%w for %q %q %q", ErrOrderLimitNotFound, k.Exchange, k.Asset, k.Pair())
}
return *el, nil
}
func (e *store) checkOrderExecutionLimits(k key.ExchangeAssetPair, price, amount float64, orderType order.Type) error {
e.mtx.RLock()
defer e.mtx.RUnlock()
if e.epaLimits == nil {
return ErrExchangeLimitNotLoaded
}
m1, ok := e.epaLimits[k]
if !ok {
return fmt.Errorf("%w for %q %q %q", ErrOrderLimitNotFound, k.Exchange, k.Asset, k.Pair())
}
err := m1.Validate(price, amount, orderType)
if err != nil {
return fmt.Errorf("%w for %q %q %q", err, k.Exchange, k.Asset, k.Pair())
}
return nil
}