Repository: hummingbot/hummingbot
Pull request: Feat / add LIMIT_MAKER order type to all cex connectors
**Before submitting this PR, please make sure**:
- [x] Your code builds clean without any errors or warnings
- [x] You are using approved title ("feat/", "fix/", "docs/", "refactor/", etc)
- [x] Update all existing CEXs to support LIMIT_MAKER where possible
- [x] Update developer manual document, so new connectors will have this implemented
- [x] Update unit testing to test for when order submitted successfully and when they are rejected

**Optional**:
- Related Github issue: #1912 
- Related Clubhouse Story: 9540


**A description of the changes proposed in the pull request**:


Problem statement:
[Feature] Add LIMIT_MAKER order type support to all market connectors
**Feature Suggestion**
LIMIT_MAKER order is a limit order which will be rejected (by the exchange) if they would immediately match and trade as a taker. We have implemented this order type for Binance, I think we should cover all existing exchanges which support this type of order. 

Tasks:
- Update all existing CEXs to support LIMIT_MAKER where possible
- Update developer manual document, so new connectors will have this implemented.
- Update unit testing to test for when order submitted successfully and when they are rejected.

Hints from issue discussion:
We should add `supported_order_types()` function to the `market_base` (return a list of order_type) class which each connector will need to implement, so that any object that uses the market class knows what are available. e.g. in PMM strategy first check if LIMIT_MAKER is supported by the connector, if not use LIMIT order type instead.

Commit message: (cleanup) raise NotImplementedError for supported_order_types in market_base

Patch to review:
diff --git a/documentation/docs/developers/connectors/tutorial.md b/documentation/docs/developers/connectors/tutorial.md
index a84f821fe8a..3198523a0f6 100644
--- a/documentation/docs/developers/connectors/tutorial.md
+++ b/documentation/docs/developers/connectors/tutorial.md
@@ -201,8 +201,8 @@ Variable(s)<div style="width:100px"/>  | Type                | Description
 `order_id`   | `str`               | A generated, client-side order ID that will be used to identify an order by the Hummingbot client.<br/> The `order_id` is generated in the `c_buy` function.
 `symbol`     | `str`               | The trading pair string representing the market on which the order should be placed. i.e. (ZRX-ETH) <br/><table><tbody><tr><td bgcolor="#ecf3ff">**Note**: Some exchanges have the trading pair symbol in `Quote-Base` format. Hummingbot requires that all trading pairs to be in `Base-Quote` format.</td></tr></tbody></table>
 `amount`     | `Decimal`           | The total value, in base currency, to buy/sell.
