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

@@ -47,6 +47,7 @@ async def ws_stream():
try: try:
data: dict = json.loads(message) data: dict = json.loads(message)
if data: if data:
VAL_KEY.set(name=VK_CHANNEL, value=json.dumps(obj=data)) VAL_KEY.set(name=VK_CHANNEL, value=json.dumps(obj=data))
# print(f'VK_SAVED: {len(data)}') # print(f'VK_SAVED: {len(data)}')
continue continue

View File

@@ -29,12 +29,12 @@ USE_VK: bool = True
# VK_FUND_RATE = 'fund_rate_extended' # VK_FUND_RATE = 'fund_rate_extended'
VK_FUND_RATE_ALL = 'fund_rate_extended_all' VK_FUND_RATE_ALL = 'fund_rate_extended_all'
CON: AsyncContextManager | None = None CON: AsyncContextManager
VAL_KEY = None VAL_KEY: valkey.Valkey
### Logging ### ### Logging ###
load_dotenv() load_dotenv()
LOG_FILEPATH: str = os.getenv("LOGS_PATH") + '/Fund_Rate_Extended_FR_ALL.log' LOG_FILEPATH: str = f'{os.getenv("LOGS_PATH")}/Fund_Rate_Extended_FR_ALL.log'
### Globals ### ### Globals ###
WSS_URL = "wss://api.starknet.extended.exchange/stream.extended.exchange/v1/funding/" WSS_URL = "wss://api.starknet.extended.exchange/stream.extended.exchange/v1/funding/"
@@ -99,8 +99,8 @@ async def main():
if USE_VK: if USE_VK:
VAL_KEY = valkey.Valkey(host='localhost', port=6379, db=0) VAL_KEY = valkey.Valkey(host='localhost', port=6379, db=0)
else: else:
VAL_KEY = None
logging.warning("VALKEY NOT BEING USED, NO DATA WILL BE PUBLISHED") logging.warning("VALKEY NOT BEING USED, NO DATA WILL BE PUBLISHED")
raise NotImplementedError('Cannot run without VK')
if USE_DB: if USE_DB:
engine = create_async_engine('mysql+asyncmy://root:pwd@localhost/fund_rate') engine = create_async_engine('mysql+asyncmy://root:pwd@localhost/fund_rate')
@@ -108,9 +108,8 @@ async def main():
# await create_rtds_btcusd_table(CON=CON) # await create_rtds_btcusd_table(CON=CON)
await ws_stream() await ws_stream()
else: else:
CON = None
logging.warning("DATABASE NOT BEING USED, NO DATA WILL BE RECORDED") logging.warning("DATABASE NOT BEING USED, NO DATA WILL BE RECORDED")
await ws_stream() raise NotImplementedError('DB not implemented')
if __name__ == '__main__': if __name__ == '__main__':

1136
algo.ipynb

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
{ {
"Updated_Timestamp": 1778178429902, "Updated_Timestamp": 1778798867547,
"Config": { "Config": {
"Loop_Sleep_Sec": 0.0, "Loop_Sleep_Sec": 0.0,
"Max_Order_Over_Notional_Ratio": 1.05, "Max_Order_Over_Notional_Ratio": 1.5,
"Max_Target_Notional": 0.0, "Max_Target_Notional": 0.0,
"Min_Time_To_Funding_Minutes": 57, "Min_Time_To_Funding_Minutes": 57,
"Min_Fund_Rate_Pct_To_Trade": 0.0, "Min_Fund_Rate_Pct_To_Trade": 0.0,

View File

@@ -8,6 +8,9 @@ from datetime import datetime
import valkey import valkey
from dotenv import load_dotenv from dotenv import load_dotenv
import modules.utils as utils import modules.utils as utils
import modules.structs as structs
from pathlib import Path
''' '''
TO DO: TO DO:
@@ -18,15 +21,24 @@ TO DO:
VK_IN: str = 'fr_orchestrator_input' VK_IN: str = 'fr_orchestrator_input'
VK_OUT: str = 'fr_orchestrator_output' VK_OUT: str = 'fr_orchestrator_output'
CONFIG_FILEPATH: str = '/algo_local_drive/algo_config.json'
# CONFIG_FILEPATH: str = 'algo_config.json'
### Logging ### ### Logging ###
load_dotenv() load_dotenv()
LOG_FILEPATH: str = f'{os.getenv("LOGS_PATH")}/Fund_Rate_Algo_Orchestrator.log' LOG_FILEPATH: str = f'{os.getenv("LOGS_PATH")}/Fund_Rate_Algo_Orchestrator.log'
async def main() -> None: async def main() -> None:
VAL_KEY: valkey.Valkey = valkey.Valkey(host='localhost', port=6379, db=0, decode_responses=True) VAL_KEY: valkey.Valkey = valkey.Valkey(host='localhost', port=6379, db=0, decode_responses=True)
CONFIG_FILEPATH: str = '/algo_local_drive/algo_config.json'
if not Path(CONFIG_FILEPATH).exists():
CONFIG_FILEPATH: str = 'algo_config.json'
# Init Load Config File
with open(file=CONFIG_FILEPATH, mode='r', encoding='utf-8') as f:
Algo_Config: dict = json.load(fp=f)
Algo_Config['Updated_Timestamp'] = round(number=datetime.now().timestamp()*1000)
# vk = structs.VK_Obj(vk_name = 'fr_orchestrator_output', data=Algo_Config)
vk = structs.VK_Orchestrator_Out()
await vk.set(VK_CON=VAL_KEY)
try: try:
VK_PUBSUB: valkey.client.PubSub = VAL_KEY.pubsub() VK_PUBSUB: valkey.client.PubSub = VAL_KEY.pubsub()
@@ -46,11 +58,16 @@ async def main() -> None:
Algo_Config: dict = json.load(fp=f) Algo_Config: dict = json.load(fp=f)
Algo_Config['Updated_Timestamp'] = timestamp Algo_Config['Updated_Timestamp'] = timestamp
if not Algo_Config:
raise ValueError(f'Algo Orchestrator, config is none: {Algo_Config}')
# Update Config w Update Data # Update Config w Update Data
Algo_Config: dict = utils.rec_set_dict(orig_dict=Algo_Config, new_dict=data) Algo_Config: dict = utils.rec_set_dict(orig_dict=Algo_Config, new_dict=data)
# Set VK KV w Updated Config # Set VK KV w Updated Config
VAL_KEY.set(name=VK_OUT, value=json.dumps(obj=Algo_Config))
# vk = structs.VK_Obj(vk_name = 'fr_orchestrator_output', data=Algo_Config)
vk = structs.VK_Orchestrator_Out()
await vk.set(VK_CON=VAL_KEY)
# Save Updated Config to File # Save Updated Config to File
with open(file=CONFIG_FILEPATH, mode='w', encoding='utf-8') as f: with open(file=CONFIG_FILEPATH, mode='w', encoding='utf-8') as f:

View File

@@ -2,7 +2,7 @@
"cells": [ "cells": [
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 4, "execution_count": 1,
"id": "3a269644", "id": "3a269644",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
@@ -12,7 +12,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 9, "execution_count": 2,
"id": "4395fabb", "id": "4395fabb",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
@@ -92,7 +92,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 10, "execution_count": 3,
"id": "2122885a", "id": "2122885a",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
@@ -102,7 +102,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 11, "execution_count": 4,
"id": "e7341726", "id": "e7341726",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
@@ -352,7 +352,7 @@
" {'symbol': 'CHIPUSDT',\n", " {'symbol': 'CHIPUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n", " 'markPrice': '0.05884243',\n",
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '50',\n", " 'leverage': '50',\n",
@@ -363,7 +363,7 @@
" 'positionSide': 'BOTH',\n", " 'positionSide': 'BOTH',\n",
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 1778212620637},\n",
" {'symbol': '1000BONKUSDT',\n", " {'symbol': '1000BONKUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -619,6 +619,21 @@
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'MINIMAXUSDT',\n",
" 'positionAmt': '0.0000',\n",
" 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n",
" 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n",
" 'leverage': '2',\n",
" 'maxNotionalValue': '80000',\n",
" 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n",
" 'notional': '0',\n",
" 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n",
" {'symbol': 'AIOTUSDT',\n", " {'symbol': 'AIOTUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -1132,18 +1147,18 @@
" {'symbol': 'HYPEUSDT',\n", " {'symbol': 'HYPEUSDT',\n",
" 'positionAmt': '0.00',\n", " 'positionAmt': '0.00',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n", " 'markPrice': '42.14600000',\n",
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '300',\n", " 'leverage': '150',\n",
" 'maxNotionalValue': '1000',\n", " 'maxNotionalValue': '2000',\n",
" 'marginType': 'cross',\n", " 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n", " 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n", " 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n", " 'positionSide': 'BOTH',\n",
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 1778174116771},\n",
" {'symbol': 'SYRUPUSDT',\n", " {'symbol': 'SYRUPUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -1436,7 +1451,7 @@
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '2',\n", " 'leverage': '2',\n",
" 'maxNotionalValue': '2000000',\n", " 'maxNotionalValue': '5000000',\n",
" 'marginType': 'cross',\n", " 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n", " 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n", " 'isAutoAddMargin': 'false',\n",
@@ -1552,7 +1567,7 @@
" {'symbol': 'SOLUSDT',\n", " {'symbol': 'SOLUSDT',\n",
" 'positionAmt': '0.00',\n", " 'positionAmt': '0.00',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n", " 'markPrice': '88.08336403',\n",
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '100',\n", " 'leverage': '100',\n",
@@ -2182,7 +2197,7 @@
" {'symbol': 'ETHUSDT',\n", " {'symbol': 'ETHUSDT',\n",
" 'positionAmt': '0.000',\n", " 'positionAmt': '0.000',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
" 'markPrice': '2327.94576938',\n", " 'markPrice': '2278.29032657',\n",
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '150',\n", " 'leverage': '150',\n",
@@ -2193,7 +2208,7 @@
" 'positionSide': 'BOTH',\n", " 'positionSide': 'BOTH',\n",
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 1777835550378},\n", " 'updateTime': 1778127016832},\n",
" {'symbol': 'ZKCUSDT',\n", " {'symbol': 'ZKCUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -2332,7 +2347,7 @@
" {'symbol': 'XAGUSDT',\n", " {'symbol': 'XAGUSDT',\n",
" 'positionAmt': '0.000',\n", " 'positionAmt': '0.000',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
" 'markPrice': '75.87000000',\n", " 'markPrice': '79.55000000',\n",
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '100',\n", " 'leverage': '100',\n",
@@ -2351,7 +2366,7 @@
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '20',\n", " 'leverage': '20',\n",
" 'maxNotionalValue': '5000',\n", " 'maxNotionalValue': '500000',\n",
" 'marginType': 'cross',\n", " 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n", " 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n", " 'isAutoAddMargin': 'false',\n",
@@ -2524,6 +2539,21 @@
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'NOTUSDT',\n",
" 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n",
" 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n",
" 'leverage': '3',\n",
" 'maxNotionalValue': '2500000',\n",
" 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n",
" 'notional': '0',\n",
" 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n",
" {'symbol': 'COLLECTUSDT',\n", " {'symbol': 'COLLECTUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -2539,6 +2569,21 @@
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'XIAOMIUSDT',\n",
" 'positionAmt': '0.00',\n",
" 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n",
" 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n",
" 'leverage': '2',\n",
" 'maxNotionalValue': '80000',\n",
" 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n",
" 'notional': '0',\n",
" 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n",
" {'symbol': 'LUNA2USDT',\n", " {'symbol': 'LUNA2USDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -2989,6 +3034,21 @@
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'BABYUSDT',\n",
" 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n",
" 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n",
" 'leverage': '3',\n",
" 'maxNotionalValue': '2500000',\n",
" 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n",
" 'notional': '0',\n",
" 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n",
" {'symbol': 'AVAXUSDT',\n", " {'symbol': 'AVAXUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -3154,6 +3214,21 @@
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'THETAUSDT',\n",
" 'positionAmt': '0.0',\n",
" 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n",
" 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n",
" 'leverage': '3',\n",
" 'maxNotionalValue': '2500000',\n",
" 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n",
" 'notional': '0',\n",
" 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n",
" {'symbol': 'UNIUSDT',\n", " {'symbol': 'UNIUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -3289,6 +3364,21 @@
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'POPMARTUSDT',\n",
" 'positionAmt': '0.0000',\n",
" 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n",
" 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n",
" 'leverage': '2',\n",
" 'maxNotionalValue': '80000',\n",
" 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n",
" 'notional': '0',\n",
" 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n",
" {'symbol': 'ARTXUSDT',\n", " {'symbol': 'ARTXUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -3304,6 +3394,21 @@
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'CARDSUSDT',\n",
" 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n",
" 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n",
" 'leverage': '2',\n",
" 'maxNotionalValue': '80000',\n",
" 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n",
" 'notional': '0',\n",
" 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n",
" {'symbol': 'SXPUSDT',\n", " {'symbol': 'SXPUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -3394,6 +3499,21 @@
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'AGTUSDT',\n",
" 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n",
" 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n",
" 'leverage': '3',\n",
" 'maxNotionalValue': '2500000',\n",
" 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n",
" 'notional': '0',\n",
" 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n",
" {'symbol': 'CHZUSDT',\n", " {'symbol': 'CHZUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -3562,7 +3682,7 @@
" {'symbol': 'INITUSDT',\n", " {'symbol': 'INITUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
" 'markPrice': '0.08765944',\n", " 'markPrice': '0.11143193',\n",
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '50',\n", " 'leverage': '50',\n",
@@ -3697,7 +3817,7 @@
" {'symbol': 'BNBUSDT',\n", " {'symbol': 'BNBUSDT',\n",
" 'positionAmt': '0.00',\n", " 'positionAmt': '0.00',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
" 'markPrice': '619.51000000',\n", " 'markPrice': '639.19000000',\n",
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '100',\n", " 'leverage': '100',\n",
@@ -3727,7 +3847,7 @@
" {'symbol': 'XMRUSDT',\n", " {'symbol': 'XMRUSDT',\n",
" 'positionAmt': '0.000',\n", " 'positionAmt': '0.000',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n", " 'markPrice': '390.77183333',\n",
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '50',\n", " 'leverage': '50',\n",
@@ -3817,7 +3937,7 @@
" {'symbol': 'DOGEUSDT',\n", " {'symbol': 'DOGEUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n", " 'markPrice': '0.10631546',\n",
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '75',\n", " 'leverage': '75',\n",
@@ -3828,7 +3948,7 @@
" 'positionSide': 'BOTH',\n", " 'positionSide': 'BOTH',\n",
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 1778098357669},\n",
" {'symbol': 'GPSUSDT',\n", " {'symbol': 'GPSUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -3971,7 +4091,7 @@
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '2',\n", " 'leverage': '2',\n",
" 'maxNotionalValue': '60000',\n", " 'maxNotionalValue': '5000000',\n",
" 'marginType': 'cross',\n", " 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n", " 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n", " 'isAutoAddMargin': 'false',\n",
@@ -4039,6 +4159,21 @@
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'DOGSUSDT',\n",
" 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n",
" 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n",
" 'leverage': '3',\n",
" 'maxNotionalValue': '2500000',\n",
" 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n",
" 'notional': '0',\n",
" 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n",
" {'symbol': 'COSUSDT',\n", " {'symbol': 'COSUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -4174,6 +4309,21 @@
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'SOXLUSDT',\n",
" 'positionAmt': '0.0000',\n",
" 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n",
" 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n",
" 'leverage': '2',\n",
" 'maxNotionalValue': '2000000',\n",
" 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n",
" 'notional': '0',\n",
" 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n",
" {'symbol': 'HEMIUSDT',\n", " {'symbol': 'HEMIUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -4346,7 +4496,7 @@
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '20',\n", " 'leverage': '20',\n",
" 'maxNotionalValue': '25000',\n", " 'maxNotionalValue': '500000',\n",
" 'marginType': 'cross',\n", " 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n", " 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n", " 'isAutoAddMargin': 'false',\n",
@@ -5047,7 +5197,7 @@
" {'symbol': '4USDT',\n", " {'symbol': '4USDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n", " 'markPrice': '0.01297365',\n",
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '50',\n", " 'leverage': '50',\n",
@@ -5058,7 +5208,7 @@
" 'positionSide': 'BOTH',\n", " 'positionSide': 'BOTH',\n",
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 1777965261969},\n",
" {'symbol': 'BELUSDT',\n", " {'symbol': 'BELUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -5330,20 +5480,20 @@
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'LITUSDT',\n", " {'symbol': 'LITUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '179',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.9766',\n",
" 'markPrice': '0.93992527',\n", " 'markPrice': '0.97480000',\n",
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '-0.32220000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0.68966790',\n",
" 'leverage': '50',\n", " 'leverage': '50',\n",
" 'maxNotionalValue': '2500',\n", " 'maxNotionalValue': '2500',\n",
" 'marginType': 'cross',\n", " 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n", " 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n", " 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n", " 'positionSide': 'BOTH',\n",
" 'notional': '0',\n", " 'notional': '174.48920000',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 1777819835027},\n", " 'updateTime': 1778213915476},\n",
" {'symbol': 'FFUSDT',\n", " {'symbol': 'FFUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -5389,6 +5539,21 @@
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'STEEMUSDT',\n",
" 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n",
" 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n",
" 'leverage': '3',\n",
" 'maxNotionalValue': '2500000',\n",
" 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n",
" 'notional': '0',\n",
" 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n",
" {'symbol': 'BMTUSDT',\n", " {'symbol': 'BMTUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -5509,6 +5674,21 @@
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'DRAMUSDT',\n",
" 'positionAmt': '0.00',\n",
" 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n",
" 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n",
" 'leverage': '2',\n",
" 'maxNotionalValue': '80000',\n",
" 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n",
" 'notional': '0',\n",
" 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n",
" {'symbol': 'BOBUSDT',\n", " {'symbol': 'BOBUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -5947,7 +6127,7 @@
" {'symbol': 'WLFIUSDT',\n", " {'symbol': 'WLFIUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
" 'markPrice': '0.05870000',\n", " 'markPrice': '0.07422827',\n",
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '25',\n", " 'leverage': '25',\n",
@@ -6154,6 +6334,21 @@
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'BILLUSDT',\n",
" 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n",
" 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n",
" 'leverage': '3',\n",
" 'maxNotionalValue': '2500000',\n",
" 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n",
" 'notional': '0',\n",
" 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n",
" {'symbol': 'CYBERUSDT',\n", " {'symbol': 'CYBERUSDT',\n",
" 'positionAmt': '0',\n", " 'positionAmt': '0',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -6607,7 +6802,7 @@
" {'symbol': 'BTCUSDT',\n", " {'symbol': 'BTCUSDT',\n",
" 'positionAmt': '0.000',\n", " 'positionAmt': '0.000',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
" 'markPrice': '78659.35912065',\n", " 'markPrice': '79558.40000000',\n",
" 'unRealizedProfit': '0.00000000',\n", " 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n", " 'liquidationPrice': '0',\n",
" 'leverage': '150',\n", " 'leverage': '150',\n",
@@ -6664,6 +6859,21 @@
" 'notional': '0',\n", " 'notional': '0',\n",
" 'isolatedWallet': '0',\n", " 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n", " 'updateTime': 0},\n",
" {'symbol': 'TENCENTUSDT',\n",
" 'positionAmt': '0.0000',\n",
" 'entryPrice': '0.0',\n",
" 'markPrice': '0.00000000',\n",
" 'unRealizedProfit': '0.00000000',\n",
" 'liquidationPrice': '0',\n",
" 'leverage': '2',\n",
" 'maxNotionalValue': '80000',\n",
" 'marginType': 'cross',\n",
" 'isolatedMargin': '0.00000000',\n",
" 'isAutoAddMargin': 'false',\n",
" 'positionSide': 'BOTH',\n",
" 'notional': '0',\n",
" 'isolatedWallet': '0',\n",
" 'updateTime': 0},\n",
" {'symbol': 'HOODUSDT',\n", " {'symbol': 'HOODUSDT',\n",
" 'positionAmt': '0.00',\n", " 'positionAmt': '0.00',\n",
" 'entryPrice': '0.0',\n", " 'entryPrice': '0.0',\n",
@@ -6876,7 +7086,7 @@
" 'updateTime': 0}]" " 'updateTime': 0}]"
] ]
}, },
"execution_count": 11, "execution_count": 4,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }

View File

@@ -9,7 +9,7 @@ services:
context: ./ context: ./
dockerfile: ./algo/Dockerfile dockerfile: ./algo/Dockerfile
volumes: volumes:
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data - /root/data:/root/data:rw # Read-write access to data
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data - /root/logs:/root/logs:rw # Read-write access to data
- ./:/algo_local_drive:rw # Read-write access to data - ./:/algo_local_drive:rw # Read-write access to data
network_mode: "host" network_mode: "host"

View File

@@ -1,4 +1,4 @@
# tail -f Fund_Rate_Aster_FR_ALL.log Fund_Rate_Extended_FR_ALL.log Fund_Rate_Engine_BFR.log Fund_Rate_Algo_Orchestrator.log Fund_Rate_Algo.log Fund_Rate_Aster_User.log Fund_Rate_Aster.log Fund_Rate_Extended_FR.log Fund_Rate_Extended_OB.log Fund_Rate_Extended_User.log # tail -f Fund_Rate_Algo.log Fund_Rate_Engine_BFR.log Fund_Rate_Algo_Orchestrator.log Fund_Rate_Aster_User.log Fund_Rate_Aster.log Fund_Rate_Extended_FR.log Fund_Rate_Extended_OB.log Fund_Rate_Extended_User.log
services: services:
# algo: # algo:
@@ -9,126 +9,106 @@ services:
# dockerfile: ./algo/Dockerfile # dockerfile: ./algo/Dockerfile
# depends_on: # depends_on:
# - algo_orchestrator # - algo_orchestrator
# - engine_best_funding_rate
# - ws_aster # - ws_aster
# - ws_aster_user # - ws_aster_user
# - ws_extended_fund_rate # - ws_extended_fund_rate
# - ws_extended_orderbook # - ws_extended_orderbook
# - ws_extended_trades
# - ws_extended_user # - ws_extended_user
# volumes: # volumes:
# - /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data # - /root/data:/root/data:rw # Read-write access to data
# - /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data # - /root/logs:/root/logs:rw # Read-write access to data
# network_mode: "host" # network_mode: "host"
algo_orchestrator: algo_orchestrator:
container_name: algo_orchestrator container_name: algo_orchestrator
restart: "unless-stopped" restart: "no"
build: build:
context: ./ context: ./
dockerfile: ./algo_orchestrator/Dockerfile dockerfile: ./algo_orchestrator/Dockerfile
volumes: volumes:
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data - /root/data:/root/data:rw # Read-write access to data
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data - /root/logs:/root/logs:rw # Read-write access to data
- ./:/algo_local_drive:rw # Read-write access to data - ./:/algo_local_drive:rw # Read-write access to data
network_mode: "host" network_mode: "host"
engine_best_funding_rate: engine_best_funding_rate:
container_name: engine_best_funding_rate container_name: engine_best_funding_rate
restart: "unless-stopped" restart: "no"
build: build:
context: ./ context: ./
dockerfile: ./engine_best_funding_rate/Dockerfile dockerfile: ./engine_best_funding_rate/Dockerfile
volumes: volumes:
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data - /root/data:/root/data:rw # Read-write access to data
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data - /root/logs:/root/logs:rw # Read-write access to data
network_mode: "host"
ws_extended_fund_rate_all:
container_name: ws_extended_fund_rate_all
restart: "unless-stopped"
build:
context: ./
dockerfile: ./ws_extended_fund_rate_all/Dockerfile
volumes:
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
network_mode: "host"
ws_aster_fund_rate_all:
container_name: ws_aster_fund_rate_all
restart: "unless-stopped"
build:
context: ./
dockerfile: ./ws_aster_fund_rate_all/Dockerfile
volumes:
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
network_mode: "host" network_mode: "host"
ws_aster: ws_aster:
container_name: ws_aster container_name: ws_aster
restart: "unless-stopped" restart: "no"
build: build:
context: ./ context: ./
dockerfile: ./ws_aster/Dockerfile dockerfile: ./ws_aster/Dockerfile
volumes: volumes:
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data - /root/data:/root/data:rw # Read-write access to data
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data - /root/logs:/root/logs:rw # Read-write access to data
network_mode: "host" network_mode: "host"
ws_aster_user: ws_aster_user:
container_name: ws_aster_user container_name: ws_aster_user
restart: "unless-stopped" restart: "no"
build: build:
context: ./ context: ./
dockerfile: ./ws_aster_user/Dockerfile dockerfile: ./ws_aster_user/Dockerfile
volumes: volumes:
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data - /root/data:/root/data:rw # Read-write access to data
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data - /root/logs:/root/logs:rw # Read-write access to data
network_mode: "host" network_mode: "host"
ws_extended_fund_rate: ws_extended_fund_rate:
container_name: ws_extended_fund_rate container_name: ws_extended_fund_rate
restart: "unless-stopped" restart: "no"
build: build:
context: ./ context: ./
dockerfile: ./ws_extended_fund_rate/Dockerfile dockerfile: ./ws_extended_fund_rate/Dockerfile
volumes: volumes:
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data - /root/data:/root/data:rw # Read-write access to data
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data - /root/logs:/root/logs:rw # Read-write access to data
network_mode: "host" network_mode: "host"
ws_extended_orderbook: ws_extended_orderbook:
container_name: ws_extended_orderbook container_name: ws_extended_orderbook
restart: "unless-stopped" restart: "no"
build: build:
context: ./ context: ./
dockerfile: ./ws_extended_orderbook/Dockerfile dockerfile: ./ws_extended_orderbook/Dockerfile
volumes: volumes:
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to dataw - /root/data:/root/data:rw # Read-write access to dataw
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data - /root/logs:/root/logs:rw # Read-write access to data
network_mode: "host" network_mode: "host"
# ws_extended_trades:
# container_name: ws_extended_trades
# restart: "unless-stopped"
# build:
# context: ./
# dockerfile: ./ws_extended_trades/Dockerfile
# volumes:
# - /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to dataw
# - /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
# network_mode: "host"
ws_extended_user: ws_extended_user:
container_name: ws_extended_user container_name: ws_extended_user
restart: "unless-stopped" restart: "no"
build: build:
context: ./ context: ./
dockerfile: ./ws_extended_user/Dockerfile dockerfile: ./ws_extended_user/Dockerfile
volumes: volumes:
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data - /root/data:/root/data:rw # Read-write access to data
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data - /root/logs:/root/logs:rw # Read-write access to data
network_mode: "host" network_mode: "host"
# ws_extended_trades:
# container_name: ws_extended_trades
# restart: "no"
# build:
# context: ./
# dockerfile: ./ws_extended_trades/Dockerfile
# volumes:
# - /root/data:/root/data:rw # Read-write access to dataw
# - /root/logs:/root/logs:rw # Read-write access to data
# network_mode: "host"
# ng: # ng:
# container_name: ng # container_name: ng
# restart: "unless-stopped" # restart: "no"
# build: # build:
# context: ./ # context: ./
# dockerfile: ./ng/Dockerfile # dockerfile: ./ng/Dockerfile
# volumes: # volumes:
# - /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data # - /root/data:/root/data:rw # Read-write access to data
# - /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data # - /root/logs:/root/logs:rw # Read-write access to data
# network_mode: "host" # network_mode: "host"

