refactor valkey into objects with health check
This commit is contained in:
@@ -47,6 +47,7 @@ async def ws_stream():
|
||||
try:
|
||||
data: dict = json.loads(message)
|
||||
if data:
|
||||
|
||||
VAL_KEY.set(name=VK_CHANNEL, value=json.dumps(obj=data))
|
||||
# print(f'VK_SAVED: {len(data)}')
|
||||
continue
|
||||
@@ -29,12 +29,12 @@ USE_VK: bool = True
|
||||
# VK_FUND_RATE = 'fund_rate_extended'
|
||||
VK_FUND_RATE_ALL = 'fund_rate_extended_all'
|
||||
|
||||
CON: AsyncContextManager | None = None
|
||||
VAL_KEY = None
|
||||
CON: AsyncContextManager
|
||||
VAL_KEY: valkey.Valkey
|
||||
|
||||
### Logging ###
|
||||
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 ###
|
||||
WSS_URL = "wss://api.starknet.extended.exchange/stream.extended.exchange/v1/funding/"
|
||||
@@ -99,8 +99,8 @@ async def main():
|
||||
if USE_VK:
|
||||
VAL_KEY = valkey.Valkey(host='localhost', port=6379, db=0)
|
||||
else:
|
||||
VAL_KEY = None
|
||||
logging.warning("VALKEY NOT BEING USED, NO DATA WILL BE PUBLISHED")
|
||||
raise NotImplementedError('Cannot run without VK')
|
||||
|
||||
if USE_DB:
|
||||
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 ws_stream()
|
||||
else:
|
||||
CON = None
|
||||
logging.warning("DATABASE NOT BEING USED, NO DATA WILL BE RECORDED")
|
||||
await ws_stream()
|
||||
raise NotImplementedError('DB not implemented')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
1136
algo.ipynb
1136
algo.ipynb
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"Updated_Timestamp": 1778178429902,
|
||||
"Updated_Timestamp": 1778798867547,
|
||||
"Config": {
|
||||
"Loop_Sleep_Sec": 0.0,
|
||||
"Max_Order_Over_Notional_Ratio": 1.05,
|
||||
"Max_Order_Over_Notional_Ratio": 1.5,
|
||||
"Max_Target_Notional": 0.0,
|
||||
"Min_Time_To_Funding_Minutes": 57,
|
||||
"Min_Fund_Rate_Pct_To_Trade": 0.0,
|
||||
|
||||
@@ -8,6 +8,9 @@ from datetime import datetime
|
||||
import valkey
|
||||
from dotenv import load_dotenv
|
||||
import modules.utils as utils
|
||||
import modules.structs as structs
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
'''
|
||||
TO DO:
|
||||
@@ -18,15 +21,24 @@ TO DO:
|
||||
VK_IN: str = 'fr_orchestrator_input'
|
||||
VK_OUT: str = 'fr_orchestrator_output'
|
||||
|
||||
CONFIG_FILEPATH: str = '/algo_local_drive/algo_config.json'
|
||||
# CONFIG_FILEPATH: str = 'algo_config.json'
|
||||
|
||||
### Logging ###
|
||||
load_dotenv()
|
||||
LOG_FILEPATH: str = f'{os.getenv("LOGS_PATH")}/Fund_Rate_Algo_Orchestrator.log'
|
||||
|
||||
async def main() -> None:
|
||||
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:
|
||||
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['Updated_Timestamp'] = timestamp
|
||||
|
||||
if not Algo_Config:
|
||||
raise ValueError(f'Algo Orchestrator, config is none: {Algo_Config}')
|
||||
|
||||
# Update Config w Update Data
|
||||
Algo_Config: dict = utils.rec_set_dict(orig_dict=Algo_Config, new_dict=data)
|
||||
# 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
|
||||
with open(file=CONFIG_FILEPATH, mode='w', encoding='utf-8') as f:
|
||||
|
||||
280
aster.ipynb
280
aster.ipynb
@@ -2,7 +2,7 @@
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 1,
|
||||
"id": "3a269644",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"execution_count": 2,
|
||||
"id": "4395fabb",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -92,7 +92,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"execution_count": 3,
|
||||
"id": "2122885a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -102,7 +102,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": 4,
|
||||
"id": "e7341726",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -352,7 +352,7 @@
|
||||
" {'symbol': 'CHIPUSDT',\n",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
" 'markPrice': '0.00000000',\n",
|
||||
" 'markPrice': '0.05884243',\n",
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '50',\n",
|
||||
@@ -363,7 +363,7 @@
|
||||
" 'positionSide': 'BOTH',\n",
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '0',\n",
|
||||
" 'updateTime': 0},\n",
|
||||
" 'updateTime': 1778212620637},\n",
|
||||
" {'symbol': '1000BONKUSDT',\n",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -619,6 +619,21 @@
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '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",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -1132,18 +1147,18 @@
|
||||
" {'symbol': 'HYPEUSDT',\n",
|
||||
" 'positionAmt': '0.00',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
" 'markPrice': '0.00000000',\n",
|
||||
" 'markPrice': '42.14600000',\n",
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '300',\n",
|
||||
" 'maxNotionalValue': '1000',\n",
|
||||
" 'leverage': '150',\n",
|
||||
" 'maxNotionalValue': '2000',\n",
|
||||
" 'marginType': 'cross',\n",
|
||||
" 'isolatedMargin': '0.00000000',\n",
|
||||
" 'isAutoAddMargin': 'false',\n",
|
||||
" 'positionSide': 'BOTH',\n",
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '0',\n",
|
||||
" 'updateTime': 0},\n",
|
||||
" 'updateTime': 1778174116771},\n",
|
||||
" {'symbol': 'SYRUPUSDT',\n",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -1436,7 +1451,7 @@
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '2',\n",
|
||||
" 'maxNotionalValue': '2000000',\n",
|
||||
" 'maxNotionalValue': '5000000',\n",
|
||||
" 'marginType': 'cross',\n",
|
||||
" 'isolatedMargin': '0.00000000',\n",
|
||||
" 'isAutoAddMargin': 'false',\n",
|
||||
@@ -1552,7 +1567,7 @@
|
||||
" {'symbol': 'SOLUSDT',\n",
|
||||
" 'positionAmt': '0.00',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
" 'markPrice': '0.00000000',\n",
|
||||
" 'markPrice': '88.08336403',\n",
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '100',\n",
|
||||
@@ -2182,7 +2197,7 @@
|
||||
" {'symbol': 'ETHUSDT',\n",
|
||||
" 'positionAmt': '0.000',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
" 'markPrice': '2327.94576938',\n",
|
||||
" 'markPrice': '2278.29032657',\n",
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '150',\n",
|
||||
@@ -2193,7 +2208,7 @@
|
||||
" 'positionSide': 'BOTH',\n",
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '0',\n",
|
||||
" 'updateTime': 1777835550378},\n",
|
||||
" 'updateTime': 1778127016832},\n",
|
||||
" {'symbol': 'ZKCUSDT',\n",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -2332,7 +2347,7 @@
|
||||
" {'symbol': 'XAGUSDT',\n",
|
||||
" 'positionAmt': '0.000',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
" 'markPrice': '75.87000000',\n",
|
||||
" 'markPrice': '79.55000000',\n",
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '100',\n",
|
||||
@@ -2351,7 +2366,7 @@
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '20',\n",
|
||||
" 'maxNotionalValue': '5000',\n",
|
||||
" 'maxNotionalValue': '500000',\n",
|
||||
" 'marginType': 'cross',\n",
|
||||
" 'isolatedMargin': '0.00000000',\n",
|
||||
" 'isAutoAddMargin': 'false',\n",
|
||||
@@ -2524,6 +2539,21 @@
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '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",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -2539,6 +2569,21 @@
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '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",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -2989,6 +3034,21 @@
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '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",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -3154,6 +3214,21 @@
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '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",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -3289,6 +3364,21 @@
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '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",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -3304,6 +3394,21 @@
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '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",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -3394,6 +3499,21 @@
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '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",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -3562,7 +3682,7 @@
|
||||
" {'symbol': 'INITUSDT',\n",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
" 'markPrice': '0.08765944',\n",
|
||||
" 'markPrice': '0.11143193',\n",
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '50',\n",
|
||||
@@ -3697,7 +3817,7 @@
|
||||
" {'symbol': 'BNBUSDT',\n",
|
||||
" 'positionAmt': '0.00',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
" 'markPrice': '619.51000000',\n",
|
||||
" 'markPrice': '639.19000000',\n",
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '100',\n",
|
||||
@@ -3727,7 +3847,7 @@
|
||||
" {'symbol': 'XMRUSDT',\n",
|
||||
" 'positionAmt': '0.000',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
" 'markPrice': '0.00000000',\n",
|
||||
" 'markPrice': '390.77183333',\n",
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '50',\n",
|
||||
@@ -3817,7 +3937,7 @@
|
||||
" {'symbol': 'DOGEUSDT',\n",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
" 'markPrice': '0.00000000',\n",
|
||||
" 'markPrice': '0.10631546',\n",
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '75',\n",
|
||||
@@ -3828,7 +3948,7 @@
|
||||
" 'positionSide': 'BOTH',\n",
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '0',\n",
|
||||
" 'updateTime': 0},\n",
|
||||
" 'updateTime': 1778098357669},\n",
|
||||
" {'symbol': 'GPSUSDT',\n",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -3971,7 +4091,7 @@
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '2',\n",
|
||||
" 'maxNotionalValue': '60000',\n",
|
||||
" 'maxNotionalValue': '5000000',\n",
|
||||
" 'marginType': 'cross',\n",
|
||||
" 'isolatedMargin': '0.00000000',\n",
|
||||
" 'isAutoAddMargin': 'false',\n",
|
||||
@@ -4039,6 +4159,21 @@
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '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",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -4174,6 +4309,21 @@
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '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",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -4346,7 +4496,7 @@
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '20',\n",
|
||||
" 'maxNotionalValue': '25000',\n",
|
||||
" 'maxNotionalValue': '500000',\n",
|
||||
" 'marginType': 'cross',\n",
|
||||
" 'isolatedMargin': '0.00000000',\n",
|
||||
" 'isAutoAddMargin': 'false',\n",
|
||||
@@ -5047,7 +5197,7 @@
|
||||
" {'symbol': '4USDT',\n",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
" 'markPrice': '0.00000000',\n",
|
||||
" 'markPrice': '0.01297365',\n",
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '50',\n",
|
||||
@@ -5058,7 +5208,7 @@
|
||||
" 'positionSide': 'BOTH',\n",
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '0',\n",
|
||||
" 'updateTime': 0},\n",
|
||||
" 'updateTime': 1777965261969},\n",
|
||||
" {'symbol': 'BELUSDT',\n",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -5330,20 +5480,20 @@
|
||||
" 'isolatedWallet': '0',\n",
|
||||
" 'updateTime': 0},\n",
|
||||
" {'symbol': 'LITUSDT',\n",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
" 'markPrice': '0.93992527',\n",
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'positionAmt': '179',\n",
|
||||
" 'entryPrice': '0.9766',\n",
|
||||
" 'markPrice': '0.97480000',\n",
|
||||
" 'unRealizedProfit': '-0.32220000',\n",
|
||||
" 'liquidationPrice': '0.68966790',\n",
|
||||
" 'leverage': '50',\n",
|
||||
" 'maxNotionalValue': '2500',\n",
|
||||
" 'marginType': 'cross',\n",
|
||||
" 'isolatedMargin': '0.00000000',\n",
|
||||
" 'isAutoAddMargin': 'false',\n",
|
||||
" 'positionSide': 'BOTH',\n",
|
||||
" 'notional': '0',\n",
|
||||
" 'notional': '174.48920000',\n",
|
||||
" 'isolatedWallet': '0',\n",
|
||||
" 'updateTime': 1777819835027},\n",
|
||||
" 'updateTime': 1778213915476},\n",
|
||||
" {'symbol': 'FFUSDT',\n",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -5389,6 +5539,21 @@
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '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",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -5509,6 +5674,21 @@
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '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",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -5947,7 +6127,7 @@
|
||||
" {'symbol': 'WLFIUSDT',\n",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
" 'markPrice': '0.05870000',\n",
|
||||
" 'markPrice': '0.07422827',\n",
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '25',\n",
|
||||
@@ -6154,6 +6334,21 @@
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '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",
|
||||
" 'positionAmt': '0',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -6607,7 +6802,7 @@
|
||||
" {'symbol': 'BTCUSDT',\n",
|
||||
" 'positionAmt': '0.000',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
" 'markPrice': '78659.35912065',\n",
|
||||
" 'markPrice': '79558.40000000',\n",
|
||||
" 'unRealizedProfit': '0.00000000',\n",
|
||||
" 'liquidationPrice': '0',\n",
|
||||
" 'leverage': '150',\n",
|
||||
@@ -6664,6 +6859,21 @@
|
||||
" 'notional': '0',\n",
|
||||
" 'isolatedWallet': '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",
|
||||
" 'positionAmt': '0.00',\n",
|
||||
" 'entryPrice': '0.0',\n",
|
||||
@@ -6876,7 +7086,7 @@
|
||||
" 'updateTime': 0}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 11,
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ services:
|
||||
context: ./
|
||||
dockerfile: ./algo/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
|
||||
- /root/data:/root/data: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
|
||||
network_mode: "host"
|
||||
|
||||
@@ -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:
|
||||
# algo:
|
||||
@@ -9,126 +9,106 @@ services:
|
||||
# dockerfile: ./algo/Dockerfile
|
||||
# depends_on:
|
||||
# - algo_orchestrator
|
||||
# - engine_best_funding_rate
|
||||
# - ws_aster
|
||||
# - ws_aster_user
|
||||
# - ws_extended_fund_rate
|
||||
# - ws_extended_orderbook
|
||||
# - ws_extended_trades
|
||||
# - ws_extended_user
|
||||
# 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
|
||||
# - /root/data:/root/data:rw # Read-write access to data
|
||||
# - /root/logs:/root/logs:rw # Read-write access to data
|
||||
# network_mode: "host"
|
||||
algo_orchestrator:
|
||||
container_name: algo_orchestrator
|
||||
restart: "unless-stopped"
|
||||
restart: "no"
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./algo_orchestrator/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
|
||||
- /root/data:/root/data: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
|
||||
network_mode: "host"
|
||||
engine_best_funding_rate:
|
||||
container_name: engine_best_funding_rate
|
||||
restart: "unless-stopped"
|
||||
restart: "no"
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./engine_best_funding_rate/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_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
|
||||
- /root/data:/root/data:rw # Read-write access to data
|
||||
- /root/logs:/root/logs:rw # Read-write access to data
|
||||
network_mode: "host"
|
||||
ws_aster:
|
||||
container_name: ws_aster
|
||||
restart: "unless-stopped"
|
||||
restart: "no"
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./ws_aster/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
|
||||
- /root/data:/root/data:rw # Read-write access to data
|
||||
- /root/logs:/root/logs:rw # Read-write access to data
|
||||
network_mode: "host"
|
||||
ws_aster_user:
|
||||
container_name: ws_aster_user
|
||||
restart: "unless-stopped"
|
||||
restart: "no"
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./ws_aster_user/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
|
||||
- /root/data:/root/data:rw # Read-write access to data
|
||||
- /root/logs:/root/logs:rw # Read-write access to data
|
||||
network_mode: "host"
|
||||
ws_extended_fund_rate:
|
||||
container_name: ws_extended_fund_rate
|
||||
restart: "unless-stopped"
|
||||
restart: "no"
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./ws_extended_fund_rate/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
|
||||
- /root/data:/root/data:rw # Read-write access to data
|
||||
- /root/logs:/root/logs:rw # Read-write access to data
|
||||
network_mode: "host"
|
||||
ws_extended_orderbook:
|
||||
container_name: ws_extended_orderbook
|
||||
restart: "unless-stopped"
|
||||
restart: "no"
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./ws_extended_orderbook/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
|
||||
- /root/data:/root/data:rw # Read-write access to dataw
|
||||
- /root/logs:/root/logs:rw # Read-write access to data
|
||||
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:
|
||||
container_name: ws_extended_user
|
||||
restart: "unless-stopped"
|
||||
restart: "no"
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./ws_extended_user/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
|
||||
- /root/data:/root/data:rw # Read-write access to data
|
||||
- /root/logs:/root/logs:rw # Read-write access to data
|
||||
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:
|
||||
# container_name: ng
|
||||
# restart: "unless-stopped"
|
||||
# restart: "no"
|
||||
# build:
|
||||
# context: ./
|
||||
# dockerfile: ./ng/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
|
||||
# - /root/data:/root/data:rw # Read-write access to data
|
||||
# - /root/logs:/root/logs:rw # Read-write access to data
|
||||
# network_mode: "host"
|
||||
File diff suppressed because one or more lines are too long
@@ -93,7 +93,10 @@ async def get_aster_exch_info() -> pd.DataFrame:
|
||||
return df
|
||||
|
||||
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['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']
|
||||
].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 = []
|
||||
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)
|
||||
@@ -204,21 +204,41 @@ 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_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 = []
|
||||
|
||||
for index, row in df_best_fr_rate.iterrows():
|
||||
df = await get_candles(symbol=row['symbol_ext'])
|
||||
try:
|
||||
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_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})
|
||||
|
||||
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:
|
||||
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)}
|
||||
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))
|
||||
logging.info(f'BFR REFRESHED @ {datetime.now()}')
|
||||
time.sleep(LOOP_SLEEP_SEC)
|
||||
|
||||
129
engine_health.py
129
engine_health.py
@@ -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())
|
||||
197
main.py
197
main.py
@@ -24,7 +24,7 @@ import math
|
||||
import os
|
||||
import time
|
||||
import traceback
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime
|
||||
from decimal import ROUND_DOWN, ROUND_UP, ROUND_HALF_UP, Decimal
|
||||
from typing import AsyncContextManager
|
||||
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.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]:
|
||||
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
|
||||
else:
|
||||
logging.info('Balances Flattened - Updating to Trade New Symbols:')
|
||||
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' {Aster.symbol} -> {best_symbol_by_exchange_aster.symbol}')
|
||||
logging.info(f' {Extend.symbol} -> {best_symbol_by_exchange_extend.symbol}')
|
||||
|
||||
await aster_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
|
||||
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(
|
||||
min_start_procedure: int = 15,
|
||||
@@ -436,6 +445,12 @@ async def post_aster_order(
|
||||
# print_summary(use_logging=True)
|
||||
else:
|
||||
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': 'You’ve 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()
|
||||
|
||||
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"}};'
|
||||
logging.info('EXTEND EDIT ORDER, NOT FOUND, CANCELLING and continuing')
|
||||
await extend_cancel_all_orders()
|
||||
|
||||
# if Extend_Open_Orders:
|
||||
# Extend_Open_Orders.pop(0)
|
||||
# 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}')
|
||||
# await get_aster_collateral()
|
||||
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
|
||||
Aster.cancel_request_pending = False
|
||||
else:
|
||||
await get_extend_notional(resp=ws_pos_updates)
|
||||
await get_extend_notional()
|
||||
Extend.cancel_request_pending = False
|
||||
utils.send_tg_alert(f'FR_ALGO - {exch} PARTIALLY FILLED ({order_id})')
|
||||
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()
|
||||
if exch=='ASTER':
|
||||
# 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
|
||||
Aster.cancel_request_pending = False
|
||||
else:
|
||||
@@ -683,7 +699,7 @@ async def get_aster_notional_position(resp: list | None = None):
|
||||
pos_dict['timestamp_arrival'] = round(datetime.now().timestamp()*1000)
|
||||
|
||||
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')
|
||||
return
|
||||
|
||||
@@ -700,12 +716,12 @@ async def get_aster_notional_position(resp: list | None = None):
|
||||
raise ValueError(e)
|
||||
|
||||
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:
|
||||
Aster.notional_position = float(pos_dict['position_amount'])*float(pos_dict['entry_price'])
|
||||
if pos_dict.get('leverage') is not None:
|
||||
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}')
|
||||
await kill_algo()
|
||||
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.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}')
|
||||
await kill_algo()
|
||||
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}')
|
||||
|
||||
async def extend_cancel_all_orders():
|
||||
global Extend_Open_Orders
|
||||
|
||||
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}')
|
||||
|
||||
### KILL ALGO ###
|
||||
@@ -851,9 +871,10 @@ async def kill_algo():
|
||||
await aster_cancel_all_orders()
|
||||
await extend_cancel_all_orders()
|
||||
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')
|
||||
|
||||
|
||||
### ALGO LOOP ###
|
||||
async def run_algo():
|
||||
global Config
|
||||
@@ -878,14 +899,36 @@ async def run_algo():
|
||||
# print('__________Start___________')
|
||||
|
||||
### Load Algo Config ###
|
||||
Config = json.loads(VAL_KEY.get('fr_orchestrator_output')) # ty:ignore[invalid-argument-type]
|
||||
Config = structs.Algo_Config(**Config)
|
||||
Config.Config.Max_Target_Notional = float(min([Aster.mult, Extend.mult]) * Config.Config.Target_Open_Cash_Position)
|
||||
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.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
|
||||
|
||||
|
||||
### 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_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_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'
|
||||
|
||||
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
|
||||
else:
|
||||
fr_best_exch = 'EXTEND'
|
||||
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
|
||||
|
||||
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 {}
|
||||
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}')
|
||||
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)
|
||||
continue
|
||||
# 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 {}
|
||||
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}')
|
||||
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)
|
||||
continue
|
||||
# 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:
|
||||
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.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')
|
||||
if signal.exchange == 'EXTEND' and Decimal(str(Aster.notional_position)) < 0:
|
||||
pass
|
||||
if signal.exchange == 'ASTER' and Decimal(str(Aster.notional_position)) > 0:
|
||||
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_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})
|
||||
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: {'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} ]
|
||||
@@ -1136,7 +1207,7 @@ async def run_algo():
|
||||
open_order_dict = dict(Aster_Open_Orders[0])
|
||||
open_order_id = str(open_order_dict['order_id'])
|
||||
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:
|
||||
logging.critical(f'Aster cant find price on order obj: {open_order_dict}; e: {e}')
|
||||
await kill_algo()
|
||||
@@ -1226,7 +1297,7 @@ async def run_algo():
|
||||
)
|
||||
|
||||
### Output Algo Status ###
|
||||
await output_algo_status('WORKING')
|
||||
await output_algo_status('HEALTHY')
|
||||
|
||||
### 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):
|
||||
@@ -1246,13 +1317,13 @@ async def run_algo():
|
||||
time.sleep(Config.Config.Loop_Sleep_Sec)
|
||||
except KeyboardInterrupt:
|
||||
logging.info('CANCELLING OPEN ORDERS')
|
||||
await output_algo_status('STOPPING')
|
||||
await output_algo_status('UNHEALTHY')
|
||||
await kill_algo()
|
||||
except Exception as e:
|
||||
logging.error(traceback.format_exc())
|
||||
logging.critical(f'*** ALGO ENGINE CRASHED: {e}')
|
||||
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)}')
|
||||
await kill_algo()
|
||||
|
||||
@@ -1275,14 +1346,23 @@ async def main():
|
||||
await set_comb_open_symbols()
|
||||
# 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}')
|
||||
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]
|
||||
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)
|
||||
|
||||
Open_Symbols.pop(0)
|
||||
@@ -1297,36 +1377,45 @@ async def main():
|
||||
Config = json.load(file)
|
||||
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}')
|
||||
|
||||
VAL_KEY.set(name='fr_orchestrator_output', value=json.dumps(obj=Config.model_dump()))
|
||||
VAL_KEY.set(name='fr_algo_working_symbol', value=json.dumps(obj={'ASTER': asdict(obj=Aster), 'EXTEND': asdict(obj=Extend)}))
|
||||
if not Config.model_dump():
|
||||
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()
|
||||
|
||||
Algo_Status = structs.Algo_Status(
|
||||
last_update_ts_ms = int(round(datetime.now().timestamp()*1000, 2)),
|
||||
status = 'WORKING',
|
||||
expected_alpha = 0.00,
|
||||
model_ratio = 0.00,
|
||||
current_ratio = 0.00,
|
||||
)
|
||||
await output_algo_status('WORKING')
|
||||
Funding_Rates_Min_Remaining_Factor_Pcts = calc_fr_minutes_remaining_factor()
|
||||
|
||||
Algo_Status = structs.Algo_Status(
|
||||
last_update_ts_ms = int(round(datetime.now().timestamp()*1000, 2)),
|
||||
status = 'HEALTHY',
|
||||
expected_alpha = 0.00,
|
||||
model_ratio = 0.00,
|
||||
current_ratio = 0.00,
|
||||
)
|
||||
await output_algo_status('HEALTHY')
|
||||
|
||||
async with engine.connect() as CON:
|
||||
### ASTER SETUP ###
|
||||
# await get_aster_collateral()
|
||||
await get_aster_notional_position()
|
||||
await get_aster_exch_info()
|
||||
await get_aster_open_orders()
|
||||
### EXTEND SETUP ###
|
||||
# await get_extend_collateral()
|
||||
await get_extend_notional()
|
||||
await get_extend_exch_info()
|
||||
await get_extend_open_orders()
|
||||
async with engine.connect() as CON:
|
||||
### ASTER SETUP ###
|
||||
# await get_aster_collateral()
|
||||
await get_aster_notional_position()
|
||||
await get_aster_exch_info()
|
||||
await get_aster_open_orders()
|
||||
### EXTEND SETUP ###
|
||||
# await get_extend_collateral()
|
||||
await get_extend_notional()
|
||||
await get_extend_exch_info()
|
||||
await get_extend_open_orders()
|
||||
|
||||
await run_algo()
|
||||
await run_algo()
|
||||
|
||||
if __name__ == '__main__':
|
||||
START_TIME = round(datetime.now().timestamp()*1000)
|
||||
|
||||
@@ -4,13 +4,15 @@ from typing import Any
|
||||
|
||||
import valkey
|
||||
from pydantic import BaseModel
|
||||
|
||||
from datetime import datetime
|
||||
from sqlalchemy.util.typing import Self
|
||||
from collections.abc import Sequence, Callable
|
||||
import modules.utils as utils
|
||||
|
||||
def ret_true():
|
||||
return True
|
||||
|
||||
|
||||
class Locked_Value(Sequence):
|
||||
def __init__(self, initial_value: Any = None, unlock_func: Callable=ret_true):
|
||||
self._value: Any = initial_value
|
||||
@@ -88,16 +90,218 @@ class Current_Previous_Value:
|
||||
self._previous_value = self._value
|
||||
self._value = v
|
||||
|
||||
### Valkey Objects ###
|
||||
class VK_Check(BaseModel):
|
||||
status: str = 'HEALTHY' # HEALTHY | UNHEALTHY | DEAD
|
||||
method: str | None
|
||||
|
||||
|
||||
class VK_Checks(BaseModel):
|
||||
status: VK_Check = VK_Check(method='check_status')
|
||||
timestamp: VK_Check = VK_Check(method='check_timestamp')
|
||||
symbol: VK_Check = VK_Check(method='check_symbol')
|
||||
|
||||
async def run_checks(self, args: dict | None = None):
|
||||
for f in self.model_dump():
|
||||
method = getattr(getattr(self, f), 'method')
|
||||
if method is not None:
|
||||
await getattr(self, method)(args = args)
|
||||
|
||||
async def check_status(self, args: dict | None = None) -> None:
|
||||
# print('checking status')
|
||||
if self.status.status == '':
|
||||
self.status.status = 'UNHEALTHY'
|
||||
else:
|
||||
self.status.status = self.status.status
|
||||
|
||||
async def check_status_nested(self, args: dict | None = None) -> None:
|
||||
# print('checking status')
|
||||
if self.status.status == '':
|
||||
self.status.status = 'UNHEALTHY'
|
||||
else:
|
||||
self.status.status = self.status.status
|
||||
|
||||
async def check_timestamp(self, args: dict | None = None) -> None:
|
||||
# print('checking timestamp')
|
||||
if args is not None:
|
||||
ts = int(args.get('timestamp', 0))
|
||||
now = round(datetime.now().timestamp()*1000)
|
||||
|
||||
if (now - ts) > 1:
|
||||
self.timestamp.status = 'UNHEALTHY'
|
||||
else:
|
||||
self.timestamp.status = 'HEALTHY'
|
||||
else:
|
||||
raise ValueError("Must pass in 'timestamp' arg")
|
||||
|
||||
async def check_symbol(self, args: dict | None = None) -> None:
|
||||
# print('checking symbol')
|
||||
|
||||
if args is not None:
|
||||
symbol = utils.symbol_to_extend_fmt(args.get('algo_symbol', ''))
|
||||
vk_symbol = utils.symbol_to_extend_fmt(args.get('vk_symbol', ''))
|
||||
if symbol == vk_symbol:
|
||||
self.symbol.status = 'HEALTHY'
|
||||
else:
|
||||
self.symbol.status = 'UNHEALTHY'
|
||||
else:
|
||||
raise ValueError("Must pass in 'algo_symbol' and 'vk_symbol' args")
|
||||
|
||||
|
||||
class VK_Obj(BaseModel):
|
||||
vk_name: str
|
||||
timestamp: int = round(datetime.now().timestamp()*1000)
|
||||
status: str = 'HEALTHY' # HEALTHY | UNHEALTHY | DEAD
|
||||
checks: VK_Checks = VK_Checks()
|
||||
data: Any = None
|
||||
|
||||
async def get(self, VK_CON: valkey.Valkey) -> None:
|
||||
print('getting')
|
||||
vk_get: str = VK_CON.get(self.vk_name) # ty:ignore[invalid-assignment]
|
||||
vk_dict: dict = json.loads(vk_get)
|
||||
self.__init__(**vk_dict)
|
||||
|
||||
async def set(self, VK_CON: valkey.Valkey, data_override: Any = None) -> None:
|
||||
print('setting')
|
||||
if data_override is not None:
|
||||
self.data = data_override
|
||||
|
||||
self.timestamp: int = round(datetime.now().timestamp()*1000)
|
||||
|
||||
j: str = self.model_dump_json()
|
||||
VK_CON.set(self.vk_name, j)
|
||||
|
||||
### Valkey Archetypes ###
|
||||
# Engine - Health
|
||||
|
||||
# Engine - Orchestrator
|
||||
class VK_Orchestrator_Output(VK_Obj):
|
||||
vk_name: str = 'fr_orchestrator_output'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
# Algo
|
||||
class VK_Working_Symbol(VK_Obj):
|
||||
vk_name: str = 'fr_algo_working_symbol'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
class VK_Algo_Status(VK_Obj):
|
||||
vk_name: str = 'algo_status'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
# User - Orders
|
||||
class VK_User_Orders_Aster(VK_Obj):
|
||||
vk_name: str = 'fr_aster_user_orders'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
class VK_User_Orders_Extend(VK_Obj):
|
||||
vk_name: str = 'fr_extended_user_orders'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
# User - Trades
|
||||
class VK_User_Trades_Extend(VK_Obj):
|
||||
vk_name: str = 'fr_extended_user_trades'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
# User - Balances
|
||||
class VK_User_Balances_Aster(VK_Obj):
|
||||
vk_name: str = 'fr_aster_user_balances'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
class VK_User_Balances_Extend(VK_Obj):
|
||||
vk_name: str = 'fr_extended_user_balances'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
# User - Positions
|
||||
class VK_User_Positions_Aster(VK_Obj):
|
||||
vk_name: str = 'fr_aster_user_positions'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
class VK_User_Positions_Extend(VK_Obj):
|
||||
vk_name: str = 'fr_extended_user_positions'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
# Fund Rates
|
||||
class VK_FR_Aster(VK_Obj):
|
||||
vk_name: str = 'fund_rate_aster'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
|
||||
class VK_FR_All_Aster(VK_Obj):
|
||||
vk_name: str = 'fund_rate_aster_all'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
class VK_FR_Extend(VK_Obj):
|
||||
vk_name: str = 'fund_rate_extended'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
class VK_FR_All_Extend(VK_Obj):
|
||||
vk_name: str = 'fund_rate_extended_all'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
# Tickers
|
||||
class VK_Ticker_Aster(VK_Obj):
|
||||
vk_name: str = 'fut_ticker_aster'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
class VK_Ticker_Extend(VK_Obj):
|
||||
vk_name: str = 'fut_ticker_extended'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
# Trades
|
||||
class VK_Trade_Aster(VK_Obj):
|
||||
vk_name: str = 'fut_last_trade_aster'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
class VK_Trade_Extend(VK_Obj):
|
||||
vk_name: str = 'fut_last_trade_extended'
|
||||
checks: VK_Checks = VK_Checks(
|
||||
symbol = VK_Check(method = None)
|
||||
)
|
||||
|
||||
|
||||
### Algo Objects ###
|
||||
class Algo_Status(BaseModel):
|
||||
last_update_ts_ms: int
|
||||
status: str # 'WORKING' | 'STOPPED'
|
||||
status: str # 'WORKING' | 'STOPPED' ///// # ENUM: 'HEALTHY' | 'UNHEALTHY' | 'DEAD'
|
||||
expected_alpha: float
|
||||
model_ratio: float
|
||||
current_ratio: float
|
||||
|
||||
|
||||
# @dataclass(kw_only=True)
|
||||
class Algo_Config_Overrides(BaseModel):
|
||||
Allow_Ordering_Aster: bool
|
||||
Allow_Ordering_Extend: bool
|
||||
@@ -107,7 +311,6 @@ class Algo_Config_Overrides(BaseModel):
|
||||
Flip_Side_For_Testing: bool
|
||||
|
||||
|
||||
# @dataclass(kw_only=True)
|
||||
class Algo_Config_Config(BaseModel):
|
||||
Loop_Sleep_Sec: int
|
||||
Max_Order_Over_Notional_Ratio: float
|
||||
@@ -119,12 +322,12 @@ class Algo_Config_Config(BaseModel):
|
||||
Switch_To_Taker_Seconds: int
|
||||
Target_Open_Cash_Position: int
|
||||
|
||||
# @dataclass(kw_only=True)
|
||||
|
||||
class Algo_Config_Logging(BaseModel):
|
||||
Log_Summary_Each_Loop: bool
|
||||
Print_Summary_Each_Loop: bool
|
||||
|
||||
# @dataclass(kw_only=True)
|
||||
|
||||
class Algo_Config(BaseModel):
|
||||
Updated_Timestamp: int
|
||||
Config: Algo_Config_Config
|
||||
|
||||
@@ -2,9 +2,18 @@ import logging
|
||||
from dotenv import load_dotenv
|
||||
import requests
|
||||
import os
|
||||
import json
|
||||
from decimal import Decimal
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class JSONEncoder_Decimal(json.JSONEncoder):
|
||||
def default(self, o):
|
||||
if isinstance(o, Decimal):
|
||||
return float(o)
|
||||
return super(JSONEncoder_Decimal, self).default(o)
|
||||
|
||||
def upsert_list_of_dicts_by_id(list_of_dicts, new_dict, id='id', seq_check_field: str | None = None, reset_seq_id: bool = False) -> list[dict]:
|
||||
for index, item in enumerate(list_of_dicts):
|
||||
if item.get(id) == new_dict.get(id):
|
||||
|
||||
6
ng.py
6
ng.py
@@ -132,6 +132,8 @@ async def get_trades_hist() -> pd.DataFrame:
|
||||
WHERE timestamp_arrival > {start_ts}
|
||||
''')
|
||||
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_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)
|
||||
@@ -150,6 +152,8 @@ async def get_trades_hist() -> pd.DataFrame:
|
||||
WHERE timestamp_arrival > {start_ts}
|
||||
''')
|
||||
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_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)
|
||||
@@ -351,4 +355,4 @@ async def root():
|
||||
}).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)
|
||||
@@ -23,4 +23,6 @@ nicegui
|
||||
x10-python-trading-starknet
|
||||
eth-keys
|
||||
eth-account
|
||||
pydantic
|
||||
pydantic
|
||||
plotly
|
||||
docker
|
||||
51
ws_aster.py
51
ws_aster.py
@@ -17,6 +17,7 @@ import os
|
||||
from dotenv import load_dotenv
|
||||
import modules.db as db
|
||||
import modules.aster_db as aster_db
|
||||
import sys
|
||||
|
||||
### Allow only ipv4 ###
|
||||
def allowed_gai_family():
|
||||
@@ -37,9 +38,10 @@ load_dotenv()
|
||||
LOG_FILEPATH: str = f'{os.getenv(key="LOGS_PATH")}/Fund_Rate_Aster.log'
|
||||
|
||||
### 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_TRADES: str = f'{SYMBOL.lower()}@aggTrade'
|
||||
|
||||
@@ -83,17 +85,23 @@ async def ws_stream():
|
||||
async for message in websocket:
|
||||
### Update Symbol if Algo Outputs Change ###
|
||||
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']
|
||||
if best_symbol != SYMBOL:
|
||||
logging.info(f'Symbol Change: {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_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
|
||||
|
||||
ts_arrival = round(datetime.now().timestamp()*1000)
|
||||
@@ -104,18 +112,25 @@ async def ws_stream():
|
||||
if channel is not None:
|
||||
match channel:
|
||||
case c if c == STREAM_MARKPRICE:
|
||||
# print(f'MP: {data}')
|
||||
VAL_KEY_OBJ = json.dumps({
|
||||
'timestamp_arrival': ts_arrival,
|
||||
'timestamp_msg': data['data']['E'],
|
||||
'symbol': data['data']['s'],
|
||||
'mark_price': data['data']['p'],
|
||||
'index_price': data['data']['i'],
|
||||
'estimated_settle_price': data['data']['P'],
|
||||
'funding_rate': data['data']['r'],
|
||||
'next_funding_time_ts_ms': data['data']['T'],
|
||||
})
|
||||
VAL_KEY.set(VK_FUND_RATE, VAL_KEY_OBJ)
|
||||
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({
|
||||
'timestamp_arrival': ts_arrival,
|
||||
'timestamp_msg': d['E'],
|
||||
'symbol': d['s'],
|
||||
'mark_price': d['p'],
|
||||
'index_price': d['i'],
|
||||
'estimated_settle_price': d['P'],
|
||||
'funding_rate': d['r'],
|
||||
'next_funding_time_ts_ms': d['T'],
|
||||
})
|
||||
VAL_KEY.set(VK_FUND_RATE, VAL_KEY_OBJ)
|
||||
# print(f'MP: {d}')
|
||||
continue
|
||||
case c if c == STREAM_BOOKTICKER:
|
||||
# print(f'BT: {data}')
|
||||
|
||||
@@ -16,6 +16,8 @@ from sqlalchemy.ext.asyncio import create_async_engine
|
||||
import valkey
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
import sys
|
||||
import modules.utils as utils
|
||||
|
||||
|
||||
### Allow only ipv4 ###
|
||||
@@ -36,10 +38,11 @@ load_dotenv()
|
||||
LOG_FILEPATH: str = f'{os.getenv("LOGS_PATH")}/Fund_Rate_Extended_FR.log'
|
||||
|
||||
### CONSTANTS ###
|
||||
SYMBOL: str = 'ETH-USD'
|
||||
SYMBOL: str = 'ENA-USD'
|
||||
|
||||
### Globals ###
|
||||
ALLOW_SYMBOL_CHG: bool = True
|
||||
LOCAL_FUNDING_RATES = []
|
||||
|
||||
def time_round_down(dt, interval_mins=5) -> int: # returns timestamp in seconds
|
||||
interval_secs = interval_mins * 60
|
||||
@@ -51,35 +54,40 @@ def time_round_down(dt, interval_mins=5) -> int: # returns timestamp in seconds
|
||||
### Websocket ###
|
||||
async def ws_stream():
|
||||
global SYMBOL
|
||||
global LOCAL_FUNDING_RATES
|
||||
|
||||
while True:
|
||||
CHANGE_SYMBOL = False
|
||||
WSS_URL = f"wss://api.starknet.extended.exchange/stream.extended.exchange/v1/funding/{SYMBOL}"
|
||||
# CHANGE_SYMBOL = False
|
||||
WSS_URL = "wss://api.starknet.extended.exchange/stream.extended.exchange/v1/funding/"
|
||||
async for websocket in websockets.connect(WSS_URL):
|
||||
if CHANGE_SYMBOL:
|
||||
break
|
||||
# if CHANGE_SYMBOL:
|
||||
# break
|
||||
logging.info(f"Connected to {WSS_URL}")
|
||||
try:
|
||||
async for message in websocket:
|
||||
### Update Symbol if Algo Outputs Change ###
|
||||
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']
|
||||
if best_symbol != SYMBOL:
|
||||
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
|
||||
SYMBOL = best_symbol
|
||||
CHANGE_SYMBOL = True
|
||||
await websocket.close()
|
||||
break
|
||||
# CHANGE_SYMBOL = True
|
||||
# await websocket.close()
|
||||
# break
|
||||
|
||||
ts_arrival = round(datetime.now().timestamp()*1000)
|
||||
if isinstance(message, str):
|
||||
try:
|
||||
data = json.loads(message)
|
||||
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
|
||||
VAL_KEY_OBJ = json.dumps({
|
||||
fr_update = {
|
||||
'sequence_id': data['seq'],
|
||||
'timestamp_arrival': ts_arrival,
|
||||
'timestamp_msg': data['ts'],
|
||||
@@ -87,8 +95,12 @@ async def ws_stream():
|
||||
'funding_rate': float(data['data']['f']),
|
||||
'funding_rate_updated_ts_ms': data['data']['T'],
|
||||
'next_funding_time_ts_ms': fr_next_update_ts,
|
||||
})
|
||||
VAL_KEY.set(VK_FUND_RATE, VAL_KEY_OBJ)
|
||||
}
|
||||
if fr_update.get('symbol') == SYMBOL:
|
||||
VAL_KEY_OBJ = json.dumps(fr_update)
|
||||
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
|
||||
else:
|
||||
logging.info(f'Initial or unexpected data struct, skipping: {data}')
|
||||
|
||||
@@ -55,14 +55,18 @@ async def ws_stream():
|
||||
async for message in websocket:
|
||||
### Update Symbol if Algo Outputs Change ###
|
||||
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]
|
||||
best_symbol: str = best_symbol_by_exchange['EXTEND']['symbol']
|
||||
if best_symbol != SYMBOL:
|
||||
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
|
||||
SYMBOL = best_symbol
|
||||
CHANGE_SYMBOL = True
|
||||
await websocket.close()
|
||||
break
|
||||
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']
|
||||
if best_symbol != SYMBOL:
|
||||
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
|
||||
SYMBOL = best_symbol
|
||||
CHANGE_SYMBOL = True
|
||||
await websocket.close()
|
||||
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)
|
||||
if isinstance(message, str):
|
||||
|
||||
@@ -58,14 +58,18 @@ async def ws_stream():
|
||||
async for message in websocket:
|
||||
### Update Symbol if Algo Outputs Change ###
|
||||
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]
|
||||
best_symbol: str = best_symbol_by_exchange['EXTEND']['symbol']
|
||||
if best_symbol != SYMBOL:
|
||||
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
|
||||
SYMBOL = best_symbol
|
||||
CHANGE_SYMBOL = True
|
||||
await websocket.close()
|
||||
break
|
||||
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']
|
||||
if best_symbol != SYMBOL:
|
||||
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
|
||||
SYMBOL = best_symbol
|
||||
CHANGE_SYMBOL = True
|
||||
await websocket.close()
|
||||
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)
|
||||
if isinstance(message, str):
|
||||
|
||||
Reference in New Issue
Block a user