exchanges: Add OKX support (#1005)

* public endpoints methods added

* Completing mapping REST endpoints

* Binanceus Wrapper methods -Partially

* Binanceus Wrapper methods -Partially

* BinaWra functions with test funs; Not Completed

* Finalizing wrapper methods & test

* Fix & Complete wrapper functions

* Finalizing wrapper methods & test

* Adding Stream Datas

* WS Test functions

* CI: Fix golangci-lint linter issues

* CI: Fix reverting unnessesary changes and type conversion issues

* CI: Fix reverting unnessesary changes and type conversion issues

* Adding Public endpoints and tests

* Adding Market and Public Endpoints

* Adding Public endoints

* Public Trading Endpoints & Authenticated Trade order methods

* Adding Authenticated Methods and Tests

* Adding algo and Funding Authenticated endpoints

* Adding funding trading endpoints and correspondint tests

* adding authenticated endpoints

* Completing Block Trading endpoints and added subaccount endpoints

* Completing sub account and grid Trading endpoints

* Adding Rate Limit and missing endpoints

* Wrapper and Websocket handlers

* Fixing Websocket Test and Push Data Handler Issues

* Fixing Websocket Test and Push Data Handler Issues

* Fixing linter issues, package dependency, and other slight tempos

* Fixing linter and slight tempos

* Update on test functions, and Rest and Websocket Endpoint handlers

* Remove okex, adding comments, and slight fixes on endpoints.

* Fixing linter issues and adding comments

* Slight code changes, updating documentation, and n and linter issues

* Fix context and configuration endpoint issues

* slight fixes on config and test files

* adding some missing test and fix linter issues

* fix linter issue

* Slight fixes on code structure, shorthand exp,and ot and other

* Fix slight linter issue

* Slight code fixes and fixing linter issues

* fixing linter iissues

* fixing linter iissues

* slight linter issue fix

* slight linter issue fix

* Fix on models, type convert funcs and endpoints

* Adding Error messages map and update of models

* Fix on error message string and linter issues

* Fix slight linter issue

* Fix slight linter issue

* Fixing type converts, models, and linter issues

* Adding Ws fixes

* Slight fix on websocket and other issues

* Adding slight websocket fixes

* Remove 'books5' channel default subscription

* Small changes on default subscription and checksum

* Fix slight websocket tempos

* Fix Wrapper function tempost and linter issues

* Resolving slight naming and other issues

* Resolve slight pointer issues

* resolve slight linter issues

* Resolve config files issue

* Update websocket and wrapper funcs with test and docs

* fixs on websocket multiplexer, types, and other slight issues

* fix slight linter issues

* slight update on web-socket orderbook and tickers

* fix slight issues and websocket runtime errors

* Slight unit test fix and assing simple semaphore

* FIx race issue

* Update on authenticated endpoints

* Fix wsSetupRun check in websocket 'setupWsAuth' func

* Update wsSetupRun check in websocket 'setupWsAuth' func

* Slight update on websocket handling

* Fix some race conditions

* fix slight tempos

* fix authenticated test issues

* Update on conditional statements

* slight update on unit test

* fix unit test tempos

* Fix slight tempos

* Change check map from struct valued to bool valued

* slight fix trial

* Slight unit test update

* Fix websocket timeout error

* Updating websocket subscription endpoints, and unit tests

* update unit tests

* Slight issue on wrapper method 'GetActiveOrders'

* Overall code update

* Addressing missing review comments

* Fix unit test tempo and linter issue

* Monor fix

* Slight update

* Slight unit test fix

* Slight fixes

* Slight fixes

* Fixing on missing review comments

* Adding WS Fixes

* slight fix

* Monor fix on unit test

* Minor convert issue

* Minor change on WS

* Monor logic fix

* Fix code structure and logic issues

* Fixing small typos

* fix slight data format issue

* Update on trade and order wrapper methods

* Adding slight update

* fix on order detail

* Slight update on FetchTradablePairs wrapper method

* Slight update on wrapper

* Update on deserialization and other slight issues

* Final update

* Resolve missing review comments

* Slight update on config and unit test

* minor fix on GetDepositAddress param

* Minor fix
This commit is contained in:
Samuael A
2022-12-09 03:44:29 +03:00
committed by GitHub
parent d7d4933f93
commit 2ac165a477
54 changed files with 16943 additions and 3977 deletions

View File

@@ -15,6 +15,7 @@ MadCozBadd | https://github.com/MadCozBadd
vadimzhukck | https://github.com/vadimzhukck
140am | https://github.com/140am
marcofranssen | https://github.com/marcofranssen
geseq | https://github.com/geseq
dackroyd | https://github.com/dackroyd
cranktakular | https://github.com/cranktakular
khcchiu | https://github.com/khcchiu

View File

@@ -43,7 +43,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
| Lbank | Yes | No | NA |
| LocalBitcoins | Yes | NA | NA |
| OKCoin International | Yes | Yes | No |
| OKEX | Yes | Yes | No |
| Okx | Yes | Yes | NA |
| Poloniex | Yes | Yes | NA |
| Yobit | Yes | NA | NA |
| ZB.COM | Yes | Yes | NA |
@@ -145,10 +145,10 @@ Binaries will be published once the codebase reaches a stable condition.
|User|Contribution Amount|
|--|--|
| [thrasher-](https://github.com/thrasher-) | 667 |
| [shazbert](https://github.com/shazbert) | 260 |
| [gloriousCode](https://github.com/gloriousCode) | 199 |
| [dependabot[bot]](https://github.com/apps/dependabot) | 99 |
| [thrasher-](https://github.com/thrasher-) | 670 |
| [shazbert](https://github.com/shazbert) | 268 |
| [gloriousCode](https://github.com/gloriousCode) | 202 |
| [dependabot[bot]](https://github.com/apps/dependabot) | 130 |
| [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 88 |
| [xtda](https://github.com/xtda) | 47 |
| [lrascao](https://github.com/lrascao) | 27 |
@@ -160,6 +160,7 @@ Binaries will be published once the codebase reaches a stable condition.
| [vadimzhukck](https://github.com/vadimzhukck) | 10 |
| [140am](https://github.com/140am) | 8 |
| [marcofranssen](https://github.com/marcofranssen) | 8 |
| [geseq](https://github.com/geseq) | 7 |
| [dackroyd](https://github.com/dackroyd) | 5 |
| [cranktakular](https://github.com/cranktakular) | 5 |
| [khcchiu](https://github.com/khcchiu) | 5 |

View File

@@ -34,7 +34,6 @@ const (
htmlScrape = "HTML String Check"
pathBinance = "https://binance-docs.github.io/apidocs/spot/en/#change-log"
pathOkCoin = "https://www.okcoin.com/docs/en/#change-change"
pathOkex = "https://www.okex.com/docs/en/#change-change"
pathFTX = "https://github.com/ftexchange/ftx"
pathBTSE = "https://www.btse.com/apiexplorer/spot/#btse-spot-api"
pathBitfinex = "https://docs.bitfinex.com/docs/changelog"
@@ -494,7 +493,7 @@ func checkChangeLog(htmlData *HTMLScrapingData) (string, error) {
dataStrings, err = htmlScrapeYobit(htmlData)
case pathLocalBitcoins:
dataStrings, err = htmlScrapeLocalBitcoins(htmlData)
case pathOkCoin, pathOkex:
case pathOkCoin:
dataStrings, err = htmlScrapeOk(htmlData)
default:
dataStrings, err = htmlScrapeDefault(htmlData)
@@ -801,7 +800,7 @@ func htmlScrapeBTCMarkets(htmlData *HTMLScrapingData) ([]string, error) {
return resp, nil
}
// htmlScrapeOk gets the check string for Okex
// htmlScrapeOk gets the check string for Okx
func htmlScrapeOk(htmlData *HTMLScrapingData) ([]string, error) {
var resp []string
temp, err := sendHTTPGetRequest(htmlData.Path, nil)

View File

@@ -403,7 +403,7 @@ func TestHTMLScrapeOk(t *testing.T) {
Val: "./#change-change",
TokenDataEnd: "./#change-",
RegExp: `./#change-\d{8}`,
Path: "https://www.okex.com/docs/en/"}
Path: "https://www.okx.com/docs/en/"}
if _, err := htmlScrapeOk(&data); err != nil {
t.Error(err)
}

View File

@@ -358,22 +358,6 @@
},
"Disabled": false
},
{
"Name": "Okex",
"CheckType": "HTML String Check",
"Data": {
"HTMLData": {
"TokenData": "a",
"Key": "href",
"Val": "./#change-change",
"TokenDataEnd": "./#change-",
"RegExp": "./#change-\\d{8}",
"CheckString": "20200331",
"Path": "https://www.okex.com/docs/en/#change-change"
}
},
"Disabled": false
},
{
"Name": "Gemini",
"CheckType": "HTML String Check",

View File

@@ -358,22 +358,6 @@
},
"Disabled": false
},
{
"Name": "Okex",
"CheckType": "HTML String Check",
"Data": {
"HTMLData": {
"TokenData": "a",
"Key": "href",
"Val": "./#change-change",
"TokenDataEnd": "./#change-",
"RegExp": "./#change-\\d{8}",
"CheckString": "20200331",
"Path": "https://www.okex.com/docs/en/#change-change"
}
},
"Disabled": false
},
{
"Name": "Gemini",
"CheckType": "HTML String Check",

View File

@@ -287,6 +287,11 @@ func main() {
URL: "https://github.com/lozdog245",
Contributions: 2,
},
{
Login: "if1live",
URL: "https://github.com/if1live",
Contributions: 2,
},
}...)
if verbose {

View File

@@ -66,7 +66,7 @@ _b in this context is an `IBotExchange` implemented struct_
| Lbank | Yes | No | Yes |
| LocalBitcoins | Yes | NA | No |
| OKCoin International | Yes | Yes | No |
| OKEX | Yes | Yes | No |
| Okx | Yes | Yes | Yes |
| Poloniex | Yes | Yes | Yes |
| Yobit | Yes | NA | No |
| ZB.COM | Yes | Yes | No |

View File

@@ -1,14 +1,15 @@
{{define "exchanges okex" -}}
{{define "exchanges okx" -}}
{{template "header" .}}
## OKex Exchange
## Okx Exchange
### Current Features
+ REST Support
+ Websocket Support
### How to enable
+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
[Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
+ Individual package example below:
@@ -27,10 +28,10 @@ below:
main.go
```go
var o exchange.IBotExchange
var ok exchange.IBotExchange
for i := range bot.Exchanges {
if bot.Exchanges[i].GetName() == "OKex" {
if bot.Exchanges[i].GetName() == "Okx" {
y = bot.Exchanges[i]
}
}
@@ -38,22 +39,22 @@ for i := range bot.Exchanges {
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := o.FetchTicker()
tick, err := ok.FetchTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := o.FetchOrderbook()
ob, err := ok.FetchOrderbook()
if err != nil {
// Handle error
}
// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
// Private calls - wrapper functions - make sure your APIKEY, APISECRET, and API_CLIENT_ID are
// set and AuthenticatedAPISupport is set to true
// Fetches current account information
accountInfo, err := o.GetAccountInfo()
accountInfo, err := ok.GetAccountInfo()
if err != nil {
// Handle error
}
@@ -65,13 +66,26 @@ if err != nil {
// Public calls
// Fetches current ticker information
ticker, err := o.GetSpotTicker()
ticker, err := ok.GetTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := o.GetSpotMarketDepth()
ob, err := ok.GetOrderBook()
if err != nil {
// Handle error
}
// Fetches historic trade data within the timeframe provided
tradeDatas, err := ok.GetHistoricTrades(...)
if err != nil {
// Handle error
}
// Returns an estimate of fee based on the type of transaction
fee, err := ok.GetFeeByType(...)
if err != nil {
// Handle error
}
@@ -79,20 +93,28 @@ if err != nil {
// Private calls - make sure your APIKEY and APISECRET are set and
// AuthenticatedAPISupport is set to true
// GetContractPosition returns contract positioning
accountInfo, err := o.GetContractPosition(...)
// Submits an order and the exchange and returns its tradeID
orderID, err := ok.SubmitOrder(...)
if err != nil {
// Handle error
}
// Submits an order and the exchange and returns its tradeID
tradeID, err := o.PlaceContractOrders(...)
// ModifyOrder will allow of changing orderbook placement and limit to market conversion
updatedOrder, err := ok.ModifyOrder(...)
if err != nil {
// Handle error
}
```
### How to do Websocket public/private calls
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### Please click GoDocs chevron above to view current GoDoc information for this package
{{template "contributions"}}
{{template "donations" .}}
{{end}}
{{end}}

View File

@@ -44,7 +44,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
| Lbank | Yes | No | NA |
| LocalBitcoins | Yes | NA | NA |
| OKCoin International | Yes | Yes | No |
| OKEX | Yes | Yes | No |
| Okx | Yes | Yes | NA |
| Poloniex | Yes | Yes | NA |
| Yobit | Yes | NA | NA |
| ZB.COM | Yes | Yes | NA |

View File

@@ -151,7 +151,7 @@ func main() {
}
func parseCLFlags() {
flag.StringVar(&exchangesToUseOverride, "exchanges", "", "a + delimited list of exchange names to run tests against eg -exchanges=bitfinex+okex")
flag.StringVar(&exchangesToUseOverride, "exchanges", "", "a + delimited list of exchange names to run tests against eg -exchanges=bitfinex+okx")
flag.StringVar(&exchangesToExcludeOverride, "excluded-exchanges", "", "a + delimited list of exchange names to ignore when they're being temperamental eg -exchangesToExlude=lbank")
flag.StringVar(&assetTypeOverride, "asset", "", "the asset type to run tests against (where applicable)")
flag.StringVar(&currencyPairOverride, "currency", "", "the currency to run tests against (where applicable)")

View File

@@ -147,11 +147,11 @@
"clientID": "ClientID",
"otpSecret": "-"
},
"okex": {
"key": "Key",
"secret": "Secret",
"clientID": "ClientID",
"otpSecret": "-"
"okx": {
"key": "Key",
"secret": "Secret",
"clientID": "ClientID",
"otpSecret": "-"
},
"poloniex": {
"key": "Key",

View File

@@ -2,9 +2,11 @@ package common
import (
"context"
"crypto/rand"
"errors"
"fmt"
"io"
"math/big"
"net"
"net/http"
"net/url"
@@ -32,6 +34,13 @@ const (
defaultTimeout = time.Second * 15
)
// Strings representing the full lower, upper case English character alphabet and base-10 numbers for generating a random string.
const (
SmallLetters = "abcdefghijklmnopqrstuvwxyz"
CapitalLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
NumberCharacters = "0123456789"
)
var (
// emailRX represents email address matching pattern
emailRX = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
@@ -470,6 +479,30 @@ func StartEndTimeCheck(start, end time.Time) error {
return nil
}
// GenerateRandomString generates a random string provided a length and list of Character types { SmallLetters, CapitalLetters, NumberCharacters}.
// if no characters are provided, the function uses a NumberCharacters(string of numeric characters).
func GenerateRandomString(length uint, characters ...string) (string, error) {
if length == 0 {
return "", errors.New("invalid length, length must be non-zero positive integer")
}
b := make([]byte, length)
chars := strings.Replace(strings.Join(characters, ""), " ", "", -1)
if chars == "" && len(characters) != 0 {
return "", errors.New("invalid characters, character must not be empty")
} else if chars == "" {
chars = NumberCharacters
}
for i := range b {
nBig, err := rand.Int(rand.Reader, big.NewInt(int64(len(chars))))
if err != nil {
return "", err
}
n := nBig.Int64()
b[i] = chars[n]
}
return string(b), nil
}
// GetAssertError returns additional information for when an assertion failure
// occurs.
func GetAssertError(required string, received interface{}) error {

View File

@@ -721,3 +721,34 @@ func TestMatchesEmailPattern(t *testing.T) {
t.Error("MatchesEmailPattern() unexpected test validation result")
}
}
func TestGenerateRandomString(t *testing.T) {
t.Parallel()
sample, err := GenerateRandomString(5, NumberCharacters)
if err != nil {
t.Errorf("GenerateRandomString() %v", err)
}
value, err := strconv.Atoi(sample)
if len(sample) != 5 || err != nil || value < 0 {
t.Error("GenerateRandomString() unexpected test validation result")
}
sample, err = GenerateRandomString(5)
if err != nil {
t.Errorf("GenerateRandomString() %v", err)
}
values, err := strconv.ParseInt(sample, 10, 64)
if len(sample) != 5 || err != nil || values < 0 {
t.Error("GenerateRandomString() unexpected test validation result")
}
_, err = GenerateRandomString(1, "")
if err == nil {
t.Errorf("GenerateRandomString() expecting %s, but found %v", "invalid characters, character must not be empty", err)
}
sample, err = GenerateRandomString(0, "")
if err != nil && !strings.Contains(err.Error(), "invalid length") {
t.Errorf("GenerateRandomString() %v", err)
}
if sample != "" {
t.Error("GenerateRandomString() unexpected test validation result")
}
}

View File

@@ -1289,7 +1289,7 @@ func TestGetPrimaryForexProvider(t *testing.T) {
func TestUpdateExchangeConfig(t *testing.T) {
t.Parallel()
ok := "OKEX"
ok := "Okx"
cfg := &Config{
Exchanges: []Exchange{
{

File diff suppressed because one or more lines are too long

View File

@@ -139,3 +139,8 @@ func (p Pair) Other(c Code) (Code, error) {
}
return EMPTYCODE, ErrCurrencyCodeEmpty
}
// IsPopulated returns true if the currency pair have both non-empty values for base and quote.
func (p Pair) IsPopulated() bool {
return !p.Base.IsEmpty() && !p.Quote.IsEmpty()
}

View File

@@ -927,3 +927,18 @@ func TestOther(t *testing.T) {
t.Fatal("unexpected value")
}
}
func TestIsPopulated(t *testing.T) {
if receiver := NewPair(BTC, USDT).IsPopulated(); !receiver {
t.Fatal("unexpected value")
}
if receiver := NewPair(BTC, NewCode("USD-1245")).IsPopulated(); !receiver {
t.Fatal("unexpected value")
}
if receiver := NewPair(BTC, EMPTYCODE).IsPopulated(); receiver {
t.Fatal("unexpected value")
}
if receiver := NewPair(EMPTYCODE, EMPTYCODE).IsPopulated(); receiver {
t.Fatal("unexpected value")
}
}

View File

@@ -218,7 +218,7 @@ Yes means supported, No means not yet implemented and NA means protocol unsuppor
| Lbank | Yes | No | NA |
| LocalBitcoins | Yes | NA | NA |
| OKCoin International | Yes | Yes | No |
| OKEX | Yes | Yes | No |
| Okx | Yes | Yes | NA |
| Poloniex | Yes | Yes | NA |
| Yobit | Yes | NA | NA |
| ZB.COM | Yes | Yes | NA |
@@ -249,7 +249,7 @@ var Exchanges = []string{
"lbank",
"localbitcoins",
"okcoin international",
"okex",
"okx",
"poloniex",
"yobit",
"zb",

View File

@@ -67,7 +67,7 @@ $ ./gctcli withdrawcryptofunds --exchange=ftx --currency=USDT --address=TJU9piX2
| Lbank | No | No | |
| LocalBitcoins | No | No | Supports BTC only |
| OKCoin International | No | No | Requires API update to version 5 |
| OKEX | No | No | Same as above |
| Okx | Yes | Yes | |
| Poloniex | Yes | Yes | |
| Yobit | No | No | |
| ZB.COM | Yes | No | Addresses must be created via their website first |

View File

@@ -88,8 +88,8 @@ A helper tool [cmd/dbseed](../cmd/dbseed/README.md) has been created for assisti
| Kraken | Y |
| lBank | Y |
| Localbitcoins | |
| Okcoin | Y |
| Okex | Y |
| Okcoin | Y |
| Okx | Y |
| Poloniex | Y |
| Yobit | |
| ZB | Y |

View File

@@ -31,7 +31,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/lbank"
"github.com/thrasher-corp/gocryptotrader/exchanges/localbitcoins"
"github.com/thrasher-corp/gocryptotrader/exchanges/okcoin"
"github.com/thrasher-corp/gocryptotrader/exchanges/okex"
"github.com/thrasher-corp/gocryptotrader/exchanges/okx"
"github.com/thrasher-corp/gocryptotrader/exchanges/poloniex"
"github.com/thrasher-corp/gocryptotrader/exchanges/yobit"
"github.com/thrasher-corp/gocryptotrader/exchanges/zb"
@@ -195,8 +195,8 @@ func (m *ExchangeManager) NewExchangeByName(name string) (exchange.IBotExchange,
exch = new(localbitcoins.LocalBitcoins)
case "okcoin international":
exch = new(okcoin.OKCoin)
case "okex":
exch = new(okex.OKEX)
case "okx":
exch = new(okx.Okx)
case "poloniex":
exch = new(poloniex.Poloniex)
case "yobit":

View File

@@ -82,7 +82,7 @@ func TestExchangeManagerRemoveExchange(t *testing.T) {
func TestNewExchangeByName(t *testing.T) {
m := SetupExchangeManager()
exchanges := []string{"binanceus", "binance", "bitfinex", "bitflyer", "bithumb", "bitmex", "bitstamp", "bittrex", "btc markets", "btse", "bybit", "coinut", "exmo", "coinbasepro", "ftx", "gateio", "gemini", "hitbtc", "huobi", "itbit", "kraken", "lbank", "localbitcoins", "okcoin international", "okex", "poloniex", "yobit", "zb", "fake"}
exchanges := []string{"binanceus", "binance", "bitfinex", "bitflyer", "bithumb", "bitmex", "bitstamp", "bittrex", "btc markets", "btse", "bybit", "coinut", "exmo", "coinbasepro", "ftx", "gateio", "gemini", "hitbtc", "huobi", "itbit", "kraken", "lbank", "localbitcoins", "okcoin international", "okx", "poloniex", "yobit", "zb", "fake"}
for i := range exchanges {
exch, err := m.NewExchangeByName(exchanges[i])
if err != nil && exchanges[i] != "fake" {

View File

@@ -13,7 +13,7 @@ var (
)
// Item stores the asset type
type Item uint16
type Item uint32
// Items stores a list of assets types
type Items []Item
@@ -34,9 +34,10 @@ const (
CoinMarginedFutures
USDTMarginedFutures
USDCMarginedFutures
Option
futuresFlag = PerpetualContract | PerpetualSwap | Futures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures | USDCMarginedFutures
supportedFlag = Spot | Margin | MarginFunding | Index | Binary | PerpetualContract | PerpetualSwap | Futures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures | USDCMarginedFutures
supportedFlag = Spot | Margin | MarginFunding | Index | Binary | PerpetualContract | PerpetualSwap | Futures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures | USDCMarginedFutures | Option
spot = "spot"
margin = "margin"
@@ -51,10 +52,11 @@ const (
coinMarginedFutures = "coinmarginedfutures"
usdtMarginedFutures = "usdtmarginedfutures"
usdcMarginedFutures = "usdcmarginedfutures"
option = "option"
)
var (
supportedList = Items{Spot, Margin, MarginFunding, Index, Binary, PerpetualContract, PerpetualSwap, Futures, UpsideProfitContract, DownsideProfitContract, CoinMarginedFutures, USDTMarginedFutures, USDCMarginedFutures}
supportedList = Items{Spot, Margin, MarginFunding, Index, Binary, PerpetualContract, PerpetualSwap, Futures, UpsideProfitContract, DownsideProfitContract, CoinMarginedFutures, USDTMarginedFutures, USDCMarginedFutures, Option}
)
// Supported returns a list of supported asset types
@@ -91,6 +93,8 @@ func (a Item) String() string {
return usdtMarginedFutures
case USDCMarginedFutures:
return usdcMarginedFutures
case Option:
return option
default:
return ""
}
@@ -186,6 +190,8 @@ func New(input string) (Item, error) {
return USDTMarginedFutures, nil
case usdcMarginedFutures:
return USDCMarginedFutures, nil
case option:
return Option, nil
default:
return 0, fmt.Errorf("%w '%v', only supports %s",
ErrNotSupported,

View File

@@ -25,12 +25,15 @@ const (
EightHour = 8 * OneHour
TwelveHour = 12 * OneHour
OneDay = 24 * OneHour
TwoDay = 2 * OneDay
ThreeDay = 3 * OneDay
SevenDay = 7 * OneDay
FifteenDay = 15 * OneDay
OneWeek = 7 * OneDay
TwoWeek = 2 * OneWeek
OneMonth = 31 * OneDay
ThreeMonth = 3 * OneMonth
SixMonth = 6 * OneMonth
OneYear = 365 * OneDay
)

View File

@@ -789,7 +789,7 @@ func TestSendWsMessages(t *testing.T) {
response := <-o.Websocket.DataHandler
if err, ok = response.(error); ok && err != nil {
if !strings.Contains(err.Error(), subscriptions[0].Channel) {
t.Error("Expecting OKEX error - 30040 message: Channel badChannel doesn't exist")
t.Error("Expecting OKCoin error - 30040 message: Channel badChannel doesn't exist")
}
}
err = o.WsLogin(context.Background())

View File

@@ -45,7 +45,7 @@ func (o *OKCoin) GetDefaultConfig() (*config.Exchange, error) {
return exchCfg, nil
}
// SetDefaults method assignes the default values for OKEX
// SetDefaults method assignes the default values for OKCoin
func (o *OKCoin) SetDefaults() {
o.SetErrorDefaults()
o.SetCheckVarDefaults()
@@ -169,7 +169,7 @@ func (o *OKCoin) Start(wg *sync.WaitGroup) error {
return nil
}
// Run implements the OKEX wrapper
// Run implements the OKCoin wrapper
func (o *OKCoin) Run() {
if o.Verbose {
log.Debugf(log.ExchangeSys,

View File

@@ -1,588 +0,0 @@
package okex
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/okgroup"
)
const (
okExRateInterval = time.Second
okExRequestRate = 6
okExAPIPath = "api/"
okExAPIURL = "https://www.okex.com/" + okExAPIPath
okExAPIVersion = "/v3/"
okExExchangeName = "OKEX"
// OkExWebsocketURL WebsocketURL
OkExWebsocketURL = "wss://real.okex.com:8443/ws/v3"
// API subsections
okGroupSpotSubsection = "spot"
okGroupFuturesSubsection = "futures"
okGroupSwapSubsection = "swap"
okGroupETTSubsection = "ett"
okGroupMarginSubsection = "margin"
// Futures based endpoints
okGroupFuturePosition = "position"
okGroupFutureLeverage = "leverage"
okGroupFutureOrder = "order"
okGroupFutureHolds = "holds"
okGroupIndices = "index"
okGroupRate = "rate"
okGroupEsimtatedPrice = "estimated_price"
okGroupOpenInterest = "open_interest"
// Perpetual swap based endpoints
okGroupSettings = "settings"
okGroupDepth = "depth"
okGroupFundingTime = "funding_time"
okGroupHistoricalFundingRate = "historical_funding_rate"
okGroupSwapInstruments = "instruments"
// ETT endpoints
okGroupConstituents = "constituents"
okGroupDefinePrice = "define-price"
okGroupPerpSwapRates = "instruments/%s/historical_funding_rate?"
okGroupPerpTickers = "instruments/ticker"
okGroupMarginPairData = "accounts/%s/availability"
okGroupMarginPairsData = "accounts/availability"
okGroupInstruments = "instruments"
)
// OKEX bases all account, spot and margin methods off okgroup implementation
type OKEX struct {
okgroup.OKGroup
}
// GetSwapMarkets gets perpetual swap markets
func (o *OKEX) GetSwapMarkets(ctx context.Context) ([]okgroup.SwapInstrumentsData, error) {
var resp []okgroup.SwapInstrumentsData
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection,
okGroupSwapInstruments,
nil, &resp, false)
}
// GetSwapInstruments gets perpetual swap instruments data
func (o *OKEX) GetSwapInstruments(ctx context.Context) ([]okgroup.PerpSwapInstrumentData, error) {
var resp []okgroup.PerpSwapInstrumentData
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection,
okGroupInstruments,
nil, &resp, false)
}
// GetAllMarginRates gets interest rates for all margin currencies on OKEX
func (o *OKEX) GetAllMarginRates(ctx context.Context) ([]okgroup.MarginCurrencyData, error) {
var resp []okgroup.MarginCurrencyData
var result []map[string]interface{}
var tempResp okgroup.MarginCurrencyData
tempResp.Data = make(map[string]okgroup.MarginData)
err := o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet,
okGroupMarginSubsection,
okGroupMarginPairsData,
nil,
&result,
true)
if err != nil {
return resp, err
}
for i := range result {
for k, v := range result[i] {
if strings.Contains(k, "currency:") {
var byteData []byte
var marginData okgroup.MarginData
currencyString := strings.Replace(k, "currency:", "", 1)
byteData, err = json.Marshal(v)
if err != nil {
return resp, err
}
err = json.Unmarshal(byteData, &marginData)
if err != nil {
return resp, err
}
tempResp.Data[currencyString] = marginData
}
var strData string
var ok bool
strData, ok = result[i]["instrument_id"].(string)
if !ok {
return resp, errors.New("type conversion failed for instrument_id")
}
tempResp.InstrumentID = strData
strData, ok = result[i]["product_id"].(string)
if !ok {
return resp, errors.New("type conversion failed for product_id")
}
tempResp.ProductID = strData
resp = append(resp, tempResp)
}
}
return resp, nil
}
// GetMarginRates gets interest rates for margin currencies
func (o *OKEX) GetMarginRates(ctx context.Context, instrumentID currency.Pair) (okgroup.MarginCurrencyData, error) {
var resp okgroup.MarginCurrencyData
resp.Data = make(map[string]okgroup.MarginData)
var result []map[string]interface{}
err := o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet,
okGroupMarginSubsection,
fmt.Sprintf(okGroupMarginPairData, instrumentID),
nil,
&result,
true)
if err != nil {
return resp, err
}
for i := range result {
for k, v := range result[i] {
var byteData []byte
var marginData okgroup.MarginData
byteData, err = json.Marshal(v)
if err != nil {
return resp, err
}
if strings.Contains(k, instrumentID.Base.String()) {
err = json.Unmarshal(byteData, &marginData)
if err != nil {
return resp, err
}
resp.Data[instrumentID.Base.String()] = marginData
} else if strings.Contains(k, instrumentID.Quote.String()) {
err = json.Unmarshal(byteData, &marginData)
if err != nil {
return resp, err
}
resp.Data[instrumentID.Quote.String()] = marginData
}
}
var strData string
var ok bool
strData, ok = result[i]["instrument_id"].(string)
if !ok {
return resp, errors.New("type conversion failed for instrument_id")
}
resp.InstrumentID = strData
strData, ok = result[i]["product_id"].(string)
if !ok {
return resp, errors.New("type conversion failed for product_id")
}
resp.ProductID = strData
}
return resp, nil
}
// GetSpotMarkets gets perpetual swap markets' data
func (o *OKEX) GetSpotMarkets(ctx context.Context) ([]okgroup.TradingPairData, error) {
var resp []okgroup.TradingPairData
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSpotSubsection, okGroupInstruments, nil, &resp, false)
}
// GetFundingRate gets funding rate of a given currency
func (o *OKEX) GetFundingRate(ctx context.Context, marketName, limit string) ([]okgroup.PerpSwapFundingRates, error) {
params := url.Values{}
params.Set("limit", limit)
var resp []okgroup.PerpSwapFundingRates
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection,
fmt.Sprintf(okGroupPerpSwapRates, marketName)+params.Encode(),
nil, &resp, false)
}
// GetPerpSwapMarkets gets perpetual swap markets' data
func (o *OKEX) GetPerpSwapMarkets(ctx context.Context) ([]okgroup.TickerData, error) {
var resp []okgroup.TickerData
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection,
okGroupPerpTickers,
nil, &resp, false)
}
// GetFuturesPostions Get the information of all holding positions in futures trading.
// Due to high energy consumption, you are advised to capture data with the "Futures Account of a Currency" API instead.
func (o *OKEX) GetFuturesPostions(ctx context.Context) (resp okgroup.GetFuturesPositionsResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, okGroupFuturePosition, nil, &resp, true)
}
// GetFuturesPostionsForCurrency Get the information of holding positions of a contract.
func (o *OKEX) GetFuturesPostionsForCurrency(ctx context.Context, instrumentID string) (resp okgroup.GetFuturesPositionsForCurrencyResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v", instrumentID, okGroupFuturePosition)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
// GetFuturesAccountOfAllCurrencies Get the futures account info of all token.
// Due to high energy consumption, you are advised to capture data with the "Futures Account of a Currency" API instead.
func (o *OKEX) GetFuturesAccountOfAllCurrencies(ctx context.Context) (resp okgroup.FuturesAccountForAllCurrenciesResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, okgroup.OKGroupAccounts, nil, &resp, true)
}
// GetFuturesAccountOfACurrency Get the futures account info of a token.
func (o *OKEX) GetFuturesAccountOfACurrency(ctx context.Context, instrumentID string) (resp okgroup.FuturesCurrencyData, _ error) {
requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupAccounts, instrumentID)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
// GetFuturesLeverage Get the leverage of the futures account
func (o *OKEX) GetFuturesLeverage(ctx context.Context, instrumentID string) (resp okgroup.GetFuturesLeverageResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupAccounts, instrumentID, okGroupFutureLeverage)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
// SetFuturesLeverage Adjusting the leverage for futures account。
// Cross margin request requirements: {"leverage":"10"}
// Fixed margin request requirements: {"instrument_id":"BTC-USD-180213","direction":"long","leverage":"10"}
func (o *OKEX) SetFuturesLeverage(ctx context.Context, request okgroup.SetFuturesLeverageRequest) (resp okgroup.SetFuturesLeverageResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupAccounts, request.Currency, okGroupFutureLeverage)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupFuturesSubsection, requestURL, request, &resp, true)
}
// GetFuturesBillDetails Shows the accounts historical coin in flow and out flow.
// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
func (o *OKEX) GetFuturesBillDetails(ctx context.Context, request okgroup.GetSpotBillDetailsForCurrencyRequest) (resp []okgroup.GetSpotBillDetailsForCurrencyResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupAccounts, request.Currency, okgroup.OKGroupLedger, okgroup.FormatParameters(request))
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
// PlaceFuturesOrder OKEx futures trading only supports limit orders.
// You can place an order only if you have enough funds. Once your order is placed, the amount will be put on hold in the order lifecycle.
// The assets and amount on hold depends on the order's specific type and parameters.
func (o *OKEX) PlaceFuturesOrder(ctx context.Context, request okgroup.PlaceFuturesOrderRequest) (resp okgroup.PlaceFuturesOrderResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupFuturesSubsection, okGroupFutureOrder, request, &resp, true)
}
// PlaceFuturesOrderBatch Batch contract placing order operation.
func (o *OKEX) PlaceFuturesOrderBatch(ctx context.Context, request okgroup.PlaceFuturesOrderBatchRequest) (resp okgroup.PlaceFuturesOrderBatchResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupFuturesSubsection, okgroup.OKGroupOrders, request, &resp, true)
}
// CancelFuturesOrder Cancelling an unfilled order.
func (o *OKEX) CancelFuturesOrder(ctx context.Context, request okgroup.CancelFuturesOrderRequest) (resp okgroup.CancelFuturesOrderResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupCancelOrder, request.InstrumentID, request.OrderID)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupFuturesSubsection, requestURL, request, &resp, true)
}
// CancelFuturesOrderBatch With best effort, cancel all open orders.
func (o *OKEX) CancelFuturesOrderBatch(ctx context.Context, request okgroup.CancelMultipleSpotOrdersRequest) (resp okgroup.CancelMultipleSpotOrdersResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupCancelBatchOrders, request.InstrumentID)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupFuturesSubsection, requestURL, request, &resp, true)
}
// GetFuturesOrderList List your orders. Cursor pagination is used.
// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
func (o *OKEX) GetFuturesOrderList(ctx context.Context, request okgroup.GetFuturesOrdersListRequest) (resp okgroup.GetFuturesOrderListResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v%v", okgroup.OKGroupOrders, request.InstrumentID, okgroup.FormatParameters(request))
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
// GetFuturesOrderDetails Get order details by order ID.
func (o *OKEX) GetFuturesOrderDetails(ctx context.Context, request okgroup.GetFuturesOrderDetailsRequest) (resp okgroup.GetFuturesOrderDetailsResponseData, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupOrders, request.InstrumentID, request.OrderID)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
// GetFuturesTransactionDetails Get details of the recent filled orders. Cursor pagination is used.
// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
func (o *OKEX) GetFuturesTransactionDetails(ctx context.Context, request okgroup.GetFuturesTransactionDetailsRequest) (resp []okgroup.GetFuturesTransactionDetailsResponse, _ error) {
requestURL := fmt.Sprintf("%v%v", okgroup.OKGroupGetSpotTransactionDetails, okgroup.FormatParameters(request))
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
// GetFuturesContractInformation Get market data. This endpoint provides the snapshots of market data and can be used without verifications.
func (o *OKEX) GetFuturesContractInformation(ctx context.Context) (resp []okgroup.GetFuturesContractInformationResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, okgroup.OKGroupInstruments, nil, &resp, false)
}
// GetAllFuturesTokenInfo Get the last traded price, best bid/ask price, 24 hour trading volume and more info of all contracts.
func (o *OKEX) GetAllFuturesTokenInfo(ctx context.Context) (resp []okgroup.GetFuturesTokenInfoResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupInstruments, okgroup.OKGroupTicker)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
// GetFuturesTokenInfoForCurrency Get the last traded price, best bid/ask price, 24 hour trading volume and more info of a contract.
func (o *OKEX) GetFuturesTokenInfoForCurrency(ctx context.Context, instrumentID string) (resp okgroup.GetFuturesTokenInfoResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okgroup.OKGroupTicker)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
// GetFuturesFilledOrder Get the recent 300 transactions of all contracts. Pagination is not supported here.
// The whole book will be returned for one request. Websocket is recommended here.
func (o *OKEX) GetFuturesFilledOrder(ctx context.Context, request okgroup.GetFuturesFilledOrderRequest) (resp []okgroup.GetFuturesFilledOrdersResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupInstruments, request.InstrumentID, okgroup.OKGroupTrades, okgroup.FormatParameters(request))
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
// GetFuturesHoldAmount Get the number of futures with hold.
func (o *OKEX) GetFuturesHoldAmount(ctx context.Context, instrumentID string) (resp okgroup.GetFuturesHoldAmountResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupAccounts, instrumentID, okGroupFutureHolds)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, true)
}
// GetFuturesIndices Get Indices of tokens. This is a public endpoint, no identity verification is needed.
func (o *OKEX) GetFuturesIndices(ctx context.Context, instrumentID string) (resp okgroup.GetFuturesIndicesResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okGroupIndices)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
// GetFuturesExchangeRates Get the fiat exchange rates. This is a public endpoint, no identity verification is needed.
func (o *OKEX) GetFuturesExchangeRates(ctx context.Context) (resp okgroup.GetFuturesExchangeRatesResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, okGroupRate, nil, &resp, false)
}
// GetFuturesEstimatedDeliveryPrice the estimated delivery price. It is available 3 hours before delivery.
// This is a public endpoint, no identity verification is needed.
func (o *OKEX) GetFuturesEstimatedDeliveryPrice(ctx context.Context, instrumentID string) (resp okgroup.GetFuturesEstimatedDeliveryPriceResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okGroupEsimtatedPrice)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
// GetFuturesOpenInterests Get the open interest of a contract. This is a public endpoint, no identity verification is needed.
func (o *OKEX) GetFuturesOpenInterests(ctx context.Context, instrumentID string) (resp okgroup.GetFuturesOpenInterestsResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okGroupOpenInterest)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
// GetFuturesCurrentPriceLimit The maximum buying price and the minimum selling price of the contract.
// This is a public endpoint, no identity verification is needed.
func (o *OKEX) GetFuturesCurrentPriceLimit(ctx context.Context, instrumentID string) (resp okgroup.GetFuturesCurrentPriceLimitResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okgroup.OKGroupPriceLimit)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
// GetFuturesCurrentMarkPrice The maximum buying price and the minimum selling price of the contract.
// This is a public endpoint, no identity verification is needed.
func (o *OKEX) GetFuturesCurrentMarkPrice(ctx context.Context, instrumentID string) (resp okgroup.GetFuturesCurrentMarkPriceResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okgroup.OKGroupMarkPrice)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
// GetFuturesForceLiquidatedOrders Get force liquidated orders. This is a public endpoint, no identity verification is needed.
func (o *OKEX) GetFuturesForceLiquidatedOrders(ctx context.Context, request okgroup.GetFuturesForceLiquidatedOrdersRequest) (resp []okgroup.GetFuturesForceLiquidatedOrdersResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupInstruments, request.InstrumentID, okgroup.OKGroupLiquidation, okgroup.FormatParameters(request))
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupFuturesSubsection, requestURL, nil, &resp, false)
}
// GetFuturesTagPrice Get the tag price. This is a public endpoint, no identity verification is needed.
func (o *OKEX) GetFuturesTagPrice(ctx context.Context, instrumentID string) (resp okgroup.GetFuturesTagPriceResponse, _ error) {
// OKEX documentation is missing for this endpoint. Guessing "tag_price" for the URL results in 404
return okgroup.GetFuturesTagPriceResponse{}, common.ErrNotYetImplemented
}
// GetSwapPostions Get the information of all holding positions in swap trading.
// Due to high energy consumption, you are advised to capture data with the "Swap Account of a Currency" API instead.
func (o *OKEX) GetSwapPostions(ctx context.Context) (resp []okgroup.GetSwapPostionsResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, okGroupFuturePosition, nil, &resp, true)
}
// GetSwapPostionsForContract Get the information of holding positions of a contract.
func (o *OKEX) GetSwapPostionsForContract(ctx context.Context, instrumentID string) (resp okgroup.GetSwapPostionsResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v", instrumentID, okGroupFuturePosition)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
}
// GetSwapAccountOfAllCurrency Get the perpetual swap account info of a token.
// Margin ratio set as 10,000 when users have no open position.
func (o *OKEX) GetSwapAccountOfAllCurrency(ctx context.Context) (resp okgroup.GetSwapAccountOfAllCurrencyResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, okgroup.OKGroupAccounts, nil, &resp, true)
}
// GetSwapAccountSettingsOfAContract Get leverage level and margin mode of a contract.
func (o *OKEX) GetSwapAccountSettingsOfAContract(ctx context.Context, instrumentID string) (resp okgroup.GetSwapAccountSettingsOfAContractResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupAccounts, instrumentID, okGroupSettings)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
}
// SetSwapLeverageLevelOfAContract Setting the leverage level of a contract
// TODO this returns invalid parameters, but matches spec. Unsure how to fix
func (o *OKEX) SetSwapLeverageLevelOfAContract(ctx context.Context, request okgroup.SetSwapLeverageLevelOfAContractRequest) (resp okgroup.SetSwapLeverageLevelOfAContractResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupAccounts, request.InstrumentID, okGroupFutureLeverage)
request.InstrumentID = ""
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupSwapSubsection, requestURL, request, &resp, true)
}
// GetSwapBillDetails Shows the accounts historical coin in flow and out flow.
// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
func (o *OKEX) GetSwapBillDetails(ctx context.Context, request okgroup.GetSpotBillDetailsForCurrencyRequest) (resp []okgroup.GetSwapBillDetailsResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupAccounts, request.Currency, okgroup.OKGroupLedger, okgroup.FormatParameters(request))
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
}
// PlaceSwapOrder OKEx perpetual swap trading only supports limit ordersUSD as quote currency for orders.
// You can place an order only if you have enough funds. Once your order is placed, the amount will be put on hold in the order lifecycle.
// The assets and amount on hold depends on the order's specific type and parameters.
func (o *OKEX) PlaceSwapOrder(ctx context.Context, request okgroup.PlaceSwapOrderRequest) (resp okgroup.PlaceSwapOrderResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupSwapSubsection, okGroupFutureOrder, request, &resp, true)
}
// PlaceMultipleSwapOrders Batch contract placing order operation.
func (o *OKEX) PlaceMultipleSwapOrders(ctx context.Context, request okgroup.PlaceMultipleSwapOrdersRequest) (resp okgroup.PlaceMultipleSwapOrdersResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupSwapSubsection, okgroup.OKGroupOrders, request, &resp, true)
}
// CancelSwapOrder Cancelling an unfilled order
func (o *OKEX) CancelSwapOrder(ctx context.Context, request okgroup.CancelSwapOrderRequest) (resp okgroup.CancelSwapOrderResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupCancelOrder, request.InstrumentID, request.OrderID)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupSwapSubsection, requestURL, nil, &resp, true)
}
// CancelMultipleSwapOrders With best effort, cancel all open orders.
func (o *OKEX) CancelMultipleSwapOrders(ctx context.Context, request okgroup.CancelMultipleSwapOrdersRequest) (resp okgroup.CancelMultipleSwapOrdersResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupCancelBatchOrders, request.InstrumentID)
request.InstrumentID = ""
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupSwapSubsection, requestURL, request, &resp, true)
}
// GetSwapOrderList List your orders. Cursor pagination is used.
// All paginated requests return the latest information (newest) as the first page sorted by newest (in chronological time) first.
func (o *OKEX) GetSwapOrderList(ctx context.Context, request okgroup.GetSwapOrderListRequest) (resp okgroup.GetSwapOrderListResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v%v", okgroup.OKGroupOrders, request.InstrumentID, okgroup.FormatParameters(request))
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
}
// GetSwapOrderDetails Get order details by order ID.
func (o *OKEX) GetSwapOrderDetails(ctx context.Context, request okgroup.GetSwapOrderDetailsRequest) (resp okgroup.GetSwapOrderListResponseData, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupOrders, request.InstrumentID, request.OrderID)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
}
// GetSwapTransactionDetails Get details of the recent filled orders
func (o *OKEX) GetSwapTransactionDetails(ctx context.Context, request okgroup.GetSwapTransactionDetailsRequest) (resp []okgroup.GetSwapTransactionDetailsResponse, _ error) {
requestURL := fmt.Sprintf("%v%v", okgroup.OKGroupGetSpotTransactionDetails, okgroup.FormatParameters(request))
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
}
// GetSwapContractInformation Get market data.
func (o *OKEX) GetSwapContractInformation(ctx context.Context) (resp []okgroup.GetSwapContractInformationResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, okgroup.OKGroupInstruments, nil, &resp, false)
}
// GetAllSwapTokensInformation Get the last traded price, best bid/ask price, 24 hour trading volume and more info of all contracts.
func (o *OKEX) GetAllSwapTokensInformation(ctx context.Context) (resp []okgroup.GetAllSwapTokensInformationResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupInstruments, okgroup.OKGroupTicker)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, false)
}
// GetSwapTokensInformationForCurrency Get the last traded price, best bid/ask price, 24 hour trading volume and more info of all contracts.
func (o *OKEX) GetSwapTokensInformationForCurrency(ctx context.Context, instrumentID string) (resp okgroup.GetAllSwapTokensInformationResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okgroup.OKGroupTicker)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, false)
}
// GetSwapFilledOrdersData Get details of the recent filled orders
func (o *OKEX) GetSwapFilledOrdersData(ctx context.Context, request *okgroup.GetSwapFilledOrdersDataRequest) (resp []okgroup.GetSwapFilledOrdersDataResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupInstruments, request.InstrumentID, okgroup.OKGroupTrades, okgroup.FormatParameters(request))
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, false)
}
// GetSwapIndices Get Indices of tokens.
func (o *OKEX) GetSwapIndices(ctx context.Context, instrumentID string) (resp okgroup.GetSwapIndecesResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okGroupIndices)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, false)
}
// GetSwapExchangeRates Get the fiat exchange rates.
func (o *OKEX) GetSwapExchangeRates(ctx context.Context) (resp okgroup.GetSwapExchangeRatesResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, okGroupRate, nil, &resp, false)
}
// GetSwapOpenInterest Get the open interest of a contract.
func (o *OKEX) GetSwapOpenInterest(ctx context.Context, instrumentID string) (resp okgroup.GetSwapExchangeRatesResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okGroupOpenInterest)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, false)
}
// GetSwapCurrentPriceLimits Get the open interest of a contract.
func (o *OKEX) GetSwapCurrentPriceLimits(ctx context.Context, instrumentID string) (resp okgroup.GetSwapCurrentPriceLimitsResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okgroup.OKGroupPriceLimit)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, false)
}
// GetSwapForceLiquidatedOrders Get force liquidated orders.
func (o *OKEX) GetSwapForceLiquidatedOrders(ctx context.Context, request okgroup.GetSwapForceLiquidatedOrdersRequest) (resp []okgroup.GetSwapForceLiquidatedOrdersResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupInstruments, request.InstrumentID, okgroup.OKGroupLiquidation, okgroup.FormatParameters(request))
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, false)
}
// GetSwapOnHoldAmountForOpenOrders Get On Hold Amount for Open Orders.
func (o *OKEX) GetSwapOnHoldAmountForOpenOrders(ctx context.Context, instrumentID string) (resp okgroup.GetSwapOnHoldAmountForOpenOrdersResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupAccounts, instrumentID, okGroupFutureHolds)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, true)
}
// GetSwapNextSettlementTime Get the time of next settlement.
func (o *OKEX) GetSwapNextSettlementTime(ctx context.Context, instrumentID string) (resp okgroup.GetSwapNextSettlementTimeResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okGroupFundingTime)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, false)
}
// GetSwapMarkPrice Get the time of next settlement.
func (o *OKEX) GetSwapMarkPrice(ctx context.Context, instrumentID string) (resp okgroup.GetSwapMarkPriceResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupInstruments, instrumentID, okgroup.OKGroupMarkPrice)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, false)
}
// GetSwapFundingRateHistory Get Funding Rate History.
func (o *OKEX) GetSwapFundingRateHistory(ctx context.Context, request okgroup.GetSwapFundingRateHistoryRequest) (resp []okgroup.GetSwapFundingRateHistoryResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v%v", okgroup.OKGroupInstruments, request.InstrumentID, okGroupHistoricalFundingRate, okgroup.FormatParameters(request))
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupSwapSubsection, requestURL, nil, &resp, false)
}
// GetETT List the assets in ETT account. Get information such as balance, amount on hold/ available.
func (o *OKEX) GetETT(ctx context.Context) (resp []okgroup.GetETTResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupETTSubsection, okgroup.OKGroupAccounts, nil, &resp, true)
}
// GetETTAccountInformationForCurrency Getting the balance, amount available/on hold of a token in ETT account.
func (o *OKEX) GetETTAccountInformationForCurrency(ctx context.Context, currency string) (resp okgroup.GetETTResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupAccounts, currency)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupETTSubsection, requestURL, nil, &resp, true)
}
// GetETTBillsDetails Bills details. All paginated requests return the latest information (newest)
// as the first page sorted by newest (in chronological time) first
func (o *OKEX) GetETTBillsDetails(ctx context.Context, currency string) (resp []okgroup.GetETTBillsDetailsResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v/%v", okgroup.OKGroupAccounts, currency, okgroup.OKGroupLedger)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupETTSubsection, requestURL, nil, &resp, true)
}
// PlaceETTOrder You can place subscription or redemption orders under ETT trading.
// You can place an order only if you have enough funds. Once your order is placed,
// the amount will be put on hold in the order lifecycle.
// The assets and amount on hold depends on the order's specific type and parameters.
func (o *OKEX) PlaceETTOrder(ctx context.Context, request *okgroup.PlaceETTOrderRequest) (resp okgroup.PlaceETTOrderResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupETTSubsection, okgroup.OKGroupOrders, nil, &resp, true)
}
// CancelETTOrder Cancel an unfilled order.
func (o *OKEX) CancelETTOrder(ctx context.Context, orderID string) (resp okgroup.PlaceETTOrderResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupOrders, orderID)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, okGroupETTSubsection, requestURL, nil, &resp, true)
}
// GetETTOrderList List your orders. Cursor pagination is used. All paginated requests return the latest information
// (newest) as the first page sorted by newest (in chronological time) first.
func (o *OKEX) GetETTOrderList(ctx context.Context, request okgroup.GetETTOrderListRequest) (resp []okgroup.GetETTOrderListResponse, _ error) {
requestURL := fmt.Sprintf("%v%v", okgroup.OKGroupOrders, okgroup.FormatParameters(request))
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupETTSubsection, requestURL, nil, &resp, true)
}
// GetETTOrderDetails Get order details by order ID.
func (o *OKEX) GetETTOrderDetails(ctx context.Context, orderID string) (resp okgroup.GetETTOrderListResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v", okgroup.OKGroupOrders, orderID)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupETTSubsection, requestURL, nil, &resp, true)
}
// GetETTConstituents Get ETT Constituents.This is a public endpoint, no identity verification is needed.
func (o *OKEX) GetETTConstituents(ctx context.Context, ett string) (resp okgroup.GetETTConstituentsResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v", okGroupConstituents, ett)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupETTSubsection, requestURL, nil, &resp, false)
}
// GetETTSettlementPriceHistory Get ETT settlement price history. This is a public endpoint, no identity verification is needed.
func (o *OKEX) GetETTSettlementPriceHistory(ctx context.Context, ett string) (resp []okgroup.GetETTSettlementPriceHistoryResponse, _ error) {
requestURL := fmt.Sprintf("%v/%v", okGroupDefinePrice, ett)
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupETTSubsection, requestURL, nil, &resp, false)
}