File diff suppressed because one or more lines are too long

View File

@@ -93,7 +93,10 @@ async def get_aster_exch_info() -> pd.DataFrame:
return df return df
def load_aster_current_fr(df_aster_exch_info: pd.DataFrame) -> pd.DataFrame: def load_aster_current_fr(df_aster_exch_info: pd.DataFrame) -> pd.DataFrame:
df = pd.DataFrame(data=json.loads(s=VAL_KEY.get(name='fund_rate_aster_all'))) # ty:ignore[invalid-argument-type] vk_get: str = VAL_KEY.get(name='fund_rate_aster_all') # ty:ignore[invalid-assignment]
if not vk_get:
raise ValueError(f'fund_rate_aster_all is empty: {vk_get}')
df = pd.DataFrame(data=json.loads(vk_get))
df: pd.DataFrame = df[['s','E','r','T']].rename({'s':'symbol','E':'funding_rate_updated_ts_ms','r':'funding_rate','T':'next_funding_ts'}, axis=1) df: pd.DataFrame = df[['s','E','r','T']].rename({'s':'symbol','E':'funding_rate_updated_ts_ms','r':'funding_rate','T':'next_funding_ts'}, axis=1)
df['funding_rate_updated_dt'] = pd.to_datetime(df['funding_rate_updated_ts_ms'], unit='ms') df['funding_rate_updated_dt'] = pd.to_datetime(df['funding_rate_updated_ts_ms'], unit='ms')
@@ -190,9 +193,6 @@ async def loop() -> None:
'net_mult_x_net_fr_abs','net_funding_rate_abs','net_funding_rate','next_funding_at_same_time','last_trade_ts_ast'] 'net_mult_x_net_fr_abs','net_funding_rate_abs','net_funding_rate','next_funding_at_same_time','last_trade_ts_ast']
].sort_values(by='net_mult_x_net_fr_abs', ascending=False).reset_index(drop=True) ].sort_values(by='net_mult_x_net_fr_abs', ascending=False).reset_index(drop=True)
# min_daily_volume = 100_000
# df_best_fr_rate = df_best_fr_rate.loc[ (df_best_fr_rate['daily_volume_ast']>=min_daily_volume) & (df_best_fr_rate['daily_volume_ext']>min_daily_volume) ,:].reset_index(drop=True)
last_trade_max_ts = [] last_trade_max_ts = []
for index, row in df_best_fr_rate.iterrows(): for index, row in df_best_fr_rate.iterrows():
r = json.loads(requests.get(f'https://api.starknet.extended.exchange/api/v1/info/markets/{row['symbol_ext']}/trades').text) r = json.loads(requests.get(f'https://api.starknet.extended.exchange/api/v1/info/markets/{row['symbol_ext']}/trades').text)
@@ -204,22 +204,42 @@ async def loop() -> None:
df_best_fr_rate['last_trade_ts_dt_ast'] = pd.to_datetime(df_best_fr_rate['last_trade_ts_ast'], unit='ms') df_best_fr_rate['last_trade_ts_dt_ast'] = pd.to_datetime(df_best_fr_rate['last_trade_ts_ast'], unit='ms')
df_best_fr_rate['last_trade_ts_dt_ext'] = pd.to_datetime(df_best_fr_rate['last_trade_ts_ext'], unit='ms') df_best_fr_rate['last_trade_ts_dt_ext'] = pd.to_datetime(df_best_fr_rate['last_trade_ts_ext'], unit='ms')
df_best_fr_rate = df_best_fr_rate.loc[( (datetime.now().timestamp()*1000 )-df_best_fr_rate['last_trade_ts_ast']) < (5*60*1000) ] # Last traded in 3min
# df_best_fr_rate = df_best_fr_rate.loc[( (datetime.now().timestamp()*1000 )-df_best_fr_rate['last_trade_ts_ext']) < (15*60*1000) ] # Last traded in 15min
# print(df_best_fr_rate.columns)
# print(df_best_fr_rate.iloc[0])
candles_ratios = [] candles_ratios = []
for index, row in df_best_fr_rate.iterrows(): for index, row in df_best_fr_rate.iterrows():
try:
df = await get_candles(symbol=row['symbol_ext']) df = await get_candles(symbol=row['symbol_ext'])
except Exception as e:
logging.warning(f'BFR failed to get candles...sleeping and retrying: {e}')
time.sleep(5)
df = await get_candles(symbol=row['symbol_ext'])
buy_ratio_ext = float(df['med_ratio_aster_over_extend'].median()) buy_ratio_ext = float(df['med_ratio_aster_over_extend'].median())
buy_ratio_std = float(df['med_ratio_aster_over_extend'].std()) buy_ratio_std = float(df['med_ratio_aster_over_extend'].std())
candles_ratios.append({'symbol_ext':row['symbol_ext'], 'buy_ratio_std': buy_ratio_std, 'buy_ratio_ext':buy_ratio_ext,'buy_ratio_ast':buy_ratio_ext*-1}) candles_ratios.append({'symbol_ext':row['symbol_ext'], 'buy_ratio_std': buy_ratio_std, 'buy_ratio_ext':buy_ratio_ext,'buy_ratio_ast':buy_ratio_ext*-1})
df_best_fr_rate = df_best_fr_rate.merge(pd.DataFrame(candles_ratios), on='symbol_ext', how='left') df_best_fr_rate = df_best_fr_rate.merge(pd.DataFrame(candles_ratios), on='symbol_ext', how='left')
### Set Unfiltered Master Data ###
master_data = df_best_fr_rate[
['symbol_ast','max_leverage_ast','lh_asset_ast','rh_asset_ast','funding_rate_ast','min_price_ast','min_order_size_ast','min_lot_size_ast','min_notional_ast','buy_ratio_ast',
'symbol_ext','max_leverage_ext','lh_asset_ext','rh_asset_ext','funding_rate_ext','min_price_ext','min_order_size_ext','min_lot_size_ext','min_notional_ext','buy_ratio_ext', 'buy_ratio_std','next_funding_at_same_time']
].to_json(orient='records')
VAL_KEY.set(name='fr_engine_best_fund_rate_master', value=str(master_data))
### Filter BFR Data ###
df_best_fr_rate = df_best_fr_rate.loc[( (datetime.now().timestamp()*1000 )-df_best_fr_rate['last_trade_ts_ast']) < (5*60*1000) ] # Last traded in 3min
df_best_fr_rate = df_best_fr_rate.loc[( (datetime.now().timestamp()*1000 )-df_best_fr_rate['last_trade_ts_ext']) < (15*60*1000) ] # Last traded in 15min
min_daily_volume = 100_000
df_best_fr_rate = df_best_fr_rate.loc[ (df_best_fr_rate['daily_volume_ast']>=min_daily_volume) & (df_best_fr_rate['daily_volume_ext']>min_daily_volume) ,:].reset_index(drop=True)
# print(df_best_fr_rate.columns)
# print(df_best_fr_rate.iloc[0])
if len(df_best_fr_rate) < 1: if len(df_best_fr_rate) < 1:
raise ValueError(f'NO BFR RATE: {df_best_fr_rate}') raise ValueError(f'NO BFR RATE: {df_best_fr_rate}')
@@ -259,12 +279,7 @@ async def loop() -> None:
best_next_funding_pair: dict[str, dict] = {'ASTER': asdict(obj=ASTER), 'EXTEND': asdict(obj=EXTEND)} best_next_funding_pair: dict[str, dict] = {'ASTER': asdict(obj=ASTER), 'EXTEND': asdict(obj=EXTEND)}
VAL_KEY.set(name='fr_engine_best_fund_rate_output', value=json.dumps(obj=best_next_funding_pair)) VAL_KEY.set(name='fr_engine_best_fund_rate_output', value=json.dumps(obj=best_next_funding_pair))
master_data = df_best_fr_rate[
['symbol_ast','max_leverage_ast','lh_asset_ast','rh_asset_ast','funding_rate_ast','min_price_ast','min_order_size_ast','min_lot_size_ast','min_notional_ast','buy_ratio_ast',
'symbol_ext','max_leverage_ext','lh_asset_ext','rh_asset_ext','funding_rate_ext','min_price_ext','min_order_size_ext','min_lot_size_ext','min_notional_ext','buy_ratio_ext', 'buy_ratio_std','next_funding_at_same_time']
].to_json(orient='records')
VAL_KEY.set(name='fr_engine_best_fund_rate_master', value=str(master_data))
print(df_best_fr_rate[['symbol_ext','max_leverage_ext','buy_ratio_ext','net_funding_rate','daily_volume_ast','buy_ratio_ast']].head(10)) print(df_best_fr_rate[['symbol_ext','max_leverage_ext','buy_ratio_ext','net_funding_rate','daily_volume_ast','buy_ratio_ast']].head(10))
logging.info(f'BFR REFRESHED @ {datetime.now()}') logging.info(f'BFR REFRESHED @ {datetime.now()}')
time.sleep(LOOP_SLEEP_SEC) time.sleep(LOOP_SLEEP_SEC)

