Skip to content

Commit

Permalink
feat: implement exchange operators and trading rule tracking
Browse files Browse the repository at this point in the history
- Add exchange operator implementation - Introduce nonce creator - Add trading rules tracker - Update order and operator models - Enhance simulation engine and dashboard - Update corresponding test cases
  • Loading branch information
gianlucapagliara committed Jan 7, 2025
1 parent aa0c39a commit fda6b5c
Show file tree
Hide file tree
Showing 15 changed files with 915 additions and 46 deletions.
10 changes: 10 additions & 0 deletions financepype/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import os
import platform
from decimal import Decimal
from hashlib import md5

s_decimal_0 = Decimal("0")
s_decimal_max = Decimal("1e20")
s_decimal_min = Decimal("1e-20")
s_decimal_inf = Decimal("inf")
s_decimal_NaN = Decimal("NaN")


def get_instance_id() -> str:
return md5(
f"{platform.uname()}_pid:{os.getpid()}_ppid:{os.getppid()}".encode()
).hexdigest()
4 changes: 0 additions & 4 deletions financepype/operations/orders/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@
class OrderType(Enum):
MARKET = "MARKET"
LIMIT = "LIMIT"
LIMIT_MAKER = "LIMIT_MAKER"

def is_limit_type(self) -> bool:
return self in (OrderType.LIMIT, OrderType.LIMIT_MAKER)


class OrderModifier(Enum):
Expand Down
54 changes: 38 additions & 16 deletions financepype/operations/orders/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@


class OrderState(Enum):
PENDING_CREATE = 0 # Initial state -> waiting for exchange to create order (order not yet in order book)
OPEN = 1 # Ready to be filled
PENDING_CANCEL = 2 # User requested cancellation of order -> waiting for confirmation from exchange
CANCELED = 3 # Order was cancelled by user
PARTIALLY_FILLED = 4 # Order partially filled -> still open
FILLED = 5 # Order completely filled -> completed
FAILED = 6 # Order failed to be created by the exchange
PENDING_CREATE = "pending_create" # Initial state -> waiting for exchange to create order (order not yet in order book)
OPEN = "open" # Ready to be filled
PENDING_CANCEL = "pending_cancel" # User requested cancellation of order -> waiting for confirmation from exchange
CANCELED = "canceled" # Order was cancelled by user
FILLED = "filled" # Order completely filled -> completed
FAILED = "failed" # Order failed to be created by the exchange


class OrderUpdate(BaseModel):
Expand All @@ -40,7 +39,7 @@ class OrderUpdate(BaseModel):
including new state, timestamps, and identifiers.
"""

trading_pair: str
trading_pair: TradingPair
update_timestamp: float # seconds
new_state: OrderState
client_order_id: str | None = None
Expand Down Expand Up @@ -164,7 +163,7 @@ def quote_asset(self) -> Any: # Type depends on the asset implementation

@property
def is_limit(self) -> bool:
return self.order_type.is_limit_type()
return self.order_type == OrderType.LIMIT

@property
def is_market(self) -> bool:
Expand Down Expand Up @@ -204,7 +203,6 @@ def is_open(self) -> bool:
return self.current_state in {
OrderState.PENDING_CREATE,
OrderState.OPEN,
OrderState.PARTIALLY_FILLED,
OrderState.PENDING_CANCEL,
}

Expand Down Expand Up @@ -247,8 +245,8 @@ def process_operation_update(self, update: OrderUpdate | TradeUpdate) -> bool:

def _update_with_order_update(self, order_update: OrderUpdate) -> bool:
"""Update the in flight order with an order update (from REST API or WS API)."""
if (
order_update.client_order_id != self.client_operation_id
if order_update.client_order_id != self.client_operation_id or (
self.operator_operation_id is not None
and order_update.exchange_order_id != self.operator_operation_id
):
return False
Expand All @@ -261,15 +259,41 @@ def _update_with_order_update(self, order_update: OrderUpdate) -> bool:
):
self.update_operator_operation_id(order_update.exchange_order_id)

if self.is_open:
self.current_state = order_update.new_state
# Check if the state transition is valid
valid = self.is_valid_state_transition(order_update)
if not valid:
return False
self.current_state = order_update.new_state

updated = prev_state != self.current_state
if updated:
self.last_update_timestamp = order_update.update_timestamp

return updated

def is_valid_state_transition(self, order_update: OrderUpdate) -> bool:
if self.is_pending_create:
if order_update.new_state not in {OrderState.OPEN, OrderState.FAILED}:
return False
elif self.is_open:
if order_update.new_state not in {
OrderState.OPEN,
OrderState.PENDING_CANCEL,
OrderState.CANCELED,
OrderState.FILLED,
}:
return False
elif self.is_pending_cancel_confirmation:
if order_update.new_state not in {
OrderState.CANCELED,
OrderState.OPEN,
OrderState.FILLED,
}:
return False
else:
return False
return True

def _update_with_trade_update(self, trade_update: TradeUpdate) -> bool:
"""Update the in flight order with a trade update (from REST API or WS API)."""
trade_id: str = trade_update.trade_id
Expand All @@ -293,8 +317,6 @@ def _update_with_trade_update(self, trade_update: TradeUpdate) -> bool:
or self.executed_amount_base >= self.amount
):
self.current_state = OrderState.FILLED
elif self.executed_amount_base > 0:
self.current_state = OrderState.PARTIALLY_FILLED

self.check_filled_condition()

Expand Down
Empty file.
Loading

0 comments on commit fda6b5c

Please sign in to comment.