Repository: ccxt/ccxt
Pull request: binance - precisions
binance's internal functionality related to currencies (i.e. `transfer`) does not work, because `currencyToPrecision` is not available (because the currency-wide precision is `undefined` at this moment)

fix #14344
fix #15691
etc.

Problem statement:
binance currency_to_precision won't work - code precision is None
Hi,

I'm using ccxt version 1.79.94.

It appears that recently the currency_to_precision stopped working properly.

Looking into it, I have found that the self.currencies[code]["precision"] entry is always None (even though I'm using a valid API key pair) so the currency_to_precision is not running the decimal_to_precision function.

Any idea what might be the cause of it?

Thanks.



how to borrow coins in Binance 
- OS: windows11
- Programming Language version: python v2022.18.2
- CCXT version:22.3


firstly, thanks for reading this article.

i tried to use margin system in Binance.

```python
exchange = ccxt.binance(config={    
        'api_key' : ',
        'secret' : '})

exchange.set_margin_mode("isolated",symbol="XRP/USDT")
exchange.borrow_margin(code="XRP",amount="100",symbol="XRP/USDT",params={})
```

```
NotSupported: binance setMarginMode() supports linear and inverse contracts only
```

please let me know example of using margin borrow 

thanks

Hints from issue discussion:
Binance

On Thu, Jul 14, 2022, 09:10 Sam Germain ***@***.***> wrote:

> what exchange are you using?
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/ccxt/ccxt/issues/14344#issuecomment-1184036069>, or
> unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AGM3H6YT3WEYE4G3IXK6R63VT6VNTANCNFSM53Q6AXRQ>
> .
> You are receiving this because you authored the thread.Message ID:
> ***@***.***>
>

I went through a few versions over the past 9 months, all the way back to `1.58.67` and it doesn't look like `self.currencies[code]["precision"]` was assigned on any of them

Can you be more specific about what method you are trying to use exactly?
@kooomix please update to latest ccxt version and re-test. if that will solve issue or not, let us know
just checked it with version '1.91.20' and still getting the same issue.

example code:

exchange = ccxt.binance({
            'apiKey': api_key,
            'secret': api_secret,
            'enableRateLimit':True,
            'options': {
                'newOrderRespType': 'FULL'
        }})
markets = exchange.load_markets()

exchange.currency_to_precision("USDT", float("361.4492880000001"))

result is: 361.4492880000001 while I would expect to get 361.449288

I am getting None for exchange.currencies["USDT"]["precision"].  This causes the currency_to_precision function to run self.number_to_string instead of self.decimal_to_precision

Thanks.


@kooomix I've submited PR (see above reference) which will fix the issue, however, don't miss accidentally, you will need to do `exchange.load_markets()` at first (you are doing correctly now, so wait for PR to be merged)
Cool, thanks.

On Mon, Jul 25, 2022, 20:28 T. Todua ***@***.***> wrote:

> @kooomix <https://github.com/kooomix> I've submited PR (see above
> reference) which will fix the issue, however, don't miss accidentally, you
> will need to do exchange.load_markets() at first (you are doing correctly
> now, so wait for PR to be merged)
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/ccxt/ccxt/issues/14344#issuecomment-1194387137>, or
> unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AGM3H66WRFUAYBEF3VXZDODVV3FB7ANCNFSM53Q6AXRQ>
> .
> You are receiving this because you were mentioned.Message ID:
> ***@***.***>
>

@kooomix you just have to be authenticated for it to work and have non-None precision, that is because Binance's fetchCurrencies endpoint requires authentication. Let us know if that does not help.
As I mentioned in the post, I'm having this issue under a valid API key... Any specific parameters I should use when initializing the Binance exchange object to make it work?
@kooomix as you can see from the verbose output and from the `currency['info']` Binance does not return a per-currency precision, they have a per-network precision instead:

```Python
import ccxt
from pprint import pprint
print('CCXT Version:', ccxt.__version__)
exchange = ccxt.binance({
    'apiKey': 'YOUR_API_KEY',
    'secret': 'YOUR_SECRET',
})
# exchange.verbose = True  # uncomment for debug output
markets = exchange.load_markets()
currency = exchange.currencies['USDT']
pprint(currency)
```

So if you want to format a number to a per-network precision, you could do something like this:

```Python
import ccxt
from pprint import pprint
print('CCXT Version:', ccxt.__version__)
exchange = ccxt.binance({
    'apiKey': 'YOUR_API_KEY',
    'secret': 'YOUR_SECRET',
})
# exchange.verbose = True  # uncomment for debug output
markets = exchange.load_markets()
currency = exchange.currencies['USDT']
networks_by_network = exchange.index_by(currency['networks'], 'network')
network = networks_by_network['TRX']
precision = exchange.precision_from_string(network['withdrawIntegerMultiple'])
amount = float("361.4492880000001")
formatted_amount = exchange.decimal_to_precision(amount, ccxt.ROUND, precision, exchange.precisionMode, exchange.paddingMode)
print(formatted_amount)
```

Also, please, note that we're going to refactor Binance's networks from a list to dictionary in the coming days.
Hope the above helps, let us know if not.

Commit message: decimal precisions