View File

@@ -0,0 +1,129 @@
import asyncio
import json
import logging
import os
import traceback
from datetime import datetime
import time
import valkey
from dotenv import load_dotenv
import modules.utils as utils
import modules.structs as structs
from pydantic import BaseModel
import docker
### Database ###
VAL_KEY: valkey.Valkey = valkey.Valkey(host='localhost', port=6379, db=0, decode_responses=True)
DOCKER = docker.from_env()
### Logging ###
load_dotenv()
LOG_FILEPATH: str = f'{os.getenv("LOGS_PATH")}/Fund_Rate_Engine_Health.log'
### CONSTANTS ###
MAX_TIME_SINCE_LAST_UPDATE_MS: int = 1000 * 60 * 3 # 1000 x 60 sec x [minutes]
LOOP_SLEEP_SEC: int = 5
### Globals ###
### Structs ###
class Health_Status(BaseModel):
status: str # ENUM: 'HEALTHY' | 'UNHEALTHY' | 'DEAD'
timestamp: int
vk_objs: list[structs.VK_Obj]
async def get_algo_working_symbol() -> str:
vk_get: str = VAL_KEY.get(name='fr_algo_working_symbol') # ty:ignore[invalid-assignment]
d = json.loads(vk_get)
algo_symbol: str = d.get('EXTEND', {}).get('symbol', '')
return algo_symbol
async def main() -> None:
vk_objs = [
structs.VK_Orchestrator_Output(),
structs.VK_Working_Symbol(),
structs.VK_User_Orders_Extend(),
structs.VK_User_Trades_Extend(),
structs.VK_User_Balances_Aster(),
structs.VK_User_Balances_Extend(),
structs.VK_User_Positions_Aster(),
structs.VK_User_Positions_Extend(),
structs.VK_FR_Aster(),
structs.VK_FR_All_Aster(),
structs.VK_FR_Extend(),
structs.VK_FR_All_Extend(),
structs.VK_Ticker_Aster(),
structs.VK_Ticker_Extend(),
structs.VK_Trade_Aster(),
structs.VK_Trade_Extend(),
]
health_status = Health_Status(
status = 'HEALTHY',
timestamp = round(number=datetime.now().timestamp()*1000),
vk_objs = vk_objs, # ty:ignore[invalid-argument-type]
)
try:
while True:
algo_symbol = await get_algo_working_symbol()
health_status.timestamp = round(number=datetime.now().timestamp()*1000)
for o in health_status.vk_objs:
vk_symbol = o.data.get('symbol') if isinstance(o.data, dict) else None
await o.checks.run_checks(args={
'timestamp': health_status.timestamp,
'algo_symbol': algo_symbol,
'vk_symbol': vk_symbol,
})
vk_statuses = [o.status for o in health_status.vk_objs]
if 'DEAD' in vk_statuses:
health_status.status = 'DEAD'
elif 'UNHEALTHY' in vk_statuses:
health_status.status = 'UNHEALTHY'
else:
health_status.status = 'HEALTHY'
if health_status.status != 'HEALTHY':
all_containers = DOCKER.containers.list(all=True)
for c in all_containers:
if c.status == 'running':
logging.warning(f"stopping: ID: {c.id}, Name: {c.name}, Status: {c.status}")
container = DOCKER.containers.get(c.id)
container.stop(timeout=10)
logging.info('Stopped all containers')
# VAL_KEY.set(name='health_status', value=json.dumps(obj=(health_status)))
logging.info(vk_statuses)
if LOOP_SLEEP_SEC > 0:
time.sleep(LOOP_SLEEP_SEC)
continue
except KeyboardInterrupt:
logging.info(msg='ORCHESTRATOR SHUTTING DOWN...')
except Exception as e:
logging.error(msg=traceback.format_exc())
logging.critical(msg=f'*** ORCHESTRATOR CRASHED: {e}')
if __name__ == '__main__':
START_TIME: int = round(number=datetime.now().timestamp()*1000)
logging.info(msg=f'Log FilePath: {LOG_FILEPATH}')
logging.basicConfig(
force=True,
filename=LOG_FILEPATH,
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filemode='w'
)
logging.info(msg=f"STARTED: {START_TIME}")
asyncio.run(main())

153
main.py
View File

