order: slight optimizations (#917)

* order: slight optimizations

* orders: add benchmarks, small optimize and change order side to uin8 for comparitive optimizations.

* orders: continue to convert string type -> uint

* orders/backtester: interim move type to orders package, later can expand or deprecate.

* orders: handle errors

* orders: optimize filters and remove error returns when its clearly not needed

* orders: remove log call

* backtester: zero value check

* orders/futures: zero value -> flag

* linter: fix

* linter: more fixes

* linters: rides again

* glorious: nits

* common: Add zero value unix check for time values; also addresses glorious nits

* glorious scott: nits

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
This commit is contained in:
Ryan O'Hara-Reid
2022-05-06 12:27:21 +10:00
committed by GitHub
parent d735effc8e
commit cdcc9630de
60 changed files with 1375 additions and 802 deletions

View File

@@ -422,7 +422,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error {
if p.asset != d.AssetType {
return fmt.Errorf("%w asset '%v' received: '%v'", errOrderNotEqualToTracker, d.AssetType, p.asset)
}
if d.Side == "" {
if d.Side == UnknownSide {
return ErrSideIsInvalid
}
if d.ID == "" {
@@ -473,7 +473,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error {
longSide = longSide.Add(decimal.NewFromFloat(p.longPositions[i].Amount))
}
if p.currentDirection == "" {
if p.currentDirection == UnknownSide {
p.currentDirection = d.Side
}

View File

@@ -8,6 +8,7 @@ import (
"time"
"github.com/gofrs/uuid"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/validate"
@@ -16,6 +17,7 @@ import (
var errValidationCheckFailed = errors.New("validation check failed")
func TestValidate(t *testing.T) {
t.Parallel()
testPair := currency.NewPair(currency.BTC, currency.LTC)
tester := []struct {
ExpectedErr error
@@ -115,7 +117,7 @@ func TestValidate(t *testing.T) {
for x := range tester {
err := tester[x].Submit.Validate(tester[x].ValidOpts)
if !errors.Is(err, tester[x].ExpectedErr) {
t.Errorf("Unexpected result. Got: %v, want: %v", err, tester[x].ExpectedErr)
t.Fatalf("Unexpected result. %d Got: %v, want: %v", x+1, err, tester[x].ExpectedErr)
}
}
}
@@ -140,17 +142,16 @@ func TestOrderSides(t *testing.T) {
func TestOrderTypes(t *testing.T) {
t.Parallel()
var ot Type = "Mo'Money"
if ot.String() != "Mo'Money" {
var ot Type
if ot.String() != "UNKNOWN" {
t.Errorf("unexpected string %s", ot.String())
}
if ot.Lower() != "mo'money" {
if ot.Lower() != "unknown" {
t.Errorf("unexpected string %s", ot.Lower())
}
if ot.Title() != "Mo'Money" {
if ot.Title() != "Unknown" {
t.Errorf("unexpected string %s", ot.Title())
}
}
@@ -267,6 +268,27 @@ func TestFilterOrdersByType(t *testing.T) {
}
}
var filterOrdersByTypeBenchmark = &[]Detail{
{Type: Limit},
{Type: Limit},
{Type: Limit},
{Type: Limit},
{Type: Limit},
{Type: Limit},
{Type: Limit},
{Type: Limit},
{Type: Limit},
{Type: Limit},
}
// 392455 3226 ns/op 15840 B/op 5 allocs/op // PREV
// 9486490 109.5 ns/op 0 B/op 0 allocs/op // CURRENT
func BenchmarkFilterOrdersByType(b *testing.B) {
for x := 0; x < b.N; x++ {
FilterOrdersByType(filterOrdersByTypeBenchmark, Limit)
}
}
func TestFilterOrdersBySide(t *testing.T) {
t.Parallel()
@@ -296,6 +318,27 @@ func TestFilterOrdersBySide(t *testing.T) {
}
}
var filterOrdersBySideBenchmark = &[]Detail{
{Side: Ask},
{Side: Ask},
{Side: Ask},
{Side: Ask},
{Side: Ask},
{Side: Ask},
{Side: Ask},
{Side: Ask},
{Side: Ask},
{Side: Ask},
}
// 372594 3049 ns/op 15840 B/op 5 allocs/op // PREV
// 7412187 148.8 ns/op 0 B/op 0 allocs/op // CURRENT
func BenchmarkFilterOrdersBySide(b *testing.B) {
for x := 0; x < b.N; x++ {
FilterOrdersBySide(filterOrdersBySideBenchmark, Ask)
}
}
func TestFilterOrdersByTimeRange(t *testing.T) {
t.Parallel()
@@ -311,34 +354,78 @@ func TestFilterOrdersByTimeRange(t *testing.T) {
},
}
FilterOrdersByTimeRange(&orders, time.Unix(0, 0), time.Unix(0, 0))
err := FilterOrdersByTimeRange(&orders, time.Unix(0, 0), time.Unix(0, 0))
if err != nil {
t.Fatal(err)
}
if len(orders) != 3 {
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 3, len(orders))
}
FilterOrdersByTimeRange(&orders, time.Unix(100, 0), time.Unix(111, 0))
err = FilterOrdersByTimeRange(&orders, time.Unix(100, 0), time.Unix(111, 0))
if err != nil {
t.Fatal(err)
}
if len(orders) != 3 {
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 3, len(orders))
}
FilterOrdersByTimeRange(&orders, time.Unix(101, 0), time.Unix(111, 0))
err = FilterOrdersByTimeRange(&orders, time.Unix(101, 0), time.Unix(111, 0))
if err != nil {
t.Fatal(err)
}
if len(orders) != 2 {
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 2, len(orders))
}
FilterOrdersByTimeRange(&orders, time.Unix(200, 0), time.Unix(300, 0))
err = FilterOrdersByTimeRange(&orders, time.Unix(200, 0), time.Unix(300, 0))
if err != nil {
t.Fatal(err)
}
if len(orders) != 0 {
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 0, len(orders))
}
orders = append(orders, Detail{})
// test for event no timestamp is set on an order, best to include it
FilterOrdersByTimeRange(&orders, time.Unix(200, 0), time.Unix(300, 0))
err = FilterOrdersByTimeRange(&orders, time.Unix(200, 0), time.Unix(300, 0))
if err != nil {
t.Fatal(err)
}
if len(orders) != 1 {
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
}
err = FilterOrdersByTimeRange(&orders, time.Unix(300, 0), time.Unix(50, 0))
if !errors.Is(err, common.ErrStartAfterEnd) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrStartAfterEnd)
}
}
func TestFilterOrdersByCurrencies(t *testing.T) {
var filterOrdersByTimeRangeBenchmark = &[]Detail{
{Date: time.Unix(100, 0)},
{Date: time.Unix(100, 0)},
{Date: time.Unix(100, 0)},
{Date: time.Unix(100, 0)},
{Date: time.Unix(100, 0)},
{Date: time.Unix(100, 0)},
{Date: time.Unix(100, 0)},
{Date: time.Unix(100, 0)},
{Date: time.Unix(100, 0)},
{Date: time.Unix(100, 0)},
}
// 390822 3335 ns/op 15840 B/op 5 allocs/op // PREV
// 6201034 172.1 ns/op 0 B/op 0 allocs/op // CURRENT
func BenchmarkFilterOrdersByTimeRange(b *testing.B) {
for x := 0; x < b.N; x++ {
err := FilterOrdersByTimeRange(filterOrdersByTimeRangeBenchmark, time.Unix(50, 0), time.Unix(150, 0))
if err != nil {
b.Fatal(err)
}
}
}
func TestFilterOrdersByPairs(t *testing.T) {
t.Parallel()
var orders = []Detail{
@@ -356,42 +443,64 @@ func TestFilterOrdersByCurrencies(t *testing.T) {
currencies := []currency.Pair{currency.NewPair(currency.BTC, currency.USD),
currency.NewPair(currency.LTC, currency.EUR),
currency.NewPair(currency.DOGE, currency.RUB)}
FilterOrdersByCurrencies(&orders, currencies)
FilterOrdersByPairs(&orders, currencies)
if len(orders) != 3 {
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)}
FilterOrdersByCurrencies(&orders, currencies)
FilterOrdersByPairs(&orders, currencies)
if len(orders) != 2 {
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 2, len(orders))
}
currencies = []currency.Pair{currency.NewPair(currency.BTC, currency.USD)}
FilterOrdersByCurrencies(&orders, currencies)
FilterOrdersByPairs(&orders, currencies)
if len(orders) != 1 {
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
}
currencies = []currency.Pair{currency.NewPair(currency.USD, currency.BTC)}
FilterOrdersByCurrencies(&orders, currencies)
FilterOrdersByPairs(&orders, currencies)
if len(orders) != 1 {
t.Errorf("Reverse Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
}
currencies = []currency.Pair{}
FilterOrdersByCurrencies(&orders, currencies)
FilterOrdersByPairs(&orders, currencies)
if len(orders) != 1 {
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
}
currencies = append(currencies, currency.EMPTYPAIR)
FilterOrdersByCurrencies(&orders, currencies)
FilterOrdersByPairs(&orders, currencies)
if len(orders) != 1 {
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
}
}
var filterOrdersByPairsBenchmark = &[]Detail{
{Pair: currency.NewPair(currency.BTC, currency.USD)},
{Pair: currency.NewPair(currency.BTC, currency.USD)},
{Pair: currency.NewPair(currency.BTC, currency.USD)},
{Pair: currency.NewPair(currency.BTC, currency.USD)},
{Pair: currency.NewPair(currency.BTC, currency.USD)},
{Pair: currency.NewPair(currency.BTC, currency.USD)},
{Pair: currency.NewPair(currency.BTC, currency.USD)},
{Pair: currency.NewPair(currency.BTC, currency.USD)},
{Pair: currency.NewPair(currency.BTC, currency.USD)},
{Pair: currency.NewPair(currency.BTC, currency.USD)},
}
// 400032 2977 ns/op 15840 B/op 5 allocs/op // PREV
// 6977242 172.8 ns/op 0 B/op 0 allocs/op // CURRENT
func BenchmarkFilterOrdersByPairs(b *testing.B) {
pairs := []currency.Pair{currency.NewPair(currency.BTC, currency.USD)}
for x := 0; x < b.N; x++ {
FilterOrdersByPairs(filterOrdersByPairsBenchmark, pairs)
}
}
func TestSortOrdersByPrice(t *testing.T) {
t.Parallel()
@@ -546,41 +655,39 @@ func TestSortOrdersByOrderType(t *testing.T) {
}
}
var stringsToOrderSide = []struct {
in string
out Side
err error
}{
{"buy", Buy, nil},
{"BUY", Buy, nil},
{"bUy", Buy, nil},
{"sell", Sell, nil},
{"SELL", Sell, nil},
{"sElL", Sell, nil},
{"bid", Bid, nil},
{"BID", Bid, nil},
{"bId", Bid, nil},
{"ask", Ask, nil},
{"ASK", Ask, nil},
{"aSk", Ask, nil},
{"lOnG", Long, nil},
{"ShoRt", Short, nil},
{"any", AnySide, nil},
{"ANY", AnySide, nil},
{"aNy", AnySide, nil},
{"woahMan", Buy, errors.New("WOAHMAN not recognised as order side")},
}
func TestStringToOrderSide(t *testing.T) {
for i := range stringsToOrderSide {
testData := &stringsToOrderSide[i]
cases := []struct {
in string
out Side
err error
}{
{"buy", Buy, nil},
{"BUY", Buy, nil},
{"bUy", Buy, nil},
{"sell", Sell, nil},
{"SELL", Sell, nil},
{"sElL", Sell, nil},
{"bid", Bid, nil},
{"BID", Bid, nil},
{"bId", Bid, nil},
{"ask", Ask, nil},
{"ASK", Ask, nil},
{"aSk", Ask, nil},
{"lOnG", Long, nil},
{"ShoRt", Short, nil},
{"any", AnySide, nil},
{"ANY", AnySide, nil},
{"aNy", AnySide, nil},
{"woahMan", UnknownSide, errUnrecognisedOrderSide},
}
for i := range cases {
testData := &cases[i]
t.Run(testData.in, func(t *testing.T) {
out, err := StringToOrderSide(testData.in)
if err != nil {
if err.Error() != testData.err.Error() {
t.Error("Unexpected error", err)
}
} else if out != testData.out {
if !errors.Is(err, testData.err) {
t.Fatalf("received: '%v' but expected: '%v'", err, testData.err)
}
if out != testData.out {
t.Errorf("Unexpected output %v. Expected %v", out, testData.out)
}
})
@@ -597,53 +704,51 @@ func BenchmarkStringToOrderSide(b *testing.B) {
}
}
var stringsToOrderType = []struct {
in string
out Type
err error
}{
{"limit", Limit, nil},
{"LIMIT", Limit, nil},
{"lImIt", Limit, nil},
{"market", Market, nil},
{"MARKET", Market, nil},
{"mArKeT", Market, nil},
{"immediate_or_cancel", ImmediateOrCancel, nil},
{"IMMEDIATE_OR_CANCEL", ImmediateOrCancel, nil},
{"iMmEdIaTe_Or_CaNcEl", ImmediateOrCancel, nil},
{"iMmEdIaTe Or CaNcEl", ImmediateOrCancel, nil},
{"stop", Stop, nil},
{"STOP", Stop, nil},
{"sToP", Stop, nil},
{"sToP LiMit", StopLimit, nil},
{"ExchangE sToP Limit", StopLimit, nil},
{"trailing_stop", TrailingStop, nil},
{"TRAILING_STOP", TrailingStop, nil},
{"tRaIlInG_sToP", TrailingStop, nil},
{"tRaIlInG sToP", TrailingStop, nil},
{"fOk", FillOrKill, nil},
{"exchange fOk", FillOrKill, nil},
{"ios", IOS, nil},
{"post_ONly", PostOnly, nil},
{"any", AnyType, nil},
{"ANY", AnyType, nil},
{"aNy", AnyType, nil},
{"trigger", Trigger, nil},
{"TRIGGER", Trigger, nil},
{"tRiGgEr", Trigger, nil},
{"woahMan", UnknownType, errors.New("WOAHMAN not recognised as order type")},
}
func TestStringToOrderType(t *testing.T) {
for i := range stringsToOrderType {
testData := &stringsToOrderType[i]
cases := []struct {
in string
out Type
err error
}{
{"limit", Limit, nil},
{"LIMIT", Limit, nil},
{"lImIt", Limit, nil},
{"market", Market, nil},
{"MARKET", Market, nil},
{"mArKeT", Market, nil},
{"immediate_or_cancel", ImmediateOrCancel, nil},
{"IMMEDIATE_OR_CANCEL", ImmediateOrCancel, nil},
{"iMmEdIaTe_Or_CaNcEl", ImmediateOrCancel, nil},
{"iMmEdIaTe Or CaNcEl", ImmediateOrCancel, nil},
{"stop", Stop, nil},
{"STOP", Stop, nil},
{"sToP", Stop, nil},
{"sToP LiMit", StopLimit, nil},
{"ExchangE sToP Limit", StopLimit, nil},
{"trailing_stop", TrailingStop, nil},
{"TRAILING_STOP", TrailingStop, nil},
{"tRaIlInG_sToP", TrailingStop, nil},
{"tRaIlInG sToP", TrailingStop, nil},
{"fOk", FillOrKill, nil},
{"exchange fOk", FillOrKill, nil},
{"ios", IOS, nil},
{"post_ONly", PostOnly, nil},
{"any", AnyType, nil},
{"ANY", AnyType, nil},
{"aNy", AnyType, nil},
{"trigger", Trigger, nil},
{"TRIGGER", Trigger, nil},
{"tRiGgEr", Trigger, nil},
{"woahMan", UnknownType, errUnrecognisedOrderType},
}
for i := range cases {
testData := &cases[i]
t.Run(testData.in, func(t *testing.T) {
out, err := StringToOrderType(testData.in)
if err != nil {
if err.Error() != testData.err.Error() {
t.Error("Unexpected error", err)
}
} else if out != testData.out {
if !errors.Is(err, testData.err) {
t.Fatalf("received: '%v' but expected: '%v'", err, testData.err)
}
if out != testData.out {
t.Errorf("Unexpected output %v. Expected %v", out, testData.out)
}
})
@@ -705,7 +810,8 @@ var stringsToOrderStatus = []struct {
{"partially canceLLed", PartiallyCancelled, nil},
{"opeN", Open, nil},
{"cLosEd", Closed, nil},
{"woahMan", UnknownStatus, errors.New("WOAHMAN not recognised as order status")},
{"cancellinG", Cancelling, nil},
{"woahMan", UnknownStatus, errUnrecognisedOrderStatus},
}
func TestStringToOrderStatus(t *testing.T) {
@@ -713,11 +819,10 @@ func TestStringToOrderStatus(t *testing.T) {
testData := &stringsToOrderStatus[i]
t.Run(testData.in, func(t *testing.T) {
out, err := StringToOrderStatus(testData.in)
if err != nil {
if err.Error() != testData.err.Error() {
t.Error("Unexpected error", err)
}
} else if out != testData.out {
if !errors.Is(err, testData.err) {
t.Fatalf("received: '%v' but expected: '%v'", err, testData.err)
}
if out != testData.out {
t.Errorf("Unexpected output %v. Expected %v", out, testData.out)
}
})
@@ -736,34 +841,7 @@ func BenchmarkStringToOrderStatus(b *testing.B) {
func TestUpdateOrderFromModify(t *testing.T) {
var leet = "1337"
od := Detail{
ImmediateOrCancel: false,
HiddenOrder: false,
FillOrKill: false,
PostOnly: false,
Leverage: 0,
Price: 0,
Amount: 0,
LimitPriceUpper: 0,
LimitPriceLower: 0,
TriggerPrice: 0,
QuoteAmount: 0,
ExecutedAmount: 0,
RemainingAmount: 0,
Fee: 0,
Exchange: "",
ID: "1",
AccountID: "",
ClientID: "",
WalletAddress: "",
Type: "",
Side: "",
Status: "",
Date: time.Time{},
LastUpdated: time.Time{},
Pair: currency.EMPTYPAIR,
Trades: nil,
}
od := Detail{ID: "1"}
updated := time.Now()
pair, err := currency.NewPairFromString("BTCUSD")
@@ -792,9 +870,9 @@ func TestUpdateOrderFromModify(t *testing.T) {
AccountID: "1",
ClientID: "1",
WalletAddress: "1",
Type: "1",
Side: "1",
Status: "1",
Type: 1,
Side: 1,
Status: 1,
AssetType: 1,
LastUpdated: updated,
Pair: pair,
@@ -859,13 +937,13 @@ func TestUpdateOrderFromModify(t *testing.T) {
if od.WalletAddress != "1" {
t.Error("Failed to update")
}
if od.Type != "1" {
if od.Type != 1 {
t.Error("Failed to update")
}
if od.Side != "1" {
if od.Side != 1 {
t.Error("Failed to update")
}
if od.Status != "1" {
if od.Status != 1 {
t.Error("Failed to update")
}
if od.AssetType != 1 {
@@ -927,34 +1005,7 @@ func TestUpdateOrderFromModify(t *testing.T) {
func TestUpdateOrderFromDetail(t *testing.T) {
var leet = "1337"
od := Detail{
ImmediateOrCancel: false,
HiddenOrder: false,
FillOrKill: false,
PostOnly: false,
Leverage: 0,
Price: 0,
Amount: 0,
LimitPriceUpper: 0,
LimitPriceLower: 0,
TriggerPrice: 0,
QuoteAmount: 0,
ExecutedAmount: 0,
RemainingAmount: 0,
Fee: 0,
Exchange: "test",
ID: "",
AccountID: "",
ClientID: "",
WalletAddress: "",
Type: "",
Side: "",
Status: "",
Date: time.Time{},
LastUpdated: time.Time{},
Pair: currency.EMPTYPAIR,
Trades: nil,
}
od := Detail{Exchange: "test"}
updated := time.Now()
pair, err := currency.NewPairFromString("BTCUSD")
@@ -983,9 +1034,9 @@ func TestUpdateOrderFromDetail(t *testing.T) {
AccountID: "1",
ClientID: "1",
WalletAddress: "1",
Type: "1",
Side: "1",
Status: "1",
Type: 1,
Side: 1,
Status: 1,
AssetType: 1,
LastUpdated: updated,
Pair: pair,
@@ -1050,13 +1101,13 @@ func TestUpdateOrderFromDetail(t *testing.T) {
if od.WalletAddress != "1" {
t.Error("Failed to update")
}
if od.Type != "1" {
if od.Type != 1 {
t.Error("Failed to update")
}
if od.Side != "1" {
if od.Side != 1 {
t.Error("Failed to update")
}
if od.Status != "1" {
if od.Status != 1 {
t.Error("Failed to update")
}
if od.AssetType != 1 {
@@ -1373,7 +1424,8 @@ func TestIsActive(t *testing.T) {
o Detail
expRes bool
}{
0: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: AnyStatus}, true},
// For now force inactive on any status
0: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: AnyStatus}, false},
1: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: New}, true},
2: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Active}, true},
3: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: PartiallyCancelled}, false},
@@ -1386,7 +1438,8 @@ func TestIsActive(t *testing.T) {
10: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Rejected}, false},
11: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Expired}, false},
12: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Hidden}, true},
13: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: UnknownStatus}, true},
// For now force inactive on unknown status
13: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: UnknownStatus}, false},
14: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Open}, true},
15: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: AutoDeleverage}, true},
16: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Closed}, false},
@@ -1395,12 +1448,24 @@ func TestIsActive(t *testing.T) {
// specific tests
for num, tt := range statusTests {
if tt.o.IsActive() != tt.expRes {
t.Errorf("statusTests[%v] failed", num)
t.Fatalf("statusTests[%v] failed", num)
}
}
}
func TestIsInctive(t *testing.T) {
var activeBenchmark = Detail{Status: Pending, Amount: 1}
// 610732089 2.414 ns/op 0 B/op 0 allocs/op // PREV
// 1000000000 1.188 ns/op 0 B/op 0 allocs/op // CURRENT
func BenchmarkIsActive(b *testing.B) {
for x := 0; x < b.N; x++ {
if !activeBenchmark.IsActive() {
b.Fatal("expected true")
}
}
}
func TestIsInactive(t *testing.T) {
orders := map[int]Detail{
0: {Amount: 0.0, Status: Active},
1: {Amount: 1.0, ExecutedAmount: 0.9, Status: Active},
@@ -1428,7 +1493,8 @@ func TestIsInctive(t *testing.T) {
o Detail
expRes bool
}{
0: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: AnyStatus}, false},
// For now force inactive on any status
0: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: AnyStatus}, true},
1: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: New}, false},
2: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Active}, false},
3: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: PartiallyCancelled}, true},
@@ -1441,7 +1507,8 @@ func TestIsInctive(t *testing.T) {
10: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Rejected}, true},
11: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Expired}, true},
12: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Hidden}, false},
13: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: UnknownStatus}, false},
// For now force inactive on unknown status
13: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: UnknownStatus}, true},
14: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Open}, false},
15: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: AutoDeleverage}, false},
16: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Closed}, true},
@@ -1455,6 +1522,17 @@ func TestIsInctive(t *testing.T) {
}
}
var inactiveBenchmark = Detail{Status: Closed, Amount: 1}
// 1000000000 1.043 ns/op 0 B/op 0 allocs/op // CURRENT
func BenchmarkIsInactive(b *testing.B) {
for x := 0; x < b.N; x++ {
if !inactiveBenchmark.IsInactive() {
b.Fatal("expected true")
}
}
}
func TestGenerateInternalOrderID(t *testing.T) {
id, err := uuid.NewV4()
if err != nil {

View File

@@ -242,66 +242,73 @@ type GetOrdersRequest struct {
}
// Status defines order status types
type Status string
type Status uint32
// All order status types
const (
AnyStatus Status = "ANY"
New Status = "NEW"
Active Status = "ACTIVE"
PartiallyCancelled Status = "PARTIALLY_CANCELLED"
PartiallyFilled Status = "PARTIALLY_FILLED"
Filled Status = "FILLED"
Cancelled Status = "CANCELLED"
PendingCancel Status = "PENDING_CANCEL"
InsufficientBalance Status = "INSUFFICIENT_BALANCE"
MarketUnavailable Status = "MARKET_UNAVAILABLE"
Rejected Status = "REJECTED"
Expired Status = "EXPIRED"
Hidden Status = "HIDDEN"
UnknownStatus Status = "UNKNOWN"
Open Status = "OPEN"
AutoDeleverage Status = "ADL"
Closed Status = "CLOSED"
Pending Status = "PENDING"
UnknownStatus Status = 0
AnyStatus Status = 1 << iota
New
Active
PartiallyCancelled
PartiallyFilled
Filled
Cancelled
PendingCancel
InsufficientBalance
MarketUnavailable
Rejected
Expired
Hidden
Open
AutoDeleverage
Closed
Pending
Cancelling
)
// Type enforces a standard for order types across the code base
type Type string
type Type uint16
// Defined package order types
const (
AnyType Type = "ANY"
Limit Type = "LIMIT"
Market Type = "MARKET"
PostOnly Type = "POST_ONLY"
ImmediateOrCancel Type = "IMMEDIATE_OR_CANCEL"
Stop Type = "STOP"
StopLimit Type = "STOP LIMIT"
StopMarket Type = "STOP MARKET"
TakeProfit Type = "TAKE PROFIT"
TakeProfitMarket Type = "TAKE PROFIT MARKET"
TrailingStop Type = "TRAILING_STOP"
FillOrKill Type = "FOK"
IOS Type = "IOS"
UnknownType Type = "UNKNOWN"
Liquidation Type = "LIQUIDATION"
Trigger Type = "TRIGGER"
UnknownType Type = 0
Limit Type = 1 << iota
Market
PostOnly
ImmediateOrCancel
Stop
StopLimit
StopMarket
TakeProfit
TakeProfitMarket
TrailingStop
FillOrKill
IOS
AnyType
Liquidation
Trigger
)
// Side enforces a standard for order sides across the code base
type Side string
type Side uint16
// Order side types
const (
AnySide Side = "ANY"
Buy Side = "BUY"
Sell Side = "SELL"
Bid Side = "BID"
Ask Side = "ASK"
UnknownSide Side = "UNKNOWN"
Long Side = "LONG"
Short Side = "SHORT"
UnknownSide Side = 0
Buy Side = 1 << iota
Sell
Bid
Ask
AnySide
Long
Short
// Backtester signal types
DoNothing
TransferredFunds
CouldNotBuy
CouldNotSell
MissingData
)
// ByPrice used for sorting orders by price

View File

@@ -14,7 +14,22 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/validate"
)
var errTimeInForceConflict = errors.New("multiple time in force options applied")
const (
orderSubmissionValidSides = Buy | Sell | Bid | Ask | Long | Short
shortSide = Short | Sell | Ask
longSide = Long | Buy | Bid
inactiveStatuses = Filled | Cancelled | InsufficientBalance | MarketUnavailable | Rejected | PartiallyCancelled | Expired | Closed | AnyStatus | Cancelling
activeStatuses = Active | Open | PartiallyFilled | New | PendingCancel | Hidden | AutoDeleverage | Pending
bypassSideFilter = UnknownSide | AnySide
bypassTypeFilter = UnknownType | AnyType
)
var (
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")
)
// Validate checks the supplied data and returns whether or not it's valid
func (s *Submit) Validate(opt ...validate.Checker) error {
@@ -30,10 +45,7 @@ func (s *Submit) Validate(opt ...validate.Checker) error {
return ErrAssetNotSet
}
if s.Side != Buy &&
s.Side != Sell &&
s.Side != Bid &&
s.Side != Ask {
if s.Side == UnknownSide || orderSubmissionValidSides&s.Side != s.Side {
return ErrSideIsInvalid
}
@@ -145,15 +157,15 @@ func (d *Detail) UpdateOrderFromDetail(m *Detail) {
d.WalletAddress = m.WalletAddress
updated = true
}
if m.Type != "" && m.Type != d.Type {
if m.Type != UnknownType && m.Type != d.Type {
d.Type = m.Type
updated = true
}
if m.Side != "" && m.Side != d.Side {
if m.Side != UnknownSide && m.Side != d.Side {
d.Side = m.Side
updated = true
}
if m.Status != "" && m.Status != d.Status {
if m.Status != UnknownStatus && m.Status != d.Status {
d.Status = m.Status
updated = true
}
@@ -161,53 +173,51 @@ func (d *Detail) UpdateOrderFromDetail(m *Detail) {
d.AssetType = m.AssetType
updated = true
}
if m.Trades != nil {
for x := range m.Trades {
var found bool
for y := range d.Trades {
if d.Trades[y].TID != m.Trades[x].TID {
continue
}
found = true
if d.Trades[y].Fee != m.Trades[x].Fee {
d.Trades[y].Fee = m.Trades[x].Fee
updated = true
}
if m.Trades[x].Price != 0 && d.Trades[y].Price != m.Trades[x].Price {
d.Trades[y].Price = m.Trades[x].Price
updated = true
}
if d.Trades[y].Side != m.Trades[x].Side {
d.Trades[y].Side = m.Trades[x].Side
updated = true
}
if d.Trades[y].Type != m.Trades[x].Type {
d.Trades[y].Type = m.Trades[x].Type
updated = true
}
if d.Trades[y].Description != m.Trades[x].Description {
d.Trades[y].Description = m.Trades[x].Description
updated = true
}
if m.Trades[x].Amount != 0 && d.Trades[y].Amount != m.Trades[x].Amount {
d.Trades[y].Amount = m.Trades[x].Amount
updated = true
}
if d.Trades[y].Timestamp != m.Trades[x].Timestamp {
d.Trades[y].Timestamp = m.Trades[x].Timestamp
updated = true
}
if d.Trades[y].IsMaker != m.Trades[x].IsMaker {
d.Trades[y].IsMaker = m.Trades[x].IsMaker
updated = true
}
for x := range m.Trades {
var found bool
for y := range d.Trades {
if d.Trades[y].TID != m.Trades[x].TID {
continue
}
if !found {
d.Trades = append(d.Trades, m.Trades[x])
found = true
if d.Trades[y].Fee != m.Trades[x].Fee {
d.Trades[y].Fee = m.Trades[x].Fee
updated = true
}
if m.Trades[x].Price != 0 && d.Trades[y].Price != m.Trades[x].Price {
d.Trades[y].Price = m.Trades[x].Price
updated = true
}
if d.Trades[y].Side != m.Trades[x].Side {
d.Trades[y].Side = m.Trades[x].Side
updated = true
}
if d.Trades[y].Type != m.Trades[x].Type {
d.Trades[y].Type = m.Trades[x].Type
updated = true
}
if d.Trades[y].Description != m.Trades[x].Description {
d.Trades[y].Description = m.Trades[x].Description
updated = true
}
if m.Trades[x].Amount != 0 && d.Trades[y].Amount != m.Trades[x].Amount {
d.Trades[y].Amount = m.Trades[x].Amount
updated = true
}
if d.Trades[y].Timestamp != m.Trades[x].Timestamp {
d.Trades[y].Timestamp = m.Trades[x].Timestamp
updated = true
}
if d.Trades[y].IsMaker != m.Trades[x].IsMaker {
d.Trades[y].IsMaker = m.Trades[x].IsMaker
updated = true
}
m.RemainingAmount -= m.Trades[x].Amount
}
if !found {
d.Trades = append(d.Trades, m.Trades[x])
updated = true
}
m.RemainingAmount -= m.Trades[x].Amount
}
if m.RemainingAmount > 0 && m.RemainingAmount != d.RemainingAmount {
d.RemainingAmount = m.RemainingAmount
@@ -309,15 +319,15 @@ func (d *Detail) UpdateOrderFromModify(m *Modify) {
d.WalletAddress = m.WalletAddress
updated = true
}
if m.Type != "" && m.Type != d.Type {
if m.Type != UnknownType && m.Type != d.Type {
d.Type = m.Type
updated = true
}
if m.Side != "" && m.Side != d.Side {
if m.Side != UnknownSide && m.Side != d.Side {
d.Side = m.Side
updated = true
}
if m.Status != "" && m.Status != d.Status {
if m.Status != UnknownStatus && m.Status != d.Status {
d.Status = m.Status
updated = true
}
@@ -325,53 +335,51 @@ func (d *Detail) UpdateOrderFromModify(m *Modify) {
d.AssetType = m.AssetType
updated = true
}
if m.Trades != nil {
for x := range m.Trades {
var found bool
for y := range d.Trades {
if d.Trades[y].TID != m.Trades[x].TID {
continue
}
found = true
if d.Trades[y].Fee != m.Trades[x].Fee {
d.Trades[y].Fee = m.Trades[x].Fee
updated = true
}
if m.Trades[x].Price != 0 && d.Trades[y].Price != m.Trades[x].Price {
d.Trades[y].Price = m.Trades[x].Price
updated = true
}
if d.Trades[y].Side != m.Trades[x].Side {
d.Trades[y].Side = m.Trades[x].Side
updated = true
}
if d.Trades[y].Type != m.Trades[x].Type {
d.Trades[y].Type = m.Trades[x].Type
updated = true
}
if d.Trades[y].Description != m.Trades[x].Description {
d.Trades[y].Description = m.Trades[x].Description
updated = true
}
if m.Trades[x].Amount != 0 && d.Trades[y].Amount != m.Trades[x].Amount {
d.Trades[y].Amount = m.Trades[x].Amount
updated = true
}
if d.Trades[y].Timestamp != m.Trades[x].Timestamp {
d.Trades[y].Timestamp = m.Trades[x].Timestamp
updated = true
}
if d.Trades[y].IsMaker != m.Trades[x].IsMaker {
d.Trades[y].IsMaker = m.Trades[x].IsMaker
updated = true
}
for x := range m.Trades {
var found bool
for y := range d.Trades {
if d.Trades[y].TID != m.Trades[x].TID {
continue
}
if !found {
d.Trades = append(d.Trades, m.Trades[x])
found = true
if d.Trades[y].Fee != m.Trades[x].Fee {
d.Trades[y].Fee = m.Trades[x].Fee
updated = true
}
if m.Trades[x].Price != 0 && d.Trades[y].Price != m.Trades[x].Price {
d.Trades[y].Price = m.Trades[x].Price
updated = true
}
if d.Trades[y].Side != m.Trades[x].Side {
d.Trades[y].Side = m.Trades[x].Side
updated = true
}
if d.Trades[y].Type != m.Trades[x].Type {
d.Trades[y].Type = m.Trades[x].Type
updated = true
}
if d.Trades[y].Description != m.Trades[x].Description {
d.Trades[y].Description = m.Trades[x].Description
updated = true
}
if m.Trades[x].Amount != 0 && d.Trades[y].Amount != m.Trades[x].Amount {
d.Trades[y].Amount = m.Trades[x].Amount
updated = true
}
if d.Trades[y].Timestamp != m.Trades[x].Timestamp {
d.Trades[y].Timestamp = m.Trades[x].Timestamp
updated = true
}
if d.Trades[y].IsMaker != m.Trades[x].IsMaker {
d.Trades[y].IsMaker = m.Trades[x].IsMaker
updated = true
}
m.RemainingAmount -= m.Trades[x].Amount
}
if !found {
d.Trades = append(d.Trades, m.Trades[x])
updated = true
}
m.RemainingAmount -= m.Trades[x].Amount
}
if m.RemainingAmount > 0 && m.RemainingAmount != d.RemainingAmount {
d.RemainingAmount = m.RemainingAmount
@@ -401,13 +409,13 @@ func (d *Detail) MatchFilter(f *Filter) bool {
if f.ID != "" && d.ID != f.ID {
return false
}
if f.Type != "" && f.Type != AnyType && d.Type != f.Type {
if f.Type != UnknownType && f.Type != AnyType && d.Type != f.Type {
return false
}
if f.Side != "" && f.Side != AnySide && d.Side != f.Side {
if f.Side != UnknownSide && f.Side != AnySide && d.Side != f.Side {
return false
}
if f.Status != "" && f.Status != AnyStatus && d.Status != f.Status {
if f.Status != UnknownStatus && f.Status != AnyStatus && d.Status != f.Status {
return false
}
if f.ClientOrderID != "" && d.ClientOrderID != f.ClientOrderID {
@@ -428,25 +436,21 @@ func (d *Detail) MatchFilter(f *Filter) bool {
return true
}
// IsActive returns true if an order has a status that indicates it is
// currently available on the exchange
// IsActive returns true if an order has a status that indicates it is currently
// available on the exchange
func (d *Detail) IsActive() bool {
if d.Amount <= 0 || d.Amount <= d.ExecutedAmount {
return false
}
return d.Status == Active || d.Status == Open || d.Status == PartiallyFilled || d.Status == New ||
d.Status == AnyStatus || d.Status == PendingCancel || d.Status == Hidden || d.Status == UnknownStatus ||
d.Status == AutoDeleverage || d.Status == Pending
return d.Status != UnknownStatus &&
d.Amount > 0 &&
d.Amount > d.ExecutedAmount &&
activeStatuses&d.Status == d.Status
}
// IsInactive returns true if an order has a status that indicates it is
// currently not available on the exchange
func (d *Detail) IsInactive() bool {
if d.Amount <= 0 || d.Amount <= d.ExecutedAmount {
return true
}
return d.Status == Filled || d.Status == Cancelled || d.Status == InsufficientBalance || d.Status == MarketUnavailable ||
d.Status == Rejected || d.Status == PartiallyCancelled || d.Status == Expired || d.Status == Closed
return d.Amount <= 0 ||
d.Amount <= d.ExecutedAmount ||
inactiveStatuses&d.Status == d.Status
}
// GenerateInternalOrderID sets a new V4 order ID or a V5 order ID if
@@ -474,50 +478,151 @@ func (d *Detail) Copy() Detail {
// String implements the stringer interface
func (t Type) String() string {
return string(t)
switch t {
case AnyType:
return "ANY"
case Limit:
return "LIMIT"
case Market:
return "MARKET"
case PostOnly:
return "POST_ONLY"
case ImmediateOrCancel:
return "IMMEDIATE_OR_CANCEL"
case Stop:
return "STOP"
case StopLimit:
return "STOP LIMIT"
case StopMarket:
return "STOP MARKET"
case TakeProfit:
return "TAKE PROFIT"
case TakeProfitMarket:
return "TAKE PROFIT MARKET"
case TrailingStop:
return "TRAILING_STOP"
case FillOrKill:
return "FOK"
case IOS:
return "IOS"
case Liquidation:
return "LIQUIDATION"
case Trigger:
return "TRIGGER"
default:
return "UNKNOWN"
}
}
// Lower returns the type lower case string
func (t Type) Lower() string {
return strings.ToLower(string(t))
return strings.ToLower(t.String())
}
// Title returns the type titleized, eg "Limit"
func (t Type) Title() string {
return strings.Title(strings.ToLower(string(t))) // nolint:staticcheck // Ignore Title usage warning
return strings.Title(strings.ToLower(t.String())) // nolint:staticcheck // Ignore Title usage warning
}
// String implements the stringer interface
func (s Side) String() string {
return string(s)
switch s {
case Buy:
return "BUY"
case Sell:
return "SELL"
case Bid:
return "BID"
case Ask:
return "ASK"
case Long:
return "LONG"
case Short:
return "SHORT"
case AnySide:
return "ANY"
// Backtester signal types below.
case DoNothing:
return "DO NOTHING"
case TransferredFunds:
return "TRANSFERRED FUNDS"
case CouldNotBuy:
return "COULD NOT BUY"
case CouldNotSell:
return "COULD NOT SELL"
case MissingData:
return "MISSING DATA"
default:
return "UNKNOWN"
}
}
// Lower returns the side lower case string
func (s Side) Lower() string {
return strings.ToLower(string(s))
return strings.ToLower(s.String())
}
// Title returns the side titleized, eg "Buy"
func (s Side) Title() string {
return strings.Title(strings.ToLower(string(s))) // nolint:staticcheck // Ignore Title usage warning
return strings.Title(strings.ToLower(s.String())) // nolint:staticcheck // Ignore Title usage warning
}
// IsShort returns if the side is short
func (s Side) IsShort() bool {
return s == Short || s == Sell
return s != UnknownSide && shortSide&s == s
}
// IsLong returns if the side is long
func (s Side) IsLong() bool {
return s == Long || s == Buy
return s != UnknownSide && longSide&s == s
}
// String implements the stringer interface
func (s Status) String() string {
return string(s)
switch s {
case AnyStatus:
return "ANY"
case New:
return "NEW"
case Active:
return "ACTIVE"
case PartiallyCancelled:
return "PARTIALLY_CANCELLED"
case PartiallyFilled:
return "PARTIALLY_FILLED"
case Filled:
return "FILLED"
case Cancelled:
return "CANCELLED"
case PendingCancel:
return "PENDING_CANCEL"
case InsufficientBalance:
return "INSUFFICIENT_BALANCE"
case MarketUnavailable:
return "MARKET_UNAVAILABLE"
case Rejected:
return "REJECTED"
case Expired:
return "EXPIRED"
case Hidden:
return "HIDDEN"
case Open:
return "OPEN"
case AutoDeleverage:
return "ADL"
case Closed:
return "CLOSED"
case Pending:
return "PENDING"
case Cancelling:
return "CANCELLING"
default:
return "UNKNOWN"
}
}
// InferCostsAndTimes infer order costs using execution information and times when available
// InferCostsAndTimes infer order costs using execution information and times
// when available
func (d *Detail) InferCostsAndTimes() {
if d.CostAsset.IsEmpty() {
d.CostAsset = d.Pair.Quote
@@ -547,83 +652,86 @@ func (d *Detail) InferCostsAndTimes() {
}
}
// FilterOrdersBySide removes any order details that don't match the
// order status provided
// FilterOrdersBySide removes any order details that don't match the order
// status provided
func FilterOrdersBySide(orders *[]Detail, side Side) {
if side == "" || side == AnySide {
if bypassSideFilter&side == side || len(*orders) == 0 {
return
}
var filteredOrders []Detail
target := 0
for i := range *orders {
if strings.EqualFold(string((*orders)[i].Side), string(side)) {
filteredOrders = append(filteredOrders, (*orders)[i])
if (*orders)[i].Side == side {
(*orders)[target] = (*orders)[i]
target++
}
}
*orders = filteredOrders
*orders = (*orders)[:target]
}
// FilterOrdersByType removes any order details that don't match the order type
// provided
func FilterOrdersByType(orders *[]Detail, orderType Type) {
if orderType == "" || orderType == AnyType {
if bypassTypeFilter&orderType == orderType || len(*orders) == 0 {
return
}
var filteredOrders []Detail
target := 0
for i := range *orders {
if strings.EqualFold(string((*orders)[i].Type), string(orderType)) {
filteredOrders = append(filteredOrders, (*orders)[i])
if (*orders)[i].Type == orderType {
(*orders)[target] = (*orders)[i]
target++
}
}
*orders = filteredOrders
*orders = (*orders)[:target]
}
// FilterOrdersByTimeRange removes any OrderDetails outside of the time range
func FilterOrdersByTimeRange(orders *[]Detail, startTime, endTime time.Time) {
if startTime.IsZero() ||
endTime.IsZero() ||
startTime.Unix() == 0 ||
endTime.Unix() == 0 ||
endTime.Before(startTime) {
return
func FilterOrdersByTimeRange(orders *[]Detail, startTime, endTime time.Time) error {
if len(*orders) == 0 {
return nil
}
var filteredOrders []Detail
if err := common.StartEndTimeCheck(startTime, endTime); err != nil {
if errors.Is(err, common.ErrDateUnset) {
return nil
}
return fmt.Errorf("cannot filter orders by time range %w", err)
}
target := 0
for i := range *orders {
if ((*orders)[i].Date.Unix() >= startTime.Unix() && (*orders)[i].Date.Unix() <= endTime.Unix()) ||
(*orders)[i].Date.IsZero() {
filteredOrders = append(filteredOrders, (*orders)[i])
(*orders)[target] = (*orders)[i]
target++
}
}
*orders = filteredOrders
*orders = (*orders)[:target]
return nil
}
// FilterOrdersByCurrencies removes any order details that do not match the
// provided currency list. It is forgiving in that the provided currencies can
// match quote or base currencies
func FilterOrdersByCurrencies(orders *[]Detail, currencies []currency.Pair) {
if len(currencies) == 0 {
return
}
if len(currencies) == 1 && currencies[0].IsEmpty() {
// FilterOrdersByPairs removes any order details that do not match the
// provided currency pairs list. It is forgiving in that the provided pairs can
// match quote or base pairs
func FilterOrdersByPairs(orders *[]Detail, pairs []currency.Pair) {
if len(pairs) == 0 ||
(len(pairs) == 1 && pairs[0].IsEmpty()) ||
len(*orders) == 0 {
return
}
var filteredOrders []Detail
for i := range *orders {
for _, c := range currencies {
if (*orders)[i].Pair.EqualIncludeReciprocal(c) {
filteredOrders = append(filteredOrders, (*orders)[i])
target := 0
for x := range *orders {
for y := range pairs {
if (*orders)[x].Pair.EqualIncludeReciprocal(pairs[y]) {
(*orders)[target] = (*orders)[x]
target++
break
}
}
}
*orders = filteredOrders
*orders = (*orders)[:target]
}
func (b ByPrice) Len() int {
@@ -735,23 +843,23 @@ func SortOrdersBySide(orders *[]Detail, reverse bool) {
// and returning a real Side
func StringToOrderSide(side string) (Side, error) {
side = strings.ToUpper(side)
switch Side(side) {
case Buy:
switch side {
case Buy.String():
return Buy, nil
case Sell:
case Sell.String():
return Sell, nil
case Bid:
case Bid.String():
return Bid, nil
case Ask:
case Ask.String():
return Ask, nil
case Long:
case Long.String():
return Long, nil
case Short:
case Short.String():
return Short, nil
case AnySide:
case AnySide.String():
return AnySide, nil
default:
return UnknownSide, errors.New(side + " not recognised as order side")
return UnknownSide, fmt.Errorf("'%s' %w", side, errUnrecognisedOrderSide)
}
}
@@ -783,7 +891,7 @@ func StringToOrderType(oType string) (Type, error) {
case Trigger.String():
return Trigger, nil
default:
return UnknownType, errors.New(oType + " not recognised as order type")
return UnknownType, fmt.Errorf("'%v' %w", oType, errUnrecognisedOrderType)
}
}
@@ -822,8 +930,10 @@ func StringToOrderStatus(status string) (Status, error) {
return InsufficientBalance, nil
case MarketUnavailable.String():
return MarketUnavailable, nil
case Cancelling.String():
return Cancelling, nil
default:
return UnknownStatus, errors.New(status + " not recognised as order status")
return UnknownStatus, fmt.Errorf("'%s' %w", status, errUnrecognisedOrderStatus)
}
}
@@ -891,7 +1001,7 @@ func (g *GetOrdersRequest) Validate(opt ...validate.Checker) error {
return ErrGetOrdersRequestIsNil
}
if !g.AssetType.IsValid() {
return fmt.Errorf("assetType %v not supported", g.AssetType)
return fmt.Errorf("%v %w", g.AssetType, asset.ErrNotSupported)
}
var errs common.Errors
for _, o := range opt {
@@ -917,7 +1027,7 @@ func (m *Modify) Validate(opt ...validate.Checker) error {
return ErrPairIsEmpty
}
if m.AssetType.String() == "" {
if m.AssetType == asset.Empty {
return ErrAssetNotSet
}