mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-21 23:16:49 +00:00
committed by
Adrian Gallagher
parent
242b02c382
commit
63a9e9fcb1
48
exchanges/order/README.md
Normal file
48
exchanges/order/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# GoCryptoTrader package Orders
|
||||
|
||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
|
||||
|
||||
|
||||
[](https://travis-ci.org/thrasher-corp/gocryptotrader)
|
||||
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
||||
[](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/orders)
|
||||
[](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
|
||||
|
||||
|
||||
This orders package is part of the GoCryptoTrader codebase.
|
||||
|
||||
## This is still in active development
|
||||
|
||||
You can track ideas, planned features and what's in progresss on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
|
||||
|
||||
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk)
|
||||
|
||||
## Current Features for orders
|
||||
|
||||
+ This package services the exchanges package with order handling.
|
||||
- Creation of order
|
||||
- Deletion of order
|
||||
- Order tracking
|
||||
|
||||
### Please click GoDocs chevron above to view current GoDoc information for this package
|
||||
|
||||
## Contribution
|
||||
|
||||
Please feel free to submit any pull requests or suggest any desired features to be added.
|
||||
|
||||
When submitting a PR, please abide by our coding guidelines:
|
||||
|
||||
+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
|
||||
+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
|
||||
+ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md).
|
||||
+ Pull requests need to be based on and opened against the `master` branch.
|
||||
|
||||
## Donations
|
||||
|
||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
||||
|
||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||
|
||||
***1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB***
|
||||
|
||||
400
exchanges/order/order_test.go
Normal file
400
exchanges/order/order_test.go
Normal file
@@ -0,0 +1,400 @@
|
||||
package order
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
)
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
testPair := currency.NewPair(currency.BTC, currency.LTC)
|
||||
tester := []struct {
|
||||
Pair currency.Pair
|
||||
Side
|
||||
Type
|
||||
Amount float64
|
||||
Price float64
|
||||
ExpectedErr error
|
||||
}{
|
||||
{
|
||||
ExpectedErr: ErrPairIsEmpty,
|
||||
}, // empty pair
|
||||
{
|
||||
Pair: testPair,
|
||||
ExpectedErr: ErrSideIsInvalid,
|
||||
}, // valid pair but invalid order side
|
||||
{
|
||||
Pair: testPair,
|
||||
Side: Buy,
|
||||
ExpectedErr: ErrTypeIsInvalid,
|
||||
}, // valid pair and order side but invalid order type
|
||||
{
|
||||
Pair: testPair,
|
||||
Side: Sell,
|
||||
ExpectedErr: ErrTypeIsInvalid,
|
||||
}, // valid pair and order side but invalid order type
|
||||
{
|
||||
Pair: testPair,
|
||||
Side: Bid,
|
||||
ExpectedErr: ErrTypeIsInvalid,
|
||||
}, // valid pair and order side but invalid order type
|
||||
{
|
||||
Pair: testPair,
|
||||
Side: Ask,
|
||||
ExpectedErr: ErrTypeIsInvalid,
|
||||
}, // valid pair and order side but invalid order type
|
||||
{
|
||||
Pair: testPair,
|
||||
Side: Ask,
|
||||
Type: Market,
|
||||
ExpectedErr: ErrAmountIsInvalid,
|
||||
}, // valid pair, order side, type but invalid amount
|
||||
{
|
||||
Pair: testPair,
|
||||
Side: Ask,
|
||||
Type: Limit,
|
||||
Amount: 1,
|
||||
ExpectedErr: ErrPriceMustBeSetIfLimitOrder,
|
||||
}, // valid pair, order side, type, amount but invalid price
|
||||
{
|
||||
Pair: testPair,
|
||||
Side: Ask,
|
||||
Type: Limit,
|
||||
Amount: 1,
|
||||
Price: 1000,
|
||||
ExpectedErr: nil,
|
||||
}, // valid order!
|
||||
}
|
||||
|
||||
for x := range tester {
|
||||
s := Submit{
|
||||
Pair: tester[x].Pair,
|
||||
OrderSide: tester[x].Side,
|
||||
OrderType: tester[x].Type,
|
||||
Amount: tester[x].Amount,
|
||||
Price: tester[x].Price,
|
||||
}
|
||||
if err := s.Validate(); err != tester[x].ExpectedErr {
|
||||
t.Errorf("Unexpected result. Got: %s, want: %s", err, tester[x].ExpectedErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderSides(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var os = Buy
|
||||
if os.String() != "BUY" {
|
||||
t.Errorf("unexpected string %s", os.String())
|
||||
}
|
||||
|
||||
if os.Lower() != "buy" {
|
||||
t.Errorf("unexpected string %s", os.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderTypes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var ot Type = "Mo'Money"
|
||||
|
||||
if ot.String() != "Mo'Money" {
|
||||
t.Errorf("unexpected string %s", ot.String())
|
||||
}
|
||||
|
||||
if ot.Lower() != "mo'money" {
|
||||
t.Errorf("unexpected string %s", ot.Lower())
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterOrdersByType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var orders = []Detail{
|
||||
{
|
||||
OrderType: ImmediateOrCancel,
|
||||
},
|
||||
{
|
||||
OrderType: Limit,
|
||||
},
|
||||
}
|
||||
|
||||
FilterOrdersByType(&orders, AnyType)
|
||||
if len(orders) != 2 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 2, len(orders))
|
||||
}
|
||||
|
||||
FilterOrdersByType(&orders, Limit)
|
||||
if len(orders) != 1 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
|
||||
}
|
||||
|
||||
FilterOrdersByType(&orders, Stop)
|
||||
if len(orders) != 0 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 0, len(orders))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterOrdersBySide(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var orders = []Detail{
|
||||
{
|
||||
OrderSide: Buy,
|
||||
},
|
||||
{
|
||||
OrderSide: Sell,
|
||||
},
|
||||
{},
|
||||
}
|
||||
|
||||
FilterOrdersBySide(&orders, AnySide)
|
||||
if len(orders) != 3 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 3, len(orders))
|
||||
}
|
||||
|
||||
FilterOrdersBySide(&orders, Buy)
|
||||
if len(orders) != 1 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
|
||||
}
|
||||
|
||||
FilterOrdersBySide(&orders, Sell)
|
||||
if len(orders) != 0 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 0, len(orders))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterOrdersByTickRange(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var orders = []Detail{
|
||||
{
|
||||
OrderDate: time.Unix(100, 0),
|
||||
},
|
||||
{
|
||||
OrderDate: time.Unix(110, 0),
|
||||
},
|
||||
{
|
||||
OrderDate: time.Unix(111, 0),
|
||||
},
|
||||
}
|
||||
|
||||
FilterOrdersByTickRange(&orders, time.Unix(0, 0), time.Unix(0, 0))
|
||||
if len(orders) != 3 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 3, len(orders))
|
||||
}
|
||||
|
||||
FilterOrdersByTickRange(&orders, time.Unix(100, 0), time.Unix(111, 0))
|
||||
if len(orders) != 3 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 3, len(orders))
|
||||
}
|
||||
|
||||
FilterOrdersByTickRange(&orders, time.Unix(101, 0), time.Unix(111, 0))
|
||||
if len(orders) != 2 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 2, len(orders))
|
||||
}
|
||||
|
||||
FilterOrdersByTickRange(&orders, time.Unix(200, 0), time.Unix(300, 0))
|
||||
if len(orders) != 0 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 0, len(orders))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterOrdersByCurrencies(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var orders = []Detail{
|
||||
{
|
||||
CurrencyPair: currency.NewPair(currency.BTC, currency.USD),
|
||||
},
|
||||
{
|
||||
CurrencyPair: currency.NewPair(currency.LTC, currency.EUR),
|
||||
},
|
||||
{
|
||||
CurrencyPair: currency.NewPair(currency.DOGE, currency.RUB),
|
||||
},
|
||||
}
|
||||
|
||||
currencies := []currency.Pair{currency.NewPair(currency.BTC, currency.USD),
|
||||
currency.NewPair(currency.LTC, currency.EUR),
|
||||
currency.NewPair(currency.DOGE, currency.RUB)}
|
||||
FilterOrdersByCurrencies(&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)
|
||||
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)
|
||||
if len(orders) != 1 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
|
||||
}
|
||||
|
||||
currencies = []currency.Pair{}
|
||||
FilterOrdersByCurrencies(&orders, currencies)
|
||||
if len(orders) != 1 {
|
||||
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortOrdersByPrice(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
orders := []Detail{
|
||||
{
|
||||
Price: 100,
|
||||
}, {
|
||||
Price: 0,
|
||||
}, {
|
||||
Price: 50,
|
||||
},
|
||||
}
|
||||
|
||||
SortOrdersByPrice(&orders, false)
|
||||
if orders[0].Price != 0 {
|
||||
t.Errorf("Expected: '%v', received: '%v'", 0, orders[0].Price)
|
||||
}
|
||||
|
||||
SortOrdersByPrice(&orders, true)
|
||||
if orders[0].Price != 100 {
|
||||
t.Errorf("Expected: '%v', received: '%v'", 100, orders[0].Price)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortOrdersByDate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
orders := []Detail{
|
||||
{
|
||||
OrderDate: time.Unix(0, 0),
|
||||
}, {
|
||||
OrderDate: time.Unix(1, 0),
|
||||
}, {
|
||||
OrderDate: time.Unix(2, 0),
|
||||
},
|
||||
}
|
||||
|
||||
SortOrdersByDate(&orders, false)
|
||||
if orders[0].OrderDate.Unix() != time.Unix(0, 0).Unix() {
|
||||
t.Errorf("Expected: '%v', received: '%v'",
|
||||
time.Unix(0, 0).Unix(),
|
||||
orders[0].OrderDate.Unix())
|
||||
}
|
||||
|
||||
SortOrdersByDate(&orders, true)
|
||||
if orders[0].OrderDate.Unix() != time.Unix(2, 0).Unix() {
|
||||
t.Errorf("Expected: '%v', received: '%v'",
|
||||
time.Unix(2, 0).Unix(),
|
||||
orders[0].OrderDate.Unix())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortOrdersByCurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
orders := []Detail{
|
||||
{
|
||||
CurrencyPair: currency.NewPairWithDelimiter(currency.BTC.String(),
|
||||
currency.USD.String(),
|
||||
"-"),
|
||||
}, {
|
||||
CurrencyPair: currency.NewPairWithDelimiter(currency.DOGE.String(),
|
||||
currency.USD.String(),
|
||||
"-"),
|
||||
}, {
|
||||
CurrencyPair: currency.NewPairWithDelimiter(currency.BTC.String(),
|
||||
currency.RUB.String(),
|
||||
"-"),
|
||||
}, {
|
||||
CurrencyPair: currency.NewPairWithDelimiter(currency.LTC.String(),
|
||||
currency.EUR.String(),
|
||||
"-"),
|
||||
}, {
|
||||
CurrencyPair: currency.NewPairWithDelimiter(currency.LTC.String(),
|
||||
currency.AUD.String(),
|
||||
"-"),
|
||||
},
|
||||
}
|
||||
|
||||
SortOrdersByCurrency(&orders, false)
|
||||
if orders[0].CurrencyPair.String() != currency.BTC.String()+"-"+currency.RUB.String() {
|
||||
t.Errorf("Expected: '%v', received: '%v'",
|
||||
currency.BTC.String()+"-"+currency.RUB.String(),
|
||||
orders[0].CurrencyPair.String())
|
||||
}
|
||||
|
||||
SortOrdersByCurrency(&orders, true)
|
||||
if orders[0].CurrencyPair.String() != currency.LTC.String()+"-"+currency.EUR.String() {
|
||||
t.Errorf("Expected: '%v', received: '%v'",
|
||||
currency.LTC.String()+"-"+currency.EUR.String(),
|
||||
orders[0].CurrencyPair.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortOrdersByOrderSide(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
orders := []Detail{
|
||||
{
|
||||
OrderSide: Buy,
|
||||
}, {
|
||||
OrderSide: Sell,
|
||||
}, {
|
||||
OrderSide: Sell,
|
||||
}, {
|
||||
OrderSide: Buy,
|
||||
},
|
||||
}
|
||||
|
||||
SortOrdersBySide(&orders, false)
|
||||
if !strings.EqualFold(orders[0].OrderSide.String(), Buy.String()) {
|
||||
t.Errorf("Expected: '%v', received: '%v'",
|
||||
Buy,
|
||||
orders[0].OrderSide)
|
||||
}
|
||||
|
||||
SortOrdersBySide(&orders, true)
|
||||
if !strings.EqualFold(orders[0].OrderSide.String(), Sell.String()) {
|
||||
t.Errorf("Expected: '%v', received: '%v'",
|
||||
Sell,
|
||||
orders[0].OrderSide)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortOrdersByOrderType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
orders := []Detail{
|
||||
{
|
||||
OrderType: Market,
|
||||
}, {
|
||||
OrderType: Limit,
|
||||
}, {
|
||||
OrderType: ImmediateOrCancel,
|
||||
}, {
|
||||
OrderType: TrailingStop,
|
||||
},
|
||||
}
|
||||
|
||||
SortOrdersByType(&orders, false)
|
||||
if !strings.EqualFold(orders[0].OrderType.String(), ImmediateOrCancel.String()) {
|
||||
t.Errorf("Expected: '%v', received: '%v'",
|
||||
ImmediateOrCancel,
|
||||
orders[0].OrderType)
|
||||
}
|
||||
|
||||
SortOrdersByType(&orders, true)
|
||||
if !strings.EqualFold(orders[0].OrderType.String(), TrailingStop.String()) {
|
||||
t.Errorf("Expected: '%v', received: '%v'",
|
||||
TrailingStop,
|
||||
orders[0].OrderType)
|
||||
}
|
||||
}
|
||||
190
exchanges/order/order_types.go
Normal file
190
exchanges/order/order_types.go
Normal file
@@ -0,0 +1,190 @@
|
||||
package order
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
)
|
||||
|
||||
const (
|
||||
limitOrder = iota
|
||||
marketOrder
|
||||
)
|
||||
|
||||
// Orders variable holds an array of pointers to order structs
|
||||
var Orders []*Order
|
||||
|
||||
// Order struct holds order values
|
||||
type Order struct {
|
||||
OrderID int
|
||||
Exchange string
|
||||
Type int
|
||||
Amount float64
|
||||
Price float64
|
||||
}
|
||||
|
||||
// vars related to orders
|
||||
var (
|
||||
ErrSubmissionIsNil = errors.New("order submission is nil")
|
||||
ErrPairIsEmpty = errors.New("order pair is empty")
|
||||
ErrSideIsInvalid = errors.New("order side is invalid")
|
||||
ErrTypeIsInvalid = errors.New("order type is invalid")
|
||||
ErrAmountIsInvalid = errors.New("order amount is invalid")
|
||||
ErrPriceMustBeSetIfLimitOrder = errors.New("order price must be set if limit order type is desired")
|
||||
)
|
||||
|
||||
// Submit contains the order submission data
|
||||
type Submit struct {
|
||||
Pair currency.Pair
|
||||
OrderType Type
|
||||
OrderSide Side
|
||||
Price float64
|
||||
Amount float64
|
||||
ClientID string
|
||||
}
|
||||
|
||||
// SubmitResponse is what is returned after submitting an order to an exchange
|
||||
type SubmitResponse struct {
|
||||
IsOrderPlaced bool
|
||||
OrderID string
|
||||
}
|
||||
|
||||
// Modify is an order modifyer
|
||||
type Modify struct {
|
||||
OrderID string
|
||||
Type
|
||||
Side
|
||||
Price float64
|
||||
Amount float64
|
||||
LimitPriceUpper float64
|
||||
LimitPriceLower float64
|
||||
CurrencyPair currency.Pair
|
||||
ImmediateOrCancel bool
|
||||
HiddenOrder bool
|
||||
FillOrKill bool
|
||||
PostOnly bool
|
||||
}
|
||||
|
||||
// ModifyResponse is an order modifying return type
|
||||
type ModifyResponse struct {
|
||||
OrderID string
|
||||
}
|
||||
|
||||
// CancelAllResponse returns the status from attempting to cancel all orders on
|
||||
// an exchagne
|
||||
type CancelAllResponse struct {
|
||||
Status map[string]string
|
||||
}
|
||||
|
||||
// Type enforces a standard for order types across the code base
|
||||
type Type string
|
||||
|
||||
// Defined package order types
|
||||
const (
|
||||
AnyType Type = "ANY"
|
||||
Limit Type = "LIMIT"
|
||||
Market Type = "MARKET"
|
||||
ImmediateOrCancel Type = "IMMEDIATE_OR_CANCEL"
|
||||
Stop Type = "STOP"
|
||||
TrailingStop Type = "TRAILINGSTOP"
|
||||
Unknown Type = "UNKNOWN"
|
||||
)
|
||||
|
||||
// Side enforces a standard for order sides across the code base
|
||||
type Side string
|
||||
|
||||
// Order side types
|
||||
const (
|
||||
AnySide Side = "ANY"
|
||||
Buy Side = "BUY"
|
||||
Sell Side = "SELL"
|
||||
Bid Side = "BID"
|
||||
Ask Side = "ASK"
|
||||
)
|
||||
|
||||
// Detail holds order detail data
|
||||
type Detail struct {
|
||||
Exchange string
|
||||
AccountID string
|
||||
ID string
|
||||
CurrencyPair currency.Pair
|
||||
OrderSide Side
|
||||
OrderType Type
|
||||
OrderDate time.Time
|
||||
Status
|
||||
Price float64
|
||||
Amount float64
|
||||
ExecutedAmount float64
|
||||
RemainingAmount float64
|
||||
Fee float64
|
||||
Trades []TradeHistory
|
||||
}
|
||||
|
||||
// TradeHistory holds exchange history data
|
||||
type TradeHistory struct {
|
||||
Timestamp time.Time
|
||||
TID int64
|
||||
Price float64
|
||||
Amount float64
|
||||
Exchange string
|
||||
Type
|
||||
Side
|
||||
Fee float64
|
||||
Description string
|
||||
}
|
||||
|
||||
// Cancel type required when requesting to cancel an order
|
||||
type Cancel struct {
|
||||
AccountID string
|
||||
OrderID string
|
||||
CurrencyPair currency.Pair
|
||||
AssetType asset.Item
|
||||
WalletAddress string
|
||||
Side
|
||||
}
|
||||
|
||||
// GetOrdersRequest used for GetOrderHistory and GetOpenOrders wrapper functions
|
||||
type GetOrdersRequest struct {
|
||||
OrderType Type
|
||||
OrderSide Side
|
||||
StartTicks time.Time
|
||||
EndTicks time.Time
|
||||
// Currencies Empty array = all currencies. Some endpoints only support
|
||||
// singular currency enquiries
|
||||
Currencies []currency.Pair
|
||||
}
|
||||
|
||||
// Status defines order status types
|
||||
type Status string
|
||||
|
||||
// All order status types
|
||||
const (
|
||||
AnyStatus Status = "ANY"
|
||||
New Status = "NEW"
|
||||
Active Status = "ACTIVE"
|
||||
PartiallyFilled Status = "PARTIALLY_FILLED"
|
||||
Filled Status = "FILLED"
|
||||
Cancelled Status = "CANCELED"
|
||||
PendingCancel Status = "PENDING_CANCEL"
|
||||
Rejected Status = "REJECTED"
|
||||
Expired Status = "EXPIRED"
|
||||
Hidden Status = "HIDDEN"
|
||||
UnknownStatus Status = "UNKNOWN"
|
||||
)
|
||||
|
||||
// ByPrice used for sorting orders by price
|
||||
type ByPrice []Detail
|
||||
|
||||
// ByOrderType used for sorting orders by order type
|
||||
type ByOrderType []Detail
|
||||
|
||||
// ByCurrency used for sorting orders by order currency
|
||||
type ByCurrency []Detail
|
||||
|
||||
// ByDate used for sorting orders by order date
|
||||
type ByDate []Detail
|
||||
|
||||
// ByOrderSide used for sorting orders by order side (buy sell)
|
||||
type ByOrderSide []Detail
|
||||
302
exchanges/order/orders.go
Normal file
302
exchanges/order/orders.go
Normal file
@@ -0,0 +1,302 @@
|
||||
package order
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
)
|
||||
|
||||
// NewOrder creates a new order and returns a an orderID
|
||||
func NewOrder(exchangeName string, amount, price float64) int {
|
||||
order := &Order{}
|
||||
if len(Orders) == 0 {
|
||||
order.OrderID = 0
|
||||
} else {
|
||||
order.OrderID = len(Orders)
|
||||
}
|
||||
|
||||
order.Exchange = exchangeName
|
||||
order.Amount = amount
|
||||
order.Price = price
|
||||
Orders = append(Orders, order)
|
||||
return order.OrderID
|
||||
}
|
||||
|
||||
// DeleteOrder deletes orders by ID and returns state
|
||||
func DeleteOrder(orderID int) bool {
|
||||
for i := range Orders {
|
||||
if Orders[i].OrderID == orderID {
|
||||
Orders = append(Orders[:i], Orders[i+1:]...)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetOrdersByExchange returns order pointer grouped by exchange
|
||||
func GetOrdersByExchange(exchange string) []*Order {
|
||||
var orders []*Order
|
||||
for i := range Orders {
|
||||
if Orders[i].Exchange == exchange {
|
||||
orders = append(orders, Orders[i])
|
||||
}
|
||||
}
|
||||
if len(orders) > 0 {
|
||||
return orders
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOrderByOrderID returns order pointer by ID
|
||||
func GetOrderByOrderID(orderID int) *Order {
|
||||
for i := range Orders {
|
||||
if Orders[i].OrderID == orderID {
|
||||
return Orders[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate checks the supplied data and returns whether or not it's valid
|
||||
func (s *Submit) Validate() error {
|
||||
if s == nil {
|
||||
return ErrSubmissionIsNil
|
||||
}
|
||||
|
||||
if s.Pair.IsEmpty() {
|
||||
return ErrPairIsEmpty
|
||||
}
|
||||
|
||||
if s.OrderSide != Buy &&
|
||||
s.OrderSide != Sell &&
|
||||
s.OrderSide != Bid &&
|
||||
s.OrderSide != Ask {
|
||||
return ErrSideIsInvalid
|
||||
}
|
||||
|
||||
if s.OrderType != Market && s.OrderType != Limit {
|
||||
return ErrTypeIsInvalid
|
||||
}
|
||||
|
||||
if s.Amount <= 0 {
|
||||
return ErrAmountIsInvalid
|
||||
}
|
||||
|
||||
if s.OrderType == Limit && s.Price <= 0 {
|
||||
return ErrPriceMustBeSetIfLimitOrder
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String implements the stringer interface
|
||||
func (t Type) String() string {
|
||||
return string(t)
|
||||
}
|
||||
|
||||
// Lower returns the type lower case string
|
||||
func (t Type) Lower() string {
|
||||
return strings.ToLower(string(t))
|
||||
}
|
||||
|
||||
// String implements the stringer interface
|
||||
func (s Side) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// Lower returns the side lower case string
|
||||
func (s Side) Lower() string {
|
||||
return strings.ToLower(string(s))
|
||||
}
|
||||
|
||||
// String implements the stringer interface
|
||||
func (s Status) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// FilterOrdersBySide removes any order details that don't match the
|
||||
// order status provided
|
||||
func FilterOrdersBySide(orders *[]Detail, side Side) {
|
||||
if side == "" || side == AnySide {
|
||||
return
|
||||
}
|
||||
|
||||
var filteredOrders []Detail
|
||||
for i := range *orders {
|
||||
if strings.EqualFold(string((*orders)[i].OrderSide), string(side)) {
|
||||
filteredOrders = append(filteredOrders, (*orders)[i])
|
||||
}
|
||||
}
|
||||
|
||||
*orders = filteredOrders
|
||||
}
|
||||
|
||||
// FilterOrdersByType removes any order details that don't match the order type
|
||||
// provided
|
||||
func FilterOrdersByType(orders *[]Detail, orderType Type) {
|
||||
if orderType == "" || orderType == AnyType {
|
||||
return
|
||||
}
|
||||
|
||||
var filteredOrders []Detail
|
||||
for i := range *orders {
|
||||
if strings.EqualFold(string((*orders)[i].OrderType), string(orderType)) {
|
||||
filteredOrders = append(filteredOrders, (*orders)[i])
|
||||
}
|
||||
}
|
||||
|
||||
*orders = filteredOrders
|
||||
}
|
||||
|
||||
// FilterOrdersByTickRange removes any OrderDetails outside of the tick range
|
||||
func FilterOrdersByTickRange(orders *[]Detail, startTicks, endTicks time.Time) {
|
||||
if startTicks.IsZero() ||
|
||||
endTicks.IsZero() ||
|
||||
startTicks.Unix() == 0 ||
|
||||
endTicks.Unix() == 0 ||
|
||||
endTicks.Before(startTicks) {
|
||||
return
|
||||
}
|
||||
|
||||
var filteredOrders []Detail
|
||||
for i := range *orders {
|
||||
if (*orders)[i].OrderDate.Unix() >= startTicks.Unix() &&
|
||||
(*orders)[i].OrderDate.Unix() <= endTicks.Unix() {
|
||||
filteredOrders = append(filteredOrders, (*orders)[i])
|
||||
}
|
||||
}
|
||||
|
||||
*orders = filteredOrders
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
var filteredOrders []Detail
|
||||
for i := range *orders {
|
||||
matchFound := false
|
||||
for _, c := range currencies {
|
||||
if !matchFound && (*orders)[i].CurrencyPair.EqualIncludeReciprocal(c) {
|
||||
matchFound = true
|
||||
}
|
||||
}
|
||||
|
||||
if matchFound {
|
||||
filteredOrders = append(filteredOrders, (*orders)[i])
|
||||
}
|
||||
}
|
||||
|
||||
*orders = filteredOrders
|
||||
}
|
||||
|
||||
func (b ByPrice) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByPrice) Less(i, j int) bool {
|
||||
return b[i].Price < b[j].Price
|
||||
}
|
||||
|
||||
func (b ByPrice) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersByPrice the caller function to sort orders
|
||||
func SortOrdersByPrice(orders *[]Detail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByPrice(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByPrice(*orders))
|
||||
}
|
||||
}
|
||||
|
||||
func (b ByOrderType) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByOrderType) Less(i, j int) bool {
|
||||
return b[i].OrderType.String() < b[j].OrderType.String()
|
||||
}
|
||||
|
||||
func (b ByOrderType) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersByType the caller function to sort orders
|
||||
func SortOrdersByType(orders *[]Detail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByOrderType(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByOrderType(*orders))
|
||||
}
|
||||
}
|
||||
|
||||
func (b ByCurrency) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByCurrency) Less(i, j int) bool {
|
||||
return b[i].CurrencyPair.String() < b[j].CurrencyPair.String()
|
||||
}
|
||||
|
||||
func (b ByCurrency) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersByCurrency the caller function to sort orders
|
||||
func SortOrdersByCurrency(orders *[]Detail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByCurrency(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByCurrency(*orders))
|
||||
}
|
||||
}
|
||||
|
||||
func (b ByDate) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByDate) Less(i, j int) bool {
|
||||
return b[i].OrderDate.Unix() < b[j].OrderDate.Unix()
|
||||
}
|
||||
|
||||
func (b ByDate) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersByDate the caller function to sort orders
|
||||
func SortOrdersByDate(orders *[]Detail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByDate(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByDate(*orders))
|
||||
}
|
||||
}
|
||||
|
||||
func (b ByOrderSide) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByOrderSide) Less(i, j int) bool {
|
||||
return b[i].OrderSide.String() < b[j].OrderSide.String()
|
||||
}
|
||||
|
||||
func (b ByOrderSide) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersBySide the caller function to sort orders
|
||||
func SortOrdersBySide(orders *[]Detail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByOrderSide(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByOrderSide(*orders))
|
||||
}
|
||||
}
|
||||
37
exchanges/order/orders_test.go
Normal file
37
exchanges/order/orders_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package order
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewOrder(t *testing.T) {
|
||||
ID := NewOrder("ANX", 2000, 20.00)
|
||||
if ID != 0 {
|
||||
t.Error("Orders_test.go NewOrder() - Error")
|
||||
}
|
||||
ID = NewOrder("BATMAN", 400, 25.00)
|
||||
if ID != 1 {
|
||||
t.Error("Orders_test.go NewOrder() - Error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteOrder(t *testing.T) {
|
||||
if value := DeleteOrder(0); !value {
|
||||
t.Error("Orders_test.go DeleteOrder() - Error")
|
||||
}
|
||||
if value := DeleteOrder(100); value {
|
||||
t.Error("Orders_test.go DeleteOrder() - Error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrdersByExchange(t *testing.T) {
|
||||
if value := GetOrdersByExchange("ANX"); len(value) != 0 {
|
||||
t.Error("Orders_test.go GetOrdersByExchange() - Error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderByOrderID(t *testing.T) {
|
||||
if value := GetOrderByOrderID(69); value != nil {
|
||||
t.Error("Orders_test.go GetOrdersByExchange() - Error")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user