Repository: Drakkar-Software/OctoBot-Trading (language: python)

Issue:
[Futures] Simulated future support



Discussion / hints:
👍 
If a second position(same side) is open, it'll result in one mean of other ?
> If a second position(same side) is open, it'll result in one mean of other ?

It's not possible to open a new position on the same side of the first one (even if in hedge position mode). You have to close the first one before. 
> > If a second position(same side) is open, it'll result in one mean of other ?
> 
> It's not possible to open a new position on the same side of the first one (even if in hedge position mode). You have to close the first one before. 

You can place another orders, when it's taken it'll DCA your position
> > > If a second position(same side) is open, it'll result in one mean of other ?
> > 
> > 
> > It's not possible to open a new position on the same side of the first one (even if in hedge position mode). You have to close the first one before.
> 
> You can place another orders, when it's taken it'll DCA your position

A new order will increase or decrease the position size depending of the side. 
> > > > If a second position(same side) is open, it'll result in one mean of other ?
> > > 
> > > 
> > > It's not possible to open a new position on the same side of the first one (even if in hedge position mode). You have to close the first one before.
> > 
> > 
> > You can place another orders, when it's taken it'll DCA your position
> 
> A new order will increase or decrease the position size depending of the side.

Two position with same side and same symbol should be merged in one (quantity should be additionnated and price is an average according to the quantity)
> > > > > If a second position(same side) is open, it'll result in one mean of other ?
> > > > 
> > > > 
> > > > It's not possible to open a new position on the same side of the first one (even if in hedge position mode). You have to close the first one before.
> > > 
> > > 
> > > You can place another orders, when it's taken it'll DCA your position
> > 
> > 
> > A new order will increase or decrease the position size depending of the side.
> 
> Two position with same side and same symbol should be merged in one (quantity should be additionnated and price is an average according to the quantity)

Yes that's what I'm saying and that's what is implemented in this PR. You should have a look at https://github.com/Drakkar-Software/OctoBot-Trading/pull/656/files#diff-169411d474771ac349f374a63ae86420c59569a23344d89ebee25da371a9dd12R221.