@@ -24,7 +24,7 @@ import math
import os import os
import time import time
import traceback import traceback
from datetime import datetime, timezone from datetime import datetime
from decimal import ROUND_DOWN, ROUND_UP, ROUND_HALF_UP, Decimal from decimal import ROUND_DOWN, ROUND_UP, ROUND_HALF_UP, Decimal
from typing import AsyncContextManager from typing import AsyncContextManager
from dataclasses import dataclass, asdict, field from dataclasses import dataclass, asdict, field
@@ -82,7 +82,7 @@ async def output_algo_status(status: str) -> None:
Algo_Status.last_update_ts_ms = int(round(datetime.now().timestamp()*1000, 2)) Algo_Status.last_update_ts_ms = int(round(datetime.now().timestamp()*1000, 2))
Algo_Status.status = status Algo_Status.status = status
VAL_KEY.set('algo_status', json.dumps(Algo_Status.model_dump())) VAL_KEY.set('algo_status', json.dumps(Algo_Status.model_dump(), cls=utils.JSONEncoder_Decimal))
def create_exchange_objs_from_dict(exchanges_dict: dict) -> tuple[structs.Perpetual_Exchange, structs.Perpetual_Exchange]: def create_exchange_objs_from_dict(exchanges_dict: dict) -> tuple[structs.Perpetual_Exchange, structs.Perpetual_Exchange]:
Aster = structs.Perpetual_Exchange( Aster = structs.Perpetual_Exchange(
@@ -129,8 +129,8 @@ async def symbol_switch(best_symbol_by_exchange_aster: structs.Perpetual_Exchang
Config.Overrides.Flatten_Open_Positions_Opportunistic = True Config.Overrides.Flatten_Open_Positions_Opportunistic = True
else: else:
logging.info('Balances Flattened - Updating to Trade New Symbols:') logging.info('Balances Flattened - Updating to Trade New Symbols:')
logging.info(f' ASTER.symbol -> {best_symbol_by_exchange_aster.symbol}') logging.info(f' {Aster.symbol} -> {best_symbol_by_exchange_aster.symbol}')
logging.info(f' EXTEND.symbol -> {best_symbol_by_exchange_extend.symbol}') logging.info(f' {Extend.symbol} -> {best_symbol_by_exchange_extend.symbol}')
await aster_cancel_all_orders() await aster_cancel_all_orders()
await extend_cancel_all_orders() await extend_cancel_all_orders()
@@ -152,7 +152,16 @@ async def symbol_switch(best_symbol_by_exchange_aster: structs.Perpetual_Exchang
Aster = best_symbol_by_exchange_aster Aster = best_symbol_by_exchange_aster
Extend = best_symbol_by_exchange_extend Extend = best_symbol_by_exchange_extend
VAL_KEY.set(name='fr_algo_working_symbol', value=json.dumps(obj={'ASTER': asdict(obj=Aster), 'EXTEND': asdict(obj=Extend)}))
if not Aster:
logging.critical(f'Main Algo, Aster is none: {Aster}')
await kill_algo()
elif not Extend:
logging.critical(f'Main Algo, Extend is none: {Extend}')
await kill_algo()
else:
logging.info(f'setting fr_algo_working_symbol: Aster: {Aster}; Extend: {Extend}')
VAL_KEY.set(name='fr_algo_working_symbol', value=json.dumps(obj={'ASTER': asdict(obj=Aster), 'EXTEND': asdict(obj=Extend)}, cls=utils.JSONEncoder_Decimal))
def calc_fr_minutes_remaining_factor( def calc_fr_minutes_remaining_factor(
min_start_procedure: int = 15, min_start_procedure: int = 15,
@@ -436,6 +445,12 @@ async def post_aster_order(
# print_summary(use_logging=True) # print_summary(use_logging=True)
else: else:
logging.critical(f'*** Aster Order Response Abnormal: {order_resp}; post_order: {post_order}') logging.critical(f'*** Aster Order Response Abnormal: {order_resp}; post_order: {post_order}')
'''
NEED TO HANDLE THE BELOW RESP
*** Aster Order Response Abnormal: {'code': -5018, 'msg': 'Youve reached the maximum notional value limit for this symbol. You can still reduce or close your position to manage your risk.'}; post_order: {'url': '/fapi/v3/order', 'method': 'POST', 'params': {'symbol': 'ENAUSDT', 'side': 'SELL', 'type': 'LIMIT', 'timeInForce': 'GTX', 'quantity': Decimal('1900'), 'price': Decimal('0.1339100'), 'reduceOnly': False}}
*** Aster Order Response Abnormal: {'code': -4226, 'msg': 'Nonce used'}; post_order: {'url': '/fapi/v3/order', 'method': 'POST', 'params': {'symbol': 'BTCUSDT', 'side': 'SELL', 'type': 'LIMIT', 'timeInForce': 'GTX', 'quantity': Decimal('0.006'), 'price': Decimal('80520.0'), 'reduceOnly': False}}
'''
await kill_algo() await kill_algo()
async def cancel_extend_order(order_id: str): async def cancel_extend_order(order_id: str):
@@ -487,6 +502,7 @@ async def post_extend_order(
elif '1142' in str(e): # 'Error response from https://api.starknet.extended.exchange/api/v1/user/order: code 400 - {"status":"ERROR","error":{"code":1142,"message":"Edit order not found"}};' elif '1142' in str(e): # 'Error response from https://api.starknet.extended.exchange/api/v1/user/order: code 400 - {"status":"ERROR","error":{"code":1142,"message":"Edit order not found"}};'
logging.info('EXTEND EDIT ORDER, NOT FOUND, CANCELLING and continuing') logging.info('EXTEND EDIT ORDER, NOT FOUND, CANCELLING and continuing')
await extend_cancel_all_orders() await extend_cancel_all_orders()
# if Extend_Open_Orders: # if Extend_Open_Orders:
# Extend_Open_Orders.pop(0) # Extend_Open_Orders.pop(0)
# time.sleep(0.1) # time.sleep(0.1)
@@ -578,11 +594,11 @@ async def handle_order_updates(exch: str, local_open_orders: list[dict], ws_open
logging.info(f'{exch} ORDER PARTIALLY FILLED: {order_id}') logging.info(f'{exch} ORDER PARTIALLY FILLED: {order_id}')
# await get_aster_collateral() # await get_aster_collateral()
if exch=='ASTER': if exch=='ASTER':
await get_aster_notional_position(resp=ws_pos_updates) await get_aster_notional_position()
Last_Aster_Fill_Time_Ts = datetime.now().timestamp()*1000 Last_Aster_Fill_Time_Ts = datetime.now().timestamp()*1000
Aster.cancel_request_pending = False Aster.cancel_request_pending = False
else: else:
await get_extend_notional(resp=ws_pos_updates) await get_extend_notional()
Extend.cancel_request_pending = False Extend.cancel_request_pending = False
utils.send_tg_alert(f'FR_ALGO - {exch} PARTIALLY FILLED ({order_id})') utils.send_tg_alert(f'FR_ALGO - {exch} PARTIALLY FILLED ({order_id})')
elif order_update_status in ['FILLED']: elif order_update_status in ['FILLED']:
@@ -591,7 +607,7 @@ async def handle_order_updates(exch: str, local_open_orders: list[dict], ws_open
# await get_aster_collateral() # await get_aster_collateral()
if exch=='ASTER': if exch=='ASTER':
# await aster_cancel_all_orders() # await aster_cancel_all_orders()
await get_aster_notional_position(resp=ws_pos_updates) await get_aster_notional_position()
Last_Aster_Fill_Time_Ts = datetime.now().timestamp()*1000 Last_Aster_Fill_Time_Ts = datetime.now().timestamp()*1000
Aster.cancel_request_pending = False Aster.cancel_request_pending = False
else: else:
@@ -683,7 +699,7 @@ async def get_aster_notional_position(resp: list | None = None):
pos_dict['timestamp_arrival'] = round(datetime.now().timestamp()*1000) pos_dict['timestamp_arrival'] = round(datetime.now().timestamp()*1000)
if previous_notional_obj: if previous_notional_obj:
if previous_notional_obj['timestamp_arrival'] > pos_dict['timestamp_arrival']: if previous_notional_obj['timestamp_arrival'] >= pos_dict['timestamp_arrival']:
# logging.info(f'ASTER NOTIONAL: prev timestamp ({pd.to_datetime(previous_notional_obj['timestamp_arrival'], unit='ms')}) > new timestamp ({pd.to_datetime(pos_dict['timestamp_arrival'], unit='ms')}); skipping') # logging.info(f'ASTER NOTIONAL: prev timestamp ({pd.to_datetime(previous_notional_obj['timestamp_arrival'], unit='ms')}) > new timestamp ({pd.to_datetime(pos_dict['timestamp_arrival'], unit='ms')}); skipping')
return return
@@ -700,12 +716,12 @@ async def get_aster_notional_position(resp: list | None = None):
raise ValueError(e) raise ValueError(e)
if pos_dict.get('notional') is not None: if pos_dict.get('notional') is not None:
Aster.notional_position = float(pos_dict['notional']) #- Aster.unrealized_pnl Aster.notional_position = float(pos_dict['notional']) - Aster.unrealized_pnl
else: else:
Aster.notional_position = float(pos_dict['position_amount'])*float(pos_dict['entry_price']) Aster.notional_position = float(pos_dict['position_amount'])*float(pos_dict['entry_price'])
if pos_dict.get('leverage') is not None: if pos_dict.get('leverage') is not None:
Aster.mult = int(pos_dict['leverage']) Aster.mult = int(pos_dict['leverage'])
if abs(Aster.notional_position) > Config.Config.Max_Target_Notional*Config.Config.Max_Order_Over_Notional_Ratio: if abs(Decimal(str(Aster.notional_position))) > Config.Config.Max_Target_Notional*Config.Config.Max_Order_Over_Notional_Ratio:
logging.info(f'BAD NOTIONAL - ASTER CHANGE: {previous_notional_position} -> {Aster.notional_position}; UR PNL: {Aster.unrealized_pnl}; MULT: {Aster.mult}; pos_dict: {pos_dict}; resp: {resp}; max_tgt_notional: {Config.Config.Max_Target_Notional}') logging.info(f'BAD NOTIONAL - ASTER CHANGE: {previous_notional_position} -> {Aster.notional_position}; UR PNL: {Aster.unrealized_pnl}; MULT: {Aster.mult}; pos_dict: {pos_dict}; resp: {resp}; max_tgt_notional: {Config.Config.Max_Target_Notional}')
await kill_algo() await kill_algo()
if Aster.notional_position != previous_notional_position: if Aster.notional_position != previous_notional_position:
@@ -778,7 +794,7 @@ async def get_extend_notional(resp: list | None = None):
Extend.notional_position = notional_pos_sided - float(Extend.unrealized_pnl) Extend.notional_position = notional_pos_sided - float(Extend.unrealized_pnl)
Extend.mult = pos_dict.get('leverage', Extend.mult) Extend.mult = pos_dict.get('leverage', Extend.mult)
if abs(Extend.notional_position) > Config.Config.Max_Target_Notional*Config.Config.Max_Order_Over_Notional_Ratio: if abs(Decimal(str(Extend.notional_position))) > Config.Config.Max_Target_Notional*Config.Config.Max_Order_Over_Notional_Ratio:
logging.info(f'BAD NOTIONAL - EXTEND CHANGE: {previous_notional_position} -> {Extend.notional_position}; UR PNL: {Extend.unrealized_pnl}; MULT: {Extend.mult}; pos_dict: {pos_dict}; resp: {resp}') logging.info(f'BAD NOTIONAL - EXTEND CHANGE: {previous_notional_position} -> {Extend.notional_position}; UR PNL: {Extend.unrealized_pnl}; MULT: {Extend.mult}; pos_dict: {pos_dict}; resp: {resp}')
await kill_algo() await kill_algo()
if Extend.notional_position != previous_notional_position: if Extend.notional_position != previous_notional_position:
@@ -843,7 +859,11 @@ async def aster_cancel_all_orders():
logging.info(f'ASTER CANCEL ALL OPEN ORDERS RESP: {r}') logging.info(f'ASTER CANCEL ALL OPEN ORDERS RESP: {r}')
async def extend_cancel_all_orders(): async def extend_cancel_all_orders():
global Extend_Open_Orders
r = await EXTEND_CLIENT.orders.mass_cancel(markets=[Extend.symbol]) r = await EXTEND_CLIENT.orders.mass_cancel(markets=[Extend.symbol])
if Extend_Open_Orders:
Extend_Open_Orders.pop(0)
logging.info(f'EXTEND CANCEL ALL OPEN ORDERS RESP: {r}') logging.info(f'EXTEND CANCEL ALL OPEN ORDERS RESP: {r}')
### KILL ALGO ### ### KILL ALGO ###
@@ -851,9 +871,10 @@ async def kill_algo():
await aster_cancel_all_orders() await aster_cancel_all_orders()
await extend_cancel_all_orders() await extend_cancel_all_orders()
logging.info('ALGO KILL FLAG ACTIVATED; CANCELLING OPEN ORDERS AND SHUTTING DOWN') logging.info('ALGO KILL FLAG ACTIVATED; CANCELLING OPEN ORDERS AND SHUTTING DOWN')
await output_algo_status('STOPPED') await output_algo_status('DEAD')
raise ValueError('KILL FLAG ACTIVATED') raise ValueError('KILL FLAG ACTIVATED')
### ALGO LOOP ### ### ALGO LOOP ###
async def run_algo(): async def run_algo():
global Config global Config
@@ -878,14 +899,36 @@ async def run_algo():
# print('__________Start___________') # print('__________Start___________')
### Load Algo Config ### ### Load Algo Config ###
Config = json.loads(VAL_KEY.get('fr_orchestrator_output')) # ty:ignore[invalid-argument-type] fr_orchestrator_output: str = VAL_KEY.get('fr_orchestrator_output') # ty:ignore[invalid-assignment]
if fr_orchestrator_output:
Config = json.loads(fr_orchestrator_output)
Config = structs.Algo_Config(**Config) Config = structs.Algo_Config(**Config)
Config.Config.Max_Target_Notional = float(min([Aster.mult, Extend.mult]) * Config.Config.Target_Open_Cash_Position) Config.Config.Max_Target_Notional = float(min([Aster.mult, Extend.mult]) * Config.Config.Target_Open_Cash_Position)
else:
logging.critical(f'fr_orchestrator_output is empty: {fr_orchestrator_output}; reloading from disk and continuing.')
with open('algo_config.json', mode='r', encoding='utf-8') as file:
Config = json.load(file)
Config = structs.Algo_Config(**Config)
if not Config.model_dump():
logging.critical(f'fr_orchestrator_output is empty - killing: {fr_orchestrator_output};')
Config.Config.Max_Target_Notional = float(min([Aster.mult, Extend.mult]) * Config.Config.Target_Open_Cash_Position)
VAL_KEY.set(name='fr_orchestrator_output', value=json.dumps(obj=Config.model_dump(), cls=utils.JSONEncoder_Decimal))
min_time_to_funding = Config.Config.Min_Time_To_Funding_Minutes * 60 * 1000 min_time_to_funding = Config.Config.Min_Time_To_Funding_Minutes * 60 * 1000
### Load Data from Feedhandlers ### ### Load Data from Feedhandlers ###
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_engine_best_fund_rate_output')) # ty:ignore[invalid-argument-type] vk_get: str = VAL_KEY.get(name='fr_engine_best_fund_rate_output') # ty:ignore[invalid-assignment]
if vk_get:
best_symbol_by_exchange: dict = json.loads(vk_get)
else:
logging.critical(f'best_symbol_by_exchange is none: {vk_get}')
await kill_algo()
raise ValueError(f'best_symbol_by_exchange is none: {vk_get}')
# best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_engine_best_fund_rate_output')) # ty:ignore[invalid-argument-type]
best_symbol_by_exchange_aster = structs.Perpetual_Exchange(**best_symbol_by_exchange['ASTER']) best_symbol_by_exchange_aster = structs.Perpetual_Exchange(**best_symbol_by_exchange['ASTER'])
best_symbol_by_exchange_extend = structs.Perpetual_Exchange(**best_symbol_by_exchange['EXTEND']) best_symbol_by_exchange_extend = structs.Perpetual_Exchange(**best_symbol_by_exchange['EXTEND'])
@@ -933,10 +976,20 @@ async def run_algo():
fr_best_exch = 'ASTER' if max([abs(fund_rate_ast), abs(fund_rate_ext)]) == abs(fund_rate_ast) else 'EXTEND' fr_best_exch = 'ASTER' if max([abs(fund_rate_ast), abs(fund_rate_ext)]) == abs(fund_rate_ast) else 'EXTEND'
fr_best_rate = fund_rate_ast if max([abs(fund_rate_ast), abs(fund_rate_ext)]) == abs(fund_rate_ast) else fund_rate_ext fr_best_rate = fund_rate_ast if max([abs(fund_rate_ast), abs(fund_rate_ext)]) == abs(fund_rate_ast) else fund_rate_ext
fr_best_side = 'BUY' if fr_best_rate < 0 else 'SELL' fr_best_side = 'BUY' if fr_best_rate < 0 else 'SELL'
if fr_best_side == 'SELL':
fr_best_exch = 'ASTER' if fr_best_exch == 'EXTEND' else 'EXTEND'
fr_best_side = 'BUY'
return net_fr, fr_best_exch, fr_best_side return net_fr, fr_best_exch, fr_best_side
else: else:
fr_best_exch = 'EXTEND' fr_best_exch = 'EXTEND'
fr_best_side = 'BUY' if fund_rate_ext < 0 else 'SELL' fr_best_side = 'BUY' if fund_rate_ext < 0 else 'SELL'
if fr_best_side == 'SELL':
fr_best_exch = 'ASTER'
fr_best_side = 'BUY'
return fund_rate_ext, fr_best_exch, fr_best_side return fund_rate_ext, fr_best_exch, fr_best_side
next_net_funding_rate, fr_best_exch, fr_best_side = calc_next_net_fund_rate(next_funding_at_same_time, fund_rate_ast=aster_fund_rate, fund_rate_ext=extend_fund_rate) next_net_funding_rate, fr_best_exch, fr_best_side = calc_next_net_fund_rate(next_funding_at_same_time, fund_rate_ast=aster_fund_rate, fund_rate_ext=extend_fund_rate)
@@ -947,7 +1000,16 @@ async def run_algo():
aster_ticker_dict: dict = json.loads(s=aster_ticker_dict) if aster_ticker_dict is not None else {} aster_ticker_dict: dict = json.loads(s=aster_ticker_dict) if aster_ticker_dict is not None else {}
if ( aster_ticker_dict.get('symbol', None) != Aster.symbol ) and not(Config.Overrides.Flatten_Open_Positions): if ( aster_ticker_dict.get('symbol', None) != Aster.symbol ) and not(Config.Overrides.Flatten_Open_Positions):
logging.warning(f'ASTER Symbol mismatch: {aster_ticker_dict}; expected symbol: {Aster.symbol}') logging.warning(f'ASTER Symbol mismatch: {aster_ticker_dict}; expected symbol: {Aster.symbol}')
VAL_KEY.set(name='fr_algo_working_symbol', value=json.dumps(obj={'ASTER': asdict(obj=Aster), 'EXTEND': asdict(obj=Extend)}))
if not Aster:
logging.critical(f'Main Algo, Aster is none: {Aster}')
await kill_algo()
elif not Extend:
logging.critical(f'Main Algo, Extend is none: {Extend}')
await kill_algo()
else:
logging.info(f'setting fr_algo_working_symbol: Aster: {Aster}; Extend: {Extend}')
VAL_KEY.set(name='fr_algo_working_symbol', value=json.dumps(obj={'ASTER': asdict(obj=Aster), 'EXTEND': asdict(obj=Extend)}, cls=utils.JSONEncoder_Decimal))
time.sleep(5) time.sleep(5)
continue continue
# raise ValueError(f'ASTER Symbol mismatch: {ASTER_TICKER_DICT}; expected symbol: {ASTER.symbol}') # raise ValueError(f'ASTER Symbol mismatch: {ASTER_TICKER_DICT}; expected symbol: {ASTER.symbol}')
@@ -956,7 +1018,16 @@ async def run_algo():
extend_ticker_dict: dict = json.loads(s=extend_ticker_dict) if extend_ticker_dict is not None else {} extend_ticker_dict: dict = json.loads(s=extend_ticker_dict) if extend_ticker_dict is not None else {}
if ( extend_ticker_dict.get('symbol', None) != Extend.symbol) and not(Config.Overrides.Flatten_Open_Positions): if ( extend_ticker_dict.get('symbol', None) != Extend.symbol) and not(Config.Overrides.Flatten_Open_Positions):
logging.warning(f'EXTEND Symbol mismatch: {extend_ticker_dict}; expected symbol: {Extend.symbol}') logging.warning(f'EXTEND Symbol mismatch: {extend_ticker_dict}; expected symbol: {Extend.symbol}')
VAL_KEY.set(name='fr_algo_working_symbol', value=json.dumps(obj={'ASTER': asdict(obj=Aster), 'EXTEND': asdict(obj=Extend)}))
if not Aster:
logging.critical(f'Main Algo, Aster is none: {Aster}')
await kill_algo()
elif not Extend:
logging.critical(f'Main Algo, Extend is none: {Extend}')
await kill_algo()
else:
logging.info(f'setting fr_algo_working_symbol: Aster: {Aster}; Extend: {Extend}')
VAL_KEY.set(name='fr_algo_working_symbol', value=json.dumps(obj={'ASTER': asdict(obj=Aster), 'EXTEND': asdict(obj=Extend)}, cls=utils.JSONEncoder_Decimal))
time.sleep(5) time.sleep(5)
continue continue
# raise ValueError(f'EXTEND Symbol mismatch: {EXTENDED_TICKER_DICT}; expected symbol: {EXTEND.symbol}') # raise ValueError(f'EXTEND Symbol mismatch: {EXTENDED_TICKER_DICT}; expected symbol: {EXTEND.symbol}')
@@ -1029,15 +1100,15 @@ async def run_algo():
if Config.Overrides.Flatten_Open_Positions: if Config.Overrides.Flatten_Open_Positions:
aster_tgt = Decimal('0.00') aster_tgt = Decimal('0.00')
elif Config.Overrides.Flatten_Open_Positions_Opportunistic: elif Config.Overrides.Flatten_Open_Positions_Opportunistic or (signal.exchange != fr_best_exch):
if signal.signal: if signal.signal:
if signal.exchange == 'EXTEND' and Decimal(str(Aster.notional_position)) > 0: if signal.exchange == 'EXTEND' and Decimal(str(Aster.notional_position)) >= 0:
aster_tgt = Decimal('0.00') aster_tgt = Decimal('0.00')
if signal.exchange == 'EXTEND' and Decimal(str(Aster.notional_position)) < 0: if signal.exchange == 'EXTEND' and Decimal(str(Aster.notional_position)) < 0:
pass pass
if signal.exchange == 'ASTER' and Decimal(str(Aster.notional_position)) > 0: if signal.exchange == 'ASTER' and Decimal(str(Aster.notional_position)) > 0:
pass pass
if signal.exchange == 'ASTER' and Decimal(str(Aster.notional_position)) < 0: if signal.exchange == 'ASTER' and Decimal(str(Aster.notional_position)) <= 0:
aster_tgt = Decimal('0.00') aster_tgt = Decimal('0.00')
aster_target = Target( aster_target = Target(
@@ -1062,7 +1133,7 @@ async def run_algo():
MKT : Aster: {Aster.symbol:<10} (best: {best_symbol_by_exchange_aster.symbol}) | Extend: {Extend.symbol:<10} (best: {best_symbol_by_exchange_extend.symbol}) MKT : Aster: {Aster.symbol:<10} (best: {best_symbol_by_exchange_aster.symbol}) | Extend: {Extend.symbol:<10} (best: {best_symbol_by_exchange_extend.symbol})
FR SWITCH : {funding_rate_switch_net:.6%} [{funding_rate_switch_net*10_000:.2f}bps] [{funding_rate_switch_net*1_000_000:.0f}pips] FR SWITCH : {funding_rate_switch_net:.6%} [{funding_rate_switch_net*10_000:.2f}bps] [{funding_rate_switch_net*1_000_000:.0f}pips]
{pd.to_datetime(aster_fund_rate_time, unit='ms')} ({(pd.to_datetime(aster_fund_rate_time, unit='ms')-datetime.now()):}) | {pd.to_datetime(extend_fund_rate_time, unit='ms')} ({(pd.to_datetime(extend_fund_rate_time, unit='ms')-datetime.now()):}) {pd.to_datetime(aster_fund_rate_time, unit='ms')} ({(pd.to_datetime(aster_fund_rate_time, unit='ms') - pd.to_datetime(datetime.now().timestamp()*1000, unit='ms')) }) | {pd.to_datetime(extend_fund_rate_time, unit='ms')} ({(pd.to_datetime(extend_fund_rate_time, unit='ms')-pd.to_datetime(datetime.now().timestamp()*1000, unit='ms'))})
ASTER: {aster_fund_rate:.6%} [{aster_fund_rate*10_000:.2f}bps] [{aster_fund_rate*1_000_000:.0f}pips] | EXTEND: {extend_fund_rate:.6%} [{extend_fund_rate*10_000:.2f}bps] [{extend_fund_rate*1_000_000:.0f}pips] ASTER: {aster_fund_rate:.6%} [{aster_fund_rate*10_000:.2f}bps] [{aster_fund_rate*1_000_000:.0f}pips] | EXTEND: {extend_fund_rate:.6%} [{extend_fund_rate*10_000:.2f}bps] [{extend_fund_rate*1_000_000:.0f}pips]
ASTER: {'LONG PAYS SHORT' if aster_fund_rate > 0 else 'SHORT PAYS LONG'} | EXTEND: {'LONG PAYS SHORT' if extend_fund_rate > 0 else 'SHORT PAYS LONG'} ASTER: {'LONG PAYS SHORT' if aster_fund_rate > 0 else 'SHORT PAYS LONG'} | EXTEND: {'LONG PAYS SHORT' if extend_fund_rate > 0 else 'SHORT PAYS LONG'}
ASTER: [ Notional Position $ : {Aster.notional_position:.4f} ] | EXTEND: [ Notional Position $ : {Extend.notional_position:.4f} ] ASTER: [ Notional Position $ : {Aster.notional_position:.4f} ] | EXTEND: [ Notional Position $ : {Extend.notional_position:.4f} ]
@@ -1136,7 +1207,7 @@ async def run_algo():
open_order_dict = dict(Aster_Open_Orders[0]) open_order_dict = dict(Aster_Open_Orders[0])
open_order_id = str(open_order_dict['order_id']) open_order_id = str(open_order_dict['order_id'])
try: try:
open_order_px = float(open_order_dict['price']) open_order_px = float(open_order_dict['price']) if open_order_dict.get('price') is not None else float(open_order_dict['last_filled_price'])
except Exception as e: except Exception as e:
logging.critical(f'Aster cant find price on order obj: {open_order_dict}; e: {e}') logging.critical(f'Aster cant find price on order obj: {open_order_dict}; e: {e}')
await kill_algo() await kill_algo()
@@ -1226,7 +1297,7 @@ async def run_algo():
) )
### Output Algo Status ### ### Output Algo Status ###
await output_algo_status('WORKING') await output_algo_status('HEALTHY')
### CHECK TIME TO FUNDING AND WHETHER TO BE ACTIVE ### ### CHECK TIME TO FUNDING AND WHETHER TO BE ACTIVE ###
if ( time_to_funding_ms > min_time_to_funding ) and (not Aster_Open_Orders) and (not Extend_Open_Orders): if ( time_to_funding_ms > min_time_to_funding ) and (not Aster_Open_Orders) and (not Extend_Open_Orders):
@@ -1246,13 +1317,13 @@ async def run_algo():
time.sleep(Config.Config.Loop_Sleep_Sec) time.sleep(Config.Config.Loop_Sleep_Sec)
except KeyboardInterrupt: except KeyboardInterrupt:
logging.info('CANCELLING OPEN ORDERS') logging.info('CANCELLING OPEN ORDERS')
await output_algo_status('STOPPING') await output_algo_status('UNHEALTHY')
await kill_algo() await kill_algo()
except Exception as e: except Exception as e:
logging.error(traceback.format_exc()) logging.error(traceback.format_exc())
logging.critical(f'*** ALGO ENGINE CRASHED: {e}') logging.critical(f'*** ALGO ENGINE CRASHED: {e}')
logging.info('CANCELLING OPEN ORDERS') logging.info('CANCELLING OPEN ORDERS')
await output_algo_status('STOPPING') await output_algo_status('UNHEALTHY')
utils.send_tg_alert(f'FR_ALGO_CRASHED: {str(e)}') utils.send_tg_alert(f'FR_ALGO_CRASHED: {str(e)}')
await kill_algo() await kill_algo()
@@ -1275,14 +1346,23 @@ async def main():
await set_comb_open_symbols() await set_comb_open_symbols()
# Open_Symbols = ['HYPE-USD'] # Open_Symbols = ['HYPE-USD']
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_engine_best_fund_rate_output')) # ty:ignore[invalid-argument-type] vk_get: str = VAL_KEY.get(name='fr_engine_best_fund_rate_output') # ty:ignore[invalid-assignment]
if vk_get:
best_symbol_by_exchange: dict = json.loads(vk_get)
else:
best_symbol_by_exchange = None
raise ValueError('best_symbol_by_exchange is none')
if Open_Symbols: if Open_Symbols or not(best_symbol_by_exchange):
logging.info(f'OPEN SYMBOLS: {Open_Symbols}') logging.info(f'OPEN SYMBOLS: {Open_Symbols}')
master_data = json.loads(s=VAL_KEY.get(name='fr_engine_best_fund_rate_master')) # ty:ignore[invalid-argument-type] master_data = json.loads(s=VAL_KEY.get(name='fr_engine_best_fund_rate_master')) # ty:ignore[invalid-argument-type]
open_symbol_to_work = Open_Symbols[0] open_symbol_to_work = Open_Symbols[0]
current_pos_master_ast = [d for d in master_data if d.get('symbol_ext') == open_symbol_to_work][0] current_pos_master_ast = [d for d in master_data if d.get('symbol_ext') == open_symbol_to_work][0]
if not current_pos_master_ast:
logging.critical(f'Open symbol not found in master data, killing algo: symbol {open_symbol_to_work}; md: {current_pos_master_ast}')
await kill_algo()
Aster, Extend = create_exchange_objs_from_dict(exchanges_dict=current_pos_master_ast) Aster, Extend = create_exchange_objs_from_dict(exchanges_dict=current_pos_master_ast)
Open_Symbols.pop(0) Open_Symbols.pop(0)
@@ -1297,22 +1377,31 @@ async def main():
Config = json.load(file) Config = json.load(file)
Config = structs.Algo_Config(**Config) Config = structs.Algo_Config(**Config)
Config.Config.Max_Target_Notional = float(min([Aster.mult, Extend.mult]) * Config.Config.Target_Open_Cash_Position)
# logging.info(f'Initial Algo Config: {ALGO_CONFIG}') # logging.info(f'Initial Algo Config: {ALGO_CONFIG}')
VAL_KEY.set(name='fr_orchestrator_output', value=json.dumps(obj=Config.model_dump())) if not Config.model_dump():
VAL_KEY.set(name='fr_algo_working_symbol', value=json.dumps(obj={'ASTER': asdict(obj=Aster), 'EXTEND': asdict(obj=Extend)})) raise ValueError(f'Main Algo, initial config is none: {Config}')
elif not Aster:
raise ValueError(f'Main Algo, Aster is none: {Aster}')
elif not Extend:
raise ValueError(f'Main Algo, Extend is none: {Extend}')
else:
Config.Config.Max_Target_Notional = float(min([Aster.mult, Extend.mult]) * Config.Config.Target_Open_Cash_Position)
VAL_KEY.set(name='fr_orchestrator_output', value=json.dumps(obj=Config.model_dump(), cls=utils.JSONEncoder_Decimal))
logging.info(f'setting fr_algo_working_symbol: Aster: {Aster}; Extend: {Extend}')
VAL_KEY.set(name='fr_algo_working_symbol', value=json.dumps(obj={'ASTER': asdict(obj=Aster), 'EXTEND': asdict(obj=Extend)}, cls=utils.JSONEncoder_Decimal))
Funding_Rates_Min_Remaining_Factor_Pcts = calc_fr_minutes_remaining_factor() Funding_Rates_Min_Remaining_Factor_Pcts = calc_fr_minutes_remaining_factor()
Algo_Status = structs.Algo_Status( Algo_Status = structs.Algo_Status(
last_update_ts_ms = int(round(datetime.now().timestamp()*1000, 2)), last_update_ts_ms = int(round(datetime.now().timestamp()*1000, 2)),
status = 'WORKING', status = 'HEALTHY',
expected_alpha = 0.00, expected_alpha = 0.00,
model_ratio = 0.00, model_ratio = 0.00,
current_ratio = 0.00, current_ratio = 0.00,
) )
await output_algo_status('WORKING') await output_algo_status('HEALTHY')
async with engine.connect() as CON: async with engine.connect() as CON:
### ASTER SETUP ### ### ASTER SETUP ###

View File

@@ -4,13 +4,15 @@ from typing import Any
import valkey import valkey
from pydantic import BaseModel from pydantic import BaseModel
from datetime import datetime
from sqlalchemy.util.typing import Self from sqlalchemy.util.typing import Self
from collections.abc import Sequence, Callable from collections.abc import Sequence, Callable
import modules.utils as utils
def ret_true(): def ret_true():
return True return True
class Locked_Value(Sequence): class Locked_Value(Sequence):
def __init__(self, initial_value: Any = None, unlock_func: Callable=ret_true): def __init__(self, initial_value: Any = None, unlock_func: Callable=ret_true):
self._value: Any = initial_value self._value: Any = initial_value
@@ -88,16 +90,218 @@ class Current_Previous_Value:
self._previous_value = self._value self._previous_value = self._value
self._value = v 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): class Algo_Status(BaseModel):
last_update_ts_ms: int last_update_ts_ms: int
status: str # 'WORKING' | 'STOPPED' status: str # 'WORKING' | 'STOPPED' ///// # ENUM: 'HEALTHY' | 'UNHEALTHY' | 'DEAD'
expected_alpha: float expected_alpha: float
model_ratio: float model_ratio: float
current_ratio: float current_ratio: float
# @dataclass(kw_only=True)
class Algo_Config_Overrides(BaseModel): class Algo_Config_Overrides(BaseModel):
Allow_Ordering_Aster: bool Allow_Ordering_Aster: bool
Allow_Ordering_Extend: bool Allow_Ordering_Extend: bool
@@ -107,7 +311,6 @@ class Algo_Config_Overrides(BaseModel):
Flip_Side_For_Testing: bool Flip_Side_For_Testing: bool
# @dataclass(kw_only=True)
class Algo_Config_Config(BaseModel): class Algo_Config_Config(BaseModel):
Loop_Sleep_Sec: int Loop_Sleep_Sec: int
Max_Order_Over_Notional_Ratio: float Max_Order_Over_Notional_Ratio: float
@@ -119,12 +322,12 @@ class Algo_Config_Config(BaseModel):
Switch_To_Taker_Seconds: int Switch_To_Taker_Seconds: int
Target_Open_Cash_Position: int Target_Open_Cash_Position: int
# @dataclass(kw_only=True)
class Algo_Config_Logging(BaseModel): class Algo_Config_Logging(BaseModel):
Log_Summary_Each_Loop: bool Log_Summary_Each_Loop: bool
Print_Summary_Each_Loop: bool Print_Summary_Each_Loop: bool
# @dataclass(kw_only=True)
class Algo_Config(BaseModel): class Algo_Config(BaseModel):
Updated_Timestamp: int Updated_Timestamp: int
Config: Algo_Config_Config Config: Algo_Config_Config

View File

@@ -2,9 +2,18 @@ import logging
from dotenv import load_dotenv from dotenv import load_dotenv
import requests import requests
import os import os
import json
from decimal import Decimal
load_dotenv() 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]: 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): for index, item in enumerate(list_of_dicts):
if item.get(id) == new_dict.get(id): if item.get(id) == new_dict.get(id):

6
ng.py
View File

@@ -132,6 +132,8 @@ async def get_trades_hist() -> pd.DataFrame:
WHERE timestamp_arrival > {start_ts} WHERE timestamp_arrival > {start_ts}
''') ''')
df_aster_orders = pd.read_sql(aster_orders, con=ENGINE) df_aster_orders = pd.read_sql(aster_orders, con=ENGINE)
if len(df_aster_orders) < 1:
return pd.DataFrame()
df_aster_orders['timestamp_dt'] = pd.to_datetime(df_aster_orders['timestamp_transaction'], unit='ms') df_aster_orders['timestamp_dt'] = pd.to_datetime(df_aster_orders['timestamp_transaction'], unit='ms')
df_aster_orders_fill = df_aster_orders.loc[df_aster_orders['execution_type']=='TRADE',:] df_aster_orders_fill = df_aster_orders.loc[df_aster_orders['execution_type']=='TRADE',:]
df_aster_orders_fill = df_aster_orders_fill[['timestamp_transaction','order_trade_time_ts','timestamp_dt','order_id','trade_id','client_order_id','order_status','side','last_filled_qty','filled_accumulated_qty','commission','last_filled_price','realized_profit']].reset_index(drop=True) df_aster_orders_fill = df_aster_orders_fill[['timestamp_transaction','order_trade_time_ts','timestamp_dt','order_id','trade_id','client_order_id','order_status','side','last_filled_qty','filled_accumulated_qty','commission','last_filled_price','realized_profit']].reset_index(drop=True)
@@ -150,6 +152,8 @@ async def get_trades_hist() -> pd.DataFrame:
WHERE timestamp_arrival > {start_ts} WHERE timestamp_arrival > {start_ts}
''') ''')
df_extend_orders = pd.read_sql(extend_orders, con=ENGINE) df_extend_orders = pd.read_sql(extend_orders, con=ENGINE)
if len(df_extend_orders) < 1:
return pd.DataFrame()
df_extend_orders['timestamp_dt'] = pd.to_datetime(df_extend_orders['updated_time_ts'], unit='ms') df_extend_orders['timestamp_dt'] = pd.to_datetime(df_extend_orders['updated_time_ts'], unit='ms')
df_extend_orders_fill = df_extend_orders.loc[df_extend_orders['status'].isin(['FILLED','PARTIALLY_FILLED']),:] df_extend_orders_fill = df_extend_orders.loc[df_extend_orders['status'].isin(['FILLED','PARTIALLY_FILLED']),:]
df_extend_orders_fill = df_extend_orders_fill[['created_time_ts','updated_time_ts','timestamp_dt','order_id','external_id','status','side','qty','filled_qty','payed_fee','price','averagePrice']].reset_index(drop=True) df_extend_orders_fill = df_extend_orders_fill[['created_time_ts','updated_time_ts','timestamp_dt','order_id','external_id','status','side','qty','filled_qty','payed_fee','price','averagePrice']].reset_index(drop=True)
@@ -351,4 +355,4 @@ async def root():
}).classes('w-full') }).classes('w-full')
ui.run(root, storage_secret="123ABC", reload=True, dark=True, title='Atwater Trading', port=9090) ui.run(root, storage_secret="123ABC", reload=True, dark=True, title='Atwater Trading', port=8060)

1365
pnl.ipynb

File diff suppressed because it is too large Load Diff

View File

@@ -24,3 +24,5 @@ x10-python-trading-starknet
eth-keys eth-keys
eth-account eth-account
pydantic pydantic
plotly
docker

View File

@@ -17,6 +17,7 @@ import os
from dotenv import load_dotenv from dotenv import load_dotenv
import modules.db as db import modules.db as db
import modules.aster_db as aster_db import modules.aster_db as aster_db
import sys
### Allow only ipv4 ### ### Allow only ipv4 ###
def allowed_gai_family(): def allowed_gai_family():
@@ -37,9 +38,10 @@ load_dotenv()
LOG_FILEPATH: str = f'{os.getenv(key="LOGS_PATH")}/Fund_Rate_Aster.log' LOG_FILEPATH: str = f'{os.getenv(key="LOGS_PATH")}/Fund_Rate_Aster.log'
### CONSTANTS ### ### CONSTANTS ###
SYMBOL: str = 'ETHUSDT' SYMBOL: str = 'ENAUSDT'
STREAM_MARKPRICE: str = f'{SYMBOL.lower()}@markPrice@1s' STREAM_MARKPRICE: str = '!markPrice@arr@1s'
# STREAM_MARKPRICE: str = f'{SYMBOL.lower()}@markPrice@1s'
STREAM_BOOKTICKER: str = f'{SYMBOL.lower()}@bookTicker' STREAM_BOOKTICKER: str = f'{SYMBOL.lower()}@bookTicker'
STREAM_TRADES: str = f'{SYMBOL.lower()}@aggTrade' STREAM_TRADES: str = f'{SYMBOL.lower()}@aggTrade'
@@ -83,17 +85,23 @@ async def ws_stream():
async for message in websocket: async for message in websocket:
### Update Symbol if Algo Outputs Change ### ### Update Symbol if Algo Outputs Change ###
if ALLOW_SYMBOL_CHG: if ALLOW_SYMBOL_CHG:
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_algo_working_symbol')) # ty:ignore[invalid-argument-type] fr_algo_working_symbol = VAL_KEY.get(name='fr_algo_working_symbol')
if not fr_algo_working_symbol:
logging.critical(f'fr_algo_working_symbol is empty - killing: {fr_algo_working_symbol}')
sys.exit(1)
best_symbol_by_exchange: dict = json.loads(fr_algo_working_symbol) # ty:ignore[invalid-argument-type]
best_symbol: str = best_symbol_by_exchange['ASTER']['symbol'] best_symbol: str = best_symbol_by_exchange['ASTER']['symbol']
if best_symbol != SYMBOL: if best_symbol != SYMBOL:
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}') logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
SYMBOL = best_symbol SYMBOL = best_symbol
await unsubscribe_streams(websocket = websocket, streams=[STREAM_MARKPRICE,STREAM_BOOKTICKER,STREAM_TRADES]) await unsubscribe_streams(websocket = websocket, streams=[STREAM_BOOKTICKER,STREAM_TRADES])
STREAM_MARKPRICE = f'{SYMBOL.lower()}@markPrice@1s' # STREAM_MARKPRICE = f'{SYMBOL.lower()}@markPrice@1s'
STREAM_BOOKTICKER = f'{SYMBOL.lower()}@bookTicker' STREAM_BOOKTICKER = f'{SYMBOL.lower()}@bookTicker'
STREAM_TRADES = f'{SYMBOL.lower()}@aggTrade' STREAM_TRADES = f'{SYMBOL.lower()}@aggTrade'
await subscribe_streams(websocket = websocket, streams=[STREAM_MARKPRICE,STREAM_BOOKTICKER,STREAM_TRADES]) await subscribe_streams(websocket = websocket, streams=[STREAM_BOOKTICKER,STREAM_TRADES])
continue continue
ts_arrival = round(datetime.now().timestamp()*1000) ts_arrival = round(datetime.now().timestamp()*1000)
@@ -104,18 +112,25 @@ async def ws_stream():
if channel is not None: if channel is not None:
match channel: match channel:
case c if c == STREAM_MARKPRICE: case c if c == STREAM_MARKPRICE:
# print(f'MP: {data}') if data.get('data'):
VAL_KEY.set('fund_rate_aster_all', json.dumps(data['data']))
else:
logging.warning(f'Data["data"] is None: {data}')
single_ticker_fr = [d for d in data['data'] if d.get('s')==SYMBOL]
if single_ticker_fr:
d = single_ticker_fr[0]
VAL_KEY_OBJ = json.dumps({ VAL_KEY_OBJ = json.dumps({
'timestamp_arrival': ts_arrival, 'timestamp_arrival': ts_arrival,
'timestamp_msg': data['data']['E'], 'timestamp_msg': d['E'],
'symbol': data['data']['s'], 'symbol': d['s'],
'mark_price': data['data']['p'], 'mark_price': d['p'],
'index_price': data['data']['i'], 'index_price': d['i'],
'estimated_settle_price': data['data']['P'], 'estimated_settle_price': d['P'],
'funding_rate': data['data']['r'], 'funding_rate': d['r'],
'next_funding_time_ts_ms': data['data']['T'], 'next_funding_time_ts_ms': d['T'],
}) })
VAL_KEY.set(VK_FUND_RATE, VAL_KEY_OBJ) VAL_KEY.set(VK_FUND_RATE, VAL_KEY_OBJ)
# print(f'MP: {d}')
continue continue
case c if c == STREAM_BOOKTICKER: case c if c == STREAM_BOOKTICKER:
# print(f'BT: {data}') # print(f'BT: {data}')

