mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-02 07:26:53 +00:00
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:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user