summaryrefslogtreecommitdiff
path: root/trade_queue.py
diff options
context:
space:
mode:
authoruvok2025-04-19 16:17:10 +0200
committeruvok2025-04-19 16:17:10 +0200
commit75188f8d900b8f33bd9c5018cc875caf2974c422 (patch)
tree31fbc16f68a19c55e7e940fd064bce4e7f465b1c /trade_queue.py
parent1b6768931337646d9b191087e7e0b8aa4c1c26b3 (diff)
Add match method
Diffstat (limited to 'trade_queue.py')
-rw-r--r--trade_queue.py59
1 files changed, 58 insertions, 1 deletions
diff --git a/trade_queue.py b/trade_queue.py
index c3a4b9d..87bbdfc 100644
--- a/trade_queue.py
+++ b/trade_queue.py
@@ -3,7 +3,7 @@ import bisect
from collections import deque
from copy import deepcopy
from decimal import Decimal
-from typing import Callable, Deque, List
+from typing import Callable, Deque, List, Tuple
from exceptions import TradeNotFound
from trade import Trade
@@ -63,6 +63,63 @@ class FIFOQueue:
self._cache_valid = False
logger.info(f"Added trade: {trade}.")
+ def match_trades(self) -> List[Tuple[Trade, Trade]]:
+ """
+ Match sell trades with buy trades in a FIFO manner, ensuring matched amounts are equal.
+ If multiple buy trades are needed for one sell trade, split the sell trade accordingly.
+
+ Returns:
+ List[Tuple[Trade, Trade]]: List of tuples with (buy_trade, sell_trade) of equal amounts.
+ """
+ matched_pairs: List[Tuple[Trade, Trade]] = []
+
+ # Locate the next sell trade
+ sell_trade = next((t for t in self.__queue if t.amount < 0), None)
+
+ while sell_trade:
+ matched_pairs.extend(self.__match_trades_impl(sell_trade))
+ self.__queue.remove(sell_trade)
+ self._cache_valid = False
+ sell_trade = next((t for t in self.__queue if t.amount < 0), None)
+
+ if not sell_trade:
+ logger.info("No more sell trades available for matching.")
+
+ return matched_pairs
+
+ def __match_trades_impl(self, sell_trade: Trade) -> List[Tuple[Trade, Trade]]:
+ matched_pairs: List[Tuple[Trade, Trade]] = []
+
+ # Convert to positive for easier calculations
+ remaining_sell_amount = -sell_trade.amount
+
+ # Use `self.remove_coins` to fetch corresponding buy trades
+ try:
+ buy_trades = self.remove_coins(remaining_sell_amount)
+ except ValueError as e:
+ logger.error(f"Failed to match trade: {e}")
+ raise
+
+ # Process the buy trades and split the sell trade if necessary
+ for buy_trade in buy_trades:
+ # remove_coins should take care this doesn't happen
+ assert buy_trade.amount <= remaining_sell_amount
+
+ matched_pairs.append(
+ (
+ buy_trade,
+ Trade(
+ buy_trade.amount,
+ buy_trade.amount * sell_trade.price_per_coin,
+ sell_trade.timestamp,
+ ),
+ )
+ )
+ remaining_sell_amount -= buy_trade.amount
+
+ logger.info(f"Matched sell trade {sell_trade} with buy trades: {matched_pairs}")
+ return matched_pairs
+
# The remove_coins method needs updating, it currently operates on the first trade in the queue, disregarding whether it's buy or sell
def remove_coins(
self, amount: float | Decimal, before_ts: str | None = None