Files
gocryptotrader/exchanges/orderbook/orderbook.go
Scott 6a15ecc65c OKEX/OKCoin API combine via OKGroup (#252)
* Initial commit

* Successful authenticated request implementation.

* Removes data from okcoin, okex. Implements some account okgroup endpoints. Adds tests

* Finishes account API endpoint implementations.

* Implements and adds tests for the following okgroup v3 API endpoints: GetSpotTradingAccounts, GetSpotTradingAccountForCurrency, GetSpotBillDetailsForCurrency, PlaceSpotOrder, PlaceMultipleSpotOrders, CancelSpotOrder, CancelMultipleSpotOrders, GetSpotOrders, GetSpotOpenOrders, GetSpotOrder, GetSpotTransactionDetails, GetSpotTokenPairDetails, GetSpotOrderBook, GetSpotAllTokenPairsInformation, GetSpotAllTokenPairsInformationForCurrency, GetSpotFilledOrdersInformation, GetSpotMarketData

* Implements and adds tests for all margin api endpoints: GetMarginTradingAccounts, GetMarginTradingAccountsForCurrency, GetMarginBillDetails, GetMarginAccountSettings, GetMarginLoanHistory, OpenMarginLoan, RepayMarginLoan, PlaceMarginOrder, PlaceMultipleMarginOrders, CancelMarginOrder, CancelMultipleMarginOrders, GetMarginOrders, GetMarginOpenOrders, GetMarginOrder, GetMarginTransactionDetails. Simplifies some Spot endpoints combining ForCurrency funcs where possible

* Adds all 29 Futures endpoints with tests. Updates comments and naming conventions. Adds standard realordertest func. Adds ability to make public API requests. Adds test purpose comments

* Adds 29 endpoints for SWAP API support. Adds tests for each endpoint. Declares response variables in function declaration. Simplifies URL parameter formatting

* Adds all ETT endpoints with tests

* Creates OKCoin and OKEX struct types. Moves okgroup tests to okcoin and okex exchanges. Updates withdraw fee calculation. Updates exchange.go exchange declaration to point to new types. Streamlines wrapper tests. Begins websocket integration

* Rebase fixes

* Deletes okcoin_types.go, okcoin_wrapper.go, okex_types.go, okex_wrapper.go. Combines okex,okcoin wrappers in okgroup_wrapper.go. Removes boilerplate url.values with new request struct type parsing. Adds github.com/google/go-querystring to go modules. Replaces USDT with USD for OKCoin tests. Moves OKEX specific endpoints (futures, swap & ett) to okex.go. Fixes recieving receiving again

* Maps the wrapper

* Parses json into time.Time instead of string + conversion

* Renames websocket.SetEnabled to websocket.SetWsStatusAndConnection. Maps main spot websocket functions for okgroup. Adds some basic ws tests

* Updates websocket default URLS, adds checksum tests, removes setdefaults from okgroup, adds WebsocketDataWrapper to wrap all okgroup websocket data responses, handles spot, swap, index and futures ticker, candle, trade, orderbook, funding fee websocket responses. Partially implements checksum validation on orderbook data. Fixes all linting issues

* Handles the calculation of okgroup websocket checksums. Adds tests

* Now all orderbook checksums are validated. Cleans up implementations and removes invalid checksum calculator functions. Adds function to parse ordertype. Puts verbose logs behind verbose check

* Removes parallel from okgroup tests. Removes unused code. Adds GetWsChannelWithoutOrderType. Improves handling of WS data types. Adds types for more ws channels. Simplifies update orderbook handling

* updates btse func name

* linting

* Fixes websocket connection issue with tests. Removes test verbosity

* Updates checksum calculation to handle more than 7 decimal places. Adds rate limiters. Uses != "" instead of len > 0. Adds new test to handle checksum calculation with 8 decimal places.

* Removes logging. Fixes orderbook wrapper references

* Adds slightly more robust resubscribe func. Adds websocket tests that can detect websocket errors

* Fixes linting issues

* Adds new WS func IsConnected() to expose ws status. Tests protect against channel timeout

* Adds test comments. Fixes parallel issues for futures tests

* Fixes error output for wrapper function
2019-03-18 15:18:36 +11:00

190 lines
5.0 KiB
Go

package orderbook
import (
"errors"
"sync"
"time"
"github.com/thrasher-/gocryptotrader/currency/pair"
)
// Const values for orderbook package
const (
ErrOrderbookForExchangeNotFound = "ticker for exchange does not exist"
ErrPrimaryCurrencyNotFound = "primary currency for orderbook not found"
ErrSecondaryCurrencyNotFound = "secondary currency for orderbook not found"
Spot = "SPOT"
)
// Vars for the orderbook package
var (
Orderbooks []Orderbook
m sync.Mutex
)
// Item stores the amount and price values
type Item struct {
Amount float64
Price float64
ID int64
}
// Base holds the fields for the orderbook base
type Base struct {
Pair pair.CurrencyPair `json:"pair"`
CurrencyPair string `json:"CurrencyPair"`
Bids []Item `json:"bids"`
Asks []Item `json:"asks"`
LastUpdated time.Time `json:"last_updated"`
AssetType string
}
// Orderbook holds the orderbook information for a currency pair and type
type Orderbook struct {
Orderbook map[pair.CurrencyItem]map[pair.CurrencyItem]map[string]Base
ExchangeName string
}
// CalculateTotalBids returns the total amount of bids and the total orderbook
// bids value
func (o *Base) CalculateTotalBids() (amountCollated, total float64) {
for _, x := range o.Bids {
amountCollated += x.Amount
total += x.Amount * x.Price
}
return amountCollated, total
}
// CalculateTotalAsks returns the total amount of asks and the total orderbook
// asks value
func (o *Base) CalculateTotalAsks() (amountCollated, total float64) {
for _, x := range o.Asks {
amountCollated += x.Amount
total += x.Amount * x.Price
}
return amountCollated, total
}
// Update updates the bids and asks
func (o *Base) Update(bids, asks []Item) {
o.Bids = bids
o.Asks = asks
o.LastUpdated = time.Now()
}
// GetOrderbook checks and returns the orderbook given an exchange name and
// currency pair if it exists
func GetOrderbook(exchange string, p pair.CurrencyPair, orderbookType string) (Base, error) {
orderbook, err := GetOrderbookByExchange(exchange)
if err != nil {
return Base{}, err
}
if !FirstCurrencyExists(exchange, p.FirstCurrency) {
return Base{}, errors.New(ErrPrimaryCurrencyNotFound)
}
if !SecondCurrencyExists(exchange, p) {
return Base{}, errors.New(ErrSecondaryCurrencyNotFound)
}
return orderbook.Orderbook[p.FirstCurrency][p.SecondCurrency][orderbookType], nil
}
// GetOrderbookByExchange returns an exchange orderbook
func GetOrderbookByExchange(exchange string) (*Orderbook, error) {
m.Lock()
defer m.Unlock()
for x := range Orderbooks {
if Orderbooks[x].ExchangeName == exchange {
return &Orderbooks[x], nil
}
}
return nil, errors.New(ErrOrderbookForExchangeNotFound)
}
// FirstCurrencyExists checks to see if the first currency of the orderbook map
// exists
func FirstCurrencyExists(exchange string, currency pair.CurrencyItem) bool {
m.Lock()
defer m.Unlock()
for _, y := range Orderbooks {
if y.ExchangeName == exchange {
if _, ok := y.Orderbook[currency]; ok {
return true
}
}
}
return false
}
// SecondCurrencyExists checks to see if the second currency of the orderbook
// map exists
func SecondCurrencyExists(exchange string, p pair.CurrencyPair) bool {
m.Lock()
defer m.Unlock()
for _, y := range Orderbooks {
if y.ExchangeName == exchange {
if _, ok := y.Orderbook[p.FirstCurrency]; ok {
if _, ok := y.Orderbook[p.FirstCurrency][p.SecondCurrency]; ok {
return true
}
}
}
}
return false
}
// CreateNewOrderbook creates a new orderbook
func CreateNewOrderbook(exchangeName string, p pair.CurrencyPair, orderbookNew Base, orderbookType string) Orderbook {
m.Lock()
defer m.Unlock()
orderbook := Orderbook{}
orderbook.ExchangeName = exchangeName
orderbook.Orderbook = make(map[pair.CurrencyItem]map[pair.CurrencyItem]map[string]Base)
a := make(map[pair.CurrencyItem]map[string]Base)
b := make(map[string]Base)
b[orderbookType] = orderbookNew
a[p.SecondCurrency] = b
orderbook.Orderbook[p.FirstCurrency] = a
Orderbooks = append(Orderbooks, orderbook)
return orderbook
}
// ProcessOrderbook processes incoming orderbooks, creating or updating the
// Orderbook list
func ProcessOrderbook(exchangeName string, p pair.CurrencyPair, orderbookNew Base, orderbookType string) {
if orderbookNew.Pair.Pair() == "" {
// set Pair if not set
orderbookNew.Pair = p
}
orderbookNew.CurrencyPair = p.Pair().String()
if orderbookNew.LastUpdated.IsZero() {
orderbookNew.LastUpdated = time.Now()
}
orderbook, err := GetOrderbookByExchange(exchangeName)
if err != nil {
CreateNewOrderbook(exchangeName, p, orderbookNew, orderbookType)
return
}
if FirstCurrencyExists(exchangeName, p.FirstCurrency) {
m.Lock()
a := make(map[string]Base)
a[orderbookType] = orderbookNew
orderbook.Orderbook[p.FirstCurrency][p.SecondCurrency] = a
m.Unlock()
return
}
m.Lock()
a := make(map[pair.CurrencyItem]map[string]Base)
b := make(map[string]Base)
b[orderbookType] = orderbookNew
a[p.SecondCurrency] = b
orderbook.Orderbook[p.FirstCurrency] = a
m.Unlock()
}