File diff suppressed because one or more lines are too long

View File

@@ -1,676 +0,0 @@
package okex
import (
"context"
"errors"
"fmt"
"sort"
"strings"
"sync"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
"github.com/thrasher-corp/gocryptotrader/exchanges/okgroup"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/protocol"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
"github.com/thrasher-corp/gocryptotrader/log"
)
// GetDefaultConfig returns a default exchange config
func (o *OKEX) GetDefaultConfig() (*config.Exchange, error) {
o.SetDefaults()
exchCfg := new(config.Exchange)
exchCfg.Name = o.Name
exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout
exchCfg.BaseCurrencies = o.BaseCurrencies
err := o.SetupDefaults(exchCfg)
if err != nil {
return nil, err
}
if o.Features.Supports.RESTCapabilities.AutoPairUpdates {
err = o.UpdateTradablePairs(context.TODO(), true)
if err != nil {
return nil, err
}
}
return exchCfg, nil
}
// SetDefaults method assignes the default values for OKEX
func (o *OKEX) SetDefaults() {
o.SetErrorDefaults()
o.SetCheckVarDefaults()
o.Name = okExExchangeName
o.Enabled = true
o.Verbose = true
o.API.CredentialsValidator.RequiresKey = true
o.API.CredentialsValidator.RequiresSecret = true
o.API.CredentialsValidator.RequiresClientID = true
// Same format used for perpetual swap and futures
futures := currency.PairStore{
RequestFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: currency.DashDelimiter,
},
ConfigFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: currency.UnderscoreDelimiter,
},
}
swap := currency.PairStore{
RequestFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: currency.DashDelimiter,
},
ConfigFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: currency.UnderscoreDelimiter,
},
}
err := o.StoreAssetPairFormat(asset.PerpetualSwap, swap)
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
err = o.StoreAssetPairFormat(asset.Futures, futures)
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
index := currency.PairStore{
RequestFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: currency.DashDelimiter,
},
ConfigFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: currency.DashDelimiter,
},
}
spot := currency.PairStore{
RequestFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: currency.DashDelimiter,
},
ConfigFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: currency.DashDelimiter,
},
}
err = o.StoreAssetPairFormat(asset.Spot, spot)
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
err = o.StoreAssetPairFormat(asset.Index, index)
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
o.Features = exchange.Features{
Supports: exchange.FeaturesSupported{
REST: true,
Websocket: true,
RESTCapabilities: protocol.Features{
TickerBatching: true,
TickerFetching: true,
KlineFetching: true,
TradeFetching: true,
OrderbookFetching: true,
AutoPairUpdates: true,
AccountInfo: true,
GetOrder: true,
GetOrders: true,
CancelOrder: true,
CancelOrders: true,
SubmitOrder: true,
SubmitOrders: true,
DepositHistory: true,
WithdrawalHistory: true,
UserTradeHistory: true,
CryptoDeposit: true,
CryptoWithdrawal: true,
TradeFee: true,
CryptoWithdrawalFee: true,
},
WebsocketCapabilities: protocol.Features{
TickerFetching: true,
TradeFetching: true,
KlineFetching: true,
OrderbookFetching: true,
Subscribe: true,
Unsubscribe: true,
AuthenticatedEndpoints: true,
MessageCorrelation: true,
GetOrders: true,
GetOrder: true,
AccountBalance: true,
},
WithdrawPermissions: exchange.AutoWithdrawCrypto |
exchange.NoFiatWithdrawals,
Kline: kline.ExchangeCapabilitiesSupported{
DateRanges: true,
Intervals: true,
},
},
Enabled: exchange.FeaturesEnabled{
AutoPairUpdates: true,
Kline: kline.ExchangeCapabilitiesEnabled{
Intervals: map[string]bool{
kline.OneMin.Word(): true,
kline.ThreeMin.Word(): true,
kline.FiveMin.Word(): true,
kline.FifteenMin.Word(): true,
kline.ThirtyMin.Word(): true,
kline.OneHour.Word(): true,
kline.TwoHour.Word(): true,
kline.FourHour.Word(): true,
kline.SixHour.Word(): true,
kline.TwelveHour.Word(): true,
kline.OneDay.Word(): true,
kline.ThreeDay.Word(): true,
kline.OneWeek.Word(): true,
},
ResultLimit: 1440,
},
},
}
o.Requester, err = request.New(o.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
// TODO: Specify each individual endpoint rate limits as per docs
request.WithLimiter(request.NewBasicRateLimit(okExRateInterval, okExRequestRate)),
)
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
o.API.Endpoints = o.NewEndpoints()
err = o.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: okExAPIURL,
exchange.WebsocketSpot: OkExWebsocketURL,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
o.Websocket = stream.New()
o.APIVersion = okExAPIVersion
o.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
o.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
o.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
}
// Start starts the OKGroup go routine
func (o *OKEX) Start(wg *sync.WaitGroup) error {
if wg == nil {
return fmt.Errorf("%T %w", wg, common.ErrNilPointer)
}
wg.Add(1)
go func() {
o.Run()
wg.Done()
}()
return nil
}
// Run implements the OKEX wrapper
func (o *OKEX) Run() {
if o.Verbose {
wsEndpoint, err := o.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
log.Error(log.ExchangeSys, err)
}
log.Debugf(log.ExchangeSys,
"%s Websocket: %s. (url: %s).\n",
o.Name,
common.IsEnabled(o.Websocket.IsEnabled()),
wsEndpoint)
}
format, err := o.GetPairFormat(asset.Spot, false)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update tradable pairs. Err: %s",
o.Name,
err)
return
}
forceUpdate := false
if !o.BypassConfigFormatUpgrades {
var enabled, avail currency.Pairs
enabled, err = o.GetEnabledPairs(asset.Spot)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update tradable pairs. Err: %s",
o.Name,
err)
return
}
avail, err = o.GetAvailablePairs(asset.Spot)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update tradable pairs. Err: %s",
o.Name,
err)
return
}
if !common.StringDataContains(enabled.Strings(), format.Delimiter) ||
!common.StringDataContains(avail.Strings(), format.Delimiter) {
forceUpdate = true
var p currency.Pairs
p, err = currency.NewPairsFromStrings([]string{currency.BTC.String() +
format.Delimiter +
currency.USDT.String()})
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update currencies.\n",
o.Name)
} else {
log.Warnf(log.ExchangeSys, exchange.ResetConfigPairsWarningMessage, o.Name, asset.Spot, p)
err = o.UpdatePairs(p, asset.Spot, true, forceUpdate)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update currencies.\n",
o.Name)
return
}
}
}
}
if !o.GetEnabledFeatures().AutoPairUpdates && !forceUpdate {
return
}
err = o.UpdateTradablePairs(context.TODO(), forceUpdate)
if err != nil {
log.Errorf(log.ExchangeSys,
"%s failed to update tradable pairs. Err: %s",
o.Name,
err)
}
}
// FetchTradablePairs returns a list of the exchanges tradable pairs
func (o *OKEX) FetchTradablePairs(ctx context.Context, i asset.Item) ([]string, error) {
var pairs []string
format, err := o.GetPairFormat(i, false)
if err != nil {
return nil, err
}
switch i {
case asset.Spot:
prods, err := o.GetSpotTokenPairDetails(ctx)
if err != nil {
return nil, err
}
for x := range prods {
pairs = append(pairs,
currency.NewPairWithDelimiter(prods[x].BaseCurrency,
prods[x].QuoteCurrency,
format.Delimiter).String())
}
return pairs, nil
case asset.Futures:
prods, err := o.GetFuturesContractInformation(ctx)
if err != nil {
return nil, err
}
for x := range prods {
p := strings.Split(prods[x].InstrumentID, currency.DashDelimiter)
pairs = append(pairs, p[0]+currency.DashDelimiter+p[1]+format.Delimiter+p[2])
}
return pairs, nil
case asset.PerpetualSwap:
prods, err := o.GetSwapContractInformation(ctx)
if err != nil {
return nil, err
}
for x := range prods {
pairs = append(pairs,
prods[x].UnderlyingIndex+
currency.DashDelimiter+
prods[x].QuoteCurrency+
format.Delimiter+
"SWAP")
}
return pairs, nil
case asset.Index:
// This is updated in futures index
return nil, errors.New("index updated in futures")
}
return nil, fmt.Errorf("%s invalid asset type", o.Name)
}
// UpdateTradablePairs updates the exchanges available pairs and stores
// them in the exchanges config
func (o *OKEX) UpdateTradablePairs(ctx context.Context, forceUpdate bool) error {
assets := o.CurrencyPairs.GetAssetTypes(false)
for x := range assets {
if assets[x] == asset.Index {
// Update from futures
continue
}
pairs, err := o.FetchTradablePairs(ctx, assets[x])
if err != nil {
return err
}
if assets[x] == asset.Futures {
var indexPairs []string
var futuresContracts []string
for i := range pairs {
item := strings.Split(pairs[i], currency.UnderscoreDelimiter)[0]
futuresContracts = append(futuresContracts, pairs[i])
if common.StringDataContains(indexPairs, item) {
continue
}
indexPairs = append(indexPairs, item)
}
var indexPair currency.Pairs
indexPair, err = currency.NewPairsFromStrings(indexPairs)
if err != nil {
return err
}
err = o.UpdatePairs(indexPair, asset.Index, false, forceUpdate)
if err != nil {
return err
}
var futurePairs currency.Pairs
for i := range futuresContracts {
var c currency.Pair
c, err = currency.NewPairDelimiter(futuresContracts[i], currency.UnderscoreDelimiter)
if err != nil {
return err
}
futurePairs = append(futurePairs, c)
}
err = o.UpdatePairs(futurePairs, asset.Futures, false, forceUpdate)
if err != nil {
return err
}
continue
}
p, err := currency.NewPairsFromStrings(pairs)
if err != nil {
return err
}
err = o.UpdatePairs(p, assets[x], false, forceUpdate)
if err != nil {
return err
}
}
return nil
}
// UpdateTickers updates the ticker for all currency pairs of a given asset type
func (o *OKEX) UpdateTickers(ctx context.Context, a asset.Item) error {
switch a {
case asset.Spot:
resp, err := o.GetSpotAllTokenPairsInformation(ctx)
if err != nil {
return err
}
enabled, err := o.GetEnabledPairs(asset.Spot)
if err != nil {
return err
}
for j := range resp {
if !enabled.Contains(resp[j].InstrumentID, true) {
continue
}
err = ticker.ProcessTicker(&ticker.Price{
Last: resp[j].Last,
High: resp[j].High24h,
Low: resp[j].Low24h,
Bid: resp[j].BestBid,
Ask: resp[j].BestAsk,
Volume: resp[j].BaseVolume24h,
QuoteVolume: resp[j].QuoteVolume24h,
Open: resp[j].Open24h,
Pair: resp[j].InstrumentID,
LastUpdated: resp[j].Timestamp,
ExchangeName: o.Name,
AssetType: a})
if err != nil {
return err
}
}
case asset.PerpetualSwap:
resp, err := o.GetAllSwapTokensInformation(ctx)
if err != nil {
return err
}
enabled, err := o.GetEnabledPairs(asset.PerpetualSwap)
if err != nil {
return err
}
for j := range resp {
p := strings.Split(resp[j].InstrumentID, currency.DashDelimiter)
nC := currency.NewPairWithDelimiter(p[0]+currency.DashDelimiter+p[1],
p[2],
currency.UnderscoreDelimiter)
if !enabled.Contains(nC, true) {
continue
}
err = ticker.ProcessTicker(&ticker.Price{
Last: resp[j].Last,
High: resp[j].High24H,
Low: resp[j].Low24H,
Bid: resp[j].BestBid,
Ask: resp[j].BestAsk,
Volume: resp[j].Volume24H,
Pair: nC,
LastUpdated: resp[j].Timestamp,
ExchangeName: o.Name,
AssetType: a})
if err != nil {
return err
}
}
case asset.Futures:
resp, err := o.GetAllFuturesTokenInfo(ctx)
if err != nil {
return err
}
enabled, err := o.GetEnabledPairs(asset.Futures)
if err != nil {
return err
}
for j := range resp {
p := strings.Split(resp[j].InstrumentID, currency.DashDelimiter)
nC := currency.NewPairWithDelimiter(p[0]+currency.DashDelimiter+p[1],
p[2],
currency.UnderscoreDelimiter)
if !enabled.Contains(nC, true) {
continue
}
err = ticker.ProcessTicker(&ticker.Price{
Last: resp[j].Last,
High: resp[j].High24h,
Low: resp[j].Low24h,
Bid: resp[j].BestBid,
Ask: resp[j].BestAsk,
Volume: resp[j].Volume24h,
Pair: nC,
LastUpdated: resp[j].Timestamp,
ExchangeName: o.Name,
AssetType: a})
if err != nil {
return err
}
}
}
return nil
}
// UpdateTicker updates and returns the ticker for a currency pair
func (o *OKEX) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) {
if err := o.UpdateTickers(ctx, a); err != nil {
return nil, err
}
return ticker.GetTicker(o.Name, p, a)
}
// FetchTicker returns the ticker for a currency pair
func (o *OKEX) FetchTicker(ctx context.Context, p currency.Pair, assetType asset.Item) (tickerData *ticker.Price, err error) {
if assetType == asset.Index {
return tickerData, errors.New("ticker fetching not supported for index")
}
fPair, err := o.FormatExchangeCurrency(p, assetType)
if err != nil {
return nil, err
}
tickerData, err = ticker.GetTicker(o.Name, fPair, assetType)
if err != nil {
return o.UpdateTicker(ctx, fPair, assetType)
}
return
}
// GetRecentTrades returns recent trade data
func (o *OKEX) GetRecentTrades(ctx context.Context, p currency.Pair, assetType asset.Item) ([]trade.Data, error) {
var err error
p, err = o.FormatExchangeCurrency(p, assetType)
if err != nil {
return nil, err
}
var resp []trade.Data
var side order.Side
switch assetType {
case asset.Spot:
var tradeData []okgroup.GetSpotFilledOrdersInformationResponse
tradeData, err = o.GetSpotFilledOrdersInformation(ctx,
okgroup.GetSpotFilledOrdersInformationRequest{
InstrumentID: p.String(),
})
if err != nil {
return nil, err
}
for i := range tradeData {
side, err = order.StringToOrderSide(tradeData[i].Side)
if err != nil {
return nil, err
}
resp = append(resp, trade.Data{
Exchange: o.Name,
TID: tradeData[i].TradeID,
CurrencyPair: p,
Side: side,
AssetType: assetType,
Price: tradeData[i].Price,
Amount: tradeData[i].Size,
Timestamp: tradeData[i].Timestamp,
})
}
case asset.Futures:
var tradeData []okgroup.GetFuturesFilledOrdersResponse
tradeData, err = o.GetFuturesFilledOrder(ctx,
okgroup.GetFuturesFilledOrderRequest{
InstrumentID: p.String(),
})
if err != nil {
return nil, err
}
for i := range tradeData {
side, err = order.StringToOrderSide(tradeData[i].Side)
if err != nil {
return nil, err
}
resp = append(resp, trade.Data{
Exchange: o.Name,
TID: tradeData[i].TradeID,
CurrencyPair: p,
Side: side,
AssetType: assetType,
Price: tradeData[i].Price,
Amount: tradeData[i].Qty,
Timestamp: tradeData[i].Timestamp,
})
}
case asset.PerpetualSwap:
var tradeData []okgroup.GetSwapFilledOrdersDataResponse
tradeData, err = o.GetSwapFilledOrdersData(ctx,
&okgroup.GetSwapFilledOrdersDataRequest{
InstrumentID: p.String(),
})
if err != nil {
return nil, err
}
for i := range tradeData {
side, err = order.StringToOrderSide(tradeData[i].Side)
if err != nil {
return nil, err
}
resp = append(resp, trade.Data{
Exchange: o.Name,
TID: tradeData[i].TradeID,
CurrencyPair: p,
Side: side,
AssetType: assetType,
Price: tradeData[i].Price,
Amount: tradeData[i].Size,
Timestamp: tradeData[i].Timestamp,
})
}
default:
return nil, fmt.Errorf("%s asset type %v unsupported", o.Name, assetType)
}
err = o.AddTradesToBuffer(resp...)
if err != nil {
return nil, err
}
sort.Sort(trade.ByDate(resp))
return resp, nil
}
// CancelBatchOrders cancels an orders by their corresponding ID numbers
func (o *OKEX) CancelBatchOrders(_ context.Context, _ []order.Cancel) (order.CancelBatchResponse, error) {
return order.CancelBatchResponse{}, common.ErrNotYetImplemented
}