View File

@@ -16,6 +16,8 @@ from sqlalchemy.ext.asyncio import create_async_engine
import valkey import valkey
import os import os
from dotenv import load_dotenv from dotenv import load_dotenv
import sys
import modules.utils as utils
### Allow only ipv4 ### ### Allow only ipv4 ###
@@ -36,10 +38,11 @@ load_dotenv()
LOG_FILEPATH: str = f'{os.getenv("LOGS_PATH")}/Fund_Rate_Extended_FR.log' LOG_FILEPATH: str = f'{os.getenv("LOGS_PATH")}/Fund_Rate_Extended_FR.log'
### CONSTANTS ### ### CONSTANTS ###
SYMBOL: str = 'ETH-USD' SYMBOL: str = 'ENA-USD'
### Globals ### ### Globals ###
ALLOW_SYMBOL_CHG: bool = True ALLOW_SYMBOL_CHG: bool = True
LOCAL_FUNDING_RATES = []
def time_round_down(dt, interval_mins=5) -> int: # returns timestamp in seconds def time_round_down(dt, interval_mins=5) -> int: # returns timestamp in seconds
interval_secs = interval_mins * 60 interval_secs = interval_mins * 60
@@ -51,35 +54,40 @@ def time_round_down(dt, interval_mins=5) -> int: # returns timestamp in seconds
### Websocket ### ### Websocket ###
async def ws_stream(): async def ws_stream():
global SYMBOL global SYMBOL
global LOCAL_FUNDING_RATES
while True: while True:
CHANGE_SYMBOL = False # CHANGE_SYMBOL = False
WSS_URL = f"wss://api.starknet.extended.exchange/stream.extended.exchange/v1/funding/{SYMBOL}" WSS_URL = "wss://api.starknet.extended.exchange/stream.extended.exchange/v1/funding/"
async for websocket in websockets.connect(WSS_URL): async for websocket in websockets.connect(WSS_URL):
if CHANGE_SYMBOL: # if CHANGE_SYMBOL:
break # break
logging.info(f"Connected to {WSS_URL}") logging.info(f"Connected to {WSS_URL}")
try: try:
async for message in websocket: async for message in websocket:
### Update Symbol if Algo Outputs Change ### ### Update Symbol if Algo Outputs Change ###
if ALLOW_SYMBOL_CHG: if ALLOW_SYMBOL_CHG:
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_algo_working_symbol')) # ty:ignore[invalid-argument-type] fr_algo_working_symbol = VAL_KEY.get(name='fr_algo_working_symbol')
if not fr_algo_working_symbol:
logging.critical(f'fr_algo_working_symbol is empty - killing: {fr_algo_working_symbol}')
sys.exit(1)
best_symbol_by_exchange: dict = json.loads(fr_algo_working_symbol) # ty:ignore[invalid-argument-type]
best_symbol: str = best_symbol_by_exchange['EXTEND']['symbol'] best_symbol: str = best_symbol_by_exchange['EXTEND']['symbol']
if best_symbol != SYMBOL: if best_symbol != SYMBOL:
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}') logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
SYMBOL = best_symbol SYMBOL = best_symbol
CHANGE_SYMBOL = True # CHANGE_SYMBOL = True
await websocket.close() # await websocket.close()
break # break
ts_arrival = round(datetime.now().timestamp()*1000) ts_arrival = round(datetime.now().timestamp()*1000)
if isinstance(message, str): if isinstance(message, str):
try: try:
data = json.loads(message) data = json.loads(message)
if data.get('data', None) is not None: if data.get('data', None) is not None:
# print(f'FR: {data}') print(f'FR: {data}')
fr_next_update_ts = (time_round_down(dt=datetime.now(timezone.utc), interval_mins=60)+(60*60))*1000 fr_next_update_ts = (time_round_down(dt=datetime.now(timezone.utc), interval_mins=60)+(60*60))*1000
VAL_KEY_OBJ = json.dumps({ fr_update = {
'sequence_id': data['seq'], 'sequence_id': data['seq'],
'timestamp_arrival': ts_arrival, 'timestamp_arrival': ts_arrival,
'timestamp_msg': data['ts'], 'timestamp_msg': data['ts'],
@@ -87,8 +95,12 @@ async def ws_stream():
'funding_rate': float(data['data']['f']), 'funding_rate': float(data['data']['f']),
'funding_rate_updated_ts_ms': data['data']['T'], 'funding_rate_updated_ts_ms': data['data']['T'],
'next_funding_time_ts_ms': fr_next_update_ts, 'next_funding_time_ts_ms': fr_next_update_ts,
}) }
if fr_update.get('symbol') == SYMBOL:
VAL_KEY_OBJ = json.dumps(fr_update)
VAL_KEY.set(VK_FUND_RATE, VAL_KEY_OBJ) VAL_KEY.set(VK_FUND_RATE, VAL_KEY_OBJ)
LOCAL_FUNDING_RATES = utils.upsert_list_of_dicts_by_id(LOCAL_FUNDING_RATES, fr_update, id='symbol', seq_check_field=None)
VAL_KEY.set('fund_rate_extended_all', json.dumps(LOCAL_FUNDING_RATES))
continue continue
else: else:
logging.info(f'Initial or unexpected data struct, skipping: {data}') logging.info(f'Initial or unexpected data struct, skipping: {data}')

