diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml
index 56a28f139526a..49c93dbc250a2 100644
--- a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml
+++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml
@@ -6,27 +6,21 @@ body:
- type: markdown
attributes:
value: |
- ## If you want your issue to be resolved quickly, we strongly recommend that you
-
- - Read the [Manual](https://github.com/ccxt/ccxt/wiki/Manual), and give special attention to the following sections:
- - [Exchange Properties](https://github.com/ccxt/ccxt/wiki/Manual#exchange-properties)
- - [Rate Limit](https://github.com/ccxt/ccxt/wiki/Manual#rate-limit)
- - [DDoS Protection](https://github.com/ccxt/ccxt/wiki/Manual#ddos-protection-by-cloudflare--incapsula)
- - [Authentication](https://github.com/ccxt/ccxt/wiki/Manual#authentication)
- - [API Keys Setup](https://github.com/ccxt/ccxt/wiki/Manual#api-keys-setup)
- - Read the [API docs](https://github.com/ccxt/ccxt/wiki/Exchange-Markets) for your exchange.
-
- ---
-
- ## Ensure
- - You have already searched for [existing issues](https://github.com/ccxt/ccxt/issues) and confirmed that this issue is not a duplicate
- - This question is directly related to ccxt
- - You have read the [FAQ](https://github.com/ccxt/ccxt/wiki/FAQ) for most frequently asked questions
- - You have read the [How to submit a good issue](https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-submit-an-issue)
- - Make sure your local version of CCXT is up to date. Check by comparing the output of `ccxt.version` to https://github.com/ccxt/ccxt/blob/master/package.json#L3
- - You have read the [Troubleshooting](https://github.com/ccxt/ccxt/wiki/Manual#troubleshooting) section of the manual and followed troubleshooting steps
-
- ---
+ ## Ensure :
+ - You have already searched across the existing issues
+ - Your local CCXT version is up to date (check the [latest available version](https://github.com/ccxt/ccxt/blob/master/package.json#L3) )
+ - Read the [FAQ](https://github.com/ccxt/ccxt/wiki/FAQ) or search for the specific subject in the [Manual](https://github.com/ccxt/ccxt/wiki/Manual) (eg: `Exchange Properties`, `Rate Limit`, `Authentication`, `API keys`, etc).
+ - Read the [Troubleshooting](https://github.com/ccxt/ccxt/wiki/Manual#troubleshooting) to better understand your issue.
+
+ ## Please:
+ - Set `exchange.verbose = true` property before calling exchange functions
+ - Provide the minimal, reproducible example/code
+ - Surround your code/output with triple backticks:
+ ````markdown
+ ```
+ your data here
+ ```
+ - Hide the keys & credentials.
- type: input
id: operating-system
@@ -39,12 +33,13 @@ body:
id: language
attributes:
multiple: true
- label: Programming Languages
- description: Which language are you using?
+ label: Programming Language
options:
- JavaScript
- Python
- PHP
+ - C#
+ - GO
validations:
required: false
@@ -58,18 +53,5 @@ body:
- type: textarea
attributes:
label: Description
- description: Describe the issue that you're having precisely. Include clear steps for how to reproduce the issue.
- validations:
- required: false
-
- - type: textarea
- attributes:
- label: Code
- description: Don't post your API keys or secret keys!
- value: |
- ```
-
-
- ```
validations:
required: false
diff --git a/.github/workflows/cs.yml b/.github/workflows/cs.yml
index b4bd74e278434..956c8e5be8063 100644
--- a/.github/workflows/cs.yml
+++ b/.github/workflows/cs.yml
@@ -126,7 +126,7 @@ jobs:
name: shared_env
- uses: actions/setup-dotnet@v4
with:
- dotnet-version: '7.0.x'
+ dotnet-version: '9.0.x'
dotnet-quality: 'preview'
- name: Download CS Files
uses: actions/download-artifact@v4
diff --git a/.github/workflows/go-app.yml b/.github/workflows/go-app.yml
index a03bd9b321f9a..8d14754bae41e 100644
--- a/.github/workflows/go-app.yml
+++ b/.github/workflows/go-app.yml
@@ -137,6 +137,8 @@ jobs:
with:
name: go-files
path: go/
+ - name: Restore shared_env
+ run: ./utils/restore_shared_env.sh
- name: Build tests
run: cd go/ && go build ./tests/main.go
- name: Install npm dependencies
@@ -151,9 +153,13 @@ jobs:
# run: go build ./go/tests/main.go
# - name: Adjust permissions
# run: cd go && chmod +x main
+ - name: Build
+ run: cd go && go build ./v4 && cd ..
+ - name: Build tests
+ run: cd go && go build ./tests/main.go && cd ..
- name: Live tests
if: env.important_modified == 'true'
- run: cd go/ && npm run live-tests-rest-go
+ run: node run-tests --go --useProxy
- name: Live tests (specific)
if: env.important_modified == 'false'
run: ./run-tests-simul.sh --go "${{ env.rest_files }}"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d69fc0580ea49..fc0fced789c38 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,31 +7,8 @@
## How To Submit An Issue
-If you want to submit an issue and you want your issue to be resolved quickly, here's a checklist for you:
-
-- Read the [Manual](https://github.com/ccxt/ccxt/wiki/Manual), and especially carefully read the following sections:
- - [Exchange Properties](https://github.com/ccxt/ccxt/wiki/Manual#exchange-properties)
- - [Rate Limit](https://github.com/ccxt/ccxt/wiki/Manual#rate-limit)
- - [DDoS Protection](https://github.com/ccxt/ccxt/wiki/Manual#ddos-protection-by-cloudflare--incapsula)
- - [Authentication](https://github.com/ccxt/ccxt/wiki/Manual#authentication)
- - [API Keys Setup](https://github.com/ccxt/ccxt/wiki/Manual#api-keys-setup)
-- Read the [Troubleshooting](https://github.com/ccxt/ccxt/wiki/Manual#troubleshooting) section and follow troubleshooting steps.
-- Read the [FAQ](https://github.com/ccxt/ccxt/wiki/FAQ) for most frequently asked questions.
-- Read the [API docs](https://github.com/ccxt/ccxt/wiki/Exchange-Markets) for your exchange.
-- Search for similar issues first to avoid duplicates.
-- If your issue is unique, along with a basic description of the failure, the following **IS REQUIRED**:
- - **set `exchange.verbose = true` property on the exchange instance before calling its functions or methods**
- - **DON'T POST SCREENSHOTS OF CODE OR ERRORS, POST THE OUTPUT AND CODE IN PLAIN TEXT!**
- - **surround code and output with triple backticks: ```GOOD```**
- - don't confuse the backtick symbol (`) with the quote symbol (\'): '''BAD'''
- - don't confuse a single backtick with triple backticks: `BAD`
- - paste a complete code snippet you're having difficulties with, avoid one-liners
- - paste the **full verbose output** of the failing method without your keys
- - the verbose output should include the request and response from the exchange (not just an error callstack)
- - write your language **and version**
- - write ccxt library version
- - which exchange it is
- - which method you're trying to call
+Read the notes when opening a [new issue on github](https://github.com/ccxt/ccxt/issues/new/choose) and provide the requested details, so we can assist you better. You can aso read [Troubleshooting](https://github.com/ccxt/ccxt/wiki/Manual#troubleshooting) section.
+
### Reporting Vulnerabilities And Critical Issues
diff --git a/README.md b/README.md
index fe928975f4c46..61487020d55cc 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# CCXT – CryptoCurrency eXchange Trading Library
-[](https://www.npmjs.com/package/ccxt) [](https://npmjs.com/package/ccxt) [](https://pypi.python.org/pypi/ccxt) [](https://www.nuget.org/packages/ccxt) [](https://godoc.org/github.com/ccxt/ccxt/go/v4) [](https://discord.gg/ccxt) [](https://github.com/ccxt/ccxt/wiki/Exchange-Markets) [](https://x.com/ccxt_official)
+[](https://www.npmjs.com/package/ccxt) [](https://npmjs.com/package/ccxt) [](https://pypi.python.org/pypi/ccxt) [](https://www.nuget.org/packages/ccxt) [](https://godoc.org/github.com/ccxt/ccxt/go/v4) [](https://discord.gg/ccxt) [](https://github.com/ccxt/ccxt/wiki/Exchange-Markets) [](https://x.com/ccxt_official)
A `JavaScript` / `Python` / `PHP` / `C#` / `Go` library for cryptocurrency trading and e-commerce with support for many bitcoin/ether/altcoin exchange markets and merchant APIs.
@@ -47,13 +47,12 @@ Current feature list:
| [](https://www.kucoin.com/ucenter/signup?rcode=E5wkqe) | kucoin | [KuCoin](https://www.kucoin.com/ucenter/signup?rcode=E5wkqe) | [](https://docs.kucoin.com) |  | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) | |
| [](https://futures.kucoin.com/?rcode=E5wkqe) | kucoinfutures | [KuCoin Futures](https://futures.kucoin.com/?rcode=E5wkqe) | [](https://docs.kucoin.com/futures) |  | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) | |
| [](https://www.mexc.com/register?inviteCode=mexc-1FQ1GNu1) | mexc | [MEXC Global](https://www.mexc.com/register?inviteCode=mexc-1FQ1GNu1) | [](https://mexcdevelop.github.io/apidocs/) |  | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) | |
-| [](https://trade.mode.network?ref=MODETRADE) | modetrade | [Mode Trade](https://trade.mode.network?ref=MODETRADE) | [](undefined) |  | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) | [](https://trade.mode.network?ref=MODETRADE) |
| [](https://www.okx.com/join/CCXT2023) | okx | [OKX](https://www.okx.com/join/CCXT2023) | [](https://www.okx.com/docs-v5/en/) |  | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) | [](https://www.okx.com/join/CCXT2023) |
| [](https://woox.io/register?ref=DIJT0CNL) | woo | [WOO X](https://woox.io/register?ref=DIJT0CNL) | [](https://docs.woox.io/) |  | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) | [](https://woox.io/register?ref=DIJT0CNL) |
| [](https://dex.woo.org/en/trade?ref=CCXT) | woofipro | [WOOFI PRO](https://dex.woo.org/en/trade?ref=CCXT) | [](https://orderly.network/docs/build-on-evm/building-on-evm) |  | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) | [](https://dex.woo.org/en/trade?ref=CCXT) |
## Supported Cryptocurrency Exchanges
-The CCXT library currently supports the following 102 cryptocurrency exchange markets and trading APIs:
+The CCXT library currently supports the following 103 cryptocurrency exchange markets and trading APIs:
|logo |id |name |ver |type |certified |pro |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------|----------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------:|--------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
@@ -112,6 +111,7 @@ Current feature list:
| [](https://www.ellipx.com) | ellipx | [Ellipx](https://www.ellipx.com) | [](https://docs.google.com/document/d/1ZXzTQYffKE_EglTaKptxGQERRnunuLHEMmar7VC9syM) |  | | |
| [](https://exmo.me/?ref=131685) | exmo | [EXMO](https://exmo.me/?ref=131685) | [](https://exmo.me/en/api_doc?ref=131685) |  | | |
| [](https://fmfw.io/referral/da948b21d6c92d69) | fmfwio | [FMFW.io](https://fmfw.io/referral/da948b21d6c92d69) | [](https://api.fmfw.io/) |  | | |
+| [](https://app.foxbit.com.br) | foxbit | [Foxbit](https://app.foxbit.com.br) | [](https://docs.foxbit.com.br) |  | | |
| [](https://www.gate.io/signup/2436035) | gate | [Gate.io](https://www.gate.io/signup/2436035) | [](https://www.gate.io/docs/developers/apiv4/en/) |  | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) |
| [](https://gemini.com/) | gemini | [Gemini](https://gemini.com/) | [](https://docs.gemini.com/rest-api) |  | | [](https://ccxt.pro) |
| [](https://global.hashkey.com/en-US/register/invite?invite_code=82FQUN) | hashkey | [HashKey Global](https://global.hashkey.com/en-US/register/invite?invite_code=82FQUN) | [](https://hashkeyglobal-apidoc.readme.io/) |  | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) |
@@ -130,7 +130,7 @@ Current feature list:
| [](https://www.luno.com/invite/44893A) | luno | [luno](https://www.luno.com/invite/44893A) | [](https://www.luno.com/en/api) |  | | [](https://ccxt.pro) |
| [](https://www.mercadobitcoin.com.br) | mercado | [Mercado Bitcoin](https://www.mercadobitcoin.com.br) | [](https://www.mercadobitcoin.com.br/api-doc) |  | | |
| [](https://www.mexc.com/register?inviteCode=mexc-1FQ1GNu1) | mexc | [MEXC Global](https://www.mexc.com/register?inviteCode=mexc-1FQ1GNu1) | [](https://mexcdevelop.github.io/apidocs/) |  | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) |
-| [](https://trade.mode.network?ref=MODETRADE) | modetrade | [Mode Trade](https://trade.mode.network?ref=MODETRADE) | [](undefined) |  | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) |
+| [](https://trade.mode.network?ref=MODETRADE) | modetrade | [Mode Trade](https://trade.mode.network?ref=MODETRADE) | [](undefined) |  | | [](https://ccxt.pro) |
| [](https://www.my.okx.com/join/CCXT2023) | myokx | [MyOKX (EEA)](https://www.my.okx.com/join/CCXT2023) | [](https://my.okx.com/docs-v5/en/#overview) |  | | [](https://ccxt.pro) |
| [](https://one.ndax.io/bfQiSL) | ndax | [NDAX](https://one.ndax.io/bfQiSL) | [](https://apidoc.ndax.io/) |  | | [](https://ccxt.pro) |
| [](https://www.novadax.com.br/?s=ccxt) | novadax | [NovaDAX](https://www.novadax.com.br/?s=ccxt) | [](https://doc.novadax.com/pt-BR/) |  | | |
@@ -220,13 +220,13 @@ console.log(version, Object.keys(exchanges));
All-in-one browser bundle (dependencies included), served from a CDN of your choice:
-* jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@4.4.92/dist/ccxt.browser.min.js
-* unpkg: https://unpkg.com/ccxt@4.4.92/dist/ccxt.browser.min.js
+* jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@4.4.96/dist/ccxt.browser.min.js
+* unpkg: https://unpkg.com/ccxt@4.4.96/dist/ccxt.browser.min.js
CDNs are not updated in real-time and may have delays. Defaulting to the most recent version without specifying the version number is not recommended. Please, keep in mind that we are not responsible for the correct operation of those CDN servers.
```HTML
-
+
```
Creates a global `ccxt` object:
diff --git a/build/dummy.txt b/build/dummy.txt
index d42b8de32404d..3b9c3b0435a35 100644
--- a/build/dummy.txt
+++ b/build/dummy.txt
@@ -38,3 +38,8 @@ release!!!!
!
!
!
+try
+!
+!
+!
+!
\ No newline at end of file
diff --git a/build/generateImplicitAPI.ts b/build/generateImplicitAPI.ts
index b2828d2a31ed3..3c283b8945598 100644
--- a/build/generateImplicitAPI.ts
+++ b/build/generateImplicitAPI.ts
@@ -216,22 +216,29 @@ function createImplicitMethodsGo(){
const exchange = exchanges[index];
const methodNames = storedCamelCaseMethods[exchange];
+ // const reusableMethod = [
+ // `func (this *${exchange}) callEndpointAsync(endpointName string, args ...interface{}) <-chan interface{} {`,
+ // ` parameters := GetArg(args, 0, nil)`,
+ // ` ch := make(chan interface{})`,
+ // ` go func() {`,
+ // ` defer close(ch)`,
+ // ` defer func() {`,
+ // ` if r := recover(); r != nil {`,
+ // ` ch <- "panic:" + ToString(r)`,
+ // ` }`,
+ // ` }()`,
+ // ` ch <- (<-this.callEndpoint (endpointName, parameters))`,
+ // ` PanicOnError(ch)`,
+ // ` }()`,
+ // ` return ch`,
+ // `}`,
+ // ``
+ // ].join('\n');
+
const methods = methodNames.map(method=> {
return [
`func (this *${exchange}) ${capitalize(method)} (args ...interface{}) <-chan interface{} {`,
- ` parameters := GetArg(args, 0, nil)`,
- ` ch := make(chan interface{})`,
- ` go func() {`,
- ` defer close(ch)`,
- ` defer func() {`,
- ` if r := recover(); r != nil {`,
- ` ch <- "panic:" + ToString(r)`,
- ` }`,
- ` }()`,
- ` ch <- (<-this.callEndpoint ("${method}", parameters))`,
- ` PanicOnError(ch)`,
- ` }()`,
- ` return ch`,
+ ` return this.callEndpointAsync("${method}", args...)`,
`}`,
``,
].join('\n')
@@ -243,7 +250,8 @@ function createImplicitMethodsGo(){
// ``,
// ].join('\n')
});
- storedGoMethods[exchange] = storedGoMethods[exchange].concat (methods)
+ // methods.unshift (reusableMethod);
+ storedGoMethods[exchange] = storedGoMethods[exchange].concat (methods)
}
}
diff --git a/build/goTranspiler.ts b/build/goTranspiler.ts
index 2e55c16b6eb3a..6aaff714d3d60 100644
--- a/build/goTranspiler.ts
+++ b/build/goTranspiler.ts
@@ -48,9 +48,10 @@ if (platform === 'win32') {
}
}
-const GLOBAL_WRAPPER_FILE = './go/v4/base/exchange_wrappers.go';
+const GLOBAL_WRAPPER_FILE = './go/v4/exchange_wrappers.go';
const EXCHANGE_WRAPPER_FOLDER = './go/v4/'
const DYNAMIC_INSTANCE_FILE = './go/v4/exchange_dynamic.go';
+const TYPED_INTERFACE_FILE = './go/v4/exchange_typed_interface.go';
// const EXCHANGE_WS_WRAPPER_FOLDER = './go/v4/exchanges/pro/wrappers/'
const ERRORS_FILE = './go/v4/exchange_errors.go';
const BASE_METHODS_FILE = './go/v4/exchange_generated.go';
@@ -68,6 +69,8 @@ const goComments: any = {};
const goTypeOptions: any = {};
const goWithMethods = {};
+const WRAPPER_METHODS: {} = {};
+
let goTests: string[] = [];
const VIRTUAL_BASE_METHODS: any = {
@@ -94,6 +97,7 @@ const VIRTUAL_BASE_METHODS: any = {
"fetchMyTrades": true,
"fetchOHLCV": true,
"fetchOpenOrders": true,
+ "fetchTradingFees": true,
"fetchOption": true,
"fetchOrder": true,
"fetchOrderBook": true,
@@ -151,6 +155,136 @@ const VIRTUAL_BASE_METHODS: any = {
"sign": false
}
+const INTERFACE_METHODS = [
+ 'cancelAllOrders',
+ 'cancelAllOrdersAfter',
+ 'cancelOrder',
+ 'cancelOrdersForSymbols',
+ 'createConvertTrade',
+ 'createDepositAddress',
+ 'createLimitBuyOrder',
+ 'createLimitOrder',
+ 'createLimitSellOrder',
+ 'createMarketBuyOrder',
+ 'createMarketBuyOrderWithCost',
+ 'createMarketOrder',
+ 'createMarketOrderWithCost',
+ 'createMarketSellOrder',
+ 'createMarketSellOrderWithCost',
+ 'createOrder',
+ 'createOrders',
+ 'createOrderWithTakeProfitAndStopLoss',
+ 'createPostOnlyOrder',
+ 'createReduceOnlyOrder',
+ 'createStopLimitOrder',
+ 'createStopLossOrder',
+ 'createStopMarketOrder',
+ 'createStopOrder',
+ 'createTakeProfitOrder',
+ 'createTrailingAmountOrder',
+ 'createTrailingPercentOrder',
+ 'createTriggerOrder',
+ 'editLimitBuyOrder',
+ 'editLimitOrder',
+ 'editLimitSellOrder',
+ 'editOrder',
+ 'editOrders',
+ 'fetchAccounts',
+ 'fetchAllGreeks',
+ 'fetchBalance',
+ 'fetchBidsAsks',
+ 'fetchBorrowInterest',
+ 'fetchBorrowRate',
+ 'fetchCanceledAndClosedOrders',
+ 'fetchClosedOrders',
+ 'fetchConvertCurrencies',
+ 'fetchConvertQuote',
+ 'fetchConvertTrade',
+ 'fetchConvertTradeHistory',
+ 'fetchCrossBorrowRate',
+ 'fetchCrossBorrowRates',
+ 'fetchCurrencies',
+ 'fetchDepositAddress',
+ 'fetchDepositAddresses',
+ 'fetchDepositAddressesByNetwork',
+ 'fetchDeposits',
+ 'fetchDepositsWithdrawals',
+ 'fetchDepositWithdrawFee',
+ 'fetchDepositWithdrawFees',
+ 'fetchFreeBalance',
+ 'fetchFundingHistory',
+ 'fetchFundingInterval',
+ 'fetchFundingIntervals',
+ 'fetchFundingRate',
+ 'fetchFundingRateHistory',
+ 'fetchFundingRates',
+ 'fetchGreeks',
+ 'fetchIndexOHLCV',
+ 'fetchIsolatedBorrowRate',
+ 'fetchIsolatedBorrowRates',
+ 'fetchLastPrices',
+ 'fetchLedger',
+ 'fetchLedgerEntry',
+ 'fetchLeverage',
+ 'fetchLeverages',
+ 'fetchLeverageTiers',
+ 'fetchLiquidations',
+ 'fetchLongShortRatio',
+ 'fetchLongShortRatioHistory',
+ 'fetchMarginAdjustmentHistory',
+ 'fetchMarginMode',
+ 'fetchMarginModes',
+ 'fetchMarketLeverageTiers',
+ 'fetchMarkets',
+ 'fetchMarkOHLCV',
+ 'fetchMarkPrice',
+ 'fetchMarkPrices',
+ 'fetchMyLiquidations',
+ 'fetchMyTrades',
+ 'fetchOHLCV',
+ 'fetchOpenInterest',
+ 'fetchOpenInterestHistory',
+ 'fetchOpenInterests',
+ 'fetchOpenOrders',
+ 'fetchOption',
+ 'fetchOptionChain',
+ 'fetchOrder',
+ 'fetchOrderBook',
+ 'fetchOrderBooks',
+ 'fetchOrders',
+ 'fetchOrderStatus',
+ 'fetchOrderTrades',
+ 'fetchPaymentMethods',
+ 'fetchPosition',
+ 'fetchPositionHistory',
+ 'fetchPositionMode',
+ 'fetchPositions',
+ 'fetchPositionsForSymbol',
+ 'fetchPositionsHistory',
+ 'fetchPositionsRisk',
+ 'fetchPremiumIndexOHLCV',
+ 'fetchStatus',
+ 'fetchTicker',
+ 'fetchTickers',
+ 'fetchTime',
+ 'fetchTrades',
+ 'fetchTradingFee',
+ 'fetchTradingFees',
+ 'fetchTradingLimits',
+ 'fetchTransactionFee',
+ 'fetchTransactionFees',
+ 'fetchTransactions',
+ 'fetchTransfer',
+ 'fetchTransfers',
+ 'fetchWithdrawals',
+ // 'setLeverage', // tmp remove we have to unify types
+ 'setMargin',
+ 'setMarginMode',
+ 'setPositionMode',
+ 'transfer',
+ 'withdraw',
+]
+
class NewTranspiler {
transpiler!: Transpiler;
@@ -431,6 +565,10 @@ class NewTranspiler {
return ` <- chan int64`; // custom handling for now
}
+ if (name.startsWith('fetchDepositWithdrawFee')) { // tmp fix later with proper types
+ return `map[string]interface{}`
+ }
+
const isPromise = type.startsWith('Promise<') && type.endsWith('>');
let wrappedType = isPromise ? type.substring(8, type.length - 1) : type;
let isList = false;
@@ -610,7 +748,7 @@ class NewTranspiler {
'createContractOrder',
'createSwapOrder',
'fetchPortfolioDetails',
- 'createVault'
+ 'createVault',
] // improve this later
if (isWs) {
if (methodName.indexOf('Snapshot') !== -1 || methodName.indexOf('Subscription') !== -1 || methodName.indexOf('Cache') !== -1) {
@@ -641,6 +779,10 @@ class NewTranspiler {
return `(res).(int64)`;
}
+ if (methodName === 'fetchDepositWithdrawFees' || methodName === 'fetchDepositWithdrawFee') {
+ return `(res).(map[string]interface{})`;
+ }
+
// handle the typescript type Dict
if (unwrappedType === 'Dict' || unwrappedType === 'map[string]interface{}') {
return `res.(map[string]interface{})`;
@@ -760,12 +902,50 @@ class NewTranspiler {
goTypeOptions[capName] = res.concat(withMethod).join("\n");
}
+
+ createMissingMethodWrapper(exchangeName: string, name: string, methodInfo: any) {
+ if (!methodInfo) {
+ return '';
+ }
+ const itf = methodInfo.interface
+ // const params = methodInfo.params;
+ const exCap = this.capitalize(exchangeName);
+ const nameCap = this.capitalize(name);
+ const wrapper = methodInfo.wrapper;
+
+ let args: string[] = [];
+ for (const param of wrapper.parameters) {
+ if (param.name === 'params' && wrapper.parameters.length === 1) {
+ args.push('params...')
+ break;
+ }
+ if (param.isOptional || param.name === 'params' || param.initializer !== undefined || param.initializer === 'undefined' || param.initializer === '{}') {
+ // if (wrapper.parameters.length === 1) {
+ // args.push(`params...`)
+ // } else {
+ // args.push(`options...`)
+ // }
+ args.push(`options...`)
+ break;
+ } else {
+ args.push(this.safeGoName(param.name))
+ }
+ }
+
+ return `func (this *${exCap}) ${itf} {return this.exchangeTyped.${nameCap}(${args.join(', ')})}`;
+ }
+
createWrapper (exchangeName: string, methodWrapper: any, isWs = false) {
const isAsync = methodWrapper.async;
+ const isExchange = exchangeName === 'Exchange';
const methodName = methodWrapper.name;
+ if (methodName.startsWith('watch') || methodName.endsWith('Ws')) {
+ return '';
+ }
if (!this.shouldCreateWrapper(methodName, isWs) || !isAsync) {
return ''; // skip aux methods like encodeUrl, parseOrder, etc
}
+
const methodNameCapitalized = methodName.charAt(0).toUpperCase() + methodName.slice(1);
const returnType = this.convertJavascriptTypeToGoType(methodName, methodWrapper.returnType, true);
const unwrappedType = this.unwrapTaskIfNeeded(returnType as string);
@@ -805,13 +985,14 @@ class NewTranspiler {
params = 'params...'
}
+ const accessor = isExchange ? 'this.Exchange.' : 'this.Core.';
const body = [
// `${two}ch:= make(chan ${unwrappedType})`,
// `${two}go func() {`,
// `${three}defer close(ch)`,
// `${three}defer ReturnPanicError(ch)`,
`${defaultParams}`,
- `${two}res := <- this.Core.${methodNameCapitalized}(${params})`,
+ `${two}res := <- ${accessor}${methodNameCapitalized}(${params})`,
`${two}if IsError(res) {`,
`${three}return ${emtpyObject}, CreateReturnError(res)`,
`${two}}`,
@@ -819,8 +1000,22 @@ class NewTranspiler {
// `${two}}()`,
// `${two}return ch`,
]
+ const interfaceMethod = `${methodNameCapitalized}(${stringArgs}) (${unwrappedType}, error)`
+ if (!WRAPPER_METHODS[exchangeName]) {
+ WRAPPER_METHODS[exchangeName] = [];
+ }
+ if (!WRAPPER_METHODS[exchangeName][methodName]) {
+ WRAPPER_METHODS[exchangeName][methodName] = {};
+ }
+ WRAPPER_METHODS[exchangeName][methodName] = {
+ wrapper: methodWrapper,
+ interface: interfaceMethod,
+ params: stringArgs,
+ }
+ // wrapperMethods[exchangeName].push([interfaceMethod, stringArgs, methodWrapper]);
+ const funcContext = isExchange ? 'ExchangeTyped' : this.capitalize(exchangeName);
const method = [
- `${one}func (this *${this.capitalize(exchangeName)}) ${methodNameCapitalized}(${stringArgs}) (${unwrappedType}, error) {`,
+ `${one}func (this *${funcContext}) ${methodNameCapitalized}(${stringArgs}) (${unwrappedType}, error) {`,
...body,
// this.getDefaultParamsWrappers(methodNameCapitalized, methodWrapper.parameters),
// `${two}res := ${isAsync ? '<-' : ''}this.${exchangeName}.${methodNameCapitalized}(${params});`,
@@ -844,30 +1039,71 @@ class NewTranspiler {
}
createGoWrappers(exchange:string, path: string, wrappers: any[], ws = false) {
+ const methodsList = wrappers.map(wrapper => wrapper.name);
+ const missingMethods = INTERFACE_METHODS.filter(method => !methodsList.includes(method));
+
const wrappersIndented = wrappers.map(wrapper => this.createWrapper(exchange, wrapper, ws)).filter(wrapper => wrapper !== '').join('\n');
+
+ let missingMethodsWrappers = '';
+ if (exchange !== 'Exchange') {
+ if (!WRAPPER_METHODS['Exchange']) {
+ throw new Error('Exchange wrapper methods are not defined, please transpile base methods first');
+ }
+ missingMethodsWrappers = missingMethods.map (m => this.createMissingMethodWrapper(exchange, m, WRAPPER_METHODS['Exchange'][m])).filter(wrapper => wrapper !== '').join('\n');
+ }
+
const shouldCreateClassWrappers = exchange === 'Exchange';
const classes = shouldCreateClassWrappers ? this.createExchangesWrappers().filter(e=> !!e).join('\n') : '';
// const exchangeName = ws ? exchange + 'Ws' : exchange;
const namespace = 'package ccxt';
const capitizedName = exchange.charAt(0).toUpperCase() + exchange.slice(1);
// const capitalizeStatement = ws ? `public class ${capitizedName}: ${exchange} { public ${capitizedName}(object args = null) : base(args) { } }` : '';
- const exchangeStruct = [
- `type ${capitizedName} struct {`,
- ` *${exchange}`,
- ` Core *${exchange}`,
- `}`
- ].join('\n');
- const newMethod = [
- 'func New' + capitizedName + '(userConfig map[string]interface{}) ' + capitizedName + ' {',
- ` p := &${exchange}{}`,
- ' p.Init(userConfig)',
- ` return ${capitizedName}{`,
- ` ${exchange}: p,`,
- ` Core: p,`,
- ` }`,
- '}'
- ].join('\n');
+ let exchangeStruct = '';
+ if (exchange === 'Exchange') {
+
+ exchangeStruct = [
+ `type ExchangeTyped struct {`,
+ ` *Exchange`,
+ `}`
+ ].join('\n');
+
+ } else {
+
+ exchangeStruct = [
+ `type ${capitizedName} struct {`,
+ ` *${exchange}`,
+ ` Core *${exchange}`,
+ ` exchangeTyped *ExchangeTyped`,
+ `}`
+ ].join('\n');
+
+ }
+
+ let newMethod = '';
+ if (exchange === 'Exchange') {
+ newMethod = [
+ 'func NewExchangeTyped(exchangePointer *Exchange) *ExchangeTyped {',
+ ` return &ExchangeTyped{`,
+ ` Exchange: exchangePointer,`,
+ ` }`,
+ '}'
+ ].join('\n');
+
+ } else {
+ newMethod = [
+ 'func New' + capitizedName + '(userConfig map[string]interface{}) *' + capitizedName + ' {',
+ ` p := &${exchange}{}`,
+ ' p.Init(userConfig)',
+ ` return &${capitizedName}{`,
+ ` ${exchange}: p,`,
+ ` Core: p,`,
+ ' exchangeTyped: NewExchangeTyped(&p.Exchange),',
+ ` }`,
+ '}'
+ ].join('\n');
+ }
+
const file = [
namespace,
@@ -879,6 +1115,9 @@ class NewTranspiler {
this.createGeneratedHeader().join('\n'),
'',
wrappersIndented,
+ '// missing typed methods from base',
+ '//nolint',
+ missingMethodsWrappers,
].join('\n')
log.magenta ('→', (path as any).yellow)
@@ -994,7 +1233,13 @@ ${constStatements.join('\n')}
return `<-this.DerivedExchange.${capitalizedMethod}(${p2})`;
});
// create wrappers with specific types
- // this.creategoWrappers('Exchange', GLOBAL_WRAPPER_FILE, baseFile.methodsTypes)
+ this.createGoWrappers('Exchange', GLOBAL_WRAPPER_FILE, baseFile.methodsTypes)
+
+ // const exchangeMethods = wrapperMethods['Exchange'];
+ // const sortedList = exchangeMethods.sort((a, b) => a.localeCompare(b));
+ // sortedList.forEach( i => {
+ // console.log(i)
+ // });
// custom transformations needed for go
@@ -1050,7 +1295,7 @@ ${constStatements.join('\n')}
})
const functionDecl = `
-func DynamicallyCreateInstance(exchangeId string, exchangeArgs map[string]interface{}) (IExchange, bool) {
+func DynamicallyCreateInstance(exchangeId string, exchangeArgs map[string]interface{}) (ICoreExchange, bool) {
switch exchangeId {
${caseStatements.join('\n')}
default:
@@ -1069,6 +1314,58 @@ ${caseStatements.join('\n')}
fs.writeFileSync (dynamicInstanceFile, file);
}
+
+ createTypedInterfaceFile(){
+ if (!WRAPPER_METHODS['Exchange']) {
+ throw new Error('Exchange wrapper methods are not defined, please transpile base methods first');
+ }
+ const exchanges = ['exchange'].concat(exchangeIds);
+
+ const caseStatements = exchanges.map(exchange => {
+ const struct = exchange === 'exchange' ? 'ExchangeTyped' : this.capitalize(exchange);
+ const args = exchange === 'exchange' ? 'nil' : 'options';
+ return` case "${exchange}":
+ itf := New${struct}(${args})
+ return itf`;
+ })
+
+ const functionDecl = `
+func CreateExchange(exchangeId string, options map[string]interface{}) IExchange {
+ exchangeId = strings.ToLower(exchangeId)
+ switch exchangeId {
+${caseStatements.join('\n')}
+ default:
+ return nil
+ }
+}
+`
+ const interfaceMethods = Object.keys(WRAPPER_METHODS['Exchange']).map(method => {
+ const methodInfo = WRAPPER_METHODS['Exchange'][method];
+ if (!INTERFACE_METHODS.includes(method)) {
+ return '';
+ }
+ return methodInfo.interface;
+ }).filter(e => !!e)
+
+ const interfaceDecl = `
+type IExchange interface {
+ IBaseExchange
+ ${interfaceMethods.join('\n ')}
+}`;
+
+ const file = [
+ 'package ccxt',
+ 'import "strings"',
+ this.createGeneratedHeader().join('\n'),
+ '',
+ interfaceDecl,
+ functionDecl,
+ ].join('\n');
+
+ fs.writeFileSync (TYPED_INTERFACE_FILE, file);
+ }
+
+
camelize(str: string) {
var res = str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function(match, index) {
if (+match === 0) return ""; // or if (/\s+/.test(match)) for white spaces
@@ -1162,31 +1459,30 @@ ${caseStatements.join('\n')}
}
const options = { goFolder, exchanges }
+ this.transpileBaseMethods (exchangeBase) // now we always need the baseMethods info
+ if (!transpilingSingleExchange && !child) {
+ this.createDynamicInstanceFile();
+ this.createTypedInterfaceFile();
+ }
+
if (!baseOnly && !examplesOnly) {
await this.transpileDerivedExchangeFiles (tsFolder, options, '.ts', force, !!(child || exchanges.length))
}
- // this.transpileExamples(); // disabled for now
- if (examplesOnly) {
- return;
- }
-
- if (transpilingSingleExchange) {
- this.createDynamicInstanceFile();
- return;
- }
if (child) {
return;
}
- this.transpileBaseMethods (exchangeBase)
- this.createDynamicInstanceFile();
if (baseOnly) {
return;
}
+ if (transpilingSingleExchange) {
+ return;
+ }
+
this.transpileTests()
@@ -1630,13 +1926,13 @@ func (this *${className}) Init(userConfig map[string]interface{}) {
// ad-hoc fixes
contentIndentend = this.regexAll (contentIndentend, [
- [/var exchange interface{} =/g,'var exchange ccxt.IExchange ='],
- [/var mockedExchange interface{} =/g,'var mockedExchange ccxt.IExchange ='],
- [/exchange interface\{\},/g, 'exchange ccxt.IExchange,'],
- [/exchange interface\{\}\)/g, 'exchange ccxt.IExchange)'],
+ [/var exchange interface{} =/g,'var exchange ccxt.ICoreExchange ='],
+ [/var mockedExchange interface{} =/g,'var mockedExchange ccxt.ICoreExchange ='],
+ [/exchange interface\{\},/g, 'exchange ccxt.ICoreExchange,'],
+ [/exchange interface\{\}\)/g, 'exchange ccxt.ICoreExchange)'],
[/exchange.(\w+)\s*=\s*(.+)/g, 'exchange.Set$1($2)'],
[/exchange\.(\w+)(,|;|\)|\s)/g, 'exchange.Get$1()$2'],
- [/InitOfflineExchange\(exchangeName interface{}\) interface\{\} {/g, 'InitOfflineExchange(exchangeName interface{}) ccxt.IExchange {'],
+ [/InitOfflineExchange\(exchangeName interface{}\) interface\{\} {/g, 'InitOfflineExchange(exchangeName interface{}) ccxt.ICoreExchange {'],
[/assert\(/g, 'Assert('],
[/GetRootException\(ex\)/g, 'GetRootException(e)'],
[/OnlySpecificTests \[\]interface\{\}/g, 'OnlySpecificTests interface{} '],
@@ -1730,8 +2026,8 @@ func (this *${className}) Init(userConfig map[string]interface{}) {
let regexes = [
[/exchange \:\= &ccxt\.Exchange\{\}/g, 'exchange := ccxt.NewExchange()'],
[/exchange := ccxt\.Exchange\{\}/g, 'exchange := ccxt.NewExchange()'],
- [/exchange interface\{\},/g, 'exchange ccxt.IExchange,'],
- [/exchange interface\{\}\)/g, 'exchange ccxt.IExchange)'],
+ [/exchange interface\{\},/g, 'exchange ccxt.ICoreExchange,'],
+ [/exchange interface\{\}\)/g, 'exchange ccxt.ICoreExchange)'],
[/testSharedMethods\./g, ''],
[/assert/gm, 'Assert'],
[/exchange.(\w+)\s*=\s*(.+)/g, 'exchange.Set$1($2)'],
diff --git a/cleanup.sh b/cleanup.sh
index ed9bd1975095d..80f7f6724d299 100755
--- a/cleanup.sh
+++ b/cleanup.sh
@@ -20,3 +20,4 @@ git checkout HEAD examples
git checkout HEAD go/v4/exchange_metadata.go
git checkout HEAD wiki/Exchange-Markets.md
git checkout HEAD wiki/Manual.md
+git checkout HEAD -- go/v4 ':(exclude)exchange'
diff --git a/cli/ts/cli.ts b/cli/ts/cli.ts
index d24e2cb9caebc..59aad221010d5 100644
--- a/cli/ts/cli.ts
+++ b/cli/ts/cli.ts
@@ -24,7 +24,8 @@ try {
ccxt = await (Function ('return import("../../ts/ccxt")') ());
local = true;
} catch (ee) {
- log.error ('Neither a local installation nor a global CCXT installation was detected, make `npm i` first');
+ log.error (ee);
+ log.error ('Neither a local installation nor a global CCXT installation was detected, make `npm i` first, Also make sure your local ccxt version does not contain any syntax errors.');
process.exit (1);
}
}
@@ -127,6 +128,7 @@ program
.option ('--poll', 'will repeat the call continously')
.option ('--i', 'iteractive mode, keeps the session opened')
.option ('--iso8601')
+ .option ('--refresh-markets', 'forces markets refresh')
.option ('--cors');
// dev related options, docs not needed
diff --git a/cli/ts/helpers.ts b/cli/ts/helpers.ts
index a8f55d359a139..e61f4c8e6307a 100644
--- a/cli/ts/helpers.ts
+++ b/cli/ts/helpers.ts
@@ -27,7 +27,8 @@ try {
// local ccxt project
ccxt = await (Function ('return import("../../ts/ccxt")') ());
} catch (ee) {
- log.error ('Neither a local nor a global ccxt installation was detected, please do `npm i` first');
+ log.error (ee);
+ log.error ('Neither a local installation nor a global CCXT installation was detected, make `npm i` first, Also make sure your local ccxt version does not contain any syntax errors.');
process.exit (1);
}
}
@@ -456,11 +457,12 @@ async function loadSettingsAndCreateExchange (
if (fs.existsSync (keysGlobal)) {
allSettings = JSON.parse (fs.readFileSync (keysGlobal).toString ());
- } else if (fs.existsSync (keysLocal)) {
- allSettings = JSON.parse (fs.readFileSync (keysLocal).toString ());
- } else {
- // log ((`( Note, CCXT CLI is being loaded without api keys, because ${keysLocal} does not exist. You can see the sample at https://github.com/ccxt/ccxt/blob/master/keys.json )` as any).yellow);
}
+ if (fs.existsSync (keysLocal)) {
+ const localSettings = JSON.parse (fs.readFileSync (keysLocal).toString ());
+ allSettings = { ...allSettings, ...localSettings };
+ }
+ // log ((`( Note, CCXT CLI is being loaded without api keys, because ${keysLocal} does not exist. You can see the sample at https://github.com/ccxt/ccxt/blob/master/keys.json )` as any).yellow);
const exchangeSettings = getExchangeSettings (exchangeId);
diff --git a/coin-ws.js b/coin-ws.js
deleted file mode 100644
index bc9c5655b712b..0000000000000
--- a/coin-ws.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import WebSocket from 'ws';
-const webSocket = new WebSocket ('https://stream.coindcx.com');
-
-webSocket.onmessage = (ev) => {
- console.log (ev)
-};
-
-webSocket.onopen = (ev) => {
- console.log (ev)
-}
diff --git a/cs/ccxt/api/bingx.cs b/cs/ccxt/api/bingx.cs
index e95ddbb0f4c5e..75528fd801017 100644
--- a/cs/ccxt/api/bingx.cs
+++ b/cs/ccxt/api/bingx.cs
@@ -766,6 +766,11 @@ public async Task