diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index e05126eb..487d6dc8 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -592,7 +592,29 @@ func (b *BTCMarkets) SubmitOrder(ctx context.Context, s *order.Submit) (*order.S if err != nil { return nil, err } - return s.DeriveSubmitResponse(tempResp.OrderID) + + submitResp, err := s.DeriveSubmitResponse(tempResp.OrderID) + if err != nil { + return nil, err + } + + if tempResp.Amount != 0 { + err = submitResp.AdjustBaseAmount(tempResp.Amount) + if err != nil { + log.Errorf(log.ExchangeSys, "Exchange %s: OrderID: %s base amount conversion error: %s\n", b.Name, submitResp.OrderID, err) + } + } + + if tempResp.TargetAmount != 0 { + err = submitResp.AdjustQuoteAmount(tempResp.TargetAmount) + if err != nil { + log.Errorf(log.ExchangeSys, "Exchange %s: OrderID: %s quote amount conversion error: %s\n", b.Name, submitResp.OrderID, err) + } + } + // With market orders the price is optional, so we can set it to the + // actual price that was filled. + submitResp.Price = tempResp.Price + return submitResp, nil } // ModifyOrder will allow of changing orderbook placement and limit to diff --git a/exchanges/order/order_test.go b/exchanges/order/order_test.go index 499b02ba..0547e242 100644 --- a/exchanges/order/order_test.go +++ b/exchanges/order/order_test.go @@ -1260,11 +1260,11 @@ func TestUpdateOrderFromDetail(t *testing.T) { func TestClassificationError_Error(t *testing.T) { class := ClassificationError{OrderID: "1337", Exchange: "test", Err: errors.New("test error")} - if class.Error() != "test - OrderID: 1337 classification error: test error" { + if class.Error() != "Exchange test: OrderID: 1337 classification error: test error" { t.Fatal("unexpected output") } class.OrderID = "" - if class.Error() != "test - classification error: test error" { + if class.Error() != "Exchange test: classification error: test error" { t.Fatal("unexpected output") } } @@ -1959,3 +1959,75 @@ func TestIsValidOrderSubmissionSide(t *testing.T) { t.Error("expected false") } } + +func TestAdjustBaseAmount(t *testing.T) { + t.Parallel() + + var s *SubmitResponse + err := s.AdjustBaseAmount(0) + if !errors.Is(err, errOrderSubmitResponseIsNil) { + t.Fatalf("received: '%v' but expected: '%v'", err, errOrderSubmitResponseIsNil) + } + + s = &SubmitResponse{} + err = s.AdjustBaseAmount(0) + if !errors.Is(err, errAmountIsZero) { + t.Fatalf("received: '%v' but expected: '%v'", err, errAmountIsZero) + } + + s.Amount = 1.7777777777 + err = s.AdjustBaseAmount(1.7777777777) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if s.Amount != 1.7777777777 { + t.Fatalf("received: '%v' but expected: '%v'", s.Amount, 1.7777777777) + } + + s.Amount = 1.7777777777 + err = s.AdjustBaseAmount(1.777) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if s.Amount != 1.777 { + t.Fatalf("received: '%v' but expected: '%v'", s.Amount, 1.777) + } +} + +func TestAdjustQuoteAmount(t *testing.T) { + t.Parallel() + + var s *SubmitResponse + err := s.AdjustQuoteAmount(0) + if !errors.Is(err, errOrderSubmitResponseIsNil) { + t.Fatalf("received: '%v' but expected: '%v'", err, errOrderSubmitResponseIsNil) + } + + s = &SubmitResponse{} + err = s.AdjustQuoteAmount(0) + if !errors.Is(err, errAmountIsZero) { + t.Fatalf("received: '%v' but expected: '%v'", err, errAmountIsZero) + } + + s.QuoteAmount = 5.222222222222 + err = s.AdjustQuoteAmount(5.222222222222) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if s.QuoteAmount != 5.222222222222 { + t.Fatalf("received: '%v' but expected: '%v'", s.Amount, 5.222222222222) + } + + s.QuoteAmount = 5.222222222222 + err = s.AdjustQuoteAmount(5.22222222) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if s.QuoteAmount != 5.22222222 { + t.Fatalf("received: '%v' but expected: '%v'", s.Amount, 5.22222222) + } +} diff --git a/exchanges/order/orders.go b/exchanges/order/orders.go index 7644239d..92bcac18 100644 --- a/exchanges/order/orders.go +++ b/exchanges/order/orders.go @@ -38,6 +38,7 @@ var ( errOrderSubmitIsNil = errors.New("order submit is nil") errOrderSubmitResponseIsNil = errors.New("order submit response is nil") errOrderDetailIsNil = errors.New("order detail is nil") + errAmountIsZero = errors.New("amount is zero") ) // IsValidOrderSubmissionSide validates that the order side is a valid submission direction @@ -475,6 +476,64 @@ func (s *Submit) DeriveSubmitResponse(orderID string) (*SubmitResponse, error) { }, nil } +// AdjustBaseAmount will adjust the base amount of a submit response if the +// exchange has modified the amount. This is usually due to decimal place +// restrictions or rounding. This will return an error if the amount is zero +// or the submit response is nil. +func (s *SubmitResponse) AdjustBaseAmount(a float64) error { + if s == nil { + return errOrderSubmitResponseIsNil + } + + if a <= 0 { + return errAmountIsZero + } + + if s.Amount == a { + return nil + } + + // Warning because amounts should conform to exchange requirements prior to + // call but this is not fatal. + log.Warnf(log.ExchangeSys, "Exchange %s: has adjusted OrderID: %s requested base amount from %v to %v", + s.Exchange, + s.OrderID, + s.Amount, + a) + + s.Amount = a + return nil +} + +// AdjustQuoteAmount will adjust the quote amount of a submit response if the +// exchange has modified the amount. This is usually due to decimal place +// restrictions or rounding. This will return an error if the amount is zero +// or the submit response is nil. +func (s *SubmitResponse) AdjustQuoteAmount(a float64) error { + if s == nil { + return errOrderSubmitResponseIsNil + } + + if a <= 0 { + return errAmountIsZero + } + + if s.QuoteAmount == a { + return nil + } + + // Warning because amounts should conform to exchange requirements prior to + // call but this is not fatal. + log.Warnf(log.ExchangeSys, "Exchange %s: has adjusted OrderID: %s requested quote amount from %v to %v", + s.Exchange, + s.OrderID, + s.Amount, + a) + + s.QuoteAmount = a + return nil +} + // DeriveDetail will construct an order detail when a successful submission // has occurred. Has an optional parameter field internal uuid for internal // management. @@ -1069,12 +1128,12 @@ func StringToOrderStatus(status string) (Status, error) { func (o *ClassificationError) Error() string { if o.OrderID != "" { - return fmt.Sprintf("%s - OrderID: %s classification error: %v", + return fmt.Sprintf("Exchange %s: OrderID: %s classification error: %v", o.Exchange, o.OrderID, o.Err) } - return fmt.Sprintf("%s - classification error: %v", + return fmt.Sprintf("Exchange %s: classification error: %v", o.Exchange, o.Err) }