refactor valkey into objects with health check

This commit is contained in:
2026-05-15 11:34:25 +09:00
parent f5f43be1a1
commit 1fd922d98f
25 changed files with 6461 additions and 9613 deletions

View File

@@ -4,13 +4,15 @@ from typing import Any
import valkey
from pydantic import BaseModel
from datetime import datetime
from sqlalchemy.util.typing import Self
from collections.abc import Sequence, Callable
import modules.utils as utils
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
@@ -88,16 +90,218 @@ class Current_Previous_Value:
self._previous_value = self._value
self._value = v
### Valkey Objects ###
class VK_Check(BaseModel):
status: str = 'HEALTHY' # HEALTHY | UNHEALTHY | DEAD
method: str | None
class VK_Checks(BaseModel):
status: VK_Check = VK_Check(method='check_status')
timestamp: VK_Check = VK_Check(method='check_timestamp')
symbol: VK_Check = VK_Check(method='check_symbol')
async def run_checks(self, args: dict | None = None):
for f in self.model_dump():
method = getattr(getattr(self, f), 'method')
if method is not None:
await getattr(self, method)(args = args)
async def check_status(self, args: dict | None = None) -> None:
# print('checking status')
if self.status.status == '':
self.status.status = 'UNHEALTHY'
else:
self.status.status = self.status.status
async def check_status_nested(self, args: dict | None = None) -> None:
# print('checking status')
if self.status.status == '':
self.status.status = 'UNHEALTHY'
else:
self.status.status = self.status.status
async def check_timestamp(self, args: dict | None = None) -> None:
# print('checking timestamp')
if args is not None:
ts = int(args.get('timestamp', 0))
now = round(datetime.now().timestamp()*1000)
if (now - ts) > 1:
self.timestamp.status = 'UNHEALTHY'
else:
self.timestamp.status = 'HEALTHY'
else:
raise ValueError("Must pass in 'timestamp' arg")
async def check_symbol(self, args: dict | None = None) -> None:
# print('checking symbol')
if args is not None:
symbol = utils.symbol_to_extend_fmt(args.get('algo_symbol', ''))
vk_symbol = utils.symbol_to_extend_fmt(args.get('vk_symbol', ''))
if symbol == vk_symbol:
self.symbol.status = 'HEALTHY'
else:
self.symbol.status = 'UNHEALTHY'
else:
raise ValueError("Must pass in 'algo_symbol' and 'vk_symbol' args")
class VK_Obj(BaseModel):
vk_name: str
timestamp: int = round(datetime.now().timestamp()*1000)
status: str = 'HEALTHY' # HEALTHY | UNHEALTHY | DEAD
checks: VK_Checks = VK_Checks()
data: Any = None
async def get(self, VK_CON: valkey.Valkey) -> None:
print('getting')
vk_get: str = VK_CON.get(self.vk_name) # ty:ignore[invalid-assignment]
vk_dict: dict = json.loads(vk_get)
self.__init__(**vk_dict)
async def set(self, VK_CON: valkey.Valkey, data_override: Any = None) -> None:
print('setting')
if data_override is not None:
self.data = data_override
self.timestamp: int = round(datetime.now().timestamp()*1000)
j: str = self.model_dump_json()
VK_CON.set(self.vk_name, j)
### Valkey Archetypes ###
# Engine - Health
# Engine - Orchestrator
class VK_Orchestrator_Output(VK_Obj):
vk_name: str = 'fr_orchestrator_output'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
# Algo
class VK_Working_Symbol(VK_Obj):
vk_name: str = 'fr_algo_working_symbol'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
class VK_Algo_Status(VK_Obj):
vk_name: str = 'algo_status'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
# User - Orders
class VK_User_Orders_Aster(VK_Obj):
vk_name: str = 'fr_aster_user_orders'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
class VK_User_Orders_Extend(VK_Obj):
vk_name: str = 'fr_extended_user_orders'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
# User - Trades
class VK_User_Trades_Extend(VK_Obj):
vk_name: str = 'fr_extended_user_trades'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
# User - Balances
class VK_User_Balances_Aster(VK_Obj):
vk_name: str = 'fr_aster_user_balances'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
class VK_User_Balances_Extend(VK_Obj):
vk_name: str = 'fr_extended_user_balances'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
# User - Positions
class VK_User_Positions_Aster(VK_Obj):
vk_name: str = 'fr_aster_user_positions'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
class VK_User_Positions_Extend(VK_Obj):
vk_name: str = 'fr_extended_user_positions'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
# Fund Rates
class VK_FR_Aster(VK_Obj):
vk_name: str = 'fund_rate_aster'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
class VK_FR_All_Aster(VK_Obj):
vk_name: str = 'fund_rate_aster_all'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
class VK_FR_Extend(VK_Obj):
vk_name: str = 'fund_rate_extended'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
class VK_FR_All_Extend(VK_Obj):
vk_name: str = 'fund_rate_extended_all'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
# Tickers
class VK_Ticker_Aster(VK_Obj):
vk_name: str = 'fut_ticker_aster'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
class VK_Ticker_Extend(VK_Obj):
vk_name: str = 'fut_ticker_extended'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
# Trades
class VK_Trade_Aster(VK_Obj):
vk_name: str = 'fut_last_trade_aster'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
class VK_Trade_Extend(VK_Obj):
vk_name: str = 'fut_last_trade_extended'
checks: VK_Checks = VK_Checks(
symbol = VK_Check(method = None)
)
### Algo Objects ###
class Algo_Status(BaseModel):
last_update_ts_ms: int
status: str # 'WORKING' | 'STOPPED'
status: str # 'WORKING' | 'STOPPED' ///// # ENUM: 'HEALTHY' | 'UNHEALTHY' | 'DEAD'
expected_alpha: float
model_ratio: float
current_ratio: float
# @dataclass(kw_only=True)
class Algo_Config_Overrides(BaseModel):
Allow_Ordering_Aster: bool
Allow_Ordering_Extend: bool
@@ -107,7 +311,6 @@ class Algo_Config_Overrides(BaseModel):
Flip_Side_For_Testing: bool
# @dataclass(kw_only=True)
class Algo_Config_Config(BaseModel):
Loop_Sleep_Sec: int
Max_Order_Over_Notional_Ratio: float
@@ -119,12 +322,12 @@ class Algo_Config_Config(BaseModel):
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

View File

@@ -2,9 +2,18 @@ import logging
from dotenv import load_dotenv
import requests
import os
import json
from decimal import Decimal
load_dotenv()
class JSONEncoder_Decimal(json.JSONEncoder):
def default(self, o):
if isinstance(o, Decimal):
return float(o)
return super(JSONEncoder_Decimal, self).default(o)
def upsert_list_of_dicts_by_id(list_of_dicts, new_dict, id='id', seq_check_field: str | None = None, reset_seq_id: bool = False) -> list[dict]:
for index, item in enumerate(list_of_dicts):
if item.get(id) == new_dict.get(id):