Files
Funding_Rate/modules/structs.py

304 lines
7.9 KiB
Python

import json
from dataclasses import dataclass, field
from typing import Any
import valkey
from pydantic import BaseModel
from sqlalchemy.util.typing import Self
from collections.abc import Sequence, Callable
def ret_true():
return True
class Locked_Value(Sequence):
def __init__(self, initial_value: Any = None, unlock_func: Callable=ret_true):
self._value: Any = initial_value
self._unlock_func: Callable = unlock_func
self._is_locked: bool = True
def __repr__(self):
return str((self._value, self._is_locked, self._unlock_func))
def __len__(self):
return len((self._value, self._is_locked, self._unlock_func))
def __getitem__(self, index):
return (self._value, self._is_locked, self._unlock_func)[index]
def __str__(self):
return str((self._value))
def unlock(self) -> Self:
if self._unlock_func():
self._is_locked = False
return self
def lock(self) -> Self:
self._is_locked = True
return self
@property
def is_locked(self):
return self._is_locked
@property
def is_unlocked(self):
return not(self._is_locked)
@property
def value(self):
return self._value
@value.setter
def value(self, v):
if not(self._is_locked):
self._value = v
else:
raise ValueError(f'Failed to set value, item is locked: {str(self.__repr__)}')
class Current_Previous_Value:
def __init__(self, value: Any = None, previous_value: Any = None):
self._value: Any = value
self._previous_value: Any = previous_value
def __repr__(self):
return str((self._value, self._previous_value))
def __len__(self):
return len((self._value, self._previous_value))
def __getitem__(self, index):
return (self._value, self._previous_value)[index]
def __str__(self):
return str(self._value)
@property
def value(self):
return self._value
@property
def previous_value(self):
return self._previous_value
@value.setter
def value(self, v):
self._previous_value = self._value
self._value = v
# @dataclass(kw_only=True)
class Algo_Config_Overrides(BaseModel):
Allow_Ordering_Aster: bool
Allow_Ordering_Extend: bool
Allow_Symbol_Change: bool
Flatten_Open_Positions: bool
Flatten_Open_Positions_Opportunistic: bool
Flip_Side_For_Testing: bool
# @dataclass(kw_only=True)
class Algo_Config_Config(BaseModel):
Loop_Sleep_Sec: int
Max_Order_Over_Notional_Ratio: float
Max_Target_Notional: float
Min_Time_To_Funding_Minutes: int
Min_Fund_Rate_Pct_To_Trade: float
Price_Worsener_Aster: int
Price_Worsener_Extend: int
Switch_To_Taker_Seconds: int
Target_Open_Cash_Position: int
# @dataclass(kw_only=True)
class Algo_Config_Logging(BaseModel):
Log_Summary_Each_Loop: bool
Print_Summary_Each_Loop: bool
# @dataclass(kw_only=True)
class Algo_Config(BaseModel):
Updated_Timestamp: int
Config: Algo_Config_Config
Logging: Algo_Config_Logging
Overrides: Algo_Config_Overrides
@dataclass(kw_only=True)
class Flags:
LIQUIDATE_POS_AND_KILL_ALGO_FLAG: bool = False
NET_FUNDING_IS_ZERO: bool = False
@dataclass(kw_only=True)
class Valkey_Stream:
client: valkey.Valkey
channel: str
data: Any = None
none_fill: Any = None
async def update(self):
r: str = self.client.get(name=self.channel) # ty:ignore[invalid-assignment]
self.data = json.loads(s=r) if r is not None else self.none_fill
@dataclass(kw_only=True)
class Position:
market: str
notional: float
qty: float
@dataclass(kw_only=True)
class Open_Positions:
Valkey: Valkey_Stream
Positions: list[Position] = field(default_factory = list)
async def update(self) -> None:
self.Valkey = await self.Valkey.update()
### Collateral ###
@dataclass(kw_only=True)
class Asset:
symbol: str
balance: float
# min_order_qty: float
@dataclass(kw_only=True)
class Collateral:
Valkey: Valkey_Stream
# Last_Updated_Ts_Ms: int
# Last_Pulled_Ts_Ms: int
Assets: list[Asset] = field(default_factory = list)
async def update(self) -> None:
self.Valkey = await self.Valkey.update()
### Orders ###
@dataclass(kw_only=True)
class Order:
symbol: str
order_id: str
client_order_id: str
side: str
order_type: str
original_qty: float
original_price: float
order_status: str
last_filled_qty: float
last_filled_price: float
commission: float
trade_is_maker: bool
@dataclass(kw_only=True)
class Order_Updates:
# Last_Updated_Ts_Ms: int
# Last_Pulled_Ts_Ms: int
Valkey: Valkey_Stream
Orders: list[Order] = field(default_factory = list)
async def update(self) -> None:
self.Valkey = await self.Valkey.update()
### Funding Rate ###
@dataclass(kw_only=True)
class Funding_Rate:
# Last_Updated_Ts_Ms: int
# Last_Pulled_Ts_Ms: int
Valkey: Valkey_Stream
timestamp_arrival: int
timestamp_msg: int
symbol: str
funding_rate: float
next_funding_time_ts_ms: int
mark_price: float
index_price: float
estimated_settle_price: float
async def update(self) -> None:
self.Valkey = await self.Valkey.update()
### Markets Info ###
@dataclass(kw_only=True)
class Market:
symbol: str
min_order_qty: float
@dataclass(kw_only=True)
class Markets_Details:
Markets: list[Market] = field(default_factory=list)
### Exchanges ###
@dataclass(kw_only=True)
class Perpetual_Exchange:
# Order_Updates: Order_Updates
# Position_Updates: Open_Positions
# Collateral_Updates: Collateral
# Funding_Rate: Funding_Rate
# Markets: Markets_Details
lh_asset: str
rh_asset: str
symbol: str = ''
symbol_asset_separator: str = ''
mult: int
initial_funding_rate: float = 0
min_price: float = 0
min_order_size: float = 0
min_lot_size: float = 0
min_notional: float = 0
buy_ratio: float = 0
notional_obj: dict = field(default_factory=dict)
notional_position: float = 0
unrealized_pnl: float = 0
buy_ratio_std: float = 0
just_rejected_count: int = 0
# async def update(self):
# await self.Collateral_Updates.update()
# await self.Order_Updates.update()
# await self.Position_Updates.update()
# await self.Funding_Rate.update()
def __post_init__(self) -> None:
self.symbol = f'{self.lh_asset.upper()}{self.symbol_asset_separator}{self.rh_asset.upper()}'
# @dataclass(kw_only=True)
# class Aster(Perpetual_Exchange):
# name: str = 'Aster'
# lh_asset: str = 'ETH'
# rh_asset: str = 'USDT'
# def __post_init__(self):
# super().__post_init__()
# self.Order_Updates = Order_Updates(Valkey=Valkey_Stream(channel = 'fr_aster_user_balances', none_fills = []))
# self.Collateral_Updates = Collateral(Valkey=Valkey_Stream(channel = 'fr_aster_user_orders', none_fills = []))
# self.Position_Updates = Open_Positions(Valkey=Valkey_Stream(channel = 'fr_aster_user_positions', none_fills = []))
# self.Funding_Rate - Funding_Rate(Valkey=Valkey_Stream(channel = 'fund_rate_aster', none_fills = None))
# @dataclass(kw_only=True)
# class Extend(Perpetual_Exchange):
# name: str = 'Extended'
# lh_asset: str = 'ETH'
# rh_asset: str = 'USD'
# symbol_asset_separator: str = '-'
# def __post_init__(self):
# super().__post_init__()
# self.Order_Updates = Order_Updates(Valkey=Valkey_Stream(channel = 'fr_aster_user_balances', none_fills = []))
# self.Collateral_Updates = Collateral(Valkey=Valkey_Stream(channel = 'fr_aster_user_orders', none_fills = []))
# self.Position_Updates = Open_Positions(Valkey=Valkey_Stream(channel = 'fr_aster_user_positions', none_fills = []))
# self.Funding_Rate - Funding_Rate(Valkey=Valkey_Stream(channel = 'fund_rate_aster', none_fills = None))