Fix patch:
diff --git a/.coveragerc b/.coveragerc
index f68b59cca..bd1577e13 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -2,6 +2,7 @@
 omit =
     octobot_trading/cli/*
     octobot_trading/api/*
+    octobot_trading/util/test_tools/*
     tests/*
     tests_additional/*
     tentacles/*
diff --git a/octobot_trading/api/__init__.py b/octobot_trading/api/__init__.py
index 07f6844c4..518e6dd9f 100644
--- a/octobot_trading/api/__init__.py
+++ b/octobot_trading/api/__init__.py
@@ -144,6 +144,9 @@
     cancel_order_with_id,
     LOGGER,
 )
+from octobot_trading.api.positions import (
+    get_positions,
+)
 
 __all__ = [
     "get_symbol_data",
@@ -247,4 +250,5 @@
     "cancel_all_open_orders",
     "cancel_all_open_orders_with_currency",
     "cancel_order_with_id",
+    "get_positions",
 ]
diff --git a/octobot_trading/api/portfolio.py b/octobot_trading/api/portfolio.py
index a127f8843..f10d4b399 100644
--- a/octobot_trading/api/portfolio.py
+++ b/octobot_trading/api/portfolio.py
@@ -13,8 +13,6 @@
 #
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
-import octobot_commons.constants
-
 import octobot_trading.exchange_channel as exchange_channel
 import octobot_trading.constants
 import octobot_trading.personal_data as personal_data
@@ -27,12 +25,8 @@ def get_portfolio(exchange_manager, as_decimal=True) -> dict:
     )
 
 
-def get_portfolio_currency(exchange_manager, currency,
-                           portfolio_type=octobot_commons.constants.PORTFOLIO_AVAILABLE) -> float:
-    return exchange_manager.exchange_personal_data.portfolio_manager.portfolio.get_currency_portfolio(
-        currency,
-        portfolio_type=portfolio_type
-    )
+def get_portfolio_currency(exchange_manager, currency) -> personal_data.Asset:
+    return exchange_manager.exchange_personal_data.portfolio_manager.portfolio.get_currency_portfolio(currency)
 
 
 def get_origin_portfolio(exchange_manager, as_decimal=True) -> dict:
diff --git a/octobot_trading/api/positions.py b/octobot_trading/api/positions.py
new file mode 100644
index 000000000..5cd66ff43
--- /dev/null
+++ b/octobot_trading/api/positions.py
@@ -0,0 +1,18 @@
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+
+def get_positions(exchange_manager) -> list:
+    return exchange_manager.exchange_personal_data.positions_manager.get_symbol_positions()
diff --git a/octobot_trading/constants.py b/octobot_trading/constants.py
index e1986c434..1128d2164 100644
--- a/octobot_trading/constants.py
+++ b/octobot_trading/constants.py
@@ -58,6 +58,14 @@
 CONFIG_DEFAULT_FEES = 0.1
 CONFIG_DEFAULT_SIMULATOR_FEES = 0
 
+DEFAULT_SYMBOL_LEVERAGE = ONE
+DEFAULT_SYMBOL_MAX_LEVERAGE = ONE_HUNDRED
+DEFAULT_SYMBOL_MARGIN_TYPE = enums.MarginType.ISOLATED
+DEFAULT_SYMBOL_CONTRACT_TYPE = enums.FutureContractType.LINEAR_PERPETUAL
+DEFAULT_SYMBOL_POSITION_MODE = enums.PositionMode.ONE_WAY
+DEFAULT_SYMBOL_FUNDING_RATE = decimal.Decimal("0.00005")
+DEFAULT_SYMBOL_MAINTENANCE_MARGIN_RATE = decimal.Decimal("0.01")
+
 # API
 API_LOGGER_TAG = "TradingApi"
 
diff --git a/octobot_trading/enums.py b/octobot_trading/enums.py
index 40f5bd22b..5bf1ae0e2 100644
--- a/octobot_trading/enums.py
+++ b/octobot_trading/enums.py
@@ -101,6 +101,24 @@ class PositionStatus(enum.Enum):
     ADL = "auto_deleveraging"
 
 
+class BlockchainTransactionStatus(enum.Enum):
+    CREATED = "created"
+    CONFIRMING = "confirming"
+    REPLACED = "replaced"
+    FAIL = "fail"
+    SUCCESS = "success"
+
+
+class TransactionType(enum.Enum):
+    BLOCKCHAIN_DEPOSIT = "blockchain_deposit"
+    BLOCKCHAIN_WITHDRAWAL = "blockchain_withdrawal"
+    FUNDING_FEE = "funding_fee"
+    TRADING_FEE = "trading_fee"
+    REALISED_PNL = "realised_pnl"
+    CLOSE_REALISED_PNL = "close_realised_pnl"
+    TRANSFER = "transfer"
+
+
 class TraderOrderType(enum.Enum):
     BUY_MARKET = "buy_market"
     BUY_LIMIT = "buy_limit"
@@ -314,8 +332,10 @@ class ExchangeConstantsPositionColumns(enum.Enum):
     ENTRY_PRICE = "entry_price"
     MARK_PRICE = "mark_price"
     LIQUIDATION_PRICE = "liquidation_price"
+    BANKRUPTCY_PRICE = "bankruptcy_price"
     UNREALISED_PNL = "unrealised_pnl"
     REALISED_PNL = "realised_pnl"
+    CLOSING_FEE = "closing_fee"
     QUANTITY = "quantity"
     SIZE = "size"
     NOTIONAL = "notional"
@@ -323,6 +343,9 @@ class ExchangeConstantsPositionColumns(enum.Enum):
     COLLATERAL = "collateral"
     LEVERAGE = "leverage"
     MARGIN_TYPE = "margin_type"
+    CONTRACT_TYPE = "contract_type"
+    POSITION_MODE = "position_mode"
+    MAINTENANCE_MARGIN_RATE = "maintenance_margin_rate"
     STATUS = "status"
     SIDE = "side"
 
diff --git a/octobot_trading/errors.py b/octobot_trading/errors.py
index 5cfaa87ae..7e85316d6 100644
--- a/octobot_trading/errors.py
+++ b/octobot_trading/errors.py
@@ -90,3 +90,15 @@ class ContractExistsError(Exception):
     """
     Raised when asking for a contract that doesn't exist
     """
+
+
+class TooManyOpenPositionError(Exception):
+    """
+    Raised when changing future contract attributes without closing positions first
+    """
+
+
+class DuplicateTransactionIdError(Exception):
+    """
+    Raised when trying to add a new transaction with a duplicate id
+    """
diff --git a/octobot_trading/exchange_data/contracts/future_contract.pxd b/octobot_trading/exchange_data/contracts/future_contract.pxd
index c0ad76a59..9245b64e1 100644
--- a/octobot_trading/exchange_data/contracts/future_contract.pxd
+++ b/octobot_trading/exchange_data/contracts/future_contract.pxd
@@ -20,6 +20,7 @@ cimport octobot_trading.exchange_data.contracts.margin_contract as margin_contra
 cdef class FutureContract(margin_contract.MarginContract):
     cdef readonly object contract_type
     cdef readonly object position_mode
+    cdef public object maintenance_margin_rate
 
     cdef readonly double minimum_tick_size
 
diff --git a/octobot_trading/exchange_data/contracts/future_contract.py b/octobot_trading/exchange_data/contracts/future_contract.py
index 6434ea269..148008856 100644
--- a/octobot_trading/exchange_data/contracts/future_contract.py
+++ b/octobot_trading/exchange_data/contracts/future_contract.py
@@ -25,12 +25,20 @@ def __init__(self, pair, margin_type, contract_type,
                  maximum_leverage=constants.ONE,
                  current_leverage=constants.ONE,
                  position_mode=enums.PositionMode.ONE_WAY,
+                 maintenance_margin_rate=constants.DEFAULT_SYMBOL_MAINTENANCE_MARGIN_RATE,
                  minimum_tick_size=0.5):
         super().__init__(pair, margin_type=margin_type, contract_size=contract_size,
                          maximum_leverage=maximum_leverage, current_leverage=current_leverage)
         self.contract_type = contract_type
         self.minimum_tick_size = minimum_tick_size
         self.position_mode = position_mode
+        self.maintenance_margin_rate = maintenance_margin_rate
+
+    def __str__(self):
+        return (f"{self.pair} "
+                f"{'inverse' if self.is_inverse_contract() else 'linear'} "
+                f"{'perpetual' if self.is_perpetual_contract() else 'future'} "
+                f"{self.margin_type.value} x{self.current_leverage}")
 
     def is_inverse_contract(self):
         """
diff --git a/octobot_trading/exchange_data/contracts/margin_contract.pxd b/octobot_trading/exchange_data/contracts/margin_contract.pxd
index a19ef7bea..f58c8e0db 100644
--- a/octobot_trading/exchange_data/contracts/margin_contract.pxd
+++ b/octobot_trading/exchange_data/contracts/margin_contract.pxd
@@ -28,5 +28,6 @@ cdef class MarginContract:
     cdef readonly dict risk_limit
 
     cpdef bint is_isolated(self)
+    cpdef bint check_leverage_update(self, object new_leverage)
     cpdef void set_current_leverage(self, object new_leverage)
     cpdef void set_margin_type(self, bint is_isolated=*, bint is_cross=*)
diff --git a/octobot_trading/exchange_data/contracts/margin_contract.py b/octobot_trading/exchange_data/contracts/margin_contract.py
index 73e4f84a2..2760c1428 100644
--- a/octobot_trading/exchange_data/contracts/margin_contract.py
+++ b/octobot_trading/exchange_data/contracts/margin_contract.py
@@ -15,7 +15,6 @@
 #  License along with this library.
 import octobot_trading.enums as enums
 import octobot_trading.constants as constants
-import octobot_trading.errors as errors
 
 
 class MarginContract:
@@ -40,16 +39,21 @@ def is_isolated(self):
         """
         return self.margin_type is enums.MarginType.ISOLATED
 
+    def check_leverage_update(self, new_leverage):
+        """
+        Check if the new leverage value is matching requirements (0 < maximum_leverage)
+        :param new_leverage: the leverage value to check
+        :return: True if valid
+        """
+        return 0 < new_leverage <= self.maximum_leverage
+
     def set_current_leverage(self, new_leverage):
         """
         Set the contract current leverage value
         :param new_leverage: the new leverage value
         """
-        if new_leverage <= self.maximum_leverage:
+        if self.check_leverage_update(new_leverage):
             self.current_leverage = new_leverage
-        else:
-            raise errors.InvalidLeverageValue(f"Trying to update leverage with {new_leverage} "
-                                              f"but maximal value is {self.maximum_leverage}")
 
     def set_margin_type(self, is_isolated=True, is_cross=False):
         """
diff --git a/octobot_trading/exchange_data/exchange_symbol_data.pxd b/octobot_trading/exchange_data/exchange_symbol_data.pxd
index 43e3e5c9b..c945fad6d 100644
--- a/octobot_trading/exchange_data/exchange_symbol_data.pxd
+++ b/octobot_trading/exchange_data/exchange_symbol_data.pxd
@@ -41,6 +41,6 @@ cdef class ExchangeSymbolData:
     cpdef void handle_order_book_update(self, list asks, list bids)
     cpdef void handle_order_book_ticker_update(self, double ask_quantity, double ask_price,
                                                double bid_quantity, double bid_price)
-    cpdef bint handle_mark_price_update(self, double mark_price, str mark_price_source)
+    cpdef bint handle_mark_price_update(self, object mark_price, str mark_price_source)
     cpdef void handle_ticker_update(self, dict ticker)
     cpdef void handle_mini_ticker_update(self, dict mini_ticker)
diff --git a/octobot_trading/exchange_data/prices/channel/prices_updater.py b/octobot_trading/exchange_data/prices/channel/prices_updater.py
index 9161548ad..ac2b2e8a9 100644
--- a/octobot_trading/exchange_data/prices/channel/prices_updater.py
+++ b/octobot_trading/exchange_data/prices/channel/prices_updater.py
@@ -14,6 +14,7 @@
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
 import asyncio
+import decimal
 
 import octobot_trading.errors as errors
 import octobot_trading.exchange_channel as exchanges_channel
@@ -43,9 +44,9 @@ async def start(self):
         if self.channel.is_paused:
             await self.pause()
         else:
-            if not self.channel.exchange_manager.is_future:
+            if self._should_subscribe():
                 await self.subscribe()
-            elif self._should_run():
+            elif self._should_fetch_on_exchange():
                 await self.start_fetching()
             else:
                 self.logger.debug(f"{self.__class__.__name__} wont be used, stopping...")
@@ -83,7 +84,7 @@ async def handle_recent_trades_update(self, exchange: str, exchange_id: str,
         """
         try:
             mark_price = prices_manager.calculate_mark_price_from_recent_trade_prices(
-                [float(last_price[enums.ExchangeConstantsOrderColumns.PRICE.value])
+                [decimal.Decimal(str(last_price[enums.ExchangeConstantsOrderColumns.PRICE.value]))
                  for last_price in recent_trades])
 
             await self.push(symbol, mark_price, mark_price_source=enums.MarkPriceSources.RECENT_TRADE_AVERAGE.value)
@@ -96,7 +97,7 @@ async def handle_ticker_update(self, exchange: str, exchange_id: str,
         Ticker channel consumer callback
         """
         try:
-            await self.push(symbol, ticker[enums.ExchangeConstantsTickersColumns.CLOSE.value],
+            await self.push(symbol, decimal.Decimal(str(ticker[enums.ExchangeConstantsTickersColumns.CLOSE.value])),
                             mark_price_source=enums.MarkPriceSources.TICKER_CLOSE_PRICE.value)
         except Exception as e:
             self.logger.exception(e, True, f"Fail to handle ticker update : {e}")
@@ -120,6 +121,7 @@ async def fetch_market_price(self, symbol: str):
             if self.channel.exchange_manager.exchange.FUNDING_WITH_MARK_PRICE:
                 mark_price, funding_rate = await self.channel.exchange_manager.exchange. \
                     get_mark_price_and_funding(symbol)
+                mark_price = decimal.Decimal(str(mark_price))
                 await self.push_funding_rate(symbol, funding_rate)
             else:
                 mark_price = await self.channel.exchange_manager.exchange.get_mark_price(symbol)
@@ -141,9 +143,13 @@ async def push_funding_rate(self, symbol, funding_rate):
                      next_funding_time=funding_rate[enums.ExchangeConstantsFundingColumns.NEXT_FUNDING_TIME.value],
                      timestamp=funding_rate[enums.ExchangeConstantsFundingColumns.LAST_FUNDING_TIME.value])
 
-    def _should_run(self) -> bool:
+    def _should_fetch_on_exchange(self) -> bool:
         return not (
                 self.channel.exchange_manager.exchange.FUNDING_WITH_MARK_PRICE
                 or self.channel.exchange_manager.exchange.MARK_PRICE_IN_TICKER
                 or self.channel.exchange_manager.exchange.MARK_PRICE_IN_POSITION
         )
+
+    def _should_subscribe(self):
+        return not self.channel.exchange_manager.is_future or \
+            not self.channel.exchange_manager.exchange.MARK_PRICE_IN_POSITION
diff --git a/octobot_trading/exchange_data/prices/price_events_manager.pxd b/octobot_trading/exchange_data/prices/price_events_manager.pxd
index b40dfd2c5..6e01e1de3 100644
--- a/octobot_trading/exchange_data/prices/price_events_manager.pxd
+++ b/octobot_trading/exchange_data/prices/price_events_manager.pxd
@@ -24,7 +24,7 @@ cdef class PriceEventsManager(util.Initializable):
 
     cpdef void reset(self)
     cpdef void handle_recent_trades(self, list recent_trades)
-    cpdef void handle_price(self, double price, double timestamp)
+    cpdef void handle_price(self, object price, double timestamp)
     cpdef object add_event(self, object price, double timestamp, bint trigger_above) # return asyncio.Event
     cpdef object remove_event(self, object event_to_remove) # object is an asyncio.Event
 
diff --git a/octobot_trading/exchange_data/prices/price_events_manager.py b/octobot_trading/exchange_data/prices/price_events_manager.py
index 007d206e6..f05645ae4 100644
--- a/octobot_trading/exchange_data/prices/price_events_manager.py
+++ b/octobot_trading/exchange_data/prices/price_events_manager.py
@@ -60,7 +60,7 @@ def handle_price(self, price, timestamp):
         :param price: the price to check
         :param timestamp: the timestamp to check
         """
-        for event_to_set in self._check_events(decimal.Decimal(str(price)), timestamp):
+        for event_to_set in self._check_events(price, timestamp):
             self._remove_and_set_event(event_to_set)
 
     def add_event(self, price, timestamp, trigger_above):
diff --git a/octobot_trading/exchange_data/prices/prices_manager.pxd b/octobot_trading/exchange_data/prices/prices_manager.pxd
index 957df7b25..0b60fa5ae 100644
--- a/octobot_trading/exchange_data/prices/prices_manager.pxd
+++ b/octobot_trading/exchange_data/prices/prices_manager.pxd
@@ -22,19 +22,19 @@ cdef class PricesManager(util.Initializable):
     cdef object exchange_manager
 
     cdef public object valid_price_received_event
+    cdef public object mark_price
 
-    cdef public double mark_price
     cdef public double mark_price_set_time
     cdef int price_validity
 
     cdef dict mark_price_from_sources
 
-    cdef void _set_mark_price_value(self, double mark_price)
+    cdef void _set_mark_price_value(self, object mark_price)
     cdef void _reset_prices(self)
     cdef void _ensure_price_validity(self)
     cdef bint _are_other_sources_valid(self, str mark_price_source)
     cdef int _compute_mark_price_validity_timeout(self)
 
-    cpdef bint set_mark_price(self, double mark_price, str mark_price_source)
+    cpdef bint set_mark_price(self, object mark_price, str mark_price_source)
 
 cpdef double calculate_mark_price_from_recent_trade_prices(list recent_trade_prices)
diff --git a/octobot_trading/exchange_data/prices/prices_manager.py b/octobot_trading/exchange_data/prices/prices_manager.py
index 33422e902..ed7e18f1b 100644
--- a/octobot_trading/exchange_data/prices/prices_manager.py
+++ b/octobot_trading/exchange_data/prices/prices_manager.py
@@ -15,19 +15,20 @@
 #  License along with this library.
 import asyncio
 
-import octobot_commons.constants as constants
+import octobot_commons.constants as commons_constants
 import octobot_commons.logging as logging
 
 import octobot_trading.enums as enums
 import octobot_trading.util as util
+import octobot_trading.constants as constants
 
 
 class PricesManager(util.Initializable):
-    MARK_PRICE_FETCH_TIMEOUT = 5 * constants.MINUTE_TO_SECONDS
+    MARK_PRICE_FETCH_TIMEOUT = 5 * commons_constants.MINUTE_TO_SECONDS
 
     def __init__(self, exchange_manager):
         super().__init__()
-        self.mark_price = 0
+        self.mark_price = constants.ZERO
         self.mark_price_set_time = 0
         self.mark_price_from_sources = {}
         self.exchange_manager = exchange_manager
@@ -127,10 +128,10 @@ def _ensure_price_validity(self):
     def _compute_mark_price_validity_timeout(self):
         refresh_threshold = self.exchange_manager.get_rest_pairs_refresh_threshold()
         if refresh_threshold is enums.RestExchangePairsRefreshMaxThresholds.FAST:
-            return 3 * constants.MINUTE_TO_SECONDS
+            return 3 * commons_constants.MINUTE_TO_SECONDS
         if refresh_threshold is enums.RestExchangePairsRefreshMaxThresholds.MEDIUM:
-            return 5 * constants.MINUTE_TO_SECONDS
-        return 7 * constants.MINUTE_TO_SECONDS
+            return 5 * commons_constants.MINUTE_TO_SECONDS
+        return 7 * commons_constants.MINUTE_TO_SECONDS
 
     def _is_mark_price_valid(self, mark_price_updated_time):
         """
diff --git a/octobot_trading/exchange_data/ticker/channel/ticker_updater.py b/octobot_trading/exchange_data/ticker/channel/ticker_updater.py
index d81ca4f51..54c3f864b 100644
--- a/octobot_trading/exchange_data/ticker/channel/ticker_updater.py
+++ b/octobot_trading/exchange_data/ticker/channel/ticker_updater.py
@@ -14,6 +14,8 @@
 #
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
+import decimal
+
 import asyncio
 
 import octobot_trading.errors as errors
@@ -146,7 +148,8 @@ async def extract_mark_price(self, symbol: str, ticker: dict):
             ticker = self.channel.exchange_manager.exchange.parse_mark_price(ticker, from_ticker=True)
             await exchanges_channel.get_chan(constants.MARK_PRICE_CHANNEL,
                                              self.channel.exchange_manager.id).get_internal_producer(). \
-                push(symbol=symbol, mark_price=ticker[enums.ExchangeConstantsMarkPriceColumns.MARK_PRICE.value])
+                push(symbol=symbol,
+                     mark_price=decimal.Decimal(str(ticker[enums.ExchangeConstantsMarkPriceColumns.MARK_PRICE.value])))
         except Exception as e:
             self.logger.exception(e, True, f"Fail to update mark price from ticker : {e}")
 
@@ -156,7 +159,10 @@ async def extract_funding_rate(self, symbol: str, ticker: dict):
             await exchanges_channel.get_chan(constants.FUNDING_CHANNEL,
                                              self.channel.exchange_manager.id).get_internal_producer(). \
                 push(symbol=symbol,
-                     funding_rate=ticker[enums.ExchangeConstantsFundingColumns.FUNDING_RATE.value],
+                     funding_rate=decimal.Decimal(str(
+                         ticker[enums.ExchangeConstantsFundingColumns.FUNDING_RATE.value])),
+                     predicted_funding_rate=decimal.Decimal(str(
+                         ticker[enums.ExchangeConstantsFundingColumns.PREDICTED_FUNDING_RATE.value])),
                      next_funding_time=ticker[enums.ExchangeConstantsFundingColumns.NEXT_FUNDING_TIME.value],
                      timestamp=ticker[enums.ExchangeConstantsFundingColumns.LAST_FUNDING_TIME.value])
         except Exception as e:
diff --git a/octobot_trading/exchanges/connectors/ccxt_exchange.py b/octobot_trading/exchanges/connectors/ccxt_exchange.py
index f86c7c4b2..8edb4b49b 100644
--- a/octobot_trading/exchanges/connectors/ccxt_exchange.py
+++ b/octobot_trading/exchanges/connectors/ccxt_exchange.py
@@ -358,9 +358,12 @@ async def cancel_order(self, order_id: str, symbol: str = None, **kwargs: dict)
             self.logger.exception(e, True, f"Order {order_id} failed to cancel | {e} ({e.__class__.__name__})")
         return cancel_resp is not None
 
-    async def get_open_positions(self, **kwargs: dict) -> list:
+    async def get_positions(self, **kwargs: dict) -> list:
         return await self.client.fetch_positions()
 
+    async def get_symbol_positions(self, symbol: str, **kwargs: dict) -> list:
+        return await self.client.fetch_positions(symbols=[symbol])
+
     async def get_funding_rate(self, symbol: str, **kwargs: dict) -> dict:
         return await self.client.fetch_funding_rate(symbol=symbol)
 
@@ -374,6 +377,9 @@ async def set_symbol_margin_type(self, symbol: str, isolated: bool):
         return await self.client.set_margin_mode(symbol=symbol,
                                                  marginType=self.CCXT_ISOLATED if isolated else self.CCXT_CROSSED)
 
+    async def set_symbol_position_mode(self, symbol: str, one_way: bool):
+        return await self.client.set_position_mode(self, hedged=not one_way, symbol=symbol)
+
     def get_trade_fee(self, symbol, order_type, quantity, price, taker_or_maker):
         fees = self.client.calculate_fee(symbol=symbol,
                                          type=order_type,
diff --git a/octobot_trading/exchanges/connectors/cryptofeed_websocket_connector.py b/octobot_trading/exchanges/connectors/cryptofeed_websocket_connector.py
index 3556dc47a..3c6a15c0f 100644
--- a/octobot_trading/exchanges/connectors/cryptofeed_websocket_connector.py
+++ b/octobot_trading/exchanges/connectors/cryptofeed_websocket_connector.py
@@ -14,6 +14,7 @@
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
 import asyncio
+import decimal
 import logging
 import os
 import threading
@@ -610,7 +611,7 @@ async def ticker(self, ticker: cryptofeed_types.Ticker, receipt_timestamp: float
         # Push (ask + bid) / 2 as close price in MARK_PRICE channel
         await self.push_to_channel(channel_name=trading_constants.MARK_PRICE_CHANNEL,
                                    symbol=symbol,
-                                   mark_price=float((ticker.ask + ticker.bid) / 2),
+                                   mark_price=(ticker.ask + ticker.bid) / decimal.Decimal(2),
                                    mark_price_source=trading_enums.MarkPriceSources.TICKER_CLOSE_PRICE.value)
 
     async def trades(self, trade: cryptofeed_types.Trade, receipt_timestamp: float):
diff --git a/octobot_trading/exchanges/implementations/future_ccxt_exchange.pxd b/octobot_trading/exchanges/implementations/future_ccxt_exchange.pxd
index 08032da16..84151ef33 100644
--- a/octobot_trading/exchanges/implementations/future_ccxt_exchange.pxd
+++ b/octobot_trading/exchanges/implementations/future_ccxt_exchange.pxd
@@ -17,4 +17,6 @@
 cimport octobot_trading.exchanges.types as exchanges_types
 
 cdef class FutureCCXTExchange(exchanges_types.FutureExchange):
-    pass
+    cpdef str get_default_type(self)
+
+    cdef str _get_pair_market_type(self, str pair)
diff --git a/octobot_trading/exchanges/implementations/future_ccxt_exchange.py b/octobot_trading/exchanges/implementations/future_ccxt_exchange.py
index 89343a287..d5493f09f 100644
--- a/octobot_trading/exchanges/implementations/future_ccxt_exchange.py
+++ b/octobot_trading/exchanges/implementations/future_ccxt_exchange.py
@@ -34,6 +34,8 @@ async def initialize_impl(self):
         self.symbols = self.connector.symbols
         self.time_frames = self.connector.time_frames
 
+        self.connector.client.options['defaultType'] = self.get_default_type()
+
     async def stop(self) -> None:
         await self.connector.stop()
         self.exchange_manager = None
@@ -42,6 +44,9 @@ async def stop(self) -> None:
     def is_supporting_exchange(cls, exchange_candidate_name) -> bool:
         return exchange_connectors.CCXTExchange.is_supporting_exchange(exchange_candidate_name)
 
+    def get_default_type(self):
+        return 'future'
+
     def get_exchange_current_time(self):
         return self.connector.get_exchange_current_time()
 
@@ -169,8 +174,11 @@ def clean_trade(self, trade):
     def clean_order(self, order):
         return self.connector.clean_order(order)
 
-    async def get_open_positions(self, **kwargs: dict) -> list:
-        return await self.connector.get_open_positions(**kwargs)
+    async def get_positions(self, **kwargs: dict) -> list:
+        return await self.connector.get_positions(**kwargs)
+
+    async def get_symbol_positions(self, symbol: str, **kwargs: dict) -> list:
+        return await self.connector.get_symbol_positions(symbol=symbol, **kwargs)
 
     async def get_funding_rate(self, symbol: str, **kwargs: dict) -> dict:
         return await self.connector.get_funding_rate(symbol=symbol, **kwargs)
@@ -184,6 +192,13 @@ async def set_symbol_leverage(self, symbol: str, leverage: int):
     async def set_symbol_margin_type(self, symbol: str, isolated: bool):
         return await self.connector.set_symbol_margin_type(symbol=symbol, isolated=isolated)
 
+    async def set_symbol_position_mode(self, symbol: str, one_way: bool):
+        return await self.connector.set_symbol_position_mode(symbol=symbol, one_way=one_way)
+
+    def _get_pair_market_type(self, pair):
+        return self.connector.client.safe_string(
+            self.connector.client.safe_value(self.connector.client.options, 'marketTypes', {}), pair, None)
+
     def parse_position(self, position_dict) -> dict:
         try:
             position_dict.update({
diff --git a/octobot_trading/exchanges/implementations/future_exchange_simulator.py b/octobot_trading/exchanges/implementations/future_exchange_simulator.py
index 450f25e2c..6d1f748b1 100644
--- a/octobot_trading/exchanges/implementations/future_exchange_simulator.py
+++ b/octobot_trading/exchanges/implementations/future_exchange_simulator.py
@@ -13,23 +13,12 @@
 #
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
-import decimal
-
 import octobot_trading.constants as constants
-import octobot_trading.enums as enums
-import octobot_trading.exchange_data.contracts as contracts
 import octobot_trading.exchanges.connectors as exchange_connectors
 import octobot_trading.exchanges.types as exchanges_types
 
 
 class FutureExchangeSimulator(exchanges_types.FutureExchange):
-    DEFAULT_SYMBOL_LEVERAGE = constants.ONE
-    DEFAULT_SYMBOL_MAX_LEVERAGE = constants.ONE_HUNDRED
-    DEFAULT_SYMBOL_MARGIN_TYPE = enums.MarginType.ISOLATED
-    DEFAULT_SYMBOL_CONTRACT_TYPE = enums.FutureContractType.LINEAR_PERPETUAL
-    DEFAULT_SYMBOL_POSITION_MODE = enums.PositionMode.ONE_WAY
-    DEFAULT_SYMBOL_FUNDING_RATE = decimal.Decimal("0.00005")
-
     def __init__(self, config, exchange_manager, backtesting):
         super().__init__(config, exchange_manager)
         self.exchange_importers = []
@@ -107,38 +96,36 @@ async def load_pair_future_contract(self, pair: str):
         Create a new FutureContract for the pair
         :param pair: the pair
         """
-        self.logger.debug(f"Loading {pair} contract...")
-        self.pair_contracts[pair] = contracts.FutureContract(
+        self.create_pair_contract(
             pair=pair,
-            current_leverage=self.DEFAULT_SYMBOL_LEVERAGE,
-            maximum_leverage=self.DEFAULT_SYMBOL_MAX_LEVERAGE,
-            margin_type=self.DEFAULT_SYMBOL_MARGIN_TYPE,
-            contract_type=self.DEFAULT_SYMBOL_CONTRACT_TYPE,
-            position_mode=self.DEFAULT_SYMBOL_POSITION_MODE)
+            current_leverage=constants.DEFAULT_SYMBOL_LEVERAGE,
+            margin_type=constants.DEFAULT_SYMBOL_MARGIN_TYPE,
+            contract_type=constants.DEFAULT_SYMBOL_CONTRACT_TYPE,
+            position_mode=constants.DEFAULT_SYMBOL_POSITION_MODE,
+            maintenance_margin_rate=constants.DEFAULT_SYMBOL_MAINTENANCE_MARGIN_RATE,
+            maximum_leverage=constants.DEFAULT_SYMBOL_MAX_LEVERAGE
+        )
 
     async def get_symbol_leverage(self, symbol: str):
-        return self.DEFAULT_SYMBOL_LEVERAGE
+        return constants.DEFAULT_SYMBOL_LEVERAGE
 
     async def get_margin_type(self, symbol: str):
-        return self.DEFAULT_SYMBOL_MARGIN_TYPE
+        return constants.DEFAULT_SYMBOL_MARGIN_TYPE
 
     async def get_contract_type(self, symbol: str):
-        return self.DEFAULT_SYMBOL_CONTRACT_TYPE
+        return constants.DEFAULT_SYMBOL_CONTRACT_TYPE
 
     async def get_funding_rate(self, symbol: str, **kwargs: dict):
-        return self.DEFAULT_SYMBOL_FUNDING_RATE
+        return constants.DEFAULT_SYMBOL_FUNDING_RATE
 
     async def get_position_mode(self, symbol: str, **kwargs: dict):
-        return self.DEFAULT_SYMBOL_POSITION_MODE
+        return constants.DEFAULT_SYMBOL_POSITION_MODE
 
     async def set_symbol_leverage(self, symbol: str, leverage: int):
-        pair_contract = self.get_pair_future_contract(symbol)
-        pair_contract.set_current_leverage(leverage)
+        pass  # let trader update the contract
 
     async def set_symbol_margin_type(self, symbol: str, isolated: bool):
-        pair_contract = self.get_pair_future_contract(symbol)
-        pair_contract.set_margin_type(is_isolated=isolated)
+        pass  # let trader update the contract
 
     async def set_symbol_position_mode(self, symbol: str, one_way: bool):
-        pair_contract = self.get_pair_future_contract(symbol)
-        pair_contract.set_position_mode(is_one_way=one_way)
+        pass  # let trader update the contract
diff --git a/octobot_trading/exchanges/implementations/spot_ccxt_exchange.pxd b/octobot_trading/exchanges/implementations/spot_ccxt_exchange.pxd
index 74506384b..495842135 100644
--- a/octobot_trading/exchanges/implementations/spot_ccxt_exchange.pxd
+++ b/octobot_trading/exchanges/implementations/spot_ccxt_exchange.pxd
@@ -17,4 +17,4 @@
 cimport octobot_trading.exchanges.types as exchanges_types
 
 cdef class SpotCCXTExchange(exchanges_types.SpotExchange):
-    pass
+    cpdef str get_default_type(self)
diff --git a/octobot_trading/exchanges/implementations/spot_ccxt_exchange.py b/octobot_trading/exchanges/implementations/spot_ccxt_exchange.py
index 8fcbc6c30..3cf2ec199 100644
--- a/octobot_trading/exchanges/implementations/spot_ccxt_exchange.py
+++ b/octobot_trading/exchanges/implementations/spot_ccxt_exchange.py
@@ -37,6 +37,8 @@ def __init__(self, config, exchange_manager):
         super().__init__(config, exchange_manager)
         self.connector = exchange_connectors.CCXTExchange(config, exchange_manager)
 
+        self.connector.client.options['defaultType'] = self.get_default_type()
+
     async def initialize_impl(self):
         await self.connector.initialize()
         self.symbols = self.connector.symbols
@@ -50,6 +52,10 @@ async def stop(self) -> None:
     def is_supporting_exchange(cls, exchange_candidate_name) -> bool:
         return exchange_connectors.CCXTExchange.is_supporting_exchange(exchange_candidate_name)
 
+    def get_default_type(self):
+        # keep default value
+        return self.connector.client.options['defaultType']
+
     async def create_order(self, order_type: enums.TraderOrderType, symbol: str, quantity: decimal.Decimal,
                            price: decimal.Decimal = None, stop_price: decimal.Decimal = None,
                            params: dict = None, **kwargs: dict) -> typing.Optional[dict]:
diff --git a/octobot_trading/exchanges/traders/trader.pxd b/octobot_trading/exchanges/traders/trader.pxd
index bbc611388..6f9738e15 100644
--- a/octobot_trading/exchanges/traders/trader.pxd
+++ b/octobot_trading/exchanges/traders/trader.pxd
@@ -37,3 +37,5 @@ cdef class Trader(util.Initializable):
     cpdef str parse_order_id(self, str order_id)
     cpdef object set_risk(self, object risk)
     cpdef object convert_order_to_trade(self, object order)
+
+    cdef bint _has_open_position(self, str symbol)
diff --git a/octobot_trading/exchanges/traders/trader.py b/octobot_trading/exchanges/traders/trader.py
index 12d47bb73..7c95c8adb 100644
--- a/octobot_trading/exchanges/traders/trader.py
+++ b/octobot_trading/exchanges/traders/trader.py
@@ -17,14 +17,15 @@
 import decimal
 
 import octobot_commons.logging as logging
-
 import octobot_commons.constants
+
 import octobot_trading.personal_data.orders.order_factory as order_factory
 import octobot_trading.personal_data.orders.order_util as order_util
 import octobot_trading.personal_data.orders.decimal_order_adapter as decimal_order_adapter
 import octobot_trading.personal_data.trades.trade_factory as trade_factory
-import octobot_trading.enums
+import octobot_trading.enums as enums
 import octobot_trading.constants
+import octobot_trading.errors as errors
 import octobot_trading.util as util
 
 
@@ -302,21 +303,104 @@ def convert_order_to_trade(self, order):
     Positions
     """
 
-    async def close_position(self, position, limit_price=None) -> None:
+    async def close_position(self, position, limit_price=None, timeout=1):
         """
         Creates a close position order
         :param position: the position to close
         :param limit_price: the close order limit price if None uses a market order
+        :param timeout: the mark price timeout
+        :return: the list of created orders
         """
-        # TODO use reduce only param
-        if limit_price is None:
-            # TODO market order
-            pass
-        else:
-            pass
+        created_orders = []
+        async with self.exchange_manager.exchange_personal_data.portfolio_manager.portfolio.lock:
+            _, _, _, price, symbol_market = await order_util.get_pre_order_data(self.exchange_manager,
+                                                                                position.symbol,
+                                                                                timeout=timeout)
+            for order_quantity, order_price in decimal_order_adapter.decimal_check_and_adapt_order_details_if_necessary(
+                    position.get_quantity_to_close().copy_abs(), price, symbol_market):
+
+                if limit_price is not None:
+                    order_type = enums.TraderOrderType.SELL_LIMIT \
+                        if position.is_long() else enums.TraderOrderType.BUY_LIMIT
+                else:
+                    order_type = enums.TraderOrderType.SELL_MARKET \
+                        if position.is_long() else enums.TraderOrderType.BUY_MARKET
+
+                # TODO add reduce_only or close_position attribute
+                current_order = order_factory.create_order_instance(trader=self,
+                                                                    order_type=order_type,
+                                                                    symbol=position.symbol,
+                                                                    current_price=order_price,
+                                                                    quantity=order_quantity,
+                                                                    price=limit_price
+                                                                    if limit_price is not None else order_price)
+                created_orders.append(
+                    await self.create_order(current_order,
+                                            self.exchange_manager.exchange_personal_data.portfolio_manager.portfolio))
+        return created_orders
+
+    async def set_leverage(self, symbol, side, leverage):
+        """
+        Updates the symbol contract leverage
+        Can raise InvalidLeverageValue if leverage value is not matching requirements
+        :param symbol: the symbol to update
+        :param side: the side to update (TODO)
+        :param leverage: the new leverage value
+        """
+        contract = self.exchange_manager.exchange.get_pair_future_contract(symbol)
+        if not contract.check_leverage_update(leverage):
+            raise errors.InvalidLeverageValue(f"Trying to update leverage with {leverage} "
+                                              f"but maximal value is {contract.maximum_leverage}")
+        if not self.simulate:
+            await self.exchange_manager.exchange.set_symbol_leverage(
+                symbol=symbol,
+                leverage=leverage
+            )
+        contract.set_current_leverage(leverage)
+
+    async def set_margin_type(self, symbol, side, margin_type):
+        """
+        Updates the symbol contract margin type
+        TODO: recreate position instances if any
+        :param symbol: the symbol to update
+        :param side: the side to update (TODO)
+        :param margin_type: the new margin type (enums.MarginType)
+        """
+        contract = self.exchange_manager.exchange.get_pair_future_contract(symbol)
+        if not self.simulate:
+            await self.exchange_manager.exchange.set_symbol_margin_type(
+                symbol=symbol,
+                isolated=margin_type is enums.MarginType.ISOLATED
+            )
+        contract.set_margin_type(
+            is_isolated=margin_type is enums.MarginType.ISOLATED,
+            is_cross=margin_type is enums.MarginType.CROSS
+        )
 
-    async def set_leverage(self, position):
-        pass  # TODO
+    async def set_position_mode(self, symbol, position_mode):
+        """
+        Updates the symbol contract position mode
+        :param symbol: the symbol to update
+        :param position_mode: the new position mode (enums.PositionMode)
+        """
+        if self._has_open_position(symbol):
+            raise errors.TooManyOpenPositionError("Can't update position mode when having open position.")
+        contract = self.exchange_manager.exchange.get_pair_future_contract(symbol)
+        if not self.simulate:
+            await self.exchange_manager.exchange.set_symbol_position_mode(
+                symbol=symbol,
+                one_way=position_mode is enums.PositionMode.ONE_WAY
+            )
+        contract.set_position_mode(
+            is_one_way=position_mode is enums.PositionMode.ONE_WAY,
+            is_hedge=position_mode is enums.PositionMode.HEDGE
+        )
 
-    async def set_margin_type(self, new_margin_type):
-        pass  # TODO
+    def _has_open_position(self, symbol):
+        """
+        Checks if open position exists for :symbol:
+        :param symbol: the position symbol
+        :return: True if open position for :symbol: exists
+        """
+        return len(self.exchange_manager.exchange_personal_data.positions_manager.get_symbol_positions(
+            symbol=symbol)) != 0
diff --git a/octobot_trading/exchanges/types/future_exchange.pxd b/octobot_trading/exchanges/types/future_exchange.pxd
index 89d8642fa..d91d3236c 100644
--- a/octobot_trading/exchanges/types/future_exchange.pxd
+++ b/octobot_trading/exchanges/types/future_exchange.pxd
@@ -15,13 +15,22 @@
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
 cimport octobot_trading.exchanges.abstract_exchange as abstract_exchange
+cimport octobot_trading.exchange_data.contracts as contracts
 
 cdef class FutureExchange(abstract_exchange.AbstractExchange):
     cdef dict pair_contracts
 
+    cpdef void create_pair_contract(self, str pair,
+                                    object current_leverage, object margin_type,
+                                    object contract_type, object position_mode,
+                                    object maintenance_margin_rate,
+                                    object maximum_leverage=*)
     cpdef double calculate_position_value(self, double quantity, double mark_price)
-    cpdef object get_pair_future_contract(self, str pair) # TODO : fix cimport positions.FutureContract
-    cpdef void set_pair_future_contract(self, str pair, object future_contract) # TODO : fix cimport positions.FutureContract
+    cpdef contracts.FutureContract get_pair_future_contract(self, str pair)
+    cpdef void set_pair_future_contract(self, str pair, contracts.FutureContract future_contract)
+    cpdef bint is_linear_symbol(self, str symbol)
+    cpdef bint is_inverse_symbol(self, str symbol)
+    cpdef bint is_futures_symbol(self, str symbol)
 
     """
     Parsers
@@ -31,5 +40,5 @@ cdef class FutureExchange(abstract_exchange.AbstractExchange):
     cpdef dict parse_funding(self, dict funding_dict, bint from_ticker=*)
     cpdef dict parse_mark_price(self, dict mark_price_dict, bint from_ticker=*)
     cpdef dict parse_liquidation(self, dict liquidation_dict)
-    cpdef str parse_position_status(self, str status)
-    cpdef str parse_position_side(self, str side)
+    cpdef object parse_position_status(self, str status)
+    cpdef object parse_position_side(self, str side)
diff --git a/octobot_trading/exchanges/types/future_exchange.py b/octobot_trading/exchanges/types/future_exchange.py
index 52dca081d..2fabb6f88 100644
--- a/octobot_trading/exchanges/types/future_exchange.py
+++ b/octobot_trading/exchanges/types/future_exchange.py
@@ -39,15 +39,39 @@ def __init__(self, config, exchange_manager):
 
     async def load_pair_future_contract(self, pair: str):
         """
-        Create a new FutureContract for the pair
-        :param pair: the pair
+        Load and create a new FutureContract for the pair
+        :param pair: the contract pair
         """
-        self.logger.debug(f"Loading {pair} contract...")
-        self.pair_contracts[pair] = contracts.FutureContract(
+        self.create_pair_contract(
             pair=pair,
             current_leverage=await self.get_symbol_leverage(pair),
             margin_type=await self.get_margin_type(pair),
-            contract_type=await self.get_contract_type(pair))
+            contract_type=await self.get_contract_type(pair),
+            position_mode=await self.get_position_mode(pair),
+            maintenance_margin_rate=await self.get_maintenance_margin_rate(pair),
+        )
+
+    def create_pair_contract(self, pair, current_leverage, margin_type,
+                             contract_type, position_mode, maintenance_margin_rate, maximum_leverage=None):
+        """
+        Create a new FutureContract for the pair
+        # TODO: support 1 contract by side when using hedge position mode --> think about another way to store contracts
+        :param pair: the contract pair
+        :param current_leverage: the contract current leverage
+        :param margin_type: the contract margin type
+        :param contract_type: the contract type
+        :param position_mode: the contract position mode
+        :param maintenance_margin_rate: the contract maintenance margin rate
+        :param maximum_leverage: the contract maximum leverage
+        """
+        self.logger.debug(f"Creating {pair} contract...")
+        self.pair_contracts[pair] = contracts.FutureContract(pair=pair,
+                                                             margin_type=margin_type,
+                                                             contract_type=contract_type,
+                                                             maximum_leverage=maximum_leverage,
+                                                             current_leverage=current_leverage,
+                                                             position_mode=position_mode,
+                                                             maintenance_margin_rate=maintenance_margin_rate)
 
     def get_pair_future_contract(self, pair):
         """
@@ -73,20 +97,20 @@ def set_pair_future_contract(self, pair, future_contract):
     Positions
     """
 
-    async def get_symbol_open_positions(self, symbol: str) -> list:
+    async def get_symbol_positions(self, symbol: str) -> list:
         """
-        Get the current user open symbol position list
+        Get the current user symbol position list
         :param symbol: the position symbol
-        :return: the user open symbol position list
+        :return: the user symbol position list
         """
-        raise NotImplementedError("get_symbol_open_positions is not implemented")
+        raise NotImplementedError("get_symbol_positions is not implemented")
 
-    async def get_open_positions(self, **kwargs: dict) -> list:
+    async def get_positions(self, **kwargs: dict) -> list:
         """
-        Get the current user open position list
-        :return: the user open position list
+        Get the current user position list
+        :return: the user position list
         """
-        raise NotImplementedError("get_open_positions is not implemented")
+        raise NotImplementedError("get_positions is not implemented")
 
     async def get_mark_price(self, symbol: str) -> dict:
         raise NotImplementedError("get_mark_price is not implemented")
@@ -155,6 +179,13 @@ async def get_position_mode(self, symbol: str):
         """
         raise NotImplementedError("get_position_mode is not implemented")
 
+    async def get_maintenance_margin_rate(self, symbol: str):
+        """
+        :param symbol: the symbol
+        :return: the symbol maintenance margin rate
+        """
+        raise NotImplementedError("get_maintenance_margin_rate is not implemented")
+
     async def set_symbol_leverage(self, symbol: str, leverage: int):
         """
         Set the symbol leverage
@@ -182,6 +213,27 @@ async def set_symbol_position_mode(self, symbol: str, one_way: bool):
         """
         raise NotImplementedError("set_symbol_position_mode is not implemented")
 
+    def is_linear_symbol(self, symbol):
+        """
+        :param symbol: the symbol
+        :return: True if the symbol is related to a linear contract
+        """
+        raise NotImplementedError("is_linear_symbol is not implemented")
+
+    def is_inverse_symbol(self, symbol):
+        """
+        :param symbol: the symbol
+        :return: True if the symbol is related to an inverse contract
+        """
+        raise NotImplementedError("is_inverse_symbol is not implemented")
+
+    def is_futures_symbol(self, symbol):
+        """
+        :param symbol: the symbol
+        :return: True if the symbol is related to a futures contract
+        """
+        raise NotImplementedError("is_futures_symbol is not implemented")
+
     """
     Parsers
     """
@@ -238,8 +290,8 @@ def parse_position_side(self, side):
         :param side: the raw side
         :return: the uniformized PositionSide instance from the raw side
         """
-        return octobot_trading.enums.PositionSide.LONG.value \
-            if side == self.LONG_STR else octobot_trading.enums.PositionSide.SHORT.value
+        return octobot_trading.enums.PositionSide.LONG \
+            if side == self.LONG_STR else octobot_trading.enums.PositionSide.SHORT
 
     def calculate_position_value(self, quantity, mark_price):
         """
diff --git a/octobot_trading/personal_data/__init__.pxd b/octobot_trading/personal_data/__init__.pxd
index 7a77dc5cf..e4028ff80 100644
--- a/octobot_trading/personal_data/__init__.pxd
+++ b/octobot_trading/personal_data/__init__.pxd
@@ -92,7 +92,19 @@ from octobot_trading.personal_data.trades cimport (
     TradesUpdater,
     Trade,
 )
-
+from octobot_trading.personal_data cimport transactions
+from octobot_trading.personal_data.transactions cimport (
+    TransactionsManager,
+    Transaction,
+    BlockchainTransaction,
+    FeeTransaction,
+    RealisedPnlTransaction,
+    TransferTransaction,
+    create_blockchain_transaction,
+    create_realised_pnl_transaction,
+    create_fee_transaction,
+    create_transfer_transaction,
+)
 from octobot_trading.personal_data cimport exchange_personal_data
 from octobot_trading.personal_data.exchange_personal_data cimport (
     ExchangePersonalData,
@@ -171,4 +183,14 @@ __all__ = [
     "TradesUpdater",
     "Trade",
     "ExchangePersonalData",
+    "TransactionsManager",
+    "Transaction",
+    "BlockchainTransaction",
+    "FeeTransaction",
+    "RealisedPnlTransaction",
+    "TransferTransaction",
+    "create_blockchain_transaction",
+    "create_realised_pnl_transaction",
+    "create_fee_transaction",
+    "create_transfer_transaction",
 ]
diff --git a/octobot_trading/personal_data/__init__.py b/octobot_trading/personal_data/__init__.py
index 1d72733a6..8faa580b0 100644
--- a/octobot_trading/personal_data/__init__.py
+++ b/octobot_trading/personal_data/__init__.py
@@ -135,6 +135,19 @@
     TradesUpdater,
     Trade,
 )
+from octobot_trading.personal_data import transactions
+from octobot_trading.personal_data.transactions import (
+    TransactionsManager,
+    Transaction,
+    BlockchainTransaction,
+    FeeTransaction,
+    RealisedPnlTransaction,
+    TransferTransaction,
+    create_blockchain_transaction,
+    create_realised_pnl_transaction,
+    create_fee_transaction,
+    create_transfer_transaction,
+)
 from octobot_trading.personal_data import exchange_personal_data
 from octobot_trading.personal_data.exchange_personal_data import (
     ExchangePersonalData,
@@ -284,4 +297,14 @@
     "AUTHENTICATED_UPDATER_SIMULATOR_PRODUCERS",
     "TraderOrderTypeClasses",
     "TraderPositionTypeClasses",
+    "TransactionsManager",
+    "Transaction",
+    "BlockchainTransaction",
+    "FeeTransaction",
+    "RealisedPnlTransaction",
+    "TransferTransaction",
+    "create_blockchain_transaction",
+    "create_realised_pnl_transaction",
+    "create_fee_transaction",
+    "create_transfer_transaction",
 ]
diff --git a/octobot_trading/personal_data/exchange_personal_data.pxd b/octobot_trading/personal_data/exchange_personal_data.pxd
index 39d97a1ca..51ca7a9fc 100644
--- a/octobot_trading/personal_data/exchange_personal_data.pxd
+++ b/octobot_trading/personal_data/exchange_personal_data.pxd
@@ -19,6 +19,7 @@ cimport octobot_trading.personal_data.orders.order as order_class
 cimport octobot_trading.personal_data.portfolios.portfolio_manager as portfolio_manager
 cimport octobot_trading.personal_data.positions.positions_manager as positions_manager
 cimport octobot_trading.personal_data.trades.trades_manager as trades_manager
+cimport octobot_trading.personal_data.transactions.transactions_manager as transactions_manager
 cimport octobot_trading.util as util
 
 cdef class ExchangePersonalData(util.Initializable):
@@ -34,6 +35,7 @@ cdef class ExchangePersonalData(util.Initializable):
     cdef public trades_manager.TradesManager trades_manager
     cdef public orders_manager.OrdersManager orders_manager
     cdef public positions_manager.PositionsManager positions_manager
+    cdef public transactions_manager.TransactionsManager transactions_manager
 
     cpdef object get_order_portfolio(self, order_class.Order order)
     cpdef void clear(self)
diff --git a/octobot_trading/personal_data/exchange_personal_data.py b/octobot_trading/personal_data/exchange_personal_data.py
index 169c50157..a24149187 100644
--- a/octobot_trading/personal_data/exchange_personal_data.py
+++ b/octobot_trading/personal_data/exchange_personal_data.py
@@ -23,6 +23,8 @@
 import octobot_trading.personal_data.positions.positions_manager as positions_manager
 import octobot_trading.personal_data.orders.orders_manager as orders_manager
 import octobot_trading.personal_data.trades.trades_manager as trades_manager
+import octobot_trading.personal_data.transactions.transactions_manager as transactions_manager
+import octobot_trading.personal_data.transactions.transaction_factory as transaction_factory
 import octobot_trading.util as util
 
 
@@ -36,10 +38,12 @@ def __init__(self, exchange_manager):
 
         self.trader = None
         self.exchange = None
+
         self.portfolio_manager = None
         self.trades_manager = None
         self.orders_manager = None
         self.positions_manager = None
+        self.transactions_manager = None
 
     async def initialize_impl(self):
         self.trader = self.exchange_manager.trader
@@ -51,10 +55,12 @@ async def initialize_impl(self):
                 self.trades_manager = trades_manager.TradesManager(self.trader)
                 self.orders_manager = orders_manager.OrdersManager(self.trader)
                 self.positions_manager = positions_manager.PositionsManager(self.trader)
+                self.transactions_manager = transactions_manager.TransactionsManager()
                 await self.portfolio_manager.initialize()
                 await self.trades_manager.initialize()
                 await self.orders_manager.initialize()
                 await self.positions_manager.initialize()
+                await self.transactions_manager.initialize()
             except Exception as e:
                 self.logger.exception(e, True, f"Error when initializing : {e}. "
                                                f"{self.exchange.name} personal data disabled.")
@@ -109,6 +115,9 @@ async def handle_portfolio_update_from_funding(self, position, funding_rate,
         try:
             changed: bool = await self.portfolio_manager.handle_balance_update_from_funding(
                 position=position, funding_rate=funding_rate, require_exchange_update=require_exchange_update)
+            transaction_factory.create_fee_transaction(self.exchange_manager, position.get_currency(), position.symbol,
+                                                       quantity=position.value * funding_rate,
+                                                       funding_rate=funding_rate)
             if should_notify:
                 await self.handle_portfolio_update_notification(self.portfolio_manager.portfolio.portfolio)
             return changed
@@ -132,10 +141,10 @@ async def handle_portfolio_profitability_update(self, balance, mark_price, symbo
             portfolio_profitability = self.portfolio_manager.portfolio_profitability
 
             if balance is not None:
-                await self.portfolio_manager.handle_balance_updated()
+                self.portfolio_manager.handle_balance_updated()
 
             if mark_price is not None and symbol is not None:
-                await self.portfolio_manager.handle_mark_price_update(symbol=symbol, mark_price=mark_price)
+                self.portfolio_manager.handle_mark_price_update(symbol=symbol, mark_price=mark_price)
 
             if should_notify:
                 await exchange_channel.get_chan(constants.BALANCE_PROFITABILITY_CHANNEL,
@@ -263,7 +272,8 @@ async def handle_position_update(self, symbol, side, position, should_notify: bo
         try:
             changed: bool = await self.positions_manager.upsert_position(symbol, side, position)
             if should_notify:
-                position_instance = self.positions_manager.get_symbol_position(symbol=symbol, side=side)
+                position_instance = self.positions_manager.get_symbol_position(
+                    symbol=symbol, side=None if position.symbol_contract.is_one_way_position_mode() else side)
                 await self.handle_position_update_notification(position_instance, is_updated=changed)
             return changed
         except Exception as e:
@@ -308,3 +318,5 @@ def clear(self):
             self.positions_manager.clear()
         if self.trades_manager is not None:
             self.trades_manager.clear()
+        if self.transactions_manager is not None:
+            self.transactions_manager.clear()
diff --git a/octobot_trading/personal_data/orders/order.py b/octobot_trading/personal_data/orders/order.py
index 2e1ceeabb..35a3ea3ab 100644
--- a/octobot_trading/personal_data/orders/order.py
+++ b/octobot_trading/personal_data/orders/order.py
@@ -243,6 +243,10 @@ def is_refreshing(self):
         return self.state is not None and self.state.is_refreshing()
 
     def get_position_side(self, future_contract):
+        """
+        :param future_contract: the associated future contract
+        :return: the position side if it can be determined, else raise an InvalidPositionSide
+        """
         if self.position_side is not None:
             return self.position_side
         if future_contract.is_one_way_position_mode():
diff --git a/octobot_trading/personal_data/portfolios/assets/future_asset.pxd b/octobot_trading/personal_data/portfolios/assets/future_asset.pxd
index 9938efd8e..4252ad3ad 100644
--- a/octobot_trading/personal_data/portfolios/assets/future_asset.pxd
+++ b/octobot_trading/personal_data/portfolios/assets/future_asset.pxd
@@ -24,11 +24,11 @@ cdef class FutureAsset(asset_class.Asset):
     cdef public object position_margin
     cdef public object order_margin
 
-    cpdef bint update(self, object total=*, object available=*, object position_margin=*,
+    cpdef object update(self, object total=*, object available=*, object position_margin=*,
                       object unrealized_pnl=*, object initial_margin=*)
-    cpdef bint set(self, object total=*, object available=*, object margin_balance=*, object initial_margin=*,
-                   object position_margin=*, object order_margin=*, object unrealized_pnl=*)
-    cpdef void set_unrealized_pnl(self, object unrealized_pnl)
+    cpdef object set(self, object total=*, object available=*, object initial_margin=*, object position_margin=*,
+                   object unrealized_pnl=*, object order_margin=*, object margin_balance=*)
+    cpdef object set_unrealized_pnl(self, object unrealized_pnl)  # needs object to forward exceptions
 
-    cdef void _update_total(self)
-    cdef void _update_available(self)
+    cdef object _update_total(self)
+    cdef object _update_available(self)
diff --git a/octobot_trading/personal_data/portfolios/assets/future_asset.py b/octobot_trading/personal_data/portfolios/assets/future_asset.py
index 35ac8bb26..ebebcc23c 100644
--- a/octobot_trading/personal_data/portfolios/assets/future_asset.py
+++ b/octobot_trading/personal_data/portfolios/assets/future_asset.py
@@ -89,18 +89,18 @@ def update(self, total=constants.ZERO, available=constants.ZERO, position_margin
         self._update_total()
         return True
 
-    def set(self, total=constants.ZERO, available=None, margin_balance=None, initial_margin=constants.ZERO,
-            position_margin=constants.ZERO, order_margin=constants.ZERO, unrealized_pnl=constants.ZERO):
+    def set(self, total=constants.ZERO, available=None, initial_margin=constants.ZERO, position_margin=constants.ZERO,
+            unrealized_pnl=constants.ZERO, order_margin=constants.ZERO, margin_balance=None):
         """
         Set available, total, initial_margin, wallet_balance, position_margin and maintenance_margin
         values for portfolio asset
         :param total: the wallet balance value
-        :param margin_balance: the margin balance value
         :param available: the available margin balance value
         :param initial_margin: the initial margin value
         :param position_margin: the position margin value
-        :param order_margin: the order margin value
         :param unrealized_pnl: the unrealized pnl value
+        :param order_margin: the order margin value
+        :param margin_balance: the margin balance value
         :return: True if updated
         """
         if position_margin == self.position_margin and order_margin == self.order_margin \
diff --git a/octobot_trading/personal_data/portfolios/assets/spot_asset.pxd b/octobot_trading/personal_data/portfolios/assets/spot_asset.pxd
index 22b76c46a..e6f59f89b 100644
--- a/octobot_trading/personal_data/portfolios/assets/spot_asset.pxd
+++ b/octobot_trading/personal_data/portfolios/assets/spot_asset.pxd
@@ -18,5 +18,5 @@ cimport octobot_trading.personal_data.portfolios.asset as asset_class
 
 
 cdef class SpotAsset(asset_class.Asset):
-    cpdef bint update(self, object available=*, object total=*)
-    cpdef bint set(self, object available, object total)
+    cpdef object update(self, object available=*, object total=*)
+    cpdef object set(self, object available, object total)
diff --git a/octobot_trading/personal_data/portfolios/portfolio.pxd b/octobot_trading/personal_data/portfolios/portfolio.pxd
index f396db205..d0dafb232 100644
--- a/octobot_trading/personal_data/portfolios/portfolio.pxd
+++ b/octobot_trading/personal_data/portfolios/portfolio.pxd
@@ -16,9 +16,8 @@
 #  License along with this library.
 
 cimport octobot_trading.personal_data.orders.order as order_class
-cimport octobot_trading.util as util
 
-cdef class Portfolio(util.Initializable):
+cdef class Portfolio:
     cdef object logger # Logger
     cdef public object lock # asyncio.Lock
 
diff --git a/octobot_trading/personal_data/portfolios/portfolio.py b/octobot_trading/personal_data/portfolios/portfolio.py
index 034705b2d..63d00c8e7 100644
--- a/octobot_trading/personal_data/portfolios/portfolio.py
+++ b/octobot_trading/personal_data/portfolios/portfolio.py
@@ -21,11 +21,10 @@
 import octobot_commons.asyncio_tools as asyncio_tools
 import octobot_trading.constants as constants
 import octobot_trading.enums as enums
-import octobot_trading.util as util
 import octobot_trading.personal_data as personal_data
 
 
-class Portfolio(util.Initializable):
+class Portfolio:
     """
     The Portfolio class manage an exchange portfolio
     This will begin by loading current exchange portfolio (by pulling user data)
@@ -44,28 +43,36 @@ def __init__(self, exchange_name, is_simulated=False):
             f"{self.__class__.__name__}{'Simulator' if is_simulated else ''}[{exchange_name}]")
         self.lock = asyncio_tools.RLock()
         self.portfolio = None
+        self.reset()
 
-    async def initialize_impl(self):
+    def __copy__(self):
         """
-        Initialize portfolio
+        Copy the portfolio instance
+        :return: the copied portfolio object
         """
-        self.reset()
+        new_portfolio = self.__class__(self._exchange_name, is_simulated=self._is_simulated)
+        new_portfolio.portfolio = copy.deepcopy(self.portfolio)
+        return new_portfolio
 
-    def reset(self):
+    def __eq__(self, other):
         """
-        Reset the portfolio dictionary
+        Compare two portfolio instances
+        :param other: the portfolio instance to compare
+        :return: True if the two portfolio instances are equals
         """
-        self.portfolio = {}
+        return self.portfolio == other.portfolio
 
-    async def copy(self):
+    def __str__(self):
         """
-        Copy the portfolio object
-        :return: the copied portfolio object
+        :return: the portfolio instance str representation
         """
-        new_portfolio: Portfolio = Portfolio(self._exchange_name, self._is_simulated)
-        await new_portfolio.initialize()
-        new_portfolio.portfolio = copy.deepcopy(self.portfolio)
-        return new_portfolio
+        return f"{personal_data.portfolio_to_float(self.portfolio)}"
+
+    def reset(self):
+        """
+        Reset the portfolio dictionary
+        """
+        self.portfolio = {}
 
     def update_portfolio_from_balance(self, balance, force_replace=True):
         """
@@ -163,9 +170,6 @@ def get_portfolio_from_amount_dict(self, amount_dict):
         return {currency: self.create_currency_asset(currency=currency, available=total, total=total).to_dict()
                 for currency, total in amount_dict.items()}
 
-    def __str__(self):
-        return f"{personal_data.portfolio_to_float(self.portfolio)}"
-
     def _update_portfolio_data(self, currency, total_value=constants.ZERO, available_value=constants.ZERO,
                                replace_value=False):
         """
diff --git a/octobot_trading/personal_data/portfolios/portfolio_manager.pxd b/octobot_trading/personal_data/portfolios/portfolio_manager.pxd
index 7a5ff4b30..301502626 100644
--- a/octobot_trading/personal_data/portfolios/portfolio_manager.pxd
+++ b/octobot_trading/personal_data/portfolios/portfolio_manager.pxd
@@ -35,10 +35,14 @@ cdef class PortfolioManager(util.Initializable):
     cdef public portfolio_value_holder.PortfolioValueHolder portfolio_value_holder
     cdef public portfolio_class.Portfolio portfolio
 
+    cpdef object handle_balance_updated(self)
     cpdef bint handle_balance_update(self, dict balance, bint is_diff_update=*)
+    cpdef object handle_profitability_recalculation(self, bint force_recompute_origin_portfolio)
+    cpdef object handle_mark_price_update(self, str symbol, object mark_price)
     cpdef void clear(self)
 
     cdef void _load_portfolio(self)
+    cdef void _reset_portfolio(self)
     cdef void _set_starting_simulated_portfolio(self)
     cdef bint _refresh_simulated_trader_portfolio_from_order(self, object order)
     cdef bint _refresh_simulated_trader_portfolio_from_position(self, object position)
diff --git a/octobot_trading/personal_data/portfolios/portfolio_manager.py b/octobot_trading/personal_data/portfolios/portfolio_manager.py
index 6edba04da..846feb168 100644
--- a/octobot_trading/personal_data/portfolios/portfolio_manager.py
+++ b/octobot_trading/personal_data/portfolios/portfolio_manager.py
@@ -13,8 +13,6 @@
 #
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
-import decimal
-
 import octobot_commons.logging as logging
 import octobot_commons.constants as commons_constants
 
@@ -44,7 +42,7 @@ async def initialize_impl(self):
         """
         Reset the portfolio instance
         """
-        await self._reset_portfolio()
+        self._reset_portfolio()
 
     def handle_balance_update(self, balance, is_diff_update=False):
         """
@@ -104,31 +102,31 @@ async def handle_balance_update_from_funding(self, position, funding_rate, requi
             return await self._refresh_real_trader_portfolio()
         return False
 
-    async def handle_balance_updated(self):
+    def handle_balance_updated(self):
         """
         Handle balance update notification
         :return: True if profitability changed
         """
-        return await self.portfolio_profitability.update_profitability()
+        return self.portfolio_profitability.update_profitability()
 
-    async def handle_profitability_recalculation(self, force_recompute_origin_portfolio):
+    def handle_profitability_recalculation(self, force_recompute_origin_portfolio):
         """
         Called before PortfolioProfitability's portfolio profitability recalculation
         to ensure portfolio values are available
         :param force_recompute_origin_portfolio: when True, force origin portfolio computation
         """
-        await self.portfolio_value_holder.handle_profitability_recalculation(force_recompute_origin_portfolio)
+        self.portfolio_value_holder.handle_profitability_recalculation(force_recompute_origin_portfolio)
 
-    async def handle_mark_price_update(self, symbol, mark_price):
+    def handle_mark_price_update(self, symbol, mark_price):
         """
         Handle a mark price update notification
         :param symbol: the update symbol
         :param mark_price: the updated mark price in float
         :return: True if profitability changed
         """
-        return await self.portfolio_profitability. \
+        return self.portfolio_profitability. \
             update_profitability(force_recompute_origin_portfolio=self.portfolio_value_holder.
-                                 update_origin_crypto_currencies_values(symbol, decimal.Decimal(str(mark_price))))
+                                 update_origin_crypto_currencies_values(symbol, mark_price))
 
     async def _refresh_real_trader_portfolio(self) -> bool:
         """
@@ -139,12 +137,11 @@ async def _refresh_real_trader_portfolio(self) -> bool:
                                                self.exchange_manager.id).get_internal_producer().\
             refresh_real_trader_portfolio()
 
-    async def _reset_portfolio(self):
+    def _reset_portfolio(self):
         """
         Reset the portfolio and portfolio profitability instances
         """
         self.portfolio = personal_data.create_portfolio_from_exchange_manager(self.exchange_manager)
-        await self.portfolio.initialize()
         self._load_portfolio()
 
         self.reference_market = util.get_reference_market(self.config)
diff --git a/octobot_trading/personal_data/portfolios/portfolio_profitability.pxd b/octobot_trading/personal_data/portfolios/portfolio_profitability.pxd
index c9fe0b652..e10e8dd11 100644
--- a/octobot_trading/personal_data/portfolios/portfolio_profitability.pxd
+++ b/octobot_trading/personal_data/portfolios/portfolio_profitability.pxd
@@ -37,6 +37,8 @@ cdef class PortfolioProfitability:
     cdef set traded_currencies_without_market_specific
     cdef public set valuated_currencies
 
+    cpdef object update_profitability(self, bint force_recompute_origin_portfolio=*)
+
     cdef object _calculate_average_market_profitability(self)
     cdef void _reset_before_profitability_calculation(self)
     cdef void _update_profitability_calculation(self)
diff --git a/octobot_trading/personal_data/portfolios/portfolio_profitability.py b/octobot_trading/personal_data/portfolios/portfolio_profitability.py
index f2ef077af..a2bc8cb9c 100644
--- a/octobot_trading/personal_data/portfolios/portfolio_profitability.py
+++ b/octobot_trading/personal_data/portfolios/portfolio_profitability.py
@@ -55,7 +55,7 @@ def get_average_market_profitability(self):
         self.portfolio_manager.portfolio_value_holder.get_current_crypto_currencies_values()
         return self._calculate_average_market_profitability()
 
-    async def update_profitability(self, force_recompute_origin_portfolio=False) -> bool:
+    def update_profitability(self, force_recompute_origin_portfolio=False):
         """
         Get profitability calls get_currencies_prices to update required data
         Then calls get_portfolio_current_value to set the current value of portfolio_current_value attribute
@@ -63,7 +63,7 @@ async def update_profitability(self, force_recompute_origin_portfolio=False) ->
         """
         self._reset_before_profitability_calculation()
         try:
-            await self.portfolio_manager.handle_profitability_recalculation(force_recompute_origin_portfolio)
+            self.portfolio_manager.handle_profitability_recalculation(force_recompute_origin_portfolio)
             self._update_profitability_calculation()
             return self.profitability_diff != constants.ZERO
         except KeyError as missing_data_exception:
diff --git a/octobot_trading/personal_data/portfolios/portfolio_value_holder.pxd b/octobot_trading/personal_data/portfolios/portfolio_value_holder.pxd
index 59f5ecc89..8749cccf8 100644
--- a/octobot_trading/personal_data/portfolios/portfolio_value_holder.pxd
+++ b/octobot_trading/personal_data/portfolios/portfolio_value_holder.pxd
@@ -44,8 +44,11 @@ cdef class PortfolioValueHolder:
     cpdef dict get_current_crypto_currencies_values(self)
     cpdef dict get_current_holdings_values(self)
     cpdef object get_origin_portfolio_current_value(self, bint refresh_values=*)
+    cpdef object handle_profitability_recalculation(self, bint force_recompute_origin_portfolio)
     # cpdef object get_currency_holding_ratio(self, str currency)
 
+    cdef object _init_portfolio_values_if_necessary(self, bint force_recompute_origin_portfolio)
+    cdef object _init_origin_portfolio_and_currencies_value(self)
     cdef object _update_portfolio_current_value(self, dict portfolio, dict currencies_values=*, bint fill_currencies_values=*)
     cdef void _fill_currencies_values(self, dict currencies_values)
     cdef dict _update_portfolio_and_currencies_current_value(self)
diff --git a/octobot_trading/personal_data/portfolios/portfolio_value_holder.py b/octobot_trading/personal_data/portfolios/portfolio_value_holder.py
index 2224f9438..7a1ad70a5 100644
--- a/octobot_trading/personal_data/portfolios/portfolio_value_holder.py
+++ b/octobot_trading/personal_data/portfolios/portfolio_value_holder.py
@@ -14,6 +14,7 @@
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
 import asyncio
+import copy
 
 import octobot_commons.logging as logging
 import octobot_commons.symbol_util as symbol_util
@@ -106,13 +107,13 @@ def get_currency_holding_ratio(self, currency):
                                         currency).total) / self.portfolio_current_value \
             if self.portfolio_current_value else constants.ZERO
 
-    async def handle_profitability_recalculation(self, force_recompute_origin_portfolio) -> None:
+    def handle_profitability_recalculation(self, force_recompute_origin_portfolio):
         """
         Initialize values required by portfolio profitability to perform its profitability calculation
         :param force_recompute_origin_portfolio: when True, force origin portfolio computation
         """
         self._update_portfolio_and_currencies_current_value()
-        await self._init_portfolio_values_if_necessary(force_recompute_origin_portfolio)
+        self._init_portfolio_values_if_necessary(force_recompute_origin_portfolio)
 
     def get_origin_portfolio_current_value(self, refresh_values=False):
         """
@@ -126,22 +127,22 @@ def get_origin_portfolio_current_value(self, refresh_values=False):
         return self._update_portfolio_current_value(self.origin_portfolio.portfolio,
                                                     currencies_values=self.current_crypto_currencies_values)
 
-    async def _init_portfolio_values_if_necessary(self, force_recompute_origin_portfolio) -> None:
+    def _init_portfolio_values_if_necessary(self, force_recompute_origin_portfolio):
         """
         Init origin portfolio values if necessary
         :param force_recompute_origin_portfolio: when True, force origin portfolio computation
         """
         if self.portfolio_origin_value == constants.ZERO:
             # try to update portfolio origin value if it's not known yet
-            await self._init_origin_portfolio_and_currencies_value()
+            self._init_origin_portfolio_and_currencies_value()
         if force_recompute_origin_portfolio:
             self._recompute_origin_portfolio_initial_value()
 
-    async def _init_origin_portfolio_and_currencies_value(self) -> None:
+    def _init_origin_portfolio_and_currencies_value(self):
         """
         Initialize origin portfolio and the origin portfolio currencies values
         """
-        self.origin_portfolio = self.origin_portfolio or await self.portfolio_manager.portfolio.copy()
+        self.origin_portfolio = self.origin_portfolio or copy.copy(self.portfolio_manager.portfolio)
         self.origin_crypto_currencies_values.update(
             self._evaluate_config_crypto_currencies_and_portfolio_values(self.origin_portfolio.portfolio,
                                                                          ignore_missing_currency_data=True))
diff --git a/octobot_trading/personal_data/portfolios/types/future_portfolio.pxd b/octobot_trading/personal_data/portfolios/types/future_portfolio.pxd
index 4b78411cc..32f796807 100644
--- a/octobot_trading/personal_data/portfolios/types/future_portfolio.pxd
+++ b/octobot_trading/personal_data/portfolios/types/future_portfolio.pxd
@@ -17,10 +17,11 @@
 cimport octobot_trading.personal_data.portfolios.portfolio as portfolio_class
 
 cdef class FuturePortfolio(portfolio_class.Portfolio):
-    cpdef void update_portfolio_from_liquidated_position(self, object position)
-    cpdef void update_portfolio_from_funding(self, object position, object funding_rate)
-    cpdef void update_portfolio_from_pnl(self, object position)
+    cpdef object update_portfolio_from_liquidated_position(self, object position)  # needs object to forward exceptions
+    cpdef object update_portfolio_from_funding(self, object position, object funding_rate)  # needs object to forward exceptions
+    cpdef object update_portfolio_from_pnl(self, object position)  # needs object to forward exceptions
 
+    # needs object to forward exceptions
     cdef object _update_future_portfolio_data(self, str currency,
                                               object wallet_value=*,
                                               object position_margin_value=*,
diff --git a/octobot_trading/personal_data/portfolios/types/future_portfolio.py b/octobot_trading/personal_data/portfolios/types/future_portfolio.py
index 1db11762e..2852c9595 100644
--- a/octobot_trading/personal_data/portfolios/types/future_portfolio.py
+++ b/octobot_trading/personal_data/portfolios/types/future_portfolio.py
@@ -38,7 +38,7 @@ def update_portfolio_data_from_order(self, order):
             get_order_position(order, contract=pair_future_contract)
 
         try:
-            update_size, have_increased_position_size = position_instance.update_from_order(order)
+            update_size, has_increased_position_size = position_instance.update_from_order(order)
             real_order_quantity = decimal.Decimal(update_size / pair_future_contract.current_leverage).copy_abs()
 
             # When inverse contract, decrease a currency market equivalent quantity from currency balance
@@ -48,8 +48,9 @@ def update_portfolio_data_from_order(self, order):
                 self._update_future_portfolio_data(
                     order.currency,
                     wallet_value=fees_update_quantity,
-                    order_margin_value=-total_update_quantity,
-                    position_margin_value=total_update_quantity if have_increased_position_size else constants.ZERO)
+                    order_margin_value=-total_update_quantity if has_increased_position_size else constants.ZERO,
+                    position_margin_value=total_update_quantity
+                    if has_increased_position_size else -total_update_quantity)
 
             # When non-inverse contract, decrease directly market quantity
             else:
@@ -59,8 +60,9 @@ def update_portfolio_data_from_order(self, order):
                 self._update_future_portfolio_data(
                     order.market,
                     wallet_value=fees_update_quantity,
-                    order_margin_value=-total_update_quantity,
-                    position_margin_value=total_update_quantity if have_increased_position_size else constants.ZERO)
+                    order_margin_value=-total_update_quantity if has_increased_position_size else constants.ZERO,
+                    position_margin_value=total_update_quantity
+                    if has_increased_position_size else -total_update_quantity)
         except (decimal.DivisionByZero, decimal.InvalidOperation) as e:
             self.logger.error(f"Failed to update from filled order : {order} ({e})")
 
@@ -71,6 +73,12 @@ def update_portfolio_available_from_order(self, order, is_new_order=True):
         :param is_new_order: True when the order is being created
         """
         pair_future_contract = order.exchange_manager.exchange.get_pair_future_contract(order.symbol)
+        position_instance = order.exchange_manager.exchange_personal_data.positions_manager. \
+            get_order_position(order, contract=pair_future_contract)
+
+        if not position_instance.is_order_increasing_size(order):
+            return  # decreasing position size order are not impacting availability
+
         try:
             real_order_quantity = (order.origin_quantity / pair_future_contract.current_leverage
                                    * (constants.ONE if is_new_order else -constants.ONE))
@@ -132,6 +140,7 @@ def update_portfolio_from_funding(self, position, funding_rate):
     def update_portfolio_from_pnl(self, position):
         """
         Updates the portfolio from a Position PNL update
+        TODO: manage portfolio PNL update when using cross margin
         :param position: position: the position instance with the new PNL
         """
         if position.symbol_contract.is_isolated():
@@ -160,14 +169,14 @@ def _update_future_portfolio_data(self, currency,
         try:
             if replace_value:
                 return self.portfolio[currency].set(total=wallet_value,
-                                                    position_margin=position_margin_value,
-                                                    initial_margin=initial_margin_value,
                                                     available=order_margin_value,
+                                                    initial_margin=initial_margin_value,
+                                                    position_margin=position_margin_value,
                                                     unrealized_pnl=unrealized_pnl_value)
             return self.portfolio[currency].update(total=wallet_value,
+                                                   available=order_margin_value,
                                                    position_margin=position_margin_value,
                                                    initial_margin=initial_margin_value,
-                                                   available=order_margin_value,
                                                    unrealized_pnl=unrealized_pnl_value)
         except KeyError:
             self.portfolio[currency] = self.create_currency_asset(currency=currency,
diff --git a/octobot_trading/personal_data/positions/channel/positions_updater.pxd b/octobot_trading/personal_data/positions/channel/positions_updater.pxd
index 75a3e5caa..5f4abf1cf 100644
--- a/octobot_trading/personal_data/positions/channel/positions_updater.pxd
+++ b/octobot_trading/personal_data/positions/channel/positions_updater.pxd
@@ -19,10 +19,10 @@ cimport octobot_trading.personal_data.positions.channel.positions as positions_c
 cimport octobot_commons.async_job as async_job
 
 cdef class PositionsUpdater(positions_channel.PositionsProducer):
-    cdef public bint should_use_open_position_per_symbol
+    cdef public bint should_use_position_per_symbol
 
-    cdef async_job.AsyncJob open_positions_job
     cdef async_job.AsyncJob position_update_job
 
     cdef bint _should_run(self)
     cdef bint _should_push_mark_price(self)
+    cdef bint _has_mark_price_in_position(self)
diff --git a/octobot_trading/personal_data/positions/channel/positions_updater.py b/octobot_trading/personal_data/positions/channel/positions_updater.py
index b26c55662..4f297b0c1 100644
--- a/octobot_trading/personal_data/positions/channel/positions_updater.py
+++ b/octobot_trading/personal_data/positions/channel/positions_updater.py
@@ -27,35 +27,39 @@
 
 class PositionsUpdater(positions_channel.PositionsProducer):
     """
-    Update open positions from exchange
+    Update positions from exchange
     Can also be used to update a specific positions from exchange
     """
 
     CHANNEL_NAME = constants.POSITIONS_CHANNEL
     POSITIONS_STARTING_REFRESH_TIME = 12
-    OPEN_POSITION_REFRESH_TIME = 9
+    POSITION_REFRESH_TIME = 9
     TIME_BETWEEN_POSITIONS_REFRESH = 3
 
     def __init__(self, channel):
         super().__init__(channel)
-        self.should_use_open_position_per_symbol = False
+        self.should_use_position_per_symbol = False
 
         # create async jobs
-        self.open_positions_job = async_job.AsyncJob(self._open_positions_fetch_and_push,
-                                                     execution_interval_delay=self.OPEN_POSITION_REFRESH_TIME,
-                                                     min_execution_delay=self.TIME_BETWEEN_POSITIONS_REFRESH)
-        self.position_update_job = async_job.AsyncJob(self._position_fetch_and_push,
-                                                      is_periodic=False,
-                                                      enable_multiple_runs=True)
-        self.position_update_job.add_job_dependency(self.open_positions_job)
-        self.open_positions_job.add_job_dependency(self.position_update_job)
+        self.position_update_job = async_job.AsyncJob(self._positions_fetch_and_push,
+                                                      execution_interval_delay=self.POSITION_REFRESH_TIME,
+                                                      min_execution_delay=self.TIME_BETWEEN_POSITIONS_REFRESH)
 
     async def initialize(self) -> None:
         """
         Initialize positions and future contracts
         """
+        # fetch future contracts from exchange
         await self.initialize_contracts()
+
+        # subscribe to mark_price channel if necessary
+        if not self._has_mark_price_in_position():
+            await exchanges_channel.get_chan(constants.MARK_PRICE_CHANNEL, self.channel.exchange_manager.id) \
+                .new_consumer(self.handle_mark_price)
+
+        # fetch current positions from exchange
         await self.initialize_positions()
+
         self.channel.exchange_manager.exchange_personal_data.positions_manager.positions_initialized = True
 
     async def initialize_contracts(self) -> None:
@@ -73,11 +77,11 @@ async def initialize_positions(self) -> None:
         Initialize data before starting jobs
         """
         try:
-            await self.fetch_and_push()
+            await self.fetch_and_push(should_initialize_contract=True)
         except NotImplementedError:
-            self.should_use_open_position_per_symbol = True
+            self.should_use_position_per_symbol = True
             try:
-                await self.fetch_and_push()
+                await self.fetch_and_push(should_initialize_contract=True)
             except NotImplementedError:
                 self.logger.warning("Position updater cannot fetch positions : required methods are not implemented")
                 await self.stop()
@@ -96,62 +100,82 @@ async def start(self):
 
         await self.initialize()
         await asyncio.sleep(self.POSITIONS_STARTING_REFRESH_TIME)
-        await self.open_positions_job.run()
+        await self.position_update_job.run()
 
-    async def fetch_and_push(self):
+    async def fetch_and_push(self, should_initialize_contract=False):
         """
-        Update open and closed positions from exchange
-        :param limit: the exchange request positions count limit
+        Update positions from exchange
+        :param should_initialize_contract: if fetch and push should create contract if necessary
         """
-        await self._open_positions_fetch_and_push()
+        await self._positions_fetch_and_push(should_initialize_contract=should_initialize_contract)
         await asyncio.sleep(self.TIME_BETWEEN_POSITIONS_REFRESH)
 
-    async def _open_positions_fetch_and_push(self):
+    async def _positions_fetch_and_push(self, should_initialize_contract=False):
         """
-        Update open positions from exchange
+        Update positions from exchange
+        :param should_initialize_contract: if the fetch method should create contract if necessary
         """
         try:
-            if self.should_use_open_position_per_symbol:
-                await self.fetch_open_position_per_symbol()
+            if self.should_use_position_per_symbol:
+                await self.fetch_position_per_symbol(should_initialize_contract=should_initialize_contract)
             else:
-                await self.fetch_open_positions()
+                await self.fetch_positions(should_initialize_contract=should_initialize_contract)
         except Exception as e:
-            self.logger.error(f"Fail to update open positions : {e}")
+            self.logger.error(f"Fail to update positions : {e}")
 
     def _should_run(self):
         return self.channel.exchange_manager.is_future
 
-    async def fetch_open_position_per_symbol(self):
-        open_positions = []
+    async def fetch_position_per_symbol(self, should_initialize_contract=False):
+        positions = []
         for symbol in self.channel.exchange_manager.exchange_config.traded_symbol_pairs:
-            positions: list = await self.channel.exchange_manager.exchange.get_symbol_open_positions(symbol=symbol)
+            fetched_positions = await self.channel.exchange_manager.exchange.get_symbol_positions(symbol=symbol)
             if positions:
-                open_positions += positions
+                positions += fetched_positions
 
-        if open_positions:
-            await self._push_open_positions(open_positions)
+        if positions:
+            if should_initialize_contract:
+                self._initialize_contract_if_necessary(positions)
+            await self._push_positions(positions)
 
     def _is_valid_position(self, position_dict):
         return position_dict and position_dict.get(enums.ExchangeConstantsPositionColumns.SYMBOL.value, None) \
                in self.channel.exchange_manager.exchange_config.traded_symbol_pairs
 
-    async def fetch_open_positions(self):
-        open_positions = [
+    async def fetch_positions(self, should_initialize_contract=False):
+        positions = [
             position
-            for position in await self.channel.exchange_manager.exchange.get_open_positions()
+            for position in await self.channel.exchange_manager.exchange.get_positions()
             if self._is_valid_position(position)
         ]
 
-        if open_positions:
-            await self._push_open_positions(open_positions)
+        if positions:
+            if should_initialize_contract:
+                self._initialize_contract_if_necessary(positions)
+            await self._push_positions(positions)
 
-    async def _push_open_positions(self, open_positions):
-        await self.push(positions=open_positions)
+    async def _push_positions(self, positions):
+        await self.push(positions=positions)
 
         if self._should_push_mark_price():
-            for position in open_positions:
+            for position in positions:
                 await self.extract_mark_price(position)
 
+    def _initialize_contract_if_necessary(self, positions):
+        for position in positions:
+            pair = position.get(enums.ExchangeConstantsPositionColumns.SYMBOL.value, None)
+            if pair and pair not in self.channel.exchange_manager.exchange.pair_contracts:
+                self.channel.exchange_manager.exchange.create_pair_contract(
+                    pair=pair,
+                    current_leverage=position.get(
+                        enums.ExchangeConstantsPositionColumns.LEVERAGE.value, constants.ZERO),
+                    margin_type=position.get(enums.ExchangeConstantsPositionColumns.MARGIN_TYPE.value, None),
+                    contract_type=position.get(enums.ExchangeConstantsPositionColumns.CONTRACT_TYPE.value, None),
+                    position_mode=position.get(enums.ExchangeConstantsPositionColumns.POSITION_MODE.value, None),
+                    maintenance_margin_rate=position.get(
+                        enums.ExchangeConstantsPositionColumns.MAINTENANCE_MARGIN_RATE.value,
+                        constants.DEFAULT_SYMBOL_MAINTENANCE_MARGIN_RATE))
+
     async def extract_mark_price(self, position_dict: dict):
         try:
             await exchanges_channel.get_chan(constants.MARK_PRICE_CHANNEL,
@@ -179,27 +203,23 @@ async def update_position_from_exchange(self, position,
                                            ignore_dependencies_check=force_job_execution,
                                            position=position, should_notify=should_notify)
 
-    async def _position_fetch_and_push(self, position, should_notify=False):
-        """
-        Update Position from exchange
-        :param position: the position to update
-        :param should_notify: if Positions channel consumers should be notified
-        :return: True if the position was updated
-        """
-        exchange_name = position.exchange_manager.exchange_name \
-            if position.exchange_manager else "cleared order's exchange"
-        self.logger.debug(f"Requested update for {position} on {exchange_name}")
-
-        raw_position = await self.channel.exchange_manager.exchange.get_position(position.position_id, position.symbol)
-        # TODO manage exchanges without get_position()
-        if raw_position is not None:
-            self.logger.debug(f"Received update for {position} on {exchange_name}: {raw_position}")
-            await self.channel.exchange_manager.exchange_personal_data.handle_position_update_from_raw(
-                position.position_id, raw_position, should_notify=should_notify)
-
     def _should_push_mark_price(self):
+        return self._has_mark_price_in_position()
+
+    def _has_mark_price_in_position(self):
         return self.channel.exchange_manager.exchange.MARK_PRICE_IN_POSITION
 
+    async def handle_mark_price(self, exchange: str, exchange_id: str, cryptocurrency: str, symbol: str, mark_price):
+        """
+        MarkPrice channel consumer callback
+        """
+        try:
+            for symbol_position in self.channel.exchange_manager.exchange_personal_data.positions_manager. \
+                    get_symbol_positions(symbol=symbol):
+                await symbol_position.update(mark_price=mark_price)
+        except Exception as e:
+            self.logger.exception(e, True, f"Fail to handle mark price : {e}")
+
     async def stop(self) -> None:
         """
         Stop producer by stopping its jobs
@@ -207,7 +227,6 @@ async def stop(self) -> None:
         await super().stop()
         if not self._should_run():
             return
-        self.open_positions_job.stop()
         self.position_update_job.stop()
 
     async def resume(self) -> None:
diff --git a/octobot_trading/personal_data/positions/channel/positions_updater_simulator.py b/octobot_trading/personal_data/positions/channel/positions_updater_simulator.py
index 357a8383b..fe4603570 100644
--- a/octobot_trading/personal_data/positions/channel/positions_updater_simulator.py
+++ b/octobot_trading/personal_data/positions/channel/positions_updater_simulator.py
@@ -13,8 +13,6 @@
 #
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
-import decimal
-
 import octobot_commons.logging as logging
 import octobot_trading.constants as constants
 import octobot_trading.exchange_channel as exchanges_channel
@@ -40,16 +38,19 @@ async def initialize(self) -> None:
             .new_consumer(self.handle_funding_rate)
         self.channel.exchange_manager.exchange_personal_data.positions_manager.positions_initialized = True
 
-    async def handle_mark_price(self, exchange: str, exchange_id: str, cryptocurrency: str, symbol: str, mark_price):
+    async def initialize_contracts(self) -> None:
         """
-        MarkPrice channel consumer callback
+        Initialize exchange FutureContracts required to manage positions
         """
-        try:
-            for symbol_position in self.channel.exchange_manager.exchange_personal_data.positions_manager. \
-                    get_symbol_positions(symbol=symbol):
-                await symbol_position.update(mark_price=decimal.Decimal(str(mark_price)))
-        except Exception as e:
-            self.logger.exception(e, True, f"Fail to handle mark price : {e}")
+        for pair in self.channel.exchange_manager.exchange_config.traded_symbol_pairs:
+            self.channel.exchange_manager.exchange.create_pair_contract(
+                pair=pair,
+                current_leverage=constants.DEFAULT_SYMBOL_LEVERAGE,
+                margin_type=constants.DEFAULT_SYMBOL_MARGIN_TYPE,
+                contract_type=constants.DEFAULT_SYMBOL_CONTRACT_TYPE,
+                position_mode=constants.DEFAULT_SYMBOL_POSITION_MODE,
+                maintenance_margin_rate=constants.DEFAULT_SYMBOL_MAINTENANCE_MARGIN_RATE,
+                maximum_leverage=constants.DEFAULT_SYMBOL_MAX_LEVERAGE)
 
     async def handle_funding_rate(self, exchange: str,
                                   exchange_id: str,
diff --git a/octobot_trading/personal_data/positions/position.pxd b/octobot_trading/personal_data/positions/position.pxd
index 7b579eec3..b58df7441 100644
--- a/octobot_trading/personal_data/positions/position.pxd
+++ b/octobot_trading/personal_data/positions/position.pxd
@@ -66,9 +66,10 @@ cdef class Position(util.Initializable):
                       object quantity,
                       object size,
                       object value,
-                      object margin,
+                      object initial_margin,
                       object unrealised_pnl,
                       object realised_pnl,
+                      object fee_to_close,
                       object status=*,
                       object side=*)
     cdef bint _should_change(self, object original_value, object new_value)
@@ -77,21 +78,22 @@ cdef class Position(util.Initializable):
     cdef void _update_quantity_or_size_if_necessary(self)
     cdef void _update_quantity(self)
     cdef void _update_realized_pnl_from_order(self, object order)
+    cdef void _update_realized_pnl_from_size_update(self, object size_update, bint is_closing=*)
     cdef object _calculates_size_update_from_filled_order(self, object order, object size_to_close)
     cdef bint _is_update_increasing_size(self, object size_update)
-    cdef void _update_size(self, object update_size)
+    cdef bint _is_update_closing(self, object size_update)
+    cdef object _update_size(self, object update_size)  # needs object to forward exceptions
     cdef void _check_and_update_size(self, object size_update)
     cdef void _update_margin(self)
+    cdef void _reset_entry_price(self)
     cdef void _update_side(self)
-    # cdef void _check_for_liquidation(self)
 
-    # cpdef void update(self, object update_size=*, object mark_price=*)
-    cpdef tuple update_from_order(self, object order)
+    cpdef bint is_order_increasing_size(self, object order)
+    cpdef object update_from_order(self, object order)  # returns tuple but needs object to forward exceptions
     cpdef void update_value(self)
-    cpdef void update_pnl(self)
+    cpdef object update_pnl(self)  # needs object to forward exceptions
     cpdef void update_initial_margin(self)
     cpdef void update_average_entry_price(self, object update_size, object update_price)
-    cpdef object get_maintenance_margin_rate(self)
     cpdef object get_initial_margin_rate(self)
     cpdef object calculate_maintenance_margin(self)
     cpdef bint update_from_raw(self, dict raw_position)
@@ -104,6 +106,7 @@ cdef class Position(util.Initializable):
     cpdef object get_two_way_taker_fee(self)
     cpdef object get_order_cost(self)
     cpdef object get_fee_to_open(self)
+    cpdef str get_currency(self)
     cpdef void update_fee_to_close(self)
     cpdef bint is_open(self)
     cpdef bint is_liquidated(self)
@@ -113,10 +116,8 @@ cdef class Position(util.Initializable):
     cpdef bint is_idle(self)
     cpdef object get_quantity_to_close(self)
     cpdef object get_unrealised_pnl_percent(self)
-    cpdef void on_pnl_update(self)
+    cpdef object on_pnl_update(self)  # needs object to forward exceptions
     cpdef str to_string(self)
     cpdef dict to_dict(self)
     cpdef void clear(self)
-    # cpdef void close(self)
-    # cpdef void reset(self)
     cpdef str get_logger_name(self)
diff --git a/octobot_trading/personal_data/positions/position.py b/octobot_trading/personal_data/positions/position.py
index fb85d322f..3ba201e0a 100644
--- a/octobot_trading/personal_data/positions/position.py
+++ b/octobot_trading/personal_data/positions/position.py
@@ -18,6 +18,7 @@
 import octobot_trading.enums as enums
 import octobot_trading.personal_data.positions.position_util as position_util
 import octobot_trading.personal_data.positions.states as positions_states
+import octobot_trading.personal_data.transactions.transaction_factory as transaction_factory
 import octobot_trading.util as util
 import octobot_trading.constants as constants
 
@@ -105,8 +106,8 @@ def _should_change(self, original_value, new_value):
 
     def _update(self, position_id, symbol, currency, market, timestamp,
                 entry_price, mark_price, liquidation_price,
-                quantity, size, value, margin,
-                unrealised_pnl, realised_pnl,
+                quantity, size, value, initial_margin,
+                unrealised_pnl, realised_pnl, fee_to_close,
                 status=None, side=None):
         changed: bool = False
 
@@ -138,8 +139,9 @@ def _update(self, position_id, symbol, currency, market, timestamp,
             self.value = value
             changed = True
 
-        if self._should_change(self.margin, margin):
-            self.margin = margin
+        if self._should_change(self.initial_margin, initial_margin):
+            self.initial_margin = initial_margin
+            self._update_margin()
             changed = True
 
         if self._should_change(self.unrealised_pnl, unrealised_pnl):
@@ -152,12 +154,20 @@ def _update(self, position_id, symbol, currency, market, timestamp,
 
         if self._should_change(self.entry_price, entry_price):
             self.entry_price = entry_price
+            changed = True
 
         if self._should_change(self.mark_price, mark_price):
             self.mark_price = mark_price
+            changed = True
+
+        if self._should_change(self.fee_to_close, fee_to_close):
+            self.fee_to_close = fee_to_close
+            self._update_margin()
+            changed = True
 
         if self._should_change(self.liquidation_price, liquidation_price):
             self.liquidation_price = liquidation_price
+            changed = True
 
         if self._should_change(self.status.value, status):
             self.status = enums.PositionStatus(status)
@@ -168,7 +178,6 @@ def _update(self, position_id, symbol, currency, market, timestamp,
         self._update_side()
         self._update_quantity_or_size_if_necessary()
         self._update_entry_price_if_necessary(mark_price)
-        self.update_pnl()
         return changed
 
     async def _ensure_position_initialized(self):
@@ -204,7 +213,7 @@ def _update_mark_price(self, mark_price):
         """
         self.mark_price = mark_price
         self._update_entry_price_if_necessary(mark_price)
-        if not self.is_idle():
+        if not self.is_idle() and self.exchange_manager.is_simulated:
             self.update_value()
             self.update_pnl()
 
@@ -216,6 +225,20 @@ def _update_entry_price_if_necessary(self, mark_price):
         if self.entry_price == constants.ZERO:
             self.entry_price = mark_price
 
+    def is_order_increasing_size(self, order):
+        """
+        Check if an order increase the position size
+        :param order: the order instance
+        :return: True if the order increase the position size when filled
+        """
+        if order.reduce_only or order.close_position:
+            return False  # Only reducing
+        return (
+                (self.is_idle() and self.symbol_contract.is_one_way_position_mode())
+                or (self.is_long() and order.is_long())
+                or (self.is_short() and order.is_short())
+        )
+
     def update_from_order(self, order):
         """
         Update position size and entry price from filled order portfolio
@@ -235,13 +258,13 @@ def update_from_order(self, order):
 
         # Calculates position quantity update from order
         size_update = self._calculates_size_update_from_filled_order(order, size_to_close)
-        is_increasing_position_size = self._is_update_increasing_size(size_update=size_update)
 
         # Updates position average entry price from order
         self.update_average_entry_price(size_update, order.filled_price)
 
-        self._update_size(size_update)
-        return size_update, is_increasing_position_size
+        # update size and realised pnl
+        has_increase_position_size = self._update_size(size_update)
+        return size_update, has_increase_position_size
 
     def _update_realized_pnl_from_order(self, order):
         """
@@ -281,22 +304,56 @@ def _is_update_increasing_size(self, size_update):
         if self.is_idle():
             return True
         if self.is_long():
-            return size_update > 0
-        return size_update < 0
+            return size_update > constants.ZERO
+        return size_update < constants.ZERO
+
+    def _is_update_closing(self, size_update):
+        """
+        :param size_update: the size update
+        :return: True if this update will close the position
+        """
+        if self.is_idle():
+            return True
+        if self.is_long():
+            return self.size + size_update <= constants.ZERO
+        return self.size + size_update >= constants.ZERO
 
     def _update_size(self, update_size):
         """
         Updates position size and triggers size related attributes update
         :param update_size: the size quantity
+        :return: True if the update increased position size
         """
+        is_update_increasing_position_size = self._is_update_increasing_size(update_size)
+        if not is_update_increasing_position_size:
+            self._update_realized_pnl_from_size_update(update_size, is_closing=self._is_update_closing(update_size))
         self._check_and_update_size(update_size)
         self._update_quantity()
         self._update_side()
-        self.update_initial_margin()
-        self.update_fee_to_close()
-        self.update_liquidation_price()
-        self.update_value()
-        self.update_pnl()
+        if self.exchange_manager.is_simulated:
+            self.update_initial_margin()
+            self.update_fee_to_close()
+            self.update_liquidation_price()
+            self.update_value()
+            self.update_pnl()
+        return is_update_increasing_position_size
+
+    def _update_realized_pnl_from_size_update(self, size_update, is_closing=False):
+        """
+        Updates the position realized pnl from update size
+        :param size_update: the position update size
+        :param is_closing: True when the position will be closed after size update
+        """
+        try:
+            realised_pnl_update = -size_update / self.size * self.unrealised_pnl
+            transaction_factory.create_realised_pnl_transaction(self.exchange_manager,
+                                                                self.get_currency(),
+                                                                self.symbol,
+                                                                realised_pnl=realised_pnl_update,
+                                                                is_closed_pnl=is_closing)
+        except (decimal.DivisionByZero, decimal.InvalidOperation):
+            realised_pnl_update = constants.ZERO
+        self.realised_pnl += realised_pnl_update
 
     def _check_and_update_size(self, size_update):
         """
@@ -341,13 +398,6 @@ def update_initial_margin(self):
     def update_average_entry_price(self, update_size, update_price):
         raise NotImplementedError("get_average_entry_price not implemented")
 
-    def get_maintenance_margin_rate(self):
-        """
-        :return: Position symbol funding rate
-        """
-        return self.exchange_manager.exchange_symbols_data. \
-            get_exchange_symbol_data(self.symbol).funding_manager.funding_rate
-
     def get_initial_margin_rate(self):
         """
         :return: Initial Margin Rate = 1 / Leverage
@@ -357,6 +407,12 @@ def get_initial_margin_rate(self):
         except (decimal.DivisionByZero, decimal.InvalidOperation):
             return constants.ZERO
 
+    def get_currency(self):
+        """
+        :return: position currency
+        """
+        return self.currency if self.symbol_contract.is_inverse_contract() else self.market
+
     def calculate_maintenance_margin(self):
         raise NotImplementedError("calculate_maintenance_margin not implemented")
 
@@ -486,12 +542,14 @@ def update_from_raw(self, raw_position):
             quantity=raw_position.get(enums.ExchangeConstantsPositionColumns.QUANTITY.value, constants.ZERO),
             size=raw_position.get(enums.ExchangeConstantsPositionColumns.SIZE.value, constants.ZERO),
             value=raw_position.get(enums.ExchangeConstantsPositionColumns.NOTIONAL.value, constants.ZERO),
-            margin=raw_position.get(enums.ExchangeConstantsPositionColumns.COLLATERAL.value, constants.ZERO),
+            initial_margin=raw_position.get(enums.ExchangeConstantsPositionColumns.INITIAL_MARGIN.value,
+                                            constants.ZERO),
             position_id=str(raw_position.get(enums.ExchangeConstantsPositionColumns.ID.value, symbol)),
             timestamp=raw_position.get(enums.ExchangeConstantsPositionColumns.TIMESTAMP.value, 0),
             unrealised_pnl=raw_position.get(enums.ExchangeConstantsPositionColumns.UNREALISED_PNL.value,
                                             constants.ZERO),
             realised_pnl=raw_position.get(enums.ExchangeConstantsPositionColumns.REALISED_PNL.value, constants.ZERO),
+            fee_to_close=raw_position.get(enums.ExchangeConstantsPositionColumns.CLOSING_FEE.value, constants.ZERO),
             status=position_util.parse_position_status(raw_position),
             side=raw_position.get(enums.ExchangeConstantsPositionColumns.SIDE.value, None)
         )
@@ -528,6 +586,13 @@ async def _check_for_liquidation(self):
                 self.status = enums.PositionStatus.LIQUIDATING
                 await positions_states.create_position_state(self)
 
+    def _reset_entry_price(self):
+        """
+        Reset the entry price to ZERO and force entry price update
+        """
+        self.entry_price = constants.ZERO
+        self._update_entry_price_if_necessary(self.mark_price)
+
     def _update_side(self):
         """
         Checks if self.side still represents the position side
@@ -535,9 +600,13 @@ def _update_side(self):
         """
         if self.symbol_contract.is_one_way_position_mode() or self.side is enums.PositionSide.UNKNOWN:
             if self.quantity >= constants.ZERO:
-                self.side = enums.PositionSide.LONG
+                if self.side is not enums.PositionSide.LONG:
+                    self.side = enums.PositionSide.LONG
+                    self._reset_entry_price()
             elif self.quantity < constants.ZERO:
-                self.side = enums.PositionSide.SHORT
+                if self.side is not enums.PositionSide.SHORT:
+                    self.side = enums.PositionSide.SHORT
+                    self._reset_entry_price()
             else:
                 self.side = enums.PositionSide.UNKNOWN
 
@@ -545,15 +614,18 @@ def __str__(self):
         return self.to_string()
 
     def to_string(self):
+        currency = self.get_currency()
         return (f"{self.symbol} | "
                 f"Size : {round(self.size, 10).normalize()} "
-                f"({self.side.value} x{self.symbol_contract.current_leverage}) | "
+                f"({round(self.value, 10).normalize()} {currency}) "
+                f"--> {self.side.value} {self.symbol_contract} | "
                 f"Mark price : {round(self.mark_price, 10).normalize()} | "
                 f"Entry price : {round(self.entry_price, 10).normalize()} | "
-                f"Unrealized PNL : {round(self.unrealised_pnl, 14).normalize()} "
-                f"({round(self.get_unrealised_pnl_percent(), 3)} %) | "
+                f"Margin : {round(self.margin, 10).normalize()} {currency} | "
+                f"Unrealised PNL : {round(self.unrealised_pnl, 14).normalize()} {currency} "
+                f"({round(self.get_unrealised_pnl_percent(), 3)}%) | "
                 f"Liquidation price : {round(self.liquidation_price, 10).normalize()} | "
-                f"Realized PNL : {round(self.realised_pnl, 14).normalize()} | "
+                f"Realised PNL : {round(self.realised_pnl, 14).normalize()} {currency} "
                 f"State : {self.state.state.value if self.state is not None else 'Unknown'} "
                 f"({self.symbol_contract.position_mode.value})")
 
@@ -578,7 +650,9 @@ async def reset(self):
         self.unrealised_pnl = constants.ZERO
         self.realised_pnl = constants.ZERO
         self.creation_time = constants.ZERO
-        await self.on_open()
+        self.on_pnl_update()  # notify portfolio to reset unrealized PNL
+        if not self.is_open():
+            await self.on_open()
 
     def clear(self):
         """
diff --git a/octobot_trading/personal_data/positions/positions_manager.pxd b/octobot_trading/personal_data/positions/positions_manager.pxd
index ced752f29..a55d4ba3f 100644
--- a/octobot_trading/personal_data/positions/positions_manager.pxd
+++ b/octobot_trading/personal_data/positions/positions_manager.pxd
@@ -28,12 +28,12 @@ cdef class PositionsManager(util.Initializable):
     cdef public object positions
 
     cdef void _reset_positions(self)
-    cdef positions_personal_data.Position _get_or_create_position(self, str symbol, object side, object contract=*)
-    cdef object _create_symbol_position(self, str symbol)
-    cdef str _generate_position_id(self, str symbol, object side, object contract=*)
+    cdef positions_personal_data.Position _get_or_create_position(self, str symbol, object side)
+    cdef object _create_symbol_position(self, str symbol, str position_id)
+    cdef str _generate_position_id(self, str symbol, object side, object expiration_time=*)
     cdef list _get_symbol_positions(self, str symbol)
 
-    cpdef positions_personal_data.Position get_symbol_position(self, str symbol, object side, object contract=*)
+    cpdef positions_personal_data.Position get_symbol_position(self, str symbol, object side)
     cpdef positions_personal_data.Position get_order_position(self, object order, object contract=*)
     cpdef list get_symbol_positions(self, str symbol=*)
     cpdef bint upsert_position_instance(self, positions_personal_data.Position position)
diff --git a/octobot_trading/personal_data/positions/positions_manager.py b/octobot_trading/personal_data/positions/positions_manager.py
index a346d00f1..fe14210e6 100644
--- a/octobot_trading/personal_data/positions/positions_manager.py
+++ b/octobot_trading/personal_data/positions/positions_manager.py
@@ -35,15 +35,14 @@ def __init__(self, trader):
     async def initialize_impl(self):
         self._reset_positions()
 
-    def get_symbol_position(self, symbol, side, contract=None):
+    def get_symbol_position(self, symbol, side):
         """
         Returns or create the symbol position instance
         :param symbol: the position symbol
         :param side: the position side
-        :param contract: the symbol contract (optional)
         :return: the existing position or the newly created position
         """
-        return self._get_or_create_position(symbol=symbol, side=side, contract=contract)
+        return self._get_or_create_position(symbol=symbol, side=side)
 
     def get_order_position(self, order, contract=None):
         """
@@ -55,8 +54,8 @@ def get_order_position(self, order, contract=None):
         future_contract = contract if contract is not None \
             else self.trader.exchange_manager.exchange.get_pair_future_contract(order.symbol)
         return self.get_symbol_position(symbol=order.symbol,
-                                        side=order.get_position_side(future_contract),
-                                        contract=future_contract)
+                                        side=None if future_contract.is_one_way_position_mode()
+                                        else order.get_position_side(future_contract))
 
     def get_symbol_positions(self, symbol=None):
         """
@@ -64,6 +63,8 @@ def get_symbol_positions(self, symbol=None):
         :param symbol: the position symbol
         :return: the symbol positions
         """
+        if symbol is None:
+            return list(self.positions.values())
         return self._get_symbol_positions(symbol)
 
     async def upsert_position(self, symbol: str, side, raw_position: dict) -> bool:
@@ -77,6 +78,7 @@ async def upsert_position(self, symbol: str, side, raw_position: dict) -> bool:
         position_id = self._generate_position_id(symbol=symbol, side=side)
         if position_id not in self.positions:
             new_position = position_factory.create_position_instance_from_raw(self.trader, raw_position=raw_position)
+            new_position.position_id = position_id
             return await self._finalize_position_creation(new_position, is_from_exchange_data=True)
 
         return self.positions[position_id].update_from_raw(raw_position)
@@ -112,19 +114,17 @@ def clear(self):
         self._reset_positions()
 
     # private
-    def _generate_position_id(self, symbol, side, contract=None):
+    def _generate_position_id(self, symbol, side, expiration_time=None):
         """
         Generate a position ID for one way and hedge position modes
         :param symbol: the position symbol
         :param side: the position side
-        :param contract: the symbol contract (optional)
+        :param expiration_time: the symbol expiration timestamp
         :return: the computed position id
         """
-        symbol_contract = self.trader.exchange_manager.exchange.get_pair_future_contract(symbol) \
-            if contract is None else contract
-        if symbol_contract.is_one_way_position_mode():
-            return symbol
-        return f"{symbol}{self.POSITION_ID_SEPARATOR}{side.value}"
+        return f"{symbol}" \
+               f"{'' if expiration_time is None else self.POSITION_ID_SEPARATOR + str(expiration_time)}" \
+               f"{'' if side is enums.PositionSide.BOTH or side is None else self.POSITION_ID_SEPARATOR + side.value}"
 
     async def _finalize_position_creation(self, new_position, is_from_exchange_data=False) -> bool:
         """
@@ -137,28 +137,30 @@ async def _finalize_position_creation(self, new_position, is_from_exchange_data=
         await new_position.initialize(is_from_exchange_data=is_from_exchange_data)
         return True
 
-    def _create_symbol_position(self, symbol):
+    def _create_symbol_position(self, symbol, position_id):
         """
         Creates a position when it doesn't exist for the specified symbol
+        :param symbol: the new position symbol
+        :param side: the new position id
         :return: the new symbol position instance
         """
         new_position = position_factory.create_symbol_position(self.trader, symbol)
-        self.positions[new_position.position_id] = new_position
+        new_position.position_id = position_id
+        self.positions[position_id] = new_position
         return new_position
 
-    def _get_or_create_position(self, symbol, side, contract=None):
+    def _get_or_create_position(self, symbol, side):
         """
         Get or create position by symbol and side
         :param symbol: the expected position symbol
         :param side: the expected position side
-        :param contract: the symbol contract (optional)
         :return: the matching position
         """
-        expected_position_id = self._generate_position_id(symbol=symbol, side=side, contract=contract)
+        expected_position_id = self._generate_position_id(symbol=symbol, side=side)
         try:
             return self.positions[expected_position_id]
         except KeyError:
-            self.positions[expected_position_id] = self._create_symbol_position(symbol)
+            self.positions[expected_position_id] = self._create_symbol_position(symbol, expected_position_id)
         return self.positions[expected_position_id]
 
     def _get_symbol_positions(self, symbol):
@@ -168,11 +170,8 @@ def _get_symbol_positions(self, symbol):
         :return: existing symbol positions list
         """
         positions = []
-        symbol_contract = self.trader.exchange_manager.exchange.get_pair_future_contract(symbol)
-        sides = [enums.PositionSide.BOTH] if symbol_contract.is_one_way_position_mode() \
-            else [enums.PositionSide.SHORT, enums.PositionSide.LONG]
-        for side in sides:
-            position_id = self._generate_position_id(symbol=symbol, side=side, contract=symbol_contract)
+        for side in [enums.PositionSide.BOTH, enums.PositionSide.SHORT, enums.PositionSide.LONG]:
+            position_id = self._generate_position_id(symbol=symbol, side=side)
             try:
                 positions.append(self.positions[position_id])
             except KeyError:
diff --git a/octobot_trading/personal_data/positions/states/open_position_state.py b/octobot_trading/personal_data/positions/states/open_position_state.py
index 9adca647c..30c89fb72 100644
--- a/octobot_trading/personal_data/positions/states/open_position_state.py
+++ b/octobot_trading/personal_data/positions/states/open_position_state.py
@@ -13,6 +13,8 @@
 #
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
+import decimal
+
 import octobot_trading.enums as enums
 import octobot_trading.personal_data.positions.position_state as position_state
 import octobot_trading.personal_data.positions.states.position_state_factory as position_state_factory
@@ -68,6 +70,11 @@ async def terminate(self):
         if not self.has_terminated:
             self.log_event_message(enums.StatesMessages.OPEN)
 
+            # set position mark price
+            self.position.mark_price = decimal.Decimal(
+                self.position.exchange_manager.exchange_symbols_data.
+                    get_exchange_symbol_data(self.position.symbol).prices_manager.mark_price)
+
             self.has_terminated = True
 
     def is_pending(self) -> bool:
diff --git a/octobot_trading/personal_data/positions/types/inverse_position.py b/octobot_trading/personal_data/positions/types/inverse_position.py
index f9f4750e6..72366fa3a 100644
--- a/octobot_trading/personal_data/positions/types/inverse_position.py
+++ b/octobot_trading/personal_data/positions/types/inverse_position.py
@@ -25,7 +25,7 @@ def update_value(self):
         Notional value = CONTRACT_QUANTITY / MARK_PRICE
         """
         try:
-            self.value = self.quantity / self.mark_price
+            self.value = self.size / self.mark_price
         except (decimal.DivisionByZero, decimal.InvalidOperation):
             self.value = constants.ZERO
 
@@ -35,11 +35,14 @@ def update_pnl(self):
         SHORT_PNL = CONTRACT_QUANTITY x [(1 / MARK_PRICE) - (1 / ENTRY_PRICE)]
         """
         try:
+            # ensure update validity
+            if self.mark_price <= constants.ZERO or self.entry_price <= constants.ZERO:
+                return
             if self.is_long():
-                self.unrealised_pnl = self.quantity * ((constants.ONE / self.entry_price) -
+                self.unrealised_pnl = self.size * ((constants.ONE / self.entry_price) -
                                                        (constants.ONE / self.mark_price))
             elif self.is_short():
-                self.unrealised_pnl = -self.quantity * ((constants.ONE / self.mark_price) -
+                self.unrealised_pnl = -self.size * ((constants.ONE / self.mark_price) -
                                                         (constants.ONE / self.entry_price))
             else:
                 self.unrealised_pnl = constants.ZERO
@@ -52,7 +55,7 @@ def update_initial_margin(self):
         Updates position initial margin = Position quantity / (entry price x leverage)
         """
         try:
-            self.initial_margin = self.quantity / (self.entry_price * self.symbol_contract.current_leverage)
+            self.initial_margin = (self.size / (self.entry_price * self.symbol_contract.current_leverage)).copy_abs()
             self._update_margin()
         except (decimal.DivisionByZero, decimal.InvalidOperation):
             self.initial_margin = constants.ZERO
@@ -62,7 +65,7 @@ def calculate_maintenance_margin(self):
         :return: Maintenance margin = (Position quantity / entry price) x Maintenance margin rate
         """
         try:
-            return (self.quantity / self.entry_price) * self.get_maintenance_margin_rate()
+            return (self.size / self.entry_price) * self.symbol_contract.maintenance_margin_rate
         except (decimal.DivisionByZero, decimal.InvalidOperation):
             return constants.ZERO
 
@@ -76,11 +79,13 @@ def update_isolated_liquidation_price(self):
             if self.is_long():
                 self.liquidation_price = (self.entry_price * self.symbol_contract.current_leverage) / \
                                          (self.symbol_contract.current_leverage + constants.ONE -
-                                          (self.get_maintenance_margin_rate() * self.symbol_contract.current_leverage))
+                                          (self.symbol_contract.maintenance_margin_rate *
+                                           self.symbol_contract.current_leverage))
             elif self.is_short():
                 self.liquidation_price = (self.entry_price * self.symbol_contract.current_leverage) / \
                                          (self.symbol_contract.current_leverage - constants.ONE +
-                                          (self.get_maintenance_margin_rate() * self.symbol_contract.current_leverage))
+                                          (self.symbol_contract.maintenance_margin_rate *
+                                           self.symbol_contract.current_leverage))
             else:
                 self.liquidation_price = constants.ZERO
             self.update_fee_to_close()
@@ -118,7 +123,7 @@ def get_fee_to_open(self):
         :return: Fee to open = (Quantity / Mark price ) x taker fee
         """
         try:
-            return (self.quantity / self.mark_price) * self.get_taker_fee()
+            return (self.size / self.mark_price) * self.get_taker_fee()
         except (decimal.DivisionByZero, decimal.InvalidOperation):
             return constants.ZERO
 
@@ -127,7 +132,7 @@ def update_fee_to_close(self):
         :return: Fee to close = (Quantity / Bankruptcy Price derived from mark price) x taker fee
         """
         try:
-            self.fee_to_close = (self.quantity / self.get_bankruptcy_price(with_mark_price=True)) * self.get_taker_fee()
+            self.fee_to_close = (self.size / self.get_bankruptcy_price(with_mark_price=True)) * self.get_taker_fee()
             self._update_margin()
         except (decimal.DivisionByZero, decimal.InvalidOperation):
             self.fee_to_close = constants.ZERO
diff --git a/octobot_trading/personal_data/positions/types/linear_position.py b/octobot_trading/personal_data/positions/types/linear_position.py
index 973fd3f20..417822e6d 100644
--- a/octobot_trading/personal_data/positions/types/linear_position.py
+++ b/octobot_trading/personal_data/positions/types/linear_position.py
@@ -24,7 +24,7 @@ def update_value(self):
         """
         Notional value = CONTRACT_QUANTITY * MARK_PRICE
         """
-        self.value = self.quantity * self.mark_price
+        self.value = self.size * self.mark_price
 
     def update_pnl(self):
         """
@@ -32,10 +32,13 @@ def update_pnl(self):
         SHORT_PNL = CONTRACT_QUANTITY x [ENTRY_PRICE - MARK_PRICE]
         """
         try:
+            # ensure update validity
+            if self.mark_price <= constants.ZERO or self.entry_price <= constants.ZERO:
+                return
             if self.is_long():
-                self.unrealised_pnl = self.quantity * (self.mark_price - self.entry_price)
+                self.unrealised_pnl = self.size * (self.mark_price - self.entry_price)
             elif self.is_short():
-                self.unrealised_pnl = -self.quantity * (self.entry_price - self.mark_price)
+                self.unrealised_pnl = -self.size * (self.entry_price - self.mark_price)
             else:
                 self.unrealised_pnl = constants.ZERO
             self.on_pnl_update()
@@ -47,7 +50,7 @@ def update_initial_margin(self):
         Updates position initial margin = (Position quantity x entry price) / leverage
         """
         try:
-            self.initial_margin = (self.quantity * self.entry_price) / self.symbol_contract.current_leverage
+            self.initial_margin = ((self.size * self.entry_price) / self.symbol_contract.current_leverage).copy_abs()
             self._update_margin()
         except (decimal.DivisionByZero, decimal.InvalidOperation):
             self.initial_margin = constants.ZERO
@@ -56,7 +59,7 @@ def calculate_maintenance_margin(self):
         """
         :return: Maintenance margin = Position quantity x entry price x Maintenance margin rate
         """
-        return self.quantity * self.entry_price * self.get_maintenance_margin_rate()
+        return self.size * self.entry_price * self.symbol_contract.maintenance_margin_rate
 
     def update_isolated_liquidation_price(self):
         """
@@ -69,10 +72,10 @@ def update_isolated_liquidation_price(self):
         try:
             if self.is_long():
                 self.liquidation_price = self.entry_price * (
-                        constants.ONE - self.get_initial_margin_rate() + self.calculate_maintenance_margin())
+                        constants.ONE - self.get_initial_margin_rate() + self.symbol_contract.maintenance_margin_rate)
             elif self.is_short():
                 self.liquidation_price = self.entry_price * (
-                        constants.ONE + self.get_initial_margin_rate() - self.calculate_maintenance_margin())
+                        constants.ONE + self.get_initial_margin_rate() - self.symbol_contract.maintenance_margin_rate)
             else:
                 self.liquidation_price = constants.ZERO
             self.update_fee_to_close()
@@ -98,7 +101,7 @@ def get_fee_to_open(self):
         """
         :return: Fee to open = (Quantity * Mark Price) x Taker fee
         """
-        return self.quantity * self.mark_price * self.get_taker_fee()
+        return self.size * self.mark_price * self.get_taker_fee()
 
     def get_order_cost(self):
         """
@@ -110,7 +113,7 @@ def update_fee_to_close(self):
         """
         :return: Fee to close = (Quantity * Bankruptcy Price derived from mark price) x Taker fee
         """
-        self.fee_to_close = self.quantity * self.get_bankruptcy_price(with_mark_price=True) * self.get_taker_fee()
+        self.fee_to_close = self.size * self.get_bankruptcy_price(with_mark_price=True) * self.get_taker_fee()
 
     def update_average_entry_price(self, update_size, update_price):
         """
diff --git a/octobot_trading/personal_data/transactions/__init__.pxd b/octobot_trading/personal_data/transactions/__init__.pxd
new file mode 100644
index 000000000..0a3ade4eb
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/__init__.pxd
@@ -0,0 +1,51 @@
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+
+from octobot_trading.personal_data.transactions cimport transaction
+from octobot_trading.personal_data.transactions.transaction cimport (
+    Transaction,
+)
+from octobot_trading.personal_data.transactions cimport transactions_manager
+from octobot_trading.personal_data.transactions.transactions_manager cimport (
+    TransactionsManager,
+)
+from octobot_trading.personal_data.transactions cimport transaction_factory
+from octobot_trading.personal_data.transactions.transaction_factory cimport (
+    create_blockchain_transaction,
+    create_realised_pnl_transaction,
+    create_fee_transaction,
+    create_transfer_transaction,
+)
+from octobot_trading.personal_data.transactions cimport types
+from octobot_trading.personal_data.transactions.types cimport (
+    BlockchainTransaction,
+    FeeTransaction,
+    RealisedPnlTransaction,
+    TransferTransaction,
+)
+
+__all__ = [
+    "TransactionsManager",
+    "Transaction",
+    "BlockchainTransaction",
+    "FeeTransaction",
+    "RealisedPnlTransaction",
+    "TransferTransaction",
+    "create_blockchain_transaction",
+    "create_realised_pnl_transaction",
+    "create_fee_transaction",
+    "create_transfer_transaction",
+]
diff --git a/octobot_trading/personal_data/transactions/__init__.py b/octobot_trading/personal_data/transactions/__init__.py
new file mode 100644
index 000000000..f28aaf592
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/__init__.py
@@ -0,0 +1,52 @@
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+
+from octobot_trading.personal_data.transactions import transactions_manager
+from octobot_trading.personal_data.transactions import transaction_factory
+from octobot_trading.personal_data.transactions import transaction
+from octobot_trading.personal_data.transactions import types
+
+from octobot_trading.personal_data.transactions.transactions_manager import (
+    TransactionsManager,
+)
+from octobot_trading.personal_data.transactions.transaction_factory import (
+    create_blockchain_transaction,
+    create_realised_pnl_transaction,
+    create_fee_transaction,
+    create_transfer_transaction,
+)
+from octobot_trading.personal_data.transactions.transaction import (
+    Transaction,
+)
+from octobot_trading.personal_data.transactions.types import (
+    BlockchainTransaction,
+    FeeTransaction,
+    RealisedPnlTransaction,
+    TransferTransaction,
+)
+
+__all__ = [
+    "TransactionsManager",
+    "Transaction",
+    "BlockchainTransaction",
+    "FeeTransaction",
+    "RealisedPnlTransaction",
+    "TransferTransaction",
+    "create_blockchain_transaction",
+    "create_realised_pnl_transaction",
+    "create_fee_transaction",
+    "create_transfer_transaction",
+]
diff --git a/octobot_trading/personal_data/transactions/transaction.pxd b/octobot_trading/personal_data/transactions/transaction.pxd
new file mode 100644
index 000000000..9fa4927c1
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/transaction.pxd
@@ -0,0 +1,29 @@
+# cython: language_level=3
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+
+cdef class Transaction:
+    cdef readonly str transaction_id
+    cdef readonly str exchange_name
+    cdef readonly str symbol
+    cdef readonly str currency
+
+    cdef readonly double creation_time
+
+    cdef readonly object logger
+    cdef readonly object transaction_type  # enums.TransactionType
+
+    cpdef void set_transaction_id(self, str new_id)
diff --git a/octobot_trading/personal_data/transactions/transaction.py b/octobot_trading/personal_data/transactions/transaction.py
new file mode 100644
index 000000000..6b09205b3
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/transaction.py
@@ -0,0 +1,43 @@
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+import uuid
+
+import octobot_commons.logging as logging
+
+
+class Transaction:
+
+    def __init__(self, exchange_name, creation_time, transaction_type, currency, symbol=None, transaction_id=None):
+        self.logger = logging.get_logger(self.__class__.__name__)
+        self.transaction_id = transaction_id
+        self.transaction_type = transaction_type
+        self.creation_time = creation_time
+
+        self.exchange_name = exchange_name
+        self.symbol = symbol
+        self.currency = currency
+
+        # generate default transaction id
+        if self.transaction_id is None:
+            self.transaction_id = str(uuid.uuid4())
+
+    def set_transaction_id(self, new_id):
+        """
+        Shouldn't be called outside of TransactionsManager.update_transaction_id
+        to maintain TransactionsManager.transactions integrity
+        :param new_id: the new transaction id
+        """
+        self.transaction_id = new_id
diff --git a/octobot_trading/personal_data/transactions/transaction_factory.pxd b/octobot_trading/personal_data/transactions/transaction_factory.pxd
new file mode 100644
index 000000000..369b47813
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/transaction_factory.pxd
@@ -0,0 +1,45 @@
+# cython: language_level=3
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+
+cimport octobot_trading.personal_data.transactions.types as transaction_types
+
+cpdef transaction_types.BlockchainTransaction create_blockchain_transaction(object exchange_manager,
+                                                                            str currency,
+                                                                            object blockchain_type,
+                                                                            str blockchain_transaction_id,
+                                                                            object blockchain_transaction_status=*,
+                                                                            str source_address=*,
+                                                                            str destination_address=*,
+                                                                            object quantity=*,
+                                                                            object transaction_fee=*,
+                                                                            bint is_deposit=*)
+cpdef transaction_types.RealisedPnlTransaction create_realised_pnl_transaction(object exchange_manager,
+                                                                               str currency,
+                                                                               str symbol,
+                                                                               object realised_pnl=*,
+                                                                               bint is_closed_pnl=*)
+cpdef transaction_types.FeeTransaction create_fee_transaction(object exchange_manager,
+                                                              str currency,
+                                                              str symbol,
+                                                              object quantity=*,
+                                                              str order_id=*,
+                                                              object funding_rate=*)
+cpdef transaction_types.TransferTransaction create_transfer_transaction(object exchange_manager,
+                                                                        str currency,
+                                                                        str symbol)
+
+cdef void _insert_transaction_instance(object exchange_manager, object transaction)
diff --git a/octobot_trading/personal_data/transactions/transaction_factory.py b/octobot_trading/personal_data/transactions/transaction_factory.py
new file mode 100644
index 000000000..b0c4b6ba5
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/transaction_factory.py
@@ -0,0 +1,87 @@
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+import octobot_trading.enums as enums
+import octobot_trading.constants as constants
+import octobot_trading.personal_data.transactions.types as transaction_types
+
+
+def create_blockchain_transaction(exchange_manager, currency,
+                                  blockchain_type, blockchain_transaction_id,
+                                  blockchain_transaction_status=enums.BlockchainTransactionStatus.CREATED,
+                                  source_address=None, destination_address=None,
+                                  quantity=constants.ZERO, transaction_fee=constants.ZERO, is_deposit=False):
+    blockchain_transaction = transaction_types.BlockchainTransaction(
+        exchange_name=exchange_manager.exchange_name,
+        creation_time=exchange_manager.exchange.get_exchange_current_time(),
+        transaction_type=enums.TransactionType.BLOCKCHAIN_DEPOSIT
+        if is_deposit else enums.TransactionType.BLOCKCHAIN_WITHDRAWAL,
+        currency=currency,
+        blockchain_type=blockchain_type,
+        blockchain_transaction_id=blockchain_transaction_id,
+        blockchain_transaction_status=blockchain_transaction_status,
+        source_address=source_address,
+        destination_address=destination_address,
+        quantity=quantity,
+        transaction_fee=transaction_fee
+    )
+    _insert_transaction_instance(exchange_manager, blockchain_transaction)
+    return blockchain_transaction
+
+
+def create_realised_pnl_transaction(exchange_manager, currency, symbol,
+                                    realised_pnl=constants.ZERO,
+                                    is_closed_pnl=False):
+    realised_pnl_transaction = transaction_types.RealisedPnlTransaction(
+        exchange_name=exchange_manager.exchange_name,
+        creation_time=exchange_manager.exchange.get_exchange_current_time(),
+        transaction_type=enums.TransactionType.CLOSE_REALISED_PNL
+        if is_closed_pnl else enums.TransactionType.REALISED_PNL,
+        currency=currency,
+        symbol=symbol,
+        realised_pnl=realised_pnl)
+    _insert_transaction_instance(exchange_manager, realised_pnl_transaction)
+    return realised_pnl_transaction
+
+
+def create_fee_transaction(exchange_manager, currency, symbol,
+                           quantity=constants.ZERO,
+                           order_id=None,
+                           funding_rate=constants.ZERO):
+    fee_transaction = transaction_types.FeeTransaction(
+        exchange_name=exchange_manager.exchange_name,
+        creation_time=exchange_manager.exchange.get_exchange_current_time(),
+        transaction_type=enums.TransactionType.TRADING_FEE if order_id else enums.TransactionType.FUNDING_FEE,
+        currency=currency,
+        symbol=symbol,
+        quantity=quantity,
+        order_id=order_id,
+        funding_rate=funding_rate)
+    _insert_transaction_instance(exchange_manager, fee_transaction)
+    return fee_transaction
+
+
+def create_transfer_transaction(exchange_manager, currency, symbol):
+    transfer_transaction = transaction_types.TransferTransaction(
+        exchange_name=exchange_manager.exchange_name,
+        creation_time=exchange_manager.exchange.get_exchange_current_time(),
+        currency=currency,
+        symbol=symbol)
+    _insert_transaction_instance(exchange_manager, transfer_transaction)
+    return transfer_transaction
+
+
+def _insert_transaction_instance(exchange_manager, transaction):
+    exchange_manager.exchange_personal_data.transactions_manager.insert_transaction_instance(transaction)
diff --git a/octobot_trading/personal_data/transactions/transactions_manager.pxd b/octobot_trading/personal_data/transactions/transactions_manager.pxd
new file mode 100644
index 000000000..219cc15e4
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/transactions_manager.pxd
@@ -0,0 +1,32 @@
+# cython: language_level=3
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+cimport octobot_trading.util as util
+
+
+cdef class TransactionsManager(util.Initializable):
+    cdef object logger
+
+    cdef public object transactions
+
+    cdef void _check_transactions_size(self)
+    cdef void _reset_transactions(self)
+    cdef void _remove_oldest_transactions(self, int nb_to_remove)
+
+    cpdef object get_transaction(self, str transaction_id)
+    cpdef object update_transaction_id(self, str transaction_id, str new_transaction_id, bint replace_if_exists=*)  # needs object to forward exceptions
+    cpdef object insert_transaction_instance(self, object transaction, bint replace_if_exists=*)  # needs object to forward exceptions
+    cpdef void clear(self)
diff --git a/octobot_trading/personal_data/transactions/transactions_manager.py b/octobot_trading/personal_data/transactions/transactions_manager.py
new file mode 100644
index 000000000..4b3c4b719
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/transactions_manager.py
@@ -0,0 +1,90 @@
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+import collections
+
+import octobot_commons.logging as logging
+
+import octobot_trading.errors as errors
+import octobot_trading.util as util
+
+
+class TransactionsManager(util.Initializable):
+    MAX_TRANSACTIONS_COUNT = 100000
+
+    def __init__(self):
+        super().__init__()
+        self.logger = logging.get_logger(self.__class__.__name__)
+        self.transactions = collections.OrderedDict()
+
+    async def initialize_impl(self):
+        self._reset_transactions()
+
+    def insert_transaction_instance(self, transaction, replace_if_exists=False):
+        """
+        Add the transaction instance to self.transactions by its transaction_id
+        Can raise DuplicateTransactionIdError if a transaction already exists
+        with the same transaction_id (when not replacing it with :replace_if_exists:)
+        :param transaction:
+        :param replace_if_exists: When True, replaces the transaction if a transaction has the same transaction_id
+        """
+        if transaction.transaction_id not in self.transactions or replace_if_exists:
+            self.transactions[transaction.transaction_id] = transaction
+            self._check_transactions_size()
+        else:
+            raise errors.DuplicateTransactionIdError(
+                f"Transaction with id '{transaction.transaction_id}' already exists")
+
+    def get_transaction(self, transaction_id):
+        """
+        Return the transaction instance with :transaction_id: as transaction_id
+        Can raise KeyError when transaction_id doesn't exist
+        :param transaction_id: the transaction id
+        :return: the transaction with the id :transaction_id:
+        """
+        return self.transactions[transaction_id]
+
+    def update_transaction_id(self, transaction_id, new_transaction_id, replace_if_exists=False):
+        """
+        Update a transaction by id
+        Can raise KeyError when initial transaction doesn't exist
+        Can raise DuplicateTransactionIdError when the :new_transaction_id: is already used
+        :param transaction_id: the transaction id to update
+        :param new_transaction_id: the transaction id to set
+        :param replace_if_exists: when True, replace the transaction with :new_transaction_id: if exist
+        """
+        transaction = self.get_transaction(transaction_id)
+        transaction.set_transaction_id(new_transaction_id)
+        try:
+            self.insert_transaction_instance(transaction, replace_if_exists=replace_if_exists)
+            self.transactions.pop(transaction_id)
+        except errors.DuplicateTransactionIdError as e:
+            transaction.set_transaction_id(transaction_id)
+            raise errors.DuplicateTransactionIdError from e
+
+    # private
+    def _check_transactions_size(self):
+        if len(self.transactions) > self.MAX_TRANSACTIONS_COUNT:
+            self._remove_oldest_transactions(int(self.MAX_TRANSACTIONS_COUNT / 10))
+
+    def _reset_transactions(self):
+        self.transactions = collections.OrderedDict()
+
+    def _remove_oldest_transactions(self, nb_to_remove):
+        for _ in range(nb_to_remove):
+            self.transactions.popitem(last=False)
+
+    def clear(self):
+        self._reset_transactions()
diff --git a/octobot_trading/personal_data/transactions/types/__init__.pxd b/octobot_trading/personal_data/transactions/types/__init__.pxd
new file mode 100644
index 000000000..7a5673a69
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/types/__init__.pxd
@@ -0,0 +1,40 @@
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+
+from octobot_trading.personal_data.transactions.types cimport blockchain_transaction
+from octobot_trading.personal_data.transactions.types cimport fee_transaction
+from octobot_trading.personal_data.transactions.types cimport realised_pnl_transaction
+from octobot_trading.personal_data.transactions.types cimport transfer_transaction
+
+from octobot_trading.personal_data.transactions.types.blockchain_transaction cimport (
+    BlockchainTransaction,
+)
+from octobot_trading.personal_data.transactions.types.fee_transaction cimport (
+    FeeTransaction,
+)
+from octobot_trading.personal_data.transactions.types.realised_pnl_transaction cimport (
+    RealisedPnlTransaction,
+)
+from octobot_trading.personal_data.transactions.types.transfer_transaction cimport (
+    TransferTransaction,
+)
+
+__all__ = [
+    "BlockchainTransaction",
+    "FeeTransaction",
+    "RealisedPnlTransaction",
+    "TransferTransaction",
+]
diff --git a/octobot_trading/personal_data/transactions/types/__init__.py b/octobot_trading/personal_data/transactions/types/__init__.py
new file mode 100644
index 000000000..b854d471d
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/types/__init__.py
@@ -0,0 +1,40 @@
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+
+from octobot_trading.personal_data.transactions.types import blockchain_transaction
+from octobot_trading.personal_data.transactions.types import fee_transaction
+from octobot_trading.personal_data.transactions.types import realised_pnl_transaction
+from octobot_trading.personal_data.transactions.types import transfer_transaction
+
+from octobot_trading.personal_data.transactions.types.blockchain_transaction import (
+    BlockchainTransaction,
+)
+from octobot_trading.personal_data.transactions.types.fee_transaction import (
+    FeeTransaction,
+)
+from octobot_trading.personal_data.transactions.types.realised_pnl_transaction import (
+    RealisedPnlTransaction,
+)
+from octobot_trading.personal_data.transactions.types.transfer_transaction import (
+    TransferTransaction,
+)
+
+__all__ = [
+    "BlockchainTransaction",
+    "FeeTransaction",
+    "RealisedPnlTransaction",
+    "TransferTransaction",
+]
diff --git a/octobot_trading/personal_data/transactions/types/blockchain_transaction.pxd b/octobot_trading/personal_data/transactions/types/blockchain_transaction.pxd
new file mode 100644
index 000000000..721751624
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/types/blockchain_transaction.pxd
@@ -0,0 +1,33 @@
+# cython: language_level=3
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+
+cimport octobot_trading.personal_data.transactions.transaction as transaction
+
+cdef class BlockchainTransaction(transaction.Transaction):
+    cdef readonly str source_address
+    cdef readonly str destination_address
+    cdef readonly str blockchain_transaction_id
+
+    cdef readonly object blockchain_type
+    cdef readonly object blockchain_transaction_status
+    cdef readonly object quantity
+    cdef readonly object transaction_fee
+
+    cpdef bint is_deposit(self)
+    cpdef bint is_withdrawal(self)
+    cpdef bint is_pending(self)
+    cpdef bint is_validated(self)
diff --git a/octobot_trading/personal_data/transactions/types/blockchain_transaction.py b/octobot_trading/personal_data/transactions/types/blockchain_transaction.py
new file mode 100644
index 000000000..11bdfec8a
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/types/blockchain_transaction.py
@@ -0,0 +1,51 @@
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+import uuid
+
+import octobot_trading.constants as constants
+import octobot_trading.enums as enums
+import octobot_trading.personal_data.transactions.transaction as transaction
+
+
+class BlockchainTransaction(transaction.Transaction):
+    def __init__(self, exchange_name, creation_time, transaction_type, currency, blockchain_type,
+                 blockchain_transaction_id,
+                 blockchain_transaction_status=enums.BlockchainTransactionStatus.CREATED,
+                 source_address=None,
+                 destination_address=None,
+                 quantity=constants.ZERO,
+                 transaction_fee=constants.ZERO):
+        self.source_address = source_address
+        self.destination_address = destination_address
+        self.blockchain_transaction_id = blockchain_transaction_id
+        self.blockchain_type = blockchain_type
+        self.blockchain_transaction_status = blockchain_transaction_status
+        self.quantity = quantity
+        self.transaction_fee = transaction_fee
+        super().__init__(exchange_name, creation_time, transaction_type, currency=currency)
+        self.transaction_id = self.blockchain_transaction_id if self.blockchain_transaction_id else str(uuid.uuid4())
+
+    def is_deposit(self):
+        return self.transaction_type is enums.TransactionType.BLOCKCHAIN_DEPOSIT
+
+    def is_withdrawal(self):
+        return self.transaction_type is enums.TransactionType.BLOCKCHAIN_WITHDRAWAL
+
+    def is_pending(self):
+        return self.blockchain_transaction_status is enums.BlockchainTransactionStatus.CONFIRMING
+
+    def is_validated(self):
+        return self.blockchain_transaction_status is enums.BlockchainTransactionStatus.SUCCESS
diff --git a/octobot_trading/personal_data/transactions/types/fee_transaction.pxd b/octobot_trading/personal_data/transactions/types/fee_transaction.pxd
new file mode 100644
index 000000000..32d44a5ae
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/types/fee_transaction.pxd
@@ -0,0 +1,27 @@
+# cython: language_level=3
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+
+cimport octobot_trading.personal_data.transactions.transaction as transaction
+
+cdef class FeeTransaction(transaction.Transaction):
+    cdef readonly object quantity
+    cdef readonly object funding_rate
+
+    cdef readonly str order_id
+
+    cpdef bint is_funding_fee(self)
+    cpdef bint is_trading_fee(self)
diff --git a/octobot_trading/personal_data/transactions/types/fee_transaction.py b/octobot_trading/personal_data/transactions/types/fee_transaction.py
new file mode 100644
index 000000000..29de6ff24
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/types/fee_transaction.py
@@ -0,0 +1,40 @@
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+import uuid
+
+import octobot_trading.constants as constants
+import octobot_trading.enums as enums
+import octobot_trading.personal_data.transactions.transaction as transaction
+
+
+class FeeTransaction(transaction.Transaction):
+    def __init__(self, exchange_name, creation_time, transaction_type, currency, symbol, quantity,
+                 order_id=None,
+                 funding_rate=constants.ZERO):
+        self.quantity = quantity
+        self.order_id = order_id
+        self.funding_rate = funding_rate
+        super().__init__(exchange_name, creation_time, transaction_type, currency, symbol=symbol)
+        self.transaction_id = f"{self.exchange_name}" \
+                              f"-{self.order_id if self.order_id else str(uuid.uuid4())[:4]}" \
+                              f"-{self.symbol}" \
+                              f"-{str(self.creation_time)}"
+
+    def is_funding_fee(self):
+        return self.transaction_type is enums.TransactionType.FUNDING_FEE
+
+    def is_trading_fee(self):
+        return self.transaction_type is enums.TransactionType.TRADING_FEE
diff --git a/octobot_trading/personal_data/transactions/types/realised_pnl_transaction.pxd b/octobot_trading/personal_data/transactions/types/realised_pnl_transaction.pxd
new file mode 100644
index 000000000..1b34521c5
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/types/realised_pnl_transaction.pxd
@@ -0,0 +1,23 @@
+# cython: language_level=3
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+
+cimport octobot_trading.personal_data.transactions.transaction as transaction
+
+cdef class RealisedPnlTransaction(transaction.Transaction):
+    cdef readonly object realised_pnl
+
+    cpdef bint is_closed_pnl(self)
diff --git a/octobot_trading/personal_data/transactions/types/realised_pnl_transaction.py b/octobot_trading/personal_data/transactions/types/realised_pnl_transaction.py
new file mode 100644
index 000000000..a4e77fbcd
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/types/realised_pnl_transaction.py
@@ -0,0 +1,33 @@
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+import uuid
+
+import octobot_trading.enums as enums
+import octobot_trading.personal_data.transactions.transaction as transaction
+
+
+class RealisedPnlTransaction(transaction.Transaction):
+    def __init__(self, exchange_name, creation_time, transaction_type, currency, symbol, realised_pnl):
+        super().__init__(exchange_name, creation_time, transaction_type, currency, symbol=symbol)
+        self.realised_pnl = realised_pnl
+        self.transaction_id = f"{self.exchange_name}" \
+                              f"-{str(uuid.uuid4())[:4]}" \
+                              f"-{self.symbol}" \
+                              f"-{str(self.creation_time)}" \
+                              f"{'-closed' if self.is_closed_pnl() else ''}"
+
+    def is_closed_pnl(self):
+        return self.transaction_type is enums.TransactionType.CLOSE_REALISED_PNL
diff --git a/octobot_trading/personal_data/transactions/types/transfer_transaction.pxd b/octobot_trading/personal_data/transactions/types/transfer_transaction.pxd
new file mode 100644
index 000000000..217480b86
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/types/transfer_transaction.pxd
@@ -0,0 +1,21 @@
+# cython: language_level=3
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+
+cimport octobot_trading.personal_data.transactions.transaction as transaction
+
+cdef class TransferTransaction(transaction.Transaction):
+    pass
diff --git a/octobot_trading/personal_data/transactions/types/transfer_transaction.py b/octobot_trading/personal_data/transactions/types/transfer_transaction.py
new file mode 100644
index 000000000..287b4d482
--- /dev/null
+++ b/octobot_trading/personal_data/transactions/types/transfer_transaction.py
@@ -0,0 +1,26 @@
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+
+import octobot_trading.enums as enums
+import octobot_trading.personal_data.transactions.transaction as transaction
+
+
+class TransferTransaction(transaction.Transaction):
+    def __init__(self, exchange_name, creation_time, currency, symbol):
+        super().__init__(exchange_name, creation_time,
+                         transaction_type=enums.TransactionType.TRANSFER,
+                         currency=currency,
+                         symbol=symbol)
diff --git a/setup.py b/setup.py
index 1f4b59070..dff5caa46 100644
--- a/setup.py
+++ b/setup.py
@@ -146,6 +146,13 @@ def build_ext(*args, **kwargs):
     "octobot_trading.personal_data.positions.states.liquidate_position_state",
     "octobot_trading.personal_data.positions.states.open_position_state",
     "octobot_trading.personal_data.positions.states.position_state_factory",
+    "octobot_trading.personal_data.transactions.transaction",
+    "octobot_trading.personal_data.transactions.transaction_factory",
+    "octobot_trading.personal_data.transactions.transactions_manager",
+    "octobot_trading.personal_data.transactions.types.blockchain_transaction",
+    "octobot_trading.personal_data.transactions.types.fee_transaction",
+    "octobot_trading.personal_data.transactions.types.realised_pnl_transaction",
+    "octobot_trading.personal_data.transactions.types.transfer_transaction",
     "octobot_trading.modes.modes_factory",
     "octobot_trading.modes.channel.abstract_mode_producer",
     "octobot_trading.modes.channel.mode",


Test patch:
diff --git a/tests/exchange_data/contracts/test_margin_contract.py b/tests/exchange_data/contracts/test_margin_contract.py
index c509758ac..14e14d25d 100644
--- a/tests/exchange_data/contracts/test_margin_contract.py
+++ b/tests/exchange_data/contracts/test_margin_contract.py
@@ -52,10 +52,6 @@ async def test_set_current_leverage():
     contract.set_current_leverage(decimal.Decimal(50))
     assert contract.current_leverage == decimal.Decimal(50)
 
-    if not os.getenv('CYTHON_IGNORE'):
-        with pytest.raises(errors.InvalidLeverageValue):
-            contract.set_current_leverage(decimal.Decimal(200))
-
 
 async def test_set_margin_type():
     contract = contracts.MarginContract(
diff --git a/tests/exchange_data/prices/test_price_events_manager.py b/tests/exchange_data/prices/test_price_events_manager.py
index bd7d5a083..16aca3a00 100644
--- a/tests/exchange_data/prices/test_price_events_manager.py
+++ b/tests/exchange_data/prices/test_price_events_manager.py
@@ -19,9 +19,11 @@
 from asyncio import Event
 from mock import patch, Mock
 
+import octobot_trading.constants as trading_constants
+
 from tests.exchange_data import price_events_manager
 from tests import event_loop
-from tests.test_utils.random_numbers import random_recent_trade, decimal_random_price, random_price, random_timestamp
+from tests.test_utils.random_numbers import decimal_random_recent_trade, decimal_random_price, random_timestamp
 
 # All test coroutines will be treated as marked.
 pytestmark = pytest.mark.asyncio
@@ -44,108 +46,109 @@ async def test_add_event(price_events_manager):
 
 
 async def test_handle_recent_trades(price_events_manager):
-    random_price_1 = random_price(min_value=2)
+    random_price_1 = decimal_random_price(min_value=decimal.Decimal(2))
     random_timestamp_1 = random_timestamp(min_value=2, max_value=1000)
-    price_event_1 = price_events_manager.add_event(decimal.Decimal(str(random_price_1)), random_timestamp_1, True)
+    price_event_1 = price_events_manager.add_event(random_price_1, random_timestamp_1, True)
     with patch.object(price_event_1, 'set', new=Mock()) as price_event_1_set:
         price_events_manager.handle_recent_trades([])
         with pytest.raises(AssertionError):
             price_event_1_set.assert_called_once()
         price_events_manager.handle_recent_trades(
-            [random_recent_trade(price=random_price(max_value=random_price_1 - 1),
-                                 timestamp=random_timestamp(max_value=random_timestamp_1 - 1)),
-             random_recent_trade(price=random_price(max_value=random_price_1 - 1),
-                                 timestamp=random_timestamp(max_value=random_timestamp_1 - 1)),
-             random_recent_trade(price=random_price(max_value=random_price_1 - 1),
-                                 timestamp=random_timestamp(max_value=random_timestamp_1 - 1))])
+            [decimal_random_recent_trade(price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE),
+                                         timestamp=random_timestamp(max_value=random_timestamp_1 - 1)),
+             decimal_random_recent_trade(price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE),
+                                         timestamp=random_timestamp(max_value=random_timestamp_1 - 1)),
+             decimal_random_recent_trade(price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE),
+                                         timestamp=random_timestamp(max_value=random_timestamp_1 - 1))])
         with pytest.raises(AssertionError):
             price_event_1_set.assert_called_once()
         price_events_manager.handle_recent_trades(
-            [random_recent_trade(price=random_price(max_value=random_price_1 - 1)),
-             random_recent_trade(price=random_price_1,
-                                 timestamp=random_timestamp_1),
-             random_recent_trade(price=random_price(max_value=random_price_1 - 1)),
-             random_recent_trade(price=random_price(max_value=random_price_1 - 1))])
+            [decimal_random_recent_trade(price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE)),
+             decimal_random_recent_trade(price=random_price_1,
+                                         timestamp=random_timestamp_1),
+             decimal_random_recent_trade(price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE)),
+             decimal_random_recent_trade(price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE))])
         price_event_1_set.assert_called_once()
 
 
 async def test_handle_recent_trades_multiple_events(price_events_manager):
-    random_price_1 = random_price(min_value=2)
-    random_price_2 = random_price(min_value=random_price_1)
+    random_price_1 = decimal_random_price(min_value=decimal.Decimal(2))
+    random_price_2 = decimal_random_price(min_value=random_price_1)
     random_timestamp_1 = random_timestamp(min_value=2, max_value=1000)
     random_timestamp_2 = random_timestamp(min_value=random_timestamp_1 + 2, max_value=5000)
-    price_event_1 = price_events_manager.add_event(decimal.Decimal(str(random_price_1)), random_timestamp_1, True)
-    price_event_2 = price_events_manager.add_event(decimal.Decimal(str(random_price_2)), random_timestamp_2, True)
+    price_event_1 = price_events_manager.add_event(random_price_1, random_timestamp_1, True)
+    price_event_2 = price_events_manager.add_event(random_price_2, random_timestamp_2, True)
     with patch.object(price_event_1, 'set', new=Mock()) as price_event_1_set, \
             patch.object(price_event_2, 'set', new=Mock()) as price_event_2_set:
         price_events_manager.handle_recent_trades(
-            [random_recent_trade(price=random_price(max_value=random_price_1 - 1)),
-             random_recent_trade(price=random_price(max_value=random_price_1 - 1)),
-             random_recent_trade(price=random_price(max_value=random_price_1 - 1))])
+            [decimal_random_recent_trade(price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE)),
+             decimal_random_recent_trade(price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE)),
+             decimal_random_recent_trade(price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE))])
         with pytest.raises(AssertionError):
             price_event_1_set.assert_called_once()
         with pytest.raises(AssertionError):
             price_event_2_set.assert_called_once()
         price_events_manager.handle_recent_trades(
-            [random_recent_trade(price=random_price(max_value=random_price_1 - 1),
-                                 timestamp=random_timestamp(max_value=random_timestamp_1 - 1)),
-             random_recent_trade(price=random_price_2 - 1,
-                                 timestamp=random_timestamp(min_value=random_timestamp_1,
-                                                            max_value=random_timestamp_2))])
+            [decimal_random_recent_trade(price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE),
+                                         timestamp=random_timestamp(max_value=random_timestamp_1 - 1)),
+             decimal_random_recent_trade(price=random_price_2 - trading_constants.ONE,
+                                         timestamp=random_timestamp(min_value=random_timestamp_1,
+                                                                    max_value=random_timestamp_2))])
         price_event_1_set.assert_called_once()
         with pytest.raises(AssertionError):
             price_event_2_set.assert_called_once()
         price_events_manager.handle_recent_trades(
-            [random_recent_trade(price=random_price(max_value=random_price_1 - 1)),
-             random_recent_trade(price=random_price_2,
-                                 timestamp=random_timestamp_2)])
+            [decimal_random_recent_trade(price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE)),
+             decimal_random_recent_trade(price=random_price_2,
+                                         timestamp=random_timestamp_2)])
         price_event_2_set.assert_called_once()
 
-    price_event_1 = price_events_manager.add_event(decimal.Decimal(str(random_price_1)), random_timestamp_1, True)
-    price_event_2 = price_events_manager.add_event(decimal.Decimal(str(random_price_2)), random_timestamp_2, True)
+    price_event_1 = price_events_manager.add_event(random_price_1, random_timestamp_1, True)
+    price_event_2 = price_events_manager.add_event(random_price_2, random_timestamp_2, True)
     with patch.object(price_event_1, 'set', new=Mock()) as price_event_1_set, \
             patch.object(price_event_2, 'set', new=Mock()) as price_event_2_set:
         price_events_manager.handle_recent_trades(
-            [random_recent_trade(price=random_price(max_value=random_price_1 - 1),
-                                 timestamp=random_timestamp(max_value=random_timestamp_1 - 1)),
-             random_recent_trade(price=random_price_2 + 10,
-                                 timestamp=random_timestamp(min_value=random_timestamp_2 + 1))])
+            [decimal_random_recent_trade(price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE),
+                                         timestamp=random_timestamp(max_value=random_timestamp_1 - 1)),
+             decimal_random_recent_trade(price=random_price_2 + decimal.Decimal(10),
+                                         timestamp=random_timestamp(min_value=random_timestamp_2 + 1))])
         price_event_1_set.assert_called_once()
         price_event_2_set.assert_called_once()
 
-    price_event_1 = price_events_manager.add_event(decimal.Decimal(str(random_price_1)), random_timestamp_1, True)
-    price_event_2 = price_events_manager.add_event(decimal.Decimal(str(random_price_2)), random_timestamp_2, True)
+    price_event_1 = price_events_manager.add_event(random_price_1, random_timestamp_1, True)
+    price_event_2 = price_events_manager.add_event(random_price_2, random_timestamp_2, True)
     with patch.object(price_event_1, 'set', new=Mock()) as price_event_1_set, \
             patch.object(price_event_2, 'set', new=Mock()) as price_event_2_set:
         price_events_manager.handle_recent_trades(
-            [random_recent_trade(price=random_price(min_value=random_price_1, max_value=random_price_2 - 1),
-                                 timestamp=random_timestamp(min_value=random_timestamp_1 - 1)),
-             random_recent_trade(price=random_price_2,
-                                 timestamp=random_timestamp(max_value=random_timestamp_2 - 1))])
+            [decimal_random_recent_trade(
+                price=decimal_random_price(min_value=random_price_1, max_value=random_price_2 - trading_constants.ONE),
+                timestamp=random_timestamp(min_value=random_timestamp_1 - 1)),
+             decimal_random_recent_trade(price=random_price_2,
+                                         timestamp=random_timestamp(max_value=random_timestamp_2 - 1))])
         price_event_1_set.assert_called_once()
         with pytest.raises(AssertionError):
             price_event_2_set.assert_called_once()
 
 
 async def test_handle_price(price_events_manager):
-    random_price_1 = random_price()
+    random_price_1 = decimal_random_price()
     random_timestamp_1 = random_timestamp(min_value=2, max_value=1000)
     price_event_1 = price_events_manager.add_event(decimal.Decimal(str(random_price_1)), random_timestamp_1, True)
     with patch.object(price_event_1, 'set', new=Mock()) as price_event_1_set:
-        price_events_manager.handle_price(0, random_timestamp())
+        price_events_manager.handle_price(trading_constants.ZERO, random_timestamp())
         with pytest.raises(AssertionError):
             price_event_1_set.assert_called_once()
-        price_events_manager.handle_price(price=random_price(max_value=random_price_1 - 1),
+        price_events_manager.handle_price(price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE),
                                           timestamp=random_timestamp())
         with pytest.raises(AssertionError):
             price_event_1_set.assert_called_once()
-        price_events_manager.handle_price(price=random_price(max_value=random_price_1 - 1),
+        price_events_manager.handle_price(price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE),
                                           timestamp=random_timestamp())
-        price_events_manager.handle_price(price=random_price(min_value=random_price_1),
+        price_events_manager.handle_price(price=decimal_random_price(min_value=random_price_1),
                                           timestamp=random_timestamp_1 - 1)
         with pytest.raises(AssertionError):
             price_event_1_set.assert_called_once()
-        price_events_manager.handle_price(price=random_price(min_value=random_price_1),
+        price_events_manager.handle_price(price=decimal_random_price(min_value=random_price_1),
                                           timestamp=random_timestamp_1 + 1)
         price_event_1_set.assert_called_once()
 
diff --git a/tests/exchange_data/prices/test_prices_manager.py b/tests/exchange_data/prices/test_prices_manager.py
index a6a4b2d40..f6af994ec 100644
--- a/tests/exchange_data/prices/test_prices_manager.py
+++ b/tests/exchange_data/prices/test_prices_manager.py
@@ -13,6 +13,7 @@
 #
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
+import decimal
 import os
 
 import pytest
@@ -20,6 +21,7 @@
 
 import octobot_commons.constants as constants
 from octobot_trading.enums import MarkPriceSources
+import octobot_trading.constants
 from octobot_trading.exchange_data.prices.prices_manager import calculate_mark_price_from_recent_trade_prices
 
 from tests.exchanges import backtesting_exchange_manager, backtesting_config, fake_backtesting
@@ -27,86 +29,88 @@
 from tests import event_loop
 
 # All test coroutines will be treated as marked.
-from tests.test_utils.random_numbers import random_price
+from tests.test_utils.random_numbers import decimal_random_price
 
 pytestmark = pytest.mark.asyncio
 
 
 async def test_initialize(prices_manager):
-    assert prices_manager.mark_price == prices_manager.mark_price_set_time == 0
+    assert prices_manager.mark_price == octobot_trading.constants.ZERO
+    assert prices_manager.mark_price_set_time == 0
     assert not prices_manager.valid_price_received_event.is_set()
 
     # should be reset in init
-    prices_manager.mark_price = 10
+    prices_manager.mark_price = decimal.Decimal(10)
     prices_manager.mark_price_set_time = 10
     prices_manager.valid_price_received_event.set()
 
     await prices_manager.initialize()
-    assert prices_manager.mark_price == prices_manager.mark_price_set_time == 0
+    assert prices_manager.mark_price == octobot_trading.constants.ZERO
+    assert prices_manager.mark_price_set_time == 0
     assert not prices_manager.valid_price_received_event.is_set()
 
 
 async def test_set_mark_price(prices_manager):
-    prices_manager.set_mark_price(10, MarkPriceSources.EXCHANGE_MARK_PRICE.value)
-    assert prices_manager.mark_price == 10
+    prices_manager.set_mark_price(decimal.Decimal(10), MarkPriceSources.EXCHANGE_MARK_PRICE.value)
+    assert prices_manager.mark_price == decimal.Decimal(10)
     check_event_is_set(prices_manager)
 
 
 async def test_set_mark_price_for_exchange_source(prices_manager):
-    prices_manager.set_mark_price(10, MarkPriceSources.EXCHANGE_MARK_PRICE.value)
-    assert prices_manager.mark_price == 10
+    prices_manager.set_mark_price(decimal.Decimal(10), MarkPriceSources.EXCHANGE_MARK_PRICE.value)
+    assert prices_manager.mark_price == decimal.Decimal(10)
     check_event_is_set(prices_manager)
-    prices_manager.set_mark_price(25, MarkPriceSources.RECENT_TRADE_AVERAGE.value)
-    assert prices_manager.mark_price == 10  # Drop first RT update
-    prices_manager.set_mark_price(30, MarkPriceSources.RECENT_TRADE_AVERAGE.value)
-    assert prices_manager.mark_price == 30
-    prices_manager.set_mark_price(20, MarkPriceSources.TICKER_CLOSE_PRICE.value)
-    assert prices_manager.mark_price == 30
-    prices_manager.set_mark_price(15, MarkPriceSources.EXCHANGE_MARK_PRICE.value)
-    assert prices_manager.mark_price == 15
+    prices_manager.set_mark_price(decimal.Decimal(25), MarkPriceSources.RECENT_TRADE_AVERAGE.value)
+    assert prices_manager.mark_price == decimal.Decimal(10)  # Drop first RT update
+    prices_manager.set_mark_price(decimal.Decimal(30), MarkPriceSources.RECENT_TRADE_AVERAGE.value)
+    assert prices_manager.mark_price == decimal.Decimal(30)
+    prices_manager.set_mark_price(decimal.Decimal(20), MarkPriceSources.TICKER_CLOSE_PRICE.value)
+    assert prices_manager.mark_price == decimal.Decimal(30)
+    prices_manager.set_mark_price(decimal.Decimal(15), MarkPriceSources.EXCHANGE_MARK_PRICE.value)
+    assert prices_manager.mark_price == decimal.Decimal(15)
 
 
 async def test_set_mark_price_for_ticker_source_only(prices_manager):
-    assert prices_manager.set_mark_price(10, MarkPriceSources.TICKER_CLOSE_PRICE.value) is True
-    assert prices_manager.mark_price == 10
+    assert prices_manager.set_mark_price(decimal.Decimal(10), MarkPriceSources.TICKER_CLOSE_PRICE.value) is True
+    assert prices_manager.mark_price == decimal.Decimal(10)
     check_event_is_set(prices_manager)
 
 
 async def test_set_mark_price_for_rt_source_only(prices_manager):
-    assert prices_manager.set_mark_price(10, MarkPriceSources.RECENT_TRADE_AVERAGE.value) is False
-    assert prices_manager.mark_price == 0  # drop first RT mark price
-    assert prices_manager.set_mark_price(25, MarkPriceSources.RECENT_TRADE_AVERAGE.value) is True
-    assert prices_manager.mark_price == 25
+    assert prices_manager.set_mark_price(decimal.Decimal(10), MarkPriceSources.RECENT_TRADE_AVERAGE.value) is False
+    assert prices_manager.mark_price == decimal.Decimal(0)  # drop first RT mark price
+    assert prices_manager.set_mark_price(decimal.Decimal(25), MarkPriceSources.RECENT_TRADE_AVERAGE.value) is True
+    assert prices_manager.mark_price == decimal.Decimal(25)
     check_event_is_set(prices_manager)
 
 
 async def test_set_mark_price_for_ticker_with_rt_source_outdated(prices_manager):
-    prices_manager.set_mark_price(5, MarkPriceSources.RECENT_TRADE_AVERAGE.value)
-    assert prices_manager.mark_price == 0  # Drop first RT update
-    assert prices_manager.set_mark_price(10, MarkPriceSources.RECENT_TRADE_AVERAGE.value) is True
-    assert prices_manager.mark_price == 10
+    prices_manager.set_mark_price(decimal.Decimal(5), MarkPriceSources.RECENT_TRADE_AVERAGE.value)
+    assert prices_manager.mark_price == decimal.Decimal(0)  # Drop first RT update
+    assert prices_manager.set_mark_price(decimal.Decimal(10), MarkPriceSources.RECENT_TRADE_AVERAGE.value) is True
+    assert prices_manager.mark_price == decimal.Decimal(10)
     check_event_is_set(prices_manager)
-    assert prices_manager.set_mark_price(40, MarkPriceSources.TICKER_CLOSE_PRICE.value) is False
-    assert prices_manager.mark_price == 10  # should not be updated because RECENT_TRADE_AVERAGE has not expired
+    assert prices_manager.set_mark_price(decimal.Decimal(40), MarkPriceSources.TICKER_CLOSE_PRICE.value) is False
+    assert prices_manager.mark_price == decimal.Decimal(10)  # should not be updated because RECENT_TRADE_AVERAGE has not expired
     check_event_is_set(prices_manager)
-    assert prices_manager.set_mark_price(20, MarkPriceSources.TICKER_CLOSE_PRICE.value) is False
-    assert prices_manager.mark_price == 10  # should not be updated because RECENT_TRADE_AVERAGE has not expired
+    assert prices_manager.set_mark_price(decimal.Decimal(20), MarkPriceSources.TICKER_CLOSE_PRICE.value) is False
+    assert prices_manager.mark_price == decimal.Decimal(10)  # should not be updated because RECENT_TRADE_AVERAGE has not expired
     check_event_is_set(prices_manager)
     # force rt source expiration
     if not os.getenv('CYTHON_IGNORE'):
         prices_manager.mark_price_from_sources[MarkPriceSources.RECENT_TRADE_AVERAGE.value] = None
-        assert prices_manager.set_mark_price(40, MarkPriceSources.TICKER_CLOSE_PRICE.value) is True
-        assert prices_manager.mark_price == 40  # should be updated because RECENT_TRADE_AVERAGE has expired
+        assert prices_manager.set_mark_price(decimal.Decimal(40), MarkPriceSources.TICKER_CLOSE_PRICE.value) is True
+        assert prices_manager.mark_price == decimal.Decimal(40)  # should be updated because RECENT_TRADE_AVERAGE has expired
         check_event_is_set(prices_manager)
-        assert prices_manager.set_mark_price(8, MarkPriceSources.RECENT_TRADE_AVERAGE.value) is False
-        assert prices_manager.mark_price == 40  # Drop first RT update after reset
+        assert prices_manager.set_mark_price(decimal.Decimal(8), MarkPriceSources.RECENT_TRADE_AVERAGE.value) is False
+        assert prices_manager.mark_price == decimal.Decimal(40)  # Drop first RT update after reset
         assert prices_manager.mark_price_from_sources[MarkPriceSources.RECENT_TRADE_AVERAGE.value][1] == 0
-        assert prices_manager.set_mark_price(15, MarkPriceSources.RECENT_TRADE_AVERAGE.value) is True
-        assert prices_manager.mark_price == 15
+        assert prices_manager.set_mark_price(decimal.Decimal(15), MarkPriceSources.RECENT_TRADE_AVERAGE.value) is True
+        assert prices_manager.mark_price == decimal.Decimal(15)
         assert prices_manager.mark_price_from_sources[MarkPriceSources.RECENT_TRADE_AVERAGE.value][1] != 0
         check_event_is_set(prices_manager)
-        assert prices_manager.set_mark_price(20, MarkPriceSources.TICKER_CLOSE_PRICE.value) is False
-        assert prices_manager.mark_price == 15  # should not be updated because RECENT_TRADE_AVERAGE has not expired
+        assert prices_manager.set_mark_price(decimal.Decimal(20), MarkPriceSources.TICKER_CLOSE_PRICE.value) is False
+        assert prices_manager.mark_price == decimal.Decimal(15)  # should not be updated because RECENT_TRADE_AVERAGE has not expired
         check_event_is_set(prices_manager)
 
         # force rt source expiration
@@ -115,8 +119,8 @@ async def test_set_mark_price_for_ticker_with_rt_source_outdated(prices_manager)
             prices_manager.mark_price_from_sources[MarkPriceSources.RECENT_TRADE_AVERAGE.value][1] -
             5 * constants.MINUTE_TO_SECONDS
         )
-        prices_manager.set_mark_price(40, MarkPriceSources.TICKER_CLOSE_PRICE.value)
-        assert prices_manager.mark_price == 40  # should be updated because RECENT_TRADE_AVERAGE has expired
+        prices_manager.set_mark_price(decimal.Decimal(40), MarkPriceSources.TICKER_CLOSE_PRICE.value)
+        assert prices_manager.mark_price == decimal.Decimal(40)  # should be updated because RECENT_TRADE_AVERAGE has expired
         check_event_is_set(prices_manager)
 
 
@@ -127,8 +131,8 @@ async def test_get_mark_price(prices_manager):
     assert not prices_manager.valid_price_received_event.is_set()
 
     # set price
-    prices_manager.set_mark_price(10, MarkPriceSources.EXCHANGE_MARK_PRICE.value)
-    assert await prices_manager.get_mark_price(0.01) == 10
+    prices_manager.set_mark_price(decimal.Decimal(10), MarkPriceSources.EXCHANGE_MARK_PRICE.value)
+    assert await prices_manager.get_mark_price(0.01) == decimal.Decimal(10)
     assert prices_manager.valid_price_received_event.is_set()
 
     # expired price
@@ -139,23 +143,23 @@ async def test_get_mark_price(prices_manager):
         assert not prices_manager.valid_price_received_event.is_set()
 
     # reset price with this time
-    prices_manager.set_mark_price(10, MarkPriceSources.EXCHANGE_MARK_PRICE.value)
-    assert await prices_manager.get_mark_price(0.01) == 10
+    prices_manager.set_mark_price(decimal.Decimal(10), MarkPriceSources.EXCHANGE_MARK_PRICE.value)
+    assert await prices_manager.get_mark_price(0.01) == decimal.Decimal(10)
     assert prices_manager.valid_price_received_event.is_set()
 
     # current time move within allowed range
     if not os.getenv('CYTHON_IGNORE'):
         prices_manager.exchange_manager.backtesting.time_manager.current_timestamp = 1
-        assert await prices_manager.get_mark_price(0.01) == 10
+        assert await prices_manager.get_mark_price(0.01) == decimal.Decimal(10)
         assert prices_manager.valid_price_received_event.is_set()
 
     # new value
-    prices_manager.set_mark_price(42.0000172, MarkPriceSources.EXCHANGE_MARK_PRICE.value)
-    assert await prices_manager.get_mark_price(0.01) == 42.0000172
+    prices_manager.set_mark_price(decimal.Decimal(42.0000172), MarkPriceSources.EXCHANGE_MARK_PRICE.value)
+    assert await prices_manager.get_mark_price(0.01) == decimal.Decimal(42.0000172)
     assert prices_manager.valid_price_received_event.is_set()
 
     # random value
-    random_mark_price = random_price()
+    random_mark_price = decimal_random_price()
     prices_manager.set_mark_price(random_mark_price, MarkPriceSources.EXCHANGE_MARK_PRICE.value)
     assert await prices_manager.get_mark_price(0.01) == random_mark_price
     assert prices_manager.valid_price_received_event.is_set()
diff --git a/tests/exchanges/traders/test_trader.py b/tests/exchanges/traders/test_trader.py
index f8c0a1952..86632a4a2 100644
--- a/tests/exchanges/traders/test_trader.py
+++ b/tests/exchanges/traders/test_trader.py
@@ -21,13 +21,16 @@
 import pytest
 import time
 from mock import AsyncMock, patch, Mock
+from octobot_commons import asyncio_tools
 
-from tests import event_loop
+from octobot_trading.errors import TooManyOpenPositionError, InvalidLeverageValue
+from octobot_trading.personal_data import LinearPosition
 import octobot_commons.constants as commons_constants
 from octobot_commons.asyncio_tools import wait_asyncio_next_cycle
 from octobot_commons.tests.test_config import load_test_config
 from octobot_trading.personal_data.orders import Order
-from octobot_trading.enums import TraderOrderType, TradeOrderSide, TradeOrderType, OrderStatus, FeePropertyColumns
+from octobot_trading.enums import TraderOrderType, TradeOrderSide, TradeOrderType, OrderStatus, FeePropertyColumns, \
+    ExchangeConstantsPositionColumns, PositionMode, MarginType
 from octobot_trading.exchanges.exchange_manager import ExchangeManager
 from octobot_trading.personal_data.orders.order_factory import create_order_instance, create_order_instance_from_raw
 from octobot_trading.personal_data.orders import BuyLimitOrder, BuyMarketOrder, SellLimitOrder, StopLossOrder
@@ -36,8 +39,13 @@
 from octobot_trading.exchanges.traders.trader import Trader
 from octobot_trading.exchanges.traders.trader_simulator import TraderSimulator
 from octobot_trading.api.exchange import cancel_ccxt_throttle_task
+from tests.exchanges.traders import get_default_future_inverse_contract, DEFAULT_FUTURE_SYMBOL
 import octobot_trading.constants as constants
 
+from tests import event_loop
+from tests.exchanges import future_simulated_exchange_manager
+from tests.exchanges.traders import future_trader_simulator_with_default_linear
+
 # All test coroutines will be treated as marked.
 pytestmark = pytest.mark.asyncio
 
@@ -52,10 +60,11 @@ class TestTrader:
     EXCHANGE_MANAGER_CLASS_STRING = "binance"
 
     @staticmethod
-    async def init_default(simulated=True):
+    async def init_default(simulated=True, is_future=False):
         config = load_test_config()
         exchange_manager = ExchangeManager(config, TestTrader.EXCHANGE_MANAGER_CLASS_STRING)
         exchange_manager.is_simulated = simulated
+        exchange_manager.is_future = is_future
         await exchange_manager.initialize()
 
         trader = TraderSimulator(config, exchange_manager)
@@ -521,7 +530,7 @@ async def test_close_filled_stop_buy_order(self):
     async def test_close_filled_sell_limit_order_one_in_two(self):
         _, exchange_manager, trader_inst = await self.init_default()
         portfolio_manager = exchange_manager.exchange_personal_data.portfolio_manager
-        initial_portfolio = copy.deepcopy(portfolio_manager.portfolio.portfolio)
+        initial_portfolio = copy.copy(portfolio_manager.portfolio)
         orders_manager = exchange_manager.exchange_personal_data.orders_manager
         trades_manager = exchange_manager.exchange_personal_data.trades_manager
 
@@ -578,7 +587,7 @@ async def test_close_filled_sell_limit_order_one_in_two(self):
     async def test_close_filled_sell_limit_order(self):
         _, exchange_manager, trader_inst = await self.init_default()
         portfolio_manager = exchange_manager.exchange_personal_data.portfolio_manager
-        initial_portfolio = copy.deepcopy(portfolio_manager.portfolio.portfolio)
+        initial_portfolio = copy.copy(portfolio_manager.portfolio)
         orders_manager = exchange_manager.exchange_personal_data.orders_manager
         trades_manager = exchange_manager.exchange_personal_data.trades_manager
 
@@ -855,6 +864,117 @@ async def test_parse_exchange_order_to_order_instance(self):
         await self.stop(exchange_manager)
 
 
+async def test_close_position(future_trader_simulator_with_default_linear):
+    _, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+
+    contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, contract)
+    position_inst = LinearPosition(trader_inst, contract)
+    await position_inst.initialize()
+    position_inst.update_from_raw(
+        {
+            ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL
+        }
+    )
+    await position_inst.update(update_size=decimal.Decimal(10), mark_price=decimal.Decimal(100))
+    exchange_manager_inst.exchange_personal_data.positions_manager.upsert_position_instance(position_inst)
+
+    if not os.getenv('CYTHON_IGNORE'):
+        with patch('octobot_trading.exchange_data.prices.prices_manager.PricesManager.get_mark_price',
+                   new=AsyncMock(return_value=30)):
+            orders = await trader_inst.close_position(position_inst, limit_price=decimal.Decimal(5))
+            assert len(orders) == 1
+            assert orders[0].order_type is TraderOrderType.SELL_LIMIT
+            assert orders[0].origin_price == 5
+            assert orders[0].origin_quantity == 10
+
+        with patch('octobot_trading.exchange_data.prices.prices_manager.PricesManager.get_mark_price',
+                   new=AsyncMock(return_value=20)):
+            orders = await trader_inst.close_position(position_inst)
+            assert len(orders) == 1
+            assert orders[0].order_type is TraderOrderType.SELL_MARKET
+            assert orders[0].origin_price == 20
+            assert orders[0].origin_quantity == 10
+
+
+async def test_set_leverage(future_trader_simulator_with_default_linear):
+    _, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+
+    contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, contract)
+
+    await trader_inst.set_leverage(DEFAULT_FUTURE_SYMBOL, None, decimal.Decimal(10))
+    assert contract.current_leverage == decimal.Decimal(10)
+    await trader_inst.set_leverage(DEFAULT_FUTURE_SYMBOL, None, decimal.Decimal(10))
+    assert contract.current_leverage == decimal.Decimal(10)
+    await trader_inst.set_leverage(DEFAULT_FUTURE_SYMBOL, None, decimal.Decimal(10))
+    assert contract.current_leverage == decimal.Decimal(10)
+
+    assert contract.maximum_leverage == decimal.Decimal(100)
+    with pytest.raises(InvalidLeverageValue):
+        await trader_inst.set_leverage(DEFAULT_FUTURE_SYMBOL, None, decimal.Decimal(200))
+
+
+async def test_set_margin_type(future_trader_simulator_with_default_linear):
+    _, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+
+    contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, contract)
+
+    await trader_inst.set_margin_type(DEFAULT_FUTURE_SYMBOL, None, MarginType.ISOLATED)
+    assert contract.is_isolated()
+    await trader_inst.set_margin_type(DEFAULT_FUTURE_SYMBOL, None, MarginType.CROSS)
+    assert not contract.is_isolated()
+    await trader_inst.set_margin_type(DEFAULT_FUTURE_SYMBOL, None, MarginType.ISOLATED)
+    assert contract.is_isolated()
+
+
+async def test_set_position_mode(future_trader_simulator_with_default_linear):
+    _, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+
+    contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, contract)
+
+    await trader_inst.set_position_mode(DEFAULT_FUTURE_SYMBOL, PositionMode.ONE_WAY)
+    assert contract.is_one_way_position_mode()
+    await trader_inst.set_position_mode(DEFAULT_FUTURE_SYMBOL, PositionMode.HEDGE)
+    assert not contract.is_one_way_position_mode()
+    await trader_inst.set_position_mode(DEFAULT_FUTURE_SYMBOL, PositionMode.ONE_WAY)
+    assert contract.is_one_way_position_mode()
+
+    position_inst = LinearPosition(trader_inst, contract)
+    await position_inst.initialize()
+    position_inst.update_from_raw(
+        {
+            ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL
+        }
+    )
+    exchange_manager_inst.exchange_personal_data.positions_manager.upsert_position_instance(position_inst)
+
+    with pytest.raises(TooManyOpenPositionError):
+        await trader_inst.set_position_mode(DEFAULT_FUTURE_SYMBOL, PositionMode.HEDGE)
+
+
+async def test__has_open_position(future_trader_simulator_with_default_linear):
+    _, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+
+    contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, contract)
+
+    if not os.getenv('CYTHON_IGNORE'):
+        assert not trader_inst._has_open_position(DEFAULT_FUTURE_SYMBOL)
+
+        position_inst = LinearPosition(trader_inst, contract)
+        await position_inst.initialize()
+        position_inst.update_from_raw(
+            {
+                ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL
+            }
+        )
+        exchange_manager_inst.exchange_personal_data.positions_manager.upsert_position_instance(position_inst)
+        assert trader_inst._has_open_position(DEFAULT_FUTURE_SYMBOL)
+
+
 def make_coroutine(response):
     async def coroutine(*args, **kwargs):
         return response
diff --git a/tests/personal_data/orders/types/limit/test_buy_limit_order.py b/tests/personal_data/orders/types/limit/test_buy_limit_order.py
index d1ff07c91..be3c7ee3f 100644
--- a/tests/personal_data/orders/types/limit/test_buy_limit_order.py
+++ b/tests/personal_data/orders/types/limit/test_buy_limit_order.py
@@ -18,9 +18,10 @@
 from octobot_trading.enums import TraderOrderType
 from octobot_commons.asyncio_tools import wait_asyncio_next_cycle
 
+import octobot_trading.constants as trading_constants
 from tests.personal_data import DEFAULT_MARKET_QUANTITY, DEFAULT_ORDER_SYMBOL
-from tests.test_utils.random_numbers import decimal_random_price, random_price, decimal_random_quantity, \
-    random_recent_trade
+from tests.test_utils.random_numbers import decimal_random_price, decimal_random_quantity, \
+    decimal_random_recent_trade
 
 from tests import event_loop
 from tests.exchanges import simulated_trader, simulated_exchange_manager
@@ -45,17 +46,17 @@ async def test_buy_limit_order_trigger(buy_limit_order):
     price_events_manager = buy_limit_order.exchange_manager.exchange_symbols_data.get_exchange_symbol_data(
         DEFAULT_ORDER_SYMBOL).price_events_manager
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=random_price(min_value=float(order_price + 1)),
-                             timestamp=buy_limit_order.timestamp)])
+        [decimal_random_recent_trade(price=decimal_random_price(min_value=order_price + trading_constants.ONE),
+                                     timestamp=buy_limit_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert not buy_limit_order.is_filled()
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=order_price,
-                             timestamp=buy_limit_order.timestamp - 1)])
+        [decimal_random_recent_trade(price=order_price,
+                                     timestamp=buy_limit_order.timestamp - 1)])
     await wait_asyncio_next_cycle()
     assert not buy_limit_order.is_filled()
-    price_events_manager.handle_recent_trades([random_recent_trade(price=order_price,
-                                                                   timestamp=buy_limit_order.timestamp)])
+    price_events_manager.handle_recent_trades([decimal_random_recent_trade(price=order_price,
+                                                                           timestamp=buy_limit_order.timestamp)])
 
     await wait_asyncio_next_cycle()
     assert buy_limit_order.is_filled()
diff --git a/tests/personal_data/orders/types/limit/test_sell_limit_order.py b/tests/personal_data/orders/types/limit/test_sell_limit_order.py
index 8e59cf87f..e1a618f18 100644
--- a/tests/personal_data/orders/types/limit/test_sell_limit_order.py
+++ b/tests/personal_data/orders/types/limit/test_sell_limit_order.py
@@ -18,13 +18,14 @@
 
 from octobot_commons.asyncio_tools import wait_asyncio_next_cycle
 from octobot_trading.enums import TraderOrderType
+import octobot_trading.constants as trading_constants
 
 from tests import event_loop
 from tests.exchanges import simulated_trader, simulated_exchange_manager
 from tests.personal_data import DEFAULT_ORDER_SYMBOL, DEFAULT_SYMBOL_QUANTITY
 from tests.personal_data.orders import sell_limit_order
-from tests.test_utils.random_numbers import decimal_random_price, decimal_random_quantity, random_price, \
-    random_recent_trade
+from tests.test_utils.random_numbers import decimal_random_price, decimal_random_quantity, \
+    decimal_random_recent_trade
 
 pytestmark = pytest.mark.asyncio
 
@@ -45,17 +46,17 @@ async def test_sell_limit_order_trigger(sell_limit_order):
     price_events_manager = sell_limit_order.exchange_manager.exchange_symbols_data.get_exchange_symbol_data(
         DEFAULT_ORDER_SYMBOL).price_events_manager
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=random_price(max_value=float(order_price - 1)),
-                             timestamp=sell_limit_order.timestamp)])
+        [decimal_random_recent_trade(price=decimal_random_price(max_value=order_price - trading_constants.ONE),
+                                     timestamp=sell_limit_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert not sell_limit_order.is_filled()
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=order_price,
-                             timestamp=sell_limit_order.timestamp - 1)])
+        [decimal_random_recent_trade(price=order_price,
+                                     timestamp=sell_limit_order.timestamp - 1)])
     await wait_asyncio_next_cycle()
     assert not sell_limit_order.is_filled()
-    price_events_manager.handle_recent_trades([random_recent_trade(price=order_price,
-                                                                   timestamp=sell_limit_order.timestamp)])
+    price_events_manager.handle_recent_trades([decimal_random_recent_trade(price=order_price,
+                                                                           timestamp=sell_limit_order.timestamp)])
 
     await wait_asyncio_next_cycle()
     assert sell_limit_order.is_filled()
diff --git a/tests/personal_data/orders/types/limit/test_stop_loss_limit_order.py b/tests/personal_data/orders/types/limit/test_stop_loss_limit_order.py
index 7dc126c34..48a404c14 100644
--- a/tests/personal_data/orders/types/limit/test_stop_loss_limit_order.py
+++ b/tests/personal_data/orders/types/limit/test_stop_loss_limit_order.py
@@ -13,13 +13,16 @@
 #
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
+import decimal
+
 import pytest
 
 from octobot_commons.asyncio_tools import wait_asyncio_next_cycle
-from octobot_trading.enums import TraderOrderType, TradeOrderSide
+from octobot_trading.enums import TraderOrderType
+import octobot_trading.constants as trading_constants
 from tests.personal_data import DEFAULT_ORDER_SYMBOL, DEFAULT_SYMBOL_QUANTITY
-from tests.test_utils.random_numbers import decimal_random_price, random_price, decimal_random_quantity, \
-    random_recent_trade
+from tests.test_utils.random_numbers import decimal_random_price, decimal_random_quantity, \
+    decimal_random_recent_trade
 
 from tests import event_loop
 from tests.exchanges import simulated_trader, simulated_exchange_manager
@@ -30,10 +33,10 @@
 
 async def test_stop_loss_limit_order_trigger(stop_loss_limit_order):
     order_price = decimal_random_price()
-    stop_loss_limit_order.limit_price = order_price - 10
+    stop_loss_limit_order.limit_price = order_price - decimal.Decimal(10)
     stop_loss_limit_order.update(
         price=order_price,
-        quantity=decimal_random_quantity(max_value=DEFAULT_SYMBOL_QUANTITY / 10),
+        quantity=decimal_random_quantity(max_value=decimal.Decimal(DEFAULT_SYMBOL_QUANTITY / 10)),
         symbol=DEFAULT_ORDER_SYMBOL,
         order_type=TraderOrderType.STOP_LOSS_LIMIT,
     )
@@ -45,17 +48,17 @@ async def test_stop_loss_limit_order_trigger(stop_loss_limit_order):
     price_events_manager = stop_loss_limit_order.exchange_manager.exchange_symbols_data.get_exchange_symbol_data(
         DEFAULT_ORDER_SYMBOL).price_events_manager
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=random_price(min_value=float(order_price + 1)),
-                             timestamp=stop_loss_limit_order.timestamp)])
+        [decimal_random_recent_trade(price=decimal_random_price(min_value=order_price + trading_constants.ONE),
+                                     timestamp=stop_loss_limit_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert not stop_loss_limit_order.is_filled()
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=order_price,
-                             timestamp=stop_loss_limit_order.timestamp - 1)])
+        [decimal_random_recent_trade(price=order_price,
+                                     timestamp=stop_loss_limit_order.timestamp - 1)])
     await wait_asyncio_next_cycle()
     assert not stop_loss_limit_order.is_filled()
-    price_events_manager.handle_recent_trades([random_recent_trade(price=order_price,
-                                                                   timestamp=stop_loss_limit_order.timestamp)])
+    price_events_manager.handle_recent_trades([decimal_random_recent_trade(price=order_price,
+                                                                           timestamp=stop_loss_limit_order.timestamp)])
 
     # wait for 2 cycles as secondary orders are created
     await wait_asyncio_next_cycle()
diff --git a/tests/personal_data/orders/types/limit/test_stop_loss_order.py b/tests/personal_data/orders/types/limit/test_stop_loss_order.py
index 661dc8d0f..ad922259e 100644
--- a/tests/personal_data/orders/types/limit/test_stop_loss_order.py
+++ b/tests/personal_data/orders/types/limit/test_stop_loss_order.py
@@ -17,9 +17,10 @@
 
 from octobot_commons.asyncio_tools import wait_asyncio_next_cycle
 from octobot_trading.enums import TraderOrderType
+import octobot_trading.constants as trading_constants
 from tests.personal_data import DEFAULT_ORDER_SYMBOL, DEFAULT_SYMBOL_QUANTITY
-from tests.test_utils.random_numbers import decimal_random_price, random_price, decimal_random_quantity, \
-    random_recent_trade
+from tests.test_utils.random_numbers import decimal_random_price, decimal_random_quantity, \
+    decimal_random_recent_trade
 
 from tests import event_loop
 from tests.exchanges import simulated_trader, simulated_exchange_manager
@@ -46,17 +47,17 @@ async def test_stop_loss_sell_order_trigger(stop_loss_sell_order):
         DEFAULT_ORDER_SYMBOL).price_events_manager
     # stop loss sell order triggers when price is bellow or equal to its trigger price
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=random_price(min_value=float(order_price + 1)),
-                             timestamp=stop_loss_sell_order.timestamp)])
+        [decimal_random_recent_trade(price=decimal_random_price(min_value=order_price + trading_constants.ONE),
+                                     timestamp=stop_loss_sell_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert not stop_loss_sell_order.is_filled()
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=order_price,
-                             timestamp=stop_loss_sell_order.timestamp - 1)])
+        [decimal_random_recent_trade(price=order_price,
+                                     timestamp=stop_loss_sell_order.timestamp - 1)])
     await wait_asyncio_next_cycle()
     assert not stop_loss_sell_order.is_filled()
-    price_events_manager.handle_recent_trades([random_recent_trade(price=order_price,
-                                                                   timestamp=stop_loss_sell_order.timestamp)])
+    price_events_manager.handle_recent_trades([decimal_random_recent_trade(price=order_price,
+                                                                           timestamp=stop_loss_sell_order.timestamp)])
 
     await wait_asyncio_next_cycle()
     assert stop_loss_sell_order.is_filled()
@@ -79,17 +80,17 @@ async def test_stop_loss_buy_order_trigger(stop_loss_buy_order):
         DEFAULT_ORDER_SYMBOL).price_events_manager
     # stop loss buy order triggers when price is above or equal to its trigger price
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=random_price(max_value=float(order_price - 1)),
-                             timestamp=stop_loss_buy_order.timestamp)])
+        [decimal_random_recent_trade(price=decimal_random_price(max_value=order_price - trading_constants.ONE),
+                                     timestamp=stop_loss_buy_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert not stop_loss_buy_order.is_filled()
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=order_price,
-                             timestamp=stop_loss_buy_order.timestamp - 1)])
+        [decimal_random_recent_trade(price=order_price,
+                                     timestamp=stop_loss_buy_order.timestamp - 1)])
     await wait_asyncio_next_cycle()
     assert not stop_loss_buy_order.is_filled()
-    price_events_manager.handle_recent_trades([random_recent_trade(price=order_price,
-                                                                   timestamp=stop_loss_buy_order.timestamp)])
+    price_events_manager.handle_recent_trades([decimal_random_recent_trade(price=order_price,
+                                                                           timestamp=stop_loss_buy_order.timestamp)])
 
     await wait_asyncio_next_cycle()
     assert stop_loss_buy_order.is_filled()
diff --git a/tests/personal_data/orders/types/limit/test_take_profit_limit_order.py b/tests/personal_data/orders/types/limit/test_take_profit_limit_order.py
index ded79a4ca..22936adae 100644
--- a/tests/personal_data/orders/types/limit/test_take_profit_limit_order.py
+++ b/tests/personal_data/orders/types/limit/test_take_profit_limit_order.py
@@ -13,12 +13,15 @@
 #
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
+import decimal
+
 import pytest
 
 from octobot_commons.asyncio_tools import wait_asyncio_next_cycle
 from octobot_trading.enums import TraderOrderType
+import octobot_trading.constants as trading_constants
 from tests.personal_data import DEFAULT_SYMBOL_QUANTITY, DEFAULT_ORDER_SYMBOL
-from tests.test_utils.random_numbers import decimal_random_price, random_price, decimal_random_quantity, random_recent_trade
+from tests.test_utils.random_numbers import decimal_random_price, decimal_random_quantity, decimal_random_recent_trade
 
 from tests import event_loop
 from tests.exchanges import simulated_trader, simulated_exchange_manager
@@ -29,10 +32,10 @@
 
 async def test_take_profit_limit_order_trigger(take_profit_limit_order):
     order_price = decimal_random_price(min_value=2)
-    take_profit_limit_order.limit_price = order_price + 10
+    take_profit_limit_order.limit_price = order_price + decimal.Decimal(10)
     take_profit_limit_order.update(
         price=order_price,
-        quantity=decimal_random_quantity(max_value=DEFAULT_SYMBOL_QUANTITY / 10),
+        quantity=decimal_random_quantity(max_value=decimal.Decimal(DEFAULT_SYMBOL_QUANTITY / 10)),
         symbol=DEFAULT_ORDER_SYMBOL,
         order_type=TraderOrderType.TAKE_PROFIT_LIMIT,
     )
@@ -44,17 +47,17 @@ async def test_take_profit_limit_order_trigger(take_profit_limit_order):
     price_events_manager = take_profit_limit_order.exchange_manager.exchange_symbols_data.get_exchange_symbol_data(
         DEFAULT_ORDER_SYMBOL).price_events_manager
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=float(random_price(max_value=float(order_price - 1))),
-                             timestamp=take_profit_limit_order.timestamp)])
+        [decimal_random_recent_trade(price=decimal_random_price(max_value=order_price - trading_constants.ONE),
+                                     timestamp=take_profit_limit_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert not take_profit_limit_order.is_filled()
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=order_price,
-                             timestamp=take_profit_limit_order.timestamp - 1)])
+        [decimal_random_recent_trade(price=order_price,
+                                     timestamp=take_profit_limit_order.timestamp - 1)])
     await wait_asyncio_next_cycle()
     assert not take_profit_limit_order.is_filled()
-    price_events_manager.handle_recent_trades([random_recent_trade(price=order_price,
-                                                                   timestamp=take_profit_limit_order.timestamp)])
+    price_events_manager.handle_recent_trades([decimal_random_recent_trade(price=order_price,
+                                                                           timestamp=take_profit_limit_order.timestamp)])
 
     # wait for 2 cycles as secondary orders are created
     await wait_asyncio_next_cycle()
diff --git a/tests/personal_data/orders/types/limit/test_take_profit_order.py b/tests/personal_data/orders/types/limit/test_take_profit_order.py
index e78e12ed8..7fba8b9c8 100644
--- a/tests/personal_data/orders/types/limit/test_take_profit_order.py
+++ b/tests/personal_data/orders/types/limit/test_take_profit_order.py
@@ -13,13 +13,16 @@
 #
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
+import decimal
+
 import pytest
 
 from octobot_commons.asyncio_tools import wait_asyncio_next_cycle
-from octobot_trading.enums import TraderOrderType, TradeOrderSide
+from octobot_trading.enums import TraderOrderType
+import octobot_trading.constants as trading_constants
 from tests.personal_data import DEFAULT_ORDER_SYMBOL, DEFAULT_SYMBOL_QUANTITY, DEFAULT_MARKET_QUANTITY
 
-from tests.test_utils.random_numbers import decimal_random_price, random_price, decimal_random_quantity, random_recent_trade
+from tests.test_utils.random_numbers import decimal_random_price, decimal_random_quantity, decimal_random_recent_trade
 from tests import event_loop
 from tests.exchanges import simulated_trader, simulated_exchange_manager
 from tests.personal_data.orders import take_profit_sell_order, take_profit_buy_order
@@ -43,17 +46,17 @@ async def test_take_profit_sell_order_trigger(take_profit_sell_order):
     price_events_manager = take_profit_sell_order.exchange_manager.exchange_symbols_data.get_exchange_symbol_data(
         DEFAULT_ORDER_SYMBOL).price_events_manager
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=random_price(max_value=float(order_price - 1)),
-                             timestamp=take_profit_sell_order.timestamp)])
+        [decimal_random_recent_trade(price=decimal_random_price(max_value=order_price - trading_constants.ONE),
+                                     timestamp=take_profit_sell_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert not take_profit_sell_order.is_filled()
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=order_price,
-                             timestamp=take_profit_sell_order.timestamp - 1)])
+        [decimal_random_recent_trade(price=order_price,
+                                     timestamp=take_profit_sell_order.timestamp - 1)])
     await wait_asyncio_next_cycle()
     assert not take_profit_sell_order.is_filled()
-    price_events_manager.handle_recent_trades([random_recent_trade(price=order_price,
-                                                                   timestamp=take_profit_sell_order.timestamp)])
+    price_events_manager.handle_recent_trades([decimal_random_recent_trade(price=order_price,
+                                                                           timestamp=take_profit_sell_order.timestamp)])
 
     # wait for 2 cycles as secondary orders are created
     await wait_asyncio_next_cycle()
@@ -65,7 +68,8 @@ async def test_take_profit_buy_order_trigger(take_profit_buy_order):
     order_price = decimal_random_price()
     take_profit_buy_order.update(
         price=order_price,
-        quantity=decimal_random_quantity(max_value=DEFAULT_MARKET_QUANTITY / (order_price * 2)),
+        quantity=decimal_random_quantity(max_value=decimal.Decimal(
+            DEFAULT_MARKET_QUANTITY / (order_price * decimal.Decimal(2)))),
         symbol=DEFAULT_ORDER_SYMBOL,
         order_type=TraderOrderType.TAKE_PROFIT,
     )
@@ -77,17 +81,17 @@ async def test_take_profit_buy_order_trigger(take_profit_buy_order):
     price_events_manager = take_profit_buy_order.exchange_manager.exchange_symbols_data.get_exchange_symbol_data(
         DEFAULT_ORDER_SYMBOL).price_events_manager
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=random_price(min_value=float(order_price - 1)),
-                             timestamp=take_profit_buy_order.timestamp)])
+        [decimal_random_recent_trade(price=decimal_random_price(min_value=order_price - trading_constants.ONE),
+                                     timestamp=take_profit_buy_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert not take_profit_buy_order.is_filled()
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=order_price,
-                             timestamp=take_profit_buy_order.timestamp - 1)])
+        [decimal_random_recent_trade(price=order_price,
+                                     timestamp=take_profit_buy_order.timestamp - 1)])
     await wait_asyncio_next_cycle()
     assert not take_profit_buy_order.is_filled()
-    price_events_manager.handle_recent_trades([random_recent_trade(price=order_price,
-                                                                   timestamp=take_profit_buy_order.timestamp)])
+    price_events_manager.handle_recent_trades([decimal_random_recent_trade(price=order_price,
+                                                                           timestamp=take_profit_buy_order.timestamp)])
 
     # wait for 2 cycles as secondary orders are created
     await wait_asyncio_next_cycle()
diff --git a/tests/personal_data/orders/types/market/test_buy_market_order.py b/tests/personal_data/orders/types/market/test_buy_market_order.py
index 1d294fd38..3e08a370d 100644
--- a/tests/personal_data/orders/types/market/test_buy_market_order.py
+++ b/tests/personal_data/orders/types/market/test_buy_market_order.py
@@ -16,7 +16,7 @@
 import pytest
 
 from octobot_trading.enums import TraderOrderType
-from tests.personal_data import DEFAULT_SYMBOL_QUANTITY, DEFAULT_ORDER_SYMBOL, DEFAULT_MARKET_QUANTITY
+from tests.personal_data import DEFAULT_ORDER_SYMBOL, DEFAULT_MARKET_QUANTITY
 from tests.test_utils.random_numbers import decimal_random_price, decimal_random_quantity
 
 from tests import event_loop
diff --git a/tests/personal_data/orders/types/trailing/test_trailing_stop_order.py b/tests/personal_data/orders/types/trailing/test_trailing_stop_order.py
index a3c9263a7..17604ba9c 100644
--- a/tests/personal_data/orders/types/trailing/test_trailing_stop_order.py
+++ b/tests/personal_data/orders/types/trailing/test_trailing_stop_order.py
@@ -23,10 +23,11 @@
 from octobot_trading.exchange_data.prices.price_events_manager import PriceEventsManager
 from octobot_trading.enums import TradeOrderType, TradeOrderSide, MarkPriceSources
 from octobot_trading.personal_data.orders import TrailingStopOrder
+import octobot_trading.constants as trading_constants
 from tests.personal_data import DEFAULT_SYMBOL_QUANTITY, DEFAULT_ORDER_SYMBOL, DEFAULT_MARKET_QUANTITY
 
-from tests.test_utils.random_numbers import decimal_random_price, random_price, \
-    decimal_random_quantity, random_recent_trade
+from tests.test_utils.random_numbers import decimal_random_price, \
+    decimal_random_quantity, decimal_random_recent_trade
 from tests import event_loop
 from tests.exchanges import simulated_trader, simulated_exchange_manager
 from tests.personal_data.orders import trailing_stop_order
@@ -42,25 +43,28 @@ async def test_trailing_stop_trigger(trailing_stop_order):
     # set mark price
     set_mark_price(trailing_stop_order, order_price)
 
-    print(f"min: {max_trailing_hit_price} max: {order_price - 1}")
+    print(f"min: {max_trailing_hit_price} max: {order_price - trading_constants.ONE}")
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=random_price(min_value=float(max_trailing_hit_price), max_value=float(order_price - 1)),
-                             timestamp=trailing_stop_order.timestamp)])
+        [decimal_random_recent_trade(
+            price=decimal_random_price(min_value=decimal.Decimal(max_trailing_hit_price),
+                                       max_value=order_price - trading_constants.ONE),
+            timestamp=trailing_stop_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert not trailing_stop_order.is_filled()
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=order_price,
-                             timestamp=trailing_stop_order.timestamp - 1)])
+        [decimal_random_recent_trade(price=order_price,
+                                     timestamp=trailing_stop_order.timestamp - 1)])
     await wait_asyncio_next_cycle()
     assert not trailing_stop_order.is_filled()
-    price_events_manager.handle_recent_trades([random_recent_trade(price=order_price,
-                                                                   timestamp=trailing_stop_order.timestamp)])
+    price_events_manager.handle_recent_trades([decimal_random_recent_trade(price=order_price,
+                                                                           timestamp=trailing_stop_order.timestamp)])
 
     await wait_asyncio_next_cycle()
     assert not trailing_stop_order.is_filled()
     # avoid decimal to float rounding issues
-    price_events_manager.handle_recent_trades([random_recent_trade(price=max_trailing_hit_price - decimal.Decimal("0.0001"),
-                                                                   timestamp=trailing_stop_order.timestamp)])
+    price_events_manager.handle_recent_trades(
+        [decimal_random_recent_trade(price=max_trailing_hit_price - decimal.Decimal("0.0001"),
+                                     timestamp=trailing_stop_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert trailing_stop_order.is_filled()
 
@@ -68,24 +72,25 @@ async def test_trailing_stop_trigger(trailing_stop_order):
 async def test_trailing_stop_with_new_price(trailing_stop_order):
     trailing_stop_order, order_price, price_events_manager = await initialize_trailing_stop(trailing_stop_order)
     await trailing_stop_order.set_trailing_percent(decimal.Decimal(2))
-    new_trailing_price = decimal_random_price(min_value=order_price * decimal.Decimal(str(0.99)), max_value=order_price *  decimal.Decimal(str(1.01)))
+    new_trailing_price = decimal_random_price(min_value=order_price * decimal.Decimal(str(0.99)),
+                                              max_value=order_price * decimal.Decimal(str(1.01)))
 
     # set mark price
     set_mark_price(trailing_stop_order, new_trailing_price)
 
     # move trailing price
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=new_trailing_price,
-                             timestamp=trailing_stop_order.timestamp)])
+        [decimal_random_recent_trade(price=new_trailing_price,
+                                     timestamp=trailing_stop_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert not trailing_stop_order.is_filled()
 
     # test fill stop loss with new order price reference
     # avoid decimal to float rounding issues
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=get_price_percent(new_trailing_price - decimal.Decimal("0.0001"),
-                                                           trailing_stop_order.trailing_percent),
-                             timestamp=trailing_stop_order.timestamp)])
+        [decimal_random_recent_trade(price=get_price_percent(new_trailing_price - decimal.Decimal("0.0001"),
+                                                             trailing_stop_order.trailing_percent),
+                                     timestamp=trailing_stop_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert trailing_stop_order.is_filled()
 
@@ -93,23 +98,24 @@ async def test_trailing_stop_with_new_price(trailing_stop_order):
 async def test_trailing_stop_with_new_old_price(trailing_stop_order):
     trailing_stop_order, order_price, price_events_manager = await initialize_trailing_stop(trailing_stop_order)
     await trailing_stop_order.set_trailing_percent(decimal.Decimal(5))
-    new_trailing_price = decimal_random_price(min_value=order_price *  decimal.Decimal(str(0.99)), max_value=order_price *  decimal.Decimal(str(1.01)))
+    new_trailing_price = decimal_random_price(min_value=order_price * decimal.Decimal(str(0.99)),
+                                              max_value=order_price * decimal.Decimal(str(1.01)))
 
     # set mark price
     set_mark_price(trailing_stop_order, new_trailing_price)
 
     # move trailing price
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=new_trailing_price,
-                             timestamp=trailing_stop_order.timestamp)])
+        [decimal_random_recent_trade(price=new_trailing_price,
+                                     timestamp=trailing_stop_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert not trailing_stop_order.is_filled()
 
     # test fill stop loss with old price
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=get_price_percent(order_price - decimal.Decimal("0.0001"),
-                                                           trailing_stop_order.trailing_percent),
-                             timestamp=trailing_stop_order.timestamp)])
+        [decimal_random_recent_trade(price=get_price_percent(order_price - decimal.Decimal("0.0001"),
+                                                             trailing_stop_order.trailing_percent),
+                                     timestamp=trailing_stop_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert trailing_stop_order.is_filled()
 
@@ -118,36 +124,38 @@ async def test_trailing_stop_with_new_price_inversed(trailing_stop_order):
     trailing_stop_order, order_price, price_events_manager = await initialize_trailing_stop(trailing_stop_order,
                                                                                             side=TradeOrderSide.BUY)
     await trailing_stop_order.set_trailing_percent(decimal.Decimal(5))
-    new_trailing_price = decimal_random_price(min_value=order_price *  decimal.Decimal(str(0.99)), max_value=order_price *  decimal.Decimal(str(1.01)))
+    new_trailing_price = decimal_random_price(min_value=order_price * decimal.Decimal(str(0.99)),
+                                              max_value=order_price * decimal.Decimal(str(1.01)))
 
     # set mark price
     set_mark_price(trailing_stop_order, new_trailing_price)
 
     # move trailing price
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=new_trailing_price,
-                             timestamp=trailing_stop_order.timestamp)])
+        [decimal_random_recent_trade(price=new_trailing_price,
+                                     timestamp=trailing_stop_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert not trailing_stop_order.is_filled()
 
     # test fill stop loss with new order price reference
     price_events_manager.handle_recent_trades(
-        [random_recent_trade(price=get_price_percent(new_trailing_price + decimal.Decimal("0.0001"),
-                                                           trailing_stop_order.trailing_percent,
-                                                           selling_side=False),
-                             timestamp=trailing_stop_order.timestamp)])
+        [decimal_random_recent_trade(price=get_price_percent(new_trailing_price + decimal.Decimal("0.0001"),
+                                                             trailing_stop_order.trailing_percent,
+                                                             selling_side=False),
+                                     timestamp=trailing_stop_order.timestamp)])
     await wait_asyncio_next_cycle()
     assert trailing_stop_order.is_filled()
 
 
-async def initialize_trailing_stop(order, side=TradeOrderSide.SELL) -> Tuple[TrailingStopOrder, float, PriceEventsManager]:
+async def initialize_trailing_stop(order, side=TradeOrderSide.SELL) -> Tuple[
+    TrailingStopOrder, decimal.Decimal, PriceEventsManager]:
     order_price = decimal_random_price()
     order_max_quantity = DEFAULT_SYMBOL_QUANTITY \
         if side is TradeOrderSide.SELL else DEFAULT_MARKET_QUANTITY / order_price
     order.update(
         price=order_price,
         # divide quantity by 2 to prevent trailing price movement to impact usable quantity
-        quantity=decimal_random_quantity(max_value=order_max_quantity / 2),
+        quantity=decimal_random_quantity(max_value=order_max_quantity / decimal.Decimal(2)),
         symbol=DEFAULT_ORDER_SYMBOL,
         order_type=TradeOrderType.TRAILING_STOP,
     )
@@ -167,5 +175,5 @@ def get_price_percent(price, percent, selling_side=True):
 def set_mark_price(order, mark_price):
     prices_manager = order.exchange_manager.exchange_symbols_data. \
         get_exchange_symbol_data(order.symbol).prices_manager
-    prices_manager.set_mark_price(float(mark_price), MarkPriceSources.EXCHANGE_MARK_PRICE.value)
+    prices_manager.set_mark_price(decimal.Decimal(mark_price), MarkPriceSources.EXCHANGE_MARK_PRICE.value)
     prices_manager.mark_price_set_time = order.timestamp
diff --git a/tests/personal_data/portfolios/test_asset.py b/tests/personal_data/portfolios/test_asset.py
index 27b906d54..8a71cf952 100644
--- a/tests/personal_data/portfolios/test_asset.py
+++ b/tests/personal_data/portfolios/test_asset.py
@@ -43,15 +43,14 @@ def test_to_dict():
 
 def test__ensure_update_validity():
     asset = assets.Asset(ASSET_CURRENCY_NAME, available=constants.ONE_HUNDRED, total=decimal.Decimal(2))
-    if not os.getenv('CYTHON_IGNORE'):
-        with pytest.raises(errors.PortfolioNegativeValueError):
-            asset._ensure_update_validity(constants.ONE, decimal.Decimal(-101))
-        asset._ensure_update_validity(constants.ZERO, -constants.ZERO)
-        with pytest.raises(errors.PortfolioNegativeValueError):
-            asset._ensure_update_validity(constants.ZERO, -constants.ONE)
-        asset._ensure_update_validity(constants.ONE, -constants.ONE)
-        with pytest.raises(errors.PortfolioNegativeValueError):
-            asset._ensure_update_validity(-constants.ONE, constants.ZERO)
+    with pytest.raises(errors.PortfolioNegativeValueError):
+        asset._ensure_update_validity(constants.ONE, decimal.Decimal(-101))
+    asset._ensure_update_validity(constants.ZERO, -constants.ZERO)
+    with pytest.raises(errors.PortfolioNegativeValueError):
+        asset._ensure_update_validity(constants.ZERO, -constants.ONE)
+    asset._ensure_update_validity(constants.ONE, -constants.ONE)
+    with pytest.raises(errors.PortfolioNegativeValueError):
+        asset._ensure_update_validity(-constants.ONE, constants.ZERO)
 
 
 def test__ensure_not_negative():
diff --git a/tests/personal_data/portfolios/test_portfolio.py b/tests/personal_data/portfolios/test_portfolio.py
index 07765a7e5..874c32ad8 100644
--- a/tests/personal_data/portfolios/test_portfolio.py
+++ b/tests/personal_data/portfolios/test_portfolio.py
@@ -13,6 +13,7 @@
 #
 #  You should have received a copy of the GNU Lesser General Public
 #  License along with this library.
+import copy
 import os
 import decimal
 
@@ -45,6 +46,21 @@ async def test_load_portfolio(backtesting_trader):
     assert portfolio_manager.portfolio.portfolio['USDT'].total == decimal.Decimal('1000')
 
 
+async def test_copy_portfolio(backtesting_trader):
+    config, exchange_manager, trader = backtesting_trader
+    portfolio_manager = exchange_manager.exchange_personal_data.portfolio_manager
+    pf1 = portfolio_manager.portfolio
+    pf2 = copy.copy(pf1)
+    pf1.portfolio['BTC'].available = decimal.Decimal('5')
+    pf1.portfolio['BTC'].total = decimal.Decimal('9')
+    pf1.portfolio['USDT'].available = decimal.Decimal('10.5')
+    pf1.portfolio['USDT'].total = decimal.Decimal('1.3')
+    assert pf1.portfolio['BTC'].available != pf2.portfolio['BTC'].available
+    assert pf1.portfolio['BTC'].total != pf2.portfolio['BTC'].total
+    assert pf1.portfolio['USDT'].available != pf2.portfolio['USDT'].available
+    assert pf1.portfolio['USDT'].total != pf2.portfolio['USDT'].total
+    assert pf1 != pf2
+
 
 async def test_get_portfolio_from_amount_dict(backtesting_trader):
     config, exchange_manager, trader = backtesting_trader
diff --git a/tests/personal_data/portfolios/test_portfolio_profitability.py b/tests/personal_data/portfolios/test_portfolio_profitability.py
index f507a03e1..d21dbadb3 100644
--- a/tests/personal_data/portfolios/test_portfolio_profitability.py
+++ b/tests/personal_data/portfolios/test_portfolio_profitability.py
@@ -45,7 +45,7 @@ async def test_simple_handle_balance_update(backtesting_trader):
     portfolio_value_holder = portfolio_manager.portfolio_value_holder
 
     # set the original values
-    await portfolio_manager.handle_balance_updated()
+    portfolio_manager.handle_balance_updated()
     assert portfolio_profitability.profitability == 0
     assert portfolio_profitability.profitability_percent == 0
     assert portfolio_profitability.profitability_diff == 0
@@ -56,7 +56,7 @@ async def test_simple_handle_balance_update(backtesting_trader):
         'BTC': {'available': 20, 'total': 20},
         'USDT': {'available': 1000, 'total': 1000}
     }, exchange_manager)
-    await portfolio_manager.handle_balance_updated()
+    portfolio_manager.handle_balance_updated()
     assert portfolio_profitability.profitability == 10
     assert portfolio_profitability.profitability_percent == 100
     assert portfolio_profitability.profitability_diff == 100
@@ -72,7 +72,7 @@ async def test_random_quantity_handle_balance_update(backtesting_trader):
     original_symbol_quantity = decimal.Decimal(str(10))
 
     # set the original values
-    await portfolio_manager.handle_balance_updated()
+    portfolio_manager.handle_balance_updated()
     assert portfolio_profitability.profitability == 0
     assert portfolio_profitability.profitability_percent == 0
     assert portfolio_profitability.profitability_diff == 0
@@ -84,7 +84,7 @@ async def test_random_quantity_handle_balance_update(backtesting_trader):
     new_prof_percent = decimal.Decimal(str((100 * new_btc_total / original_symbol_quantity) - 100))
     portfolio_manager.portfolio.update_portfolio_from_balance({'BTC': {'available': decimal.Decimal(str(new_btc_available)), 'total': new_btc_total}},
                              exchange_manager)
-    await portfolio_manager.handle_balance_updated()
+    portfolio_manager.handle_balance_updated()
     assert portfolio_profitability.profitability == new_btc_total - original_symbol_quantity
     assert portfolio_profitability.profitability_percent == new_prof_percent
     assert portfolio_profitability.profitability_diff == new_prof_percent - 0
@@ -94,7 +94,7 @@ async def test_random_quantity_handle_balance_update(backtesting_trader):
     new_btc_total_2 = decimal_random_quantity(min_value=new_btc_available, max_value=12)
     new_prof_percent_2 = decimal.Decimal(str((100 * new_btc_total_2 / original_symbol_quantity) - 100))
     portfolio_manager.portfolio.update_portfolio_from_balance({'BTC': {'total': new_btc_total_2}}, exchange_manager)
-    await portfolio_manager.handle_balance_updated()
+    portfolio_manager.handle_balance_updated()
     assert portfolio_profitability.profitability == new_btc_total_2 - original_symbol_quantity
     assert portfolio_profitability.profitability_percent == new_prof_percent_2
     assert portfolio_profitability.profitability_diff == new_prof_percent_2 - new_prof_percent
diff --git a/tests/personal_data/portfolios/test_portfolio_value_holder.py b/tests/personal_data/portfolios/test_portfolio_value_holder.py
index 557b7a7d1..82c0250cf 100644
--- a/tests/personal_data/portfolios/test_portfolio_value_holder.py
+++ b/tests/personal_data/portfolios/test_portfolio_value_holder.py
@@ -18,7 +18,7 @@
 import pytest
 
 import octobot_trading.constants as constants
-from tests.test_utils.random_numbers import decimal_random_quantity, random_price
+from tests.test_utils.random_numbers import decimal_random_quantity, decimal_random_price, random_price
 
 from tests.exchanges import backtesting_trader, backtesting_config, backtesting_exchange_manager, fake_backtesting
 from tests import event_loop
@@ -41,7 +41,7 @@ async def test_get_current_crypto_currencies_values(backtesting_trader):
         'XLM': {'available': decimal_random_quantity(), 'total': decimal_random_quantity()},
         'USDT': {'available': decimal_random_quantity(), 'total': decimal_random_quantity()}
     }, exchange_manager)
-    await portfolio_manager.handle_balance_updated()
+    portfolio_manager.handle_balance_updated()
 
     assert portfolio_value_holder.get_current_crypto_currencies_values() == {
         'BTC': constants.ONE,
@@ -56,10 +56,10 @@ async def test_get_current_crypto_currencies_values(backtesting_trader):
     exchange_manager.client_symbols.append("XRP/BTC")
     if not os.getenv('CYTHON_IGNORE'):
         portfolio_value_holder.missing_currency_data_in_exchange.remove("XRP")
-        await portfolio_manager.handle_mark_price_update("XRP/BTC", decimal.Decimal("0.005"))
+        portfolio_manager.handle_mark_price_update("XRP/BTC", decimal.Decimal("0.005"))
         exchange_manager.client_symbols.append("NANO/BTC")
         portfolio_value_holder.missing_currency_data_in_exchange.remove("NANO")
-        await portfolio_manager.handle_mark_price_update("NANO/BTC", decimal.Decimal("0.05"))
+        portfolio_manager.handle_mark_price_update("NANO/BTC", decimal.Decimal("0.05"))
         exchange_manager.client_symbols.append("BTC/USDT")
 
         assert portfolio_value_holder.get_current_crypto_currencies_values() == {
@@ -70,39 +70,39 @@ async def test_get_current_crypto_currencies_values(backtesting_trader):
             'XLM': constants.ZERO,
             'USDT': constants.ZERO
         }
-        xlm_btc_price = random_price(max_value=0.05)
+        xlm_btc_price = decimal_random_price(max_value=decimal.Decimal(0.05))
         portfolio_value_holder.missing_currency_data_in_exchange.remove("XLM")
-        await portfolio_manager.handle_mark_price_update("XLM/BTC", xlm_btc_price)
+        portfolio_manager.handle_mark_price_update("XLM/BTC", xlm_btc_price)
         assert portfolio_value_holder.get_current_crypto_currencies_values() == {
             'BTC': constants.ONE,
             'ETH': constants.ZERO,
             'XRP': decimal.Decimal("0.005"),
             'NANO': decimal.Decimal("0.05"),
-            'XLM': decimal.Decimal(str(xlm_btc_price)),
+            'XLM': xlm_btc_price,
             'USDT': constants.ZERO
         }
-        usdt_btc_price = random_price(max_value=0.01)
+        usdt_btc_price = decimal_random_price(max_value=decimal.Decimal('0.01'))
         portfolio_value_holder.missing_currency_data_in_exchange.remove("USDT")
-        await portfolio_manager.handle_mark_price_update("BTC/USDT", usdt_btc_price)
+        portfolio_manager.handle_mark_price_update("BTC/USDT", usdt_btc_price)
         assert portfolio_value_holder.get_current_crypto_currencies_values() == {
             'BTC': constants.ONE,
             'ETH': constants.ZERO,
             'XRP': decimal.Decimal("0.005"),
             'NANO': decimal.Decimal("0.05"),
-            'XLM': decimal.Decimal(str(xlm_btc_price)),
-            'USDT': constants.ONE / decimal.Decimal(str(usdt_btc_price))
+            'XLM': xlm_btc_price,
+            'USDT': constants.ONE / usdt_btc_price
         }
-        eth_btc_price = random_price(max_value=1)
+        eth_btc_price = decimal_random_price(max_value=constants.ONE)
         exchange_manager.client_symbols.append("ETH/BTC")
         portfolio_value_holder.missing_currency_data_in_exchange.remove("ETH")
-        await portfolio_manager.handle_mark_price_update("ETH/BTC", eth_btc_price)
+        portfolio_manager.handle_mark_price_update("ETH/BTC", eth_btc_price)
         assert portfolio_value_holder.get_current_crypto_currencies_values() == {
             'BTC': constants.ONE,
             'ETH': decimal.Decimal(str(eth_btc_price)),
             'XRP': decimal.Decimal("0.005"),
             'NANO': decimal.Decimal("0.05"),
-            'XLM': decimal.Decimal(str(xlm_btc_price)),
-            'USDT': constants.ONE / decimal.Decimal(str(usdt_btc_price))
+            'XLM': xlm_btc_price,
+            'USDT': constants.ONE / usdt_btc_price
         }
 
 
@@ -118,28 +118,28 @@ async def test_get_current_holdings_values(backtesting_trader):
         'XRP': {'available': decimal.Decimal("10000"), 'total': decimal.Decimal("10000")},
         'USDT': {'available': decimal.Decimal("1000"), 'total': decimal.Decimal("1000")}
     }, exchange_manager)
-    await portfolio_manager.handle_balance_updated()
+    portfolio_manager.handle_balance_updated()
     assert portfolio_value_holder.get_current_holdings_values() == {
         'BTC': decimal.Decimal("10"),
         'ETH': constants.ZERO,
         'XRP': constants.ZERO,
         'USDT': constants.ZERO
     }
-    await portfolio_manager.handle_mark_price_update("ETH/BTC", 50)
+    portfolio_manager.handle_mark_price_update("ETH/BTC", 50)
     assert portfolio_value_holder.get_current_holdings_values() == {
         'BTC': decimal.Decimal("10"),
         'ETH': decimal.Decimal("5000"),
         'XRP': constants.ZERO,
         'USDT': constants.ZERO
     }
-    await portfolio_manager.handle_mark_price_update("XRP/USDT", 2.5)
+    portfolio_manager.handle_mark_price_update("XRP/USDT", 2.5)
     assert portfolio_value_holder.get_current_holdings_values() == {
         'BTC': decimal.Decimal("10"),
         'ETH': decimal.Decimal("5000"),
         'XRP': constants.ZERO,
         'USDT': constants.ZERO
     }
-    await portfolio_manager.handle_mark_price_update("XRP/BTC", 0.00001)
+    portfolio_manager.handle_mark_price_update("XRP/BTC", decimal.Decimal('0.00001'))
     assert portfolio_value_holder.get_current_holdings_values() == {
         'BTC': decimal.Decimal("10"),
         'ETH': decimal.Decimal("5000"),
@@ -149,7 +149,7 @@ async def test_get_current_holdings_values(backtesting_trader):
     if not os.getenv('CYTHON_IGNORE'):
         exchange_manager.client_symbols.append("XRP/BTC")
         portfolio_value_holder.missing_currency_data_in_exchange.remove("XRP")
-        await portfolio_manager.handle_mark_price_update("XRP/BTC", 0.00001)
+        portfolio_manager.handle_mark_price_update("XRP/BTC", decimal.Decimal('0.00001'))
         assert portfolio_value_holder.get_current_holdings_values() == {
             'BTC': decimal.Decimal(10),
             'ETH': decimal.Decimal(5000),
@@ -158,7 +158,7 @@ async def test_get_current_holdings_values(backtesting_trader):
         }
         exchange_manager.client_symbols.append("BTC/USDT")
         portfolio_value_holder.missing_currency_data_in_exchange.remove("USDT")
-        await portfolio_manager.handle_mark_price_update("BTC/USDT", 5000)
+        portfolio_manager.handle_mark_price_update("BTC/USDT", 5000)
         assert portfolio_value_holder.get_current_holdings_values() == {
             'BTC': decimal.Decimal(10),
             'ETH': decimal.Decimal(5000),
@@ -172,7 +172,7 @@ async def test_get_origin_portfolio_current_value(backtesting_trader):
     portfolio_manager = exchange_manager.exchange_personal_data.portfolio_manager
     portfolio_value_holder = portfolio_manager.portfolio_value_holder
 
-    await portfolio_manager.handle_profitability_recalculation(True)
+    portfolio_manager.handle_profitability_recalculation(True)
     assert portfolio_value_holder.get_origin_portfolio_current_value() == decimal.Decimal(str(10))
 
 
@@ -182,7 +182,7 @@ async def test_get_origin_portfolio_current_value_with_different_reference_marke
     portfolio_value_holder = portfolio_manager.portfolio_value_holder
 
     portfolio_manager.reference_market = "USDT"
-    await portfolio_manager.handle_profitability_recalculation(True)
+    portfolio_manager.handle_profitability_recalculation(True)
     assert portfolio_value_holder.get_origin_portfolio_current_value() == decimal.Decimal(str(1000))
 
 
diff --git a/tests/personal_data/portfolios/types/test_future_portfolio.py b/tests/personal_data/portfolios/types/test_future_portfolio.py
index a97283262..67089a035 100644
--- a/tests/personal_data/portfolios/types/test_future_portfolio.py
+++ b/tests/personal_data/portfolios/types/test_future_portfolio.py
@@ -39,7 +39,7 @@
 async def _update_position_mark_price(exchange_manager_inst, mark_price, symbol=DEFAULT_FUTURE_SYMBOL, side=None):
     symbol_position = exchange_manager_inst.exchange_personal_data.positions_manager. \
         get_symbol_position(symbol=symbol, side=side)
-    symbol_position.update(mark_price=mark_price)
+    await symbol_position.update(mark_price=mark_price)
 
 
 async def test_initial_portfolio(future_trader_simulator_with_default_linear):
@@ -64,9 +64,6 @@ async def test_update_future_portfolio_from_order(
                       quantity=decimal.Decimal(str(2)),
                       price=buy_price)
 
-    # update position mark price
-    await _update_position_mark_price(exchange_manager_inst, buy_price, DEFAULT_FUTURE_SYMBOL)
-
     # test buy order creation
     portfolio_manager.portfolio.update_portfolio_available(market_buy, True)
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(900))
@@ -77,10 +74,15 @@ async def test_update_future_portfolio_from_order(
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").position_margin == constants.ZERO
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").order_margin == decimal.Decimal(str(100))
 
+    # create a new position from order
     await fill_market_order(market_buy)
+
+    # update position mark price
+    await _update_position_mark_price(exchange_manager_inst, buy_price, DEFAULT_FUTURE_SYMBOL)
+
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(900))
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal(str(900))
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").unrealized_pnl == -constants.ONE_HUNDRED
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal(str(1000))
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").unrealized_pnl == 0
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").initial_margin == constants.ZERO
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").wallet_balance == decimal.Decimal(str(1000))
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").position_margin == decimal.Decimal(str(100))
@@ -193,47 +195,80 @@ async def test_update_portfolio_data_from_order_with_market_buy_long_linear_cont
     market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
                       symbol=DEFAULT_FUTURE_SYMBOL,
                       current_price=buy_price,
-                      quantity=decimal.Decimal(str(3)),  # real quantity = 3 / 5 = 0.6
-                      price=buy_price)
-
-    # update position mark price
-    await _update_position_mark_price(exchange_manager_inst, buy_price, DEFAULT_FUTURE_SYMBOL)
+                      quantity=decimal.Decimal(3),  # real quantity = 3 / 5 = 0.6
+                      price=buy_price)  # real cost = 0.6 * 1000 = 600
 
     # test buy order creation
     portfolio_manager.portfolio.update_portfolio_available(market_buy, True)
+
+    # create position from order
     await fill_market_order(market_buy)
+
+    # update position mark price
+    await _update_position_mark_price(exchange_manager_inst, buy_price, DEFAULT_FUTURE_SYMBOL)
+
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(400.0))
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal(str(400.0))
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal(str(1000.0))
 
     # Test buy order
     market_buy = BuyMarketOrder(trader_inst)
     market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
                       symbol=DEFAULT_FUTURE_SYMBOL,
-                      current_price=decimal.Decimal(str(7.5)),
-                      quantity=decimal.Decimal(str(30)),
-                      price=decimal.Decimal(str(7.5)))
+                      current_price=buy_price,
+                      quantity=decimal.Decimal(1),  # 0.2
+                      price=buy_price)  # 200
 
     # test buy order creation
     portfolio_manager.portfolio.update_portfolio_available(market_buy, True)
     await fill_market_order(market_buy)
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(355.0))
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal(str(355.0))
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(200.0))
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal(str(1000.0))
 
     # Test reducing LONG position with a sell market order
     market_sell = SellMarketOrder(trader_inst)
     market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
                        symbol=DEFAULT_FUTURE_SYMBOL,
-                       current_price=decimal.Decimal(str(54)),
-                       quantity=decimal.Decimal(str(20)),
-                       price=decimal.Decimal(str(54)))
+                       current_price=buy_price,
+                       quantity=decimal.Decimal(0.5),  # 0.1
+                       price=buy_price)  # 100
+
+    # Should restore available
+    portfolio_manager.portfolio.update_portfolio_available(market_sell, True)
+    await fill_market_order(market_sell)
+    assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal(10)
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(299.8))
+    assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal(10)
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal(str(999.8))
+
+    # Test buy order 2
+    market_buy = BuyMarketOrder(trader_inst)
+    market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
+                      symbol=DEFAULT_FUTURE_SYMBOL,
+                      current_price=buy_price,
+                      quantity=decimal.Decimal(0.5),  # 0.1
+                      price=buy_price)  # 100
+
+    # increase position size
+    portfolio_manager.portfolio.update_portfolio_available(market_buy, True)
+    await fill_market_order(market_buy)
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(199.8))
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal(str(999.8))
+
+    # Test reducing again LONG position with a sell market order
+    market_sell = SellMarketOrder(trader_inst)
+    market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
+                       symbol=DEFAULT_FUTURE_SYMBOL,
+                       current_price=buy_price,
+                       quantity=decimal.Decimal(4),  # 0.8
+                       price=buy_price)  # 800
 
-    # Should restore to first test step asserts
+    # decrease position size
     portfolio_manager.portfolio.update_portfolio_available(market_sell, True)
     await fill_market_order(market_sell)
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal(10)
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(354.568))
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(998.2))
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal(10)
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal(str('570.568'))
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal(str(998.2))
 
 
 async def test_update_portfolio_data_from_order_that_triggers_negative_portfolio_linear_contract(
@@ -251,9 +286,9 @@ async def test_update_portfolio_data_from_order_that_triggers_negative_portfolio
                       price=decimal.Decimal(str(1000)))
 
     # test buy order creation
-    if not os.getenv('CYTHON_IGNORE'):
-        with pytest.raises(errors.PortfolioNegativeValueError):
-            portfolio_manager.portfolio.update_portfolio_available(market_buy, True)
+    # if not os.getenv('CYTHON_IGNORE'):
+    with pytest.raises(errors.PortfolioNegativeValueError):
+        portfolio_manager.portfolio.update_portfolio_available(market_buy, True)
 
 
 async def test_update_portfolio_data_from_order_with_cancelled_and_filled_orders_linear_contract(
@@ -287,9 +322,6 @@ async def test_update_portfolio_data_from_order_with_cancelled_and_filled_orders
                      quantity=decimal.Decimal(str(46.5)),
                      price=decimal.Decimal(str(80)))
 
-    # update position mark price
-    await _update_position_mark_price(exchange_manager_inst, sell_price, DEFAULT_FUTURE_SYMBOL)
-
     portfolio_manager.portfolio.update_portfolio_available(stop_loss, True)
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('1000')
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1000')
@@ -303,8 +335,9 @@ async def test_update_portfolio_data_from_order_with_cancelled_and_filled_orders
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1000')
 
     await fill_market_order(market_sell)
+    await _update_position_mark_price(exchange_manager_inst, sell_price, DEFAULT_FUTURE_SYMBOL)
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('714.616')
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1191.616')
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('999.616')
 
     # cancel other orders
     portfolio_manager.portfolio.update_portfolio_available(stop_loss, False)
@@ -312,14 +345,14 @@ async def test_update_portfolio_data_from_order_with_cancelled_and_filled_orders
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal('10')
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('807.616')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal('10')
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1191.616')
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('999.616')
 
 
 async def test_update_portfolio_data_from_order_with_huge_loss_on_filled_orders_linear_contract(
         future_trader_simulator_with_default_linear):
     config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
     portfolio_manager = exchange_manager_inst.exchange_personal_data.portfolio_manager
-    default_contract.set_current_leverage(decimal.Decimal(10))
+    default_contract.set_current_leverage(decimal.Decimal(2))
 
     # Test sell order
     market_sell = SellMarketOrder(trader_inst)
@@ -340,29 +373,24 @@ async def test_update_portfolio_data_from_order_with_huge_loss_on_filled_orders_
                       price=buy_order_price)
 
     portfolio_manager.portfolio.update_portfolio_available(market_sell, True)
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('975.0')
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('875.0')
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1000')
 
-    # update position mark price
-    await _update_position_mark_price(exchange_manager_inst, sell_order_price, DEFAULT_FUTURE_SYMBOL)
-
     # Open short position
     await fill_market_order(market_sell)
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('974.9')
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1024.9')
+    await _update_position_mark_price(exchange_manager_inst, sell_order_price, DEFAULT_FUTURE_SYMBOL)
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('874.9')
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('999.9')
 
     portfolio_manager.portfolio.update_portfolio_available(market_buy, True)
-    # => 975 - 250 = 725
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('961.9')
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1024.9')
-
-    # update position mark price
-    await _update_position_mark_price(exchange_manager_inst, buy_order_price, DEFAULT_FUTURE_SYMBOL)
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('874.9')
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('999.9')
 
     # Close short position with loss
     await fill_market_order(market_buy)
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('974.9')
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1011.9')
+    await _update_position_mark_price(exchange_manager_inst, buy_order_price, DEFAULT_FUTURE_SYMBOL)
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('902.4')
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('902.4')
 
 
 async def test_update_portfolio_from_liquidated_position_with_long_position_inverse_contract(
@@ -372,12 +400,15 @@ async def test_update_portfolio_from_liquidated_position_with_long_position_inve
     default_contract.set_current_leverage(decimal.Decimal(10))
 
     position_inst = LinearPosition(trader_inst, default_contract)
+    await position_inst.initialize()
     position_inst.update_from_raw(
         {
             enums.ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL
         }
     )
     await position_inst.update(update_size=decimal.Decimal(10), mark_price=decimal.Decimal(100))
+    exchange_manager_inst.exchange_personal_data.positions_manager.upsert_position_instance(position_inst)
+
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('1000')
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1000')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal('10')
@@ -398,12 +429,16 @@ async def test_update_portfolio_from_liquidated_position_with_short_position_inv
     trader_inst.exchange_manager.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, default_contract)
 
     position_inst = InversePosition(trader_inst, default_contract)
+    await position_inst.initialize()
     position_inst.update_from_raw(
         {
             enums.ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL
         }
     )
     await position_inst.update(update_size=decimal.Decimal(-10000), mark_price=decimal.Decimal(100))
+
+    exchange_manager_inst.exchange_personal_data.positions_manager.upsert_position_instance(position_inst)
+
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('1000')
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1000')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal('10')
@@ -430,12 +465,14 @@ async def test_update_portfolio_from_liquidated_position_with_orders_on_short_po
     trader_inst.exchange_manager.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, default_contract)
 
     position_inst = LinearPosition(trader_inst, default_contract)
+    await position_inst.initialize()
     position_inst.update_from_raw(
         {
             enums.ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL
         }
     )
     await position_inst.update(update_size=decimal.Decimal(-100), mark_price=decimal.Decimal(99))
+    exchange_manager_inst.exchange_personal_data.positions_manager.upsert_position_instance(position_inst)
 
     # Test sell order
     market_sell = SellMarketOrder(trader_inst)
@@ -454,7 +491,7 @@ async def test_update_portfolio_from_liquidated_position_with_orders_on_short_po
 
     await fill_market_order(market_sell)
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('989.902')
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1009.702')
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('999.802')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal('10')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal('10')
 
@@ -467,23 +504,22 @@ async def test_update_portfolio_from_liquidated_position_with_orders_on_short_po
                       price=decimal.Decimal(90))
 
     portfolio_manager.portfolio.update_portfolio_available(market_buy, True)
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('971.902')
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1009.702')
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('989.902')
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('999.802')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal('10')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal('10')
 
-    await fill_market_order(market_buy)
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('989.902')
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('991.702')
+    await fill_market_order(market_buy)  # reducing position with positive PNL
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('999.802')
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1089.802')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal('10')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal('10')
 
     portfolio_manager.portfolio.get_currency_portfolio("BTC").wallet_balance = decimal.Decimal('10')
     portfolio_manager.portfolio.get_currency_portfolio("USDT").wallet_balance = decimal.Decimal('1000')
-    await position_inst.update(mark_price=decimal.Decimal(126))
-    portfolio_manager.portfolio.update_portfolio_from_liquidated_position(position_inst)
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('748')
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('694')
+    await position_inst.update(mark_price=position_inst.liquidation_price)
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('808.2010')
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('808.2010')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal('10')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal('10')
 
@@ -495,38 +531,44 @@ async def test_update_portfolio_from_funding_with_long_position(
     default_contract.set_current_leverage(decimal.Decimal(10))
 
     position_inst = InversePosition(trader_inst, default_contract)
+    await position_inst.initialize()
     await position_inst.update(update_size=decimal.Decimal(10), mark_price=decimal.Decimal(100))
     position_inst.update_from_raw(
         {
             enums.ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL
         }
     )
+    exchange_manager_inst.exchange_personal_data.positions_manager.upsert_position_instance(position_inst)
+
     # long position holders have to pay the short position holders
     portfolio_manager.portfolio.update_portfolio_from_funding(position=position_inst,
                                                               funding_rate=decimal.Decimal(0.0001))
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('1000')
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1000')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal(
-        '9.999998999999999999999952078')
+        '9.999989999999999999999520783')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal(
-        '9.999998999999999999999952078')
+        '9.999989999999999999999520783')
 
     position_inst = InversePosition(trader_inst, default_contract)
+    await position_inst.initialize()
     await position_inst.update(update_size=decimal.Decimal(10), mark_price=decimal.Decimal(100))
     position_inst.update_from_raw(
         {
             enums.ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL
         }
     )
+    exchange_manager_inst.exchange_personal_data.positions_manager.upsert_position_instance(position_inst)
+
     # short position holders have to pay the long position holders
     portfolio_manager.portfolio.update_portfolio_from_funding(position=position_inst,
                                                               funding_rate=decimal.Decimal(-0.0002))
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('1000')
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1000')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal(
-        '10.00000100000000000000004792')
+        '10.00001000000000000000047922')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal(
-        '10.00000100000000000000004792')
+        '10.00001000000000000000047922')
 
 
 async def test_update_portfolio_from_funding_with_short_position(
@@ -536,38 +578,44 @@ async def test_update_portfolio_from_funding_with_short_position(
     default_contract.set_current_leverage(decimal.Decimal(10))
 
     position_inst = InversePosition(trader_inst, default_contract)
+    await position_inst.initialize()
     await position_inst.update(update_size=decimal.Decimal(-10), mark_price=decimal.Decimal(100))
     position_inst.update_from_raw(
         {
             enums.ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL
         }
     )
+    exchange_manager_inst.exchange_personal_data.positions_manager.upsert_position_instance(position_inst)
+
     # long position holders have to pay the short position holders
     portfolio_manager.portfolio.update_portfolio_from_funding(position=position_inst,
                                                               funding_rate=decimal.Decimal(0.0003))
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('1000')
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1000')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal(
-        '10.00000299999999999999973719')
+        '10.00002999999999999999737189')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal(
-        '10.00000299999999999999973719')
+        '10.00002999999999999999737189')
 
     position_inst = InversePosition(trader_inst, default_contract)
+    await position_inst.initialize()
     await position_inst.update(update_size=decimal.Decimal(-10), mark_price=decimal.Decimal(100))
     position_inst.update_from_raw(
         {
             enums.ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL
         }
     )
+    exchange_manager_inst.exchange_personal_data.positions_manager.upsert_position_instance(position_inst)
+
     # short position holders have to pay the long position holders
     portfolio_manager.portfolio.update_portfolio_from_funding(position=position_inst,
                                                               funding_rate=decimal.Decimal(-0.0004))
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('1000')
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1000')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal(
-        '9.999998999999999999999545503')
+        '9.999989999999999999995455021')
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal(
-        '9.999998999999999999999545503')
+        '9.999989999999999999995455021')
 
 
 async def test_update_portfolio_data_with_fees(future_trader_simulator_with_default_linear):
@@ -584,9 +632,6 @@ async def test_update_portfolio_data_with_fees(future_trader_simulator_with_defa
                        quantity=decimal.Decimal(25),
                        price=sell_order_price)
 
-    # update position mark price
-    await _update_position_mark_price(exchange_manager_inst, sell_order_price, DEFAULT_FUTURE_SYMBOL)
-
     if not os.getenv('CYTHON_IGNORE'):
         with mock.patch.object(market_sell, "get_total_fees", mock.Mock(return_value=5)):
             portfolio_manager.portfolio.update_portfolio_available(market_sell, True)
@@ -595,8 +640,9 @@ async def test_update_portfolio_data_with_fees(future_trader_simulator_with_defa
 
             # fill order with fees
             await fill_market_order(market_sell)
+            await _update_position_mark_price(exchange_manager_inst, sell_order_price, DEFAULT_FUTURE_SYMBOL)
             assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal('970.0')
-            assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('1020')
+            assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal('995')
 
 
 async def test_update_portfolio_reduce_size_with_market_sell_long_linear_contract(
@@ -606,12 +652,14 @@ async def test_update_portfolio_reduce_size_with_market_sell_long_linear_contrac
     default_contract.set_current_leverage(decimal.Decimal(10))
 
     position_inst = LinearPosition(trader_inst, default_contract)
+    await position_inst.initialize()
     await position_inst.update(update_size=decimal.Decimal(10), mark_price=decimal.Decimal(101))
     position_inst.update_from_raw(
         {
             enums.ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL
         }
     )
+    exchange_manager_inst.exchange_personal_data.positions_manager.upsert_position_instance(position_inst)
 
     # Test sell order
     market_sell = SellMarketOrder(trader_inst)
@@ -623,15 +671,15 @@ async def test_update_portfolio_reduce_size_with_market_sell_long_linear_contrac
 
     # test sell order creation
     portfolio_manager.portfolio.update_portfolio_available(market_sell, True)
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(747.5))
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(1000))
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal(str(1000))
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal(str(10))
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal(str(10))
 
     # fill order
     await fill_market_order(market_sell)
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal("1251.49")
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str("746.49"))
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal("998.99")
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str("998.99"))
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal(str(10))
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal(str(10))
 
@@ -643,12 +691,14 @@ async def test_update_portfolio_reduce_size_with_market_buy_short_linear_contrac
     default_contract.set_current_leverage(decimal.Decimal(10))
 
     position_inst = LinearPosition(trader_inst, default_contract)
-    position_inst.update(update_size=decimal.Decimal(-70), mark_price=decimal.Decimal(9))
+    await position_inst.initialize()
+    await position_inst.update(update_size=decimal.Decimal(-70), mark_price=decimal.Decimal(9))
     position_inst.update_from_raw(
         {
             enums.ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL
         }
     )
+    exchange_manager_inst.exchange_personal_data.positions_manager.upsert_position_instance(position_inst)
 
     # Test buy order
     market_buy = BuyMarketOrder(trader_inst)
@@ -660,15 +710,15 @@ async def test_update_portfolio_reduce_size_with_market_buy_short_linear_contrac
 
     # test buy order creation
     portfolio_manager.portfolio.update_portfolio_available(market_buy, True)
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(991))
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(1000))
     assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal(str(1000))
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal(str(10))
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal(str(10))
 
     # fill order
     await fill_market_order(market_buy)
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal(991)
-    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(991))
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").total == decimal.Decimal(1000)
+    assert portfolio_manager.portfolio.get_currency_portfolio("USDT").available == decimal.Decimal(str(1000))
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").available == decimal.Decimal(str(10))
     assert portfolio_manager.portfolio.get_currency_portfolio("BTC").total == decimal.Decimal(str(10))
 
@@ -679,12 +729,14 @@ async def test_update_portfolio_from_pnl_with_long_inverse_contract(future_trade
     default_contract.set_current_leverage(decimal.Decimal(10))
 
     position_inst = InversePosition(trader_inst, default_contract)
-    position_inst.update(update_size=decimal.Decimal(-70), mark_price=decimal.Decimal(10))
+    await position_inst.initialize()
+    await position_inst.update(update_size=decimal.Decimal(-70), mark_price=decimal.Decimal(10))
     position_inst.update_from_raw(
         {
             enums.ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL
         }
     )
+    exchange_manager_inst.exchange_personal_data.positions_manager.upsert_position_instance(position_inst)
 
     position_inst.unrealised_pnl = decimal.Decimal(0.1)
     portfolio_manager.portfolio.update_portfolio_from_pnl(position_inst)
diff --git a/tests/personal_data/positions/test_position.py b/tests/personal_data/positions/test_position.py
index ee9251019..e0efb46b7 100644
--- a/tests/personal_data/positions/test_position.py
+++ b/tests/personal_data/positions/test_position.py
@@ -22,7 +22,7 @@
 import octobot_trading.constants as constants
 import octobot_trading.enums as enums
 import octobot_trading.personal_data as personal_data
-from octobot_trading.personal_data import SellLimitOrder, BuyLimitOrder
+from octobot_trading.personal_data import SellLimitOrder, BuyLimitOrder, BuyMarketOrder, SellMarketOrder
 
 from tests import event_loop
 from tests.exchanges import future_simulated_exchange_manager
@@ -41,12 +41,53 @@ async def test_update_entry_price(future_trader_simulator_with_default_linear):
     assert position_inst.entry_price == constants.ZERO
     assert position_inst.mark_price == constants.ZERO
 
-    mark_price = decimal_random_price(1)
+    mark_price = decimal_random_price(constants.ONE)
     await position_inst.update(mark_price=mark_price, update_size=constants.ONE)
     assert position_inst.entry_price == mark_price
     assert position_inst.mark_price == mark_price
 
 
+async def test_update_entry_price_when_switching_side_on_one_way(future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+
+    symbol_contract = default_contract
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    await position_inst.initialize()
+    position_inst.update_from_raw(
+        {
+            enums.ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL
+        }
+    )
+
+    mark_price = decimal.Decimal(80)
+    await position_inst.update(mark_price=mark_price, update_size=decimal.Decimal(0.01))
+    assert position_inst.entry_price == mark_price
+    assert position_inst.mark_price == mark_price
+
+    # updating mark price
+    await position_inst.update(mark_price=decimal.Decimal(90))
+    assert position_inst.is_long()
+
+    # switching long to short with new mark price
+    mark_price = decimal.Decimal(100)
+    await position_inst.update(mark_price=mark_price, update_size=-decimal.Decimal(0.02))
+    assert position_inst.is_short()
+    assert position_inst.mark_price == mark_price
+    assert position_inst.entry_price == mark_price
+
+    # updating mark price
+    await position_inst.update(mark_price=decimal.Decimal(60))
+    assert position_inst.is_short()
+
+    # switching short to long with new mark price
+    mark_price = decimal.Decimal(50)
+    await position_inst.update(mark_price=mark_price, update_size=decimal.Decimal(0.03))
+    assert position_inst.is_long()
+    assert position_inst.mark_price == mark_price
+    assert position_inst.entry_price == mark_price
+
+
 async def test_update_update_quantity(future_trader_simulator_with_default_linear):
     config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
 
@@ -143,6 +184,44 @@ async def test__is_update_increasing_size(future_trader_simulator_with_default_l
         assert not position_inst._is_update_increasing_size(decimal.Decimal(5))
 
 
+async def test__is_update_closing(future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=False)
+
+    if not os.getenv('CYTHON_IGNORE'):
+        # Idle
+        position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+        assert position_inst._is_update_closing(decimal.Decimal(-5))
+        assert position_inst._is_update_closing(decimal.Decimal(5))
+
+        # LONG
+        position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+        await position_inst.update(update_size=decimal.Decimal(100))
+        assert not position_inst._is_update_closing(decimal.Decimal(-5))
+        assert not position_inst._is_update_closing(decimal.Decimal(5))
+        assert not position_inst._is_update_closing(decimal.Decimal(100))
+        assert not position_inst._is_update_closing(decimal.Decimal(500))
+        assert not position_inst._is_update_closing(decimal.Decimal(99))
+        assert position_inst._is_update_closing(decimal.Decimal(-100))
+        assert not position_inst._is_update_closing(decimal.Decimal(-99))
+        assert position_inst._is_update_closing(decimal.Decimal(-500))
+
+        # SHORT
+        position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+        await position_inst.update(update_size=decimal.Decimal(-100))
+        assert not position_inst._is_update_closing(decimal.Decimal(-5))
+        assert not position_inst._is_update_closing(decimal.Decimal(5))
+        assert position_inst._is_update_closing(decimal.Decimal(100))
+        assert position_inst._is_update_closing(decimal.Decimal(500))
+        assert not position_inst._is_update_closing(decimal.Decimal(99))
+        assert not position_inst._is_update_closing(decimal.Decimal(-100))
+        assert not position_inst._is_update_closing(decimal.Decimal(-99))
+        assert not position_inst._is_update_closing(decimal.Decimal(-500))
+
+
 async def test_get_quantity_to_close(future_trader_simulator_with_default_linear):
     config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
 
@@ -174,7 +253,8 @@ async def test_update_size_from_order_with_long_one_way_position(future_trader_s
     assert position_inst.update_from_order(limit_sell) == (decimal.Decimal(-2), False)
 
 
-async def test_update_size_from_order_with_long_close_position_one_way_position(future_trader_simulator_with_default_linear):
+async def test_update_size_from_order_with_long_close_position_one_way_position(
+        future_trader_simulator_with_default_linear):
     config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
     symbol_contract = default_contract
     symbol_contract.set_position_mode(is_one_way=True)
@@ -191,7 +271,8 @@ async def test_update_size_from_order_with_long_close_position_one_way_position(
     assert position_inst.update_from_order(limit_sell) == (-constants.ONE_HUNDRED, False)
 
 
-async def test_update_size_from_order_with_long_reduce_only_one_way_position(future_trader_simulator_with_default_linear):
+async def test_update_size_from_order_with_long_reduce_only_one_way_position(
+        future_trader_simulator_with_default_linear):
     config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
     symbol_contract = default_contract
     symbol_contract.set_position_mode(is_one_way=True)
@@ -255,7 +336,8 @@ async def test_update_size_from_order_with_short_one_way_position(future_trader_
     assert position_inst.update_from_order(buy_limit) == (decimal.Decimal(2), False)
 
 
-async def test_update_size_from_order_with_short_close_position_one_way_position(future_trader_simulator_with_default_linear):
+async def test_update_size_from_order_with_short_close_position_one_way_position(
+        future_trader_simulator_with_default_linear):
     config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
     symbol_contract = default_contract
     symbol_contract.set_position_mode(is_one_way=True)
@@ -272,7 +354,8 @@ async def test_update_size_from_order_with_short_close_position_one_way_position
     assert position_inst.update_from_order(buy_limit) == (constants.ONE_HUNDRED, False)
 
 
-async def test_update_size_from_order_with_short_reduce_only_one_way_position(future_trader_simulator_with_default_linear):
+async def test_update_size_from_order_with_short_reduce_only_one_way_position(
+        future_trader_simulator_with_default_linear):
     config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
     symbol_contract = default_contract
     symbol_contract.set_position_mode(is_one_way=True)
@@ -307,14 +390,15 @@ async def test_update_size_from_order_realized_pnl_position(future_trader_simula
     symbol_contract = default_contract
     symbol_contract.set_position_mode(is_one_way=True)
 
+    mark_price = decimal.Decimal(15)
     position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
     position_inst.entry_price = decimal.Decimal(10)
-    await position_inst.update(update_size=-constants.ONE_HUNDRED)
+    await position_inst.update(mark_price=mark_price, update_size=-constants.ONE_HUNDRED)
 
     buy_limit = BuyLimitOrder(trader_inst)
     buy_limit.update(order_type=enums.TraderOrderType.BUY_LIMIT,
                      symbol=DEFAULT_FUTURE_SYMBOL,
-                     current_price=decimal.Decimal(15),
+                     current_price=mark_price,
                      quantity=constants.ONE,
                      price=decimal.Decimal(21))
 
@@ -322,11 +406,12 @@ async def test_update_size_from_order_realized_pnl_position(future_trader_simula
         with mock.patch.object(buy_limit, "get_total_fees", mock.Mock(return_value=5)):
             assert position_inst.update_from_order(buy_limit) == (constants.ONE, False)
         assert position_inst.size == decimal.Decimal("-99")
-        assert position_inst.entry_price == decimal.Decimal("10.10101010101010101010101010")
+        assert position_inst.entry_price == decimal.Decimal("15.15151515151515151515151515")
         assert position_inst.realised_pnl == decimal.Decimal("-5")
 
 
-async def test_update_size_from_order_with_short_overbought_one_way_position(future_trader_simulator_with_default_linear):
+async def test_update_size_from_order_with_short_overbought_one_way_position(
+        future_trader_simulator_with_default_linear):
     config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
     symbol_contract = default_contract
     symbol_contract.set_position_mode(is_one_way=True)
@@ -381,3 +466,692 @@ async def test_update_size_from_order_with_short_overbought_hedge_position(futur
     assert position_inst.size == constants.ZERO
     assert position_inst.is_idle()
 
+
+async def test__calculates_size_update_from_filled_order_long_increasing_one_way(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=decimal.Decimal(10))
+    market_buy = BuyMarketOrder(trader_inst)
+    market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
+                      symbol=DEFAULT_FUTURE_SYMBOL,
+                      current_price=decimal.Decimal(10),
+                      quantity=decimal.Decimal(5),
+                      price=decimal.Decimal(10))
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_buy, position_inst.get_quantity_to_close()) == decimal.Decimal(5)
+
+
+async def test__calculates_size_update_from_filled_order_long_increasing_one_way_reduce_only(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=decimal.Decimal(10))
+    market_buy = BuyMarketOrder(trader_inst)
+    market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
+                      symbol=DEFAULT_FUTURE_SYMBOL,
+                      current_price=decimal.Decimal(10),
+                      quantity=decimal.Decimal(5),
+                      price=decimal.Decimal(10))
+    market_buy.reduce_only = True
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_buy, position_inst.get_quantity_to_close()) == constants.ZERO
+
+
+async def test__calculates_size_update_from_filled_order_long_decreasing_one_way(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=decimal.Decimal(10))
+    market_sell = SellMarketOrder(trader_inst)
+    market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
+                       symbol=DEFAULT_FUTURE_SYMBOL,
+                       current_price=decimal.Decimal(10),
+                       quantity=decimal.Decimal(5),
+                       price=decimal.Decimal(10))
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_sell, position_inst.get_quantity_to_close()) == -decimal.Decimal(5)
+
+
+async def test__calculates_size_update_from_filled_order_long_decreasing_reduce_only_one_way(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=decimal.Decimal(10))
+    market_sell = SellMarketOrder(trader_inst)
+    market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
+                       symbol=DEFAULT_FUTURE_SYMBOL,
+                       current_price=decimal.Decimal(10),
+                       quantity=decimal.Decimal(5),
+                       price=decimal.Decimal(10))
+    market_sell.reduce_only = True
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_sell, position_inst.get_quantity_to_close()) == -decimal.Decimal(5)
+
+
+async def test__calculates_size_update_from_filled_order_long_switching_one_way(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=decimal.Decimal(10))
+    market_sell = SellMarketOrder(trader_inst)
+    market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
+                       symbol=DEFAULT_FUTURE_SYMBOL,
+                       current_price=decimal.Decimal(10),
+                       quantity=decimal.Decimal(20),
+                       price=decimal.Decimal(10))
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_sell, position_inst.get_quantity_to_close()) == -decimal.Decimal(20)
+
+
+async def test__calculates_size_update_from_filled_order_long_switching_reduce_only_one_way(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=decimal.Decimal(10))
+    market_sell = SellMarketOrder(trader_inst)
+    market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
+                       symbol=DEFAULT_FUTURE_SYMBOL,
+                       current_price=decimal.Decimal(10),
+                       quantity=decimal.Decimal(20),
+                       price=decimal.Decimal(10))
+    market_sell.reduce_only = True
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_sell, position_inst.get_quantity_to_close()) == -decimal.Decimal(10)
+
+
+async def test__calculates_size_update_from_filled_order_short_increasing_one_way(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=-decimal.Decimal(10))
+    market_sell = SellMarketOrder(trader_inst)
+    market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
+                       symbol=DEFAULT_FUTURE_SYMBOL,
+                       current_price=decimal.Decimal(10),
+                       quantity=decimal.Decimal(5),
+                       price=decimal.Decimal(10))
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_sell, position_inst.get_quantity_to_close()) == -decimal.Decimal(5)
+
+
+async def test__calculates_size_update_from_filled_order_short_increasing_one_way_reduce_only(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=-decimal.Decimal(10))
+    market_sell = SellMarketOrder(trader_inst)
+    market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
+                       symbol=DEFAULT_FUTURE_SYMBOL,
+                       current_price=decimal.Decimal(10),
+                       quantity=decimal.Decimal(5),
+                       price=decimal.Decimal(10))
+    market_sell.reduce_only = True
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_sell, position_inst.get_quantity_to_close()) == constants.ZERO
+
+
+async def test__calculates_size_update_from_filled_order_short_decreasing_one_way(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=-decimal.Decimal(10))
+    market_buy = BuyMarketOrder(trader_inst)
+    market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
+                      symbol=DEFAULT_FUTURE_SYMBOL,
+                      current_price=decimal.Decimal(10),
+                      quantity=decimal.Decimal(5),
+                      price=decimal.Decimal(10))
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_buy, position_inst.get_quantity_to_close()) == decimal.Decimal(5)
+
+
+async def test__calculates_size_update_from_filled_order_short_decreasing_reduce_only_one_way(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=-decimal.Decimal(10))
+    market_buy = BuyMarketOrder(trader_inst)
+    market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
+                      symbol=DEFAULT_FUTURE_SYMBOL,
+                      current_price=decimal.Decimal(10),
+                      quantity=decimal.Decimal(5),
+                      price=decimal.Decimal(10))
+    market_buy.reduce_only = True
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_buy, position_inst.get_quantity_to_close()) == decimal.Decimal(5)
+
+
+async def test__calculates_size_update_from_filled_order_short_switching_one_way(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=-decimal.Decimal(10))
+    market_buy = BuyMarketOrder(trader_inst)
+    market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
+                      symbol=DEFAULT_FUTURE_SYMBOL,
+                      current_price=decimal.Decimal(10),
+                      quantity=decimal.Decimal(30),
+                      price=decimal.Decimal(10))
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_buy, position_inst.get_quantity_to_close()) == decimal.Decimal(30)
+
+
+async def test__calculates_size_update_from_filled_order_short_switching_reduce_only_one_way(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=-decimal.Decimal(10))
+    market_buy = BuyMarketOrder(trader_inst)
+    market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
+                      symbol=DEFAULT_FUTURE_SYMBOL,
+                      current_price=decimal.Decimal(10),
+                      quantity=decimal.Decimal(30),
+                      price=decimal.Decimal(10))
+    market_buy.reduce_only = True
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_buy, position_inst.get_quantity_to_close()) == decimal.Decimal(10)
+
+
+async def test__calculates_size_update_from_filled_order_long_increasing_hedge(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=decimal.Decimal(10))
+    market_buy = BuyMarketOrder(trader_inst)
+    market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
+                      symbol=DEFAULT_FUTURE_SYMBOL,
+                      current_price=decimal.Decimal(10),
+                      quantity=decimal.Decimal(5),
+                      price=decimal.Decimal(10))
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_buy, position_inst.get_quantity_to_close()) == decimal.Decimal(5)
+
+
+async def test__calculates_size_update_from_filled_order_long_increasing_one_way_reduce_only(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=decimal.Decimal(10))
+    market_buy = BuyMarketOrder(trader_inst)
+    market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
+                      symbol=DEFAULT_FUTURE_SYMBOL,
+                      current_price=decimal.Decimal(10),
+                      quantity=decimal.Decimal(5),
+                      price=decimal.Decimal(10))
+    market_buy.reduce_only = True
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_buy, position_inst.get_quantity_to_close()) == constants.ZERO
+
+
+async def test__calculates_size_update_from_filled_order_long_decreasing_hedge(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=False)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=decimal.Decimal(10))
+    market_sell = SellMarketOrder(trader_inst)
+    market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
+                       symbol=DEFAULT_FUTURE_SYMBOL,
+                       current_price=decimal.Decimal(10),
+                       quantity=decimal.Decimal(5),
+                       price=decimal.Decimal(10))
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_sell, position_inst.get_quantity_to_close()) == -decimal.Decimal(5)
+
+
+async def test__calculates_size_update_from_filled_order_long_decreasing_reduce_only_hedge(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=False)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=decimal.Decimal(10))
+    market_sell = SellMarketOrder(trader_inst)
+    market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
+                       symbol=DEFAULT_FUTURE_SYMBOL,
+                       current_price=decimal.Decimal(10),
+                       quantity=decimal.Decimal(5),
+                       price=decimal.Decimal(10))
+    market_sell.reduce_only = True
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_sell, position_inst.get_quantity_to_close()) == -decimal.Decimal(5)
+
+
+async def test__calculates_size_update_from_filled_order_long_switching_hedge(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=False)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=decimal.Decimal(10))
+    market_sell = SellMarketOrder(trader_inst)
+    market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
+                       symbol=DEFAULT_FUTURE_SYMBOL,
+                       current_price=decimal.Decimal(10),
+                       quantity=decimal.Decimal(20),
+                       price=decimal.Decimal(10))
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_sell, position_inst.get_quantity_to_close()) == -decimal.Decimal(10)
+
+
+async def test__calculates_size_update_from_filled_order_long_switching_reduce_only_hedge(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=False)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=decimal.Decimal(10))
+    market_sell = SellMarketOrder(trader_inst)
+    market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
+                       symbol=DEFAULT_FUTURE_SYMBOL,
+                       current_price=decimal.Decimal(10),
+                       quantity=decimal.Decimal(20),
+                       price=decimal.Decimal(10))
+    market_sell.reduce_only = True
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_sell, position_inst.get_quantity_to_close()) == -decimal.Decimal(10)
+
+
+async def test__calculates_size_update_from_filled_order_short_increasing_hedge(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=False)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=-decimal.Decimal(10))
+    market_sell = SellMarketOrder(trader_inst)
+    market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
+                       symbol=DEFAULT_FUTURE_SYMBOL,
+                       current_price=decimal.Decimal(10),
+                       quantity=decimal.Decimal(5),
+                       price=decimal.Decimal(10))
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_sell, position_inst.get_quantity_to_close()) == -decimal.Decimal(5)
+
+
+async def test__calculates_size_update_from_filled_order_short_increasing_one_way_reduce_only(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=False)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=-decimal.Decimal(10))
+    market_sell = SellMarketOrder(trader_inst)
+    market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
+                       symbol=DEFAULT_FUTURE_SYMBOL,
+                       current_price=decimal.Decimal(10),
+                       quantity=decimal.Decimal(5),
+                       price=decimal.Decimal(10))
+    market_sell.reduce_only = True
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_sell, position_inst.get_quantity_to_close()) == constants.ZERO
+
+
+async def test__calculates_size_update_from_filled_order_short_decreasing_hedge(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=False)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=-decimal.Decimal(10))
+    market_buy = BuyMarketOrder(trader_inst)
+    market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
+                      symbol=DEFAULT_FUTURE_SYMBOL,
+                      current_price=decimal.Decimal(10),
+                      quantity=decimal.Decimal(5),
+                      price=decimal.Decimal(10))
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_buy, position_inst.get_quantity_to_close()) == decimal.Decimal(5)
+
+
+async def test__calculates_size_update_from_filled_order_short_decreasing_reduce_only_hedge(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=False)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=-decimal.Decimal(10))
+    market_buy = BuyMarketOrder(trader_inst)
+    market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
+                      symbol=DEFAULT_FUTURE_SYMBOL,
+                      current_price=decimal.Decimal(10),
+                      quantity=decimal.Decimal(5),
+                      price=decimal.Decimal(10))
+    market_buy.reduce_only = True
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_buy, position_inst.get_quantity_to_close()) == decimal.Decimal(5)
+
+
+async def test__calculates_size_update_from_filled_order_short_switching_hedge(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=False)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=-decimal.Decimal(10))
+    market_buy = BuyMarketOrder(trader_inst)
+    market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
+                      symbol=DEFAULT_FUTURE_SYMBOL,
+                      current_price=decimal.Decimal(10),
+                      quantity=decimal.Decimal(30),
+                      price=decimal.Decimal(10))
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_buy, position_inst.get_quantity_to_close()) == decimal.Decimal(10)
+
+
+async def test__calculates_size_update_from_filled_order_short_switching_reduce_only_hedge(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=False)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    await position_inst.update(update_size=-decimal.Decimal(10))
+    market_buy = BuyMarketOrder(trader_inst)
+    market_buy.update(order_type=enums.TraderOrderType.BUY_MARKET,
+                      symbol=DEFAULT_FUTURE_SYMBOL,
+                      current_price=decimal.Decimal(10),
+                      quantity=decimal.Decimal(30),
+                      price=decimal.Decimal(10))
+    market_buy.reduce_only = True
+    if os.getenv('CYTHON_IGNORE'):
+        return
+    assert position_inst._calculates_size_update_from_filled_order(
+        market_buy, position_inst.get_quantity_to_close()) == decimal.Decimal(10)
+
+
+async def test_is_order_increasing_size_one_way(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    symbol_contract.set_position_mode(is_one_way=True)
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+
+    # orders
+    buy_market = BuyMarketOrder(trader_inst)
+    reduce_buy_market = BuyMarketOrder(trader_inst)
+    reduce_buy_market.reduce_only = True
+    close_buy_market = BuyMarketOrder(trader_inst)
+    close_buy_market.close_position = True
+    sell_market = SellMarketOrder(trader_inst)
+    reduce_sell_market = SellMarketOrder(trader_inst)
+    reduce_sell_market.reduce_only = True
+    close_sell_market = SellMarketOrder(trader_inst)
+    close_sell_market.close_position = True
+
+    # when idle
+    assert position_inst.is_order_increasing_size(buy_market)
+    assert position_inst.is_order_increasing_size(sell_market)
+    assert not position_inst.is_order_increasing_size(reduce_buy_market)
+    assert not position_inst.is_order_increasing_size(close_buy_market)
+    assert not position_inst.is_order_increasing_size(reduce_sell_market)
+    assert not position_inst.is_order_increasing_size(close_sell_market)
+
+    # when long
+    await position_inst.update(update_size=decimal.Decimal(10))
+    assert position_inst.is_order_increasing_size(buy_market)
+    assert not position_inst.is_order_increasing_size(sell_market)
+    assert not position_inst.is_order_increasing_size(reduce_buy_market)
+    assert not position_inst.is_order_increasing_size(close_buy_market)
+    assert not position_inst.is_order_increasing_size(reduce_sell_market)
+    assert not position_inst.is_order_increasing_size(close_sell_market)
+
+    # when short
+    await position_inst.update(update_size=decimal.Decimal(-100))
+    assert not position_inst.is_order_increasing_size(buy_market)
+    assert position_inst.is_order_increasing_size(sell_market)
+    assert not position_inst.is_order_increasing_size(reduce_buy_market)
+    assert not position_inst.is_order_increasing_size(close_buy_market)
+    assert not position_inst.is_order_increasing_size(reduce_sell_market)
+    assert not position_inst.is_order_increasing_size(close_sell_market)
+
+
+async def test_is_order_increasing_size_hedge(
+        future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+    symbol_contract = default_contract
+    symbol_contract.set_position_mode(is_one_way=False)
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+
+    # orders
+    buy_market = BuyMarketOrder(trader_inst)
+    reduce_buy_market = BuyMarketOrder(trader_inst)
+    reduce_buy_market.reduce_only = True
+    close_buy_market = BuyMarketOrder(trader_inst)
+    close_buy_market.close_position = True
+    sell_market = SellMarketOrder(trader_inst)
+    reduce_sell_market = SellMarketOrder(trader_inst)
+    reduce_sell_market.reduce_only = True
+    close_sell_market = SellMarketOrder(trader_inst)
+    close_sell_market.close_position = True
+
+    # when idle on long side
+    position_inst.side = enums.PositionSide.LONG
+    assert position_inst.is_order_increasing_size(buy_market)
+    assert not position_inst.is_order_increasing_size(sell_market)
+    assert not position_inst.is_order_increasing_size(reduce_buy_market)
+    assert not position_inst.is_order_increasing_size(close_buy_market)
+    assert not position_inst.is_order_increasing_size(reduce_sell_market)
+    assert not position_inst.is_order_increasing_size(close_sell_market)
+
+    # when idle on short side
+    position_inst.side = enums.PositionSide.SHORT
+    assert not position_inst.is_order_increasing_size(buy_market)
+    assert position_inst.is_order_increasing_size(sell_market)
+    assert not position_inst.is_order_increasing_size(reduce_buy_market)
+    assert not position_inst.is_order_increasing_size(close_buy_market)
+    assert not position_inst.is_order_increasing_size(reduce_sell_market)
+    assert not position_inst.is_order_increasing_size(close_sell_market)
+
+    # when long
+    position_inst.side = enums.PositionSide.LONG
+    await position_inst.update(update_size=decimal.Decimal(10))
+    assert position_inst.is_order_increasing_size(buy_market)
+    assert not position_inst.is_order_increasing_size(sell_market)
+    assert not position_inst.is_order_increasing_size(reduce_buy_market)
+    assert not position_inst.is_order_increasing_size(close_buy_market)
+    assert not position_inst.is_order_increasing_size(reduce_sell_market)
+    assert not position_inst.is_order_increasing_size(close_sell_market)
+
+    # when short
+    position_inst.side = enums.PositionSide.SHORT
+    await position_inst.update(update_size=decimal.Decimal(-100))
+    assert not position_inst.is_order_increasing_size(buy_market)
+    assert position_inst.is_order_increasing_size(sell_market)
+    assert not position_inst.is_order_increasing_size(reduce_buy_market)
+    assert not position_inst.is_order_increasing_size(close_buy_market)
+    assert not position_inst.is_order_increasing_size(reduce_sell_market)
+    assert not position_inst.is_order_increasing_size(close_sell_market)
+
+
+async def test__update_realized_pnl_from_size_update_long(future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+
+    tm = exchange_manager_inst.exchange_personal_data.transactions_manager
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    position_inst.update_from_raw({enums.ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL})
+    mark_price = constants.ONE
+    position_size = decimal.Decimal(10)
+    await position_inst.update(mark_price=mark_price, update_size=position_size)
+
+    # update pnl to +50%
+    await position_inst.update(mark_price=mark_price * decimal.Decimal(1.5))
+
+    # reduce size by 20% --> 0.2
+    await position_inst.update(-position_size * decimal.Decimal(0.2))  # = 0.2
+    # = position_size / decimal.Decimal(2) * decimal.Decimal(0.8)
+    assert position_inst.unrealised_pnl == decimal.Decimal('3.999999999999999944488848768')
+    # = position_size / decimal.Decimal(2) * decimal.Decimal(0.2)
+    assert position_inst.realised_pnl == decimal.Decimal('1.000000000000000055511151232')
+
+    assert len(tm.transactions) == 1
+    assert list(tm.transactions.values())[-1].symbol == DEFAULT_FUTURE_SYMBOL
+    assert list(tm.transactions.values())[-1].realised_pnl == decimal.Decimal('1.000000000000000055511151232')
+
+    position_size = decimal.Decimal(8)
+
+    # reduce size by 50% --> 0.5
+    await position_inst.update(-position_size * decimal.Decimal(0.5))
+    # = position_size / decimal.Decimal(4) * decimal.Decimal(0.5)
+    assert position_inst.unrealised_pnl == decimal.Decimal('1.999999999999999944488848768')
+    # = position_size / decimal.Decimal(4) * decimal.Decimal(0.5) + previous realised pnl
+    assert position_inst.realised_pnl == decimal.Decimal('3.000000000000000055511151232')
+
+    assert len(tm.transactions) == 2
+    assert list(tm.transactions.values())[-1].realised_pnl == decimal.Decimal('3.000000000000000055511151232') - \
+           decimal.Decimal('1.000000000000000055511151232')
+
+    position_size = decimal.Decimal(4)
+
+    # Increase by 50% --> shouldn't increase realised pnl
+    await position_inst.update(position_size * decimal.Decimal(0.5))
+    assert position_inst.realised_pnl == decimal.Decimal('3.000000000000000055511151232')  # = previous realised pnl
+    assert len(tm.transactions) == 2
+
+
+async def test__update_realized_pnl_from_size_update_short(future_trader_simulator_with_default_linear):
+    config, exchange_manager_inst, trader_inst, default_contract = future_trader_simulator_with_default_linear
+
+    tm = exchange_manager_inst.exchange_personal_data.transactions_manager
+    symbol_contract = default_contract
+    exchange_manager_inst.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, symbol_contract)
+    symbol_contract.set_position_mode(is_one_way=True)
+    position_inst = personal_data.LinearPosition(trader_inst, symbol_contract)
+    position_inst.update_from_raw({enums.ExchangeConstantsPositionColumns.SYMBOL.value: DEFAULT_FUTURE_SYMBOL})
+    mark_price = constants.ONE
+    position_size = decimal.Decimal(100)
+    await position_inst.update(mark_price=mark_price, update_size=-position_size)
+
+    # update pnl to +50%
+    await position_inst.update(mark_price=mark_price * decimal.Decimal(0.5))
+
+    # reduce size by 30% --> 0.3
+    await position_inst.update(position_size * decimal.Decimal(0.3))  # = 0.3
+    assert position_inst.unrealised_pnl == decimal.Decimal('35.00000000000000055511151232')
+    assert position_inst.realised_pnl == decimal.Decimal('14.99999999999999944488848768')
+
+    assert len(tm.transactions) == 1
+    assert list(tm.transactions.values())[-1].symbol == DEFAULT_FUTURE_SYMBOL
+    assert list(tm.transactions.values())[-1].realised_pnl == decimal.Decimal('14.99999999999999944488848768')
+
+    position_size = decimal.Decimal(70)
+
+    # reduce size by 40% --> 0.4
+    await position_inst.update(position_size * decimal.Decimal(0.4))
+    assert position_inst.unrealised_pnl == decimal.Decimal('20.99999999999999977795539508')
+    assert position_inst.realised_pnl == decimal.Decimal('29.00000000000000022204460492')
+
+    assert len(tm.transactions) == 2
+    assert list(tm.transactions.values())[-1].realised_pnl == decimal.Decimal('29.00000000000000022204460492') - \
+           decimal.Decimal('14.99999999999999944488848768')
+
+    position_size = decimal.Decimal(42)
+
+    # Increase by 50% --> shouldn't increase realised pnl
+    await position_inst.update(-position_size * decimal.Decimal(0.5))
+    assert position_inst.realised_pnl == decimal.Decimal('29.00000000000000022204460492')  # = previous realised pnl
+    assert len(tm.transactions) == 2
diff --git a/tests/personal_data/positions/test_positions_manager.py b/tests/personal_data/positions/test_positions_manager.py
index 4eaac73ca..a5525dbdc 100644
--- a/tests/personal_data/positions/test_positions_manager.py
+++ b/tests/personal_data/positions/test_positions_manager.py
@@ -15,6 +15,7 @@
 #  License along with this library.
 import decimal
 import os
+import time
 
 import pytest
 
@@ -38,12 +39,11 @@ async def test_get_symbol_position(future_trader_simulator_with_default_linear):
     symbol_contract = default_contract
     trader.exchange_manager.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL,  default_contract)
 
-    symbol_contract.set_position_mode(is_one_way=True)
-    p1 = positions_manager.get_symbol_position(symbol=DEFAULT_FUTURE_SYMBOL, side=enums.PositionSide.LONG)
+    p1 = positions_manager.get_symbol_position(symbol=DEFAULT_FUTURE_SYMBOL, side=None)
     assert p1
-    p1bis = positions_manager.get_symbol_position(symbol=DEFAULT_FUTURE_SYMBOL, side=enums.PositionSide.SHORT)
+    p1bis = positions_manager.get_symbol_position(symbol=DEFAULT_FUTURE_SYMBOL, side=None)
     assert p1 is p1bis
-    symbol_contract.set_position_mode(is_one_way=False)
+
     p2 = positions_manager.get_symbol_position(symbol=DEFAULT_FUTURE_SYMBOL, side=enums.PositionSide.LONG)
     assert p2
     p2bis = positions_manager.get_symbol_position(symbol=DEFAULT_FUTURE_SYMBOL, side=enums.PositionSide.SHORT)
@@ -64,13 +64,6 @@ async def test_get_symbol_positions(future_trader_simulator_with_default_linear)
     p2 = positions_manager.get_symbol_position(symbol=DEFAULT_FUTURE_SYMBOL, side=enums.PositionSide.SHORT)
     assert len(positions_manager.get_symbol_positions(symbol=DEFAULT_FUTURE_SYMBOL)) == 2
     assert positions_manager.get_symbol_positions(symbol=DEFAULT_FUTURE_SYMBOL) == [p2, p1]
-    symbol_contract.set_position_mode(is_one_way=True)
-    assert len(positions_manager.get_symbol_positions(symbol=DEFAULT_FUTURE_SYMBOL)) == 1
-    p1 = positions_manager.get_symbol_position(symbol=DEFAULT_FUTURE_SYMBOL, side=None)
-    assert len(positions_manager.get_symbol_positions(symbol=DEFAULT_FUTURE_SYMBOL)) == 1
-    assert positions_manager.get_symbol_positions(symbol=DEFAULT_FUTURE_SYMBOL) == [p1]
-    p2 = positions_manager.get_symbol_position(symbol=DEFAULT_FUTURE_SYMBOL, side=None)
-    assert len(positions_manager.get_symbol_positions(symbol=DEFAULT_FUTURE_SYMBOL)) == 1
 
 
 async def test_get_order_position(future_trader_simulator_with_default_linear):
@@ -80,7 +73,7 @@ async def test_get_order_position(future_trader_simulator_with_default_linear):
     trader.exchange_manager.exchange.set_pair_future_contract(DEFAULT_FUTURE_SYMBOL, 
                                                               default_contract)
 
-    p1 = positions_manager.get_symbol_position(symbol=DEFAULT_FUTURE_SYMBOL, side=enums.PositionSide.SHORT)
+    p1 = positions_manager.get_symbol_position(symbol=DEFAULT_FUTURE_SYMBOL, side=enums.PositionSide.BOTH)
     market_sell = SellMarketOrder(trader)
     market_sell.update(order_type=enums.TraderOrderType.SELL_MARKET,
                        symbol=DEFAULT_FUTURE_SYMBOL,
@@ -97,13 +90,19 @@ async def test__generate_position_id(future_trader_simulator_with_default_linear
                                                               default_contract)
 
     if not os.getenv('CYTHON_IGNORE'):
+        sep = positions_mgr.PositionsManager.POSITION_ID_SEPARATOR
+        current_time = time.time()
         symbol_contract.set_position_mode(is_one_way=True)
-        assert positions_manager._generate_position_id(DEFAULT_FUTURE_SYMBOL, None) == \
-               DEFAULT_FUTURE_SYMBOL
+        assert positions_manager._generate_position_id(DEFAULT_FUTURE_SYMBOL, None) == DEFAULT_FUTURE_SYMBOL
+        assert positions_manager._generate_position_id(DEFAULT_FUTURE_SYMBOL, None, None) == DEFAULT_FUTURE_SYMBOL
+        assert positions_manager._generate_position_id(DEFAULT_FUTURE_SYMBOL, None, expiration_time=current_time) == \
+               DEFAULT_FUTURE_SYMBOL + sep + str(current_time)
         symbol_contract.set_position_mode(is_one_way=False)
         assert positions_manager._generate_position_id(DEFAULT_FUTURE_SYMBOL, enums.PositionSide.LONG) == \
-               DEFAULT_FUTURE_SYMBOL + positions_mgr.PositionsManager.POSITION_ID_SEPARATOR \
-               + enums.PositionSide.LONG.value
+               DEFAULT_FUTURE_SYMBOL + sep + enums.PositionSide.LONG.value
+        assert positions_manager._generate_position_id(DEFAULT_FUTURE_SYMBOL, enums.PositionSide.LONG, current_time) == \
+               DEFAULT_FUTURE_SYMBOL + sep + str(current_time) + sep + enums.PositionSide.LONG.value
         assert positions_manager._generate_position_id(DEFAULT_FUTURE_SYMBOL, enums.PositionSide.SHORT) == \
-               DEFAULT_FUTURE_SYMBOL + positions_mgr.PositionsManager.POSITION_ID_SEPARATOR \
-               + enums.PositionSide.SHORT.value
+               DEFAULT_FUTURE_SYMBOL + sep + enums.PositionSide.SHORT.value
+        assert positions_manager._generate_position_id(DEFAULT_FUTURE_SYMBOL, enums.PositionSide.SHORT, current_time) == \
+               DEFAULT_FUTURE_SYMBOL + sep + str(current_time) + sep + enums.PositionSide.SHORT.value
diff --git a/tests/personal_data/positions/types/test_inverse_position.py b/tests/personal_data/positions/types/test_inverse_position.py
index b54c6b94a..d791fc8e3 100644
--- a/tests/personal_data/positions/types/test_inverse_position.py
+++ b/tests/personal_data/positions/types/test_inverse_position.py
@@ -113,9 +113,8 @@ async def test_update_initial_margin(future_trader_simulator_with_default_invers
     position_inst.update_initial_margin()
     assert position_inst.initial_margin == constants.ONE
     default_contract.set_current_leverage(constants.ONE_HUNDRED)
-    await position_inst.update(update_size=constants.ONE_HUNDRED, mark_price=constants.ONE_HUNDRED)
     position_inst.update_initial_margin()
-    assert position_inst.initial_margin == decimal.Decimal("0.0002")
+    assert position_inst.initial_margin == decimal.Decimal("0.01")
 
 
 async def test_calculate_maintenance_margin(future_trader_simulator_with_default_inverse):
@@ -126,11 +125,11 @@ async def test_calculate_maintenance_margin(future_trader_simulator_with_default
     await position_inst.update(update_size=constants.ZERO, mark_price=constants.ZERO)
     assert position_inst.calculate_maintenance_margin() == constants.ZERO
     await position_inst.update(update_size=constants.ONE_HUNDRED, mark_price=constants.ONE_HUNDRED)
-    assert position_inst.calculate_maintenance_margin() == constants.ZERO
+    assert position_inst.calculate_maintenance_margin() == decimal.Decimal('0.01')
     exchange_manager_inst.exchange_symbols_data.get_exchange_symbol_data(
         DEFAULT_FUTURE_SYMBOL).funding_manager.funding_rate = decimal.Decimal(DEFAULT_FUTURE_FUNDING_RATE)
     await position_inst.update(update_size=constants.ONE_HUNDRED, mark_price=constants.ONE_HUNDRED)
-    assert position_inst.calculate_maintenance_margin() == decimal.Decimal("0.02000000000000000041633363423")
+    assert position_inst.calculate_maintenance_margin() == decimal.Decimal("0.02")
 
 
 async def test_update_isolated_liquidation_price_with_long(future_trader_simulator_with_default_inverse):
@@ -143,12 +142,12 @@ async def test_update_isolated_liquidation_price_with_long(future_trader_simulat
     position_inst.entry_price = constants.ONE_HUNDRED
     await position_inst.update(update_size=constants.ONE_HUNDRED, mark_price=constants.ONE_HUNDRED)
     position_inst.update_isolated_liquidation_price()
-    assert position_inst.liquidation_price == decimal.Decimal("50.25125628140703518113600205")
+    assert position_inst.liquidation_price == decimal.Decimal("50.25125628140703517587939698")
     default_contract.set_current_leverage(constants.ONE_HUNDRED)
     await position_inst.update(update_size=constants.ONE_HUNDRED,
                                mark_price=decimal.Decimal(2) * constants.ONE_HUNDRED)
     position_inst.update_isolated_liquidation_price()
-    assert position_inst.liquidation_price == decimal.Decimal("100.0000000000000000208166817")
+    assert position_inst.liquidation_price == decimal.Decimal("1E+2")
 
 
 async def test_update_isolated_liquidation_price_with_short(future_trader_simulator_with_default_inverse):
@@ -161,12 +160,12 @@ async def test_update_isolated_liquidation_price_with_short(future_trader_simula
     position_inst.entry_price = constants.ONE_HUNDRED
     await position_inst.update(update_size=-constants.ONE_HUNDRED, mark_price=constants.ONE_HUNDRED)
     position_inst.update_isolated_liquidation_price()
-    assert position_inst.liquidation_price == decimal.Decimal("9999.999999999999791833182880")
+    assert position_inst.liquidation_price == decimal.Decimal("1.00E+4")
     default_contract.set_current_leverage(constants.ONE_HUNDRED)
     await position_inst.update(update_size=-constants.ONE_HUNDRED,
                                mark_price=constants.ONE_HUNDRED / decimal.Decimal(10))
     position_inst.update_isolated_liquidation_price()
-    assert position_inst.liquidation_price == decimal.Decimal("99.99999999999999997918331830")
+    assert position_inst.liquidation_price == decimal.Decimal("1E+2")
 
 
 async def test_get_bankruptcy_price_with_long(future_trader_simulator_with_default_inverse):
diff --git a/tests/personal_data/positions/types/test_linear_position.py b/tests/personal_data/positions/types/test_linear_position.py
index 309733e80..564e4979b 100644
--- a/tests/personal_data/positions/types/test_linear_position.py
+++ b/tests/personal_data/positions/types/test_linear_position.py
@@ -54,7 +54,7 @@ async def test_update_pnl_with_long_linear_position(future_trader_simulator_with
     position_inst.update_pnl()
     assert position_inst.unrealised_pnl == constants.ZERO
     await position_inst.update(update_size=constants.ONE_HUNDRED,
-                         mark_price=decimal.Decimal(2) * constants.ONE_HUNDRED)
+                               mark_price=decimal.Decimal(2) * constants.ONE_HUNDRED)
     position_inst.update_pnl()
     assert position_inst.unrealised_pnl == decimal.Decimal("20000")
 
@@ -68,7 +68,7 @@ async def test_update_pnl_with_short_linear_position(future_trader_simulator_wit
     position_inst.update_pnl()
     assert position_inst.unrealised_pnl == constants.ZERO
     await position_inst.update(update_size=-constants.ONE_HUNDRED,
-                         mark_price=constants.ONE_HUNDRED / decimal.Decimal(10))
+                               mark_price=constants.ONE_HUNDRED / decimal.Decimal(10))
     position_inst.update_pnl()
     assert position_inst.unrealised_pnl == decimal.Decimal("18000")
 
@@ -85,7 +85,7 @@ async def test_update_pnl_with_loss_with_long_linear_position(future_trader_simu
     exchange_manager_inst.exchange_personal_data.portfolio_manager.portfolio.get_currency_portfolio(
         "USDT").wallet_balance = decimal.Decimal(100000)  # TO prevent portfolio negative error
     await position_inst.update(update_size=constants.ONE_HUNDRED,
-                         mark_price=constants.ONE_HUNDRED / decimal.Decimal(2.535485))
+                               mark_price=constants.ONE_HUNDRED / decimal.Decimal(2.535485))
     position_inst.update_pnl()
     assert position_inst.unrealised_pnl == decimal.Decimal("-12111.96280001656484319345490")
 
@@ -102,7 +102,7 @@ async def test_update_pnl_with_loss_with_short(future_trader_simulator_with_defa
     exchange_manager_inst.exchange_personal_data.portfolio_manager.portfolio.get_currency_portfolio(
         "USDT").wallet_balance = decimal.Decimal(100000)  # TO prevent portfolio negative error
     await position_inst.update(update_size=-constants.ONE_HUNDRED,
-                         mark_price=constants.ONE_HUNDRED * decimal.Decimal(1.0954))
+                               mark_price=constants.ONE_HUNDRED * decimal.Decimal(1.0954))
     position_inst.update_pnl()
     assert position_inst.unrealised_pnl == decimal.Decimal("-1907.999999999998586019955840")
 
@@ -118,9 +118,8 @@ async def test_update_initial_margin(future_trader_simulator_with_default_linear
     position_inst.update_initial_margin()
     assert position_inst.initial_margin == decimal.Decimal("10000")
     default_contract.set_current_leverage(constants.ONE_HUNDRED)
-    await position_inst.update(update_size=constants.ONE_HUNDRED, mark_price=constants.ONE_HUNDRED)
     position_inst.update_initial_margin()
-    assert position_inst.initial_margin == decimal.Decimal("2")
+    assert position_inst.initial_margin == decimal.Decimal("100")
 
 
 async def test_calculate_maintenance_margin(future_trader_simulator_with_default_linear):
@@ -131,11 +130,11 @@ async def test_calculate_maintenance_margin(future_trader_simulator_with_default
     await position_inst.update(update_size=constants.ZERO, mark_price=constants.ZERO)
     assert position_inst.calculate_maintenance_margin() == constants.ZERO
     await position_inst.update(update_size=constants.ONE_HUNDRED, mark_price=constants.ONE_HUNDRED)
-    assert position_inst.calculate_maintenance_margin() == constants.ZERO
+    assert position_inst.calculate_maintenance_margin() == constants.ONE_HUNDRED
     exchange_manager_inst.exchange_symbols_data.get_exchange_symbol_data(
         DEFAULT_FUTURE_SYMBOL).funding_manager.funding_rate = decimal.Decimal(DEFAULT_FUTURE_FUNDING_RATE)
     await position_inst.update(update_size=constants.ONE_HUNDRED, mark_price=constants.ONE_HUNDRED)
-    assert position_inst.calculate_maintenance_margin() == decimal.Decimal('0E-59')
+    assert position_inst.calculate_maintenance_margin() == decimal.Decimal('200')
 
 
 async def test_update_isolated_liquidation_price_with_long(future_trader_simulator_with_default_linear):
@@ -148,12 +147,12 @@ async def test_update_isolated_liquidation_price_with_long(future_trader_simulat
     position_inst.entry_price = constants.ONE_HUNDRED
     await position_inst.update(update_size=constants.ONE_HUNDRED, mark_price=constants.ONE_HUNDRED)
     position_inst.update_isolated_liquidation_price()
-    assert position_inst.liquidation_price == constants.ZERO
+    assert position_inst.liquidation_price == constants.ONE
     default_contract.set_current_leverage(constants.ONE_HUNDRED)
     await position_inst.update(update_size=-constants.ONE_HUNDRED,
-                         mark_price=decimal.Decimal(2) * constants.ONE_HUNDRED)
+                               mark_price=decimal.Decimal(1.2) * constants.ONE_HUNDRED)
     position_inst.update_isolated_liquidation_price()
-    assert position_inst.liquidation_price == decimal.Decimal('602.0000000000000083266726846')
+    assert position_inst.liquidation_price == constants.ZERO
 
 
 async def test_update_isolated_liquidation_price_with_short(
@@ -167,12 +166,12 @@ async def test_update_isolated_liquidation_price_with_short(
     position_inst.entry_price = constants.ONE_HUNDRED
     await position_inst.update(update_size=-constants.ONE_HUNDRED, mark_price=constants.ONE_HUNDRED)
     position_inst.update_isolated_liquidation_price()
-    assert position_inst.liquidation_price == decimal.Decimal("10200.00000000000020816681712")
+    assert position_inst.liquidation_price == decimal.Decimal("199.0")
     default_contract.set_current_leverage(constants.ONE_HUNDRED)
     await position_inst.update(update_size=-constants.ONE_HUNDRED,
-                         mark_price=constants.ONE_HUNDRED / decimal.Decimal(10))
+                               mark_price=constants.ONE_HUNDRED / decimal.Decimal(10))
     position_inst.update_isolated_liquidation_price()
-    assert position_inst.liquidation_price == decimal.Decimal("301.0000000000000041633363423")
+    assert position_inst.liquidation_price == decimal.Decimal("100")
 
 
 async def test_get_bankruptcy_price_with_long(future_trader_simulator_with_default_linear):
@@ -188,7 +187,7 @@ async def test_get_bankruptcy_price_with_long(future_trader_simulator_with_defau
     assert position_inst.get_bankruptcy_price() == decimal.Decimal("99.00")
     assert position_inst.get_bankruptcy_price(with_mark_price=True) == decimal.Decimal("100")
     await position_inst.update(update_size=constants.ONE_HUNDRED,
-                         mark_price=decimal.Decimal(2) * constants.ONE_HUNDRED)
+                               mark_price=decimal.Decimal(2) * constants.ONE_HUNDRED)
     assert position_inst.get_bankruptcy_price() == decimal.Decimal("99.00")
     assert position_inst.get_bankruptcy_price(with_mark_price=True) == decimal.Decimal("200")
 
@@ -208,7 +207,7 @@ async def test_get_bankruptcy_price_with_short(future_trader_simulator_with_defa
     exchange_manager_inst.exchange_personal_data.portfolio_manager.portfolio.get_currency_portfolio(
         "USDT").wallet_balance = decimal.Decimal(10000)  # TO prevent portfolio negative error
     await position_inst.update(update_size=constants.ONE_HUNDRED,
-                         mark_price=decimal.Decimal(2) * constants.ONE_HUNDRED)
+                               mark_price=decimal.Decimal(2) * constants.ONE_HUNDRED)
     assert position_inst.get_bankruptcy_price() == constants.ZERO
     assert position_inst.get_bankruptcy_price(with_mark_price=True) == constants.ZERO
 
diff --git a/tests/personal_data/transactions/__init__.py b/tests/personal_data/transactions/__init__.py
new file mode 100644
index 000000000..adbc2745b
--- /dev/null
+++ b/tests/personal_data/transactions/__init__.py
@@ -0,0 +1,15 @@
+#  Drakkar-Software OctoBot-Trading
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
diff --git a/tests/personal_data/transactions/test_transaction_factory.py b/tests/personal_data/transactions/test_transaction_factory.py
new file mode 100644
index 000000000..88969f95a
--- /dev/null
+++ b/tests/personal_data/transactions/test_transaction_factory.py
@@ -0,0 +1,102 @@
+#  Drakkar-Software OctoBot
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+import decimal
+import uuid
+
+import time
+
+import pytest
+
+import octobot_trading.enums as enums
+import octobot_trading.constants as constants
+import octobot_trading.personal_data.transactions.transaction_factory as transaction_factory
+
+from tests.exchanges import backtesting_trader, backtesting_config, backtesting_exchange_manager, fake_backtesting
+from tests import event_loop
+
+# All test coroutines will be treated as marked.
+pytestmark = pytest.mark.asyncio
+
+TRANSACTION_SYMBOL = "BTC/USDT"
+TRANSACTION_CURRENCY = "BTC"
+
+
+async def test_create_blockchain_transaction(backtesting_trader):
+    _, exchange_manager, _ = backtesting_trader
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 0
+    t_id = str(uuid.uuid4())
+    transaction = transaction_factory.create_blockchain_transaction(exchange_manager,
+                                                                    is_deposit=True,
+                                                                    currency=TRANSACTION_CURRENCY,
+                                                                    blockchain_type="TEST",
+                                                                    blockchain_transaction_id=t_id,
+                                                                    blockchain_transaction_status=enums.BlockchainTransactionStatus.CONFIRMING,
+                                                                    source_address="0x123456789",
+                                                                    quantity=decimal.Decimal(50),
+                                                                    transaction_fee=decimal.Decimal(0.1))
+    assert transaction.currency == TRANSACTION_CURRENCY
+    assert transaction.creation_time <= time.time()
+    assert transaction.transaction_id == t_id
+    assert transaction.blockchain_transaction_id == t_id
+    assert transaction.blockchain_transaction_status is enums.BlockchainTransactionStatus.CONFIRMING
+    assert transaction.source_address == "0x123456789"
+    assert transaction.quantity == decimal.Decimal(50)
+    assert transaction.transaction_fee == decimal.Decimal(0.1)
+    assert transaction.is_deposit()
+    assert not transaction.is_withdrawal()
+    assert not transaction.is_validated()
+    assert transaction.is_pending()
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 1
+
+
+async def test_create_realised_pnl_transaction(backtesting_trader):
+    _, exchange_manager, _ = backtesting_trader
+    transaction = transaction_factory.create_realised_pnl_transaction(exchange_manager,
+                                                                      currency=TRANSACTION_CURRENCY,
+                                                                      symbol=TRANSACTION_SYMBOL,
+                                                                      realised_pnl=decimal.Decimal(0.1))
+    assert transaction.currency == TRANSACTION_CURRENCY
+    assert transaction.symbol == TRANSACTION_SYMBOL
+    assert transaction.realised_pnl == decimal.Decimal(0.1)
+    assert transaction.creation_time <= time.time()
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 1
+
+
+async def test_create_fee_transaction(backtesting_trader):
+    _, exchange_manager, _ = backtesting_trader
+    o_id = str(uuid.uuid4())
+    transaction = transaction_factory.create_fee_transaction(exchange_manager,
+                                                             currency=TRANSACTION_CURRENCY,
+                                                             symbol=TRANSACTION_SYMBOL,
+                                                             quantity=decimal.Decimal(10), order_id=o_id)
+    assert transaction.currency == TRANSACTION_CURRENCY
+    assert transaction.symbol == TRANSACTION_SYMBOL
+    assert transaction.creation_time <= time.time()
+    assert transaction.order_id == o_id
+    assert transaction.transaction_id
+    assert transaction.quantity == decimal.Decimal(10)
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 1
+
+
+async def test_create_transfer_transaction(backtesting_trader):
+    _, exchange_manager, _ = backtesting_trader
+    transaction = transaction_factory.create_transfer_transaction(exchange_manager,
+                                                                  currency=TRANSACTION_CURRENCY,
+                                                                  symbol=TRANSACTION_SYMBOL)
+    assert transaction.currency == TRANSACTION_CURRENCY
+    assert transaction.symbol == TRANSACTION_SYMBOL
+    assert transaction.creation_time <= time.time()
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 1
diff --git a/tests/personal_data/transactions/test_transactions_manager.py b/tests/personal_data/transactions/test_transactions_manager.py
new file mode 100644
index 000000000..c9343dfea
--- /dev/null
+++ b/tests/personal_data/transactions/test_transactions_manager.py
@@ -0,0 +1,204 @@
+#  Drakkar-Software OctoBot
+#  Copyright (c) Drakkar-Software, All rights reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 3.0 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library.
+import decimal
+import uuid
+
+import pytest
+
+import octobot_trading.enums as enums
+import octobot_trading.errors as errors
+import octobot_trading.constants as constants
+import octobot_trading.personal_data.transactions.types as transaction_types
+
+from tests.exchanges import backtesting_trader, backtesting_config, backtesting_exchange_manager, fake_backtesting
+from tests import event_loop
+
+# All test coroutines will be treated as marked.
+pytestmark = pytest.mark.asyncio
+
+TRANSACTION_SYMBOL = "BTC/USDT"
+TRANSACTION_CURRENCY = "BTC"
+
+
+async def test_initialized(backtesting_trader):
+    _, exchange_manager, _ = backtesting_trader
+    assert exchange_manager.exchange_personal_data.transactions_manager.is_initialized
+
+
+async def test_insert_transaction_instance(backtesting_trader):
+    _, exchange_manager, _ = backtesting_trader
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 0
+    t_id = str(uuid.uuid4())
+    t_id_2 = str(uuid.uuid4())
+    transaction = transaction_types.RealisedPnlTransaction(
+        exchange_name=exchange_manager.exchange_name,
+        creation_time=exchange_manager.exchange.get_exchange_current_time(),
+        transaction_type=enums.TransactionType.REALISED_PNL,
+        currency=TRANSACTION_CURRENCY,
+        symbol=TRANSACTION_SYMBOL,
+        realised_pnl=constants.ZERO)
+    transaction.set_transaction_id(t_id)
+    transaction_2 = transaction_types.BlockchainTransaction(
+        exchange_name=exchange_manager.exchange_name,
+        creation_time=exchange_manager.exchange.get_exchange_current_time(),
+        currency=TRANSACTION_CURRENCY,
+        transaction_type=enums.TransactionType.BLOCKCHAIN_DEPOSIT,
+        blockchain_type="Test",
+        blockchain_transaction_id=t_id,
+        blockchain_transaction_status=enums.BlockchainTransactionStatus.CONFIRMING,
+        source_address="0x123456789",
+        destination_address=None,
+        quantity=decimal.Decimal(50),
+        transaction_fee=decimal.Decimal(0.1)
+    )
+    transaction_2.set_transaction_id(t_id)
+
+    # succeed to upsert unknown transaction
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 0
+    exchange_manager.exchange_personal_data.transactions_manager.insert_transaction_instance(transaction)
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 1
+
+    # can't add transaction with the same transaction id without replacing it --> DuplicateTransactionIdError
+    with pytest.raises(errors.DuplicateTransactionIdError):
+        exchange_manager.exchange_personal_data.transactions_manager.insert_transaction_instance(transaction_2)
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 1
+    assert list(exchange_manager.exchange_personal_data.transactions_manager.transactions.values())[-1] is transaction
+
+    # can replace a transaction with the same transaction id
+    exchange_manager.exchange_personal_data.transactions_manager.insert_transaction_instance(transaction_2,
+                                                                                             replace_if_exists=True)
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 1
+
+    # succeed to upsert a transaction with a new id
+    transaction.set_transaction_id(t_id_2)
+    exchange_manager.exchange_personal_data.transactions_manager.insert_transaction_instance(transaction)
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 2
+
+    # check transaction instances
+    assert list(exchange_manager.exchange_personal_data.transactions_manager.transactions.values())[0] is transaction_2
+    assert list(exchange_manager.exchange_personal_data.transactions_manager.transactions.values())[-1] is transaction
+
+
+async def test_get_transaction(backtesting_trader):
+    _, exchange_manager, _ = backtesting_trader
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 0
+    transaction = transaction_types.RealisedPnlTransaction(
+        exchange_name=exchange_manager.exchange_name,
+        creation_time=exchange_manager.exchange.get_exchange_current_time(),
+        transaction_type=enums.TransactionType.REALISED_PNL,
+        currency=TRANSACTION_CURRENCY,
+        symbol=TRANSACTION_SYMBOL,
+        realised_pnl=constants.ZERO)
+    transaction_2 = transaction_types.RealisedPnlTransaction(
+        exchange_name=exchange_manager.exchange_name,
+        creation_time=exchange_manager.exchange.get_exchange_current_time(),
+        transaction_type=enums.TransactionType.REALISED_PNL,
+        currency=TRANSACTION_CURRENCY,
+        symbol=TRANSACTION_SYMBOL,
+        realised_pnl=constants.ZERO)
+    exchange_manager.exchange_personal_data.transactions_manager.insert_transaction_instance(transaction)
+
+    # succeed to return transaction instance
+    assert exchange_manager.exchange_personal_data.transactions_manager. \
+               get_transaction(transaction.transaction_id) is transaction
+
+    # can't get unknown transaction id
+    with pytest.raises(KeyError):
+        exchange_manager.exchange_personal_data.transactions_manager.get_transaction(str(uuid.uuid4()))
+
+    # can't get a transaction which has not been added
+    with pytest.raises(KeyError):
+        exchange_manager.exchange_personal_data.transactions_manager.get_transaction(transaction_2.transaction_id)
+
+    # succeed to return transaction instances
+    exchange_manager.exchange_personal_data.transactions_manager.insert_transaction_instance(transaction_2)
+    assert exchange_manager.exchange_personal_data.transactions_manager. \
+               get_transaction(transaction.transaction_id) is transaction
+    assert exchange_manager.exchange_personal_data.transactions_manager. \
+               get_transaction(transaction_2.transaction_id) is transaction_2
+
+
+async def test_update_transaction_id(backtesting_trader):
+    _, exchange_manager, _ = backtesting_trader
+    t_id = str(uuid.uuid4())
+    t_id_2 = str(uuid.uuid4())
+    t_id_3 = str(uuid.uuid4())
+    t_id_4 = str(uuid.uuid4())
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 0
+    transaction = transaction_types.RealisedPnlTransaction(
+        exchange_name=exchange_manager.exchange_name,
+        creation_time=exchange_manager.exchange.get_exchange_current_time(),
+        transaction_type=enums.TransactionType.REALISED_PNL,
+        currency=TRANSACTION_CURRENCY,
+        symbol=TRANSACTION_SYMBOL,
+        realised_pnl=constants.ZERO)
+    transaction.set_transaction_id(t_id)
+    transaction_2 = transaction_types.RealisedPnlTransaction(
+        exchange_name=exchange_manager.exchange_name,
+        creation_time=exchange_manager.exchange.get_exchange_current_time(),
+        transaction_type=enums.TransactionType.REALISED_PNL,
+        currency=TRANSACTION_CURRENCY,
+        symbol=TRANSACTION_SYMBOL,
+        realised_pnl=constants.ZERO)
+    transaction_2.set_transaction_id(t_id_2)
+
+    # Add transaction instances to TransactionsManager.transactions
+    exchange_manager.exchange_personal_data.transactions_manager.insert_transaction_instance(transaction)
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 1
+    exchange_manager.exchange_personal_data.transactions_manager.insert_transaction_instance(transaction_2)
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 2
+
+    # Update the first transaction id and see if the previous entry was properly deleted
+    exchange_manager.exchange_personal_data.transactions_manager.update_transaction_id(t_id, t_id_3)
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 2
+    assert transaction.transaction_id == t_id_3
+    assert list(exchange_manager.exchange_personal_data.transactions_manager.transactions.values())[-1] is transaction
+    assert list(exchange_manager.exchange_personal_data.transactions_manager.transactions.values())[-1].transaction_id == t_id_3
+
+    # ensure that the first transaction_id doesn't exist anymore
+    with pytest.raises(KeyError):
+        exchange_manager.exchange_personal_data.transactions_manager.get_transaction(t_id)
+
+    # try to update with an already used transaction id without replacing it
+    with pytest.raises(errors.DuplicateTransactionIdError):
+        exchange_manager.exchange_personal_data.transactions_manager.update_transaction_id(t_id_2, t_id_3)
+
+    # check if the transaction keeps its old transaction id
+    assert transaction_2.transaction_id == t_id_2
+
+    # check if transaction_2 was not dropped from the list
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 2
+    exchange_manager.exchange_personal_data.transactions_manager.get_transaction(t_id_2)
+
+    # try to update with an already used transaction id with replacement
+    exchange_manager.exchange_personal_data.transactions_manager.update_transaction_id(t_id_2, t_id_3,
+                                                                                       replace_if_exists=True)
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 1  # transaction replaced by transaction_2
+    assert transaction_2.transaction_id == t_id_3
+    assert list(exchange_manager.exchange_personal_data.transactions_manager.transactions.values())[-1] is transaction_2
+    assert list(exchange_manager.exchange_personal_data.transactions_manager.transactions.values())[
+               -1].transaction_id == t_id_3
+
+    # try to update an unknown transaction
+    with pytest.raises(KeyError):
+        exchange_manager.exchange_personal_data.transactions_manager.update_transaction_id(str(uuid.uuid4()),
+                                                                                           str(uuid.uuid4()))
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 1
+
+    # restore transaction in list
+    transaction.set_transaction_id(t_id_4)
+    exchange_manager.exchange_personal_data.transactions_manager.insert_transaction_instance(transaction)
+    assert len(exchange_manager.exchange_personal_data.transactions_manager.transactions) == 2
diff --git a/tests/test_utils/random_numbers.py b/tests/test_utils/random_numbers.py
index b853b7e56..3a4974477 100644
--- a/tests/test_utils/random_numbers.py
+++ b/tests/test_utils/random_numbers.py
@@ -114,5 +114,14 @@ def random_recent_trade(price=None, timestamp: float = None) -> dict:
     }
 
 
+def decimal_random_recent_trade(price=None, timestamp: float = None) -> dict:
+    return {
+        ECOC.PRICE.value: price if price is not None else decimal_random_price(),
+        ECOC.AMOUNT.value: decimal_random_quantity(),
+        ECOC.COST.value: decimal_random_quantity(),
+        ECOC.TIMESTAMP.value: timestamp if timestamp is not None else decimal_random_quantity(),
+    }
+
+
 def random_recent_trades(count: int = 2) -> list:
     return [random_recent_trade() for _ in range(count)]