Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

API documentation [Changelog](https://www.kucoin.com/docs-new/change-log)

Current synchronized API documentation version [20250313](https://www.kucoin.com/docs-new/change-log#20250313)

## 2025-03-21(1.2.0)
- Update the latest APIs, documentation, etc
- Remove range validation in Python
- Update API KEY verification version to 3
- Fix the bug related to resubscribing
- The Node.js SDK has been changed to the official unified version
- Fix issues with automated test execution.

## 2025-03-04(Python 1.1.1)
- Update __init__.py to enhance the import experience
- Reduce unnecessary log output
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ RUN --mount=type=cache,target=/root/.m2,sharing=locked mvn -U clean package -Dsk
# build tools
FROM openapitools/openapi-generator-cli:v7.7.0

RUN apt-get update && apt-get install python3 python3-pip python3.8-venv nodejs npm -y
RUN apt-get update && apt-get install python3 python3-pip python3.8-venv nodejs jq npm -y
RUN pip install yapf
ENV GOLANG_VERSION=1.22.2
RUN curl -OL https://golang.org/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz && \
Expand Down
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,11 @@ SUBDIRS := $(shell find ./sdk -mindepth 1 -maxdepth 1 -type d)
test: $(SUBDIRS)
$(SUBDIRS):
@echo "Running tests in $@"
@docker run --rm -v ./$@:/app -w /app $(IMAGE_NAME):$(IMAGE_TAG) \
@docker run --rm \
-v venv-test-cache:/app/.venv-test \
-v go-mod-cache:/go/pkg/mod \
-v go-build-cache:/root/.cache/go-build \
-v ./$@:/app -w /app $(IMAGE_NAME):$(IMAGE_TAG) \
bash run_test.sh
@echo "Completed tests in $@"

Expand All @@ -97,7 +101,7 @@ generate: setup-logs
$(call generate-postman)
$(call generate-code,golang,/pkg/generate)
$(call generate-code,python,/kucoin_universal_sdk/generate)
$(call generate-code,node,/src/generate,v0.1.1-alpha)
$(call generate-code,node,/src/generate)

.PHONY: gen-postman
gen-postman: preprocessor
Expand Down
19 changes: 9 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# KuCoin Universal SDK

![License Badge](https://img.shields.io/badge/license-MIT-green)
![Language](https://img.shields.io/badge/language-Python|Go-blue)
![Language](https://img.shields.io/badge/language-Python|Go|Node.js-blue)

The **KuCoin Universal SDK** is the official SDK provided by KuCoin, offering a unified and seamless interface for accessing KuCoin's trading platform. Built using code generation technology, it ensures consistency and rapid updates across multiple programming languages, simplifying integration with consistent APIs.

Expand Down Expand Up @@ -32,9 +32,9 @@ The **KuCoin Universal SDK** is the official SDK provided by KuCoin, offering a

## 🛠️ Installation

### Latest Version: `1.1.0`(Global API version)
### Latest Version: `1.2.0`(Global API version)

### Python Installation(`1.1.1`)
### Python Installation

```bash
pip install kucoin-universal-sdk
Expand All @@ -47,8 +47,7 @@ go get github.com/Kucoin/kucoin-universal-sdk/sdk/golang
go mod tidy
```

### Node.js Installation (`0.1.1-alpha`)
Note: This SDK is currently in the Alpha phase. We are actively iterating and improving its features, stability, and documentation. Feedback and contributions are highly encouraged to help us refine the SDK.
### Node.js Installation
```bash
npm install kucoin-universal-sdk
```
Expand All @@ -64,12 +63,12 @@ Here's a quick example to get you started with the SDK in **Python**.
import logging
import os

from kucoin_universal_sdk.api.client import DefaultClient
from kucoin_universal_sdk.generate.spot.market.model_get_part_order_book_req import GetPartOrderBookReqBuilder
from kucoin_universal_sdk.model.client_option import ClientOptionBuilder
from kucoin_universal_sdk.model.constants import GLOBAL_API_ENDPOINT, GLOBAL_FUTURES_API_ENDPOINT, \
from kucoin_universal_sdk.api import DefaultClient
from kucoin_universal_sdk.generate.spot.market import GetPartOrderBookReqBuilder
from kucoin_universal_sdk.model import ClientOptionBuilder
from kucoin_universal_sdk.model import GLOBAL_API_ENDPOINT, GLOBAL_FUTURES_API_ENDPOINT, \
GLOBAL_BROKER_API_ENDPOINT
from kucoin_universal_sdk.model.transport_option import TransportOptionBuilder
from kucoin_universal_sdk.model import TransportOptionBuilder


def example():
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.1.0
v1.2.0
Original file line number Diff line number Diff line change
Expand Up @@ -452,13 +452,13 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo

case API:
case TEST: {
allModels.forEach(m-> {
String importName = (String)m.get("importPath");
allModels.forEach(m -> {
String importName = (String) m.get("importPath");
String fileName = m.getModel().getClassFilename();
// from .model_get_part_order_book_req import GetPartOrderBookReqBuilder
exports.add(String.format("from .%s import %s", fileName, importName));
if (m.getModel().getVendorExtensions().containsKey("x-request-model")) {
exports.add(String.format("from .%s import %s", fileName, importName+"Builder"));
exports.add(String.format("from .%s import %s", fileName, importName + "Builder"));
}
});
exports.add(String.format("from .%s import %sAPI", toApiFilename(meta.getSubService()), formatService(meta.getSubService())));
Expand All @@ -477,8 +477,8 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
}
case WS:
case WS_TEST: {
allModels.forEach(m-> {
String importName = (String)m.get("importPath");
allModels.forEach(m -> {
String importName = (String) m.get("importPath");
String fileName = m.getModel().getClassFilename();
// from .model_get_part_order_book_req import GetPartOrderBookReqBuilder
exports.add(String.format("from .%s import %s", fileName, importName));
Expand Down Expand Up @@ -775,8 +775,8 @@ public Map<String, Object> postProcessSupportingFileData(Map<String, Object> obj
throw new RuntimeException("read csv fail", e);
}

services.forEach(s-> {
Map<String,String> specialKeywords = Map.of("copytrading", "CopyTrading", "viplending", "VIPLending");
services.forEach(s -> {
Map<String, String> specialKeywords = Map.of("copytrading", "CopyTrading", "viplending", "VIPLending");
String service = formatService(specialKeywords.getOrDefault(s, s));
serviceExports.add(String.format("from .%s_api import %sService", s, service));
});
Expand Down Expand Up @@ -1178,100 +1178,23 @@ private PythonType mapType(IJsonSchemaValidationProperties cp) {
}

private PythonType numberType(IJsonSchemaValidationProperties cp) {
if (cp.getHasValidation()) {
PythonType floatt = new PythonType("float");

// e.g. confloat(ge=10, le=100, strict=True)
if (cp.getMaximum() != null) {
if (cp.getExclusiveMaximum()) {
floatt.constrain("lt", cp.getMaximum(), false);
} else {
floatt.constrain("le", cp.getMaximum(), false);
}
}
if (cp.getMinimum() != null) {
if (cp.getExclusiveMinimum()) {
floatt.constrain("gt", cp.getMinimum(), false);
} else {
floatt.constrain("ge", cp.getMinimum(), false);
}
}
if (cp.getMultipleOf() != null) {
floatt.constrain("multiple_of", cp.getMultipleOf());
}

return floatt;
} else {
return new PythonType("float");
}
return new PythonType("float");
}

private PythonType intType(IJsonSchemaValidationProperties cp) {
if (cp.getHasValidation()) {
PythonType pt = new PythonType("int");
// e.g. conint(ge=10, le=100, strict=True)
pt.constrain("strict", true);
if (cp.getMaximum() != null) {
if (cp.getExclusiveMaximum()) {
pt.constrain("lt", cp.getMaximum(), false);
} else {
pt.constrain("le", cp.getMaximum(), false);
}
}
if (cp.getMinimum() != null) {
if (cp.getExclusiveMinimum()) {
pt.constrain("gt", cp.getMinimum(), false);
} else {
pt.constrain("ge", cp.getMinimum(), false);
}
}
if (cp.getMultipleOf() != null) {
pt.constrain("multiple_of", cp.getMultipleOf());
}
return pt;
} else {
return new PythonType("int");
}
return new PythonType("int");
}

private PythonType binaryType(IJsonSchemaValidationProperties cp) {
if (cp.getHasValidation()) {
PythonType bytest = new PythonType("bytes");
PythonType strt = new PythonType("str");

// e.g. conbytes(min_length=2, max_length=10)
bytest.constrain("strict", true);
strt.constrain("strict", true);
if (cp.getMaxLength() != null) {
bytest.constrain("max_length", cp.getMaxLength());
strt.constrain("max_length", cp.getMaxLength());
}
if (cp.getMinLength() != null) {
bytest.constrain("min_length", cp.getMinLength());
strt.constrain("min_length", cp.getMinLength());
}
if (cp.getPattern() != null) {
moduleImports.add("pydantic", "field_validator");
// use validator instead as regex doesn't support flags, e.g. IGNORECASE
//fieldCustomization.add(Locale.ROOT, String.format(Locale.ROOT, "regex=r'%s'", cp.getPattern()));
}

moduleImports.add("typing", "Union");
PythonType pt = new PythonType("Union");
pt.addTypeParam(bytest);
pt.addTypeParam(strt);
return pt;
} else {
// same as above which has validation
moduleImports.add("pydantic", "StrictBytes");
moduleImports.add("pydantic", "StrictStr");
moduleImports.add("typing", "Union");

PythonType pt = new PythonType("Union");
pt.addTypeParam(new PythonType("StrictBytes"));
pt.addTypeParam(new PythonType("StrictStr"));
return pt;
}
// same as above which has validation
moduleImports.add("pydantic", "StrictBytes");
moduleImports.add("pydantic", "StrictStr");
moduleImports.add("typing", "Union");

PythonType pt = new PythonType("Union");
pt.addTypeParam(new PythonType("StrictBytes"));
pt.addTypeParam(new PythonType("StrictStr"));
return pt;
}

private PythonType boolType(IJsonSchemaValidationProperties cp) {
Expand All @@ -1281,29 +1204,6 @@ private PythonType boolType(IJsonSchemaValidationProperties cp) {
private PythonType decimalType(IJsonSchemaValidationProperties cp) {
PythonType pt = new PythonType("Decimal");
moduleImports.add("decimal", "Decimal");

if (cp.getHasValidation()) {
// e.g. condecimal(ge=10, le=100, strict=True)
pt.constrain("strict", true);
if (cp.getMaximum() != null) {
if (cp.getExclusiveMaximum()) {
pt.constrain("gt", cp.getMaximum(), false);
} else {
pt.constrain("ge", cp.getMaximum(), false);
}
}
if (cp.getMinimum() != null) {
if (cp.getExclusiveMinimum()) {
pt.constrain("lt", cp.getMinimum(), false);
} else {
pt.constrain("le", cp.getMinimum(), false);
}
}
if (cp.getMultipleOf() != null) {
pt.constrain("multiple_of", cp.getMultipleOf());
}
}

return pt;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ private void checkUpdateExtension(String path, PathItem.HttpMethod httpMethod, O
{"API-CHANNEL", operation.getExtensions().getOrDefault("x-api-channel", "NULL").toString().toUpperCase()},
{"API-PERMISSION", operation.getExtensions().getOrDefault("x-api-permission", "NULL").toString().toUpperCase()},
{"API-RATE-LIMIT-POOL", operation.getExtensions().getOrDefault("x-api-rate-limit-pool", "NULL").toString().toUpperCase()},
{"API-RATE-LIMIT", operation.getExtensions().getOrDefault("x-api-rate-limit", "NULL").toString().toUpperCase()},
{"API-RATE-LIMIT-WEIGHT", operation.getExtensions().getOrDefault("x-api-rate-limit-weight", "NULL").toString().toUpperCase()},
};

String[] extraComment = AsciiTable.getTable(AsciiTable.BASIC_ASCII_NO_DATA_SEPARATORS, new Column[]{
Expand Down
2 changes: 1 addition & 1 deletion generator/postman/script_broker_template.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function auth(apiKey, method, url, data) {
'KC-API-PASSPHRASE': sign(apiKey.passphrase || '', apiKey.secret),
'Content-Type': 'application/json',
'User-Agent': `Kucoin-Universal-Postman-SDK/{{SDK-VERSION}}`,
'KC-API-KEY-VERSION': 2,
'KC-API-KEY-VERSION': 3,
'KC-API-PARTNER': apiKey.partner,
'KC-BROKER-NAME': apiKey.brokerName,
'KC-API-PARTNER-VERIFY': 'true',
Expand Down
2 changes: 1 addition & 1 deletion generator/postman/script_template.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function auth(apiKey, method, url, data) {
'KC-API-PASSPHRASE': sign(apiKey.passphrase || '', apiKey.secret),
'Content-Type': 'application/json',
'User-Agent': `Kucoin-Universal-Postman-SDK/{{SDK-VERSION}}`,
'KC-API-KEY-VERSION': 2,
'KC-API-KEY-VERSION': 3,
};
}

Expand Down
20 changes: 20 additions & 0 deletions sdk/golang/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# Changelog

API documentation [Changelog](https://www.kucoin.com/docs-new/change-log)

Current synchronized API documentation version [20250313](https://www.kucoin.com/docs-new/change-log#20250313)

## 2025-03-21(1.2.0)
- Update the latest APIs, documentation, etc
- Remove range validation in Python
- Update API KEY verification version to 3
- Fix the bug related to resubscribing
- The Node.js SDK has been changed to the official unified version
- Fix issues with automated test execution.

## 2025-03-04(Python 1.1.1)
- Update __init__.py to enhance the import experience
- Reduce unnecessary log output
- Optimize WebSocket reconnection logic

## 2025-02-26(Nodejs 0.1.0-alpha)
- Release Node.js implementation

## 2025-01-16(1.1.0)
- Updated the API sequence to be consistent with the documentation.
- Updated the license.
Expand Down
2 changes: 1 addition & 1 deletion sdk/golang/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ For an overview of the project and SDKs in other languages, refer to the [Main R

## 📦 Installation

### Latest Version: `1.1.0`
### Latest Version: `1.2.0`
Install the Golang SDK using `go get`:

```bash
Expand Down
5 changes: 3 additions & 2 deletions sdk/golang/example/example_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/google/uuid"
"io"
"net/http"
"os"
"strconv"
"time"

"github.com/google/uuid"
)

type KcSigner struct {
Expand All @@ -37,7 +38,7 @@ func (ks *KcSigner) Headers(plain string) map[string]string {
"KC-API-PASSPHRASE": ks.apiPassPhrase,
"KC-API-TIMESTAMP": t,
"KC-API-SIGN": s,
"KC-API-KEY-VERSION": "2",
"KC-API-KEY-VERSION": "3",
}
return ksHeaders
}
Expand Down
4 changes: 2 additions & 2 deletions sdk/golang/internal/infra/default_signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (ks *KcSigner) Headers(plain string) map[string]string {
"KC-API-PASSPHRASE": ks.apiPassPhrase,
"KC-API-TIMESTAMP": t,
"KC-API-SIGN": s,
"KC-API-KEY-VERSION": "2",
"KC-API-KEY-VERSION": "3",
}
return ksHeaders
}
Expand All @@ -55,7 +55,7 @@ func (ks *KcSigner) BrokerHeaders(plain string) map[string]string {
"KC-API-PASSPHRASE": ks.apiPassPhrase,
"KC-API-TIMESTAMP": t,
"KC-API-SIGN": s,
"KC-API-KEY-VERSION": "2",
"KC-API-KEY-VERSION": "3",
"KC-API-PARTNER": ks.brokerPartner,
"KC-BROKER-NAME": ks.brokerName,
"KC-API-PARTNER-VERIFY": "true",
Expand Down
2 changes: 1 addition & 1 deletion sdk/golang/internal/infra/default_ws_callback.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (s *CallbackManager) GetSubInfo() []*util.SubInfo {
}
for topic, _ := range topics {
parts := strings.Split(topic, ":")
if len(parts) == 2 {
if len(parts) == 2 && parts[1] != "all" {
info.Args = append(info.Args, parts[1])
}
info.Callback = s.topicCallbackMapping[topic].callback
Expand Down
Loading
Loading