Repository: ccxt/ccxt
Pull request: Binance :: unify symbols in a backwards compatible way [WIP]
fixes: https://github.com/ccxt/ccxt/issues/11121

- both market Ids and unified symbols can be used (as long as default type is correct)
- legacy support for `type = future` and `type = delivery` 
- small fix on `safeMarket`

Demo:

```
 n binanceusdm fetchTicker "BTCUSDT" --sandbox
binanceusdm.fetchTicker (BTCUSDT)
2022-12-27T18:00:55.084Z iteration 0 passed in 3258 ms

{
  symbol: 'BTC/USDT:USDT',
  timestamp: 1672164051674,
  datetime: '2022-12-27T18:00:51.674Z',
  high: 16980,
  low: 16709,
  bid: undefined,
  bidVolume: undefined,
  ask: undefined,
  askVolume: undefined,
  vwap: 16853.82,
  open: 16833.5,
  close: 16718,
  last: 16718,
  previousClose: undefined,
  change: -115.5,
  percentage: -0.686,
  average: 16775.75,
  baseVolume: 125305.888,
  quoteVolume: 2111882956.78,
  info: {
    symbol: 'BTCUSDT',
    priceChange: '-115.50',
    priceChangePercent: '-0.686',
    weightedAvgPrice: '16853.82',
    lastPrice: '16718.00',
```
```
n binance fetchTicker "BTCUSDT"           
Debugger attached.
2022-12-27T18:01:43.470Z
Node.js: v17.9.0
CCXT v2.4.66
binance.fetchTicker (BTCUSDT)
2022-12-27T18:01:47.315Z iteration 0 passed in 2058 ms

{
  symbol: 'BTC/USDT',
  timestamp: 1672164106737,
  datetime: '2022-12-27T18:01:46.737Z',
  high: 16972.83,
  low: 16620.04,
  bid: 16674.6,
  bidVolume: 0.00107,
  ask: 16674.69,
  askVolume: 0.18788,
  vwap: 16836.48618742,
  open: 16841.9,
  close: 16674.8,
  last: 16674.8,
  previousClose: 16841.9,
  change: -167.1,
  percentage: -0.992,
  average: 16758.35,
  baseVolume: 165610.1454,
  quoteVolume: 2788292925.523453,
  info: {
    symbol: 'BTCUSDT',
    priceChange: '-167.10000000',
    priceChangePercent: '-0.992',
    weightedAvgPrice: '16836.48618742',
    prevClosePrice: '16841.90000000',
    lastPrice: '16674.80000000',
    lastQty: '0.00611000',
    bidPrice: '16674.60000000',
    bidQty: '0.00107000',
    askPrice: '16674.69000000',
    askQty: '0.18788000',
    openPrice: '16841.90000000',
    highPrice: '16972.83000000',
    lowPrice: '16620.04000000',
    volume: '165610.14540000',
    quoteVolume: '2788292925.52345310',
    openTime: '1672077706737',
    closeTime: '1672164106737',
    firstId: '2384561850',
    lastId: '2389449142',
    count: '4887293'
  }
```

Problem statement:
Uniform `market["type"]` field semantics
The `market["type"]` field returned by `Exchange.fetch_markets()` seems to have different semantics across exchanges.

Here is a comparison of the market type literals across 4 exchanges:

| Exchange | Spot   | Margin   | Delivery futures | Perpetual swaps | Options  |
|----------|--------|----------|------------------|-----------------|----------|
| **Binance**  | 'spot' | 'margin' | 'delivery'       | 'future'        | -        |
| **FTX**      | 'spot' | -        | 'future'         | 'swap'          | -        |
| **OKEx**     | 'spot' | -        | 'futures'        | 'swap'          | 'option' |
| **Gate.io**  | 'spot' | 'margin' | 'future'         | 'swap'          | 'option  |

Is there a plan to make the market type part of the public API?
Is there an exchange-independent way to determine if a symbol is a delivery future or a swap?

Thanks!

Hints from issue discussion:
> Is there a plan to make the market type part of the public API?

Yes, working on that now: https://github.com/ccxt/ccxt/issues/10931

> Is there an exchange-independent way to determine if a symbol is a delivery future or a swap?

We will unify the `type` values and will add that to the docs.
I'd suggest we call expiring contracts `future` and swaps `swap` everywhere
btw this is a separate issue from `defaultType`, we won't be able to change that so easily
as sidenote, we might aim to change `type` key with other (i.e. `defaultType` or whatever) to have a failsafe stable key to avoid conflicting. If 'type' is widely used at this moment, then it might stay as an alias for 1 year or so, while everywhere in docs/examples, `defaultType` should be used to make users to forget about `type` slowly (while for 1 year, still supporting it as alias)
Hello!
I noticed another bug related to this issue.

On Binance USDⓈ-M perpetual swaps and delivery futures have the same type: `future`.

Steps to reproduce:

```python
exchange = ccxt.binanceusdm()
markets = exchange.load_markets()
print(f"BTC/USDT type       = {markets['BTC/USDT']['type']}")
print(f"BTCUSDT_220325 type = {markets['BTCUSDT_220325']['type']}")

# BTC/USDT type       = future
# BTCUSDT_220325 type = future
```
@mattepozz yep, that is a known issue that we are in the process of solving, it is there for historical reasons, cause Binance named their API sections that way before, and changed that naming since then.
@kroitor Thanks for the info!
Also in this example, delivery is ture, no swap field exists. It's not consistent with document.
```
    'spot':     true,         // whether the market is a spot market
    'margin':   true,         // whether the market is a margin market
    'future':   false,        // whether the market is a expiring future
    'swap':     false,        // whether the market is a perpetual swap
    'option':   false,        // whether the market is an option contract
```
```json
{
  "active": true,
  "base": "BTC",
  "baseId": "BTC",
  "contractSize": 100,
  "delivery": true,
  "exchange": "binance",
  "expiry": null,
  "expiryDatetime": null,
  "future": false,
  "id": "BTCUSD_PERP",
  "info": {
    "baseAsset": "BTC",
    "baseAssetPrecision": "8",
    "contractSize": "100",
    "contractStatus": "TRADING",
    "contractType": "PERPETUAL",
    "deliveryDate": "4133404800000",
    "equalQtyPrecision": "4",
    "filters": [
      {
        "filterType": "PRICE_FILTER",
        "maxPrice": "4520958",
        "minPrice": "1000",
        "tickSize": "0.1"
      },
      {
        "filterType": "LOT_SIZE",
        "maxQty": "1000000",
        "minQty": "1",
        "stepSize": "1"
      },
      {
        "filterType": "MARKET_LOT_SIZE",
        "maxQty": "60000",
        "minQty": "1",
        "stepSize": "1"
      },
      {
        "filterType": "MAX_NUM_ORDERS",
        "limit": "200"
      },
      {
        "filterType": "MAX_NUM_ALGO_ORDERS",
        "limit": "100"
      },
      {
        "filterType": "PERCENT_PRICE",
        "multiplierDecimal": "4",
        "multiplierDown": "0.9500",
        "multiplierUp": "1.0500"
      }
    ],
    "liquidationFee": "0.015000",
    "maintMarginPercent": "2.5000",
    "marginAsset": "BTC",
    "marketTakeBound": "0.05",
    "maxMoveOrderLimit": "10000",
    "onboardDate": "1597042800000",
    "orderTypes": [
      "LIMIT",
      "MARKET",
      "STOP",
      "STOP_MARKET",
      "TAKE_PROFIT",
      "TAKE_PROFIT_MARKET",
      "TRAILING_STOP_MARKET"
    ],
    "pair": "BTCUSD",
    "pricePrecision": "1",
    "quantityPrecision": "0",
    "quoteAsset": "USD",
    "quotePrecision": "8",
    "requiredMarginPercent": "5.0000",
    "symbol": "BTCUSD_PERP",
    "timeInForce": [
      "GTC",
      "IOC",
      "FOK",
      "GTX"
    ],
    "triggerProtect": "0.0500",
    "underlyingSubType": [],
    "underlyingType": "COIN"
  },
  "inverse": true,
  "limits": {
    "amount": {
      "max": 1000000,
      "min": 1
    },
    "cost": {
      "max": null,
      "min": null
    },
    "market": {
      "max": 60000,
      "min": 1
    },
    "price": {
      "max": 4520958,
      "min": 1000
    }
  },
  "linear": false,
  "lowercaseId": "btcusd_perp",
  "maker": 0.0001,
  "margin": false,
  "precision": {
    "amount": 0,
    "base": 8,
    "price": 1,
    "quote": 8
  },
  "quote": "USD",
  "quoteId": "USD",
  "settle": "BTC",
  "settleId": "BTC",
  "spot": false,
  "symbol": "BTC/USD",
  "taker": 0.0005,
  "type": "delivery"
}
```
In this example, both linear and inverse are null. But linear should be true and inverse should be false instead.
```json
{
  "active": false,
  "base": "WPR",
  "baseId": "wpr",
  "contract": false,
  "contractSize": null,
  "exchange": "huobi",
  "expiry": null,
  "expiryDatetime": null,
  "future": false,
  "id": "wpreth",
  "info": {
    "amount-precision": "2",
    "api-trading": "enabled",
    "base-currency": "wpr",
    "buy-market-max-order-value": "270",
    "limit-order-max-buy-amt": "120000000",
    "limit-order-max-order-amt": "120000000",
    "limit-order-max-sell-amt": "120000000",
    "limit-order-min-order-amt": "0.01",
    "max-order-amt": "120000000",
    "min-order-amt": "0.01",
    "min-order-value": "0.001",
    "price-precision": "8",
    "quote-currency": "eth",
    "sell-market-max-order-amt": "12000000",
    "sell-market-min-order-amt": "0.1",
    "state": "offline",
    "symbol": "wpreth",
    "symbol-partition": "innovation",
    "tags": "",
    "value-precision": "8"
  },
  "inverse": null,
  "limits": {
    "amount": {
      "max": 120000000,
      "min": 0.01
    },
    "cost": {
      "max": null,
      "min": 0.001
    },
    "leverage": {
      "max": 1,
      "superMax": 1
    },
    "price": {
      "max": null,
      "min": 1e-08
    }
  },
  "linear": null,
  "maker": 0.002,
  "precision": {
    "amount": 0.01,
    "cost": 1e-08,
    "price": 1e-08
  },
  "quote": "ETH",
  "quoteId": "eth",
  "settle": null,
  "settleId": null,
  "spot": true,
  "swap": false,
  "symbol": "WPR/ETH",
  "taker": 0.002,
  "type": "spot"
}
```
> 

That's a spot market, `linear` and `inverse` refer to the settle currency. A linear market means that the trades are settled in the quote currency, and an inverse market means that the trades are settled in the base currency. Settle currencies only exist for `contract` or `derivative` markets. In a derivative market, you exchange the settle currency for contracts; if you aren't receiving contracts, then you receive the settle currency, and linear/inverse refers to what currency you receive. A spot market exchanges one currency for another, there is not settle currency, if you are selling the base, you receive the quote, if you are selling the quote, you receive the base, so the concept of linear/inverse doesn't apply.

Commit message: add support for aliases

Patch to review:
diff --git a/js/binance.js b/js/binance.js
index aaa63fd177bb2..0cf947fe948f2 100644
--- a/js/binance.js
+++ b/js/binance.js
@@ -1,4 +1,3 @@
-'use strict';
 
 //  ---------------------------------------------------------------------------
 
@@ -788,7 +787,7 @@ module.exports = class binance extends Exchange {
                     'taker': this.parseNumber ('0.001'),
                     'maker': this.parseNumber ('0.001'),
                 },
