mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
exchanges: Add Kucoin support (#1102)
* init * updates config * wrapper configuration * updates exchange readme * adds SendAuthHTTPRequest and SendHTTPRequest * adds ratelimit file * adds test case and minor fixes * improve error handling * update testcases and improve GetSymbols API * adds SPOT API's * minor fix * WIP * WIP * adds test case * adds check in test case * fixes in Auth. HTTP * improvements * adds trade, kline support and testcases * adds SPOT API and testcases for same * adds SPOT API and testcases * adds SPOT API and testcase * WIP * adds API's * adds API's * adds test cases * adds comment to exported data types * adds API and test cases * adds API * adds API * rearrange functions * WIP: adds API * adds API for Post Order SPOT * adds API and few fixes * fixes * WIP * WIP * add PostBulkOrder API and its test case * fix issues * adds cancel order APIs and test cases for same * add minor test fixes * add API * adds API * fixes * add API * adds API and test cases * fix test * adds API * adds test * fix test * adds API and test * adds deposit API and test cases * WIP * adds API and test cases * WIP * WIP * add public future API and test cases * WIP * remove v2 API and replace them with v1 * update test cases * adds future order API and test cases * adds futures order API * adds API * add API and test cases * adds API and test cases * adds API and test cases * adds API and test cases * Adding wrapper functions * Fix on wrapper function * Adding websocket support * Complete addressing WS push datas * Adding spot push data unit tests * adding futures websocket push data handlers * Adding futures websocket push data handlers * Added unit tests * Updating unit tests * Updating wrapper and unit test functions * Adding missing wrapper functions and code cleaning up * Resolved linter issues * Fixing websocket issues * Fixing websocket issues * Slight fix on config_example file * Minor update * Basic nits updates * Fix minor linter issues * Minor update * Minor unit test update * Minor unit test update * Code update and linter issues fix * Removed unnecessary type conversion codes * Monor update based on review comment * Fix based on review comments * Adding rate-limiter * Websocket update and overall minor fixes * Removed IsAssetTypeEnabled method implementation * Fix connection and formatting issues * Updating orderbook issues * Very minor label fix * Minor error returning fix * code cleaning up and minor spelling fix * Updates on unit test * Update on unit tests and slight code structure * unit test update * orderbook update and minor fix * fix on race * Mini linter fix * fix minor parameter and unit test issues * handler funcs and models update * Fixing websocket and unit test issues * order side string for active orders * Fix on websocket and unit tests * Minor type changes * Minor Orderbook fix and unit test update * Small fix on orderbook * Updating orderbook functionality * FIx on websocket orderbook handlers * Small update on kucoin websocket * fix missed review comments * fix based on review comments * Updating websocket orderbook and fixing unit tests * Minor fixes * unit test update * Updating unit test according to enabled asset type * toggle canManipulateRealOrders const * Unit test update * Fix minor issues * minor fix * documentation fix * wrapper coverage and unused params fix * testing and minor changes * documentation, websocket and unit test update * minor linter fix * Websocket spot/margin subscription update * minor ticker update fix * minor fixes on endpoints * timestamp and number convert method and unit tests * timestamp convert minor update * minor type and conversion fix * create a common timestamp convert and fix minor issues * linter and ticker fix * Updating unit tests and order placing endpoint methods * Added a pairs check * Fix config test error * rm unused error variable * Fix source of linter issue * code update: convert, wrapper and websocket fix * minor code update * Websocket code and unit tests update * Websocket ticker ask/bid type change and small error msg fix * docs update * fix: websocket orderbook handling * change orderbook channel to marketOrderbookLevel2Channels and fix websocket orderbook update * Minor func rename and reciever change * Minor orderbook unit test issue fix * comment: about why we used a random delimiter '-' for futures * update config files and FetchTradablePair func for futures pairs * futures config pairs update * remove ConnextionMonitorDelay from websocket setup * fix on types and futures pair conversion * updating config pairs * change NewPairFromString to DeriveFrom * unit tests update * unit tests update * Added TickerBatching * added GetStandardConfig to GetDefaultConfig --------- Co-authored-by: Jaydeep Rajpurohit <jaydeeppurohit1996@gmail.com>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -49,4 +49,4 @@ __debug_bin
|
||||
|
||||
# Coverage reports
|
||||
coverage.txt
|
||||
wrapperconfig.json
|
||||
wrapperconfig.json
|
||||
|
||||
@@ -6,8 +6,8 @@ gloriousCode | https://github.com/gloriousCode
|
||||
dependabot[bot] | https://github.com/apps/dependabot
|
||||
dependabot-preview[bot] | https://github.com/apps/dependabot-preview
|
||||
xtda | https://github.com/xtda
|
||||
lrascao | https://github.com/lrascao
|
||||
gbjk | https://github.com/gbjk
|
||||
lrascao | https://github.com/lrascao
|
||||
Rots | https://github.com/Rots
|
||||
vazha | https://github.com/vazha
|
||||
ydm | https://github.com/ydm
|
||||
@@ -19,12 +19,12 @@ marcofranssen | https://github.com/marcofranssen
|
||||
geseq | https://github.com/geseq
|
||||
Beadko | https://github.com/Beadko
|
||||
TaltaM | https://github.com/TaltaM
|
||||
samuael | https://github.com/samuael
|
||||
dackroyd | https://github.com/dackroyd
|
||||
cranktakular | https://github.com/cranktakular
|
||||
khcchiu | https://github.com/khcchiu
|
||||
samuael | https://github.com/samuael
|
||||
woshidama323 | https://github.com/woshidama323
|
||||
yangrq1018 | https://github.com/yangrq1018
|
||||
woshidama323 | https://github.com/woshidama323
|
||||
crackcomm | https://github.com/crackcomm
|
||||
azhang | https://github.com/azhang
|
||||
andreygrehov | https://github.com/andreygrehov
|
||||
|
||||
15
README.md
15
README.md
@@ -39,6 +39,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
||||
| Huobi.Pro | Yes | Yes | NA |
|
||||
| ItBit | Yes | NA | No |
|
||||
| Kraken | Yes | Yes | NA |
|
||||
| Kucoin | Yes | Yes | NA |
|
||||
| Lbank | Yes | No | NA |
|
||||
| Okcoin | Yes | Yes | No |
|
||||
| Okx | Yes | Yes | NA |
|
||||
@@ -143,14 +144,14 @@ Binaries will be published once the codebase reaches a stable condition.
|
||||
|
||||
|User|Contribution Amount|
|
||||
|--|--|
|
||||
| [thrasher-](https://github.com/thrasher-) | 682 |
|
||||
| [shazbert](https://github.com/shazbert) | 299 |
|
||||
| [gloriousCode](https://github.com/gloriousCode) | 217 |
|
||||
| [dependabot[bot]](https://github.com/apps/dependabot) | 202 |
|
||||
| [thrasher-](https://github.com/thrasher-) | 683 |
|
||||
| [shazbert](https://github.com/shazbert) | 301 |
|
||||
| [gloriousCode](https://github.com/gloriousCode) | 219 |
|
||||
| [dependabot[bot]](https://github.com/apps/dependabot) | 207 |
|
||||
| [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 88 |
|
||||
| [xtda](https://github.com/xtda) | 47 |
|
||||
| [gbjk](https://github.com/gbjk) | 35 |
|
||||
| [lrascao](https://github.com/lrascao) | 27 |
|
||||
| [gbjk](https://github.com/gbjk) | 26 |
|
||||
| [Rots](https://github.com/Rots) | 15 |
|
||||
| [vazha](https://github.com/vazha) | 15 |
|
||||
| [ydm](https://github.com/ydm) | 15 |
|
||||
@@ -162,12 +163,12 @@ Binaries will be published once the codebase reaches a stable condition.
|
||||
| [geseq](https://github.com/geseq) | 8 |
|
||||
| [Beadko](https://github.com/Beadko) | 6 |
|
||||
| [TaltaM](https://github.com/TaltaM) | 6 |
|
||||
| [samuael](https://github.com/samuael) | 6 |
|
||||
| [dackroyd](https://github.com/dackroyd) | 5 |
|
||||
| [cranktakular](https://github.com/cranktakular) | 5 |
|
||||
| [khcchiu](https://github.com/khcchiu) | 5 |
|
||||
| [samuael](https://github.com/samuael) | 5 |
|
||||
| [yangrq1018](https://github.com/yangrq1018) | 4 |
|
||||
| [woshidama323](https://github.com/woshidama323) | 3 |
|
||||
| [yangrq1018](https://github.com/yangrq1018) | 3 |
|
||||
| [crackcomm](https://github.com/crackcomm) | 3 |
|
||||
| [azhang](https://github.com/azhang) | 2 |
|
||||
| [andreygrehov](https://github.com/andreygrehov) | 2 |
|
||||
|
||||
@@ -62,6 +62,7 @@ _b in this context is an `IBotExchange` implemented struct_
|
||||
| Huobi.Pro | Yes | Yes | No |
|
||||
| ItBit | Yes | NA | No |
|
||||
| Kraken | Yes | Yes | No |
|
||||
| Kucoin | Yes | No | Yes |
|
||||
| Lbank | Yes | No | Yes |
|
||||
| Okcoin | Yes | Yes | Yes |
|
||||
| Okx | Yes | Yes | Yes |
|
||||
|
||||
@@ -40,6 +40,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
||||
| Huobi.Pro | Yes | Yes | NA |
|
||||
| ItBit | Yes | NA | No |
|
||||
| Kraken | Yes | Yes | NA |
|
||||
| Kucoin | Yes | Yes | NA |
|
||||
| Lbank | Yes | No | NA |
|
||||
| Okcoin | Yes | Yes | No |
|
||||
| Okx | Yes | Yes | NA |
|
||||
|
||||
@@ -136,6 +136,11 @@
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"kucoin":{
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"lbank": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
|
||||
@@ -2,6 +2,7 @@ package convert
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
@@ -242,3 +243,56 @@ func (f StringToFloat64) Float64() float64 {
|
||||
func (f StringToFloat64) Decimal() decimal.Decimal {
|
||||
return decimal.NewFromFloat(float64(f))
|
||||
}
|
||||
|
||||
// ExchangeTime provides timestamp to time conversion method.
|
||||
type ExchangeTime time.Time
|
||||
|
||||
// UnmarshalJSON is custom type json unmarshaller for ExchangeTime
|
||||
func (k *ExchangeTime) UnmarshalJSON(data []byte) error {
|
||||
var timestamp interface{}
|
||||
err := json.Unmarshal(data, ×tamp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var standard int64
|
||||
switch value := timestamp.(type) {
|
||||
case string:
|
||||
if value == "" {
|
||||
// Setting the time to zero value because some timestamp fields could return an empty string while there is no error
|
||||
// So, in such cases, Time returns zero timestamp.
|
||||
break
|
||||
}
|
||||
standard, err = strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case int64:
|
||||
standard = value
|
||||
case float64:
|
||||
// Warning: converting float64 to int64 instance may create loss of precision in the timestamp information.
|
||||
// be aware or consider customizing this section if found necessary.
|
||||
standard = int64(value)
|
||||
case nil:
|
||||
// for some exchange timestamp fields, if the timestamp information is not specified,
|
||||
// the data is 'nil' instead of zero value string or integer value.
|
||||
default:
|
||||
return fmt.Errorf("unsupported timestamp type %T", timestamp)
|
||||
}
|
||||
|
||||
switch {
|
||||
case standard == 0:
|
||||
*k = ExchangeTime(time.Time{})
|
||||
case standard >= 1e13:
|
||||
*k = ExchangeTime(time.Unix(standard/1e9, standard%1e9))
|
||||
case standard > 9999999999:
|
||||
*k = ExchangeTime(time.UnixMilli(standard))
|
||||
default:
|
||||
*k = ExchangeTime(time.Unix(standard, 0))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Time returns a time.Time instance from ExchangeTime instance object.
|
||||
func (k ExchangeTime) Time() time.Time {
|
||||
return time.Time(k)
|
||||
}
|
||||
|
||||
@@ -400,3 +400,86 @@ func BenchmarkStringToFloat64(b *testing.B) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExchangeTimeUnmarshalJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
unmarshaledResult := &struct {
|
||||
Timestamp ExchangeTime `json:"ts"`
|
||||
}{}
|
||||
data1 := `{"ts":""}`
|
||||
result := time.Time{}
|
||||
err := json.Unmarshal([]byte(data1), &unmarshaledResult)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !unmarshaledResult.Timestamp.Time().Equal(result) {
|
||||
t.Errorf("found %v, but expected %v", unmarshaledResult.Timestamp.Time(), result)
|
||||
}
|
||||
data2 := `{"ts":"1685564775371"}`
|
||||
result = time.UnixMilli(1685564775371)
|
||||
err = json.Unmarshal([]byte(data2), &unmarshaledResult)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !unmarshaledResult.Timestamp.Time().Equal(result) {
|
||||
t.Errorf("found %v, but expected %v", unmarshaledResult.Timestamp.Time(), result)
|
||||
}
|
||||
data3 := `{"ts":1685564775371}`
|
||||
err = json.Unmarshal([]byte(data3), &unmarshaledResult)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !unmarshaledResult.Timestamp.Time().Equal(result) {
|
||||
t.Errorf("found %v, but expected %v", unmarshaledResult.Timestamp.Time(), result)
|
||||
}
|
||||
data4 := `{"ts":"1685564775"}`
|
||||
result = time.Unix(1685564775, 0)
|
||||
err = json.Unmarshal([]byte(data4), &unmarshaledResult)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !unmarshaledResult.Timestamp.Time().Equal(result) {
|
||||
t.Errorf("found %v, but expected %v", unmarshaledResult.Timestamp.Time(), result)
|
||||
}
|
||||
data5 := `{"ts":1685564775}`
|
||||
err = json.Unmarshal([]byte(data5), &unmarshaledResult)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !unmarshaledResult.Timestamp.Time().Equal(result) {
|
||||
t.Errorf("found %v, but expected %v", unmarshaledResult.Timestamp.Time(), result)
|
||||
}
|
||||
data6 := `{"ts":"1685564775371320000"}`
|
||||
result = time.Unix(int64(1685564775371320000)/1e9, int64(1685564775371320000)%1e9)
|
||||
err = json.Unmarshal([]byte(data6), &unmarshaledResult)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !unmarshaledResult.Timestamp.Time().Equal(result) {
|
||||
t.Errorf("found %v, but expected %v", unmarshaledResult.Timestamp.Time(), result)
|
||||
}
|
||||
data7 := `{"ts":"abcdefg"}`
|
||||
err = json.Unmarshal([]byte(data7), &unmarshaledResult)
|
||||
if err == nil {
|
||||
t.Fatal("expecting error but found nil")
|
||||
}
|
||||
data8 := `{"ts":0}`
|
||||
result = time.Time{}
|
||||
err = json.Unmarshal([]byte(data8), &unmarshaledResult)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !unmarshaledResult.Timestamp.Time().Equal(result) {
|
||||
t.Errorf("found %v, but expected %v", unmarshaledResult.Timestamp.Time(), result)
|
||||
}
|
||||
}
|
||||
|
||||
// 2239239 516.1 ns/op 424 B/op 9 allocs/op
|
||||
func BenchmarkExchangeTimeUnmarshaling(b *testing.B) {
|
||||
unmarshaledResult := &struct {
|
||||
Timestamp ExchangeTime `json:"ts"`
|
||||
}{}
|
||||
data5 := `{"ts":1685564775}`
|
||||
result := time.Unix(1685564775, 0)
|
||||
var err error
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = json.Unmarshal([]byte(data5), &unmarshaledResult); err != nil {
|
||||
b.Fatal(err)
|
||||
} else if !unmarshaledResult.Timestamp.Time().Equal(result) {
|
||||
b.Fatalf("found %v, but expected %v", unmarshaledResult.Timestamp.Time(), result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1679,7 +1679,6 @@ var (
|
||||
YFI = NewCode("YFI")
|
||||
BAL = NewCode("BAL")
|
||||
UMA = NewCode("UMA")
|
||||
KDA = NewCode("KDA")
|
||||
SNX = NewCode("SNX")
|
||||
CRV = NewCode("CRV")
|
||||
OXT = NewCode("OXT")
|
||||
@@ -3008,6 +3007,13 @@ var (
|
||||
USDFL = NewCode("USDFL")
|
||||
FLUSD = NewCode("FLUSD")
|
||||
DUSD = NewCode("DUSD")
|
||||
USDD = NewCode("USDD")
|
||||
KDA = NewCode("KDA")
|
||||
XCN = NewCode("XCN")
|
||||
TEL = NewCode("TEL")
|
||||
XDC = NewCode("XDC")
|
||||
MHC = NewCode("MHC")
|
||||
OXEN = NewCode("OXEN")
|
||||
STETH = NewCode("STETH")
|
||||
|
||||
stables = Currencies{
|
||||
|
||||
@@ -215,6 +215,7 @@ Yes means supported, No means not yet implemented and NA means protocol unsuppor
|
||||
| Huobi.Pro | Yes | Yes | NA |
|
||||
| ItBit | Yes | NA | No |
|
||||
| Kraken | Yes | Yes | NA |
|
||||
| Kucoin | Yes | Yes | No |
|
||||
| Lbank | Yes | No | NA |
|
||||
| Okcoin | Yes | Yes | No |
|
||||
| Okx | Yes | Yes | NA |
|
||||
@@ -245,6 +246,7 @@ var Exchanges = []string{
|
||||
"huobi",
|
||||
"itbit",
|
||||
"kraken",
|
||||
"kucoin",
|
||||
"lbank",
|
||||
"okcoin",
|
||||
"okx",
|
||||
|
||||
@@ -63,6 +63,7 @@ $ ./gctcli withdrawcryptofunds --exchange=binance --currency=USDT --address=TJU9
|
||||
| Huobi.Pro | Yes | Yes | |
|
||||
| ItBit | No | No | |
|
||||
| Kraken | Yes | Yes | Front-end and API don't match total available transfer chains |
|
||||
| Kucoin | Yes | Yes | |
|
||||
| Lbank | No | No | |
|
||||
| Okcoin | Yes | Yes | |
|
||||
| Okx | Yes | Yes | |
|
||||
|
||||
@@ -84,7 +84,8 @@ A helper tool [cmd/dbseed](../cmd/dbseed/README.md) has been created for assisti
|
||||
| HitBTC | Y |
|
||||
| Huobi | Y |
|
||||
| itBIT | |
|
||||
| Kraken | Y |
|
||||
| Kraken | Y |
|
||||
| Kucoin | Y |
|
||||
| lBank | Y |
|
||||
| Okcoin | Y |
|
||||
| Okx | Y |
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/huobi"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/itbit"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kraken"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kucoin"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/lbank"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/okcoin"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/okx"
|
||||
@@ -196,6 +197,8 @@ func (m *ExchangeManager) NewExchangeByName(name string) (exchange.IBotExchange,
|
||||
exch = new(itbit.ItBit)
|
||||
case "kraken":
|
||||
exch = new(kraken.Kraken)
|
||||
case "kucoin":
|
||||
exch = new(kucoin.Kucoin)
|
||||
case "lbank":
|
||||
exch = new(lbank.Lbank)
|
||||
case "okcoin":
|
||||
|
||||
140
exchanges/kucoin/README.md
Normal file
140
exchanges/kucoin/README.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# GoCryptoTrader package Kucoin
|
||||
|
||||
<img src="/common/gctlogo.png?raw=true" width="350px" height="350px" hspace="70">
|
||||
|
||||
|
||||
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
||||
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
||||
[](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/kucoin)
|
||||
[](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
|
||||
|
||||
|
||||
This kucoin package is part of the GoCryptoTrader codebase.
|
||||
|
||||
## This is still in active development
|
||||
|
||||
You can track ideas, planned features and what's in progress on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
|
||||
|
||||
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk)
|
||||
|
||||
## Kucoin 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)
|
||||
|
||||
+ Individual package example below:
|
||||
|
||||
```go
|
||||
// Exchanges will be abstracted out in further updates and examples will be
|
||||
// supplied then
|
||||
```
|
||||
|
||||
### How to do REST public/private calls
|
||||
|
||||
+ If enabled via "configuration".json file the exchange will be added to the
|
||||
IBotExchange array in the ```go var bot Bot``` and you will only be able to use
|
||||
the wrapper interface functions for accessing exchange data. View routines.go
|
||||
for an example of integration usage with GoCryptoTrader. Rudimentary example
|
||||
below:
|
||||
|
||||
main.go
|
||||
```go
|
||||
var b exchange.IBotExchange
|
||||
|
||||
for i := range bot.Exchanges {
|
||||
if bot.Exchanges[i].GetName() == "Kucoin" {
|
||||
b = bot.Exchanges[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Public calls - wrapper functions
|
||||
|
||||
// Fetches current ticker information
|
||||
tick, err := b.FetchTicker()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Fetches current orderbook information
|
||||
ob, err := b.FetchOrderbook()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
|
||||
// set and AuthenticatedAPISupport is set to true
|
||||
|
||||
// Fetches current account information
|
||||
accountInfo, err := b.GetAccountInfo()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
```
|
||||
|
||||
+ If enabled via individually importing package, rudimentary example below:
|
||||
|
||||
```go
|
||||
// Public calls
|
||||
|
||||
// Fetches current ticker information
|
||||
ticker, err := b.GetTicker()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Fetches current orderbook information
|
||||
ob, err := b.GetOrderBook()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Private calls - make sure your APIKEY and APISECRET are set and
|
||||
// AuthenticatedAPISupport is set to true
|
||||
|
||||
// GetUserInfo returns account info
|
||||
accountInfo, err := b.GetUserInfo(...)
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Submits an order and the exchange and returns its tradeID
|
||||
tradeID, err := b.Trade(...)
|
||||
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
|
||||
|
||||
## Contribution
|
||||
|
||||
Please feel free to submit any pull requests or suggest any desired features to be added.
|
||||
|
||||
When submitting a PR, please abide by our coding guidelines:
|
||||
|
||||
+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
|
||||
+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
|
||||
+ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md).
|
||||
+ Pull requests need to be based on and opened against the `master` branch.
|
||||
|
||||
## Donations
|
||||
|
||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
||||
|
||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||
|
||||
***bc1qk0jareu4jytc0cfrhr5wgshsq8282awpavfahc***
|
||||
1851
exchanges/kucoin/kucoin.go
Normal file
1851
exchanges/kucoin/kucoin.go
Normal file
File diff suppressed because it is too large
Load Diff
67
exchanges/kucoin/kucoin_convert.go
Normal file
67
exchanges/kucoin/kucoin_convert.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package kucoin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// UnmarshalJSON valid data to SubAccountsResponse of return nil if the data is empty list.
|
||||
// this is added to handle the empty list returned when there are no accounts.
|
||||
func (a *SubAccountsResponse) UnmarshalJSON(data []byte) error {
|
||||
var result interface{}
|
||||
err := json.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var ok bool
|
||||
if a, ok = result.(*SubAccountsResponse); ok {
|
||||
if a == nil {
|
||||
return errNoValidResponseFromServer
|
||||
}
|
||||
return nil
|
||||
} else if _, ok := result.([]interface{}); ok {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("%w can not unmarshal to SubAccountsResponse", errMalformedData)
|
||||
}
|
||||
|
||||
// kucoinNumber unmarshals and extract numeric value from a byte slice.
|
||||
type kucoinNumber float64
|
||||
|
||||
// Float64 returns an float64 value from kucoinNumeric instance
|
||||
func (a *kucoinNumber) Float64() float64 {
|
||||
return float64(*a)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decerializes integer and string data having an integer value to int64
|
||||
func (a *kucoinNumber) UnmarshalJSON(data []byte) error {
|
||||
var value interface{}
|
||||
err := json.Unmarshal(data, &value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch val := value.(type) {
|
||||
case float64:
|
||||
*a = kucoinNumber(val)
|
||||
case float32:
|
||||
*a = kucoinNumber(val)
|
||||
case string:
|
||||
if val == "" {
|
||||
*a = kucoinNumber(0) // setting empty string value to zero to reset previous value if exist.
|
||||
return nil
|
||||
}
|
||||
value, err := strconv.ParseFloat(val, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*a = kucoinNumber(value)
|
||||
case int64:
|
||||
*a = kucoinNumber(val)
|
||||
case int32:
|
||||
*a = kucoinNumber(val)
|
||||
default:
|
||||
return fmt.Errorf("unsupported input numeric type %T", value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
821
exchanges/kucoin/kucoin_futures.go
Normal file
821
exchanges/kucoin/kucoin_futures.go
Normal file
@@ -0,0 +1,821 @@
|
||||
package kucoin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
)
|
||||
|
||||
const (
|
||||
kucoinFuturesAPIURL = "https://api-futures.kucoin.com/api"
|
||||
kucoinWebsocketURL = "wss://ws-api.kucoin.com/endpoint"
|
||||
|
||||
// Public market endpoints
|
||||
kucoinFuturesOpenContracts = "/v1/contracts/active"
|
||||
kucoinFuturesContract = "/v1/contracts/"
|
||||
kucoinFuturesRealTimeTicker = "/v1/ticker"
|
||||
kucoinFuturesFullOrderbook = "/v1/level2/snapshot"
|
||||
kucoinFuturesPartOrderbook20 = "/v1/level2/depth20"
|
||||
kucoinFuturesPartOrderbook100 = "/v1/level2/depth100"
|
||||
kucoinFuturesTradeHistory = "/v1/trade/history"
|
||||
kucoinFuturesInterestRate = "/v1/interest/query"
|
||||
kucoinFuturesIndex = "/v1/index/query"
|
||||
kucoinFuturesMarkPrice = "/v1/mark-price/%s/current"
|
||||
kucoinFuturesPremiumIndex = "/v1/premium/query"
|
||||
kucoinFuturesFundingRate = "/v1/funding-rate/%s/current"
|
||||
kucoinFuturesServerTime = "/v1/timestamp"
|
||||
kucoinFuturesServiceStatus = "/v1/status"
|
||||
kucoinFuturesKline = "/v1/kline/query"
|
||||
|
||||
// Authenticated endpoints
|
||||
kucoinFuturesOrder = "/v1/orders"
|
||||
kucoinFuturesCancelOrder = "/v1/orders/"
|
||||
kucoinFuturesStopOrder = "/v1/stopOrders"
|
||||
kucoinFuturesRecentCompletedOrder = "/v1/recentDoneOrders"
|
||||
kucoinFuturesGetOrderDetails = "/v1/orders/"
|
||||
kucoinFuturesGetOrderDetailsByClientID = "/v1/orders/byClientOid"
|
||||
|
||||
kucoinFuturesFills = "/v1/fills"
|
||||
kucoinFuturesRecentFills = "/v1/recentFills"
|
||||
kucoinFuturesOpenOrderStats = "/v1/openOrderStatistics"
|
||||
kucoinFuturesPosition = "/v1/position"
|
||||
kucoinFuturesPositionList = "/v1/positions"
|
||||
kucoinFuturesSetAutoDeposit = "/v1/position/margin/auto-deposit-status"
|
||||
kucoinFuturesAddMargin = "/v1/position/margin/deposit-margin"
|
||||
kucoinFuturesRiskLimitLevel = "/v1/contracts/risk-limit/"
|
||||
kucoinFuturesUpdateRiskLmitLevel = "/v1/position/risk-limit-level/change"
|
||||
kucoinFuturesFundingHistory = "/v1/funding-history"
|
||||
|
||||
kucoinFuturesAccountOverview = "/v1/account-overview"
|
||||
kucoinFuturesTransactionHistory = "/v1/transaction-history"
|
||||
kucoinFuturesSubAccountAPI = "/v1/sub/api-key"
|
||||
kucoinFuturesDepositAddress = "/v1/deposit-address"
|
||||
kucoinFuturesDepositsList = "/v1/deposit-list"
|
||||
kucoinFuturesWithdrawalLimit = "/v1/withdrawals/quotas"
|
||||
kucoinFuturesWithdrawalList = "/v1/withdrawal-list"
|
||||
kucoinFuturesCancelWithdrawal = "/v1/withdrawals/"
|
||||
kucoinFuturesTransferFundtoMainAccount = "/v3/transfer-out"
|
||||
kucoinFuturesTransferFundtoFuturesAccount = "/v1/transfer-in"
|
||||
kucoinFuturesTransferOutList = "/v1/transfer-list"
|
||||
kucoinFuturesCancelTransferOut = "/v1/cancel/transfer-out"
|
||||
)
|
||||
|
||||
// GetFuturesOpenContracts gets all open futures contract with its details
|
||||
func (ku *Kucoin) GetFuturesOpenContracts(ctx context.Context) ([]Contract, error) {
|
||||
var resp []Contract
|
||||
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, kucoinFuturesOpenContracts, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesContract get contract details
|
||||
func (ku *Kucoin) GetFuturesContract(ctx context.Context, symbol string) (*Contract, error) {
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
var resp *Contract
|
||||
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, kucoinFuturesContract+symbol, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesRealTimeTicker get real time ticker
|
||||
func (ku *Kucoin) GetFuturesRealTimeTicker(ctx context.Context, symbol string) (*FuturesTicker, error) {
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
var resp *FuturesTicker
|
||||
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesRealTimeTicker, params), &resp)
|
||||
}
|
||||
|
||||
// GetFuturesOrderbook gets full orderbook for a specified symbol
|
||||
func (ku *Kucoin) GetFuturesOrderbook(ctx context.Context, symbol string) (*Orderbook, error) {
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
var o futuresOrderbookResponse
|
||||
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresRetrieveFullOrderbookLevel2EPL, common.EncodeURLValues(kucoinFuturesFullOrderbook, params), &o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return constructFuturesOrderbook(&o)
|
||||
}
|
||||
|
||||
// GetFuturesPartOrderbook20 gets orderbook for a specified symbol with depth 20
|
||||
func (ku *Kucoin) GetFuturesPartOrderbook20(ctx context.Context, symbol string) (*Orderbook, error) {
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
var o futuresOrderbookResponse
|
||||
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesPartOrderbook20, params), &o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return constructFuturesOrderbook(&o)
|
||||
}
|
||||
|
||||
// GetFuturesPartOrderbook100 gets orderbook for a specified symbol with depth 100
|
||||
func (ku *Kucoin) GetFuturesPartOrderbook100(ctx context.Context, symbol string) (*Orderbook, error) {
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
var o futuresOrderbookResponse
|
||||
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesPartOrderbook100, params), &o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return constructFuturesOrderbook(&o)
|
||||
}
|
||||
|
||||
// GetFuturesTradeHistory get last 100 trades for symbol
|
||||
func (ku *Kucoin) GetFuturesTradeHistory(ctx context.Context, symbol string) ([]FuturesTrade, error) {
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
var resp []FuturesTrade
|
||||
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesTradeHistory, params), &resp)
|
||||
}
|
||||
|
||||
// GetFuturesInterestRate get interest rate
|
||||
func (ku *Kucoin) GetFuturesInterestRate(ctx context.Context, symbol string, startAt, endAt time.Time, reverse, forward bool, offset, maxCount int64) (*FundingInterestRateResponse, error) {
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
|
||||
if !startAt.IsZero() {
|
||||
params.Set("startAt", strconv.FormatInt(startAt.UnixMilli(), 10))
|
||||
}
|
||||
if !endAt.IsZero() {
|
||||
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
|
||||
}
|
||||
params.Set("reverse", strconv.FormatBool(reverse))
|
||||
params.Set("forward", strconv.FormatBool(forward))
|
||||
if offset != 0 {
|
||||
params.Set("offset", strconv.FormatInt(offset, 10))
|
||||
}
|
||||
if maxCount != 0 {
|
||||
params.Set("maxCount", strconv.FormatInt(maxCount, 10))
|
||||
}
|
||||
var resp *FundingInterestRateResponse
|
||||
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesInterestRate, params), &resp)
|
||||
}
|
||||
|
||||
// GetFuturesIndexList retrieves futures index information for a symbol
|
||||
func (ku *Kucoin) GetFuturesIndexList(ctx context.Context, symbol string, startAt, endAt time.Time, reverse, forward bool, offset, maxCount int64) (*FuturesIndexResponse, error) {
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
if !startAt.IsZero() {
|
||||
params.Set("startAt", strconv.FormatInt(startAt.UnixMilli(), 10))
|
||||
}
|
||||
if !endAt.IsZero() {
|
||||
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
|
||||
}
|
||||
params.Set("reverse", strconv.FormatBool(reverse))
|
||||
params.Set("forward", strconv.FormatBool(forward))
|
||||
if offset != 0 {
|
||||
params.Set("offset", strconv.FormatInt(offset, 10))
|
||||
}
|
||||
if maxCount != 0 {
|
||||
params.Set("maxCount", strconv.FormatInt(maxCount, 10))
|
||||
}
|
||||
var resp *FuturesIndexResponse
|
||||
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesIndex, params), &resp)
|
||||
}
|
||||
|
||||
// GetFuturesCurrentMarkPrice get current mark price
|
||||
func (ku *Kucoin) GetFuturesCurrentMarkPrice(ctx context.Context, symbol string) (*FuturesMarkPrice, error) {
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
var resp *FuturesMarkPrice
|
||||
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, fmt.Sprintf(kucoinFuturesMarkPrice, symbol), &resp)
|
||||
}
|
||||
|
||||
// GetFuturesPremiumIndex get premium index
|
||||
func (ku *Kucoin) GetFuturesPremiumIndex(ctx context.Context, symbol string, startAt, endAt time.Time, reverse, forward bool, offset, maxCount int64) (*FuturesInterestRateResponse, error) {
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
if !startAt.IsZero() {
|
||||
params.Set("startAt", strconv.FormatInt(startAt.UnixMilli(), 10))
|
||||
}
|
||||
if !endAt.IsZero() {
|
||||
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
|
||||
}
|
||||
params.Set("reverse", strconv.FormatBool(reverse))
|
||||
params.Set("forward", strconv.FormatBool(forward))
|
||||
if offset != 0 {
|
||||
params.Set("offset", strconv.FormatInt(offset, 10))
|
||||
}
|
||||
if maxCount != 0 {
|
||||
params.Set("maxCount", strconv.FormatInt(maxCount, 10))
|
||||
}
|
||||
var resp *FuturesInterestRateResponse
|
||||
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesPremiumIndex, params), &resp)
|
||||
}
|
||||
|
||||
// GetFuturesCurrentFundingRate get current funding rate
|
||||
func (ku *Kucoin) GetFuturesCurrentFundingRate(ctx context.Context, symbol string) (*FuturesFundingRate, error) {
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
var resp *FuturesFundingRate
|
||||
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, fmt.Sprintf(kucoinFuturesFundingRate, symbol), &resp)
|
||||
}
|
||||
|
||||
// GetFuturesServerTime get server time
|
||||
func (ku *Kucoin) GetFuturesServerTime(ctx context.Context) (time.Time, error) {
|
||||
resp := struct {
|
||||
Data convert.ExchangeTime `json:"data"`
|
||||
Error
|
||||
}{}
|
||||
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, kucoinFuturesServerTime, &resp)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
return resp.Data.Time(), nil
|
||||
}
|
||||
|
||||
// GetFuturesServiceStatus get service status
|
||||
func (ku *Kucoin) GetFuturesServiceStatus(ctx context.Context) (*FuturesServiceStatus, error) {
|
||||
var resp *FuturesServiceStatus
|
||||
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, kucoinFuturesServiceStatus, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesKline get contract's kline data
|
||||
func (ku *Kucoin) GetFuturesKline(ctx context.Context, granularity int64, symbol string, from, to time.Time) ([]FuturesKline, error) {
|
||||
if granularity == 0 {
|
||||
return nil, errors.New("granularity can not be empty")
|
||||
}
|
||||
if !common.StringDataContains(validGranularity, strconv.FormatInt(granularity, 10)) {
|
||||
return nil, errors.New("invalid granularity")
|
||||
}
|
||||
params := url.Values{}
|
||||
// The granularity (granularity parameter of K-line) represents the number of minutes, the available granularity scope is: 1,5,15,30,60,120,240,480,720,1440,10080. Requests beyond the above range will be rejected.
|
||||
params.Set("granularity", strconv.FormatInt(granularity, 10))
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
params.Set("symbol", symbol)
|
||||
if !from.IsZero() {
|
||||
params.Set("from", strconv.FormatInt(from.UnixMilli(), 10))
|
||||
}
|
||||
if !to.IsZero() {
|
||||
params.Set("to", strconv.FormatInt(to.UnixMilli(), 10))
|
||||
}
|
||||
var resp [][6]float64
|
||||
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesKline, params), &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kline := make([]FuturesKline, len(resp))
|
||||
for i := range resp {
|
||||
kline[i] = FuturesKline{
|
||||
StartTime: time.UnixMilli(int64(resp[i][0])),
|
||||
Open: resp[i][1],
|
||||
High: resp[i][2],
|
||||
Low: resp[i][3],
|
||||
Close: resp[i][4],
|
||||
Volume: resp[i][5],
|
||||
}
|
||||
}
|
||||
return kline, nil
|
||||
}
|
||||
|
||||
// PostFuturesOrder used to place two types of futures orders: limit and market
|
||||
func (ku *Kucoin) PostFuturesOrder(ctx context.Context, arg *FuturesOrderParam) (string, error) {
|
||||
if arg.Leverage < 0.01 {
|
||||
return "", fmt.Errorf("%w must be greater than 0.01", errInvalidLeverage)
|
||||
}
|
||||
if arg.ClientOrderID == "" {
|
||||
return "", errInvalidClientOrderID
|
||||
}
|
||||
if arg.Side == "" {
|
||||
return "", fmt.Errorf("%w, empty order side", order.ErrSideIsInvalid)
|
||||
}
|
||||
if arg.Symbol.IsEmpty() {
|
||||
return "", currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
if arg.Stop != "" {
|
||||
if arg.StopPriceType == "" {
|
||||
return "", errInvalidStopPriceType
|
||||
}
|
||||
if arg.StopPrice <= 0 {
|
||||
return "", fmt.Errorf("%w, stopPrice is required", errInvalidPrice)
|
||||
}
|
||||
}
|
||||
switch arg.OrderType {
|
||||
case "limit", "":
|
||||
if arg.Price <= 0 {
|
||||
return "", fmt.Errorf("%w %f", errInvalidPrice, arg.Price)
|
||||
}
|
||||
if arg.Size <= 0 {
|
||||
return "", fmt.Errorf("%w, must be non-zero positive value", errInvalidSize)
|
||||
}
|
||||
if arg.VisibleSize < 0 {
|
||||
return "", fmt.Errorf("%w, visible size must be non-zero positive value", errInvalidSize)
|
||||
}
|
||||
case "market":
|
||||
if arg.Size <= 0 {
|
||||
return "", fmt.Errorf("%w, market size must be > 0", errInvalidSize)
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("%w, order type= %s", order.ErrTypeIsInvalid, arg.OrderType)
|
||||
}
|
||||
resp := struct {
|
||||
OrderID string `json:"orderId"`
|
||||
}{}
|
||||
return resp.OrderID, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresPlaceOrderEPL, http.MethodPost, kucoinFuturesOrder, &arg, &resp)
|
||||
}
|
||||
|
||||
// CancelFuturesOrder used to cancel single order previously placed including a stop order
|
||||
func (ku *Kucoin) CancelFuturesOrder(ctx context.Context, orderID string) ([]string, error) {
|
||||
resp := struct {
|
||||
CancelledOrderIDs []string `json:"cancelledOrderIds"`
|
||||
}{}
|
||||
|
||||
if orderID == "" {
|
||||
return resp.CancelledOrderIDs, errors.New("orderID can't be empty")
|
||||
}
|
||||
return resp.CancelledOrderIDs, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresCancelAnOrderEPL, http.MethodDelete, kucoinFuturesCancelOrder+orderID, nil, &resp)
|
||||
}
|
||||
|
||||
// CancelAllFuturesOpenOrders used to cancel all futures order excluding stop orders
|
||||
func (ku *Kucoin) CancelAllFuturesOpenOrders(ctx context.Context, symbol string) ([]string, error) {
|
||||
params := url.Values{}
|
||||
if symbol != "" {
|
||||
params.Set("symbol", symbol)
|
||||
}
|
||||
resp := struct {
|
||||
CancelledOrderIDs []string `json:"cancelledOrderIds"`
|
||||
}{}
|
||||
return resp.CancelledOrderIDs, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresLimitOrderMassCancelationEPL, http.MethodDelete, common.EncodeURLValues(kucoinFuturesOrder, params), nil, &resp)
|
||||
}
|
||||
|
||||
// CancelAllFuturesStopOrders used to cancel all untriggered stop orders
|
||||
func (ku *Kucoin) CancelAllFuturesStopOrders(ctx context.Context, symbol string) ([]string, error) {
|
||||
params := url.Values{}
|
||||
if symbol != "" {
|
||||
params.Set("symbol", symbol)
|
||||
}
|
||||
resp := struct {
|
||||
CancelledOrderIDs []string `json:"cancelledOrderIds"`
|
||||
}{}
|
||||
return resp.CancelledOrderIDs, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodDelete, common.EncodeURLValues(kucoinFuturesStopOrder, params), nil, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesOrders gets the user current futures order list
|
||||
func (ku *Kucoin) GetFuturesOrders(ctx context.Context, status, symbol, side, orderType string, startAt, endAt time.Time) (*FutureOrdersResponse, error) {
|
||||
params := url.Values{}
|
||||
if status != "" {
|
||||
params.Set("status", status)
|
||||
}
|
||||
if symbol != "" {
|
||||
params.Set("symbol", symbol)
|
||||
}
|
||||
if side != "" {
|
||||
params.Set("side", side)
|
||||
}
|
||||
if orderType != "" {
|
||||
params.Set("type", orderType)
|
||||
}
|
||||
if !startAt.IsZero() {
|
||||
params.Set("startAt", strconv.FormatInt(startAt.UnixMilli(), 10))
|
||||
}
|
||||
if !endAt.IsZero() {
|
||||
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
|
||||
}
|
||||
var resp *FutureOrdersResponse
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRetrieveOrderListEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesOrder, params), nil, &resp)
|
||||
}
|
||||
|
||||
// GetUntriggeredFuturesStopOrders gets the untriggered stop orders list
|
||||
func (ku *Kucoin) GetUntriggeredFuturesStopOrders(ctx context.Context, symbol, side, orderType string, startAt, endAt time.Time) (*FutureOrdersResponse, error) {
|
||||
params := url.Values{}
|
||||
if symbol != "" {
|
||||
params.Set("symbol", symbol)
|
||||
}
|
||||
if side != "" {
|
||||
params.Set("side", side)
|
||||
}
|
||||
if orderType != "" {
|
||||
params.Set("type", orderType)
|
||||
}
|
||||
if !startAt.IsZero() {
|
||||
params.Set("startAt", strconv.FormatInt(startAt.UnixMilli(), 10))
|
||||
}
|
||||
if !endAt.IsZero() {
|
||||
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
|
||||
}
|
||||
var resp *FutureOrdersResponse
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesStopOrder, params), nil, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesRecentCompletedOrders gets list of recent 1000 orders in the last 24 hours
|
||||
func (ku *Kucoin) GetFuturesRecentCompletedOrders(ctx context.Context) ([]FuturesOrder, error) {
|
||||
var resp []FuturesOrder
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, kucoinFuturesRecentCompletedOrder, nil, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesOrderDetails gets single order details by order ID
|
||||
func (ku *Kucoin) GetFuturesOrderDetails(ctx context.Context, orderID string) (*FuturesOrder, error) {
|
||||
var resp *FuturesOrder
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, kucoinFuturesGetOrderDetails+orderID, nil, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesOrderDetailsByClientID gets single order details by client ID
|
||||
func (ku *Kucoin) GetFuturesOrderDetailsByClientID(ctx context.Context, clientID string) (*FuturesOrder, error) {
|
||||
if clientID == "" {
|
||||
return nil, errors.New("clientID can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("clientOid", clientID)
|
||||
var resp *FuturesOrder
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesGetOrderDetailsByClientID, params), nil, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesFills gets list of recent fills
|
||||
func (ku *Kucoin) GetFuturesFills(ctx context.Context, orderID, symbol, side, orderType string, startAt, endAt time.Time) (*FutureFillsResponse, error) {
|
||||
params := url.Values{}
|
||||
if orderID != "" {
|
||||
params.Set("orderId", orderID)
|
||||
}
|
||||
if symbol != "" {
|
||||
params.Set("symbol", symbol)
|
||||
}
|
||||
if side != "" {
|
||||
params.Set("side", side)
|
||||
}
|
||||
if orderType != "" {
|
||||
params.Set("type", orderType)
|
||||
}
|
||||
if !startAt.IsZero() {
|
||||
params.Set("startAt", strconv.FormatInt(startAt.UnixMilli(), 10))
|
||||
}
|
||||
if !endAt.IsZero() {
|
||||
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
|
||||
}
|
||||
var resp *FutureFillsResponse
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRetrieveFillsEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesFills, params), nil, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesRecentFills gets list of 1000 recent fills in the last 24 hrs
|
||||
func (ku *Kucoin) GetFuturesRecentFills(ctx context.Context) ([]FuturesFill, error) {
|
||||
var resp []FuturesFill
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRecentFillsEPL, http.MethodGet, kucoinFuturesRecentFills, nil, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesOpenOrderStats gets the total number and value of the all your active orders
|
||||
func (ku *Kucoin) GetFuturesOpenOrderStats(ctx context.Context, symbol string) (*FuturesOpenOrderStats, error) {
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
var resp *FuturesOpenOrderStats
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesOpenOrderStats, params), nil, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesPosition gets the position details of a specified position
|
||||
func (ku *Kucoin) GetFuturesPosition(ctx context.Context, symbol string) (*FuturesPosition, error) {
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
var resp *FuturesPosition
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesPosition, params), nil, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesPositionList gets the list of position with details
|
||||
func (ku *Kucoin) GetFuturesPositionList(ctx context.Context) ([]FuturesPosition, error) {
|
||||
var resp []FuturesPosition
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRetrievePositionListEPL, http.MethodGet, kucoinFuturesPositionList, nil, &resp)
|
||||
}
|
||||
|
||||
// SetAutoDepositMargin enable/disable of auto-deposit margin
|
||||
func (ku *Kucoin) SetAutoDepositMargin(ctx context.Context, symbol string, status bool) (bool, error) {
|
||||
params := make(map[string]interface{})
|
||||
if symbol == "" {
|
||||
return false, errors.New("symbol can't be empty")
|
||||
}
|
||||
params["symbol"] = symbol
|
||||
params["status"] = status
|
||||
var resp bool
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodPost, kucoinFuturesSetAutoDeposit, params, &resp)
|
||||
}
|
||||
|
||||
// AddMargin is used to add margin manually
|
||||
func (ku *Kucoin) AddMargin(ctx context.Context, symbol, uniqueID string, margin float64) (*FuturesPosition, error) {
|
||||
params := make(map[string]interface{})
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
params["symbol"] = symbol
|
||||
if uniqueID == "" {
|
||||
return nil, errors.New("uniqueID can't be empty")
|
||||
}
|
||||
params["bizNo"] = uniqueID
|
||||
if margin <= 0 {
|
||||
return nil, errors.New("margin can't be zero or negative")
|
||||
}
|
||||
params["margin"] = strconv.FormatFloat(margin, 'f', -1, 64)
|
||||
var resp *FuturesPosition
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodPost, kucoinFuturesAddMargin, params, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesRiskLimitLevel gets information about risk limit level of a specific contract
|
||||
func (ku *Kucoin) GetFuturesRiskLimitLevel(ctx context.Context, symbol string) ([]FuturesRiskLimitLevel, error) {
|
||||
var resp []FuturesRiskLimitLevel
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, kucoinFuturesRiskLimitLevel+symbol, nil, &resp)
|
||||
}
|
||||
|
||||
// FuturesUpdateRiskLmitLevel is used to adjustment the risk limit level
|
||||
func (ku *Kucoin) FuturesUpdateRiskLmitLevel(ctx context.Context, symbol string, level int64) (bool, error) {
|
||||
params := make(map[string]interface{})
|
||||
if symbol == "" {
|
||||
return false, errors.New("symbol can't be empty")
|
||||
}
|
||||
params["symbol"] = symbol
|
||||
params["level"] = strconv.FormatInt(level, 10)
|
||||
var resp bool
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodPost, kucoinFuturesUpdateRiskLmitLevel, params, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesFundingHistory gets information about funding history
|
||||
func (ku *Kucoin) GetFuturesFundingHistory(ctx context.Context, symbol string, offset, maxCount int64, reverse, forward bool, startAt, endAt time.Time) (*FuturesFundingHistoryResponse, error) {
|
||||
if symbol == "" {
|
||||
return nil, errors.New("symbol can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
if !startAt.IsZero() {
|
||||
params.Set("startAt", strconv.FormatInt(startAt.UnixMilli(), 10))
|
||||
}
|
||||
if !endAt.IsZero() {
|
||||
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
|
||||
}
|
||||
params.Set("reverse", strconv.FormatBool(reverse))
|
||||
params.Set("forward", strconv.FormatBool(forward))
|
||||
if offset != 0 {
|
||||
params.Set("offset", strconv.FormatInt(offset, 10))
|
||||
}
|
||||
if maxCount != 0 {
|
||||
params.Set("maxCount", strconv.FormatInt(maxCount, 10))
|
||||
}
|
||||
var resp *FuturesFundingHistoryResponse
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRetrieveFundingHistoryEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesFundingHistory, params), nil, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesAccountOverview gets future account overview
|
||||
func (ku *Kucoin) GetFuturesAccountOverview(ctx context.Context, currency string) (FuturesAccount, error) {
|
||||
params := url.Values{}
|
||||
if currency != "" {
|
||||
params.Set("currency", currency)
|
||||
}
|
||||
resp := FuturesAccount{}
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRetrieveAccountOverviewEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesAccountOverview, params), nil, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesTransactionHistory gets future transaction history
|
||||
func (ku *Kucoin) GetFuturesTransactionHistory(ctx context.Context, currency, txType string, offset, maxCount int64, forward bool, startAt, endAt time.Time) (*FuturesTransactionHistoryResponse, error) {
|
||||
params := url.Values{}
|
||||
if currency != "" {
|
||||
params.Set("currency", currency)
|
||||
}
|
||||
if txType != "" {
|
||||
params.Set("type", txType)
|
||||
}
|
||||
if !startAt.IsZero() {
|
||||
params.Set("startAt", strconv.FormatInt(startAt.UnixMilli(), 10))
|
||||
}
|
||||
if !endAt.IsZero() {
|
||||
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
|
||||
}
|
||||
params.Set("forward", strconv.FormatBool(forward))
|
||||
if offset != 0 {
|
||||
params.Set("offset", strconv.FormatInt(offset, 10))
|
||||
}
|
||||
if maxCount != 0 {
|
||||
params.Set("maxCount", strconv.FormatInt(maxCount, 10))
|
||||
}
|
||||
var resp *FuturesTransactionHistoryResponse
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRetrieveTransactionHistoryEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesTransactionHistory, params), nil, &resp)
|
||||
}
|
||||
|
||||
// CreateFuturesSubAccountAPIKey is used to create Futures APIs for sub-accounts
|
||||
func (ku *Kucoin) CreateFuturesSubAccountAPIKey(ctx context.Context, ipWhitelist, passphrase, permission, remark, subName string) (*APIKeyDetail, error) {
|
||||
params := make(map[string]interface{})
|
||||
if ipWhitelist != "" {
|
||||
params["ipWhitelist"] = ipWhitelist
|
||||
}
|
||||
if passphrase == "" {
|
||||
return nil, errors.New("passphrase can't be empty")
|
||||
}
|
||||
params["passphrase"] = passphrase
|
||||
if permission != "" {
|
||||
params["permission"] = permission
|
||||
}
|
||||
if remark == "" {
|
||||
return nil, errors.New("remark can't be empty")
|
||||
}
|
||||
params["remark"] = remark
|
||||
if subName == "" {
|
||||
return nil, errors.New("subName can't be empty")
|
||||
}
|
||||
params["subName"] = subName
|
||||
var resp *APIKeyDetail
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodPost, kucoinFuturesSubAccountAPI, params, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesDepositAddress gets deposit address for currency
|
||||
func (ku *Kucoin) GetFuturesDepositAddress(ctx context.Context, currency string) (*DepositAddress, error) {
|
||||
if currency == "" {
|
||||
return nil, errors.New("currency can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("currency", currency)
|
||||
var resp *DepositAddress
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesDepositAddress, params), nil, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesDepositsList gets deposits list
|
||||
func (ku *Kucoin) GetFuturesDepositsList(ctx context.Context, currency, status string, startAt, endAt time.Time) (*FuturesDepositDetailsResponse, error) {
|
||||
params := url.Values{}
|
||||
if currency != "" {
|
||||
params.Set("currency", currency)
|
||||
}
|
||||
if status != "" {
|
||||
params.Set("status", status)
|
||||
}
|
||||
if !startAt.IsZero() {
|
||||
params.Set("startAt", strconv.FormatInt(startAt.UnixMilli(), 10))
|
||||
}
|
||||
if !endAt.IsZero() {
|
||||
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
|
||||
}
|
||||
var resp *FuturesDepositDetailsResponse
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesDepositsList, params), nil, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesWithdrawalLimit gets withdrawal limits for currency
|
||||
func (ku *Kucoin) GetFuturesWithdrawalLimit(ctx context.Context, currency string) (*FuturesWithdrawalLimit, error) {
|
||||
if currency == "" {
|
||||
return nil, errors.New("currency can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("currency", currency)
|
||||
var resp *FuturesWithdrawalLimit
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesWithdrawalLimit, params), nil, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesWithdrawalList gets withdrawal list
|
||||
func (ku *Kucoin) GetFuturesWithdrawalList(ctx context.Context, currency, status string, startAt, endAt time.Time) (*FuturesWithdrawalsListResponse, error) {
|
||||
params := url.Values{}
|
||||
if currency != "" {
|
||||
params.Set("currency", currency)
|
||||
}
|
||||
if status != "" {
|
||||
params.Set("status", status)
|
||||
}
|
||||
if !startAt.IsZero() {
|
||||
params.Set("startAt", strconv.FormatInt(startAt.UnixMilli(), 10))
|
||||
}
|
||||
if !endAt.IsZero() {
|
||||
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
|
||||
}
|
||||
var resp *FuturesWithdrawalsListResponse
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesWithdrawalList, params), nil, &resp)
|
||||
}
|
||||
|
||||
// CancelFuturesWithdrawal is used to cancel withdrawal request of only PROCESSING status
|
||||
func (ku *Kucoin) CancelFuturesWithdrawal(ctx context.Context, withdrawalID string) (bool, error) {
|
||||
var resp bool
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodDelete, kucoinFuturesCancelWithdrawal+withdrawalID, nil, &resp)
|
||||
}
|
||||
|
||||
// TransferFuturesFundsToMainAccount helps in transferring funds from futures to main/trade account
|
||||
func (ku *Kucoin) TransferFuturesFundsToMainAccount(ctx context.Context, amount float64, currency, recAccountType string) (*TransferRes, error) {
|
||||
params := make(map[string]interface{})
|
||||
if amount <= 0 {
|
||||
return nil, errors.New("amount can't be zero or negative")
|
||||
}
|
||||
params["amount"] = amount
|
||||
if currency == "" {
|
||||
return nil, errors.New("currency can't be empty")
|
||||
}
|
||||
params["currency"] = currency
|
||||
if recAccountType == "" {
|
||||
return nil, errors.New("recAccountType can't be empty")
|
||||
}
|
||||
params["recAccountType"] = recAccountType
|
||||
var resp *TransferRes
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodPost, kucoinFuturesTransferFundtoMainAccount, params, &resp)
|
||||
}
|
||||
|
||||
// TransferFundsToFuturesAccount helps in transferring funds from payee account to futures account
|
||||
func (ku *Kucoin) TransferFundsToFuturesAccount(ctx context.Context, amount float64, currency, payAccountType string) error {
|
||||
params := make(map[string]interface{})
|
||||
if amount <= 0 {
|
||||
return errors.New("amount can't be zero or negative")
|
||||
}
|
||||
params["amount"] = amount
|
||||
if currency == "" {
|
||||
return errors.New("currency can't be empty")
|
||||
}
|
||||
params["currency"] = currency
|
||||
if payAccountType == "" {
|
||||
return errors.New("payAccountType can't be empty")
|
||||
}
|
||||
params["payAccountType"] = payAccountType
|
||||
resp := struct {
|
||||
Error
|
||||
}{}
|
||||
return ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodPost, kucoinFuturesTransferFundtoFuturesAccount, params, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesTransferOutList gets list of transfer out
|
||||
func (ku *Kucoin) GetFuturesTransferOutList(ctx context.Context, currency, status string, startAt, endAt time.Time) (*TransferListsResponse, error) {
|
||||
if currency == "" {
|
||||
return nil, errors.New("currency can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("currency", currency)
|
||||
if status != "" {
|
||||
params.Set("status", status)
|
||||
}
|
||||
if !startAt.IsZero() {
|
||||
params.Set("startAt", strconv.FormatInt(startAt.UnixMilli(), 10))
|
||||
}
|
||||
if !endAt.IsZero() {
|
||||
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
|
||||
}
|
||||
var resp *TransferListsResponse
|
||||
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesTransferOutList, params), nil, &resp)
|
||||
}
|
||||
|
||||
// CancelFuturesTransferOut is used to cancel transfer out request of only PROCESSING status
|
||||
func (ku *Kucoin) CancelFuturesTransferOut(ctx context.Context, applyID string) error {
|
||||
if applyID == "" {
|
||||
return errors.New("applyID can't be empty")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("applyId", applyID)
|
||||
resp := struct {
|
||||
Error
|
||||
}{}
|
||||
return ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodDelete, common.EncodeURLValues(kucoinFuturesCancelTransferOut, params), nil, &resp)
|
||||
}
|
||||
|
||||
func processFuturesOB(ob [][2]float64) []orderbook.Item {
|
||||
o := make([]orderbook.Item, len(ob))
|
||||
for x := range ob {
|
||||
o[x] = orderbook.Item{
|
||||
Price: ob[x][0],
|
||||
Amount: ob[x][1],
|
||||
}
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func constructFuturesOrderbook(o *futuresOrderbookResponse) (*Orderbook, error) {
|
||||
var (
|
||||
s Orderbook
|
||||
err error
|
||||
)
|
||||
s.Bids = processFuturesOB(o.Bids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Asks = processFuturesOB(o.Asks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Sequence = o.Sequence
|
||||
s.Time = o.Time.Time()
|
||||
return &s, err
|
||||
}
|
||||
451
exchanges/kucoin/kucoin_futures_types.go
Normal file
451
exchanges/kucoin/kucoin_futures_types.go
Normal file
@@ -0,0 +1,451 @@
|
||||
package kucoin
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
||||
)
|
||||
|
||||
var (
|
||||
validGranularity = []string{
|
||||
"1", "5", "15", "30", "60", "120", "240", "480", "720", "1440", "10080",
|
||||
}
|
||||
)
|
||||
|
||||
// Contract store contract details
|
||||
type Contract struct {
|
||||
Symbol string `json:"symbol"`
|
||||
RootSymbol string `json:"rootSymbol"`
|
||||
ContractType string `json:"type"`
|
||||
FirstOpenDate convert.ExchangeTime `json:"firstOpenDate"`
|
||||
ExpireDate convert.ExchangeTime `json:"expireDate"`
|
||||
SettleDate convert.ExchangeTime `json:"settleDate"`
|
||||
BaseCurrency string `json:"baseCurrency"`
|
||||
QuoteCurrency string `json:"quoteCurrency"`
|
||||
SettleCurrency string `json:"settleCurrency"`
|
||||
MaxOrderQty float64 `json:"maxOrderQty"`
|
||||
MaxPrice float64 `json:"maxPrice"`
|
||||
LotSize float64 `json:"lotSize"`
|
||||
TickSize float64 `json:"tickSize"`
|
||||
IndexPriceTickSize float64 `json:"indexPriceTickSize"`
|
||||
Multiplier float64 `json:"multiplier"`
|
||||
InitialMargin float64 `json:"initialMargin"`
|
||||
MaintainMargin float64 `json:"maintainMargin"`
|
||||
MaxRiskLimit float64 `json:"maxRiskLimit"`
|
||||
MinRiskLimit float64 `json:"minRiskLimit"`
|
||||
RiskStep float64 `json:"riskStep"`
|
||||
MakerFeeRate float64 `json:"makerFeeRate"`
|
||||
TakerFeeRate float64 `json:"takerFeeRate"`
|
||||
TakerFixFee float64 `json:"takerFixFee"`
|
||||
MakerFixFee float64 `json:"makerFixFee"`
|
||||
SettlementFee float64 `json:"settlementFee"`
|
||||
IsDeleverage bool `json:"isDeleverage"`
|
||||
IsQuanto bool `json:"isQuanto"`
|
||||
IsInverse bool `json:"isInverse"`
|
||||
MarkMethod string `json:"markMethod"`
|
||||
FairMethod string `json:"fairMethod"`
|
||||
FundingBaseSymbol string `json:"fundingBaseSymbol"`
|
||||
FundingQuoteSymbol string `json:"fundingQuoteSymbol"`
|
||||
FundingRateSymbol string `json:"fundingRateSymbol"`
|
||||
IndexSymbol string `json:"indexSymbol"`
|
||||
SettlementSymbol string `json:"settlementSymbol"`
|
||||
Status string `json:"status"`
|
||||
FundingFeeRate float64 `json:"fundingFeeRate"`
|
||||
PredictedFundingFeeRate float64 `json:"predictedFundingFeeRate"`
|
||||
OpenInterest string `json:"openInterest"`
|
||||
TurnoverOf24h float64 `json:"turnoverOf24h"`
|
||||
VolumeOf24h float64 `json:"volumeOf24h"`
|
||||
MarkPrice float64 `json:"markPrice"`
|
||||
IndexPrice float64 `json:"indexPrice"`
|
||||
LastTradePrice float64 `json:"lastTradePrice"`
|
||||
NextFundingRateTime float64 `json:"nextFundingRateTime"`
|
||||
MaxLeverage float64 `json:"maxLeverage"`
|
||||
SourceExchanges []string `json:"sourceExchanges"`
|
||||
PremiumsSymbol1M string `json:"premiumsSymbol1M"`
|
||||
PremiumsSymbol8H string `json:"premiumsSymbol8H"`
|
||||
FundingBaseSymbol1M string `json:"fundingBaseSymbol1M"`
|
||||
FundingQuoteSymbol1M string `json:"fundingQuoteSymbol1M"`
|
||||
LowPrice float64 `json:"lowPrice"`
|
||||
HighPrice float64 `json:"highPrice"`
|
||||
PriceChgPct float64 `json:"priceChgPct"`
|
||||
PriceChg float64 `json:"priceChg"`
|
||||
}
|
||||
|
||||
// FuturesTicker stores ticker data
|
||||
type FuturesTicker struct {
|
||||
Sequence int64 `json:"sequence"`
|
||||
Symbol string `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
Price float64 `json:"price"`
|
||||
BestBidSize float64 `json:"bestBidSize"`
|
||||
BestBidPrice float64 `json:"bestBidPrice"`
|
||||
BestAskSize float64 `json:"bestAskSize"`
|
||||
BestAskPrice float64 `json:"bestAskPrice"`
|
||||
TradeID string `json:"tradeId"`
|
||||
FilledTime convert.ExchangeTime `json:"time"`
|
||||
}
|
||||
|
||||
type futuresOrderbookResponse struct {
|
||||
Asks [][2]float64 `json:"asks"`
|
||||
Bids [][2]float64 `json:"bids"`
|
||||
Time convert.ExchangeTime `json:"ts"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Symbol string `json:"symbol"`
|
||||
}
|
||||
|
||||
// FuturesTrade stores trade data
|
||||
type FuturesTrade struct {
|
||||
Sequence int64 `json:"sequence"`
|
||||
TradeID string `json:"tradeId"`
|
||||
TakerOrderID string `json:"takerOrderId"`
|
||||
MakerOrderID string `json:"makerOrderId"`
|
||||
Price float64 `json:"price,string"`
|
||||
Size float64 `json:"size"`
|
||||
Side string `json:"side"`
|
||||
FilledTime convert.ExchangeTime `json:"ts"`
|
||||
}
|
||||
|
||||
// FuturesInterestRate stores interest rate data
|
||||
type FuturesInterestRate struct {
|
||||
Symbol string `json:"symbol"`
|
||||
TimePoint convert.ExchangeTime `json:"timePoint"`
|
||||
Value float64 `json:"value"`
|
||||
Granularity int64 `json:"granularity"`
|
||||
}
|
||||
|
||||
// Decomposition stores decomposition data
|
||||
type Decomposition struct {
|
||||
Exchange string `json:"exchange"`
|
||||
Price float64 `json:"price"`
|
||||
Weight float64 `json:"weight"`
|
||||
}
|
||||
|
||||
// FuturesIndex stores index data
|
||||
type FuturesIndex struct {
|
||||
FuturesInterestRate
|
||||
DecompositionList []Decomposition `json:"decompositionList"`
|
||||
}
|
||||
|
||||
// FuturesMarkPrice stores mark price data
|
||||
type FuturesMarkPrice struct {
|
||||
FuturesInterestRate
|
||||
IndexPrice float64 `json:"indexPrice"`
|
||||
}
|
||||
|
||||
// FuturesFundingRate stores funding rate data
|
||||
type FuturesFundingRate struct {
|
||||
FuturesInterestRate
|
||||
PredictedValue float64 `json:"predictedValue"`
|
||||
}
|
||||
|
||||
// FuturesKline stores kline data
|
||||
type FuturesKline struct {
|
||||
StartTime time.Time
|
||||
Open float64
|
||||
Close float64
|
||||
High float64
|
||||
Low float64
|
||||
Volume float64
|
||||
}
|
||||
|
||||
// FutureOrdersResponse represents a future order response list detail.
|
||||
type FutureOrdersResponse struct {
|
||||
CurrentPage int64 `json:"currentPage"`
|
||||
PageSize int64 `json:"pageSize"`
|
||||
TotalNum int64 `json:"totalNum"`
|
||||
TotalPage int64 `json:"totalPage"`
|
||||
Items []FuturesOrder `json:"items"`
|
||||
}
|
||||
|
||||
// FuturesOrder represents futures order information
|
||||
type FuturesOrder struct {
|
||||
ID string `json:"id"`
|
||||
Symbol string `json:"symbol"`
|
||||
OrderType string `json:"type"`
|
||||
Side string `json:"side"`
|
||||
Price float64 `json:"price,string"`
|
||||
Size float64 `json:"size"`
|
||||
Value float64 `json:"value,string"`
|
||||
DealValue float64 `json:"dealValue,string"`
|
||||
DealSize float64 `json:"dealSize"`
|
||||
Stp string `json:"stp"`
|
||||
Stop string `json:"stop"`
|
||||
StopPriceType string `json:"stopPriceType"`
|
||||
StopTriggered bool `json:"stopTriggered"`
|
||||
StopPrice float64 `json:"stopPrice,string"`
|
||||
TimeInForce string `json:"timeInForce"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
Hidden bool `json:"hidden"`
|
||||
Iceberg bool `json:"iceberg"`
|
||||
Leverage float64 `json:"leverage,string"`
|
||||
ForceHold bool `json:"forceHold"`
|
||||
CloseOrder bool `json:"closeOrder"`
|
||||
VisibleSize float64 `json:"visibleSize"`
|
||||
ClientOid string `json:"clientOid"`
|
||||
Remark string `json:"remark"`
|
||||
Tags string `json:"tags"`
|
||||
IsActive bool `json:"isActive"`
|
||||
CancelExist bool `json:"cancelExist"`
|
||||
CreatedAt convert.ExchangeTime `json:"createdAt"`
|
||||
UpdatedAt convert.ExchangeTime `json:"updatedAt"`
|
||||
EndAt convert.ExchangeTime `json:"endAt"`
|
||||
OrderTime convert.ExchangeTime `json:"orderTime"`
|
||||
SettleCurrency string `json:"settleCurrency"`
|
||||
Status string `json:"status"`
|
||||
FilledValue float64 `json:"filledValue,string"`
|
||||
FilledSize float64 `json:"filledSize"`
|
||||
ReduceOnly bool `json:"reduceOnly"`
|
||||
}
|
||||
|
||||
// FutureFillsResponse represents a future fills list response detail.
|
||||
type FutureFillsResponse struct {
|
||||
CurrentPage int64 `json:"currentPage"`
|
||||
PageSize int64 `json:"pageSize"`
|
||||
TotalNum int64 `json:"totalNum"`
|
||||
TotalPage int64 `json:"totalPage"`
|
||||
Items []FuturesFill `json:"items"`
|
||||
}
|
||||
|
||||
// FuturesFill represents list of recent fills for futures orders.
|
||||
type FuturesFill struct {
|
||||
Symbol string `json:"symbol"`
|
||||
TradeID string `json:"tradeId"`
|
||||
OrderID string `json:"orderId"`
|
||||
Side string `json:"side"`
|
||||
Liquidity string `json:"liquidity"`
|
||||
ForceTaker bool `json:"forceTaker"`
|
||||
Price float64 `json:"price,string"`
|
||||
Size float64 `json:"size,string"`
|
||||
Value float64 `json:"value,string"`
|
||||
FeeRate float64 `json:"feeRate,string"`
|
||||
FixFee float64 `json:"fixFee,string"`
|
||||
FeeCurrency string `json:"feeCurrency"`
|
||||
Stop string `json:"stop"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
OrderType string `json:"orderType"`
|
||||
TradeType string `json:"tradeType"`
|
||||
CreatedAt convert.ExchangeTime `json:"createdAt"`
|
||||
SettleCurrency string `json:"settleCurrency"`
|
||||
TradeTime convert.ExchangeTime `json:"tradeTime"`
|
||||
}
|
||||
|
||||
// FuturesOpenOrderStats represents futures open order summary stats information.
|
||||
type FuturesOpenOrderStats struct {
|
||||
OpenOrderBuySize int64 `json:"openOrderBuySize"`
|
||||
OpenOrderSellSize int64 `json:"openOrderSellSize"`
|
||||
OpenOrderBuyCost float64 `json:"openOrderBuyCost,string"`
|
||||
OpenOrderSellCost float64 `json:"openOrderSellCost,string"`
|
||||
SettleCurrency string `json:"settleCurrency"`
|
||||
}
|
||||
|
||||
// FuturesPosition represents futures position detailed information.
|
||||
type FuturesPosition struct {
|
||||
ID string `json:"id"`
|
||||
Symbol string `json:"symbol"`
|
||||
AutoDeposit bool `json:"autoDeposit"`
|
||||
MaintMarginReq float64 `json:"maintMarginReq"`
|
||||
RiskLimit int64 `json:"riskLimit"`
|
||||
RealLeverage float64 `json:"realLeverage"`
|
||||
CrossMode bool `json:"crossMode"`
|
||||
ADLRankingPercentile float64 `json:"delevPercentage"`
|
||||
OpeningTimestamp convert.ExchangeTime `json:"openingTimestamp"`
|
||||
CurrentTimestamp convert.ExchangeTime `json:"currentTimestamp"`
|
||||
CurrentQty int64 `json:"currentQty"`
|
||||
CurrentCost float64 `json:"currentCost"` // Current position value
|
||||
CurrentComm float64 `json:"currentComm"` // Current commission
|
||||
UnrealisedCost float64 `json:"unrealisedCost"`
|
||||
RealisedGrossCost float64 `json:"realisedGrossCost"`
|
||||
RealisedCost float64 `json:"realisedCost"`
|
||||
IsOpen bool `json:"isOpen"`
|
||||
MarkPrice float64 `json:"markPrice"`
|
||||
MarkValue float64 `json:"markValue"`
|
||||
PosCost float64 `json:"posCost"` // Position value
|
||||
PosCross float64 `json:"posCross"` // Added margin
|
||||
PosInit float64 `json:"posInit"` // Leverage margin
|
||||
PosComm float64 `json:"posComm"` // Bankruptcy cost
|
||||
PosLoss float64 `json:"posLoss"` // Funding fees paid out
|
||||
PosMargin float64 `json:"posMargin"` // Position margin
|
||||
PosMaint float64 `json:"posMaint"` // Maintenance margin
|
||||
MaintMargin float64 `json:"maintMargin"`
|
||||
RealisedGrossPnl float64 `json:"realisedGrossPnl"`
|
||||
RealisedPnl float64 `json:"realisedPnl"`
|
||||
UnrealisedPnl float64 `json:"unrealisedPnl"`
|
||||
UnrealisedPnlPcnt float64 `json:"unrealisedPnlPcnt"`
|
||||
UnrealisedRoePcnt float64 `json:"unrealisedRoePcnt"`
|
||||
AvgEntryPrice float64 `json:"avgEntryPrice"`
|
||||
LiquidationPrice float64 `json:"liquidationPrice"`
|
||||
BankruptPrice float64 `json:"bankruptPrice"`
|
||||
SettleCurrency string `json:"settleCurrency"`
|
||||
MaintainMargin float64 `json:"maintainMargin"`
|
||||
RiskLimitLevel int64 `json:"riskLimitLevel"`
|
||||
}
|
||||
|
||||
// FuturesRiskLimitLevel represents futures risk limit level information.
|
||||
type FuturesRiskLimitLevel struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Level int64 `json:"level"`
|
||||
MaxRiskLimit float64 `json:"maxRiskLimit"`
|
||||
MinRiskLimit float64 `json:"minRiskLimit"`
|
||||
MaxLeverage float64 `json:"maxLeverage"`
|
||||
InitialMargin float64 `json:"initialMargin"`
|
||||
MaintainMargin float64 `json:"maintainMargin"`
|
||||
}
|
||||
|
||||
// FuturesFundingHistory represents futures funding information.
|
||||
type FuturesFundingHistory struct {
|
||||
ID string `json:"id"`
|
||||
Symbol string `json:"symbol"`
|
||||
Time convert.ExchangeTime `json:"timePoint"`
|
||||
FundingRate float64 `json:"fundingRate"`
|
||||
MarkPrice float64 `json:"markPrice"`
|
||||
PositionQty float64 `json:"positionQty"`
|
||||
PositionCost float64 `json:"positionCost"`
|
||||
Funding float64 `json:"funding"`
|
||||
SettleCurrency string `json:"settleCurrency"`
|
||||
}
|
||||
|
||||
// FuturesAccount holds futures account detail information.
|
||||
type FuturesAccount struct {
|
||||
AccountEquity float64 `json:"accountEquity"` // marginBalance + Unrealised PNL
|
||||
UnrealisedPNL float64 `json:"unrealisedPNL"` // unrealised profit and loss
|
||||
MarginBalance float64 `json:"marginBalance"` // positionMargin + orderMargin + frozenFunds + availableBalance - unrealisedPNL
|
||||
PositionMargin float64 `json:"positionMargin"`
|
||||
OrderMargin float64 `json:"orderMargin"`
|
||||
FrozenFunds float64 `json:"frozenFunds"` // frozen funds for withdrawal and out-transfer
|
||||
AvailableBalance float64 `json:"availableBalance"`
|
||||
Currency string `json:"currency"`
|
||||
}
|
||||
|
||||
// FuturesTransactionHistory represents a transaction history
|
||||
type FuturesTransactionHistory struct {
|
||||
Time convert.ExchangeTime `json:"time"`
|
||||
Type string `json:"type"`
|
||||
Amount float64 `json:"amount"`
|
||||
Fee float64 `json:"fee"`
|
||||
AccountEquity float64 `json:"accountEquity"`
|
||||
Status string `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
Offset int64 `json:"offset"`
|
||||
Currency string `json:"currency"`
|
||||
}
|
||||
|
||||
// APIKeyDetail represents the API key detail
|
||||
type APIKeyDetail struct {
|
||||
SubName string `json:"subName"`
|
||||
Remark string `json:"remark"`
|
||||
APIKey string `json:"apiKey"`
|
||||
APISecret string `json:"apiSecret"`
|
||||
Passphrase string `json:"passphrase"`
|
||||
Permission string `json:"permission"`
|
||||
IPWhitelist string `json:"ipWhitelist"`
|
||||
CreateAt convert.ExchangeTime `json:"createdAt"`
|
||||
}
|
||||
|
||||
// FuturesDepositDetailsResponse represents a futures deposits list detail response.
|
||||
type FuturesDepositDetailsResponse struct {
|
||||
CurrentPage int64 `json:"currentPage"`
|
||||
PageSize int64 `json:"pageSize"`
|
||||
TotalNum int64 `json:"totalNum"`
|
||||
TotalPage int64 `json:"totalPage"`
|
||||
Items []FuturesDepositDetail `json:"items"`
|
||||
}
|
||||
|
||||
// FuturesDepositDetail represents futures deposit detail information.
|
||||
type FuturesDepositDetail struct {
|
||||
Currency string `json:"currency"`
|
||||
Status string `json:"status"`
|
||||
Address string `json:"address"`
|
||||
IsInner bool `json:"isInner"`
|
||||
Amount float64 `json:"amount"`
|
||||
Fee float64 `json:"fee"`
|
||||
WalletTxID string `json:"walletTxId"`
|
||||
CreatedAt convert.ExchangeTime `json:"createdAt"`
|
||||
}
|
||||
|
||||
// FuturesWithdrawalLimit represents withdrawal limit information.
|
||||
type FuturesWithdrawalLimit struct {
|
||||
Currency string `json:"currency"`
|
||||
ChainID string `json:"chainId"`
|
||||
LimitAmount float64 `json:"limitAmount"`
|
||||
UsedAmount float64 `json:"usedAmount"`
|
||||
RemainAmount float64 `json:"remainAmount"`
|
||||
AvailableAmount float64 `json:"availableAmount"`
|
||||
WithdrawMinFee float64 `json:"withdrawMinFee"`
|
||||
InnerWithdrawMinFee float64 `json:"innerWithdrawMinFee"`
|
||||
WithdrawMinSize float64 `json:"withdrawMinSize"`
|
||||
IsWithdrawEnabled bool `json:"isWithdrawEnabled"`
|
||||
Precision float64 `json:"precision"`
|
||||
}
|
||||
|
||||
// FuturesWithdrawalsListResponse represents a list of futures Withdrawal history instance.
|
||||
type FuturesWithdrawalsListResponse struct {
|
||||
CurrentPage int64 `json:"currentPage"`
|
||||
PageSize int64 `json:"pageSize"`
|
||||
TotalNum int64 `json:"totalNum"`
|
||||
TotalPage int64 `json:"totalPage"`
|
||||
Items []FuturesWithdrawalHistory `json:"items"`
|
||||
}
|
||||
|
||||
// FuturesWithdrawalHistory represents a list of Futures withdrawal history.
|
||||
type FuturesWithdrawalHistory struct {
|
||||
WithdrawalID string `json:"withdrawalId"`
|
||||
Currency string `json:"currency"`
|
||||
Status string `json:"status"`
|
||||
Address string `json:"address"`
|
||||
Memo string `json:"memo"`
|
||||
IsInner bool `json:"isInner"`
|
||||
Amount float64 `json:"amount"`
|
||||
Fee float64 `json:"fee"`
|
||||
WalletTxID string `json:"walletTxId"`
|
||||
CreatedAt convert.ExchangeTime `json:"createdAt"`
|
||||
Remark string `json:"remark"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// TransferBase represents transfer base information.
|
||||
type TransferBase struct {
|
||||
ApplyID string `json:"applyId"`
|
||||
Currency string `json:"currency"`
|
||||
RecRemark string `json:"recRemark"`
|
||||
RecSystem string `json:"recSystem"`
|
||||
Status string `json:"status"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Reason string `json:"reason"`
|
||||
CreatedAt convert.ExchangeTime `json:"createdAt"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
// TransferRes represents a transfer response
|
||||
type TransferRes struct {
|
||||
TransferBase
|
||||
BizNo string `json:"bizNo"`
|
||||
PayAccountType string `json:"payAccountType"`
|
||||
PayTag string `json:"payTag"`
|
||||
RecAccountType string `json:"recAccountType"`
|
||||
RecTag string `json:"recTag"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
Serial int64 `json:"sn"`
|
||||
UpdatedAt convert.ExchangeTime `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// TransferListsResponse represents a transfer lists detail.
|
||||
type TransferListsResponse struct {
|
||||
CurrentPage int64 `json:"currentPage"`
|
||||
PageSize int64 `json:"pageSize"`
|
||||
TotalNum int64 `json:"totalNum"`
|
||||
TotalPage int64 `json:"totalPage"`
|
||||
Items []Transfer `json:"items"`
|
||||
}
|
||||
|
||||
// Transfer represents a transfer detail.
|
||||
type Transfer struct {
|
||||
TransferBase
|
||||
Offset int64 `json:"offset"`
|
||||
}
|
||||
|
||||
// FuturesServiceStatus represents service status.
|
||||
type FuturesServiceStatus struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"msg"`
|
||||
}
|
||||
244
exchanges/kucoin/kucoin_ratelimit.go
Normal file
244
exchanges/kucoin/kucoin_ratelimit.go
Normal file
@@ -0,0 +1,244 @@
|
||||
package kucoin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
const (
|
||||
threeSecondsInterval = time.Second * 3
|
||||
oneMinuteInterval = time.Minute
|
||||
)
|
||||
|
||||
// RateLimit implements the request.Limiter interface
|
||||
type RateLimit struct {
|
||||
RetrieveAccountLedger *rate.Limiter
|
||||
MasterSubUserTransfer *rate.Limiter
|
||||
RetrieveDepositList *rate.Limiter
|
||||
RetrieveV1HistoricalDepositList *rate.Limiter
|
||||
RetrieveWithdrawalList *rate.Limiter
|
||||
RetrieveV1HistoricalWithdrawalList *rate.Limiter
|
||||
PlaceOrder *rate.Limiter
|
||||
PlaceMarginOrders *rate.Limiter
|
||||
PlaceBulkOrders *rate.Limiter
|
||||
CancelOrder *rate.Limiter
|
||||
CancelAllOrders *rate.Limiter
|
||||
ListOrders *rate.Limiter
|
||||
ListFills *rate.Limiter
|
||||
RetrieveFullOrderbook *rate.Limiter
|
||||
RetrieveMarginAccount *rate.Limiter
|
||||
SpotRate *rate.Limiter
|
||||
FuturesRate *rate.Limiter
|
||||
|
||||
FRetrieveAccountOverviewRate *rate.Limiter
|
||||
FRetrieveTransactionHistoryRate *rate.Limiter
|
||||
FPlaceOrderRate *rate.Limiter
|
||||
FCancelAnOrderRate *rate.Limiter
|
||||
FLimitOrderMassCancelationRate *rate.Limiter
|
||||
FRetrieveOrderListRate *rate.Limiter
|
||||
FRetrieveFillsRate *rate.Limiter
|
||||
FRecentFillsRate *rate.Limiter
|
||||
FRetrievePositionListRate *rate.Limiter
|
||||
FRetrieveFundingHistoryRate *rate.Limiter
|
||||
FRetrieveFullOrderbookLevel2Rate *rate.Limiter
|
||||
}
|
||||
|
||||
// rate of request per interval
|
||||
const (
|
||||
retrieveAccountLedgerRate = 18
|
||||
masterSubUserTransferRate = 3
|
||||
retrieveDepositListRate = 6
|
||||
retrieveV1HistoricalDepositListRate = 6
|
||||
retrieveWithdrawalListRate = 6
|
||||
retrieveV1HistoricalWithdrawalListRate = 6
|
||||
placeOrderRate = 45
|
||||
placeMarginOrdersRate = 45
|
||||
placeBulkOrdersRate = 3
|
||||
cancelOrderRate = 60
|
||||
cancelAllOrdersRate = 3
|
||||
listOrdersRate = 30
|
||||
listFillsRate = 9
|
||||
retrieveFullOrderbookRate = 30
|
||||
retrieveMarginAccountRate = 1
|
||||
|
||||
futuresRetrieveAccountOverviewRate = 30
|
||||
futuresRetrieveTransactionHistoryRate = 9
|
||||
futuresPlaceOrderRate = 30
|
||||
futuresCancelAnOrderRate = 40
|
||||
futuresLimitOrderMassCancelationRate = 9
|
||||
futuresRetrieveOrderListRate = 30
|
||||
futuresRetrieveFillsRate = 9
|
||||
futuresRecentFillsRate = 9
|
||||
futuresRetrievePositionListRate = 9
|
||||
futuresRetrieveFundingHistoryRate = 9
|
||||
futuresRetrieveFullOrderbookLevel2Rate = 30
|
||||
|
||||
defaultSpotRate = 1200
|
||||
defaultFuturesRate = 1200
|
||||
)
|
||||
|
||||
const (
|
||||
// for spot endpoints
|
||||
retrieveAccountLedgerEPL request.EndpointLimit = iota
|
||||
masterSubUserTransferEPL
|
||||
retrieveDepositListEPL
|
||||
retrieveV1HistoricalDepositListEPL
|
||||
retrieveWithdrawalListEPL
|
||||
retrieveV1HistoricalWithdrawalListEPL
|
||||
placeOrderEPL
|
||||
placeMarginOrdersEPL
|
||||
placeBulkOrdersEPL
|
||||
cancelOrderEPL
|
||||
cancelAllOrdersEPL
|
||||
listOrdersEPL
|
||||
listFillsEPL
|
||||
retrieveFullOrderbookEPL
|
||||
retrieveMarginAccountEPL
|
||||
defaultSpotEPL
|
||||
|
||||
// for futures endpoints
|
||||
futuresRetrieveAccountOverviewEPL
|
||||
futuresRetrieveTransactionHistoryEPL
|
||||
futuresPlaceOrderEPL
|
||||
futuresCancelAnOrderEPL
|
||||
futuresLimitOrderMassCancelationEPL
|
||||
futuresRetrieveOrderListEPL
|
||||
futuresRetrieveFillsEPL
|
||||
futuresRecentFillsEPL
|
||||
futuresRetrievePositionListEPL
|
||||
futuresRetrieveFundingHistoryEPL
|
||||
futuresRetrieveFullOrderbookLevel2EPL
|
||||
defaultFuturesEPL
|
||||
)
|
||||
|
||||
// Limit executes rate limiting functionality for Kucoin
|
||||
func (r *RateLimit) Limit(ctx context.Context, epl request.EndpointLimit) error {
|
||||
var limiter *rate.Limiter
|
||||
var tokens int
|
||||
switch epl {
|
||||
case retrieveAccountLedgerEPL:
|
||||
return r.RetrieveAccountLedger.Wait(ctx)
|
||||
case masterSubUserTransferEPL:
|
||||
return r.MasterSubUserTransfer.Wait(ctx)
|
||||
case retrieveDepositListEPL:
|
||||
return r.RetrieveDepositList.Wait(ctx)
|
||||
case retrieveV1HistoricalDepositListEPL:
|
||||
return r.RetrieveV1HistoricalDepositList.Wait(ctx)
|
||||
case retrieveWithdrawalListEPL:
|
||||
return r.RetrieveWithdrawalList.Wait(ctx)
|
||||
case retrieveV1HistoricalWithdrawalListEPL:
|
||||
return r.RetrieveV1HistoricalWithdrawalList.Wait(ctx)
|
||||
case placeOrderEPL:
|
||||
return r.PlaceOrder.Wait(ctx)
|
||||
case placeMarginOrdersEPL:
|
||||
return r.PlaceMarginOrders.Wait(ctx)
|
||||
case placeBulkOrdersEPL:
|
||||
return r.PlaceBulkOrders.Wait(ctx)
|
||||
case cancelOrderEPL:
|
||||
return r.CancelOrder.Wait(ctx)
|
||||
case cancelAllOrdersEPL:
|
||||
return r.CancelAllOrders.Wait(ctx)
|
||||
case listOrdersEPL:
|
||||
return r.ListOrders.Wait(ctx)
|
||||
case listFillsEPL:
|
||||
return r.ListFills.Wait(ctx)
|
||||
case retrieveFullOrderbookEPL:
|
||||
return r.RetrieveFullOrderbook.Wait(ctx)
|
||||
case retrieveMarginAccountEPL:
|
||||
return r.RetrieveMarginAccount.Wait(ctx)
|
||||
case futuresRetrieveAccountOverviewEPL:
|
||||
return r.FRetrieveAccountOverviewRate.Wait(ctx)
|
||||
case futuresRetrieveTransactionHistoryEPL:
|
||||
return r.FRetrieveTransactionHistoryRate.Wait(ctx)
|
||||
case futuresPlaceOrderEPL:
|
||||
return r.FPlaceOrderRate.Wait(ctx)
|
||||
case futuresCancelAnOrderEPL:
|
||||
return r.FCancelAnOrderRate.Wait(ctx)
|
||||
case futuresLimitOrderMassCancelationEPL:
|
||||
return r.FLimitOrderMassCancelationRate.Wait(ctx)
|
||||
case futuresRetrieveOrderListEPL:
|
||||
return r.FRetrieveOrderListRate.Wait(ctx)
|
||||
case futuresRetrieveFillsEPL:
|
||||
return r.FRetrieveFillsRate.Wait(ctx)
|
||||
case futuresRecentFillsEPL:
|
||||
return r.FRecentFillsRate.Wait(ctx)
|
||||
case futuresRetrievePositionListEPL:
|
||||
return r.FRetrievePositionListRate.Wait(ctx)
|
||||
case futuresRetrieveFundingHistoryEPL:
|
||||
return r.FRetrieveFundingHistoryRate.Wait(ctx)
|
||||
case futuresRetrieveFullOrderbookLevel2EPL:
|
||||
return r.FRetrieveFullOrderbookLevel2Rate.Wait(ctx)
|
||||
case defaultSpotEPL:
|
||||
limiter, tokens = r.SpotRate, 1
|
||||
case defaultFuturesEPL:
|
||||
limiter, tokens = r.FuturesRate, 1
|
||||
default:
|
||||
return errors.New("endpoint rate limit functionality not found")
|
||||
}
|
||||
var finalDelay time.Duration
|
||||
var reserves = make([]*rate.Reservation, tokens)
|
||||
for i := 0; i < tokens; i++ {
|
||||
// Consume tokens 1 at a time as this avoids needing burst capacity in the limiter,
|
||||
// which would otherwise allow the rate limit to be exceeded over short periods
|
||||
reserves[i] = limiter.Reserve()
|
||||
finalDelay = reserves[i].Delay()
|
||||
}
|
||||
|
||||
if dl, ok := ctx.Deadline(); ok && dl.Before(time.Now().Add(finalDelay)) {
|
||||
// Cancel all potential reservations to free up rate limiter if deadline
|
||||
// is exceeded.
|
||||
for x := range reserves {
|
||||
reserves[x].Cancel()
|
||||
}
|
||||
return fmt.Errorf("rate limit delay of %s will exceed deadline: %w",
|
||||
finalDelay,
|
||||
context.DeadlineExceeded)
|
||||
}
|
||||
|
||||
time.Sleep(finalDelay)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetRateLimit returns a RateLimit instance, which implements the request.Limiter interface.
|
||||
func SetRateLimit() *RateLimit {
|
||||
return &RateLimit{
|
||||
// spot specific rate limiters
|
||||
RetrieveAccountLedger: request.NewRateLimit(threeSecondsInterval, retrieveAccountLedgerRate),
|
||||
MasterSubUserTransfer: request.NewRateLimit(threeSecondsInterval, masterSubUserTransferRate),
|
||||
RetrieveDepositList: request.NewRateLimit(threeSecondsInterval, retrieveDepositListRate),
|
||||
RetrieveV1HistoricalDepositList: request.NewRateLimit(threeSecondsInterval, retrieveV1HistoricalDepositListRate),
|
||||
RetrieveWithdrawalList: request.NewRateLimit(threeSecondsInterval, retrieveWithdrawalListRate),
|
||||
RetrieveV1HistoricalWithdrawalList: request.NewRateLimit(threeSecondsInterval, retrieveV1HistoricalWithdrawalListRate),
|
||||
PlaceOrder: request.NewRateLimit(threeSecondsInterval, placeOrderRate),
|
||||
PlaceMarginOrders: request.NewRateLimit(threeSecondsInterval, placeMarginOrdersRate),
|
||||
PlaceBulkOrders: request.NewRateLimit(threeSecondsInterval, placeBulkOrdersRate),
|
||||
CancelOrder: request.NewRateLimit(threeSecondsInterval, cancelOrderRate),
|
||||
CancelAllOrders: request.NewRateLimit(threeSecondsInterval, cancelAllOrdersRate),
|
||||
ListOrders: request.NewRateLimit(threeSecondsInterval, listOrdersRate),
|
||||
ListFills: request.NewRateLimit(threeSecondsInterval, listFillsRate),
|
||||
RetrieveFullOrderbook: request.NewRateLimit(threeSecondsInterval, retrieveFullOrderbookRate),
|
||||
RetrieveMarginAccount: request.NewRateLimit(threeSecondsInterval, retrieveMarginAccountRate),
|
||||
|
||||
// default spot and futures rates
|
||||
SpotRate: request.NewRateLimit(oneMinuteInterval, defaultSpotRate),
|
||||
FuturesRate: request.NewRateLimit(oneMinuteInterval, defaultFuturesRate),
|
||||
|
||||
// futures specific rate limiters
|
||||
FRetrieveAccountOverviewRate: request.NewRateLimit(threeSecondsInterval, futuresRetrieveAccountOverviewRate),
|
||||
FRetrieveTransactionHistoryRate: request.NewRateLimit(threeSecondsInterval, futuresRetrieveTransactionHistoryRate),
|
||||
FPlaceOrderRate: request.NewRateLimit(threeSecondsInterval, futuresPlaceOrderRate),
|
||||
FCancelAnOrderRate: request.NewRateLimit(threeSecondsInterval, futuresCancelAnOrderRate),
|
||||
FLimitOrderMassCancelationRate: request.NewRateLimit(threeSecondsInterval, futuresLimitOrderMassCancelationRate),
|
||||
FRetrieveOrderListRate: request.NewRateLimit(threeSecondsInterval, futuresRetrieveOrderListRate),
|
||||
FRetrieveFillsRate: request.NewRateLimit(threeSecondsInterval, futuresRetrieveFillsRate),
|
||||
FRecentFillsRate: request.NewRateLimit(threeSecondsInterval, futuresRecentFillsRate),
|
||||
FRetrievePositionListRate: request.NewRateLimit(threeSecondsInterval, futuresRetrievePositionListRate),
|
||||
FRetrieveFundingHistoryRate: request.NewRateLimit(threeSecondsInterval, futuresRetrieveFundingHistoryRate),
|
||||
FRetrieveFullOrderbookLevel2Rate: request.NewRateLimit(threeSecondsInterval, futuresRetrieveFullOrderbookLevel2Rate),
|
||||
}
|
||||
}
|
||||
2421
exchanges/kucoin/kucoin_test.go
Normal file
2421
exchanges/kucoin/kucoin_test.go
Normal file
File diff suppressed because it is too large
Load Diff
1498
exchanges/kucoin/kucoin_types.go
Normal file
1498
exchanges/kucoin/kucoin_types.go
Normal file
File diff suppressed because it is too large
Load Diff
1912
exchanges/kucoin/kucoin_websocket.go
Normal file
1912
exchanges/kucoin/kucoin_websocket.go
Normal file
File diff suppressed because it is too large
Load Diff
1502
exchanges/kucoin/kucoin_wrapper.go
Normal file
1502
exchanges/kucoin/kucoin_wrapper.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -67,6 +67,11 @@ type Submit struct {
|
||||
TriggerPrice float64
|
||||
ClientID string // TODO: Shift to credentials
|
||||
ClientOrderID string
|
||||
|
||||
// The system will first borrow you funds at the optimal interest rate and then place an order for you.
|
||||
// see kucoin_wrapper.go
|
||||
AutoBorrow bool
|
||||
|
||||
// MarginType such as isolated or cross margin for when an exchange
|
||||
// supports margin type definition when submitting an order eg okx
|
||||
MarginType margin.Type
|
||||
@@ -77,6 +82,9 @@ type Submit struct {
|
||||
// RetrieveFeeDelay some exchanges take time to properly save order data
|
||||
// and cannot retrieve fees data immediately
|
||||
RetrieveFeeDelay time.Duration
|
||||
|
||||
// Hidden when enabled orders not displaying in order book.
|
||||
Hidden bool
|
||||
// TradeMode specifies the trading mode for margin and non-margin orders: see okcoin_wrapper.go
|
||||
TradeMode string
|
||||
}
|
||||
@@ -109,6 +117,9 @@ type SubmitResponse struct {
|
||||
Fee float64
|
||||
FeeAsset currency.Code
|
||||
Cost float64
|
||||
|
||||
BorrowSize float64
|
||||
LoanApplyID string
|
||||
MarginType margin.Type
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ var Exchanges = []string{
|
||||
"huobi",
|
||||
"itbit",
|
||||
"kraken",
|
||||
"kucoin",
|
||||
"lbank",
|
||||
"okcoin",
|
||||
"okx",
|
||||
|
||||
@@ -80,6 +80,7 @@ _b in this context is an `IBotExchange` implemented struct_
|
||||
| Huobi.Pro | Yes | Yes | No |
|
||||
| ItBit | Yes | NA | No |
|
||||
| Kraken | Yes | Yes | No |
|
||||
| Kucoin | Yes | No | Yes |
|
||||
| Lbank | Yes | No | Yes |
|
||||
| Okcoin | Yes | Yes | Yes |
|
||||
| Okx | Yes | Yes | Yes |
|
||||
|
||||
107
testdata/configtest.json
vendored
107
testdata/configtest.json
vendored
File diff suppressed because one or more lines are too long
1
testdata/exchangelist.csv
vendored
1
testdata/exchangelist.csv
vendored
@@ -17,6 +17,7 @@ hitbtc,
|
||||
huobi,
|
||||
itbit,
|
||||
kraken,
|
||||
kucoin,
|
||||
lbank,
|
||||
okcoin,
|
||||
okx,
|
||||
|
||||
|
Reference in New Issue
Block a user