-`order_type` | `OrderType`         | OrderType.LIMIT or OrderType.MARKET
-`price`      | `Optional[Decimal]` | If `order_type` is `LIMIT`, it represents the rate at which the `amount` base currency is being bought/sold at.<br/>If `order_type` is `MARKET`, this is **not** used(`price = s_decimal_0`). <br/><table><tbody><tr><td bgcolor="#ecf3ff">**Note**: `s_decimal_0 = Decimal(0)` </td></tr></tbody></table>
+`order_type` | `OrderType`         | OrderType.LIMIT, OrderType.LIMIT_MAKER or OrderType.MARKET
+`price`      | `Optional[Decimal]` | If `order_type` is `LIMIT`, it represents the rate at which the `amount` base currency is being bought/sold at.<br/>If `order_type` is `LIMIT_MAKER`, it also represents the rate at which the `amount` base currency is being bought/sold at. However, this `OrderType` is expected to be a **post only** order(i.e should ideally be rejected by the exchange if it'll cross the market)<br/>If `order_type` is `MARKET`, this is **not** used(`price = s_decimal_0`). <br/><table><tbody><tr><td bgcolor="#ecf3ff">**Note**: `s_decimal_0 = Decimal(0)` </td></tr></tbody></table>
 
 #### Cancelling Orders
 
@@ -291,6 +291,7 @@ Function<div style="width:150px"/> | Input Parameter(s) | Expected Output(s) | D
 `stop_network` | `None` | `None` | An asynchronous wrapper function for `_stop_network`. Used by `NetworkBase` class to handle when a single market goes offline.
 `check_network` | `None` | `NetworkStatus` | `An asynchronous function used by `NetworkBase` class to check if the market is online/offline.
 `get_order` | `client_order_id:str`| `Dict[str, Any]` | Gets status update for a particular order via rest API.<br/><br/><table><tbody><tr><td bgcolor="#ecf3ff">**Note**: You are required to retrieve the exchange order ID for the specified `client_order_id`. You can do this by calling the `get_exchange_order_id` function available in the `InFlightOrderBase`.</td></tr></tbody></table>
+`supported_order_types` | `None` | `List[OrderType]` | Returns a list of OrderType(s) supported by the exchange. Examples are: `OrderType.LIMIT`, `OrderType.LIMIT_MAKER` and `OrderType.MARKET`.
 `place_order` | `order_id:str`<br/>`symbol:str`<br/>`amount:Decimal`<br/>`is_buy:bool`<br/>`order_type:OrderType`<br/>`price:Decimal`| `Dict[str, Any]` | An asynchronous wrapper for placing orders through the REST API. Returns a JSON response from the API.
 `cancel_all` | `timeout_seconds:float`| `List[CancellationResult]` | An asynchronous function that cancels all active orders. Used by Hummingbot's top level "stop" and "exit" commands(cancelling outstanding orders on exit). Returns a `List` of [`CancellationResult`](https://github.com/CoinAlpha/hummingbot/blob/master/hummingbot/core/data_type/cancellation_result.py).<br/><br/>A `CancellationResult` is an object that indicates if an order has been successfully cancelled with a boolean variable.
 `_stop_network` | `None` | `None` | Synchronous function that handles when a single market goes offline
diff --git a/hummingbot/market/binance/binance_market.pyx b/hummingbot/market/binance/binance_market.pyx
index b3142889e19..7caf9f1925e 100755
--- a/hummingbot/market/binance/binance_market.pyx
+++ b/hummingbot/market/binance/binance_market.pyx
@@ -843,6 +843,9 @@ cdef class BinanceMarket(MarketBase):
     def to_hb_order_type(binance_type: str) -> OrderType:
         return OrderType[binance_type]
 
+    def supported_order_types(self):
+        return [OrderType.LIMIT, OrderType.MARKET, OrderType.LIMIT_MAKER]
+
     async def create_order(self,
                            trade_type: TradeType,
                            order_id: str,
diff --git a/hummingbot/market/bittrex/bittrex_market.pyx b/hummingbot/market/bittrex/bittrex_market.pyx
index 6514581a1cf..f3075147104 100644
--- a/hummingbot/market/bittrex/bittrex_market.pyx
+++ b/hummingbot/market/bittrex/bittrex_market.pyx
@@ -682,6 +682,9 @@ cdef class BittrexMarket(MarketBase):
 
         return quantized_amount
 
+    def supported_order_types(self):
+        return [OrderType.LIMIT, OrderType.MARKET, OrderType.LIMIT_MAKER]
+
     async def place_order(self,
                           order_id: str,
                           trading_pair: str,
@@ -713,6 +716,15 @@ cdef class BittrexMarket(MarketBase):
                 "timeInForce": "IMMEDIATE_OR_CANCEL"
                 # Available options [IMMEDIATE_OR_CANCEL, FILL_OR_KILL]
             }
+        elif order_type is OrderType.LIMIT_MAKER:
+            body = {
+                    "marketSymbol": str(trading_pair),
+                    "direction": "BUY" if is_buy else "SELL",
+                    "type": "LIMIT",
+                    "quantity": f"{amount:f}",
+                    "limit": f"{price:f}",
+                    "timeInForce": "POST_ONLY_GOOD_TIL_CANCELLED"
+                    }
 
         api_response = await self._api_request("POST", path_url=path_url, body=body)
 
diff --git a/hummingbot/market/coinbase_pro/coinbase_pro_market.pyx b/hummingbot/market/coinbase_pro/coinbase_pro_market.pyx
index 3650ec42b4d..a5f70ade75d 100755
--- a/hummingbot/market/coinbase_pro/coinbase_pro_market.pyx
+++ b/hummingbot/market/coinbase_pro/coinbase_pro_market.pyx
@@ -712,6 +712,9 @@ cdef class CoinbaseProMarket(MarketBase):
                 self.logger().error("Unexpected error in user stream listener loop.", exc_info=True)
                 await asyncio.sleep(5.0)
 
+    def supported_order_types(self):
+        return [OrderType.LIMIT, OrderType.MARKET, OrderType.LIMIT_MAKER]
+
     async def place_order(self, order_id: str, trading_pair: str, amount: Decimal, is_buy: bool, order_type: OrderType,
                           price: Decimal):
         """
@@ -723,10 +726,13 @@ cdef class CoinbaseProMarket(MarketBase):
             "size": f"{amount:f}",
             "product_id": trading_pair,
             "side": "buy" if is_buy else "sell",
-            "type": "limit" if order_type is OrderType.LIMIT else "market",
+            "type": "market" if order_type is OrderType.MARKET else "limit",
         }
         if order_type is OrderType.LIMIT:
             data["price"] = f"{price:f}"
+        elif order_type is OrderType.LIMIT_MAKER:
+            data["price"] = f"{price:f}"
+            data["post_only"] = True
         order_result = await self._api_request("post", path_url=path_url, data=data)
         return order_result
 
diff --git a/hummingbot/market/eterbase/eterbase_market.pyx b/hummingbot/market/eterbase/eterbase_market.pyx
index 5281837fd2b..b00e5f1d345 100644
--- a/hummingbot/market/eterbase/eterbase_market.pyx
+++ b/hummingbot/market/eterbase/eterbase_market.pyx
@@ -759,6 +759,9 @@ cdef class EterbaseMarket(MarketBase):
                 self.logger().error("Unexpected error in user stream listener loop.", exc_info=True)
                 await asyncio.sleep(5.0)
 
+    def supported_order_types(self):
+        return [OrderType.LIMIT, OrderType.LIMIT_MAKER, OrderType.MARKET]
+
     async def place_order(self, order_id: str, trading_pair: str, amount: Decimal, is_buy: bool, order_type: OrderType,
                           price: Decimal, cost: Optional[Decimal]):
         """
@@ -768,7 +771,7 @@ cdef class EterbaseMarket(MarketBase):
         tp_map_mkrtid: Dict[str, str] = await EterbaseAPIOrderBookDataSource.get_map_market_id()
         path_url = "/orders"
 
-        if order_type is OrderType.LIMIT:
+        if order_type is OrderType.LIMIT or order_type is OrderType.LIMIT_MAKER:
             type_order = 2
         elif order_type is OrderType.MARKET:
             type_order = 1
@@ -790,9 +793,11 @@ cdef class EterbaseMarket(MarketBase):
             "refId": order_id
         }
 
-        if order_type is OrderType.LIMIT:
+        if order_type is OrderType.LIMIT or order_type is OrderType.LIMIT_MAKER:
             data["limitPrice"] = str(price)
             data["qty"] = str(amount)
+            if order_type is OrderType.LIMIT_MAKER:
+                data["postOnly"] = True
         elif order_type is OrderType.MARKET:
             if is_buy:
                 data["cost"] = str(cost)
diff --git a/hummingbot/market/huobi/huobi_market.pyx b/hummingbot/market/huobi/huobi_market.pyx
index 9bfddce3e1f..d278d5b28a8 100644
--- a/hummingbot/market/huobi/huobi_market.pyx
+++ b/hummingbot/market/huobi/huobi_market.pyx
@@ -594,6 +594,9 @@ cdef class HuobiMarket(MarketBase):
     def ready(self) -> bool:
         return all(self.status_dict.values())
 
+    def supported_order_types(self):
+        return [OrderType.LIMIT, OrderType.LIMIT_MAKER, OrderType.MARKET]
+
     async def place_order(self,
                           order_id: str,
                           trading_pair: str,
@@ -603,7 +606,13 @@ cdef class HuobiMarket(MarketBase):
                           price: Decimal) -> str:
         path_url = "order/orders/place"
         side = "buy" if is_buy else "sell"
-        order_type_str = "limit" if order_type is OrderType.LIMIT else "market"
+        if order_type is OrderType.LIMIT:
+            order_type_str = "limit"
+        elif order_type is OrderType.LIMIT_MAKER:
+            order_type_str = "limit-maker"
+        elif order_type is OrderType.MARKET:
+            order_type_str = "market"
+
         params = {
             "account-id": self._account_id,
             "amount": f"{amount:f}",
@@ -611,7 +620,7 @@ cdef class HuobiMarket(MarketBase):
             "symbol": trading_pair,
             "type": f"{side}-{order_type_str}",
         }
-        if order_type is OrderType.LIMIT:
+        if order_type is OrderType.LIMIT or order_type is OrderType.LIMIT_MAKER:
             params["price"] = f"{price:f}"
         exchange_order_id = await self._api_request(
             "post",
diff --git a/hummingbot/market/kraken/kraken_market.pyx b/hummingbot/market/kraken/kraken_market.pyx
index f6ed4595a7d..18e448b43b2 100755
--- a/hummingbot/market/kraken/kraken_market.pyx
+++ b/hummingbot/market/kraken/kraken_market.pyx
@@ -805,6 +805,9 @@ cdef class KrakenMarket(MarketBase):
                                          is_auth_required=True)
         return result
 
+    def supported_order_types(self):
+        return [OrderType.LIMIT, OrderType.MARKET]
+
     async def place_order(self,
                           userref: int,
                           trading_pair: str,
diff --git a/hummingbot/market/kucoin/kucoin_market.pyx b/hummingbot/market/kucoin/kucoin_market.pyx
index 3d1fbc79c6e..705732801a5 100644
--- a/hummingbot/market/kucoin/kucoin_market.pyx
+++ b/hummingbot/market/kucoin/kucoin_market.pyx
@@ -525,6 +525,9 @@ cdef class KucoinMarket(MarketBase):
     def get_all_balances(self) -> Dict[str, Decimal]:
         return self._account_balances.copy()
 
+    def supported_order_types(self):
+        return [OrderType.LIMIT, OrderType.LIMIT_MAKER, OrderType.MARKET]
+
     async def place_order(self,
                           order_id: str,
                           trading_pair: str,
@@ -534,7 +537,7 @@ cdef class KucoinMarket(MarketBase):
                           price: Decimal) -> str:
         path_url = "/api/v1/orders"
         side = "buy" if is_buy else "sell"
-        order_type_str = "limit" if order_type is OrderType.LIMIT else "market"
+        order_type_str = "market" if order_type is OrderType.MARKET else "limit"
         params = {
             "size": str(amount),
             "clientOid": order_id,
@@ -544,6 +547,9 @@ cdef class KucoinMarket(MarketBase):
         }
         if order_type is OrderType.LIMIT:
             params["price"] = str(price)
+        elif order_type is OrderType.LIMIT_MAKER:
+            params["price"] = str(price)
+            params["postOnly"] = True
         exchange_order_id = await self._api_request(
             "post",
             path_url=path_url,
diff --git a/hummingbot/market/liquid/liquid_market.pyx b/hummingbot/market/liquid/liquid_market.pyx
index fdf2da02c65..4150ff270c0 100644
--- a/hummingbot/market/liquid/liquid_market.pyx
+++ b/hummingbot/market/liquid/liquid_market.pyx
@@ -862,6 +862,9 @@ cdef class LiquidMarket(MarketBase):
                 self.logger().error("Unexpected error in user stream listener loop.", exc_info=True)
                 await asyncio.sleep(5.0)
 
+    def supported_order_types(self):
+        return [OrderType.LIMIT, OrderType.MARKET, OrderType.LIMIT_MAKER]
+
     async def place_order(self, order_id: str, trading_pair: str, amount: Decimal, is_buy: bool, order_type: OrderType,
                           price: Decimal):
         """
@@ -871,6 +874,13 @@ cdef class LiquidMarket(MarketBase):
         path_url = Constants.ORDER_CREATION_URI
         product_id = self._product_dict.get(trading_pair).get('id')
 
+        if order_type is OrderType.LIMIT:
+            order_type_str = "limit"
+        elif order_type is OrderType.MARKET:
+            order_type_str = "market"
+        elif order_type is OrderType.LIMIT_MAKER:
+            order_type_str = "limit_post_only"
+
         data = {
             "order": {
                 "client_order_id": order_id,
@@ -878,7 +888,7 @@ cdef class LiquidMarket(MarketBase):
                 "quantity": "{:10.8f}".format(amount),
                 "product_id": product_id,
                 "side": "buy" if is_buy else "sell",
-                "order_type": "limit" if order_type is OrderType.LIMIT else "market",
+                "order_type": order_type_str,
             }
         }
 
diff --git a/hummingbot/market/market_base.pyx b/hummingbot/market/market_base.pyx
index ef22a022659..b5922b2f142 100755
--- a/hummingbot/market/market_base.pyx
+++ b/hummingbot/market/market_base.pyx
@@ -194,6 +194,9 @@ cdef class MarketBase(NetworkIterator):
         order_size_quantum = self.c_get_order_size_quantum(trading_pair, amount)
         return (amount // order_size_quantum) * order_size_quantum
 
+    def supported_order_types(self):
+        raise NotImplementedError
+
     # ----------------------------------------------------------------------------------------------------------
     # </editor-fold>
 
diff --git a/hummingbot/strategy/pure_market_making/pure_market_making.pyx b/hummingbot/strategy/pure_market_making/pure_market_making.pyx
index a0c5fa3c086..e4dd7846a5a 100644
--- a/hummingbot/strategy/pure_market_making/pure_market_making.pyx
+++ b/hummingbot/strategy/pure_market_making/pure_market_making.pyx
@@ -129,9 +129,10 @@ cdef class PureMarketMakingStrategy(StrategyBase):
 
         self._cancel_timestamp = 0
         self._create_timestamp = 0
-        self._limit_order_type = OrderType.LIMIT
-        if market_info.market.name == "binance" and not take_if_crossed and paper_trade_disabled():
+        if paper_trade_disabled() and not take_if_crossed and OrderType.LIMIT_MAKER in self._market_info.market.supported_order_types():
             self._limit_order_type = OrderType.LIMIT_MAKER
+        else:
+            self._limit_order_type = OrderType.LIMIT
         self._all_markets_ready = False
         self._filled_buys_balance = 0
         self._filled_sells_balance = 0
diff --git a/test/integration/assets/mock_data/fixture_bittrex.py b/test/integration/assets/mock_data/fixture_bittrex.py
index ddd5968228b..2953b9cfb3a 100644
--- a/test/integration/assets/mock_data/fixture_bittrex.py
+++ b/test/integration/assets/mock_data/fixture_bittrex.py
@@ -1,4 +1,8 @@
 class FixtureBittrex:
+    EMPTY = {}
+
+    LIMIT_MAKER_ERROR = {}
+
     PING = {"serverTime": 1582535502000}
 
     BALANCES = [{"currencySymbol": "BTC", "total": "0.00279886", "available": "0.00279886"},
diff --git a/test/integration/assets/mock_data/fixture_coinbase_pro.py b/test/integration/assets/mock_data/fixture_coinbase_pro.py
index faa5d71f8ca..3e3e82eca41 100644
--- a/test/integration/assets/mock_data/fixture_coinbase_pro.py
+++ b/test/integration/assets/mock_data/fixture_coinbase_pro.py
@@ -1,4 +1,8 @@
 class FixtureCoinbasePro:
+    EMPTY = {}
+
+    LIMIT_MAKER_ERROR = {}
+
     ACCOUNTS_GET = [{"id": "2d36cb78-5145-41fe-90b0-3f204f2e357d", "currency": "USDC", "balance": "90.1480261500000000",
                      "available": "90.14802615", "hold": "0.0000000000000000",
                      "profile_id": "bc2f3a64-0c0b-49ce-bb3e-5efc978b5b5c", "trading_enabled": True},
diff --git a/test/integration/assets/mock_data/fixture_huobi.py b/test/integration/assets/mock_data/fixture_huobi.py
index 4651158eb30..d244465191f 100644
--- a/test/integration/assets/mock_data/fixture_huobi.py
+++ b/test/integration/assets/mock_data/fixture_huobi.py
@@ -1,4 +1,6 @@
 class FixtureHuobi:
+    LIMIT_MAKER_ERROR = {}
+
     GET_ACCOUNTS = {"status": "ok", "data": [{"id": 11899168, "type": "spot", "subtype": "", "state": "working"}]}
 
     GET_BALANCES = {"status": "ok", "data": {"id": 11899168, "type": "spot", "state": "working",
diff --git a/test/integration/assets/mock_data/fixture_kucoin.py b/test/integration/assets/mock_data/fixture_kucoin.py
index df39c76b1bf..b7d72fe0a70 100644
--- a/test/integration/assets/mock_data/fixture_kucoin.py
+++ b/test/integration/assets/mock_data/fixture_kucoin.py
@@ -1,4 +1,8 @@
 class FixtureKucoin:
+    EMPTY = {}
+
+    LIMIT_MAKER_ERROR = {}
+
     GET_ACCOUNT = {"code": "200000", "data": [
         {"balance": "0.1910973", "available": "0.1910973", "holds": "0", "currency": "ETH",
          "id": "5e3291017e612d0009cb8fa6", "type": "trade"},
diff --git a/test/integration/assets/mock_data/fixture_liquid.py b/test/integration/assets/mock_data/fixture_liquid.py
index b5910b9fa4c..bbdeee8190f 100644
--- a/test/integration/assets/mock_data/fixture_liquid.py
+++ b/test/integration/assets/mock_data/fixture_liquid.py
@@ -10,6 +10,10 @@ class FixtureLiquid:
     2. Serve as a reference for future lookup the data structure passing among stages.
     """
 
+    EMPTY = {}
+
+    LIMIT_MAKER_ERROR = {}
+
     EXCHANGE_MARKETS_DATA = [
         {
             'id': '418',
@@ -561,18 +565,17 @@ class FixtureLiquid:
     ]
     ORDERS_GET = {
         "models": [
-                {"id": 2017991459, "order_type": "market", "quantity": "1.0", "disc_quantity": "0.0",
-                 "iceberg_total_quantity": "0.0", "side": "buy", "filled_quantity": "1.0", "price": "0.00079",
-                 "created_at": 1579499313, "updated_at": 1579499313, "status": "filled", "leverage_level": 1,
-                 "source_exchange": None, "product_id": 500, "margin_type": None, "take_profit": None,
-                 "stop_loss": None, "trading_type": "spot", "product_code": "CASH", "funding_currency": "ETH",
-                 "crypto_account_id": None, "currency_pair_code": "CELETH", "average_price": "0.00079",
-                 "target": "spot", "order_fee": "0.00000079", "source_action": "manual", "unwound_trade_id": None,
-                 "trade_id": None, "client_order_id": "buy-CEL-ETH-1579499312988332", "settings": None,
-                 "trailing_stop_type": None, "trailing_stop_value": None, "executions": [
-                    {"id": 253095921, "quantity": "1.0", "price": "0.00079", "taker_side": "buy",
-                     "created_at": 1579499313, "my_side": "buy"}], "stop_triggered_time": None}
-        ],
+            {"id": 2017991459, "order_type": "market", "quantity": "1.0", "disc_quantity": "0.0",
+             "iceberg_total_quantity": "0.0", "side": "buy", "filled_quantity": "1.0", "price": "0.00079",
+             "created_at": 1579499313, "updated_at": 1579499313, "status": "filled", "leverage_level": 1,
+             "source_exchange": None, "product_id": 500, "margin_type": None, "take_profit": None,
+             "stop_loss": None, "trading_type": "spot", "product_code": "CASH", "funding_currency": "ETH",
+             "crypto_account_id": None, "currency_pair_code": "CELETH", "average_price": "0.00079",
+             "target": "spot", "order_fee": "0.00000079", "source_action": "manual", "unwound_trade_id": None,
+             "trade_id": None, "client_order_id": "buy-CEL-ETH-1579499312988332", "settings": None,
+             "trailing_stop_type": None, "trailing_stop_value": None, "executions": [
+                 {"id": 253095921, "quantity": "1.0", "price": "0.00079", "taker_side": "buy",
+                  "created_at": 1579499313, "my_side": "buy"}], "stop_triggered_time": None}],
         "current_page": 1,
         "total_pages": 1
     }
@@ -607,8 +610,8 @@ class FixtureLiquid:
          "source_action": "manual", "unwound_trade_id": None, "trade_id": None,
          "client_order_id": "sell-CEL-ETH-1579499322010384", "settings": None, "trailing_stop_type": None,
          "trailing_stop_value": None, "executions": [
-            {"id": 253095951, "quantity": "1.0", "price": "0.00078", "taker_side": "sell", "created_at": 1579499322,
-             "my_side": "sell"}], "stop_triggered_time": None}], "total_pages": 10000,
+             {"id": 253095951, "quantity": "1.0", "price": "0.00078", "taker_side": "sell", "created_at": 1579499322,
+              "my_side": "sell"}], "stop_triggered_time": None}], "total_pages": 10000,
         "current_page": 1}
 
     ORDER_BUY_LIMIT = {"id": 2021509801, "order_type": "limit", "quantity": "1.0", "disc_quantity": "0.0",
@@ -631,8 +634,8 @@ class FixtureLiquid:
              "target": "spot", "order_fee": "0.00000078", "source_action": "manual", "unwound_trade_id": None,
              "trade_id": None, "client_order_id": "buy-CEL-ETH-1579575660659385", "settings": None,
              "trailing_stop_type": None, "trailing_stop_value": None, "executions": [
-                {"id": 253491440, "quantity": "1.0", "price": "0.00078", "taker_side": "buy", "created_at": 1579575661,
-                 "my_side": "buy"}], "stop_triggered_time": None}
+                 {"id": 253491440, "quantity": "1.0", "price": "0.00078", "taker_side": "buy", "created_at": 1579575661,
+                  "my_side": "buy"}], "stop_triggered_time": None}
         ],
         "current_page": 1,
         "total_pages": 1
@@ -657,8 +660,8 @@ class FixtureLiquid:
          "source_action": "manual", "unwound_trade_id": None, "trade_id": None,
          "client_order_id": "sell-CEL-ETH-1579575692882646", "settings": None, "trailing_stop_type": None,
          "trailing_stop_value": None, "executions": [
-            {"id": 253095951, "quantity": "1.0", "price": "0.00072226", "taker_side": "sell", "created_at": 1579499322,
-             "my_side": "sell"}], "stop_triggered_time": None}], "total_pages": 10000,
+             {"id": 253095951, "quantity": "1.0", "price": "0.00072226", "taker_side": "sell", "created_at": 1579499322,
+              "my_side": "sell"}], "stop_triggered_time": None}], "total_pages": 10000,
         "current_page": 1}
 
     ORDER_BUY_CANCEL_ALL = {"id": 2022033542, "order_type": "limit", "quantity": "1.0", "disc_quantity": "0.0",
diff --git a/test/integration/test_bittrex_market.py b/test/integration/test_bittrex_market.py
index 919ee24a095..d5846b4bd4d 100644
--- a/test/integration/test_bittrex_market.py
+++ b/test/integration/test_bittrex_market.py
@@ -24,6 +24,7 @@
 from hummingbot.core.event.event_logger import EventLogger
 from hummingbot.core.event.events import (
     MarketEvent,
+    MarketOrderFailureEvent,
     BuyOrderCompletedEvent,
     SellOrderCompletedEvent,
     OrderFilledEvent,
@@ -74,7 +75,8 @@ class BittrexMarketUnitTest(unittest.TestCase):
         MarketEvent.TransactionFailure,
         MarketEvent.BuyOrderCreated,
         MarketEvent.SellOrderCreated,
-        MarketEvent.OrderCancelled
+        MarketEvent.OrderCancelled,
+        MarketEvent.OrderFailure
     ]
 
     market: BittrexMarket
@@ -304,6 +306,61 @@ def test_limit_sell(self):
         # Reset the logs
         self.market_logger.clear()
 
+    def test_limit_maker_rejections(self):
+        trading_pair = "ETH-USDT"
+
+        # Try to put a buy limit maker order that is going to match, this should triggers order failure event.
+        price: Decimal = self.market.get_price(trading_pair, True) * Decimal('1.02')
+        price: Decimal = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, 1)
+
+        order_id = self.place_order(True, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10001,
+                                    FixtureBittrex.LIMIT_MAKER_ERROR, FixtureBittrex.EMPTY)
+        [order_failure_event] = self.run_parallel(self.market_logger.wait_for(MarketOrderFailureEvent))
+        self.assertEqual(order_id, order_failure_event.order_id)
+
+        self.market_logger.clear()
+
+        # Try to put a sell limit maker order that is going to match, this should triggers order failure event.
+        price: Decimal = self.market.get_price(trading_pair, True) * Decimal('0.98')
+        price: Decimal = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, 1)
+
+        order_id = self.place_order(False, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10002,
+                                    FixtureBittrex.LIMIT_MAKER_ERROR, FixtureBittrex.EMPTY)
+        [order_failure_event] = self.run_parallel(self.market_logger.wait_for(MarketOrderFailureEvent))
+        self.assertEqual(order_id, order_failure_event.order_id)
+
+    def test_limit_makers_unfilled(self):
+        trading_pair = "ETH-USDT"
+        price = self.market.get_price(trading_pair, True) * Decimal("0.8")
+        price = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, 1)
+
+        order_id = self.place_order(True, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10001,
+                                    FixtureBittrex.ORDER_PLACE_OPEN, FixtureBittrex.WS_ORDER_OPEN)
+        [order_created_event] = self.run_parallel(self.market_logger.wait_for(BuyOrderCreatedEvent))
+        order_created_event: BuyOrderCreatedEvent = order_created_event
+        self.assertEqual(order_id, order_created_event.order_id)
+
+        price = self.market.get_price(trading_pair, True) * Decimal("1.2")
+        price = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, 1)
+
+        order_id = self.place_order(False, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10002,
+                                    FixtureBittrex.ORDER_PLACE_OPEN, FixtureBittrex.WS_ORDER_OPEN)
+        [order_created_event] = self.run_parallel(self.market_logger.wait_for(SellOrderCreatedEvent))
+        order_created_event: BuyOrderCreatedEvent = order_created_event
+        self.assertEqual(order_id, order_created_event.order_id)
+
+        [cancellation_results] = self.run_parallel(self.market.cancel_all(5))
+        for cr in cancellation_results:
+            self.assertEqual(cr.success, True)
+
     def test_market_buy(self):
         self.assertGreater(self.market.get_balance("USDT"), 20)
         trading_pair = "ETH-USDT"
diff --git a/test/integration/test_coinbase_pro_market.py b/test/integration/test_coinbase_pro_market.py
index f0709befb57..931730b0326 100644
--- a/test/integration/test_coinbase_pro_market.py
+++ b/test/integration/test_coinbase_pro_market.py
@@ -24,6 +24,7 @@
 from hummingbot.core.event.event_logger import EventLogger
 from hummingbot.core.event.events import (
     MarketEvent,
+    MarketOrderFailureEvent,
     MarketWithdrawAssetEvent,
     BuyOrderCompletedEvent,
     SellOrderCompletedEvent,
@@ -77,7 +78,8 @@ class CoinbaseProMarketUnitTest(unittest.TestCase):
         MarketEvent.TransactionFailure,
         MarketEvent.BuyOrderCreated,
         MarketEvent.SellOrderCreated,
-        MarketEvent.OrderCancelled
+        MarketEvent.OrderCancelled,
+        MarketEvent.OrderFailure
     ]
 
     market: CoinbaseProMarket
@@ -254,6 +256,61 @@ def test_limit_buy(self):
         # Reset the logs
         self.market_logger.clear()
 
+    def test_limit_maker_rejections(self):
+        trading_pair = "ETH-USDC"
+
+        # Try to put a buy limit maker order that is going to match, this should triggers order failure event.
+        price: Decimal = self.market.get_price(trading_pair, True) * Decimal('1.02')
+        price: Decimal = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal("0.02"))
+
+        order_id = self.place_order(True, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10001,
+                                    FixtureCoinbasePro.LIMIT_MAKER_ERROR, FixtureCoinbasePro.EMPTY)
+        [order_failure_event] = self.run_parallel(self.market_logger.wait_for(MarketOrderFailureEvent))
+        self.assertEqual(order_id, order_failure_event.order_id)
+
+        self.market_logger.clear()
+
+        # Try to put a sell limit maker order that is going to match, this should triggers order failure event.
+        price: Decimal = self.market.get_price(trading_pair, True) * Decimal('0.98')
+        price: Decimal = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal("0.02"))
+
+        order_id = self.place_order(False, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10002,
+                                    FixtureCoinbasePro.LIMIT_MAKER_ERROR, FixtureCoinbasePro.EMPTY)
+        [order_failure_event] = self.run_parallel(self.market_logger.wait_for(MarketOrderFailureEvent))
+        self.assertEqual(order_id, order_failure_event.order_id)
+
+    def test_limit_makers_unfilled(self):
+        trading_pair = "ETH-USDC"
+        price = self.market.get_price(trading_pair, True) * Decimal("0.8")
+        price = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal("0.02"))
+
+        order_id = self.place_order(True, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10001,
+                                    FixtureCoinbasePro.ORDERS_LIMIT, FixtureCoinbasePro.WS_ORDER_OPEN)
+        [order_created_event] = self.run_parallel(self.market_logger.wait_for(BuyOrderCreatedEvent))
+        order_created_event: BuyOrderCreatedEvent = order_created_event
+        self.assertEqual(order_id, order_created_event.order_id)
+
+        price = self.market.get_price(trading_pair, True) * Decimal("1.2")
+        price = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal("0.02"))
+
+        order_id = self.place_order(False, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10002,
+                                    FixtureCoinbasePro.ORDERS_LIMIT_2, FixtureCoinbasePro.WS_ORDER_OPEN)
+        [order_created_event] = self.run_parallel(self.market_logger.wait_for(SellOrderCreatedEvent))
+        order_created_event: BuyOrderCreatedEvent = order_created_event
+        self.assertEqual(order_id, order_created_event.order_id)
+
+        [cancellation_results] = self.run_parallel(self.market.cancel_all(5))
+        for cr in cancellation_results:
+            self.assertEqual(cr.success, True)
+
     def test_limit_sell(self):
         trading_pair = "ETH-USDC"
         amount: Decimal = Decimal("0.02")
diff --git a/test/integration/test_eterbase_market.py b/test/integration/test_eterbase_market.py
index 038feba4520..429184fb1c4 100644
--- a/test/integration/test_eterbase_market.py
+++ b/test/integration/test_eterbase_market.py
@@ -24,6 +24,7 @@
 from hummingbot.core.event.event_logger import EventLogger
 from hummingbot.core.event.events import (
     MarketEvent,
+    MarketOrderFailureEvent,
     MarketWithdrawAssetEvent,
     BuyOrderCompletedEvent,
     SellOrderCompletedEvent,
@@ -64,7 +65,8 @@ class EterbaseMarketUnitTest(unittest.TestCase):
         MarketEvent.TransactionFailure,
         MarketEvent.BuyOrderCreated,
         MarketEvent.SellOrderCreated,
-        MarketEvent.OrderCancelled
+        MarketEvent.OrderCancelled,
+        MarketEvent.OrderFailure
     ]
 
     market: EterbaseMarket
@@ -195,6 +197,53 @@ def test_limit_sell(self):
         # Reset the logs
         self.market_logger.clear()
 
+    def test_limit_maker_rejections(self):
+        trading_pair = "ETHEUR"
+
+        # Try to put a buy limit maker order that is going to match, this should triggers order failure event.
+        price: Decimal = self.market.get_price(trading_pair, True) * Decimal('1.02')
+        price: Decimal = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal("0.01"))
+
+        order_id = self.market.buy(trading_pair, amount, OrderType.LIMIT_MAKER, price)
+        [order_failure_event] = self.run_parallel(self.market_logger.wait_for(MarketOrderFailureEvent))
+        self.assertEqual(order_id, order_failure_event.order_id)
+
+        self.market_logger.clear()
+
+        # Try to put a sell limit maker order that is going to match, this should triggers order failure event.
+        price: Decimal = self.market.get_price(trading_pair, True) * Decimal('0.98')
+        price: Decimal = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal("0.01"))
+
+        order_id = self.market.sell(trading_pair, amount, OrderType.LIMIT_MAKER, price)
+        [order_failure_event] = self.run_parallel(self.market_logger.wait_for(MarketOrderFailureEvent))
+        self.assertEqual(order_id, order_failure_event.order_id)
+
+    def test_limit_makers_unfilled(self):
+        trading_pair = "ETHEUR"
+        price = self.market.get_price(trading_pair, True) * Decimal("0.8")
+        price = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal("0.01"))
+
+        order_id = self.market.buy(trading_pair, amount, OrderType.LIMIT_MAKER, price)
+        [order_created_event] = self.run_parallel(self.market_logger.wait_for(BuyOrderCreatedEvent))
+        order_created_event: BuyOrderCreatedEvent = order_created_event
+        self.assertEqual(order_id, order_created_event.order_id)
+
+        price = self.market.get_price(trading_pair, True) * Decimal("1.2")
+        price = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal("0.01"))
+
+        order_id = self.market.sell(trading_pair, amount, OrderType.LIMIT_MAKER, price)
+        [order_created_event] = self.run_parallel(self.market_logger.wait_for(SellOrderCreatedEvent))
+        order_created_event: BuyOrderCreatedEvent = order_created_event
+        self.assertEqual(order_id, order_created_event.order_id)
+
+        [cancellation_results] = self.run_parallel(self.market.cancel_all(5))
+        for cr in cancellation_results:
+            self.assertEqual(cr.success, True)
+
     # NOTE that orders of non-USD pairs (including USDC pairs) are LIMIT only
     def test_market_buy(self):
         self.assertGreater(self.market.get_balance("ETH"), Decimal("0.2"))
diff --git a/test/integration/test_huobi_market.py b/test/integration/test_huobi_market.py
index 7c49a700d68..c069fea9189 100644
--- a/test/integration/test_huobi_market.py
+++ b/test/integration/test_huobi_market.py
@@ -24,6 +24,7 @@
 from hummingbot.core.event.event_logger import EventLogger
 from hummingbot.core.event.events import (
     MarketEvent,
+    MarketOrderFailureEvent,
     BuyOrderCompletedEvent,
     SellOrderCompletedEvent,
     OrderFilledEvent,
@@ -71,7 +72,8 @@ class HuobiMarketUnitTest(unittest.TestCase):
         MarketEvent.TransactionFailure,
         MarketEvent.BuyOrderCreated,
         MarketEvent.SellOrderCreated,
-        MarketEvent.OrderCancelled
+        MarketEvent.OrderCancelled,
+        MarketEvent.OrderFailure
     ]
 
     market: HuobiMarket
@@ -296,6 +298,61 @@ def test_limit_sell(self):
         # Reset the logs
         self.market_logger.clear()
 
+    def test_limit_maker_rejections(self):
+        trading_pair = "ethusdt"
+
+        # Try to put a buy limit maker order that is going to match, this should triggers order failure event.
+        price: Decimal = self.market.get_price(trading_pair, True) * Decimal('1.02')
+        price: Decimal = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal("0.06"))
+
+        order_id = self.place_order(True, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10001,
+                                    FixtureHuobi.LIMIT_MAKER_ERROR)
+        [order_failure_event] = self.run_parallel(self.market_logger.wait_for(MarketOrderFailureEvent))
+        self.assertEqual(order_id, order_failure_event.order_id)
+
+        self.market_logger.clear()
+
+        # Try to put a sell limit maker order that is going to match, this should triggers order failure event.
+        price: Decimal = self.market.get_price(trading_pair, True) * Decimal('0.98')
+        price: Decimal = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal("0.06"))
+
+        order_id = self.place_order(False, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10002,
+                                    FixtureHuobi.LIMIT_MAKER_ERROR)
+        [order_failure_event] = self.run_parallel(self.market_logger.wait_for(MarketOrderFailureEvent))
+        self.assertEqual(order_id, order_failure_event.order_id)
+
+    def test_limit_makers_unfilled(self):
+        trading_pair = "ethusdt"
+        price = self.market.get_price(trading_pair, True) * Decimal("0.8")
+        price = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal("0.06"))
+
+        order_id = self.place_order(True, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10001,
+                                    FixtureHuobi.ORDER_GET_LIMIT_BUY_UNFILLED)
+        [order_created_event] = self.run_parallel(self.market_logger.wait_for(BuyOrderCreatedEvent))
+        order_created_event: BuyOrderCreatedEvent = order_created_event
+        self.assertEqual(order_id, order_created_event.order_id)
+
+        price = self.market.get_price(trading_pair, True) * Decimal("1.2")
+        price = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal("0.06"))
+
+        order_id = self.place_order(False, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10002,
+                                    FixtureHuobi.ORDER_GET_LIMIT_SELL_UNFILLED)
+        [order_created_event] = self.run_parallel(self.market_logger.wait_for(SellOrderCreatedEvent))
+        order_created_event: BuyOrderCreatedEvent = order_created_event
+        self.assertEqual(order_id, order_created_event.order_id)
+
+        [cancellation_results] = self.run_parallel(self.market.cancel_all(5))
+        for cr in cancellation_results:
+            self.assertEqual(cr.success, True)
+
     def test_market_buy(self):
         trading_pair = "ethusdt"
         amount: Decimal = Decimal("0.06")
diff --git a/test/integration/test_kucoin_market.py b/test/integration/test_kucoin_market.py
index c01a0c4634c..3c17dc5a4ae 100644
--- a/test/integration/test_kucoin_market.py
+++ b/test/integration/test_kucoin_market.py
@@ -24,6 +24,7 @@
 from hummingbot.core.event.event_logger import EventLogger
 from hummingbot.core.event.events import (
     MarketEvent,
+    MarketOrderFailureEvent,
     BuyOrderCompletedEvent,
     SellOrderCompletedEvent,
     OrderFilledEvent,
@@ -73,7 +74,8 @@ class KucoinMarketUnitTest(unittest.TestCase):
         MarketEvent.TransactionFailure,
         MarketEvent.BuyOrderCreated,
         MarketEvent.SellOrderCreated,
-        MarketEvent.OrderCancelled
+        MarketEvent.OrderCancelled,
+        MarketEvent.OrderFailure
     ]
 
     market: KucoinMarket
@@ -284,6 +286,61 @@ def test_limit_sell(self):
         # Reset the logs
         self.market_logger.clear()
 
+    def test_limit_maker_rejections(self):
+        trading_pair = "ETH-USDT"
+
+        # Try to put a buy limit maker order that is going to match, this should triggers order failure event.
+        price: Decimal = self.market.get_price(trading_pair, True) * Decimal('1.02')
+        price: Decimal = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal(0.01))
+
+        order_id = self.place_order(True, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10001,
+                                    FixtureKucoin.LIMIT_MAKER_ERROR, FixtureKucoin.EMPTY)
+        [order_failure_event] = self.run_parallel(self.market_logger.wait_for(MarketOrderFailureEvent))
+        self.assertEqual(order_id, order_failure_event.order_id)
+
+        self.market_logger.clear()
+
+        # Try to put a sell limit maker order that is going to match, this should triggers order failure event.
+        price: Decimal = self.market.get_price(trading_pair, True) * Decimal('0.98')
+        price: Decimal = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal(0.01))
+
+        order_id = self.place_order(False, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10002,
+                                    FixtureKucoin.LIMIT_MAKER_ERROR, FixtureKucoin.EMPTY)
+        [order_failure_event] = self.run_parallel(self.market_logger.wait_for(MarketOrderFailureEvent))
+        self.assertEqual(order_id, order_failure_event.order_id)
+
+    def test_limit_makers_unfilled(self):
+        trading_pair = "ETH-USDT"
+        price = self.market.get_price(trading_pair, True) * Decimal("0.8")
+        price = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal(0.01))
+
+        order_id = self.place_order(True, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10001,
+                                    FixtureKucoin.ORDER_PLACE, FixtureKucoin.ORDER_GET_BUY_UNMATCHED)
+        [order_created_event] = self.run_parallel(self.market_logger.wait_for(BuyOrderCreatedEvent))
+        order_created_event: BuyOrderCreatedEvent = order_created_event
+        self.assertEqual(order_id, order_created_event.order_id)
+
+        price = self.market.get_price(trading_pair, True) * Decimal("1.2")
+        price = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, Decimal(0.01))
+
+        order_id = self.place_order(False, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10002,
+                                    FixtureKucoin.ORDER_PLACE, FixtureKucoin.ORDER_GET_SELL_UNMATCHED)
+        [order_created_event] = self.run_parallel(self.market_logger.wait_for(SellOrderCreatedEvent))
+        order_created_event: BuyOrderCreatedEvent = order_created_event
+        self.assertEqual(order_id, order_created_event.order_id)
+
+        [cancellation_results] = self.run_parallel(self.market.cancel_all(5))
+        for cr in cancellation_results:
+            self.assertEqual(cr.success, True)
+
     def test_market_buy(self):
         self.assertGreater(self.market.get_balance("ETH"), Decimal("0.05"))
         trading_pair = "ETH-USDT"
diff --git a/test/integration/test_liquid_market.py b/test/integration/test_liquid_market.py
index 2feeda1694b..c97caca145a 100644
--- a/test/integration/test_liquid_market.py
+++ b/test/integration/test_liquid_market.py
@@ -24,6 +24,7 @@
 from hummingbot.core.event.events import (
     BuyOrderCompletedEvent,
     BuyOrderCreatedEvent,
+    MarketOrderFailureEvent,
     MarketEvent,
     MarketWithdrawAssetEvent,
     OrderCancelledEvent,
@@ -73,7 +74,8 @@ class LiquidMarketUnitTest(unittest.TestCase):
         MarketEvent.TransactionFailure,
         MarketEvent.BuyOrderCreated,
         MarketEvent.SellOrderCreated,
-        MarketEvent.OrderCancelled
+        MarketEvent.OrderCancelled,
+        MarketEvent.OrderFailure
     ]
 
     market: LiquidMarket
@@ -327,6 +329,61 @@ def test_limit_buy_and_sell(self):
         self.assertTrue(any([isinstance(event, SellOrderCreatedEvent) and event.order_id == order_id
                              for event in self.market_logger.event_log]))
 
+    def test_limit_maker_rejections(self):
+        trading_pair = "CEL-ETH"
+
+        # Try to put a buy limit maker order that is going to match, this should triggers order failure event.
+        price: Decimal = self.market.get_price(trading_pair, True) * Decimal('1.02')
+        price: Decimal = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, 1)
+
+        order_id = self.place_order(True, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10001,
+                                    FixtureLiquid.LIMIT_MAKER_ERROR, FixtureLiquid.EMPTY)
+        [order_failure_event] = self.run_parallel(self.market_logger.wait_for(MarketOrderFailureEvent))
+        self.assertEqual(order_id, order_failure_event.order_id)
+
+        self.market_logger.clear()
+
+        # Try to put a sell limit maker order that is going to match, this should triggers order failure event.
+        price: Decimal = self.market.get_price(trading_pair, True) * Decimal('0.98')
+        price: Decimal = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, 1)
+
+        order_id = self.place_order(False, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10002,
+                                    FixtureLiquid.LIMIT_MAKER_ERROR, FixtureLiquid.EMPTY)
+        [order_failure_event] = self.run_parallel(self.market_logger.wait_for(MarketOrderFailureEvent))
+        self.assertEqual(order_id, order_failure_event.order_id)
+
+    def test_limit_makers_unfilled(self):
+        trading_pair = "CEL-USDT"
+        price = self.market.get_price(trading_pair, True) * Decimal("0.8")
+        price = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, 1)
+
+        order_id = self.place_order(True, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10001,
+                                    FixtureLiquid.ORDER_BUY_LIMIT, FixtureLiquid.ORDERS_GET_AFTER_SELL_LIMIT)
+        [order_created_event] = self.run_parallel(self.market_logger.wait_for(BuyOrderCreatedEvent))
+        order_created_event: BuyOrderCreatedEvent = order_created_event
+        self.assertEqual(order_id, order_created_event.order_id)
+
+        price = self.market.get_price(trading_pair, True) * Decimal("1.2")
+        price = self.market.quantize_order_price(trading_pair, price)
+        amount = self.market.quantize_order_amount(trading_pair, 1)
+
+        order_id = self.place_order(False, trading_pair, amount, OrderType.LIMIT_MAKER,
+                                    price, 10002,
+                                    FixtureLiquid.ORDER_SELL_LIMIT, FixtureLiquid.ORDERS_GET_AFTER_SELL_LIMIT)
+        [order_created_event] = self.run_parallel(self.market_logger.wait_for(SellOrderCreatedEvent))
+        order_created_event: BuyOrderCreatedEvent = order_created_event
+        self.assertEqual(order_id, order_created_event.order_id)
+
+        [cancellation_results] = self.run_parallel(self.market.cancel_all(5))
+        for cr in cancellation_results:
+            self.assertEqual(cr.success, True)
+
     @unittest.skipUnless(any("test_withdraw" in arg for arg in sys.argv), "Withdraw test requires manual action.")
     def test_withdraw(self):
         # CEL_ABI contract file can be found in


Code review comments:
diff_hunk: @@ -194,6 +194,9 @@ cdef class MarketBase(NetworkIterator):
         order_size_quantum = self.c_get_order_size_quantum(trading_pair, amount)
         return (amount // order_size_quantum) * order_size_quantum
 
+    def supported_order_types(self):
+        raise NotImplementedError
path: hummingbot/market/market_base.pyx
text: @user1:
Sorry, after some consideration, I think it'd be better if this returns the common MARKET and LIMIT order types so that paper trade and DEX markets will have these automatically. 

@author:
Done!