View File

@@ -1,16 +1,16 @@
# GoCryptoTrader package Okex
# GoCryptoTrader package Okgroup
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
[![Build Status](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
[![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/okex)
[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/okgroup)
[![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
This okex package is part of the GoCryptoTrader codebase.
This OKCoin package is part of the GoCryptoTrader codebase.
## This is still in active development
@@ -18,7 +18,7 @@ You can track ideas, planned features and what's in progress on this Trello boar
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://gocryptotrader.herokuapp.com/)
## OKex Exchange
## OKCoin Exchange
### Current Features
@@ -48,8 +48,8 @@ main.go
var o exchange.IBotExchange
for i := range Bot.Exchanges {
if Bot.Exchanges[i].GetName() == "OKex" {
y = Bot.Exchanges[i]
if Bot.Exchanges[i].GetName() == "OKCoin" {
o = Bot.Exchanges[i]
}
}

View File

@@ -82,11 +82,11 @@ const (
okGroupGetRepayment = "repayment"
)
// OKGroup is the overaching type across the all of OKEx's exchange methods
// OKGroup is the overaching type across the all of OKCoin's exchange methods
type OKGroup struct {
exchange.Base
ExchangeName string
// Spot and contract market error codes as per https://www.okex.com/rest_request.html
// Spot and contract market error codes
ErrorCodes map[string]error
// Stores for corresponding variable checks
ContractTypes []string
@@ -121,7 +121,7 @@ func (o *OKGroup) TransferAccountFunds(ctx context.Context, request TransferAcco
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupAccountSubsection, okGroupFundsTransfer, request, &resp, true)
}
// AccountWithdraw withdrawal of tokens to OKCoin International, other OKEx accounts or other addresses.
// AccountWithdraw withdrawal of tokens to OKCoin International or other addresses.
func (o *OKGroup) AccountWithdraw(ctx context.Context, request AccountWithdrawRequest) (resp AccountWithdrawResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupAccountSubsection, okGroupWithdraw, request, &resp, true)
}
@@ -435,8 +435,7 @@ func (o *OKGroup) RepayMarginLoan(ctx context.Context, request RepayMarginLoanRe
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupMarginTradingSubsection, requestURL, request, &resp, true)
}
// PlaceMarginOrder OKEx API only supports limit and market orders (more orders will become available in the future).
// You can place an order only if you have enough funds. Once your order is placed, the amount will be put on hold.
// PlaceMarginOrder You can place an order only if you have enough funds. Once your order is placed, the amount will be put on hold.
func (o *OKGroup) PlaceMarginOrder(ctx context.Context, request *PlaceOrderRequest) (resp PlaceOrderResponse, _ error) {
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, okGroupMarginTradingSubsection, OKGroupOrders, request, &resp, true)
}
@@ -529,7 +528,7 @@ func (o *OKGroup) GetMarginTransactionDetails(ctx context.Context, request GetSp
return resp, o.SendHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, okGroupMarginTradingSubsection, requestURL, nil, &resp, true)
}
// FormatParameters Formats URL parameters, useful for optional parameters due to OKEX signature check
// FormatParameters Formats URL parameters, useful for optional parameters due to OKCoin signature check
func FormatParameters(request interface{}) (parameters string) {
v, err := query.Values(request)
if err != nil {

View File

@@ -143,8 +143,8 @@ type TransferAccountFundsResponse struct {
type AccountWithdrawRequest struct {
Amount float64 `json:"amount"` // [required] withdrawal amount
Currency string `json:"currency"` // [required] token
Destination int64 `json:"destination"` // [required] withdrawal address(2:OKCoin International 3:OKEx 4:others)
Fee float64 `json:"fee"` // [required] Network transaction fee≥0. Withdrawals to OKCoin or OKEx are fee-free, please set as 0. Withdrawal to external digital asset address requires network transaction fee.
Destination int64 `json:"destination"` // [required] withdrawal address(2:OKCoin International 3:others)
Fee float64 `json:"fee"` // [required] Network transaction fee≥0. Withdrawals to OKCoin are fee-free, please set as 0. Withdrawal to external digital asset address requires network transaction fee.
ToAddress string `json:"to_address"` // [required] verified digital asset address, email or mobile number,some digital asset address format is address+tag , eg: "ARDOR-7JF3-8F2E-QUWZ-CAN7F123456"
TradePwd string `json:"trade_pwd"` // [required] fund password
}

View File

@@ -26,7 +26,7 @@ import (
)
// Note: GoCryptoTrader wrapper funcs currently only support SPOT trades.
// Therefore this OKGroup_Wrapper can be shared between OKEX and OKCoin.
// Therefore this OKGroup_Wrapper can be shared between OKCoin and OKCoin.
// When circumstances change, wrapper funcs can be split appropriately
var errNoAccountDepositAddress = errors.New("no account deposit address")

View File

@@ -1,16 +1,16 @@
# GoCryptoTrader package Okex
# GoCryptoTrader package Okx
<img src="/common/gctlogo.png?raw=true" width="350px" height="350px" hspace="70">
[![Build Status](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
[![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/okex)
[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/okx)
[![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
This okex package is part of the GoCryptoTrader codebase.
This okx package is part of the GoCryptoTrader codebase.
## This is still in active development
@@ -18,15 +18,16 @@ You can track ideas, planned features and what's in progress on this Trello boar
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk)
## OKex Exchange
## Okx Exchange
### Current Features
+ REST Support
+ Websocket Support
### How to enable
+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
[Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
+ Individual package example below:
@@ -45,10 +46,10 @@ below:
main.go
```go
var o exchange.IBotExchange
var ok exchange.IBotExchange
for i := range bot.Exchanges {
if bot.Exchanges[i].GetName() == "OKex" {
if bot.Exchanges[i].GetName() == "Okx" {
y = bot.Exchanges[i]
}
}
@@ -56,22 +57,22 @@ for i := range bot.Exchanges {
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := o.FetchTicker()
tick, err := ok.FetchTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := o.FetchOrderbook()
ob, err := ok.FetchOrderbook()
if err != nil {
// Handle error
}
// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
// Private calls - wrapper functions - make sure your APIKEY, APISECRET, and API_CLIENT_ID are
// set and AuthenticatedAPISupport is set to true
// Fetches current account information
accountInfo, err := o.GetAccountInfo()
accountInfo, err := ok.GetAccountInfo()
if err != nil {
// Handle error
}
@@ -83,13 +84,26 @@ if err != nil {
// Public calls
// Fetches current ticker information
ticker, err := o.GetSpotTicker()
ticker, err := ok.GetTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := o.GetSpotMarketDepth()
ob, err := ok.GetOrderBook()
if err != nil {
// Handle error
}
// Fetches historic trade data within the timeframe provided
tradeDatas, err := ok.GetHistoricTrades(...)
if err != nil {
// Handle error
}
// Returns an estimate of fee based on the type of transaction
fee, err := ok.GetFeeByType(...)
if err != nil {
// Handle error
}
@@ -97,17 +111,25 @@ if err != nil {
// Private calls - make sure your APIKEY and APISECRET are set and
// AuthenticatedAPISupport is set to true
// GetContractPosition returns contract positioning
accountInfo, err := o.GetContractPosition(...)
// Submits an order and the exchange and returns its tradeID
orderID, err := ok.SubmitOrder(...)
if err != nil {
// Handle error
}
// Submits an order and the exchange and returns its tradeID
tradeID, err := o.PlaceContractOrders(...)
// ModifyOrder will allow of changing orderbook placement and limit to market conversion
updatedOrder, err := ok.ModifyOrder(...)
if err != nil {
// Handle error
}
```
### How to do Websocket public/private calls
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### Please click GoDocs chevron above to view current GoDoc information for this package

4319
exchanges/okx/okx.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,524 @@
package okx
import "errors"
// ErrorCodes and their corresponding error messages
var (
ErrorCodes = map[string]error{
"60001": errors.New("\"OK_ACCESS_KEY\" can not be empty"),
"60002": errors.New("\"OK_ACCESS_SIGN\" can not be empty"),
"60003": errors.New("\"OK_ACCESS_PASSPHRASE\" can not be empty"),
"60004": errors.New("invalid OK_ACCESS_TIMESTAMP"),
"60005": errors.New("invalid OK_ACCESS_KEY"),
"60006": errors.New("timestamp request expired"),
"60007": errors.New("invalid sign"),
"60008": errors.New("public websocket services do not support subscribing to private channels"),
"60009": errors.New("login failed"),
"60011": errors.New("please log in"),
"60012": errors.New("illegal request"),
"60013": errors.New("invalid args"),
"60014": errors.New("requests too frequent"),
"60015": errors.New("connection closed as there was no data transmission in the last 30 seconds"),
"60016": errors.New("buffer is full, cannot write data"),
"60017": errors.New("invalid url path"),
"60018": errors.New("the {0} {1} {2} {3} {4} does not exist"),
"60019": errors.New("invalid op {op}"),
"60020": errors.New("apikey subscription amount exceeds the limit {0}"),
"60021": errors.New("this operation does not support multiple accounts login"),
"60022": errors.New("bulk login partially succeeded"),
"60023": errors.New("bulk login requests too frequent"),
"60024": errors.New("wrong passphrase"),
"60025": errors.New("token subscription amount exceeds the limit {0}"),
"60026": errors.New("batch login by apikey and token simultaneously is not supported"),
"60027": errors.New("parameter {0} can not be empty"),
"60028": errors.New("private websocket services do not support subscribing to public channels"),
"60029": errors.New("only users who're VIP5 and above can subscribe to books-l2-tbt order book channels"),
"60030": errors.New("only users who're VIP4 and above can subscribe to books50-l2-tbt order book channels"),
"63999": errors.New("internal system error"),
"70000": errors.New("rFQ does not exist"),
"70001": errors.New("quote does not exist"),
"70002": errors.New("block trade does not exist"),
"70003": errors.New("public block trade does not exist"),
"70004": errors.New("invalid instrument {0}"),
"70005": errors.New("the number of legs in RFQ cannot exceed maximum value"),
"70006": errors.New("does not meet the minimum asset requirement"),
"70007": errors.New("underlying index {0} does not exist under instType {1}"),
"70008": errors.New("operation failed under MMP status, the frozen window is {0} seconds"),
"70009": errors.New("data must have at least 1 valid element"),
"70011": errors.New("duplicate setting for instType {0}"),
"70100": errors.New("duplicate instruments in legs array"),
"70101": errors.New("duplicate clRfqId"),
"70102": errors.New("no counterparties specified"),
"70103": errors.New("invalid counterparty"),
"70105": errors.New("the total value should be greater than the min notional value {0}"),
"70106": errors.New("the trading amount does not meet the min tradable amount requirement"),
"70107": errors.New("the number of counterparties cannot exceed maximum value"),
"70109": errors.New("counterparties for selected instruments are currently unavailable"),
"70200": errors.New("the RFQ with {0} status cannot be canceled"),
"70203": errors.New("cancellation failed as rfq count exceeds the limit {0}"),
"70207": errors.New("cancellation failed as you do not have any active RFQs"),
"70208": errors.New("cancellation failed as service is unavailable now, please try again later"),
"70301": errors.New("duplicate clQuoteId"),
"70303": errors.New("the RFQ with {0} status cannot be quoted"),
"70304": errors.New("price should be an integer multiple of the tick size"),
"70305": errors.New("bid price cannot be higher than offer price"),
"70306": errors.New("the legs of quote do not match the legs of {0}"),
"70307": errors.New("size should be in integral multiples of the lot size"),
"70308": errors.New("quote to your own RFQ is not allowed"),
"70309": errors.New("quote to the same RFQ with the same side is not allowed"),
"70310": errors.New("quoted price of instId {0} cannot exceed your preset price limit"),
"70400": errors.New("the quote with {0} status cannot be canceled"),
"70408": errors.New("cancellation failed as quote count exceeds the limit {0}"),
"70409": errors.New("cancellation failed as you do not have any active quotes"),
"70501": errors.New("RFQ {0} is not quoted by {1}"),
"70502": errors.New("the legs do not match the legs of {0}"),
"70503": errors.New("the legs of execution do not match the legs of {0}"),
"70504": errors.New("execution failed as the RFQ status is {0}"),
"70505": errors.New("execution failed as the quote status is {0}"),
"70511": errors.New("execution is being processed"),
"56000": errors.New("block trade does not exist"),
"56001": errors.New("the number of multi-legs cannot exceed {0}"),
"56002": errors.New("the number of multi-legs does not match with the verified one"),
"56003": errors.New("duplicated clBlockTdId"),
"56004": errors.New("trade with yourself is not allowed"),
"56005": errors.New("\"clBlockTdId\" should be the same as the verified one"),
"56006": errors.New("the role should be different from the verified one"),
"56007": errors.New("leg no.{0} does not match with the verified one"),
"56008": errors.New("duplicate instruments in legs array"),
"59000": errors.New("your settings failed as you have positions or open orders"),
"59001": errors.New("switching unavailable as you have borrowings"),
"59004": errors.New("only IDs with the same instrument type are supported"),
"59005": errors.New("when users transfer the margin by themselves in isolated mode, the value of the asset allocated to the position for the first time must be greater than 10,000 USDT"),
"59100": errors.New("you have open positions. please cancel all open positions before changing the leverage"),
"59101": errors.New("you have pending orders with isolated positions. please cancel all the pending isolated margin orders and adjust the leverage"),
"59102": errors.New("leverage exceeds the maximum leverage. please adjust the leverage"),
"59103": errors.New("leverage is too low and no sufficient margin in your account. please adjust the leverage"),
"59104": errors.New("the leverage is too high. the borrowed position has exceeded the maximum position of this leverage. please adjust the leverage"),
"59105": errors.New("leverage can not be less than {0}. please adjust the leverage"),
"59106": errors.New("the max available margin corresponding to your order tier please adjust your margin and place a new order"),
"59107": errors.New("you have pending cross margin orders under the service, please modify the leverage after canceling all pending cross margin orders"),
"59108": errors.New("low leverage and insufficient margin, please adjust the leverage"),
"59109": errors.New("account equity less than the required margin amount after adjustment. please adjust the leverage "),
"59110": errors.New("the instrument type corresponding to this {0} does not support the tgtCcy parameter"),
"59111": errors.New("you cannot query the leverage of cross derivatives positions under a PM account"),
"59112": errors.New("you have isolated/cross pending orders. please cancel them before adjusting your leverage"),
"59200": errors.New("insufficient account balance"),
"59201": errors.New("negative account balance"),
"59300": errors.New("margin call failed. position does not exist"),
"59301": errors.New("margin adjustment failed for exceeding the max limit"),
"59302": errors.New("you have pending close orders, please modify the margin after canceling the orders"),
"59303": errors.New("insufficient available margin, add margin or reduce the borrowing amount"),
"59304": errors.New("insufficient equity for borrowing, keep enough funds to pay interest for at least one day"),
"59305": errors.New("use VIP loan first to set the VIP loan priority"),
"59306": errors.New("your borrowing amount exceeds the max limit"),
"59307": errors.New("you are not eligible for VIP loans"),
"59308": errors.New("unable to repay VIP loan due to insufficient borrow limit"),
"59309": errors.New("unable to repay an amount that exceeds the borrowed amount"),
"59310": errors.New("your account does not support VIP loan"),
"59311": errors.New("unable to set up as there is VIP loan"),
"59312": errors.New("{currency} does not support VIP loans"),
"59401": errors.New("holdings already reached the limit"),
"59402": errors.New("none of the passed instId is in live state, please check them separately"),
"59500": errors.New("only the APIKey of the main account has permission"),
"59501": errors.New("only 50 APIKeys can be created per account"),
"59502": errors.New("note name cannot be duplicate with the currently created APIKey note name"),
"59503": errors.New("each APIKey can bind up to 20 IP addresses"),
"59504": errors.New("the sub account does not support the withdrawal function"),
"59505": errors.New("the passphrase format is incorrect"),
"59506": errors.New("apikey does not exist"),
"59507": errors.New("the two accounts involved in a transfer must be two different sub accounts under the same parent account"),
"59508": errors.New("the sub account of {0} is suspended"),
"59509": errors.New("account does not have permission to reset MMP status"),
"59510": errors.New("sub-account does not exist"),
"59512": errors.New("unable to set up this permission for ND brokers sub accounts. by default, all ND sub accounts can transfer funds out"),
"59601": errors.New("this sub-account name already exists, try another name"),
"59602": errors.New("number of API keys exceeds the limit"),
"59603": errors.New("number of sub accounts exceeds the limit"),
"59604": errors.New("only the main account APIkey can access this API"),
"59605": errors.New("this API key does not exist in your sub-account, try another API key"),
"59606": errors.New("transfer funds to your main account before deleting your sub-account"),
"59608": errors.New("only the broker account has permission to operate this API"),
"59609": errors.New("broker already exists"),
"59610": errors.New("broker does not exist"),
"59611": errors.New("broker unverified"),
"59612": errors.New("cannot convert time format"),
"59613": errors.New("there is currently no escrow relationship established with the sub account"),
"59614": errors.New("managed sub account do not support this operation"),
"59615": errors.New("the time interval between the begin date and end date cannot exceed 180 days"),
"59616": errors.New("begin date cannot be greater than end date"),
"59617": errors.New("sub-account created. failed to set up account level"),
"59618": errors.New("failed to create sub-account"),
"58000": errors.New("account type {0} does not supported when getting the sub-account balance"),
"58001": errors.New("incorrect trade password"),
"58002": errors.New("please activate savings account first"),
"58003": errors.New("currency type is not supported by savings account"),
"58004": errors.New("account blocked (transfer & withdrawal endpoint: either end of the account does not authorize the transfer)"),
"58005": errors.New("the purchase/redeemed amount must be no greater than {0}"),
"58006": errors.New("service unavailable for token {0}"),
"58007": errors.New("abnormal assets interface. please try again later"),
"58008": errors.New("you do not have assets in this currency"),
"58009": errors.New("currency pair do not exist"),
"58010": errors.New("the chain {0} is not supported"),
"58011": errors.New("sorry, we are unable to provide services to unverified users in {Region} due to local laws and regulations. please verify your account in order to use the services"),
"58012": errors.New("sorry, you can't transfer assets to this recipient as OKX are unable to provide services to unverified users in {region} due to local laws and regulations"),
"58100": errors.New("the trading product triggers risk control, and the platform has suspended the fund transfer-out function with related users. please wait patiently"),
"58101": errors.New("transfer suspended (transfer endpoint: either end of the account does not authorize the transfer)"),
"58102": errors.New("too frequent transfer (transfer too frequently)"),
"58104": errors.New("since your P2P transaction is abnormal, you are restricted from making fund transfers. please contact customer support to remove the restriction"),
"58105": errors.New(`since your P2P transaction is abnormal, you are restricted from making fund transfers. please transfer funds on our website or app to complete identity verification`),
"58106": errors.New("please enable the account for spot contract"),
"58107": errors.New("please enable the account for futures contract"),
"58108": errors.New("please enable the account for option contract"),
"58109": errors.New("please enable the account for swap contract"),
"58110": errors.New("the contract triggers risk control, and the platform has suspended the fund transfer function of it. please wait patiently"),
"58111": errors.New("funds transfer unavailable as the perpetual contract is charging the funding fee. please try again later"),
"58112": errors.New("your fund transfer failed. please try again later"),
"58114": errors.New("transfer amount must be more than 0"),
"58115": errors.New("sub-account does not exist"),
"58116": errors.New("transfer amount exceeds the limit"),
"58117": errors.New("account assets are abnormal, please deal with negative assets before transferring"),
"58119": errors.New("{0} sub-account has no permission to transfer out, please set first"),
"58120": errors.New("the transfer service is temporarily unavailable, please try again later"),
"58121": errors.New("this transfer will result in a high-risk level of your position, which may lead to forced liquidation. You need to re-adjust the transfer amount to make sure the position is at a safe level before proceeding with the transfer"),
"58122": errors.New("a portion of your spot is being used for delta offset between positions. if the transfer amount exceeds the available amount, it may affect current spot-derivatives risk offset structure, which will result in an increased maintenance margin requirement (MMR) rate. please be aware of your risk level"),
"58123": errors.New("parameter from can not equal to parameter to"),
"58200": errors.New("withdrawal from {0} to {1} is unavailable for this currency"),
"58201": errors.New("withdrawal amount exceeds the daily limit"),
"58202": errors.New("the minimum withdrawal amount for NEO is 1, and the amount must be an integer"),
"58203": errors.New("please add a withdrawal address"),
"58204": errors.New("withdrawal suspended"),
"58205": errors.New("withdrawal amount exceeds the upper limit"),
"58206": errors.New("withdrawal amount is lower than the lower limit"),
"58207": errors.New("withdrawal address is not in the verification-free whitelist"),
"58208": errors.New("withdrawal failed. please link your email"),
"58209": errors.New("sub-accounts cannot be deposits or withdrawals"),
"58210": errors.New("withdrawal fee exceeds the upper limit"),
"58211": errors.New("withdrawal fee is lower than the lower limit (withdrawal endpoint: incorrect fee)"),
"58212": errors.New("withdrawal fee should be {0} of the withdrawal amount"),
"58214": errors.New("withdrawals suspended due to {chainName} maintenance"),
"58215": errors.New("withdrawal ID does not exist"),
"58216": errors.New("operation not allowed"),
"58217": errors.New("you cannot withdraw your asset at the moment due to a risk detected in your withdrawal address, contact customer support for details"),
"58218": errors.New("your saved withdrawal account has expired"),
"58220": errors.New("the withdrawal order is already canceled"),
"58221": errors.New("missing label of withdrawal address"),
"58222": errors.New("illegal withdrawal address"),
"58224": errors.New("this type of crypto does not support on-chain withdrawing to OKX addresses. please withdraw through internal transfers"),
"58225": errors.New("sorry, you can't transfer assets to this recipient as OKX are unable to provide services to unverified users in {region} due to local laws and regulations"),
"58300": errors.New("deposit-address count exceeds the limit"),
"58301": errors.New("deposit-address not exist"),
"58302": errors.New("deposit-address needs tag"),
"58303": errors.New("deposit for the chain {0} is closed now"),
"58304": errors.New("failed to create invoice"),
"58350": errors.New("insufficient balance"),
"58351": errors.New("invoice expired"),
"58352": errors.New("invalid invoice"),
"58353": errors.New("deposit amount must be within limits"),
"58354": errors.New("you have reached the limit of 10000 invoices per day"),
"58355": errors.New("permission denied. please contact your account manager"),
"58356": errors.New("the accounts of the same node do not support the lightning network deposit or withdrawal"),
"58357": errors.New("{0} is not allowed to create a deposit address"),
"58358": errors.New("fromCcy should not be the same as toCcy"),
"58370": errors.New("the daily usage of small assets convert exceeds the limit"),
"58371": errors.New("small assets exceed the maximum limit"),
"58372": errors.New("insufficient small assets"),
"55000": errors.New("cannot be transferred out within 30 minutes after delivery"),
"54000": errors.New("margin transactions unavailable"),
"54001": errors.New("only multi-currency margin account can be set to borrow coins automatically"),
"52900": errors.New("general Invalid request"),
"52901": errors.New("invalid base asset"),
"52902": errors.New("invalid quote asset"),
"52903": errors.New("invalid quote amount"),
"52904": errors.New("invalid quote side"),
"52905": errors.New("invalid quote price"),
"52907": errors.New("order not found"),
"52908": errors.New("invalid order ID"),
"52909": errors.New("duplicated client order Id"),
"52910": errors.New("service unavailable, please try again later"),
"52911": errors.New("RFQ service unavailable, please try again later"),
"52912": errors.New("server timeout"),
"52913": errors.New("trade rejected"),
"52915": errors.New("cannot quote due to large amounts of RFQ and insufficient liquidity, please try again later"),
"52916": errors.New("insufficient balance in funding account"),
"52917": errors.New("RFQ quantity cannot be less than the lower limit"),
"52918": errors.New("insufficient balance in funding account"),
"52919": errors.New("parameter {param} of convert trading is inconsistent with the quotation"),
"52920": errors.New("quantity of convert trading cannot exceed the quotation quantity"),
"52921": errors.New("quote traded, please ask for quote again"),
"52922": errors.New("quote expired, please ask for quote again"),
"52923": errors.New("service unavailable, please try again later"),
"51720": errors.New("redeem error"),
"51721": errors.New("cancel redeem error"),
"51722": errors.New("redeem already complete"),
"51723": errors.New("early redemption is not supported"),
"51724": errors.New("redemption is currently not supported"),
"51725": errors.New("cancellation is currently not supported"),
"51726": errors.New("cancellation of subscriptions/redemptions is not supported"),
"51727": errors.New("the subscription quantity is below the minimum requirement"),
"51728": errors.New("the subscription quantity is above the maximum limit"),
"51729": errors.New("this project has not reached the redemption date"),
"51730": errors.New("sold out"),
"52000": errors.New("no market data found"),
"51000": errors.New("parameter {0} error"),
"51001": errors.New("instrument ID does not exist"),
"51002": errors.New("instrument ID does not match underlying index"),
"51003": errors.New("either client order ID or order ID is required"),
"51004": errors.New("order amount exceeds current tier limit, please lower the leverage"),
"51005": errors.New("order amount exceeds the limit"),
"51006": errors.New("order price is not within the price limit (max buy price: {0} min sell price: {1})"),
"51007": errors.New("order placement failed. order amount should be at least 1 contract (showing up when placing an order with less than 1 contract)"),
"51008": errors.New("delegate failed. Insufficient {0} balance in account"),
"51009": errors.New("order placement function is blocked by the platform"),
"51010": errors.New("operation is not supported under the current account mode"),
"51011": errors.New("duplicated order ID"),
"51012": errors.New("token does not exist"),
"51014": errors.New("index does not exist"),
"51015": errors.New("instrument ID does not match instrument type"),
"51016": errors.New("duplicated client order ID"),
"51017": errors.New("borrow amount exceeds the limit"),
"51018": errors.New("user with option account can not hold net short positions"),
"51019": errors.New("no net long positions can be held under cross margin mode in options"),
"51020": errors.New("order amount should be greater than the min available amount"),
"51021": errors.New("contract to be listed"),
"51022": errors.New("contract suspended"),
"51023": errors.New("position does not exist"),
"51024": errors.New("trading account is blocked"),
"51025": errors.New("order count exceeds the limit"),
"51026": errors.New("instrument type does not match underlying index"),
"51027": errors.New("contract expired"),
"51028": errors.New("contract under delivery"),
"51029": errors.New("contract is being settled"),
"51030": errors.New("funding fee is being settled"),
"51031": errors.New("this order price is not within the closing price range"),
"51032": errors.New("closing all positions at market price"),
"51033": errors.New("the total amount per order for this pair has reached the upper limit"),
"51037": errors.New("the current account risk status only supports you to place IOC orders that can reduce the risk of your account"),
"51038": errors.New("there is already an IOC order under the current risk module that reduces the risk of the account"),
"51039": errors.New("leverage cannot be adjusted for the cross positions of futures and perpetual swap under the PM account"),
"51040": errors.New("cannot adjust margins for long isolated options positions"),
"51041": errors.New("portfolio margin account only supports net mode"),
"51044": errors.New("the order type {0}, {1} is not allowed to set stop loss and take profit"),
"51046": errors.New("the take profit trigger price should be higher than the order price"),
"51047": errors.New("the stop loss trigger price should be lower than the order price"),
"51048": errors.New("the take profit trigger price should be lower than the order price"),
"51049": errors.New("the stop loss trigger price should be higher than the order price"),
"51050": errors.New("the take profit trigger price should be higher than the best ask price"),
"51051": errors.New("the stop loss trigger price should be lower than the best ask price"),
"51052": errors.New("the take profit trigger price should be lower than the best bid price"),
"51053": errors.New("the stop loss trigger price should be higher than the best bid price"),
"51054": errors.New("getting information timed out, please try again later"),
"51055": errors.New("futures grid is not available in portfolio margin mode"),
"51056": errors.New("action not allowed"),
"51057": errors.New("futures grid is not available in simple trading mode"),
"51058": errors.New("no position"),
"51059": errors.New("strategy for the current state does not support this operation"),
"51101": errors.New("entered amount exceeds the max pending order amount per transaction"),
"51102": errors.New("entered amount exceeds the max pending count"),
"51103": errors.New("entered amount exceeds the max pending order count of the underlying asset"),
"51104": errors.New("entered amount exceeds the max pending order amount of the underlying asset"),
"51105": errors.New("entered amount exceeds the max order amount of the contract"),
"51106": errors.New("entered amount exceeds the max order amount of the underlying asset"),
"51107": errors.New("entered amount exceeds the max holding amount"),
"51108": errors.New("positions exceed the limit for closing out with the market price"),
"51109": errors.New("no available offer"),
"51110": errors.New("you can only place a limit order after call auction has started"),
"51111": errors.New("maximum {0} orders can be placed in bulk"),
"51112": errors.New("close order size exceeds your available size"),
"51113": errors.New("market-price liquidation requests too frequent"),
"51115": errors.New("cancel all pending close-orders before liquidation"),
"51116": errors.New("order price or trigger price exceeds {0}"),
"51117": errors.New("pending close-orders count exceeds limit"),
"51120": errors.New("order quantity is less than {0}, please try again"),
"51121": errors.New("order count should be the integer multiples of the lot size"),
"51122": errors.New("order price should be higher than the min price {0}"),
"51124": errors.New("you can only place limit orders during call auction"),
"51125": errors.New("currently there are reduce + reverse position pending orders in margin trading. please cancel all reduce + reverse position pending orders and continue"),
"51126": errors.New("currently there are reduce only pending orders in margin trading. please cancel all reduce only pending orders and continue"),
"51127": errors.New("available balance is 0"),
"51128": errors.New("multi-currency margin account can not do cross-margin trading"),
"51129": errors.New("the value of the position and buy order has reached the position limit, and no further buying is allowed"),
"51130": errors.New("fixed margin currency error"),
"51131": errors.New("insufficient balance"),
"51132": errors.New("your position amount is negative and less than the minimum trading amount"),
"51133": errors.New("reduce-only feature is unavailable for the spot transactions by multi-currency margin account"),
"51134": errors.New("closing failed. please check your holdings and pending orders"),
"51135": errors.New("your closing price has triggered the limit price, and the max buy price is {0}"),
"51136": errors.New("your closing price has triggered the limit price, and the min sell price is {0}"),
"51137": errors.New("your opening price has triggered the limit price, and the max buy price is {0}"),
"51138": errors.New("your opening price has triggered the limit price, and the min sell price is {0}"),
"51139": errors.New("reduce-only feature is unavailable for the spot transactions by simple account"),
"51145": errors.New("when users transfer the margin by themselves in isolated mode, its not supported to place orders in advance"),
"51147": errors.New("the total value of assets in your trading account needs to be greater than 50,000 USD to trade options"),
"51148": errors.New("reduceOnly cannot increase the position quantity"),
"51149": errors.New("order timed out, please try again later"),
"51150": errors.New("the precision of the number of trades or the price exceeds the limit"),
"51201": errors.New("value of per market order cannot exceed 1,000,000 USDT"),
"51202": errors.New("market - order amount exceeds the max amount"),
"51203": errors.New("order amount exceeds the limit {0}"),
"51204": errors.New("the price for the limit order can not be empty"),
"51205": errors.New("reduce-only is not available"),
"51206": errors.New("please cancel the reduce only order before placing the current {0} order to avoid opening a reverse position"),
"51250": errors.New("algo order price is out of the available range"),
"51251": errors.New("algo order type error (when user place an iceberg order)"),
"51252": errors.New("algo order price is out of the available range"),
"51253": errors.New("average amount exceeds the limit of per iceberg order"),
"51254": errors.New("iceberg average amount error (when user place an iceberg order)"),
"51255": errors.New("limit of per iceberg order: total amount/1000 < x <= total amount"),
"51256": errors.New("iceberg order price variance error"),
"51257": errors.New("trail order callback rate error"),
"51258": errors.New("trail - order placement failed. the trigger price of a sell order should be higher than the last transaction price"),
"51259": errors.New("trail - order placement failed. the trigger price of a buy order should be lower than the last transaction price"),
"51260": errors.New("maximum {0} pending trail - orders can be held at the same time"),
"51261": errors.New("each user can hold up to {0} pending stop - orders at the same time"),
"51262": errors.New("maximum {0} pending iceberg orders can be held at the same time"),
"51263": errors.New("maximum {0} pending time-weighted orders can be held at the same time"),
"51264": errors.New("average amount exceeds the limit of per time-weighted order"),
"51265": errors.New("time-weighted order limit error"),
"51267": errors.New("time-weighted order strategy initiative rate error"),
"51268": errors.New("time-weighted order strategy initiative range error"),
"51269": errors.New("time-weighted order interval error, the interval should be {0}<= x<={1}"),
"51270": errors.New("the limit of time-weighted order price variance is 0 < x <= 1%"),
"51271": errors.New("sweep ratio should be 0 < x <= 100%"),
"51272": errors.New("price variance should be 0 < x <= 1%"),
"51273": errors.New("total amount should be more than {0}"),
"51274": errors.New("total quantity of time-weighted order must be larger than single order limit"),
"51275": errors.New("the amount of single stop-market order can not exceed the upper limit"),
"51276": errors.New("stop - market orders cannot specify a price"),
"51277": errors.New("TP trigger price can not be higher than the last price"),
"51278": errors.New("SL trigger price can not be lower than the last price"),
"51279": errors.New("TP trigger price can not be lower than the last price"),
"51280": errors.New("SL trigger price can not be higher than the last price"),
"51281": errors.New("trigger not support the tgtCcy parameter"),
"51282": errors.New("the range of price variance is {0}~{1}"),
"51283": errors.New("the range of time interval is {0}~{1}"),
"51284": errors.New("the range of average amount is {0}~{1}"),
"51285": errors.New("the range of total amount is {0}~{1}"),
"51286": errors.New("the total amount should not be less than {0}"),
"51287": errors.New("contract not supported"),
"51288": errors.New("we are stopping the bot. please do not click it multiple times"),
"51289": errors.New("bot configuration does not exist. please try again later"),
"51290": errors.New("the bot engine is being upgraded. please try again later"),
"51291": errors.New("this bot does not exist or has been stopped"),
"51292": errors.New("this bot type does not exist"),
"51293": errors.New("this bot does not exist"),
"51294": errors.New("this bot cannot be created temporarily. please try again later"),
"51295": errors.New("portfolio margin account does not support ordType {0} in trading bot mode"),
"51298": errors.New("trigger orders are not available in the net mode of futures and perpetual swaps"),
"51299": errors.New("order did not go through. you can hold maximum {0} orders of this type"),
"51300": errors.New("TP trigger price can not be higher than the mark price"),
"51302": errors.New("SL trigger price can not be lower than the mark price"),
"51303": errors.New("TP trigger price can not be lower than the mark price"),
"51304": errors.New("SL trigger price can not be higher than the mark price"),
"51305": errors.New("TP trigger price can not be higher than the index price"),
"51306": errors.New("SL trigger price can not be lower than the index price"),
"51307": errors.New("TP trigger price can not be lower than the index price"),
"51308": errors.New("SL trigger price can not be higher than the index price"),
"51309": errors.New("cannot create trading bot during call auction"),
"51310": errors.New("when users transfer the margin by themselves in isolated mode, strategic orders with ordType iceberg and twap will not be supported"),
"51311": errors.New("failed to place trailing stop order. callback rate should be within {0}<x<={1}"),
"51312": errors.New("failed to place trailing stop order. order amount should be within {0}<x<={1}"),
"51313": errors.New("manual transfer in isolated mode does not support bot trading"),
"51317": errors.New("trigger orders are not available by margin"),
"51340": errors.New("used margin must be greater than {0}{1}"),
"51341": errors.New("position closing not allowed"),
"51342": errors.New("closing order already exists.please try again later"),
"51343": errors.New("TP price must be less than the lower price"),
"51344": errors.New("SL price must be greater than the upper price"),
"51345": errors.New("policy type is not grid policy"),
"51346": errors.New("the highest price cannot be lower than the lowest price"),
"51347": errors.New("no profit available"),
"51348": errors.New("stop price should be less than the lowest price in the range"),
"51349": errors.New("stop profit price should be greater than the highest price in the range"),
"51350": errors.New("no recommended parameters"),
"51351": errors.New("single income must be greater than 0"),
"51370": errors.New("the range of lever is {0}~{1}"),
"51400": errors.New("cancellation failed as the order does not exist"),
"51401": errors.New("cancellation failed as the order is already canceled"),
"51402": errors.New("cancellation failed as the order is already completed"),
"51403": errors.New("cancellation failed as the order type does not support cancellation"),
"51404": errors.New("order cancellation unavailable during the second phase of call auction"),
"51405": errors.New("cancellation failed as you do not have any pending orders"),
"51406": errors.New("canceled - order count exceeds the limit {0}"),
"51407": errors.New("either order ID or client order ID is required"),
"51408": errors.New("pair ID or name does not match the order info"),
"51409": errors.New("either pair ID or pair name ID is required"),
"51410": errors.New("cancellation failed as the order is already under cancelling status"),
"51411": errors.New("account does not have permission for mass cancellation"),
"51412": errors.New("the order has been triggered and cannot be cancelled"),
"51413": errors.New("cancellation failed as the order type is not supported by endpoint"),
"51415": errors.New("unable to place order. spot trading only supports using the last price as trigger price. please select \"Last\" and try again"),
"51500": errors.New("either order price or amount is required"),
"51501": errors.New("maximum {0} orders can be modified"),
"51502": errors.New("order modification failed for insufficient margin"),
"51503": errors.New("order modification failed as the order does not exist"),
"51506": errors.New("order modification unavailable for the order type"),
"51508": errors.New("orders are not allowed to be modified during the call auction"),
"51509": errors.New("modification failed as the order has been canceled"),
"51510": errors.New("modification failed as the order has been completed"),
"51511": errors.New("operation failed as the order price did not meet the requirement for post only"),
"51512": errors.New("failed to amend bulk orders. you cannot add duplicate batch orders in your portfolio margin account"),
"51513": errors.New("number of modification requests that are currently in progress for an order cannot exceed 3"),
"51600": errors.New("status not found"),
"51601": errors.New("order status and order ID cannot exist at the same time"),
"51602": errors.New("either order status or order ID is required"),
"51603": errors.New("order does not exist"),
"51607": errors.New("the file is generating"),
"50100": errors.New("API frozen, please contact customer service"),
"50101": errors.New("APIKey does not match current environment"),
"50102": errors.New("timestamp request expired"),
"50103": errors.New("request header \"OK_ACCESS_KEY\" can not be empty"),
"50104": errors.New("request header \"OK_ACCESS_PASSPHRASE\" can not be empty"),
"50105": errors.New("request header \"OK_ACCESS_PASSPHRASE\" incorrect"),
"50106": errors.New("request header \"OK_ACCESS_SIGN\" can not be empty"),
"50107": errors.New("request header \"OK_ACCESS_TIMESTAMP\" can not be empty"),
"50108": errors.New("exchange ID does not exist"),
"50109": errors.New("exchange domain does not exist"),
"50110": errors.New("your IP {0} is not in linking trusted IP addresses.(You can add your IP in linking trusted IP addresses)"),
"50111": errors.New("invalid OK_ACCESS_KEY"),
"50112": errors.New("invalid OK_ACCESS_TIMESTAMP"),
"50113": errors.New("invalid signature"),
"50114": errors.New("invalid authorization"),
"50115": errors.New("invalid request method"),
"1": errors.New("operation failed"),
"2": errors.New("bulk operation partially succeeded"),
"50000": errors.New("body can not be empty"),
"50001": errors.New("service temporarily unavailable, please try again later"),
"50002": errors.New("json data format error"),
"50004": errors.New("endpoint request timeout (does not mean that the request was successful or failed, please check the request result)"),
"50005": errors.New("API is offline or unavailable"),
"50006": errors.New("invalid Content_Type, please use \"application/json\" format"),
"50007": errors.New("account blocked"),
"50008": errors.New("user does not exist"),
"50009": errors.New("account is suspended due to ongoing liquidation"),
"50010": errors.New("user ID can not be empty"),
"50011": errors.New("requests too frequent"),
"50012": errors.New("account status invalid"),
"50013": errors.New("system is busy, please try again later"),
"50014": errors.New("parameter {0} can not be empty"),
"50015": errors.New("either parameter {0} or {1} is required"),
"50016": errors.New("parameter {0} does not match parameter {1}"),
"50017": errors.New("the position is frozen due to ADL. operation restricted"),
"50018": errors.New("currency {0} is frozen due to ADL. operation restricted"),
"50019": errors.New("the account is frozen due to ADL. operation restricted"),
"50020": errors.New("the position is frozen due to liquidation. operation restricted"),
"50021": errors.New("currency {0} is frozen due to liquidation. operation restricted"),
"50022": errors.New("the account is frozen due to liquidation. operation restricted"),
"50023": errors.New("funding fee frozen. operation restricted"),
"50024": errors.New("parameter {0} and {1} can not exist at the same time"),
"50025": errors.New("parameter {0} count exceeds the limit {1}"),
"50026": errors.New("system error, please try again later"),
"50027": errors.New("the account is restricted from trading"),
"50028": errors.New("unable to take the order, please reach out to support center for details"),
"50029": errors.New(`this instrument ({0}) is unavailable at present due to risk management. please contact customer service for help`),
"50030": errors.New("no permission to use this API"),
"50031": errors.New("amount cannot exceed 100"),
"50032": errors.New("this asset is blocked, allow its trading and try again"),
"50033": errors.New("this instrument is blocked, allow its trading and try again"),
"50034": errors.New("you are not currently on the whitelist, please contact customer service"),
"50035": errors.New("this endpoint requires that APIKey must be bound to IP"),
"50036": errors.New("invalid expTime"),
"50037": errors.New("order expired"),
"50038": errors.New("this feature is temporarily unavailable in demo trading"),
"50039": errors.New("the before parameter is not available for implementing timestamp pagination"),
}
)

3176
exchanges/okx/okx_test.go Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,742 @@
package okx
import (
"encoding/json"
"regexp"
"strconv"
"strings"
"time"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
type okxNumericalValue float64
// UnmarshalJSON is custom type json unmarshaller for okxNumericalValue
func (a *okxNumericalValue) UnmarshalJSON(data []byte) error {
var num string
err := json.Unmarshal(data, &num)
if err != nil {
return err
}
if num == "" {
return nil
}
v, err := strconv.ParseFloat(num, 64)
if err != nil {
return err
}
*a = okxNumericalValue(v)
return nil
}
// Float64 returns a float64 value for okxNumericalValue
func (a *okxNumericalValue) Float64() float64 { return float64(*a) }
type okxUnixMilliTime int64
// UnmarshalJSON deserializes byte data to okxunixMilliTime instance.
func (a *okxUnixMilliTime) UnmarshalJSON(data []byte) error {
var num string
err := json.Unmarshal(data, &num)
if err != nil {
return err
}
if num == "" {
return nil
}
value, err := strconv.ParseInt(num, 10, 64)
if err != nil {
return err
}
*a = okxUnixMilliTime(value)
return nil
}
// Time returns the time instance from unix value of integer.
func (a *okxUnixMilliTime) Time() time.Time {
return time.UnixMilli(int64(*a))
}
// numbersOnlyRegexp for checking the value is numerics only
var numbersOnlyRegexp = regexp.MustCompile(`^\d*$`)
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *Instrument) UnmarshalJSON(data []byte) error {
type Alias Instrument
chil := &struct {
*Alias
ListTime string `json:"listTime"`
ExpTime string `json:"expTime"`
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
if numbersOnlyRegexp.MatchString(chil.ListTime) {
var val int
if val, err = strconv.Atoi(chil.ListTime); err == nil {
a.ListTime = time.UnixMilli(int64(val))
}
}
if numbersOnlyRegexp.MatchString(chil.ExpTime) {
var val int
if val, err = strconv.Atoi(chil.ExpTime); err == nil {
a.ExpTime = time.UnixMilli(int64(val))
}
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON decoder for OpenInterestResponse instance.
func (a *OpenInterest) UnmarshalJSON(data []byte) error {
type Alias OpenInterest
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{Alias: (*Alias)(a)}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *FundingRateResponse) UnmarshalJSON(data []byte) error {
type Alias FundingRateResponse
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
FundingRate string `json:"fundingRate"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *LimitPriceResponse) UnmarshalJSON(data []byte) error {
type Alias LimitPriceResponse
chil := &struct {
*Alias
Timestamp int64 `json:"ts,string"`
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes the account and position response.
func (a *TickerResponse) UnmarshalJSON(data []byte) error {
type Alias TickerResponse
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, chil); err != nil {
return err
}
var err error
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *OptionMarketDataResponse) UnmarshalJSON(data []byte) error {
type Alias OptionMarketDataResponse
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, asset item, and timestamp information.
func (a *DeliveryEstimatedPrice) UnmarshalJSON(data []byte) error {
type Alias DeliveryEstimatedPrice
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON custom Unmarshaler to convert the Instrument type string to an asset.Item instance.
func (a *LiquidationOrder) UnmarshalJSON(data []byte) error {
type Alias LiquidationOrder
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON unmarshals the timestamp for mark price data
func (a *MarkPrice) UnmarshalJSON(data []byte) error {
type Alias MarkPrice
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if chil.InstrumentType == "" {
a.InstrumentType = asset.Empty
} else if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *OrderDetail) UnmarshalJSON(data []byte) error {
type Alias OrderDetail
chil := &struct {
*Alias
Side string `json:"side"`
UpdateTime int64 `json:"uTime,string"`
CreationTime int64 `json:"cTime,string"`
InstrumentType string `json:"instType"`
FillTime string `json:"fillTime"`
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, chil); err != nil {
return err
}
var err error
a.UpdateTime = time.UnixMilli(chil.UpdateTime)
a.CreationTime = time.UnixMilli(chil.CreationTime)
a.Side, err = order.StringToOrderSide(chil.Side)
if chil.FillTime == "" {
a.FillTime = time.Time{}
} else {
var value int64
value, err = strconv.ParseInt(chil.FillTime, 10, 64)
if err != nil {
return err
}
a.FillTime = time.UnixMilli(value)
}
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *PendingOrderItem) UnmarshalJSON(data []byte) error {
type Alias PendingOrderItem
chil := &struct {
*Alias
Side string `json:"side"`
UpdateTime string `json:"uTime"`
CreationTime string `json:"cTime"`
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
uTime, err := strconv.ParseInt(chil.UpdateTime, 10, 64)
if err != nil {
return err
}
cTime, err := strconv.ParseInt(chil.CreationTime, 10, 64)
if err != nil {
return err
}
a.Side, err = order.StringToOrderSide(chil.Side)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
a.CreationTime = time.UnixMilli(cTime)
a.UpdateTime = time.UnixMilli(uTime)
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *TransactionDetail) UnmarshalJSON(data []byte) error {
type Alias TransactionDetail
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *AlgoOrderResponse) UnmarshalJSON(data []byte) error {
type Alias AlgoOrderResponse
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *PositionData) UnmarshalJSON(data []byte) error {
type Alias PositionData
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *AccountPosition) UnmarshalJSON(data []byte) error {
type Alias AccountPosition
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserialises the JSON info, asset item instance, and including the timestamp
func (a *AccountPositionHistory) UnmarshalJSON(data []byte) error {
type Alias AccountPositionHistory
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *BillsDetailResponse) UnmarshalJSON(data []byte) error {
type Alias BillsDetailResponse
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *TradeFeeRate) UnmarshalJSON(data []byte) error {
type Alias TradeFeeRate
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *PositionBuilderData) UnmarshalJSON(data []byte) error {
type Alias PositionBuilderData
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *RfqTradeResponse) UnmarshalJSON(data []byte) error {
type Alias RfqTradeResponse
chil := &struct {
*Alias
CreationTime int64 `json:"cTime,string"`
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, chil); err != nil {
return err
}
a.CreationTime = time.UnixMilli(chil.CreationTime)
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *BlockTicker) UnmarshalJSON(data []byte) error {
type Alias BlockTicker
chil := &struct {
*Alias
Timestamp int64 `json:"ts,string"`
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *BlockTrade) UnmarshalJSON(data []byte) error {
type Alias BlockTrade
chil := &struct {
*Alias
Side string `json:"side"`
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, chil); err != nil {
return err
}
switch {
case strings.EqualFold(chil.Side, "buy"):
a.Side = order.Buy
case strings.EqualFold(chil.Side, "sell"):
a.Side = order.Sell
default:
a.Side = order.UnknownSide
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *UnitConvertResponse) UnmarshalJSON(data []byte) error {
type Alias UnitConvertResponse
chil := &struct {
*Alias
ConvertType int `json:"type,string"`
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, chil); err != nil {
return err
}
switch chil.ConvertType {
case 1:
a.ConvertType = 1
case 2:
a.ConvertType = 2
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *GridAlgoSuborder) UnmarshalJSON(data []byte) error {
type Alias GridAlgoSuborder
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *GridAlgoOrderResponse) UnmarshalJSON(data []byte) error {
type Alias GridAlgoOrderResponse
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *AlgoOrderPosition) UnmarshalJSON(data []byte) error {
type Alias AlgoOrderPosition
chil := &struct {
*Alias
InstrumentType string `json:"instType"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
chil.InstrumentType = strings.ToUpper(chil.InstrumentType)
if a.InstrumentType, err = GetAssetTypeFromInstrumentType(chil.InstrumentType); err != nil {
return err
}
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *QuoteLeg) UnmarshalJSON(data []byte) error {
type Alias QuoteLeg
chil := &struct {
*Alias
Side string `json:"side"`
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, chil); err != nil {
return err
}
chil.Side = strings.ToLower(chil.Side)
if chil.Side == "buy" {
a.Side = order.Buy
} else {
a.Side = order.Sell
}
return nil
}
// MarshalJSON serialized QuoteLeg instance into bytes
func (a *QuoteLeg) MarshalJSON() ([]byte, error) {
type Alias QuoteLeg
chil := &struct {
*Alias
Side string `json:"side"`
}{
Alias: (*Alias)(a),
}
if a.Side == order.Buy {
chil.Side = "buy"
} else {
chil.Side = "sell"
}
return json.Marshal(chil)
}
// MarshalJSON serialized CreateQuoteParams instance into bytes
func (a *CreateQuoteParams) MarshalJSON() ([]byte, error) {
type Alias CreateQuoteParams
chil := &struct {
*Alias
QuoteSide string `json:"quoteSide"`
}{
Alias: (*Alias)(a),
}
if a.QuoteSide == order.Buy {
chil.QuoteSide = "buy"
} else {
chil.QuoteSide = "sell"
}
return json.Marshal(chil)
}
// MarshalJSON serializes the WebsocketLoginData object
func (a *WebsocketLoginData) MarshalJSON() ([]byte, error) {
type Alias WebsocketLoginData
return json.Marshal(struct {
Timestamp int64 `json:"timestamp"`
*Alias
}{
Timestamp: a.Timestamp.UTC().Unix(),
Alias: (*Alias)(a),
})
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *WebsocketLoginData) UnmarshalJSON(data []byte) error {
type Alias WebsocketLoginData
chil := &struct {
*Alias
Timestamp int64 `json:"timestamp"`
}{
Alias: (*Alias)(a),
}
if err := json.Unmarshal(data, chil); err != nil {
return err
}
a.Timestamp = time.UnixMilli(chil.Timestamp)
return nil
}
// UnmarshalJSON deserializes JSON, and timestamp information.
func (a *CurrencyOneClickRepay) UnmarshalJSON(data []byte) error {
type Alias CurrencyOneClickRepay
chil := &struct {
*Alias
UpdateTime int64 `json:"uTime,string"`
FillToSize string `json:"fillToSz"`
FillFromSize string `json:"fillFromSz"`
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, chil)
if err != nil {
return err
}
a.UpdateTime = time.Unix(chil.UpdateTime, 0)
return nil
}

3169
exchanges/okx/okx_types.go Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1471
exchanges/okx/okx_wrapper.go Normal file

File diff suppressed because it is too large Load Diff

1034
exchanges/okx/ratelimit.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -286,7 +286,7 @@ const (
)
// Type enforces a standard for order types across the code base
type Type uint16
type Type uint32
// Defined package order types
const (
@@ -306,6 +306,7 @@ const (
AnyType
Liquidation
Trigger
OptimalLimitIOC
)
// Side enforces a standard for order sides across the code base

View File

@@ -611,6 +611,8 @@ func (t Type) String() string {
return "LIQUIDATION"
case Trigger:
return "TRIGGER"
case OptimalLimitIOC:
return "OPTIMAL_LIMIT_IOC"
default:
return "UNKNOWN"
}
@@ -1008,6 +1010,8 @@ func StringToOrderType(oType string) (Type, error) {
return AnyType, nil
case Trigger.String():
return Trigger, nil
case OptimalLimitIOC.String():
return OptimalLimitIOC, nil
default:
return UnknownType, fmt.Errorf("'%v' %w", oType, errUnrecognisedOrderType)
}
@@ -1022,7 +1026,7 @@ func StringToOrderStatus(status string) (Status, error) {
return AnyStatus, nil
case New.String(), "PLACED", "ACCEPTED":
return New, nil
case Active.String(), "STATUS_ACTIVE":
case Active.String(), "STATUS_ACTIVE", "LIVE":
return Active, nil
case PartiallyFilled.String(), "PARTIALLY MATCHED", "PARTIALLY FILLED":
return PartiallyFilled, nil

View File

@@ -16,7 +16,7 @@ const (
WebsocketResponseExtendedTimeout = (15 * time.Second)
// WebsocketChannelOverrideCapacity used in websocket testing
// Defines channel capacity as defaults size can block tests
WebsocketChannelOverrideCapacity = 75
WebsocketChannelOverrideCapacity = 500
MockTesting = "Mock testing framework in use for %s exchange on REST endpoints only"
LiveTesting = "Mock testing bypassed; live testing of REST endpoints in use for %s exchange"
@@ -38,7 +38,7 @@ func GetWebsocketStructChannelOverride() chan struct{} {
func NewTestWebsocket() *stream.Websocket {
return &stream.Websocket{
Init: true,
DataHandler: make(chan interface{}, 75),
DataHandler: make(chan interface{}, WebsocketChannelOverrideCapacity),
ToRoutine: make(chan interface{}, 1000),
TrafficAlert: make(chan struct{}),
ReadMessageErrors: make(chan error),

View File

@@ -8,7 +8,7 @@ import (
)
const (
testExchange = "OKEX"
testExchange = "Okx"
)
func TestLenByPrice(t *testing.T) {

View File

@@ -870,7 +870,6 @@ func (w *Websocket) UnsubscribeChannels(channels []ChannelSubscription) error {
w.exchangeName)
}
w.subscriptionMutex.Lock()
defer w.subscriptionMutex.Unlock()
channels:
for x := range channels {
@@ -879,10 +878,12 @@ channels:
continue channels
}
}
w.subscriptionMutex.Unlock()
return fmt.Errorf("%s websocket: subscription not found in list: %+v",
w.exchangeName,
channels[x])
}
w.subscriptionMutex.Unlock()
return w.Unsubscriber(channels)
}
@@ -902,16 +903,17 @@ func (w *Websocket) SubscribeToChannels(channels []ChannelSubscription) error {
w.exchangeName)
}
w.subscriptionMutex.Lock()
defer w.subscriptionMutex.Unlock()
for x := range channels {
for y := range w.subscriptions {
if channels[x].Equal(&w.subscriptions[y]) {
w.subscriptionMutex.Unlock()
return fmt.Errorf("%s websocket: %v already subscribed",
w.exchangeName,
channels[x])
}
}
}
w.subscriptionMutex.Unlock()
if err := w.Subscriber(channels); err != nil {
return fmt.Errorf("%v %w: %v", w.exchangeName, ErrSubscriptionFailure, err)
}
@@ -921,12 +923,16 @@ func (w *Websocket) SubscribeToChannels(channels []ChannelSubscription) error {
// AddSuccessfulSubscriptions adds subscriptions to the subscription lists that
// has been successfully subscribed
func (w *Websocket) AddSuccessfulSubscriptions(channels ...ChannelSubscription) {
w.subscriptionMutex.Lock()
w.subscriptions = append(w.subscriptions, channels...)
w.subscriptionMutex.Unlock()
}
// RemoveSuccessfulUnsubscriptions removes subscriptions from the subscription
// list that has been successfulling unsubscribed
func (w *Websocket) RemoveSuccessfulUnsubscriptions(channels ...ChannelSubscription) {
w.subscriptionMutex.Lock()
defer w.subscriptionMutex.Unlock()
for x := range channels {
for y := range w.subscriptions {
if channels[x].Equal(&w.subscriptions[y]) {

View File

@@ -38,7 +38,7 @@ var Exchanges = []string{
"lbank",
"localbitcoins",
"okcoin international",
"okex",
"okx",
"poloniex",
"yobit",
"zb",

View File

@@ -84,7 +84,7 @@ _b in this context is an `IBotExchange` implemented struct_
| Lbank | Yes | No | Yes |
| LocalBitcoins | Yes | NA | No |
| OKCoin International | Yes | Yes | No |
| OKEX | Yes | Yes | No |
| Okx | Yes | Yes | Yes |
| Poloniex | Yes | Yes | Yes |
| Yobit | Yes | NA | No |
| ZB.COM | Yes | Yes | No |

View File

@@ -191,7 +191,7 @@ func (z *ZB) Setup(exch *config.Exchange) error {
})
}
// Start starts the OKEX go routine
// Start starts the ZB go routine
func (z *ZB) Start(wg *sync.WaitGroup) error {
if wg == nil {
return fmt.Errorf("%T %w", wg, common.ErrNilPointer)
@@ -204,7 +204,7 @@ func (z *ZB) Start(wg *sync.WaitGroup) error {
return nil
}
// Run implements the OKEX wrapper
// Run implements the ZB wrapper
func (z *ZB) Run() {
if z.Verbose {
z.PrintEnabledPairs()

View File

@@ -152,10 +152,10 @@ func TestExchangeAddressExists(t *testing.T) {
func TestAddExchangeAddress(t *testing.T) {
t.Parallel()
newBase := Base{}
newBase.AddExchangeAddress("OKEX", currency.BTC, 100)
newBase.AddExchangeAddress("OKEX", currency.BTC, 200)
newBase.AddExchangeAddress("Okx", currency.BTC, 100)
newBase.AddExchangeAddress("Okx", currency.BTC, 200)
if !newBase.ExchangeAddressExists("OKEX", currency.BTC) {
if !newBase.ExchangeAddressExists("Okx", currency.BTC) {
t.Error("address doesn't exist")
}
}
@@ -369,13 +369,13 @@ func TestUpdatePortfolio(t *testing.T) {
func TestGetPortfolioByExchange(t *testing.T) {
t.Parallel()
newBase := Base{}
newBase.AddExchangeAddress("OKEX", currency.LTC, 0.07)
newBase.AddExchangeAddress("Okx", currency.LTC, 0.07)
newBase.AddExchangeAddress("Bitfinex", currency.LTC, 0.05)
err := newBase.AddAddress("someaddress", "LTC", currency.NewCode(PersonalAddress), 0.03)
if err != nil {
t.Fatal(err)
}
value := newBase.GetPortfolioByExchange("OKEX")
value := newBase.GetPortfolioByExchange("Okx")
result, ok := value[currency.LTC]
if !ok {
t.Error("missing portfolio entry")
@@ -399,7 +399,7 @@ func TestGetPortfolioByExchange(t *testing.T) {
func TestGetExchangePortfolio(t *testing.T) {
t.Parallel()
newBase := Base{}
err := newBase.AddAddress("OKEX", ExchangeAddress, currency.LTC, 0.03)
err := newBase.AddAddress("Okx", ExchangeAddress, currency.LTC, 0.03)
if err != nil {
t.Fatal(err)
}
@@ -481,7 +481,7 @@ func TestGetPortfolioSummary(t *testing.T) {
// Exchange holdings
newBase.AddExchangeAddress("Bitfinex", currency.LTC, 20)
newBase.AddExchangeAddress("Bitfinex", currency.BTC, 100)
newBase.AddExchangeAddress("OKEX", currency.ETH, 42)
newBase.AddExchangeAddress("Okx", currency.ETH, 42)
value := newBase.GetPortfolioSummary()

File diff suppressed because one or more lines are too long

View File

@@ -21,7 +21,7 @@ kraken,
lbank,
localbitcoins,
okcoin international,
okex,
okx,
poloniex,
yobit,
zb,
1 binanceus
21 lbank
22 localbitcoins
23 okcoin international
24 okex okx
25 poloniex
26 yobit
27 zb