-                'future': {
+                'linear': {
                     'trading': {
                         'feeSide': 'quote',
                         'tierBased': true,
@@ -823,7 +822,7 @@ module.exports = class binance extends Exchange {
                         },
                     },
                 },
-                'delivery': {
+                'inverse': {
                     'trading': {
                         'feeSide': 'base',
                         'tierBased': true,
@@ -867,10 +866,12 @@ module.exports = class binance extends Exchange {
             'precisionMode': DECIMAL_PLACES,
             // exchange-specific options
             'options': {
+                'fetchMarkets': [ 'spot', 'linear', 'inverse' ],
                 'fetchCurrencies': true, // this is a private call and it requires API keys
                 // 'fetchTradesMethod': 'publicGetAggTrades', // publicGetTrades, publicGetHistoricalTrades
                 'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel
                 'defaultType': 'spot', // 'spot', 'future', 'margin', 'delivery'
+                'defaultSubType': undefined, // 'linear', 'inverse'
                 'hasAlreadyAuthenticatedSuccessfully': false,
                 'warnOnFetchOpenOrdersWithoutSymbol': true,
                 // not an error
@@ -900,15 +901,17 @@ module.exports = class binance extends Exchange {
                     'funding': 'FUNDING',
                     'margin': 'MARGIN',
                     'cross': 'MARGIN',
-                    'future': 'UMFUTURE',
-                    'delivery': 'CMFUTURE',
+                    'future': 'UMFUTURE', // backwards compatibility
+                    'delivery': 'CMFUTURE', // backwards compatbility
+                    'linear': 'UMFUTURE',
+                    'inverse': 'CMFUTURE',
                 },
                 'accountsById': {
                     'MAIN': 'spot',
                     'FUNDING': 'funding',
                     'MARGIN': 'margin',
-                    'UMFUTURE': 'future',
-                    'CMFUTURE': 'delivery',
+                    'UMFUTURE': 'linear',
+                    'CMFUTURE': 'inverse',
                 },
                 'networks': {
                     'ERC20': 'ETH',
@@ -1373,6 +1376,63 @@ module.exports = class binance extends Exchange {
         });
     }
 
+    isInverse (type, subType = undefined) {
+        if (subType === undefined) {
+            return type === 'delivery';
+        } else {
+            return subType === 'inverse';
+        }
+    }
+
+    isLinear (type, subType = undefined) {
+        if (subType === undefined) {
+            return type === 'future';
+        } else {
+            return subType === 'linear';
+        }
+    }
+
+    market (symbol) {
+        if (this.markets === undefined) {
+            throw new ExchangeError (this.id + ' markets not loaded');
+        }
+        // defaultType has legacy support on binance
+        let defaultType = this.safeString2 (this.options, 'defaultSubType', 'defaultType');
+        const isLegacyLinear = defaultType === 'future';
+        const isLegacyInverse = defaultType === 'delivery';
+        const isLegacy = isLegacyLinear || isLegacyInverse;
+        if (typeof symbol === 'string') {
+            if (symbol in this.markets) {
+                const market = this.markets[symbol];
+                // begin diff
+                if (isLegacy && market['spot']) {
+                    const settle = isLegacyLinear ? market['quote'] : market['base'];
+                    return super.market (symbol + ':' + settle);
+                } else {
+                    return market;
+                }
+                // end diff
+            } else if (symbol in this.markets_by_id) {
+                const markets = this.markets_by_id[symbol];
+                // begin diff
+                const filter = {
+                    'future': 'linear',
+                    'delivery': 'inverse',
+                };
+                defaultType = this.safeString (filter, defaultType, defaultType);
+                // end diff
+                for (let i = 0; i < markets.length; i++) {
+                    const market = markets[i];
+                    if (market[defaultType]) {
+                        return market;
+                    }
+                }
+                return markets[0];
+            }
+        }
+        throw new BadSymbol (this.id + ' does not have market symbol ' + symbol);
+    }
+
     costToPrecision (symbol, cost) {
         return this.decimalToPrecision (cost, TRUNCATE, this.markets[symbol]['precision']['quote'], this.precisionMode, this.paddingMode);
     }
@@ -1401,10 +1461,12 @@ module.exports = class binance extends Exchange {
         const defaultType = this.safeString2 (this.options, 'fetchTime', 'defaultType', 'spot');
         const type = this.safeString (params, 'type', defaultType);
         const query = this.omit (params, 'type');
+        let subType = undefined;
+        [ subType, params ] = this.handleSubTypeAndParams ('fetchTime', undefined, params);
         let method = 'publicGetTime';
-        if (type === 'future') {
+        if (this.isLinear (type, subType)) {
             method = 'fapiPublicGetTime';
-        } else if (type === 'delivery') {
+        } else if (this.isInverse (type, subType)) {
             method = 'dapiPublicGetTime';
         }
         const response = await this[method] (query);
@@ -1596,23 +1658,27 @@ module.exports = class binance extends Exchange {
          * @param {object} params extra parameters specific to the exchange api endpoint
          * @returns {[object]} an array of objects representing market data
          */
-        const defaultType = this.safeString2 (this.options, 'fetchMarkets', 'defaultType', 'spot');
-        const type = this.safeString (params, 'type', defaultType);
-        const query = this.omit (params, 'type');
-        const spot = (type === 'spot');
-        const margin = (type === 'margin');
-        const future = (type === 'future');
-        const delivery = (type === 'delivery');
-        if ((!spot) && (!margin) && (!future) && (!delivery)) {
-            throw new ExchangeError (this.id + " does not support '" + type + "' type, set exchange.options['defaultType'] to 'spot', 'margin', 'delivery' or 'future'"); // eslint-disable-line quotes
-        }
-        let method = 'publicGetExchangeInfo';
-        if (future) {
-            method = 'fapiPublicGetExchangeInfo';
-        } else if (delivery) {
-            method = 'dapiPublicGetExchangeInfo';
+        let promises = [];
+        const fetchMarkets = this.safeValue (this.options, 'fetchMarkets', [ 'spot', 'linear', 'inverse' ]);
+        for (let i = 0; i < fetchMarkets.length; i++) {
+            const marketType = fetchMarkets[i];
+            if (marketType === 'spot') {
+                promises.push (this.publicGetExchangeInfo (params));
+            } else if (marketType === 'linear') {
+                promises.push (this.fapiPublicGetExchangeInfo (params));
+            } else if (marketType === 'inverse') {
+                promises.push (this.dapiPublicGetExchangeInfo (params));
+            } else {
+                throw new ExchangeError (this.id + ' fetchMarkets() this.options fetchMarkets "' + marketType + '" is not a supported market type');
+            }
         }
-        const response = await this[method] (query);
+        promises = await Promise.all (promises);
+        const spotMarkets = this.safeValue (promises[0], 'symbols', []);
+        const futureMarkets = this.safeValue (promises[1], 'symbols', []);
+        const deliveryMarkets = this.safeValue (promises[2], 'symbols', []);
+        let markets = spotMarkets;
+        markets = this.arrayConcat (markets, futureMarkets);
+        markets = this.arrayConcat (markets, deliveryMarkets);
         //
         // spot / margin
         //
@@ -1765,9 +1831,10 @@ module.exports = class binance extends Exchange {
         if (this.options['adjustForTimeDifference']) {
             await this.loadTimeDifference ();
         }
-        const markets = this.safeValue (response, 'symbols', []);
         const result = [];
         for (let i = 0; i < markets.length; i++) {
+            let swap = false;
+            let future = false;
             const market = markets[i];
             const id = this.safeString (market, 'symbol');
             const lowercaseId = this.safeStringLower (market, 'symbol');
@@ -1777,16 +1844,15 @@ module.exports = class binance extends Exchange {
             const base = this.safeCurrencyCode (baseId);
             const quote = this.safeCurrencyCode (quoteId);
             const settle = this.safeCurrencyCode (settleId);
-            const contract = future || delivery;
             const contractType = this.safeString (market, 'contractType');
-            const idSymbol = contract && (contractType !== 'PERPETUAL');
-            let symbol = undefined;
-            let expiry = undefined;
-            if (idSymbol) {
-                symbol = id;
-                expiry = this.safeInteger (market, 'deliveryDate');
+            const contract = ('contractType' in market);
+            const spot = !contract;
+            let expiry = this.safeInteger (market, 'deliveryDate');
+            if (contractType === 'PERPETUAL') {
+                expiry = undefined;
+                swap = true;
             } else {
-                symbol = base + '/' + quote;
+                future = true;
             }
             const filters = this.safeValue (market, 'filters', []);
             const filtersByType = this.indexBy (filters, 'filterType');
@@ -1795,11 +1861,19 @@ module.exports = class binance extends Exchange {
             let fees = this.fees;
             let linear = undefined;
             let inverse = undefined;
+            let symbol = base + '/' + quote;
             if (contract) {
+                if (swap) {
+                    expiry = this.safeInteger (market, 'deliveryDate');
+                    symbol = symbol + ':' + settle;
+                } else if (future) {
+                    symbol = symbol + ':' + settle + '-' + this.yymmdd (expiry);
+                }
                 contractSize = this.safeNumber (market, 'contractSize', this.parseNumber ('1'));
-                fees = this.fees[type];
                 linear = settle === quote;
                 inverse = settle === base;
+                const feesType = linear ? 'linear' : 'inverse';
+                fees = this.safeValue (this.fees, feesType, {});
             }
             let active = (status === 'TRADING');
             if (spot) {
@@ -1812,6 +1886,14 @@ module.exports = class binance extends Exchange {
                 }
             }
             const isMarginTradingAllowed = this.safeValue (market, 'isMarginTradingAllowed', false);
+            let unifiedType = undefined;
+            if (spot) {
+                unifiedType = 'spot';
+            } else if (swap) {
+                unifiedType = 'swap';
+            } else if (future) {
+                unifiedType = 'future';
+            }
             const entry = {
                 'id': id,
                 'lowercaseId': lowercaseId,
@@ -1822,12 +1904,11 @@ module.exports = class binance extends Exchange {
                 'baseId': baseId,
                 'quoteId': quoteId,
                 'settleId': settleId,
-                'type': type,
+                'type': unifiedType,
                 'spot': spot,
                 'margin': spot && isMarginTradingAllowed,
-                'swap': future,
+                'swap': swap,
                 'future': future,
-                'delivery': delivery,
                 'option': false,
                 'active': active,
                 'contract': contract,
@@ -1903,6 +1984,26 @@ module.exports = class binance extends Exchange {
         return result;
     }
 
+    setMarkets (markets, currencies = undefined) {
+        super.setMarkets (markets, currencies);
+        const values = Object.values (this.markets);
+        let hasSpot = false;
+        for (let i = 0; i < values.length; i++) {
+            const entry = values[i];
+            hasSpot = hasSpot || entry['spot'];
+        }
+        if (!hasSpot) {
+            // we add aliases for swap markets
+            for (let i = 0; i < values.length; i++) {
+                const entry = values[i];
+                if (entry['swap']) {
+                    const symbolAlias = entry['base'] + '/' + entry['quote'];
+                    this.markets[symbolAlias] = entry;
+                }
+            }
+        }
+    }
+
     parseBalanceHelper (entry) {
         const account = this.account ();
         account['used'] = this.safeString (entry, 'locked');
@@ -2012,14 +2113,16 @@ module.exports = class binance extends Exchange {
         await this.loadMarkets ();
         const defaultType = this.safeString2 (this.options, 'fetchBalance', 'defaultType', 'spot');
         const type = this.safeString (params, 'type', defaultType);
+        let subType = undefined;
+        [ subType, params ] = this.handleSubTypeAndParams ('fetchBalance', undefined, params);
         const [ marginMode, query ] = this.handleMarginModeAndParams ('fetchBalance', params);
         let method = 'privateGetAccount';
         const request = {};
-        if (type === 'future') {
+        if (this.isLinear (type, subType)) {
             const options = this.safeValue (this.options, type, {});
             const fetchBalanceOptions = this.safeValue (options, 'fetchBalance', {});
             method = this.safeString (fetchBalanceOptions, 'method', 'fapiPrivateV2GetAccount');
-        } else if (type === 'delivery') {
+        } else if (this.isInverse (type, subType)) {
             const options = this.safeValue (this.options, type, {});
             const fetchBalanceOptions = this.safeValue (options, 'fetchBalance', {});
             method = this.safeString (fetchBalanceOptions, 'method', 'dapiPrivateGetAccount');
@@ -2477,11 +2580,13 @@ module.exports = class binance extends Exchange {
         await this.loadMarkets ();
         const defaultType = this.safeString2 (this.options, 'fetchBidsAsks', 'defaultType', 'spot');
         const type = this.safeString (params, 'type', defaultType);
+        let subType = undefined;
+        [ subType, params ] = this.handleSubTypeAndParams ('fetchBidsAsks', undefined, params);
         const query = this.omit (params, 'type');
         let method = undefined;
-        if (type === 'future') {
+        if (this.isLinear (type, subType)) {
             method = 'fapiPublicGetTickerBookTicker';
-        } else if (type === 'delivery') {
+        } else if (this.isInverse (type, subType)) {
             method = 'dapiPublicGetTickerBookTicker';
         } else {
             method = 'publicGetTickerBookTicker';
@@ -2502,11 +2607,13 @@ module.exports = class binance extends Exchange {
         await this.loadMarkets ();
         const defaultType = this.safeString2 (this.options, 'fetchTickers', 'defaultType', 'spot');
         const type = this.safeString (params, 'type', defaultType);
+        let subType = undefined;
+        [ subType, params ] = this.handleSubTypeAndParams ('fetchTickers', undefined, params);
         const query = this.omit (params, 'type');
         let defaultMethod = undefined;
-        if (type === 'future') {
+        if (this.isLinear (type, subType)) {
             defaultMethod = 'fapiPublicGetTicker24hr';
-        } else if (type === 'delivery') {
+        } else if (this.isInverse (type, subType)) {
             defaultMethod = 'dapiPublicGetTicker24hr';
         } else {
             defaultMethod = 'publicGetTicker24hr';
@@ -2810,38 +2917,25 @@ module.exports = class binance extends Exchange {
             // 'endTime': 789,   // Timestamp in ms to get aggregate trades until INCLUSIVE.
             // 'limit': 500,     // default = 500, maximum = 1000
         };
-        const [ type, query ] = this.handleMarketTypeAndParams ('fetchTrades', market, params);
         let defaultMethod = undefined;
-        if (type === 'future') {
-            if (market['linear']) {
-                defaultMethod = 'fapiPublicGetAggTrades';
-            } else if (market['inverse']) {
-                defaultMethod = 'dapiPublicGetAggTrades';
-            }
-        } else if (type === 'delivery') {
+        if (market['linear']) {
+            defaultMethod = 'fapiPublicGetAggTrades';
+        } else if (market['inverse']) {
             defaultMethod = 'dapiPublicGetAggTrades';
         } else {
             defaultMethod = 'publicGetAggTrades';
         }
         let method = this.safeString (this.options, 'fetchTradesMethod', defaultMethod);
         if (method === 'publicGetAggTrades') {
-            if (type === 'future') {
-                if (market['linear']) {
-                    method = 'fapiPublicGetAggTrades';
-                } else if (market['inverse']) {
-                    method = 'dapiPublicGetAggTrades';
-                }
-            } else if (type === 'delivery') {
+            if (market['linear']) {
+                method = 'fapiPublicGetAggTrades';
+            } else if (market['inverse']) {
                 method = 'dapiPublicGetAggTrades';
             }
         } else if (method === 'publicGetHistoricalTrades') {
-            if (type === 'future') {
-                if (market['linear']) {
-                    method = 'fapiPublicGetHistoricalTrades';
-                } else if (market['inverse']) {
-                    method = 'dapiPublicGetHistoricalTrades';
-                }
-            } else if (type === 'delivery') {
+            if (market['linear']) {
+                method = 'fapiPublicGetHistoricalTrades';
+            } else if (market['inverse']) {
                 method = 'dapiPublicGetHistoricalTrades';
             }
         }
@@ -2863,7 +2957,7 @@ module.exports = class binance extends Exchange {
         // - 'tradeId' accepted and returned by this method is "aggregate" trade id
         //   which is different from actual trade id
         // - setting both fromId and time window results in error
-        const response = await this[method] (this.extend (request, query));
+        const response = await this[method] (this.extend (request, params));
         //
         // aggregate trades
         //
@@ -3276,8 +3370,7 @@ module.exports = class binance extends Exchange {
          */
         await this.loadMarkets ();
         const market = this.market (symbol);
-        const defaultType = this.safeString2 (this.options, 'createOrder', 'defaultType', 'spot');
-        const marketType = this.safeString (params, 'type', defaultType);
+        const marketType = this.safeString (params, 'type', market['type']);
         const clientOrderId = this.safeString2 (params, 'newClientOrderId', 'clientOrderId');
         const postOnly = this.safeValue (params, 'postOnly', false);
         const [ marginMode, query ] = this.handleMarginModeAndParams ('createOrder', params);
@@ -3286,9 +3379,9 @@ module.exports = class binance extends Exchange {
             'side': side.toUpperCase (),
         };
         let method = 'privatePostOrder';
-        if (marketType === 'future') {
+        if (market['linear']) {
             method = 'fapiPrivatePostOrder';
-        } else if (marketType === 'delivery') {
+        } else if (market['inverse']) {
             method = 'dapiPrivatePostOrder';
         } else if (marketType === 'margin' || marginMode !== undefined) {
             method = 'sapiPostMarginOrder';
@@ -3476,9 +3569,9 @@ module.exports = class binance extends Exchange {
             'symbol': market['id'],
         };
         let method = 'privateGetOrder';
-        if (type === 'future') {
+        if (market['linear']) {
             method = 'fapiPrivateGetOrder';
-        } else if (type === 'delivery') {
+        } else if (market['inverse']) {
             method = 'dapiPrivateGetOrder';
         } else if (type === 'margin' || marginMode !== undefined) {
             method = 'sapiGetMarginOrder';
@@ -3521,9 +3614,9 @@ module.exports = class binance extends Exchange {
             'symbol': market['id'],
         };
         let method = 'privateGetAllOrders';
-        if (type === 'future') {
+        if (market['linear']) {
             method = 'fapiPrivateGetAllOrders';
-        } else if (type === 'delivery') {
+        } else if (market['inverse']) {
             method = 'dapiPrivateGetAllOrders';
         } else if (type === 'margin' || marginMode !== undefined) {
             method = 'sapiGetMarginAllOrders';
@@ -3537,8 +3630,7 @@ module.exports = class binance extends Exchange {
         if (limit !== undefined) {
             request['limit'] = limit;
         }
-        const requestParams = this.omit (query, [ 'type' ]);
-        const response = await this[method] (this.extend (request, requestParams));
+        const response = await this[method] (this.extend (request, query));
         //
         //  spot
         //
@@ -3602,7 +3694,9 @@ module.exports = class binance extends Exchange {
         let market = undefined;
         let type = undefined;
         const request = {};
-        const [ marginMode, query ] = this.handleMarginModeAndParams ('fetchOpenOrders', params);
+        let query = {};
+        let marginMode = undefined;
+        [ marginMode, query ] = this.handleMarginModeAndParams ('fetchOpenOrders', params);
         if (symbol !== undefined) {
             market = this.market (symbol);
             request['symbol'] = market['id'];
@@ -3618,11 +3712,13 @@ module.exports = class binance extends Exchange {
             const defaultType = this.safeString2 (this.options, 'fetchOpenOrders', 'defaultType', 'spot');
             type = this.safeString (query, 'type', defaultType);
         }
+        let subType = undefined;
+        [ subType, query ] = this.handleSubTypeAndParams ('fetchOpenOrders', market, params);
         const requestParams = this.omit (query, 'type');
         let method = 'privateGetOpenOrders';
-        if (type === 'future') {
+        if (this.isLinear (type, subType)) {
             method = 'fapiPrivateGetOpenOrders';
-        } else if (type === 'delivery') {
+        } else if (this.isInverse (type, subType)) {
             method = 'dapiPrivateGetOpenOrders';
         } else if (type === 'margin' || marginMode !== undefined) {
             method = 'sapiGetMarginOpenOrders';
@@ -3683,17 +3779,14 @@ module.exports = class binance extends Exchange {
             request['origClientOrderId'] = origClientOrderId;
         }
         let method = 'privateDeleteOrder';
-        if (type === 'future') {
+        if (market['linear']) {
             method = 'fapiPrivateDeleteOrder';
-        } else if (type === 'delivery') {
+        } else if (market['inverse']) {
             method = 'dapiPrivateDeleteOrder';
         } else if (type === 'margin' || marginMode !== undefined) {
             method = 'sapiDeleteMarginOrder';
             if (marginMode === 'isolated') {
                 request['isIsolated'] = true;
-                if (symbol === undefined) {
-                    throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument for isolated markets');
-                }
             }
         }
         const requestParams = this.omit (query, [ 'type', 'origClientOrderId', 'clientOrderId' ]);
@@ -3719,16 +3812,15 @@ module.exports = class binance extends Exchange {
         const request = {
             'symbol': market['id'],
         };
-        const defaultType = this.safeString2 (this.options, 'cancelAllOrders', 'defaultType', 'spot');
-        const type = this.safeString (params, 'type', defaultType);
+        const type = this.safeString (params, 'type', market['type']);
         params = this.omit (params, [ 'type' ]);
         const [ marginMode, query ] = this.handleMarginModeAndParams ('cancelAllOrders', params);
         let method = 'privateDeleteOpenOrders';
-        if (type === 'future') {
+        if (market['linear']) {
             method = 'fapiPrivateDeleteAllOpenOrders';
-        } else if (type === 'delivery') {
+        } else if (market['inverse']) {
             method = 'dapiPrivateDeleteAllOpenOrders';
-        } else if (type === 'margin' || marginMode !== undefined) {
+        } else if ((type === 'margin') || (marginMode !== undefined)) {
             method = 'sapiDeleteMarginOpenOrders';
             if (marginMode === 'isolated') {
                 request['isIsolated'] = true;
@@ -3792,8 +3884,6 @@ module.exports = class binance extends Exchange {
         const type = this.safeString (params, 'type', market['type']);
         params = this.omit (params, 'type');
         let method = undefined;
-        const linear = (type === 'future');
-        const inverse = (type === 'delivery');
         let marginMode = undefined;
         [ marginMode, params ] = this.handleMarginModeAndParams ('fetchMyTrades', params);
         if (type === 'spot' || type === 'margin') {
@@ -3804,9 +3894,9 @@ module.exports = class binance extends Exchange {
                     request['isIsolated'] = true;
                 }
             }
-        } else if (linear) {
+        } else if (market['linear']) {
             method = 'fapiPrivateGetUserTrades';
-        } else if (inverse) {
+        } else if (market['inverse']) {
             method = 'dapiPrivateGetUserTrades';
         }
         let endTime = this.safeInteger2 (params, 'until', 'endTime');
@@ -3820,7 +3910,7 @@ module.exports = class binance extends Exchange {
             const currentTimestamp = this.milliseconds ();
             const oneWeek = 7 * 24 * 60 * 60 * 1000;
             if ((currentTimestamp - startTime) >= oneWeek) {
-                if ((endTime === undefined) && linear) {
+                if ((endTime === undefined) && market['linear']) {
                     endTime = this.sum (startTime, oneWeek);
                     endTime = Math.min (endTime, currentTimestamp);
                 }
@@ -3831,7 +3921,7 @@ module.exports = class binance extends Exchange {
             params = this.omit (params, [ 'endTime', 'until' ]);
         }
         if (limit !== undefined) {
-            if (type === 'future' || type === 'delivery') {
+            if (market['contract']) {
                 limit = Math.min (limit, 1000); // above 1000, returns error
             }
             request['limit'] = limit;
@@ -5062,17 +5152,22 @@ module.exports = class binance extends Exchange {
          */
         await this.loadMarkets ();
         let method = undefined;
-        const defaultType = this.safeString2 (this.options, 'fetchTradingFees', 'defaultType', 'future');
+        const defaultType = this.safeString2 (this.options, 'fetchTradingFees', 'defaultType', 'linear');
         const type = this.safeString (params, 'type', defaultType);
-        const query = this.omit (params, 'type');
-        if ((type === 'spot') || (type === 'margin')) {
+        params = this.omit (params, 'type');
+        let subType = undefined;
+        [ subType, params ] = this.handleSubTypeAndParams ('fetchTradingFees', undefined, params);
+        const isSpotOrMargin = (type === 'spot') || (type === 'margin');
+        const isLinear = this.isLinear (type, subType);
+        const isInverse = this.isInverse (type, subType);
+        if (isSpotOrMargin) {
             method = 'sapiGetAssetTradeFee';
-        } else if (type === 'future') {
+        } else if (isLinear) {
             method = 'fapiPrivateGetAccount';
-        } else if (type === 'delivery') {
+        } else if (isInverse) {
             method = 'dapiPrivateGetAccount';
         }
-        const response = await this[method] (query);
+        const response = await this[method] (params);
         //
         // sapi / spot
         //
@@ -5121,7 +5216,7 @@ module.exports = class binance extends Exchange {
         //         "updateTime": 0
         //     }
         //
-        if ((type === 'spot') || (type === 'margin')) {
+        if (isSpotOrMargin) {
             //
             //    [
             //       {
@@ -5143,7 +5238,7 @@ module.exports = class binance extends Exchange {
                 result[symbol] = fee;
             }
             return result;
-        } else if (type === 'future') {
+        } else if (isLinear) {
             //
             //     {
             //         "feeTier": 0,       // account commisssion tier
@@ -5183,7 +5278,7 @@ module.exports = class binance extends Exchange {
                 };
             }
             return result;
-        } else if (type === 'delivery') {
+        } else if (isInverse) {
             //
             //     {
             //         "canDeposit": true,
@@ -5303,21 +5398,19 @@ module.exports = class binance extends Exchange {
         let method = undefined;
         const defaultType = this.safeString2 (this.options, 'fetchFundingRateHistory', 'defaultType', 'future');
         const type = this.safeString (params, 'type', defaultType);
-        params = this.omit (params, 'type');
-        if (type === 'future') {
-            method = 'fapiPublicGetFundingRate';
-        } else if (type === 'delivery') {
-            method = 'dapiPublicGetFundingRate';
-        }
+        let market = undefined;
         if (symbol !== undefined) {
-            const market = this.market (symbol);
+            market = this.market (symbol);
             symbol = market['symbol'];
             request['symbol'] = market['id'];
-            if (market['linear']) {
-                method = 'fapiPublicGetFundingRate';
-            } else if (market['inverse']) {
-                method = 'dapiPublicGetFundingRate';
-            }
+        }
+        let subType = undefined;
+        [ subType, params ] = this.handleSubTypeAndParams ('fetchFundingRateHistory', market, params);
+        params = this.omit (params, 'type');
+        if (this.isLinear (type, subType)) {
+            method = 'fapiPublicGetFundingRate';
+        } else if (this.isInverse (type, subType)) {
+            method = 'dapiPublicGetFundingRate';
         }
         if (method === undefined) {
             throw new NotSupported (this.id + ' fetchFundingRateHistory() is not supported for ' + type + ' markets');
@@ -5372,10 +5465,12 @@ module.exports = class binance extends Exchange {
         let method = undefined;
         const defaultType = this.safeString2 (this.options, 'fetchFundingRates', 'defaultType', 'future');
         const type = this.safeString (params, 'type', defaultType);
+        let subType = undefined;
+        [ subType, params ] = this.handleSubTypeAndParams ('fetchFundingRates', undefined, params);
         const query = this.omit (params, 'type');
-        if (type === 'future') {
+        if (this.isLinear (type, subType)) {
             method = 'fapiPublicGetPremiumIndex';
-        } else if (type === 'delivery') {
+        } else if (this.isInverse (type, subType)) {
             method = 'dapiPublicGetPremiumIndex';
         } else {
             throw new NotSupported (this.id + ' fetchFundingRates() supports linear and inverse contracts only');
@@ -5453,8 +5548,8 @@ module.exports = class binance extends Exchange {
         for (let i = 0; i < positions.length; i++) {
             const position = positions[i];
             const marketId = this.safeString (position, 'symbol');
-            const market = this.safeMarket (marketId);
-            const code = (this.options['defaultType'] === 'future') ? market['quote'] : market['base'];
+            const market = this.safeMarket (marketId, undefined, undefined, 'contract');
+            const code = market['linear'] ? market['quote'] : market['base'];
             // sometimes not all the codes are correctly returned...
             if (code in balances) {
                 const parsed = this.parseAccountPosition (this.extend (position, {
@@ -5509,7 +5604,7 @@ module.exports = class binance extends Exchange {
         //     }
         //
         const marketId = this.safeString (position, 'symbol');
-        market = this.safeMarket (marketId, market);
+        market = this.safeMarket (marketId, market, undefined, 'contract');
         const symbol = this.safeString (market, 'symbol');
         const leverageString = this.safeString (position, 'leverage');
         const leverage = parseInt (leverageString);
@@ -5703,7 +5798,7 @@ module.exports = class binance extends Exchange {
         //     }
         //
         const marketId = this.safeString (position, 'symbol');
-        market = this.safeMarket (marketId, market);
+        market = this.safeMarket (marketId, market, undefined, 'contract');
         const symbol = this.safeString (market, 'symbol');
         const leverageBrackets = this.safeValue (this.options, 'leverageBrackets', {});
         const leverageBracket = this.safeValue (leverageBrackets, symbol, []);
@@ -5843,9 +5938,11 @@ module.exports = class binance extends Exchange {
             const defaultType = this.safeString (this.options, 'defaultType', 'future');
             const type = this.safeString (params, 'type', defaultType);
             const query = this.omit (params, 'type');
-            if (type === 'future') {
+            let subType = undefined;
+            [ subType, params ] = this.handleSubTypeAndParams ('loadLeverageBrackets', undefined, params);
+            if (this.isLinear (type, subType)) {
                 method = 'fapiPrivateGetLeverageBracket';
-            } else if (type === 'delivery') {
+            } else if (this.isInverse (type, subType)) {
                 method = 'dapiPrivateV2GetLeverageBracket';
             } else {
                 throw new NotSupported (this.id + ' loadLeverageBrackets() supports linear and inverse contracts only');
@@ -5881,10 +5978,12 @@ module.exports = class binance extends Exchange {
          */
         await this.loadMarkets ();
         const [ type, query ] = this.handleMarketTypeAndParams ('fetchLeverageTiers', undefined, params);
+        let subType = undefined;
+        [ subType, params ] = this.handleSubTypeAndParams ('fetchLeverageTiers', undefined, params);
         let method = undefined;
-        if (type === 'future') {
+        if (this.isLinear (type, subType)) {
             method = 'fapiPrivateGetLeverageBracket';
-        } else if (type === 'delivery') {
+        } else if (this.isInverse (type, subType)) {
             method = 'dapiPrivateV2GetLeverageBracket';
         } else {
             throw new NotSupported (this.id + ' fetchLeverageTiers() supports linear and inverse contracts only');
@@ -5955,7 +6054,7 @@ module.exports = class binance extends Exchange {
         //    }
         //
         const marketId = this.safeString (info, 'symbol');
-        market = this.safeMarket (marketId, market);
+        market = this.safeMarket (marketId, market, undefined, 'contract');
         const brackets = this.safeValue (info, 'brackets', []);
         const tiers = [];
         for (let j = 0; j < brackets.length; j++) {
@@ -6011,10 +6110,12 @@ module.exports = class binance extends Exchange {
         let method = undefined;
         const defaultType = this.safeString (this.options, 'defaultType', 'future');
         const type = this.safeString (params, 'type', defaultType);
-        const query = this.omit (params, 'type');
-        if (type === 'future') {
+        let query = this.omit (params, 'type');
+        let subType = undefined;
+        [ subType, query ] = this.handleSubTypeAndParams ('fetchAccountPositions', undefined, params);
+        if (this.isLinear (type, subType)) {
             method = 'fapiPrivateGetAccount';
-        } else if (type === 'delivery') {
+        } else if (this.isInverse (type, subType)) {
             method = 'dapiPrivateGetAccount';
         } else {
             throw new NotSupported (this.id + ' fetchPositions() supports linear and inverse contracts only');
@@ -6046,8 +6147,10 @@ module.exports = class binance extends Exchange {
         let defaultType = 'future';
         defaultType = this.safeString (this.options, 'defaultType', defaultType);
         const type = this.safeString (params, 'type', defaultType);
+        let subType = undefined;
+        [ subType, params ] = this.handleSubTypeAndParams ('fetchPositionsRisk', undefined, params);
         params = this.omit (params, 'type');
-        if ((type === 'future') || (type === 'linear')) {
+        if (this.isLinear (type, subType)) {
             method = 'fapiPrivateGetPositionRisk';
             // ### Response examples ###
             //
@@ -6103,7 +6206,7 @@ module.exports = class binance extends Exchange {
             //             "updateTime": 0
             //         }
             //     ]
-        } else if ((type === 'delivery') || (type === 'inverse')) {
+        } else if (this.isInverse (type, subType)) {
             method = 'dapiPrivateGetPositionRisk';
         } else {
             throw new NotSupported (this.id + ' fetchPositionsRisk() supports linear and inverse contracts only');
@@ -6132,33 +6235,30 @@ module.exports = class binance extends Exchange {
         await this.loadMarkets ();
         let market = undefined;
         let method = undefined;
-        let defaultType = 'future';
         const request = {
             'incomeType': 'FUNDING_FEE', // "TRANSFER"，"WELCOME_BONUS", "REALIZED_PNL"，"FUNDING_FEE", "COMMISSION" and "INSURANCE_CLEAR"
         };
         if (symbol !== undefined) {
             market = this.market (symbol);
             request['symbol'] = market['id'];
-            if (market['linear']) {
-                defaultType = 'future';
-            } else if (market['inverse']) {
-                defaultType = 'delivery';
-            } else {
-                throw new NotSupported (this.id + ' fetchFundingHistory() supports linear and inverse contracts only');
+            if (!market['swap']) {
+                throw new NotSupported (this.id + ' fetchFundingHistory() supports swap contracts only');
             }
         }
+        let subType = undefined;
+        [ subType, params ] = this.handleSubTypeAndParams ('fetchFundingHistory', market, params);
         if (since !== undefined) {
             request['startTime'] = since;
         }
         if (limit !== undefined) {
             request['limit'] = limit;
         }
-        defaultType = this.safeString2 (this.options, 'fetchFundingHistory', 'defaultType', defaultType);
+        const defaultType = this.safeString2 (this.options, 'fetchFundingHistory', 'defaultType', 'future');
         const type = this.safeString (params, 'type', defaultType);
         params = this.omit (params, 'type');
-        if ((type === 'future') || (type === 'linear')) {
+        if (this.isLinear (type, subType)) {
             method = 'fapiPrivateGetIncome';
-        } else if ((type === 'delivery') || (type === 'inverse')) {
+        } else if (this.isInverse (type, subType)) {
             method = 'dapiPrivateGetIncome';
         } else {
             throw new NotSupported (this.id + ' fetchFundingHistory() supports linear and inverse contracts only');
@@ -6287,7 +6387,7 @@ module.exports = class binance extends Exchange {
             'dualSidePosition': dualSidePosition,
         };
         let method = undefined;
-        if (type === 'delivery') {
+        if (this.isInverse (type)) {
             method = 'dapiPrivatePostPositionSideDual';
         } else {
             // default to future
@@ -6498,7 +6598,7 @@ module.exports = class binance extends Exchange {
         };
         let method = undefined;
         let code = undefined;
-        if (type === 'future') {
+        if (market['linear']) {
             method = 'fapiPrivatePostPositionMargin';
             code = market['quote'];
         } else {
@@ -6938,7 +7038,7 @@ module.exports = class binance extends Exchange {
         }
         const symbolKey = market['linear'] ? 'symbol' : 'pair';
         request[symbolKey] = market['id'];
-        if (market['delivery']) {
+        if (market['inverse']) {
             request['contractType'] = this.safeString (params, 'contractType', 'CURRENT_QUARTER');
         }
         if (since !== undefined) {
@@ -6978,7 +7078,6 @@ module.exports = class binance extends Exchange {
     parseOpenInterest (interest, market = undefined) {
         const timestamp = this.safeInteger (interest, 'timestamp');
         const id = this.safeString (interest, 'symbol');
-        market = this.safeMarket (id, market);
         const amount = this.safeNumber (interest, 'sumOpenInterest');
         const value = this.safeNumber (interest, 'sumOpenInterestValue');
         return {
diff --git a/js/binancecoinm.js b/js/binancecoinm.js
index d4b6991a72378..9c319d7f26b5c 100644
--- a/js/binancecoinm.js
+++ b/js/binancecoinm.js
@@ -28,7 +28,8 @@ module.exports = class binancecoinm extends binance {
                 'createStopMarketOrder': true,
             },
             'options': {
-                'defaultType': 'delivery',
+                'fetchMarkets': [ 'inverse' ],
+                'defaultSubType': 'inverse',
                 'leverageBrackets': undefined,
             },
         });
diff --git a/js/binanceusdm.js b/js/binanceusdm.js
index a77b3924ece4d..4cbea22e321be 100644
--- a/js/binanceusdm.js
+++ b/js/binanceusdm.js
@@ -28,7 +28,8 @@ module.exports = class binanceusdm extends binance {
                 'createStopMarketOrder': true,
             },
             'options': {
-                'defaultType': 'future',
+                'fetchMarkets': [ 'linear' ],
+                'defaultSubType': 'linear',
                 // https://www.binance.com/en/support/faq/360033162192
                 // tier amount, maintenance margin, initial margin
                 'leverageBrackets': undefined,
diff --git a/js/mexc3.js b/js/mexc3.js
index e09d07eec74c7..ed294aef8f436 100644
--- a/js/mexc3.js
+++ b/js/mexc3.js
@@ -1504,7 +1504,7 @@ module.exports = class mexc3 extends Exchange {
             ticker = this.safeValue (response, 'data', {});
         }
         // when it's single symbol request, the returned structure is different (singular object) for both spot & swap, thus we need to wrap inside array
-        return this.parseTicker (ticker, symbol);
+        return this.parseTicker (ticker, market);
     }
 
     parseTicker (ticker, market = undefined) {
diff --git a/js/pro/binancecoinm.js b/js/pro/binancecoinm.js
index 317891afd1851..ea4a182294839 100644
--- a/js/pro/binancecoinm.js
+++ b/js/pro/binancecoinm.js
@@ -15,7 +15,8 @@ module.exports = class binancecoinm extends binance {
                 'logo': 'https://user-images.githubusercontent.com/1294454/117738721-668c8d80-b205-11eb-8c49-3fad84c4a07f.jpg',
             },
             'options': {
-                'defaultType': 'delivery',
+                'defaultType': 'swap',
+                'defaultSubType': 'inverse',
             },
         });
     }
diff --git a/js/pro/binanceusdm.js b/js/pro/binanceusdm.js
index 90840d4b5ca0a..c228f0addccd0 100644
--- a/js/pro/binanceusdm.js
+++ b/js/pro/binanceusdm.js
@@ -15,7 +15,8 @@ module.exports = class binanceusdm extends binance {
                 'logo': 'https://user-images.githubusercontent.com/1294454/117738721-668c8d80-b205-11eb-8c49-3fad84c4a07f.jpg',
             },
             'options': {
-                'defaultType': 'future',
+                'defaultType': 'swap',
+                'defaultSubType': 'linear',
             },
         });
     }


Code review comments:
diff_hunk: @@ -1984,6 +1984,26 @@ module.exports = class binance extends Exchange {
         return result;
     }
 
+    setMarkets (markets, currencies = undefined) {
+        super.setMarkets (markets, currencies);
+        const values = Object.values (this.markets);
+        let hasSpot = false;
+        for (let i = 0; i < values.length; i++) {
+            const entry = values[i];
+            hasSpot = hasSpot || entry['spot'];
path: js/binance.js
text: @author:
if `hasSpot` becomes true we can `break`