View File

@@ -55,7 +55,9 @@ async def ws_stream():
async for message in websocket: async for message in websocket:
### Update Symbol if Algo Outputs Change ### ### Update Symbol if Algo Outputs Change ###
if ALLOW_SYMBOL_CHG: if ALLOW_SYMBOL_CHG:
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_algo_working_symbol')) # ty:ignore[invalid-argument-type] vk_get: str = VAL_KEY.get(name='fr_algo_working_symbol') # ty:ignore[invalid-assignment]
if vk_get:
best_symbol_by_exchange: dict = json.loads(s=vk_get)
best_symbol: str = best_symbol_by_exchange['EXTEND']['symbol'] best_symbol: str = best_symbol_by_exchange['EXTEND']['symbol']
if best_symbol != SYMBOL: if best_symbol != SYMBOL:
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}') logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
@@ -63,6 +65,8 @@ async def ws_stream():
CHANGE_SYMBOL = True CHANGE_SYMBOL = True
await websocket.close() await websocket.close()
break break
else:
logging.warning('Extend Orderbook WS: "fr_algo_working_symbol" is None; not switching to new symbol...')
ts_arrival = round(datetime.now().timestamp()*1000) ts_arrival = round(datetime.now().timestamp()*1000)
if isinstance(message, str): if isinstance(message, str):

