Repository: ccxt/ccxt
Pull request: fix(mexc): stop infinite loop caused by fetchLeverageTiers
It looks like `riskIncrVol` is only undefined if the market is scheduled to be delisted and can't have an order placed in it.

I wasn't really sure what a solution to this would be, because this might still be useful information if you are in a position or want to know some history, but nonetheless, it's not information that we can get, so I think this might be the best that we can do

Problem statement:
an endless loop after using fetch_leverage_tiers in mexc
### Operating System

Ubuntu 22.04

### Programming Languages

Python

### CCXT Version

4.1.1

### Description

hello
exchange=MEXC
When I used the “**fetch_leverage_tiers**”  in mexc.py, I encountered an endless loop and found that it was a problem in the “**parse_market_leverage_tiers**”. Specifically, when maxVol=5500 and riskIncrVol=0, the **while**  did not add any value to the “**floor**”, resulting in the **while**  not stopping,this is  a bug?

### Code

```python
      def parse_market_leverage_tiers(self, info, market=None):
        """
            @param info {object} Exchange response for 1 market
            @param market {object} CCXT market
        """
        maintenanceMarginRate = self.safe_string(info, 'maintenanceMarginRate')
        initialMarginRate = self.safe_string(info, 'initialMarginRate')
        maxVol = self.safe_string(info, 'maxVol')
        riskIncrVol = self.safe_string(info, 'riskIncrVol')
        riskIncrMmr = self.safe_string(info, 'riskIncrMmr')
        riskIncrImr = self.safe_string(info, 'riskIncrImr')
        floor = '0'
        tiers = []
        quoteId = self.safe_string(info, 'quoteCoin')
        while(Precise.string_lt(floor, maxVol)):
            cap = Precise.string_add(floor, riskIncrVol)
            tiers.append({
                'tier': self.parse_number(Precise.string_div(cap, riskIncrVol)),
                'currency': self.safe_currency_code(quoteId),
                'notionalFloor': self.parse_number(floor),
                'notionalCap': self.parse_number(cap),
                'maintenanceMarginRate': self.parse_number(maintenanceMarginRate),
                'maxLeverage': self.parse_number(Precise.string_div('1', initialMarginRate)),
                'info': info,
            })
            initialMarginRate = Precise.string_add(initialMarginRate, riskIncrImr)
            maintenanceMarginRate = Precise.string_add(maintenanceMarginRate, riskIncrMmr)
            floor = cap
        return tiers

```

