Source code for fractal.core.entities.gmx_v2

from dataclasses import dataclass, field
from typing import List

import numpy as np

from fractal.core.base.entity import (EntityException, GlobalState,
                                      InternalState)
from fractal.core.entities.hedge import BaseHedgeEntity


[docs] class GMXV2EntityException(EntityException): """ Exception raised for errors in the GMX V2 entity. """
[docs] class GMXV2Position: """ A position in the GMXHedge. It includes the amount and the entry price of the position. """ def __init__(self, amount: float, entry_price: float): """ Initializes the GMXPosition. Args: amount (float): The amount of the position in product (e.g. BTC, ETH, etc.) entry_price (float): The entry price of the position in notional (e.g. USD, USDC, etc.) """ self.amount: float = amount self.entry_price: float = entry_price def __repr__(self): return f"GMXPosition(amount={self.amount}, entry_price={self.entry_price})"
[docs] def pnl(self, price: float) -> float: """ Calculates the profit and loss (PNL) of the position. PNL = Amount * (Price - Entry Price) """ return self.amount * (price - self.entry_price)
[docs] @dataclass class GMXV2GlobalState(GlobalState): """ Represents the global state of the GMX V2 entity. GMX V2 has two-sided funding rates and borrowing rates. price: float - The current price of the product. funding_rate_short: float - The short funding rate. funding_rate_long: float - The long funding rate. borrowing_rate_short: float - The short borrowing rate. borrowing_rate_long: float - The long borrowing rate. longs_pay_shorts: bool - Whether longs pay shorts. """ price: float = 0.0 funding_rate_short: float = 0.0 funding_rate_long: float = 0.0 borrowing_rate_short: float = 0.0 borrowing_rate_long: float = 0.0 longs_pay_shorts: bool = True
[docs] @dataclass class GMXV2InternalState(InternalState): """ Represents the internal state of the GMX V2 entity. It includes the collateral and the positions of the entity. """ collateral: float = 0.0 positions: List[GMXV2Position] = field(default_factory=list)
[docs] class GMXV2Entity(BaseHedgeEntity): """ Represents a GMX isolated market entity. """ def __init__(self, *args, trading_fee: float = 0.001, liquidation_leverage: float = 100, **kwargs): """ Initializes the GMX entity. Args: trading_fee (float, optional): Trading fee. Defaults to 0.001. liquidation_leverage (float, optional): Liquidation leverage in GMX. Defaults to 100. """ super().__init__(*args, **kwargs) self.TRADING_FEE = trading_fee self.LIQUIDATION_LEVERAGE = liquidation_leverage def _initialize_states(self): self._internal_state: GMXV2InternalState = GMXV2InternalState() self._global_state: GMXV2GlobalState = GMXV2GlobalState()
[docs] def action_deposit(self, amount_in_notional: float): """ Deposits a specified amount of notional into the entity's collateral. Args: amount_in_notional (float): The amount of notional to deposit. """ if amount_in_notional < 0: raise GMXV2EntityException(f"Invalid deposit amount: {amount_in_notional} < 0.") self._internal_state.collateral += amount_in_notional
[docs] def action_withdraw(self, amount_in_notional: float) -> float: """ Withdraws a specified amount of notional from the entity's collateral. Args: amount_in_notional (float): The amount of notional to withdraw. Returns: float: The remaining balance after the withdrawal. Raises: ValueError: If there is not enough balance to withdraw. """ if self.balance < amount_in_notional: raise GMXV2EntityException(f"Not enough balance to withdraw: {self.balance} < {amount_in_notional}.") max_withdrawal: float = self.balance - np.abs(self.size * self._global_state.price) / self.LIQUIDATION_LEVERAGE if amount_in_notional > max_withdrawal: raise GMXV2EntityException(f"Exceeds maximum withdrawal limit: {amount_in_notional} > {max_withdrawal}.") self._internal_state.collateral -= amount_in_notional
[docs] def action_open_position(self, amount_in_product: float): """ Opens a position with a specified amount of product. Args: amount_in_product (float): The amount of product to open the position with. """ self._internal_state.positions.append( GMXV2Position(amount=amount_in_product, entry_price=self._global_state.price)) self._internal_state.collateral -= np.abs(amount_in_product * self.TRADING_FEE * self._global_state.price) self._clearing() # consider only one position for simplicity
@property def pnl(self) -> float: """ Calculates the total profit and loss (PNL) of all positions. PNL is a sum of PNLs of all positions. Returns: float: The total PNL. """ return sum(pos.pnl(self._global_state.price) for pos in self._internal_state.positions) @property def balance(self): """ Calculates the current balance of the entity. Balance is a sum of the collateral and the PNL. Returns: float: The current balance. """ return self._internal_state.collateral + self.pnl @property def size(self): """ Calculates the total size of all positions. Size is a sum of all position amounts. Returns: float: The total size. """ return sum(pos.amount for pos in self._internal_state.positions) @property def leverage(self): """ Calculates the leverage of the entity. The leverage is the absolute ratio of the total position size to the balance. Returns: float: The leverage. """ if self.balance == 0 and self.size == 0: return 0 return np.abs(self.size * self._global_state.price / self.balance) def _check_liquidation(self) -> bool: """ Checks if the entity is at risk of liquidation. Liquidation occurs when the leverage exceeds the liquidation leverage. Returns: bool: True if the entity is at risk of liquidation, False otherwise. """ return self.leverage >= self.LIQUIDATION_LEVERAGE def _clearing(self): """ Performs clearing of the entity's state. It helps to manage only one position for simplicity. """ self._internal_state.collateral = self.balance size = self.size price = self._global_state.price self._internal_state.positions = [GMXV2Position(amount=size, entry_price=price)]
[docs] def update_state(self, state: GMXV2GlobalState, *args, **kwargs) -> None: """ Updates the entity's state with the given global state. 1. Updates the global state. 2. Check liquidation. 3. Settle fundings and borrowings. Args: state (GMXGlobalState): The global state to update with. """ self._global_state = state if self._check_liquidation(): self._internal_state.collateral = 0 self._internal_state.positions = [] # settle fundings and borrowings global_price: float = self._global_state.price current_size: float = self.size if current_size > 0: self._internal_state.collateral += current_size * global_price * self._global_state.funding_rate_long self._internal_state.collateral -= current_size * global_price * self._global_state.borrowing_rate_long elif current_size < 0: self._internal_state.collateral -= current_size * global_price * self._global_state.funding_rate_short self._internal_state.collateral += current_size * global_price * self._global_state.borrowing_rate_short