View File

@@ -58,7 +58,9 @@ async def ws_stream():
async for message in websocket: async for message in websocket:
### Update Symbol if Algo Outputs Change ### ### Update Symbol if Algo Outputs Change ###
if ALLOW_SYMBOL_CHG: if ALLOW_SYMBOL_CHG:
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_algo_working_symbol')) # ty:ignore[invalid-argument-type] vk_get: str = VAL_KEY.get(name='fr_algo_working_symbol') # ty:ignore[invalid-assignment]
if vk_get:
best_symbol_by_exchange: dict = json.loads(s=vk_get)
best_symbol: str = best_symbol_by_exchange['EXTEND']['symbol'] best_symbol: str = best_symbol_by_exchange['EXTEND']['symbol']
if best_symbol != SYMBOL: if best_symbol != SYMBOL:
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}') logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
@@ -66,6 +68,8 @@ async def ws_stream():
CHANGE_SYMBOL = True CHANGE_SYMBOL = True
await websocket.close() await websocket.close()
break break
else:
logging.warning('Extend Trades WS: "fr_algo_working_symbol" is None; not switching to new symbol...')
ts_arrival = round(datetime.now().timestamp()*1000) ts_arrival = round(datetime.now().timestamp()*1000)
if isinstance(message, str): if isinstance(message, str):