Patch to review:
diff --git a/js/binance.js b/js/binance.js
index 13d2fb9a61461..096540d66e7c1 100644
--- a/js/binance.js
+++ b/js/binance.js
@@ -1311,88 +1311,105 @@ module.exports = class binance extends Exchange {
         const result = {};
         for (let i = 0; i < response.length; i++) {
             //
-            //     {
-            //         coin: 'LINK',
-            //         depositAllEnable: true,
-            //         withdrawAllEnable: true,
-            //         name: 'ChainLink',
-            //         free: '0.06168',
-            //         locked: '0',
-            //         freeze: '0',
-            //         withdrawing: '0',
-            //         ipoing: '0',
-            //         ipoable: '0',
-            //         storage: '0',
-            //         isLegalMoney: false,
-            //         trading: true,
-            //         networkList: [
-            //             {
-            //                 network: 'BNB',
-            //                 coin: 'LINK',
-            //                 withdrawIntegerMultiple: '0',
-            //                 isDefault: false,
-            //                 depositEnable: true,
-            //                 withdrawEnable: true,
-            //                 depositDesc: '',
-            //                 withdrawDesc: '',
-            //                 specialTips: 'Both a MEMO and an Address are required to successfully deposit your LINK BEP2 tokens to Binance.',
-            //                 name: 'BEP2',
-            //                 resetAddressStatus: false,
-            //                 addressRegex: '^(bnb1)[0-9a-z]{38}$',
-            //                 memoRegex: '^[0-9A-Za-z\\-_]{1,120}$',
-            //                 withdrawFee: '0.002',
-            //                 withdrawMin: '0.01',
-            //                 withdrawMax: '9999999',
-            //                 minConfirm: 1,
-            //                 unLockConfirm: 0
-            //             },
-            //             {
-            //                 network: 'BSC',
-            //                 coin: 'LINK',
-            //                 withdrawIntegerMultiple: '0.00000001',
-            //                 isDefault: false,
-            //                 depositEnable: true,
-            //                 withdrawEnable: true,
-            //                 depositDesc: '',
-            //                 withdrawDesc: '',
-            //                 specialTips: '',
-            //                 name: 'BEP20 (BSC)',
-            //                 resetAddressStatus: false,
-            //                 addressRegex: '^(0x)[0-9A-Fa-f]{40}$',
-            //                 memoRegex: '',
-            //                 withdrawFee: '0.005',
-            //                 withdrawMin: '0.01',
-            //                 withdrawMax: '9999999',
-            //                 minConfirm: 15,
-            //                 unLockConfirm: 0
-            //             },
-            //             {
-            //                 network: 'ETH',
-            //                 coin: 'LINK',
-            //                 withdrawIntegerMultiple: '0.00000001',
-            //                 isDefault: true,
-            //                 depositEnable: true,
-            //                 withdrawEnable: true,
-            //                 depositDesc: '',
-            //                 withdrawDesc: '',
-            //                 name: 'ERC20',
-            //                 resetAddressStatus: false,
-            //                 addressRegex: '^(0x)[0-9A-Fa-f]{40}$',
-            //                 memoRegex: '',
-            //                 withdrawFee: '0.34',
-            //                 withdrawMin: '0.68',
-            //                 withdrawMax: '0',
-            //                 minConfirm: 12,
-            //                 unLockConfirm: 0
-            //             }
-            //         ]
-            //     }
+            //    {
+            //        "coin": "LINK",
+            //        "depositAllEnable": true,
+            //        "withdrawAllEnable": true,
+            //        "name": "ChainLink",
+            //        "free": "0",
+            //        "locked": "0",
+            //        "freeze": "0",
+            //        "withdrawing": "0",
+            //        "ipoing": "0",
+            //        "ipoable": "0",
+            //        "storage": "0",
+            //        "isLegalMoney": false,
+            //        "trading": true,
+            //        "networkList": [
+            //            {
+            //                "network": "BSC",
+            //                "coin": "LINK",
+            //                "withdrawIntegerMultiple": "0.00000001",
+            //                "isDefault": false,
+            //                "depositEnable": true,
+            //                "withdrawEnable": true,
+            //                "depositDesc": "",
+            //                "withdrawDesc": "",
+            //                "specialTips": "",
+            //                "specialWithdrawTips": "The network you have selected is BSC. Please ensure that the withdrawal address supports the Binance Smart Chain network. You will lose your assets if the chosen platform does not support retrievals.",
+            //                "name": "BNB Smart Chain (BEP20)",
+            //                "resetAddressStatus": false,
+            //                "addressRegex": "^(0x)[0-9A-Fa-f]{40}$",
+            //                "addressRule": "",
+            //                "memoRegex": "",
+            //                "withdrawFee": "0.012",
+            //                "withdrawMin": "0.024",
+            //                "withdrawMax": "9999999999.99999999",
+            //                "minConfirm": "15",
+            //                "unLockConfirm": "0",
+            //                "sameAddress": false,
+            //                "estimatedArrivalTime": "5",
+            //                "busy": false,
+            //                "country": "AE,BINANCE_BAHRAIN_BSC"
+            //            },
+            //            {
+            //                "network": "BNB",
+            //                "coin": "LINK",
+            //                "withdrawIntegerMultiple": "0.00000001",
+            //                "isDefault": false,
+            //                "depositEnable": true,
+            //                "withdrawEnable": true,
+            //                "depositDesc": "",
+            //                "withdrawDesc": "",
+            //                "specialTips": "Both a MEMO and an Address are required to successfully deposit your LINK BEP2 tokens to Binance.",
+            //                "specialWithdrawTips": "",
+            //                "name": "BNB Beacon Chain (BEP2)",
+            //                "resetAddressStatus": false,
+            //                "addressRegex": "^(bnb1)[0-9a-z]{38}$",
+            //                "addressRule": "",
+            //                "memoRegex": "^[0-9A-Za-z\\-_]{1,120}$",
+            //                "withdrawFee": "0.002",
+            //                "withdrawMin": "0.01",
+            //                "withdrawMax": "10000000000",
+            //                "minConfirm": "1",
+            //                "unLockConfirm": "0",
+            //                "sameAddress": true,
+            //                "estimatedArrivalTime": "5",
+            //                "busy": false,
+            //                "country": "AE,BINANCE_BAHRAIN_BSC"
+            //            },
+            //            {
+            //                "network": "ETH",
+            //                "coin": "LINK",
+            //                "withdrawIntegerMultiple": "0.00000001",
+            //                "isDefault": true,
+            //                "depositEnable": true,
+            //                "withdrawEnable": true,
+            //                "depositDesc": "",
+            //                "withdrawDesc": "",
+            //                "name": "Ethereum (ERC20)",
+            //                "resetAddressStatus": false,
+            //                "addressRegex": "^(0x)[0-9A-Fa-f]{40}$",
+            //                "addressRule": "",
+            //                "memoRegex": "",
+            //                "withdrawFee": "0.55",
+            //                "withdrawMin": "1.1",
+            //                "withdrawMax": "10000000000",
+            //                "minConfirm": "12",
+            //                "unLockConfirm": "0",
+            //                "sameAddress": false,
+            //                "estimatedArrivalTime": "5",
+            //                "busy": false,
+            //                "country": "AE,BINANCE_BAHRAIN_BSC"
+            //            }
+            //        ]
+            //    }
             //
             const entry = response[i];
             const id = this.safeString (entry, 'coin');
             const name = this.safeString (entry, 'name');
             const code = this.safeCurrencyCode (id);
-            const precision = undefined;
+            let minPrecision = undefined;
             let isWithdrawEnabled = true;
             let isDepositEnabled = true;
             const networkList = this.safeValue (entry, 'networkList', []);
@@ -1412,14 +1429,24 @@ module.exports = class binance extends Exchange {
                 if (isDefault || (fee === undefined)) {
                     fee = withdrawFee;
                 }
+                const precisionTick = this.safeString (networkItem, 'withdrawIntegerMultiple');
+                // avoid zero values, which are mostly from fiat or leveraged tokens : https://github.com/ccxt/ccxt/pull/14902#issuecomment-1271636731
+                // so, when there is zero instead of i.e. 0.001, then we skip those cases, because we don't know the precision - it might be because of network is suspended or other reasons
+                if (!Precise.stringEq (precisionTick, '0')) {
+                    minPrecision = (minPrecision === undefined) ? precisionTick : Precise.stringMin (minPrecision, precisionTick);
+                }
             }
             const trading = this.safeValue (entry, 'trading');
             const active = (isWithdrawEnabled && isDepositEnabled && trading);
+            let finalPrecisionDigit = undefined;
+            if (minPrecision !== undefined) {
+                finalPrecisionDigit = this.parseNumber (this.numberToString (this.precisionFromString (minPrecision)));
+            }
             result[code] = {
                 'id': id,
                 'name': name,
                 'code': code,
-                'precision': precision,
+                'precision': finalPrecisionDigit,
                 'info': entry,
                 'active': active,
                 'deposit': isDepositEnabled,


Code review comments:
diff_hunk: @@ -1412,14 +1429,24 @@ module.exports = class binance extends Exchange {
                 if (isDefault || (fee === undefined)) {
                     fee = withdrawFee;
                 }
+                const precisionTick = this.safeString (networkItem, 'withdrawIntegerMultiple');
+                // avoid zero values, which are mostly from fiat or leveraged tokens : https://github.com/ccxt/ccxt/pull/14902#issuecomment-1271636731
+                // so, when there is zero instead of i.e. 0.001, then we skip those cases, because we don't know the precision - it might be because of network is suspended or other reasons
+                if (!Precise.stringEq (precisionTick, '0')) {
+                    minPrecision = (minPrecision === undefined) ? precisionTick : Precise.stringMin (minPrecision, precisionTick);
+                }
             }
             const trading = this.safeValue (entry, 'trading');
             const active = (isWithdrawEnabled && isDepositEnabled && trading);
+            let finalPrecisionDigit = undefined;
+            if (minPrecision !== undefined) {
+                finalPrecisionDigit = this.parseNumber (this.numberToString (this.precisionFromString (minPrecision)));
path: js/binance.js
text: @user1:
`finalPrecisionDigit` is a bit ambiguous; why not something like `maxDecimalPlaces` or smth?