feat(bybit): improve market orders for UTA
diff --git a/ts/src/bybit.ts b/ts/src/bybit.ts
index 87a9c5bd7d28..a4d6b4cf72ff 100644
--- a/ts/src/bybit.ts
+++ b/ts/src/bybit.ts
@@ -40,7 +40,7 @@ export default class bybit extends Exchange {
                 'closeAllPositions': false,
                 'closePosition': false,
                 'createMarketBuyOrderWithCost': true,
-                'createMarketSellOrderWithCost': false,
+                'createMarketSellOrderWithCost': true,
                 'createOrder': true,
                 'createOrders': true,
                 'createOrderWithTakeProfitAndStopLoss': true,
@@ -960,7 +960,7 @@ export default class bybit extends Exchange {
                 'fetchMarkets': [ 'spot', 'linear', 'inverse', 'option' ],
                 'enableUnifiedMargin': undefined,
                 'enableUnifiedAccount': undefined,
-                'createMarketBuyOrderRequiresPrice': true,
+                'createMarketBuyOrderRequiresPrice': true, // only true for classic accounts
                 'createUnifiedMarginAccount': false,
                 'defaultType': 'swap',  // 'swap', 'future', 'option', 'spot'
                 'defaultSubType': 'linear',  // 'linear', 'inverse'
@@ -3455,8 +3455,31 @@ export default class bybit extends Exchange {
         if (!market['spot']) {
             throw new NotSupported (this.id + ' createMarketBuyOrderWithCost() supports spot orders only');
         }
-        params['createMarketBuyOrderRequiresPrice'] = false;
-        return await this.createOrder (symbol, 'market', 'buy', cost, undefined, params);
+        return await this.createOrder (symbol, 'market', 'buy', cost, 1, params);
+    }
+
+    async createMarketSellOrderWithCost (symbol: string, cost, params = {}) {
+        /**
+         * @method
+         * @name bybit#createMarkeSellOrderWithCost
+         * @see https://bybit-exchange.github.io/docs/v5/order/create-order
+         * @description create a market sell order by providing the symbol and cost
+         * @param {string} symbol unified symbol of the market to create an order in
+         * @param {float} cost how much you want to trade in units of the quote currency
+         * @param {object} [params] extra parameters specific to the exchange API endpoint
+         * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
+         */
+        await this.loadMarkets ();
+        const types = await this.isUnifiedEnabled ();
+        const enableUnifiedAccount = types[1];
+        if (!enableUnifiedAccount) {
+            throw new NotSupported (this.id + ' createMarketSellOrderWithCost() supports UTA accounts only');
+        }
+        const market = this.market (symbol);
+        if (!market['spot']) {
+            throw new NotSupported (this.id + ' createMarketSellOrderWithCost() supports spot orders only');
+        }
+        return await this.createOrder (symbol, 'market', 'sell', cost, 1, params);
     }
 
     async createOrder (symbol: string, type: OrderType, side: OrderSide, amount, price = undefined, params = {}) {
@@ -3501,7 +3524,7 @@ export default class bybit extends Exchange {
         }
         const trailingAmount = this.safeString2 (params, 'trailingAmount', 'trailingStop');
         const isTrailingAmountOrder = trailingAmount !== undefined;
-        const orderRequest = this.createOrderRequest (symbol, type, side, amount, price, params);
+        const orderRequest = this.createOrderRequest (symbol, type, side, amount, price, params, enableUnifiedAccount);
         let response = undefined;
         if (isTrailingAmountOrder) {
             response = await this.privatePostV5PositionTradingStop (orderRequest);
@@ -3524,7 +3547,7 @@ export default class bybit extends Exchange {
         return this.parseOrder (order, market);
     }
 
-    createOrderRequest (symbol: string, type: OrderType, side: OrderSide, amount, price = undefined, params = {}) {
+    createOrderRequest (symbol: string, type: OrderType, side: OrderSide, amount, price = undefined, params = {}, isUTA = true) {
         const market = this.market (symbol);
         symbol = market['symbol'];
         const lowerCaseType = type.toLowerCase ();
@@ -3565,12 +3588,33 @@ export default class bybit extends Exchange {
         } else if (market['option']) {
             request['category'] = 'option';
         }
-        if (market['spot'] && (type === 'market') && (side === 'buy')) {
+        const cost = this.safeString (params, 'cost');
+        params = this.omit (params, 'cost');
+        // if the cost is inferable, let's keep the old logic and ignore marketUnit, to minimize the impact of the changes
+        const isMarketBuyAndCostInferable = (lowerCaseType === 'market') && (side === 'buy') && ((price !== undefined) || (cost !== undefined));
+        if (market['spot'] && (type === 'market') && isUTA && !isMarketBuyAndCostInferable) {
+            // UTA account can specify the cost of the order on both sides
+            if ((cost !== undefined) || (price !== undefined)) {
+                request['marketUnit'] = 'quoteCoin';
+                let orderCost = undefined;
+                if (cost !== undefined) {
+                    orderCost = cost;
+                } else {
+                    const amountString = this.numberToString (amount);
+                    const priceString = this.numberToString (price);
+                    const quoteAmount = Precise.stringMul (amountString, priceString);
+                    orderCost = quoteAmount;
+                }
+                request['qty'] = this.costToPrecision (symbol, orderCost);
+            } else {
+                request['marketUnit'] = 'baseCoin';
+                request['qty'] = this.amountToPrecision (symbol, amount);
+            }
+        } else if (market['spot'] && (type === 'market') && (side === 'buy')) {
+            // classic accounts
             // for market buy it requires the amount of quote currency to spend
             let createMarketBuyOrderRequiresPrice = true;
             [ createMarketBuyOrderRequiresPrice, params ] = this.handleOptionAndParams (params, 'createOrder', 'createMarketBuyOrderRequiresPrice', true);
-            const cost = this.safeNumber (params, 'cost');
-            params = this.omit (params, 'cost');
             if (createMarketBuyOrderRequiresPrice) {
                 if ((price === undefined) && (cost === undefined)) {
                     throw new InvalidOrder (this.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend (amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to false and pass the cost to spend in the amount argument');
@@ -3686,6 +3730,8 @@ export default class bybit extends Exchange {
          * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
          */
         await this.loadMarkets ();
+        const accounts = await this.isUnifiedEnabled ();
+        const isUta = accounts[1];
         const ordersRequests = [];
         const orderSymbols = [];
         for (let i = 0; i < orders.length; i++) {
@@ -3697,7 +3743,7 @@ export default class bybit extends Exchange {
             const amount = this.safeValue (rawOrder, 'amount');
             const price = this.safeValue (rawOrder, 'price');
             const orderParams = this.safeValue (rawOrder, 'params', {});
-            const orderRequest = this.createOrderRequest (marketId, type, side, amount, price, orderParams);
+            const orderRequest = this.createOrderRequest (marketId, type, side, amount, price, orderParams, isUta);
             ordersRequests.push (orderRequest);
         }
         const symbols = this.marketSymbols (orderSymbols, undefined, false, true, true);
diff --git a/ts/src/test/static/request/bybit.json b/ts/src/test/static/request/bybit.json
index 0d99684ffbb5..d56fea7de554 100644
--- a/ts/src/test/static/request/bybit.json
+++ b/ts/src/test/static/request/bybit.json
@@ -216,9 +216,12 @@
                 "output": "{\"symbol\":\"BTCUSDT\",\"side\":\"Buy\",\"orderType\":\"Limit\",\"category\":\"linear\",\"qty\":\"0.001\",\"price\":\"41000\",\"triggerDirection\":1,\"triggerPrice\":\"40000\",\"stopLoss\":\"39500\"}"
             },
             {
-                "description": "Spot market buy order with createMarketBuyOrderRequiresPrice set to false on the testnet",
+                "description": "Spot market buy order with createMarketBuyOrderRequiresPrice set to false on the testnet (classic only)",
                 "method": "createOrder",
                 "url": "https://api-testnet.bybit.com/v5/order/create",
+                "options": {
+                    "enableUnifiedAccount": false
+                },
                 "input": [
                     "BTC/USDT",
                     "market",
@@ -274,6 +277,47 @@
                   }
                 ],
                 "output": "{\"symbol\":\"BTCUSD\",\"side\":\"Sell\",\"orderType\":\"Market\",\"category\":\"inverse\",\"qty\":\"1\",\"reduceOnly\":true}"
+            },
+            {
+                "description": "Create market buy with base amount (UTA only)",
+                "method": "createOrder",
+                "url": "https://api-testnet.bybit.com/v5/order/create",
+                "input": [
+                  "LTC/USDT",
+                  "market",
+                  "buy",
+                  0.2
+                ],
+                "output": "{\"symbol\":\"LTCUSDT\",\"side\":\"Buy\",\"orderType\":\"Market\",\"category\":\"spot\",\"marketUnit\":\"baseCoin\",\"qty\":\"0.2\"}"
+            },
+            {
+                "description": "Create market sell with cost (UTA only)",
+                "method": "createOrder",
+                "url": "https://api-testnet.bybit.com/v5/order/create",
+                "input": [
+                  "LTC/USDT",
+                  "market",
+                  "sell",
+                  0.2,
+                  10
+                ],
+                "output": "{\"symbol\":\"LTCUSDT\",\"side\":\"Sell\",\"orderType\":\"Market\",\"category\":\"spot\",\"marketUnit\":\"quoteCoin\",\"qty\":\"2\"}"
+            },
+            {
+                "description": "market sell with cost in params (UTA only)",
+                "method": "createOrder",
+                "url": "https://api-testnet.bybit.com/v5/order/create",
+                "input": [
+                  "LTC/USDT",
+                  "market",
+                  "sell",
+                  null,
+                  null,
+                  {
+                    "cost": 3
+                  }
+                ],
+                "output": "{\"symbol\":\"LTCUSDT\",\"side\":\"Sell\",\"orderType\":\"Market\",\"category\":\"spot\",\"marketUnit\":\"quoteCoin\",\"qty\":\"3\"}"
             }
         ],
         "createMarketBuyOrderWithCost": [
@@ -288,6 +332,18 @@
                 "output": "{\"symbol\":\"LTCUSDT\",\"side\":\"Buy\",\"orderType\":\"Market\",\"category\":\"spot\",\"qty\":\"10\"}"
             }
         ],
+        "createMarketSellOrderWithCost": [
+            {
+                "description": "market sell order with cost (UTA only)",
+                "method": "createMarkeSellOrderWithCost",
+                "url": "https://api-testnet.bybit.com/v5/order/create",
+                "input": [
+                  "LTC/USDT",
+                  5
+                ],
+                "output": "{\"symbol\":\"LTCUSDT\",\"side\":\"Sell\",\"orderType\":\"Market\",\"category\":\"spot\",\"marketUnit\":\"quoteCoin\",\"qty\":\"5\"}"
+            }
+        ],
         "createOrders": [
             {
                 "description": "Swap create orders",
diff --git a/ts/src/test/test.ts b/ts/src/test/test.ts
index cbe5258b6d16..3d4ebe0f34e7 100644
--- a/ts/src/test/test.ts
+++ b/ts/src/test/test.ts
@@ -1236,6 +1236,9 @@ export default class testMainClass extends baseMainTestClass {
             const results = methods[method];
             for (let j = 0; j < results.length; j++) {
                 const result = results[j];
+                const oldExchangeOptions = exchange.options; // snapshot options;
+                const testExchangeOptions = exchange.safeValue (result, 'options', {});
+                exchange.options = exchange.deepExtend (oldExchangeOptions, testExchangeOptions); // custom options to be used in the tests
                 const description = exchange.safeValue (result, 'description');
                 if ((testName !== undefined) && (testName !== description)) {
                     continue;
@@ -1247,6 +1250,8 @@ export default class testMainClass extends baseMainTestClass {
                 const type = exchange.safeString (exchangeData, 'outputType');
                 const skipKeys = exchange.safeValue (exchangeData, 'skipKeys', []);
                 await this.testMethodStatically (exchange, method, result, type, skipKeys);
+                // reset options
+                exchange.options = oldExchangeOptions;
             }
         }
         await close (exchange);
@@ -1264,6 +1269,9 @@ export default class testMainClass extends baseMainTestClass {
             for (let j = 0; j < results.length; j++) {
                 const result = results[j];
                 const description = exchange.safeValue (result, 'description');
+                const oldExchangeOptions = exchange.options; // snapshot options;
+                const testExchangeOptions = exchange.safeValue (result, 'options', {});
+                exchange.options = exchange.deepExtend (oldExchangeOptions, testExchangeOptions); // custom options to be used in the tests
                 const isDisabled = exchange.safeValue (result, 'disabled', false);
                 if (isDisabled) {
                     continue;
@@ -1277,6 +1285,8 @@ export default class testMainClass extends baseMainTestClass {
                 }
                 const skipKeys = exchange.safeValue (exchangeData, 'skipKeys', []);
                 await this.testResponseStatically (exchange, method, skipKeys, result);
+                // reset options
+                exchange.options = oldExchangeOptions;
             }
         }
         await close (exchange);

- UTA accounts can place market orders using the base or quote amount on both sides (buy and sell)