mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-31 07:26:44 +00:00
orders: add Filter() method to GetOrdersRequest and force FilteredOrders return type on wrapper functions (#1055)
* orders: filter options return on validate * exchanges: force filter usage by wrapper return type and shift method functionality * exchanges: update tests and fix err shadow * exchanges: shadowland * exchanges: more shadow land spookyness * glorious: nits and ftx upgrade * linter: fix * orders: preserve unpopulated fields through filtering operation * glorious: nits Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -373,20 +374,21 @@ func TestFilterOrdersByType(t *testing.T) {
|
||||
{
|
||||
Type: Limit,
|
||||
},
|
||||
{}, // Unpopulated fields are preserved for API differences
|
||||
}
|
||||
|
||||
FilterOrdersByType(&orders, AnyType)
|
||||
if len(orders) != 2 {
|
||||
if len(orders) != 3 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 2, len(orders))
|
||||
}
|
||||
|
||||
FilterOrdersByType(&orders, Limit)
|
||||
if len(orders) != 1 {
|
||||
if len(orders) != 2 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
|
||||
}
|
||||
|
||||
FilterOrdersByType(&orders, Stop)
|
||||
if len(orders) != 0 {
|
||||
if len(orders) != 1 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 0, len(orders))
|
||||
}
|
||||
}
|
||||
@@ -424,7 +426,7 @@ func TestFilterOrdersBySide(t *testing.T) {
|
||||
{
|
||||
Side: Sell,
|
||||
},
|
||||
{},
|
||||
{}, // Unpopulated fields are preserved for API differences
|
||||
}
|
||||
|
||||
FilterOrdersBySide(&orders, AnySide)
|
||||
@@ -433,12 +435,12 @@ func TestFilterOrdersBySide(t *testing.T) {
|
||||
}
|
||||
|
||||
FilterOrdersBySide(&orders, Buy)
|
||||
if len(orders) != 1 {
|
||||
if len(orders) != 2 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
|
||||
}
|
||||
|
||||
FilterOrdersBySide(&orders, Sell)
|
||||
if len(orders) != 0 {
|
||||
if len(orders) != 1 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 0, len(orders))
|
||||
}
|
||||
}
|
||||
@@ -567,43 +569,44 @@ func TestFilterOrdersByPairs(t *testing.T) {
|
||||
{
|
||||
Pair: currency.NewPair(currency.DOGE, currency.RUB),
|
||||
},
|
||||
{}, // Unpopulated fields are preserved for API differences
|
||||
}
|
||||
|
||||
currencies := []currency.Pair{currency.NewPair(currency.BTC, currency.USD),
|
||||
currency.NewPair(currency.LTC, currency.EUR),
|
||||
currency.NewPair(currency.DOGE, currency.RUB)}
|
||||
FilterOrdersByPairs(&orders, currencies)
|
||||
if len(orders) != 3 {
|
||||
if len(orders) != 4 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 3, len(orders))
|
||||
}
|
||||
|
||||
currencies = []currency.Pair{currency.NewPair(currency.BTC, currency.USD),
|
||||
currency.NewPair(currency.LTC, currency.EUR)}
|
||||
FilterOrdersByPairs(&orders, currencies)
|
||||
if len(orders) != 2 {
|
||||
if len(orders) != 3 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 2, len(orders))
|
||||
}
|
||||
|
||||
currencies = []currency.Pair{currency.NewPair(currency.BTC, currency.USD)}
|
||||
FilterOrdersByPairs(&orders, currencies)
|
||||
if len(orders) != 1 {
|
||||
if len(orders) != 2 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
|
||||
}
|
||||
|
||||
currencies = []currency.Pair{currency.NewPair(currency.USD, currency.BTC)}
|
||||
FilterOrdersByPairs(&orders, currencies)
|
||||
if len(orders) != 1 {
|
||||
if len(orders) != 2 {
|
||||
t.Errorf("Reverse Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
|
||||
}
|
||||
|
||||
currencies = []currency.Pair{}
|
||||
FilterOrdersByPairs(&orders, currencies)
|
||||
if len(orders) != 1 {
|
||||
if len(orders) != 2 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
|
||||
}
|
||||
currencies = append(currencies, currency.EMPTYPAIR)
|
||||
FilterOrdersByPairs(&orders, currencies)
|
||||
if len(orders) != 1 {
|
||||
if len(orders) != 2 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
|
||||
}
|
||||
}
|
||||
@@ -1310,25 +1313,43 @@ func TestValidationOnOrderTypes(t *testing.T) {
|
||||
}
|
||||
|
||||
var getOrders *GetOrdersRequest
|
||||
if getOrders.Validate() != ErrGetOrdersRequestIsNil {
|
||||
t.Fatal("unexpected error")
|
||||
err = getOrders.Validate()
|
||||
if !errors.Is(err, ErrGetOrdersRequestIsNil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, ErrGetOrdersRequestIsNil)
|
||||
}
|
||||
|
||||
getOrders = new(GetOrdersRequest)
|
||||
if getOrders.Validate() == nil {
|
||||
t.Fatal("should error since assetType hasn't been provided")
|
||||
err = getOrders.Validate()
|
||||
if !errors.Is(err, asset.ErrNotSupported) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, asset.ErrNotSupported)
|
||||
}
|
||||
|
||||
if getOrders.Validate(validate.Check(func() error {
|
||||
return errors.New("this should error")
|
||||
})) == nil {
|
||||
t.Fatal("expected error")
|
||||
getOrders.AssetType = asset.Spot
|
||||
err = getOrders.Validate()
|
||||
if !errors.Is(err, errUnrecognisedOrderSide) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, errUnrecognisedOrderSide)
|
||||
}
|
||||
|
||||
if getOrders.Validate(validate.Check(func() error {
|
||||
getOrders.Side = AnySide
|
||||
err = getOrders.Validate()
|
||||
if !errors.Is(err, errUnrecognisedOrderType) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, errUnrecognisedOrderType)
|
||||
}
|
||||
|
||||
var errTestError = errors.New("test error")
|
||||
getOrders.Type = AnyType
|
||||
err = getOrders.Validate(validate.Check(func() error {
|
||||
return errTestError
|
||||
}))
|
||||
if !errors.Is(err, errTestError) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, errTestError)
|
||||
}
|
||||
|
||||
err = getOrders.Validate(validate.Check(func() error {
|
||||
return nil
|
||||
})) == nil {
|
||||
t.Fatal("should output an error since assetType isn't provided")
|
||||
}))
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
var modifyOrder *Modify
|
||||
@@ -1869,3 +1890,58 @@ func TestDeriveCancel(t *testing.T) {
|
||||
t.Fatalf("unexpected values %+v", cancel)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrdersRequest_Filter(t *testing.T) {
|
||||
request := new(GetOrdersRequest)
|
||||
request.AssetType = asset.Spot
|
||||
request.Type = AnyType
|
||||
request.Side = AnySide
|
||||
|
||||
var orders = []Detail{
|
||||
{OrderID: "0", Pair: btcusd, AssetType: asset.Spot, Type: Limit, Side: Buy},
|
||||
{OrderID: "1", Pair: btcusd, AssetType: asset.Spot, Type: Limit, Side: Sell},
|
||||
{OrderID: "2", Pair: btcusd, AssetType: asset.Spot, Type: Market, Side: Buy},
|
||||
{OrderID: "3", Pair: btcusd, AssetType: asset.Spot, Type: Market, Side: Sell},
|
||||
{OrderID: "4", Pair: btcusd, AssetType: asset.Futures, Type: Limit, Side: Buy},
|
||||
{OrderID: "5", Pair: btcusd, AssetType: asset.Futures, Type: Limit, Side: Sell},
|
||||
{OrderID: "6", Pair: btcusd, AssetType: asset.Futures, Type: Market, Side: Buy},
|
||||
{OrderID: "7", Pair: btcusd, AssetType: asset.Futures, Type: Market, Side: Sell},
|
||||
{OrderID: "8", Pair: btcltc, AssetType: asset.Spot, Type: Limit, Side: Buy},
|
||||
{OrderID: "9", Pair: btcltc, AssetType: asset.Spot, Type: Limit, Side: Sell},
|
||||
{OrderID: "10", Pair: btcltc, AssetType: asset.Spot, Type: Market, Side: Buy},
|
||||
{OrderID: "11", Pair: btcltc, AssetType: asset.Spot, Type: Market, Side: Sell},
|
||||
{OrderID: "12", Pair: btcltc, AssetType: asset.Futures, Type: Limit, Side: Buy},
|
||||
{OrderID: "13", Pair: btcltc, AssetType: asset.Futures, Type: Limit, Side: Sell},
|
||||
{OrderID: "14", Pair: btcltc, AssetType: asset.Futures, Type: Market, Side: Buy},
|
||||
{OrderID: "15", Pair: btcltc, AssetType: asset.Futures, Type: Market, Side: Sell},
|
||||
}
|
||||
|
||||
shinyAndClean := request.Filter("test", orders)
|
||||
if len(shinyAndClean) != 16 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(shinyAndClean), 16)
|
||||
}
|
||||
|
||||
for x := range shinyAndClean {
|
||||
if strconv.FormatInt(int64(x), 10) != shinyAndClean[x].OrderID {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", shinyAndClean[x].OrderID, int64(x))
|
||||
}
|
||||
}
|
||||
|
||||
request.Pairs = []currency.Pair{btcltc}
|
||||
|
||||
// Kicks off time error
|
||||
request.EndTime = time.Unix(1336, 0)
|
||||
request.StartTime = time.Unix(1337, 0)
|
||||
|
||||
shinyAndClean = request.Filter("test", orders)
|
||||
|
||||
if len(shinyAndClean) != 8 {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", len(shinyAndClean), 8)
|
||||
}
|
||||
|
||||
for x := range shinyAndClean {
|
||||
if strconv.FormatInt(int64(x)+8, 10) != shinyAndClean[x].OrderID {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", shinyAndClean[x].OrderID, int64(x)+8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,3 +356,8 @@ type ClassificationError struct {
|
||||
OrderID string
|
||||
Err error
|
||||
}
|
||||
|
||||
// FilteredOrders defines orders that have been filtered at the wrapper level
|
||||
// forcing required filter operations when calling method Filter() on
|
||||
// GetOrdersRequest.
|
||||
type FilteredOrders []Detail
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/validate"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -772,7 +773,7 @@ func FilterOrdersBySide(orders *[]Detail, side Side) {
|
||||
|
||||
target := 0
|
||||
for i := range *orders {
|
||||
if (*orders)[i].Side == side {
|
||||
if (*orders)[i].Side == UnknownSide || (*orders)[i].Side == side {
|
||||
(*orders)[target] = (*orders)[i]
|
||||
target++
|
||||
}
|
||||
@@ -789,7 +790,7 @@ func FilterOrdersByType(orders *[]Detail, orderType Type) {
|
||||
|
||||
target := 0
|
||||
for i := range *orders {
|
||||
if (*orders)[i].Type == orderType {
|
||||
if (*orders)[i].Type == UnknownType || (*orders)[i].Type == orderType {
|
||||
(*orders)[target] = (*orders)[i]
|
||||
target++
|
||||
}
|
||||
@@ -834,6 +835,12 @@ func FilterOrdersByPairs(orders *[]Detail, pairs []currency.Pair) {
|
||||
|
||||
target := 0
|
||||
for x := range *orders {
|
||||
if (*orders)[x].Pair.IsEmpty() { // If pair not set then keep
|
||||
(*orders)[target] = (*orders)[x]
|
||||
target++
|
||||
continue
|
||||
}
|
||||
|
||||
for y := range pairs {
|
||||
if (*orders)[x].Pair.EqualIncludeReciprocal(pairs[y]) {
|
||||
(*orders)[target] = (*orders)[x]
|
||||
@@ -1106,14 +1113,25 @@ func (c *Cancel) Validate(opt ...validate.Checker) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate checks internal struct requirements
|
||||
// Validate checks internal struct requirements and returns filter requirement
|
||||
// options for wrapper standardization procedures.
|
||||
func (g *GetOrdersRequest) Validate(opt ...validate.Checker) error {
|
||||
if g == nil {
|
||||
return ErrGetOrdersRequestIsNil
|
||||
}
|
||||
|
||||
if !g.AssetType.IsValid() {
|
||||
return fmt.Errorf("%v %w", g.AssetType, asset.ErrNotSupported)
|
||||
}
|
||||
|
||||
if g.Side == UnknownSide {
|
||||
return errUnrecognisedOrderSide
|
||||
}
|
||||
|
||||
if g.Type == UnknownType {
|
||||
return errUnrecognisedOrderType
|
||||
}
|
||||
|
||||
var errs common.Errors
|
||||
for _, o := range opt {
|
||||
err := o.Check()
|
||||
@@ -1121,13 +1139,26 @@ func (g *GetOrdersRequest) Validate(opt ...validate.Checker) error {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filter reduces slice by optional fields
|
||||
func (g *GetOrdersRequest) Filter(exch string, orders []Detail) FilteredOrders {
|
||||
filtered := make([]Detail, len(orders))
|
||||
copy(filtered, orders)
|
||||
FilterOrdersByPairs(&filtered, g.Pairs)
|
||||
FilterOrdersByType(&filtered, g.Type)
|
||||
FilterOrdersBySide(&filtered, g.Side)
|
||||
err := FilterOrdersByTimeRange(&filtered, g.StartTime, g.EndTime)
|
||||
if err != nil {
|
||||
log.Errorf(log.ExchangeSys, "%s %v", exch, err)
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// Validate checks internal struct requirements
|
||||
func (m *Modify) Validate(opt ...validate.Checker) error {
|
||||
if m == nil {
|
||||
|
||||
Reference in New Issue
Block a user