```python
market = {
    "id": "BTC2_USDT",
    "symbol": "BTC2/USDT:USDT",
    "base": "BTC2",
    "quote": "USDT",
    "settle": "USDT",
    "baseId": "BTC2",
    "quoteId": "USDT",
    "settleId": "USDT",
    "type": "swap",
    "spot": false,
    "margin": false,
    "swap": true,
    "future": false,
    "option": false,
    "active": true,
    "contract": true,
    "linear": true,
    "inverse": false,
    "taker": 0.0002,
    "maker": 0.0,
    "contractSize": 10.0,
    "expiry": null,
    "expiryDatetime": null,
    "strike": null,
    "optionType": null,
    "precision": {
        "amount": 1.0,
        "price": 0.0001
    },
    "limits": {
        "leverage": {
            "min": 1.0,
            "max": 20.0
        },
        "amount": {
            "min": 1.0,
            "max": 5500.0
        },
        "price": {
            "min": null,
            "max": null
        },
        "cost": {
            "min": null,
            "max": null
        }
    },
    "info": {
        "symbol": "BTC2_USDT",
        "displayName": "BTC2_USDT永续",
        "displayNameEn": "BTC2_USDT PERPETUAL",
        "positionOpenType": "3",
        "baseCoin": "BTC2",
        "quoteCoin": "USDT",
        "baseCoinName": "BTC2",
        "quoteCoinName": "USDT",
        "futureType": "1",
        "settleCoin": "USDT",
        "contractSize": "10",
        "minLeverage": "1",
        "maxLeverage": "20",
        "priceScale": "4",
        "volScale": "0",
        "amountScale": "4",
        "priceUnit": "0.0001",
        "volUnit": "1",
        "minVol": "1",
        "maxVol": "5500",
        "bidLimitPriceRate": "0.2",
        "askLimitPriceRate": "0.2",
        "takerFeeRate": "0.0002",
        "makerFeeRate": "0",
        "maintenanceMarginRate": "0.004",
        "initialMarginRate": "0.05",
        "riskBaseVol": "0",
        "riskIncrVol": "0",
        "riskLongShortSwitch": "0",
        "riskIncrMmr": "0.004",
        "riskIncrImr": "0.004",
        "riskLevelLimit": "1",
        "priceCoefficientVariation": "0.05",
        "indexOrigin": [
            "MEXC"
        ],
        "state": "0",
        "isNew": false,
        "isHot": false,
        "isHidden": true,
        "conceptPlate": [],
        "riskLimitType": "BY_VOLUME",
        "maxNumOrders": [
            "200",
            "50"
        ],
        "marketOrderMaxLevel": "20",
        "marketOrderPriceLimitRate1": "0.2",
        "marketOrderPriceLimitRate2": "0.005",
        "triggerProtect": "0.1",
        "appraisal": "0",
        "showAppraisalCountdown": "0",
        "automaticDelivery": "1",
        "deliveryTime": "1712127600853",
        "deliveryPriceTrend": "2",
        "apiAllowed": false,
        "depthStepList": [
            "0.0001",
            "0.001",
            "0.01"
        ],
        "limitMaxVol": "5500",
        "threshold": "0",
        "baseCoinIconUrl": ", "id": "449"
    }
}

info = {
    "symbol": "BTC2_USDT",
    "displayName": "BTC2_USDT永续",
    "displayNameEn": "BTC2_USDT PERPETUAL",
    "positionOpenType": "3",
    "baseCoin": "BTC2",
    "quoteCoin": "USDT",
    "baseCoinName": "BTC2",
    "quoteCoinName": "USDT",
    "futureType": "1",
    "settleCoin": "USDT",
    "contractSize": "10",
    "minLeverage": "1",
    "maxLeverage": "20",
    "priceScale": "4",
    "volScale": "0",
    "amountScale": "4",
    "priceUnit": "0.0001",
    "volUnit": "1",
    "minVol": "1",
    "maxVol": "5500",
    "bidLimitPriceRate": "0.2",
    "askLimitPriceRate": "0.2",
    "takerFeeRate": "0.0002",
    "makerFeeRate": "0",
    "maintenanceMarginRate": "0.004",
    "initialMarginRate": "0.05",
    "riskBaseVol": "0",
    "riskIncrVol": "0",
    "riskLongShortSwitch": "0",
    "riskIncrMmr": "0.004",
    "riskIncrImr": "0.004",
    "riskLevelLimit": "1",
    "priceCoefficientVariation": "0.05",
    "indexOrigin": [
        "MEXC"
    ],
    "state": "0",
    "isNew": false,
    "isHot": false,
    "isHidden": true,
    "conceptPlate": [],
    "riskLimitType": "BY_VOLUME",
    "maxNumOrders": [
        "200",
        "50"
    ],
    "marketOrderMaxLevel": "20",
    "marketOrderPriceLimitRate1": "0.2",
    "marketOrderPriceLimitRate2": "0.005",
    "triggerProtect": "0.1",
    "appraisal": "0",
    "showAppraisalCountdown": "0",
    "automaticDelivery": "1",
    "deliveryTime": "1712127600853",
    "deliveryPriceTrend": "2",
    "apiAllowed": false,
    "depthStepList": [
        "0.0001",
        "0.001",
        "0.01"
    ],
    "limitMaxVol": "5500",
    "threshold": "0",
    "baseCoinIconUrl": "",
    "id": "449"
}
```

Hints from issue discussion:
@Nuokaqiao, I think you're right, will fix this asap.

Commit message: fix(mexc): stop infinite loop caused by fetchLeverageTiers, fixes: #22017

