Files
gocryptotrader/exchanges/okcoin/okcoin_ws_trade.go
Samuael A 9c83231696 exchanges: Update OKCoin V5 exchange support (#1206)
* starting public endpoints

* Adding public endpoints

* added public spot market endpoints

* websocket subscriptions updates

* websocket push data handlers completing

* linter fix

* Added funding private endpoints

* Adding authenticated account endpoints

* Added fiat and OTC-RFQ authenticated endpoints

* trading authenticated endpoints

* completing trade endpoints and add public wrapper endpoints

* Authenticated wrapper functions and corresponding unit test

* Adding authenticated websocket endpoint and fixing wrapper functions

* Documentation and exchange websocket update

* update websocket orderbook checksum handling

* linter issues fix and unit test update

* remove invalid orderbook endpoint and unit test

* Documentation, handlers, and model types update

* minot fix

* Minor fixes

* Updating unit tests and added missing endpoints

* Add missing credential check

* Minor unit test fixes

* fix minor linter issue

* add snaphot test unit test

* Fix on update checksum and documentation update

* update exchange, add UpdateOrderExecutionLimits, and update documentation

* Minor fix on tickers fetching

* Minor websocket fix and smaill unit tests

* Minor websocket and naming fixes

* uncomment default channels

* Fix type and unit test issues

* websocket channels and data handling update

* Update Advanced-Algo websocket handling and minor fixes

* documentation and minor code fixes

* Fix name changes

* documentation contribution update

* intervalToString method update

* fix exchange_wrapper_standard tests

* Fix minor issues based on exchange_wrapper_standards_test

* Fix wrapper extended candlestick check

* websocket orders fetching error check method update

* Exchange name check and change

* docs: Add missing contributors

---------

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
2023-08-22 09:44:39 +10:00

184 lines
5.6 KiB
Go

package okcoin
import (
"encoding/json"
"errors"
"fmt"
"reflect"
"strconv"
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
)
// WsPlaceOrder place trade order through the websocket channel.
func (o *Okcoin) WsPlaceOrder(arg *PlaceTradeOrderParam) (*TradeOrderResponse, error) {
err := arg.validateTradeOrderParameter()
if err != nil {
return nil, err
}
var resp []TradeOrderResponse
err = o.SendWebsocketRequest("order", arg, &resp, true)
if err != nil {
return nil, err
}
if len(resp) == 0 {
return nil, errNoValidResponseFromServer
}
if resp[0].SCode != "0" {
return nil, fmt.Errorf("code: %s msg: %s", resp[0].SCode, resp[0].SMsg)
}
return &resp[0], nil
}
// WsPlaceMultipleOrder place orders in batches through the websocket stream. Maximum 20 orders can be placed per request. Request parameters should be passed in the form of an array.
func (o *Okcoin) WsPlaceMultipleOrder(args []PlaceTradeOrderParam) ([]TradeOrderResponse, error) {
var err error
if len(args) == 0 {
return nil, fmt.Errorf("%w, 0 length place order requests", errNilArgument)
}
for x := range args {
err = args[x].validateTradeOrderParameter()
if err != nil {
return nil, err
}
}
var resp []TradeOrderResponse
return resp, o.SendWebsocketRequest("batch-orders", args, &resp, true)
}
// WsCancelTradeOrder cancels a single trade order through the websocket stream.
func (o *Okcoin) WsCancelTradeOrder(arg *CancelTradeOrderRequest) (*TradeOrderResponse, error) {
if arg == nil {
return nil, errNilArgument
}
if arg.InstrumentID == "" {
return nil, errMissingInstrumentID
}
if arg.OrderID == "" && arg.ClientOrderID == "" {
return nil, errOrderIDOrClientOrderIDRequired
}
var resp []TradeOrderResponse
err := o.SendWebsocketRequest("cancel-order", &arg, &resp, true)
if err != nil {
return nil, err
}
if len(resp) == 0 {
return nil, errNoValidResponseFromServer
}
if resp[0].SCode != "0" {
return nil, fmt.Errorf("code: %s msg: %s", resp[0].SCode, resp[0].SMsg)
}
return &resp[0], nil
}
// WsCancelMultipleOrders cancel incomplete orders in batches through the websocket stream. Maximum 20 orders can be canceled per request.
// Request parameters should be passed in the form of an array.
func (o *Okcoin) WsCancelMultipleOrders(args []CancelTradeOrderRequest) ([]TradeOrderResponse, error) {
var err error
if len(args) == 0 {
return nil, fmt.Errorf("%w, 0 length place order requests", errNilArgument)
}
for x := range args {
err = args[x].validate()
if err != nil {
return nil, err
}
}
var resp []TradeOrderResponse
return resp, o.SendWebsocketRequest("batch-cancel-orders", args, &resp, true)
}
// WsAmendOrder amends an incomplete order through the websocket connection
func (o *Okcoin) WsAmendOrder(arg *AmendTradeOrderRequestParam) (*AmendTradeOrderResponse, error) {
err := arg.validate()
if err != nil {
return nil, err
}
var resp []AmendTradeOrderResponse
err = o.SendWebsocketRequest("amend-order", &arg, &resp, true)
if err != nil {
if len(resp) > 0 && resp[0].StatusCode != "0" && resp[0].StatusCode != "" {
return nil, fmt.Errorf("%w, code: %s msg: %s", err, resp[0].StatusCode, resp[0].StatusMessage)
}
return nil, err
}
if len(resp) == 0 {
return nil, errNoValidResponseFromServer
}
if resp[0].StatusCode != "0" {
return nil, fmt.Errorf("code: %s msg: %s", resp[0].StatusCode, resp[0].StatusMessage)
}
return &resp[0], nil
}
// WsAmendMultipleOrder amends multiple trade orders.
func (o *Okcoin) WsAmendMultipleOrder(args []AmendTradeOrderRequestParam) ([]AmendTradeOrderResponse, error) {
if len(args) == 0 {
return nil, fmt.Errorf("%w, please provide at least one trade order amendment request", errNilArgument)
}
for x := range args {
err := args[x].validate()
if err != nil {
return nil, err
}
}
var resp []AmendTradeOrderResponse
return resp, o.SendWebsocketRequest("batch-amend-orders", args, &resp, true)
}
// SendWebsocketRequest send a request through the websocket connection.
func (o *Okcoin) SendWebsocketRequest(operation string, data, result interface{}, authenticated bool) error {
switch {
case !o.Websocket.IsEnabled():
return errors.New(stream.WebsocketNotEnabled)
case !o.Websocket.IsConnected():
return stream.ErrNotConnected
case !o.Websocket.CanUseAuthenticatedEndpoints() && authenticated:
return errors.New("websocket connection not authenticated")
}
req := &struct {
ID string `json:"id"`
Operation string `json:"op"`
Arguments interface{} `json:"args"`
}{
ID: strconv.FormatInt(o.Websocket.Conn.GenerateMessageID(false), 10),
Operation: operation,
}
if reflect.TypeOf(data).Kind() == reflect.Slice {
req.Arguments = data
} else {
req.Arguments = []interface{}{data}
}
var byteData []byte
var err error
// TODO: ratelimits for websocket
if authenticated {
byteData, err = o.Websocket.AuthConn.SendMessageReturnResponse(req.ID, req)
} else {
byteData, err = o.Websocket.Conn.SendMessageReturnResponse(req.ID, req)
}
if err != nil {
return err
}
response := struct {
ID string `json:"id"`
Operation string `json:"op"`
Data interface{} `json:"data"`
Code string `json:"code"`
Message string `json:"msg"`
}{
Data: &result,
}
err = json.Unmarshal(byteData, &response)
if err != nil {
return err
}
if response.Code != "" && response.Code != "0" && response.Code != "1" && response.Code != "2" {
if response.Message == "" {
response.Message = websocketErrorCodes[response.Code]
}
return fmt.Errorf("%s websocket error code: %s message: %s", o.Name, response.Code, response.Message)
}
return nil
}