Patch to review:
diff --git a/ts/src/mexc.ts b/ts/src/mexc.ts
index 5c9793262e64f..98e6c34b7602d 100644
--- a/ts/src/mexc.ts
+++ b/ts/src/mexc.ts
@@ -4295,8 +4295,9 @@ export default class mexc extends Exchange {
         /**
          * @method
          * @name mexc#fetchLeverageTiers
-         * @description retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes
-         * @param {string[]|undefined} symbols list of unified market symbols
+         * @description retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes, if a market has a leverage tier of 0, then the leverage tiers cannot be obtained for this market
+         * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-the-contract-information
+         * @param {string[]} [symbols] list of unified market symbols
          * @param {object} [params] extra parameters specific to the exchange API endpoint
          * @returns {object} a dictionary of [leverage tiers structures]{@link https://docs.ccxt.com/#/?id=leverage-tiers-structure}, indexed by market symbols
          */
@@ -4353,10 +4354,44 @@ export default class mexc extends Exchange {
     }
 
     parseMarketLeverageTiers (info, market: Market = undefined) {
-        /**
-            @param info {object} Exchange response for 1 market
-            @param market {object} CCXT market
-         */
+        //
+        //    {
+        //        "symbol": "BTC_USDT",
+        //        "displayName": "BTC_USDT永续",
+        //        "displayNameEn": "BTC_USDT SWAP",
+        //        "positionOpenType": 3,
+        //        "baseCoin": "BTC",
+        //        "quoteCoin": "USDT",
+        //        "settleCoin": "USDT",
+        //        "contractSize": 0.0001,
+        //        "minLeverage": 1,
+        //        "maxLeverage": 125,
+        //        "priceScale": 2,
+        //        "volScale": 0,
+        //        "amountScale": 4,
+        //        "priceUnit": 0.5,
+        //        "volUnit": 1,
+        //        "minVol": 1,
+        //        "maxVol": 1000000,
+        //        "bidLimitPriceRate": 0.1,
+        //        "askLimitPriceRate": 0.1,
+        //        "takerFeeRate": 0.0006,
+        //        "makerFeeRate": 0.0002,
+        //        "maintenanceMarginRate": 0.004,
+        //        "initialMarginRate": 0.008,
+        //        "riskBaseVol": 10000,
+        //        "riskIncrVol": 200000,
+        //        "riskIncrMmr": 0.004,
+        //        "riskIncrImr": 0.004,
+        //        "riskLevelLimit": 5,
+        //        "priceCoefficientVariation": 0.1,
+        //        "indexOrigin": ["BINANCE","GATEIO","HUOBI","MXC"],
+        //        "state": 0, // 0 enabled, 1 delivery, 2 completed, 3 offline, 4 pause
+        //        "isNew": false,
+        //        "isHot": true,
+        //        "isHidden": false
+        //    }
+        //
         let maintenanceMarginRate = this.safeString (info, 'maintenanceMarginRate');
         let initialMarginRate = this.safeString (info, 'initialMarginRate');
         const maxVol = this.safeString (info, 'maxVol');
@@ -4366,6 +4401,19 @@ export default class mexc extends Exchange {
         let floor = '0';
         const tiers = [];
         const quoteId = this.safeString (info, 'quoteCoin');
+        if (riskIncrVol === '0') {
+            return [
+                {
+                    'tier': 0,
+                    'currency': this.safeCurrencyCode (quoteId),
+                    'notionalFloor': undefined,
+                    'notionalCap': undefined,
+                    'maintenanceMarginRate': undefined,
+                    'maxLeverage': undefined,
+                    'info': info,
+                },
+            ];
+        }
         while (Precise.stringLt (floor, maxVol)) {
             const cap = Precise.stringAdd (floor, riskIncrVol);
             tiers.push ({


Code review comments:
diff_hunk: @@ -4366,6 +4401,19 @@ export default class mexc extends Exchange {
         let floor = '0';
         const tiers = [];
         const quoteId = this.safeString (info, 'quoteCoin');
+        if (riskIncrVol === '0') {
+            return [
+                {
+                    'tier': 0,
+                    'currency': this.safeCurrencyCode (quoteId),
+                    'notionalFloor': undefined,
+                    'notionalCap': undefined,
+                    'maintenanceMarginRate': undefined,
+                    'maxLeverage': undefined,
path: ts/src/mexc.ts
text: @user1:
should we add maxLeverage here?

```
info: {
        symbol: 'BTC2_USDT',
        displayName: 'BTC2_USDT永续',
        displayNameEn: 'BTC2_USDT PERPETUAL',
        positionOpenType: '3',
        baseCoin: 'BTC2',
        quoteCoin: 'USDT',
        baseCoinName: 'BTC2',
        quoteCoinName: 'USDT',
        futureType: '1',
        settleCoin: 'USDT',
        contractSize: '10',
        minLeverage: '1',
        maxLeverage: '20',
        priceScale: '4',
        volScale: '0',
        amountScale: '4',
        priceUnit: '0.0001',
        volUnit: '1',
        minVol: '1',
        maxVol: '5500',
        bidLimitPriceRate: '0.2',
        askLimitPriceRate: '0.2',
        takerFeeRate: '0.0002',
        makerFeeRate: '0',
        maintenanceMarginRate: '0.004',
        initialMarginRate: '0.05',
        riskBaseVol: '0',
        riskIncrVol: '0',
        riskLongShortSwitch: '0',
        riskIncrMmr: '0.004',
        riskIncrImr: '0.004',
        riskLevelLimit: '1',
        priceCoefficientVariation: '0.05',
        indexOrigin: [ 'MEXC' ],
        state: '0',
        isNew: false,
        isHot: false,
        isHidden: true,
        conceptPlate: [],
        riskLimitType: 'BY_VOLUME',
        maxNumOrders: [ 200, 50 ],
        marketOrderMaxLevel: '20',
        marketOrderPriceLimitRate1: '0.2',
        marketOrderPriceLimitRate2: '0.005',
        triggerProtect: '0.1',
        appraisal: '0',
        showAppraisalCountdown: '0',
        automaticDelivery: '1',
        deliveryTime: '1712127600853',
        deliveryPriceTrend: '2',
        apiAllowed: false,
        depthStepList: [ '0.0001', '0.001', '0.01' ],
        limitMaxVol: '5500',
        threshold: '0',
        baseCoinIconUrl: '',
        id: '449'
      }
```