2026-05-04 18:04:45 +00:00
2026-04-30 04:32:49 +00:00
from x10 . utils . http import WrappedApiResponse
from x10 . perpetual . trading_client . trading_client import PerpetualTradingClient
2026-04-21 20:22:33 +00:00
import asyncio
import json
import logging
import math
import os
import time
import traceback
from datetime import datetime , timezone
2026-05-04 18:04:45 +00:00
from decimal import ROUND_DOWN , ROUND_UP , ROUND_HALF_UP , Decimal
2026-04-21 20:22:33 +00:00
from typing import AsyncContextManager
2026-04-30 04:32:49 +00:00
from dataclasses import dataclass , asdict
2026-04-24 07:29:26 +00:00
from typing import Any
2026-04-21 20:22:33 +00:00
import numpy as np
import pandas as pd
import requests
2026-04-24 07:29:26 +00:00
2026-04-21 20:22:33 +00:00
# import talib
import valkey
2026-04-24 07:29:26 +00:00
from dotenv import load_dotenv
2026-04-21 20:22:33 +00:00
from sqlalchemy import text
from sqlalchemy . ext . asyncio import create_async_engine
2026-04-30 04:32:49 +00:00
from x10 . models . order import OrderSide , PlacedOrderModel
2026-04-24 07:29:26 +00:00
import modules . utils as utils
2026-04-23 06:39:51 +00:00
import modules . aster_auth as aster_auth
import modules . extended_auth as extend_auth
2026-04-25 23:43:28 +00:00
import modules . structs as structs
2026-04-21 20:22:33 +00:00
2026-04-30 04:32:49 +00:00
### Clients ###
EXTEND_CLIENT : PerpetualTradingClient
CON : AsyncContextManager
VAL_KEY : valkey . Valkey
2026-04-21 20:22:33 +00:00
### Logging ###
load_dotenv ( )
2026-04-30 04:32:49 +00:00
LOG_FILEPATH : str = f ' { os . getenv ( key = " LOGS_PATH " ) } /Fund_Rate_Algo.log '
2026-04-21 20:22:33 +00:00
2026-04-25 23:43:28 +00:00
### Algo Config ###
2026-04-30 04:32:49 +00:00
ALGO_CONFIG : structs . Algo_Config
2026-04-27 17:57:58 +00:00
MIN_TIME_TO_FUNDING : int
2026-04-24 07:29:26 +00:00
2026-04-29 16:18:42 +00:00
### EXCHANGES ###
2026-05-01 20:45:26 +00:00
ASTER : structs . Perpetual_Exchange
EXTEND : structs . Perpetual_Exchange
2026-04-23 06:39:51 +00:00
### GLOBALS ###
2026-05-01 20:45:26 +00:00
Open_Symbols : list [ str ] = [ ]
2026-04-29 16:18:42 +00:00
Last_Aster_Fill_Time_Ts : float = 0.00
Just_Rejected_Or_Expired : bool = False
2026-05-05 16:38:45 +00:00
at_notional_target : structs . Locked_Value = structs . Locked_Value ( None )
ALPHA_TGT_NOTIONAL : structs . Current_Previous_Value = structs . Current_Previous_Value ( None )
2026-04-29 16:18:42 +00:00
2026-05-05 16:38:45 +00:00
ASTER_OPEN_ORDERS : list [ dict ] = [ ]
EXTEND_OPEN_ORDERS : list [ dict ] = [ ]
2026-04-24 07:29:26 +00:00
2026-04-23 06:39:51 +00:00
### FLAGS ###
2026-04-25 23:43:28 +00:00
Flags = structs . Flags ( )
2026-04-23 06:39:51 +00:00
2026-04-23 17:33:01 +00:00
### UTILS ###
2026-05-01 20:45:26 +00:00
# def round_decimal_down(value, decimal_places):
# # Construct precision string like '0.01' for 2 places
# fmt = f'0.{"0" * decimal_places}' if decimal_places > 0 else '0'
# precision = Decimal(fmt)
# return Decimal(str(value)).quantize(precision, rounding=ROUND_HALF_UP)
2026-04-23 16:34:47 +00:00
2026-05-05 16:38:45 +00:00
2026-04-23 16:34:47 +00:00
### OPEN ORDERS ###
2026-05-05 16:38:45 +00:00
async def handle_order_updates ( exch : str , local_open_orders : list [ dict ] , ws_open_orders : list [ dict ] ) - > list [ dict ] : # exch = 'ASTER' | 'EXTEND'
global Just_Rejected_Or_Expired
global Last_Aster_Fill_Time_Ts
if ws_open_orders :
for idx , o in enumerate ( local_open_orders ) :
o = dict ( o )
if o . get ( ' order_id ' ) is not None :
ws_order_id_field = ' order_id '
elif o . get ( ' orderId ' ) is not None :
ws_order_id_field = ' orderId '
else :
ws_order_id_field = ' id '
order_id = o [ ws_order_id_field ]
order_orig_status : str = o . get ( ' status ' ) if o . get ( ' status ' ) is not None else o [ ' order_status ' ] # ty:ignore[invalid-assignment]
order_update : list [ dict ] = [ dict ( ou ) for ou in ws_open_orders if dict ( ou ) . get ( ' order_id ' , None ) == order_id ]
if len ( order_update ) > 0 :
order_update : dict = order_update [ 0 ]
order_update_status : str = order_update . get ( ' status ' ) if order_update . get ( ' status ' ) is not None else order_update [ ' order_status ' ] # ty:ignore[invalid-assignment]
order_status_changed : bool = order_orig_status . upper ( ) != order_update_status . upper ( )
local_open_orders [ idx ] [ ' order_id ' ] = order_id
local_open_orders [ idx ] [ ' status ' ] = order_update_status
local_open_orders [ idx ] [ ' price ' ] = order_update . get ( ' price ' , 0 ) if order_update . get ( ' price ' ) is not None else order_update [ ' original_price ' ]
if order_status_changed :
logging . info ( f ' { exch } ORDER ( { order_id } ): { order_orig_status } -> { order_update_status } ' )
local_open_orders [ idx ] = order_update
if order_update_status in [ ' CANCELLED ' , ' CANCELED ' , ' EXPIRED ' , ' REJECTED ' ] :
logging . info ( f ' { exch } ORDER CANCELLED or EXPIRED: { order_id } ' )
local_open_orders . pop ( idx )
Just_Rejected_Or_Expired = True
utils . send_tg_alert ( f ' FR_ALGO - { exch } REJECTED ( { order_id } ) ' )
elif order_update_status in [ ' PARTIALLY_FILLED ' ] :
logging . info ( f ' { exch } ORDER PARTIALLY FILLED: { order_id } ' )
# await get_aster_collateral()
if exch == ' ASTER ' :
await get_aster_notional_position ( resp = ws_open_orders )
Last_Aster_Fill_Time_Ts = datetime . now ( ) . timestamp ( ) * 1000
else :
await get_extend_notional ( )
utils . send_tg_alert ( f ' FR_ALGO - { exch } PARTIALLY FILLED ( { order_id } ) ' )
elif order_update_status in [ ' FILLED ' ] :
logging . info ( f ' { exch } ORDER FILLED: { order_id } ' )
local_open_orders . pop ( idx )
# await get_aster_collateral()
if exch == ' ASTER ' :
await get_aster_notional_position ( resp = ws_open_orders )
Last_Aster_Fill_Time_Ts = datetime . now ( ) . timestamp ( ) * 1000
else :
await get_extend_notional ( )
utils . send_tg_alert ( f ' FR_ALGO - { exch } FILLED ( { order_id } ) ' )
else :
logging . critical ( f ' { exch } ORDER STATUS CHG TO UNEXPECTED VALUE, KILLING... ( { order_id } ): { order_orig_status } -> { order_update_status } ' )
await kill_algo ( )
return local_open_orders
2026-04-23 16:34:47 +00:00
async def get_aster_open_orders ( ) :
global ASTER_OPEN_ORDERS
fut_acct_openOrders = {
" url " : " /fapi/v3/openOrders " ,
" method " : " GET " ,
" params " : { }
}
2026-05-05 16:38:45 +00:00
ASTER_OPEN_ORDERS = await aster_auth . post_authenticated_url ( fut_acct_openOrders ) # ty:ignore[invalid-assignment]
2026-04-23 16:34:47 +00:00
async def get_extend_open_orders ( ) :
global EXTEND_OPEN_ORDERS
EXTEND_OPEN_ORDERS = list ( dict ( await EXTEND_CLIENT . account . get_open_orders ( ) ) . get ( ' data ' , 0 ) )
### WALLLET ###
2026-05-01 20:45:26 +00:00
async def get_aster_account_open_symbols ( ) - > list [ str ] :
fut_acct_positionRisk : dict = {
" url " : " /fapi/v3/positionRisk " ,
" method " : " GET " ,
" params " : {
2026-05-04 18:04:45 +00:00
' symbol ' : ' '
2026-05-01 20:45:26 +00:00
}
}
2026-05-04 18:04:45 +00:00
try :
resp : list = await aster_auth . post_authenticated_url ( req = fut_acct_positionRisk ) # ty:ignore[invalid-assignment]
except Exception as e :
logging . critical ( f ' JSONDecodeError trying to get Aster open orders: { e } ; resp: { resp } ' )
await kill_algo ( )
resp : list = [ ]
2026-05-01 20:45:26 +00:00
ld = [ utils . symbol_to_extend_fmt ( x [ ' symbol ' ] ) for x in resp if abs ( float ( x . get ( ' positionAmt ' , 0 ) ) ) > 0 ]
return ld
2026-04-23 16:34:47 +00:00
2026-04-30 04:32:49 +00:00
async def get_aster_notional_position ( resp : list | None = None ) :
2026-04-29 16:18:42 +00:00
global ASTER
2026-04-23 16:34:47 +00:00
2026-05-05 16:38:45 +00:00
previous_notional_obj = ASTER . notional_obj
previous_notional_position = ASTER . notional_position
2026-04-27 17:57:58 +00:00
2026-05-01 20:45:26 +00:00
if resp :
2026-05-05 16:38:45 +00:00
pos_dict = [ x for x in resp if x . get ( ' symbol ' , None ) == ASTER . symbol ]
if pos_dict :
pos_dict = pos_dict [ 0 ]
else :
pos_dict = { }
pos_dict [ ' side ' ] = ' LONG '
pos_dict [ ' entry_price ' ] = 0.00
pos_dict [ ' position_amount ' ] = 0.00
pos_dict [ ' unrealized_pnl ' ] = 0.00
pos_dict [ ' timestamp_arrival ' ] = round ( datetime . now ( ) . timestamp ( ) * 1000 )
# logging.info('get_aster_notional - No Positions')
else :
logging . info ( ' Getting Aster Notionals from API ' )
2026-04-30 04:32:49 +00:00
fut_acct_positionRisk : dict = {
2026-04-24 07:29:26 +00:00
" url " : " /fapi/v3/positionRisk " ,
" method " : " GET " ,
" params " : {
2026-04-25 23:43:28 +00:00
' symbol ' : ASTER . symbol ,
2026-04-24 07:29:26 +00:00
}
}
2026-05-01 20:45:26 +00:00
try :
resp : list = await aster_auth . post_authenticated_url ( req = fut_acct_positionRisk ) # ty:ignore[invalid-assignment]
2026-05-04 18:04:45 +00:00
except Exception as e :
logging . critical ( f ' JSONDecodeError trying to get Aster notional: { e } ; resp: { resp } ' )
await kill_algo ( )
resp : list = [ ]
2026-05-05 16:38:45 +00:00
pos_dict = [ x for x in resp if x . get ( ' symbol ' , None ) == ASTER . symbol ]
if pos_dict :
pos_dict = pos_dict [ 0 ]
else :
pos_dict = { }
pos_dict [ ' side ' ] = ' LONG '
pos_dict [ ' entry_price ' ] = 0.00
pos_dict [ ' position_amount ' ] = 0.00
pos_dict [ ' unrealized_pnl ' ] = 0.00
logging . info ( ' get_aster_notional - No Positions ' )
pos_dict [ ' timestamp_arrival ' ] = round ( datetime . now ( ) . timestamp ( ) * 1000 )
2026-05-04 18:04:45 +00:00
2026-05-05 16:38:45 +00:00
if previous_notional_obj :
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')
2026-04-27 17:57:58 +00:00
return
2026-04-24 07:29:26 +00:00
2026-05-05 16:38:45 +00:00
ASTER . notional_obj = pos_dict
2026-04-25 23:43:28 +00:00
2026-05-05 16:38:45 +00:00
if len ( pos_dict ) < 1 :
logging . info ( f ' BAD NOTIONAL - ASTER CHANGE: Empty pos_dict: { pos_dict } ; resp: { resp } ' )
2026-04-25 23:43:28 +00:00
await kill_algo ( )
2026-04-23 16:34:47 +00:00
2026-05-05 16:38:45 +00:00
ASTER . unrealized_pnl = float ( pos_dict [ ' unrealized_pnl ' ] ) if pos_dict . get ( ' unrealized_pnl ' ) is not None else float ( pos_dict [ ' unRealizedProfit ' ] )
2026-04-24 07:29:26 +00:00
2026-05-05 16:38:45 +00:00
if pos_dict . get ( ' notional ' ) is not None :
ASTER . notional_position = float ( pos_dict [ ' notional ' ] ) - ASTER . unrealized_pnl
2026-04-24 07:29:26 +00:00
else :
2026-05-05 16:38:45 +00:00
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 ) > ALGO_CONFIG . Config . Max_Target_Notional * ALGO_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: { ALGO_CONFIG . Config . Max_Target_Notional } ' )
2026-04-25 23:43:28 +00:00
await kill_algo ( )
2026-05-05 16:38:45 +00:00
if ASTER . notional_position != previous_notional_position :
logging . info ( f ' ASTER NOTIONAL CHANGE: { previous_notional_position : .2f } -> { ASTER . notional_position : .2f } ; UR PNL: { ASTER . unrealized_pnl : .2f } ; MULT: { ASTER . mult : .0f } ; resp: { bool ( resp ) } ' )
2026-05-01 20:45:26 +00:00
async def get_extend_account_open_symbols ( ) - > list [ str ] :
resp = dict ( await EXTEND_CLIENT . account . get_positions ( ) ) . get ( ' data ' , [ ] )
ld = [ x . market for x in list ( resp ) if abs ( float ( x . size ) ) > 0 ]
return ld
async def set_comb_open_symbols ( ) - > None :
global Open_Symbols
open_aster_symbols = await get_aster_account_open_symbols ( )
open_extend_symbols = await get_extend_account_open_symbols ( )
Open_Symbols = list ( set ( open_aster_symbols + open_extend_symbols ) )
2026-04-23 16:34:47 +00:00
2026-04-30 04:32:49 +00:00
async def get_extend_notional ( resp : list | None = None ) :
2026-04-29 16:18:42 +00:00
global EXTEND
2026-04-23 16:34:47 +00:00
2026-05-05 16:38:45 +00:00
previous_notional_obj = EXTEND . notional_obj
previous_notional_position = EXTEND . notional_position
2026-04-28 22:40:35 +00:00
2026-04-24 07:29:26 +00:00
if not resp :
2026-04-28 22:40:35 +00:00
resp = dict ( await EXTEND_CLIENT . account . get_positions ( ) ) . get ( ' data ' , [ ] )
2026-04-29 16:18:42 +00:00
pos_dict = [ dict ( d ) for d in resp if dict ( d ) . get ( ' market ' ) == EXTEND . symbol ]
2026-04-28 22:40:35 +00:00
if pos_dict :
pos_dict = pos_dict [ 0 ]
2026-05-04 18:04:45 +00:00
pos_dict [ ' timestamp_arrival ' ] = round ( datetime . now ( ) . timestamp ( ) * 1000 )
2026-04-25 23:43:28 +00:00
else :
2026-04-28 22:40:35 +00:00
pos_dict = { }
pos_dict [ ' side ' ] = ' LONG '
pos_dict [ ' value ' ] = 0.00
2026-05-04 18:04:45 +00:00
pos_dict [ ' unrealised_pnl ' ] = 0.00
pos_dict [ ' timestamp_arrival ' ] = round ( datetime . now ( ) . timestamp ( ) * 1000 )
2026-04-28 22:40:35 +00:00
logging . info ( ' get_extend_notional - No Positions ' )
else :
2026-04-29 16:18:42 +00:00
pos_dict = [ dict ( d ) for d in resp if dict ( d ) . get ( ' market ' ) == EXTEND . symbol ]
2026-04-28 22:40:35 +00:00
if pos_dict :
pos_dict = pos_dict [ 0 ]
else :
pos_dict = { }
pos_dict [ ' side ' ] = ' LONG '
pos_dict [ ' value ' ] = 0.00
2026-05-04 18:04:45 +00:00
pos_dict [ ' unrealised_pnl ' ] = 0.00
pos_dict [ ' timestamp_arrival ' ] = round ( datetime . now ( ) . timestamp ( ) * 1000 )
2026-05-01 20:45:26 +00:00
# logging.info('get_extend_notional - No Positions')
2026-05-04 18:04:45 +00:00
# pos_dict['timestamp_arrival'] = round(datetime.now().timestamp()*1000)
2026-04-28 22:40:35 +00:00
2026-05-05 16:38:45 +00:00
if previous_notional_obj :
2026-04-28 22:40:35 +00:00
if previous_notional_obj [ ' timestamp_arrival ' ] > pos_dict [ ' timestamp_arrival ' ] :
2026-05-04 18:04:45 +00:00
# logging.info(f'EXTEND 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')
2026-04-28 22:40:35 +00:00
return
2026-05-04 18:04:45 +00:00
else :
previous_notional_obj = { }
2026-04-28 22:40:35 +00:00
2026-05-05 16:38:45 +00:00
EXTEND . notional_obj = pos_dict
2026-04-28 22:40:35 +00:00
2026-05-05 16:38:45 +00:00
EXTEND . unrealized_pnl = pos_dict . get ( ' unrealised_pnl ' , 0 )
2026-04-28 22:40:35 +00:00
position_side = pos_dict [ ' side ' ] # LONG or SHORT
notional_pos_abs = abs ( float ( pos_dict [ ' value ' ] ) )
if position_side == ' LONG ' :
notional_pos_sided = notional_pos_abs
elif position_side == ' SHORT ' :
notional_pos_sided = notional_pos_abs * - 1
else :
logging . info ( f ' EXTEND BAD SIDE ON POSITION UPDATE: { pos_dict } ' )
2026-05-05 16:38:45 +00:00
EXTEND . notional_position = notional_pos_sided - float ( EXTEND . unrealized_pnl )
2026-05-01 20:45:26 +00:00
EXTEND . mult = pos_dict . get ( ' leverage ' , EXTEND . mult )
2026-05-05 16:38:45 +00:00
if abs ( EXTEND . notional_position ) > ALGO_CONFIG . Config . Max_Target_Notional * ALGO_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 } ' )
2026-04-28 22:40:35 +00:00
await kill_algo ( )
2026-05-05 16:38:45 +00:00
if EXTEND . notional_position != previous_notional_position :
logging . info ( f ' EXTEND NOTIONAL CHANGE: { previous_notional_position } [ { previous_notional_obj . get ( ' timestamp_arrival ' ) } ] -> { EXTEND . notional_position : .2f } [ { EXTEND . notional_obj [ ' timestamp_arrival ' ] } ]; UR PNL: { EXTEND . unrealized_pnl : .2f } ; MULT: { EXTEND . mult } ; resp: { bool ( resp ) } ' )
2026-04-23 16:34:47 +00:00
### EXCHANGE INFO ###
2026-05-01 20:45:26 +00:00
async def get_aster_exch_info ( symbol_override : str | None = None ) :
global ASTER
2026-04-23 16:34:47 +00:00
2026-05-01 20:45:26 +00:00
if symbol_override :
ASTER . symbol = utils . symbol_to_aster_fmt ( symbol_override )
2026-04-30 04:32:49 +00:00
fut_acct_exchangeInfo : dict = {
2026-04-23 16:34:47 +00:00
" url " : " /fapi/v3/exchangeInfo " ,
" method " : " GET " ,
" params " : { }
}
2026-04-30 04:32:49 +00:00
r : dict = await aster_auth . post_authenticated_url ( fut_acct_exchangeInfo ) # ty:ignore[invalid-assignment]
s : list = r [ ' symbols ' ]
2026-05-01 20:45:26 +00:00
d : dict = [ d for d in s if d . get ( ' symbol ' , None ) == ASTER . symbol ] [ 0 ]
2026-04-30 04:32:49 +00:00
f : dict = [ f for f in d [ ' filters ' ] if f . get ( ' filterType ' , None ) == ' LOT_SIZE ' ] [ 0 ]
2026-05-01 20:45:26 +00:00
q : dict = [ f for f in d [ ' filters ' ] if f . get ( ' filterType ' , None ) == ' PRICE_FILTER ' ] [ 0 ]
2026-05-04 18:04:45 +00:00
n : dict = [ f for f in d [ ' filters ' ] if f . get ( ' filterType ' , None ) == ' MIN_NOTIONAL ' ] [ 0 ]
2026-05-01 20:45:26 +00:00
min_qty = float ( f [ ' minQty ' ] )
min_qty = int ( min_qty ) if min_qty == int ( min_qty ) else min_qty
min_price = float ( q [ ' minPrice ' ] )
min_price = int ( min_price ) if min_price == int ( min_price ) else min_price
ASTER . min_order_size = min_qty
ASTER . min_price = min_price
2026-05-04 18:04:45 +00:00
ASTER . min_notional = float ( n [ ' notional ' ] )
2026-04-23 16:34:47 +00:00
2026-05-01 20:45:26 +00:00
async def get_extend_exch_info ( symbol_override : str | None = None ) :
global EXTEND
2026-04-23 16:34:47 +00:00
2026-05-01 20:45:26 +00:00
if symbol_override :
EXTEND . symbol = utils . symbol_to_extend_fmt ( symbol_override )
2026-04-23 16:34:47 +00:00
r = await EXTEND_CLIENT . markets_info . get_markets_dict ( )
2026-05-01 20:45:26 +00:00
EXTEND . min_order_size = float ( r [ EXTEND . symbol ] . trading_config . min_order_size )
EXTEND . min_price = float ( r [ EXTEND . symbol ] . trading_config . min_price_change )
2026-04-23 16:34:47 +00:00
2026-04-23 17:33:01 +00:00
### CANCEL ORDERS ###
async def aster_cancel_all_orders ( ) :
2026-04-24 07:29:26 +00:00
cancel_all_open_orders = {
" url " : " /fapi/v3/allOpenOrders " ,
" method " : " DELETE " ,
" params " : {
2026-05-01 20:45:26 +00:00
' symbol ' : ASTER . symbol ,
2026-04-24 07:29:26 +00:00
}
}
r = await aster_auth . post_authenticated_url ( cancel_all_open_orders )
logging . info ( f ' ASTER CANCEL ALL OPEN ORDERS RESP: { r } ' )
async def extend_cancel_all_orders ( ) :
2026-04-29 16:18:42 +00:00
r = await EXTEND_CLIENT . orders . mass_cancel ( markets = [ EXTEND . symbol ] )
2026-04-24 07:29:26 +00:00
logging . info ( f ' EXTEND CANCEL ALL OPEN ORDERS RESP: { r } ' )
2026-04-23 17:33:01 +00:00
2026-04-24 07:29:26 +00:00
### KILL ALGO ###
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 ' )
raise ValueError ( ' KILL FLAG ACTIVATED ' )
2026-04-23 17:33:01 +00:00
2026-04-23 16:34:47 +00:00
### ALGO LOOP ###
2026-04-23 06:39:51 +00:00
async def run_algo ( ) :
2026-04-29 16:18:42 +00:00
global ASTER
global EXTEND
2026-04-25 23:43:28 +00:00
global ALGO_CONFIG
2026-04-27 17:57:58 +00:00
global MIN_TIME_TO_FUNDING
2026-04-26 06:10:18 +00:00
global ASTER_OPEN_ORDERS
global EXTEND_OPEN_ORDERS
2026-05-05 16:38:45 +00:00
2026-04-29 16:18:42 +00:00
global Last_Aster_Fill_Time_Ts
global Just_Rejected_Or_Expired
2026-05-05 16:38:45 +00:00
global ALPHA_TGT_NOTIONAL
global at_notional_target
2026-04-24 07:29:26 +00:00
2026-04-21 20:22:33 +00:00
try :
while True :
loop_start = time . time ( )
2026-04-25 23:43:28 +00:00
# print('__________Start___________')
2026-05-05 16:38:45 +00:00
### Load ALGO CONIFG ###
2026-04-30 04:32:49 +00:00
ALGO_CONFIG = json . loads ( VAL_KEY . get ( ' fr_orchestrator_output ' ) ) # ty:ignore[invalid-argument-type]
2026-04-29 16:18:42 +00:00
ALGO_CONFIG = structs . Algo_Config ( * * ALGO_CONFIG )
ALGO_CONFIG . Config . Max_Target_Notional = float ( min ( [ ASTER . mult , EXTEND . mult ] ) * ALGO_CONFIG . Config . Target_Open_Cash_Position )
2026-04-25 23:43:28 +00:00
2026-04-29 16:18:42 +00:00
MIN_TIME_TO_FUNDING = ALGO_CONFIG . Config . Min_Time_To_Funding_Minutes * 60 * 1000
2026-04-24 07:29:26 +00:00
### Load Data from Feedhandlers ###
2026-04-30 04:32:49 +00:00
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 ' ] )
2026-04-29 16:18:42 +00:00
2026-05-01 20:45:26 +00:00
ASTER_FUND_RATE_DICT : Any = VAL_KEY . get ( ' fund_rate_aster ' )
ASTER_FUND_RATE_DICT : dict = json . loads ( s = ASTER_FUND_RATE_DICT ) if ASTER_FUND_RATE_DICT is not None else { }
if ASTER_FUND_RATE_DICT . get ( ' symbol ' , None ) != ASTER . symbol :
ASTER_FUND_RATE : float = ASTER . initial_funding_rate
# logging.info(f'ASTER Symbol mismatch: {ASTER_FUND_RATE_DICT}; expected symbol: {ASTER.symbol}')
# raise ValueError(f'ASTER Symbol mismatch: {ASTER_FUND_RATE_DICT}; expected symbol: {ASTER.symbol}')
else :
ASTER_FUND_RATE : float = float ( ASTER_FUND_RATE_DICT . get ( ' funding_rate ' , 0 ) )
EXTENDED_FUND_RATE_DICT : Any = VAL_KEY . get ( ' fund_rate_extended ' )
EXTENDED_FUND_RATE_DICT : dict = json . loads ( s = EXTENDED_FUND_RATE_DICT ) if EXTENDED_FUND_RATE_DICT is not None else { }
if EXTENDED_FUND_RATE_DICT . get ( ' symbol ' , None ) != EXTEND . symbol :
EXTEND_FUND_RATE : float = EXTEND . initial_funding_rate
# logging.info(f'ASTER Symbol mismatch: {EXTENDED_FUND_RATE_DICT}; expected symbol: {EXTEND.symbol}')
# raise ValueError(f'ASTER Symbol mismatch: {EXTENDED_FUND_RATE_DICT}; expected symbol: {EXTEND.symbol}')
else :
EXTEND_FUND_RATE : float = float ( EXTENDED_FUND_RATE_DICT . get ( ' funding_rate ' , 0 ) )
2026-04-26 06:10:18 +00:00
2026-04-29 16:18:42 +00:00
if ALGO_CONFIG . Overrides . Flip_Side_For_Testing :
2026-04-26 06:10:18 +00:00
ASTER_FUND_RATE = ASTER_FUND_RATE * - 1
EXTEND_FUND_RATE = EXTEND_FUND_RATE * - 1
2026-04-23 06:39:51 +00:00
ASTER_FUND_RATE_TIME = float ( ASTER_FUND_RATE_DICT . get ( ' next_funding_time_ts_ms ' , 0 ) )
2026-05-04 18:04:45 +00:00
ASTER_FUND_RATE_TIME = ASTER_FUND_RATE_TIME + ( 60 * 60 * 1000 ) if ASTER_FUND_RATE_TIME < ( datetime . now ( ) . timestamp ( ) * 1000 ) else ASTER_FUND_RATE_TIME
2026-05-01 20:45:26 +00:00
EXTEND_FUND_RATE_TIME = max ( [ float ( EXTENDED_FUND_RATE_DICT . get ( ' next_funding_time_ts_ms ' , 0 ) ) , 0 ] )
2026-05-04 18:04:45 +00:00
EXTEND_FUND_RATE_TIME = EXTEND_FUND_RATE_TIME + ( 60 * 60 * 1000 ) if EXTEND_FUND_RATE_TIME < ( datetime . now ( ) . timestamp ( ) * 1000 ) else EXTEND_FUND_RATE_TIME
2026-04-24 07:29:26 +00:00
2026-05-01 20:45:26 +00:00
ASTER_TICKER_DICT : Any = VAL_KEY . get ( ' fut_ticker_aster ' )
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 ( ALGO_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 ) } ) )
time . sleep ( 5 )
continue
# raise ValueError(f'ASTER Symbol mismatch: {ASTER_TICKER_DICT}; expected symbol: {ASTER.symbol}')
EXTENDED_TICKER_DICT : Any = VAL_KEY . get ( ' fut_ticker_extended ' )
EXTENDED_TICKER_DICT : dict = json . loads ( s = EXTENDED_TICKER_DICT ) if EXTENDED_TICKER_DICT is not None else { }
if ( EXTENDED_TICKER_DICT . get ( ' symbol ' , None ) != EXTEND . symbol ) and not ( ALGO_CONFIG . Overrides . Flatten_Open_Positions ) :
logging . warning ( f ' EXTEND Symbol mismatch: { EXTENDED_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 ) } ) )
time . sleep ( 5 )
continue
# raise ValueError(f'EXTEND Symbol mismatch: {EXTENDED_TICKER_DICT}; expected symbol: {EXTEND.symbol}')
2026-04-24 07:29:26 +00:00
2026-05-05 16:38:45 +00:00
### Load Local Notional Updates from WS ###
2026-04-30 04:32:49 +00:00
ASTER_WS_POS_UPDATES : Any = VAL_KEY . get ( name = ' fr_aster_user_positions ' )
ASTER_WS_POS_UPDATES : list = json . loads ( s = ASTER_WS_POS_UPDATES ) if ASTER_WS_POS_UPDATES is not None else [ ]
EXTEND_WS_POS_UPDATES : Any = VAL_KEY . get ( ' fr_extended_user_positions ' )
EXTEND_WS_POS_UPDATES : list = json . loads ( EXTEND_WS_POS_UPDATES ) if EXTEND_WS_POS_UPDATES is not None else [ ]
2026-04-24 07:29:26 +00:00
2026-05-05 16:38:45 +00:00
if len ( ASTER_WS_POS_UPDATES ) > 0 :
await get_aster_notional_position ( resp = ASTER_WS_POS_UPDATES )
if len ( EXTEND_WS_POS_UPDATES ) > 0 :
await get_extend_notional ( resp = EXTEND_WS_POS_UPDATES )
### Load Local Order Updates from WS ###
2026-04-30 04:32:49 +00:00
ASTER_WS_ORDER_UPDATES : Any = VAL_KEY . get ( ' fr_aster_user_orders ' )
ASTER_WS_ORDER_UPDATES : list = json . loads ( ASTER_WS_ORDER_UPDATES ) if ASTER_WS_ORDER_UPDATES is not None else [ ]
EXTEND_WS_ORDER_UPDATES : Any = VAL_KEY . get ( ' fr_extended_user_orders ' )
EXTEND_WS_ORDER_UPDATES : list = json . loads ( EXTEND_WS_ORDER_UPDATES ) if EXTEND_WS_ORDER_UPDATES is not None else [ ]
2026-05-05 16:38:45 +00:00
### CHECK NO MORE THAN 1 OPEN ORDER ON EITHER EXCHANGE ###
2026-04-24 07:29:26 +00:00
if len ( ASTER_OPEN_ORDERS ) > 1 or len ( EXTEND_OPEN_ORDERS ) > 1 :
logging . info ( f ' MORE THAN 1 ORDER OPEN - KILLING ALGO: ASTER_OPEN_ORDERS ( { len ( ASTER_OPEN_ORDERS ) } ): { ASTER_OPEN_ORDERS } ; EXTEND_OPEN_ORDERS ( { len ( EXTEND_OPEN_ORDERS ) } ): { EXTEND_OPEN_ORDERS } ' )
await kill_algo ( )
raise ValueError ( ' NOT HERE: MORE THAN 1 ORDER OPEN - KILLING ALGO: ASTER_OPEN_ORDERS ' )
2026-05-05 16:38:45 +00:00
### Update Local Open Orders w Changes from WS ###
ASTER_OPEN_ORDERS = await handle_order_updates ( exch = ' ASTER ' , local_open_orders = ASTER_OPEN_ORDERS , ws_open_orders = ASTER_WS_ORDER_UPDATES )
EXTEND_OPEN_ORDERS = await handle_order_updates ( exch = ' EXTEND ' , local_open_orders = EXTEND_OPEN_ORDERS , ws_open_orders = EXTEND_WS_ORDER_UPDATES )
2026-04-24 07:29:26 +00:00
### CHECK TIME TO FUNDING AND WHETHER TO BE ACTIVE ###
now_ms = round ( datetime . now ( ) . timestamp ( ) * 1000 )
time_to_funding_ms = min ( [ ASTER_FUND_RATE_TIME , EXTEND_FUND_RATE_TIME ] ) - now_ms
if ( time_to_funding_ms > MIN_TIME_TO_FUNDING ) and ( not ASTER_OPEN_ORDERS ) and ( not EXTEND_OPEN_ORDERS ) :
2026-04-27 17:57:58 +00:00
logging . info ( f ' Outside action window (minutes) and no active order (sleeping for 5 sec): { pd . to_datetime ( time_to_funding_ms , unit = ' ms ' ) . minute } > { pd . to_datetime ( MIN_TIME_TO_FUNDING , unit = ' ms ' ) . minute } ' )
2026-04-24 07:29:26 +00:00
time . sleep ( 5 )
continue
2026-04-23 17:33:01 +00:00
min_between_fundings = round ( ( abs ( ASTER_FUND_RATE_TIME - EXTEND_FUND_RATE_TIME ) / 1000 / 60 ) )
FUNDINGS_AT_SAME_TIME_NEXT_HR = min_between_fundings < 5
2026-04-23 06:39:51 +00:00
if ( abs ( ASTER_FUND_RATE ) > abs ( EXTEND_FUND_RATE ) ) and FUNDINGS_AT_SAME_TIME_NEXT_HR :
ALPHA_EXCH = ' ASTER '
ALPHA_FUND_RATE = ASTER_FUND_RATE
else :
ALPHA_EXCH = ' EXTEND '
ALPHA_FUND_RATE = EXTEND_FUND_RATE
if ALPHA_FUND_RATE < 0 :
ALPHA_CARRY_SIDE = ' BUY '
2026-05-05 16:38:45 +00:00
ALPHA_TGT_NOTIONAL . value = ALGO_CONFIG . Config . Max_Target_Notional
2026-04-23 06:39:51 +00:00
else :
ALPHA_CARRY_SIDE = ' SELL '
2026-05-05 16:38:45 +00:00
ALPHA_TGT_NOTIONAL . value = ALGO_CONFIG . Config . Max_Target_Notional * - 1
2026-04-23 06:39:51 +00:00
def calc_next_net_fund_rate ( FUNDINGS_AT_SAME_TIME_NEXT_HR : bool ) - > float :
if FUNDINGS_AT_SAME_TIME_NEXT_HR :
2026-04-29 16:18:42 +00:00
return max ( [ ASTER_FUND_RATE , EXTEND_FUND_RATE ] ) - min ( [ ASTER_FUND_RATE , EXTEND_FUND_RATE ] )
2026-04-23 06:39:51 +00:00
else :
return EXTEND_FUND_RATE
2026-05-05 16:38:45 +00:00
### Calculate Alpha ###
def unlock_notional_tgt ( ) - > bool :
return ( ALPHA_TGT_NOTIONAL . value != ALPHA_TGT_NOTIONAL . previous_value )
at_notional_target . unlock ( )
sec_until_funding = round ( ( EXTEND_FUND_RATE_TIME - ( datetime . now ( ) . timestamp ( ) * 1000 ) ) / 1000 )
if at_notional_target . is_locked and ( sec_until_funding > ( 60 * 5 ) ) and at_notional_target . value :
ALPHA_TGT_NOTIONAL_FINAL = 0.00
ALPHA_CARRY_SIDE = ' BUY ' if ALPHA_CARRY_SIDE == ' SELL ' else ' SELL '
else :
ALPHA_TGT_NOTIONAL_FINAL = ALPHA_TGT_NOTIONAL . value
ALPHA_CARRY_SIDE = ALPHA_CARRY_SIDE
2026-04-23 06:39:51 +00:00
NEXT_NET_FUNDING_RATE = calc_next_net_fund_rate ( FUNDINGS_AT_SAME_TIME_NEXT_HR )
2026-04-29 16:18:42 +00:00
Flags . NET_FUNDING_IS_ZERO = ( NEXT_NET_FUNDING_RATE > = ( ( ALGO_CONFIG . Config . Min_Fund_Rate_Pct_To_Trade * - 1 ) / 100 ) ) and ( NEXT_NET_FUNDING_RATE < = ( ALGO_CONFIG . Config . Min_Fund_Rate_Pct_To_Trade / 100 ) )
2026-05-05 16:38:45 +00:00
# if Flags.NET_FUNDING_IS_ZERO or ALGO_CONFIG.Overrides.Flatten_Open_Positions or ALGO_CONFIG.Overrides.Flatten_Open_Positions_Opportunistic:
# ALPHA_TGT_NOTIONAL.value = 0.00
if ALGO_CONFIG . Overrides . Flatten_Open_Positions or ALGO_CONFIG . Overrides . Flatten_Open_Positions_Opportunistic or ALPHA_TGT_NOTIONAL_FINAL == 0.00 :
2026-05-04 18:04:45 +00:00
# ROUNDING = ROUND_UP
ROUNDING = ROUND_HALF_UP
else :
ROUNDING = ROUND_DOWN
2026-04-23 06:39:51 +00:00
if ALPHA_EXCH == ' EXTEND ' :
2026-05-05 16:38:45 +00:00
ASTER_TGT_NOTIONAL = ALPHA_TGT_NOTIONAL_FINAL * - 1
2026-04-23 17:33:01 +00:00
if ALPHA_CARRY_SIDE == ' BUY ' :
ASTER_TOB_PX = float ( ASTER_TICKER_DICT [ ' best_ask_px ' ] )
EXTEND_TOB_PX = float ( EXTENDED_TICKER_DICT [ ' best_bid_px ' ] )
2026-05-05 16:38:45 +00:00
current_ratio = ( ASTER_TOB_PX / EXTEND_TOB_PX ) - 1
alpha_model_ratio = EXTEND . buy_ratio
alpha_signal : bool = current_ratio > EXTEND . buy_ratio
2026-04-23 17:33:01 +00:00
else :
ASTER_TOB_PX = float ( ASTER_TICKER_DICT [ ' best_bid_px ' ] )
EXTEND_TOB_PX = float ( EXTENDED_TICKER_DICT [ ' best_ask_px ' ] )
2026-05-05 16:38:45 +00:00
current_ratio = ( ASTER_TOB_PX / EXTEND_TOB_PX ) - 1
alpha_model_ratio = EXTEND . buy_ratio
alpha_signal : bool = current_ratio < EXTEND . buy_ratio
2026-04-23 06:39:51 +00:00
else :
2026-05-05 16:38:45 +00:00
ASTER_TGT_NOTIONAL = ALPHA_TGT_NOTIONAL_FINAL
2026-04-23 17:33:01 +00:00
if ALPHA_CARRY_SIDE == ' BUY ' :
ASTER_TOB_PX = float ( ASTER_TICKER_DICT [ ' best_bid_px ' ] )
EXTEND_TOB_PX = float ( EXTENDED_TICKER_DICT [ ' best_ask_px ' ] )
2026-05-05 16:38:45 +00:00
current_ratio = ( ( ASTER_TOB_PX / EXTEND_TOB_PX ) - 1 ) * - 1
alpha_model_ratio = ASTER . buy_ratio
alpha_signal : bool = current_ratio > ASTER . buy_ratio
2026-04-23 17:33:01 +00:00
else :
ASTER_TOB_PX = float ( ASTER_TICKER_DICT [ ' best_ask_px ' ] )
EXTEND_TOB_PX = float ( EXTENDED_TICKER_DICT [ ' best_bid_px ' ] )
2026-05-05 16:38:45 +00:00
current_ratio = ( ( ASTER_TOB_PX / EXTEND_TOB_PX ) - 1 ) * - 1
alpha_model_ratio = ASTER . buy_ratio
alpha_signal : bool = current_ratio < ASTER . buy_ratio
2026-04-23 17:33:01 +00:00
2026-05-05 16:38:45 +00:00
EXTEND_TGT_NOTIONAL = ASTER . notional_position * - 1
2026-04-23 17:33:01 +00:00
2026-05-05 16:38:45 +00:00
ASTER_TGT_TAIL = structs . Current_Previous_Value ( ( ASTER_TGT_NOTIONAL - ( float ( ASTER . notional_position ) + float ( ASTER . unrealized_pnl ) ) ) )
# ASTER_TGT_TAIL.value = ASTER_TGT_NOTIONAL - ( float(ASTER.notional_position) + float(ASTER.unrealized_pnl) )
EXTEND_TGT_TAIL = structs . Current_Previous_Value ( ( EXTEND_TGT_NOTIONAL - ( float ( EXTEND . notional_position ) ) ) )
2026-04-28 22:40:35 +00:00
2026-04-23 06:39:51 +00:00
2026-05-01 20:45:26 +00:00
min_order_size = ASTER . min_order_size
min_order_size = int ( min_order_size ) if min_order_size == int ( min_order_size ) else min_order_size
2026-05-05 16:38:45 +00:00
ASTER_TGT_TAIL_BASE_QTY = Decimal ( str ( float ( ASTER_TGT_TAIL . value ) / float ( ASTER_TOB_PX ) ) ) . quantize ( Decimal ( str ( min_order_size ) ) , rounding = ROUNDING )
2026-05-04 18:04:45 +00:00
if ASTER . min_lot_size :
ASTER_TGT_TAIL_BASE_QTY = float ( ASTER_TGT_TAIL_BASE_QTY ) - ( float ( ASTER_TGT_TAIL_BASE_QTY ) % ASTER . min_lot_size )
ASTER_TGT_TAIL_BASE_QTY = Decimal ( str ( ASTER_TGT_TAIL_BASE_QTY ) ) . quantize ( Decimal ( str ( min_order_size ) ) , rounding = ROUNDING )
2026-04-23 17:33:01 +00:00
2026-05-01 20:45:26 +00:00
min_order_size = EXTEND . min_order_size
min_order_size = int ( min_order_size ) if min_order_size == int ( min_order_size ) else min_order_size
2026-05-05 16:38:45 +00:00
EXTEND_TGT_TAIL_BASE_QTY = Decimal ( str ( float ( EXTEND_TGT_TAIL . value ) / float ( EXTEND_TOB_PX ) ) ) . quantize ( Decimal ( str ( min_order_size ) ) , rounding = ROUNDING )
2026-05-04 18:04:45 +00:00
if EXTEND . min_lot_size :
EXTEND_TGT_TAIL_BASE_QTY = float ( EXTEND_TGT_TAIL_BASE_QTY ) - ( float ( EXTEND_TGT_TAIL_BASE_QTY ) % EXTEND . min_lot_size )
EXTEND_TGT_TAIL_BASE_QTY = Decimal ( str ( EXTEND_TGT_TAIL_BASE_QTY ) ) . quantize ( Decimal ( str ( min_order_size ) ) , rounding = ROUNDING )
2026-04-23 03:11:52 +00:00
2026-05-05 16:38:45 +00:00
### Define if Orderable ###
ASTER_TGT_TAIL_ORDERABLE = ( Decimal ( str ( abs ( ASTER_TGT_TAIL_BASE_QTY ) ) ) > = Decimal ( str ( abs ( ASTER . min_order_size ) ) ) ) and ( Decimal ( str ( abs ( ASTER_TGT_TAIL . value ) ) ) > Decimal ( str ( abs ( ASTER . min_notional ) ) ) )
EXTEND_TGT_TAIL_ORDERABLE = ( Decimal ( str ( abs ( EXTEND_TGT_TAIL_BASE_QTY ) ) ) > = Decimal ( str ( abs ( EXTEND . min_order_size ) ) ) ) and ( Decimal ( str ( abs ( EXTEND_TGT_TAIL . value ) ) ) > Decimal ( str ( abs ( EXTEND . min_notional ) ) ) )
2026-05-04 18:04:45 +00:00
2026-05-05 16:38:45 +00:00
# if not ASTER_TGT_TAIL_ORDERABLE:
# if abs(ASTER_TGT_TAIL_BASE_QTY) > 0:
# if ALGO_CONFIG.Overrides.Flatten_Open_Positions or ALGO_CONFIG.Overrides.Flatten_Open_Positions_Opportunistic or ALPHA_TGT_NOTIONAL_FINAL == 0.00:
# logging.info('* Trying to flatten small Aster balance, was originally not orderable.')
# ASTER_TGT_TAIL_ORDERABLE = True
# if not EXTEND_TGT_TAIL_ORDERABLE:
# if abs(EXTEND_TGT_TAIL_BASE_QTY) > 0:
# if ALGO_CONFIG.Overrides.Flatten_Open_Positions or ALGO_CONFIG.Overrides.Flatten_Open_Positions_Opportunistic or ALPHA_TGT_NOTIONAL_FINAL == 0.00:
# logging.info('* Trying to flatten small Extend balance, was originally not orderable.')
# EXTEND_TGT_TAIL_ORDERABLE = True
if at_notional_target . is_unlocked :
at_notional_target . value = not ( ASTER_TGT_TAIL_ORDERABLE ) and not ( EXTEND_TGT_TAIL_ORDERABLE )
if at_notional_target . value :
at_notional_target . _unlock_func = unlock_notional_tgt
at_notional_target . lock ( )
continue
2026-04-23 06:39:51 +00:00
2026-05-05 16:38:45 +00:00
### Check if Currently Hedged ###
# Hedge_Ratio = abs( ( EXTEND.notional_position + ASTER.notional_position ) / max([ASTER.notional_position, 0.01]) ) * 100
# Currently_Hedged = Hedge_Ratio < 1.00
2026-04-28 22:40:35 +00:00
2026-05-05 16:38:45 +00:00
### Logging ###
2026-04-25 23:43:28 +00:00
def print_summary ( use_logging : bool = False ) :
2026-04-30 04:32:49 +00:00
OUT : Any = logging . info if use_logging else print
2026-04-25 23:43:28 +00:00
2026-05-05 16:38:45 +00:00
# ASTER: [ Available Collateral: {ASTER_AVAIL_COLLATERAL:.4f} ] | EXTEND: [ Available Collateral: {EXTEND_AVAIL_COLLATERAL:.4f} ]
2026-04-25 23:43:28 +00:00
OUT ( f '''
2026-04-29 16:18:42 +00:00
FLIP SIDES FOR TESTING ? : { ALGO_CONFIG . Overrides . Flip_Side_For_Testing } ; ASTER ORDER ENABLED ? { ALGO_CONFIG . Overrides . Allow_Ordering_Aster } ; EXTEND ORDER ENABLED ? { ALGO_CONFIG . Overrides . Allow_Ordering_Extend }
2026-05-05 16:38:45 +00:00
MKT : Aster : { ASTER . symbol : < 10 } ( best : { best_symbol_by_exchange_aster . symbol } ) | Extend : { EXTEND . symbol : < 10 } ( best : { best_symbol_by_exchange_extend . symbol } )
{ 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 ( ) ) : } )
ASTER : { ASTER_FUND_RATE : .6 % } [ { ASTER_FUND_RATE * 10_000 : .2 f } bps ] [ { ASTER_FUND_RATE * 1_000_000 : .0 f } pips ] | EXTEND : { EXTEND_FUND_RATE : .6 % } [ { EXTEND_FUND_RATE * 10_000 : .2 f } bps ] [ { EXTEND_FUND_RATE * 1_000_000 : .0 f } 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 : .4 f } ] | EXTEND : [ Notional Position $ : { EXTEND . notional_position : .4 f } ]
2026-04-25 23:43:28 +00:00
SAME TIME ? : { FUNDINGS_AT_SAME_TIME_NEXT_HR } [ Minutes Between Fundings : { min_between_fundings } ]
2026-04-29 16:18:42 +00:00
NET FUNDING : { NEXT_NET_FUNDING_RATE : .6 % } [ { NEXT_NET_FUNDING_RATE * 10_000 : .2 f } bps ] [ { NEXT_NET_FUNDING_RATE * 1_000_000 : .0 f } pips ] ; Is Zero ? : { Flags . NET_FUNDING_IS_ZERO } [ Min : { ALGO_CONFIG . Config . Min_Fund_Rate_Pct_To_Trade } ]
2026-05-05 16:38:45 +00:00
2026-04-25 23:43:28 +00:00
ALPHA SIDE : { ALPHA_EXCH } [ { ALPHA_CARRY_SIDE } ]
2026-05-05 16:38:45 +00:00
ALPHA SIGNAL : { alpha_signal } ; Current { current_ratio : .4 f } [ { current_ratio * 10_000 : .2 f } scl ] { " > " if ALPHA_CARRY_SIDE == ' BUY ' else " < " } Model { alpha_model_ratio : .4 f } [ { alpha_model_ratio * 10_000 : .2 f } scl ]
2026-04-24 07:29:26 +00:00
2026-05-05 16:38:45 +00:00
TGT NOTIONAL : $ { abs ( ALPHA_TGT_NOTIONAL_FINAL ) : .2 f } ; Flatten Open Positions Flag ? { ALGO_CONFIG . Overrides . Flatten_Open_Positions } ; Opportunistic ? { ALGO_CONFIG . Overrides . Flatten_Open_Positions_Opportunistic }
AT TARGET ? : { at_notional_target . value } ; is_locked ? : { at_notional_target . is_locked }
> 5 min Fund ? : { ( sec_until_funding > ( 60 * 5 ) ) }
2026-04-23 06:39:51 +00:00
2026-05-05 16:38:45 +00:00
ASTER : { ASTER . notional_position : .4 f } - > { ASTER_TGT_NOTIONAL : .2 f } [ Remain : { ASTER_TGT_TAIL . value : .4 f } ] | EXTEND : { EXTEND . notional_position : .4 f } - > { EXTEND_TGT_NOTIONAL : .2 f } [ Remain : { EXTEND_TGT_TAIL . value : .4 f } ]
ASTER : { ASTER_TGT_NOTIONAL : .2 f } - { ASTER . notional_position : .2 f } + { ASTER . unrealized_pnl : .2 f } = { ASTER_TGT_TAIL . value : 2 f } | EXTEND : { EXTEND_TGT_NOTIONAL : .2 f } - { EXTEND . notional_position : .2 f } + { EXTEND . unrealized_pnl : .2 f } = { EXTEND_TGT_TAIL . value : 2 f }
ASTER : { ASTER_TGT_TAIL_BASE_QTY : .4 f } > { ASTER . min_order_size : .4 f } min [ Order : { ASTER_TGT_TAIL_ORDERABLE } ] | EXTEND : { EXTEND_TGT_TAIL_BASE_QTY : .4 f } > { EXTEND . min_order_size : .4 f } min [ Order : { EXTEND_TGT_TAIL_ORDERABLE } ]
2026-04-25 23:43:28 +00:00
- - - ASTER OPEN ORDERS - - -
{ ASTER_OPEN_ORDERS }
- - - EXTEND OPEN ORDERS - - -
{ EXTEND_OPEN_ORDERS }
''' )
2026-05-05 16:38:45 +00:00
2026-04-29 16:18:42 +00:00
if ALGO_CONFIG . Logging . Log_Summary_Each_Loop :
2026-04-27 17:57:58 +00:00
print_summary ( use_logging = True )
2026-04-29 16:18:42 +00:00
if ALGO_CONFIG . Logging . Print_Summary_Each_Loop :
2026-04-27 17:57:58 +00:00
print_summary ( use_logging = False )
2026-04-23 06:39:51 +00:00
2026-05-05 16:38:45 +00:00
### Define Ordering Logic ###
'''
Notes
- handle increasing vs flattening
- if increasing , set not reduce only
- if flattening , set as reduce only and make sure allowed to trade below min notional , and qty calc should be exact
- handle opportunistic vs immediate
- handle cancel - replace manually for aster and sometimes manually for extend ( e . g . cant change certain things on an existing order )
- gracefully handle err responses ( well known err codes e . g . ) and response errors ( e . g . json fails to parse )
'''
async def cancel_aster_order ( open_order_id : str ) :
global ASTER_OPEN_ORDERS
start = time . time ( )
cancel_order : dict = {
" url " : " /fapi/v3/order " ,
" method " : " DELETE " ,
" params " : {
' symbol ' : ASTER . symbol ,
' orderId ' : open_order_id ,
}
}
cr : dict = await aster_auth . post_authenticated_url ( cancel_order ) # ty:ignore[invalid-assignment]
if cr . get ( ' status ' , None ) == ' CANCELED ' :
ASTER_OPEN_ORDERS . pop ( 0 )
else :
logging . warning ( f ' ASTER ORDER FAILED TO CANCEL DURING CR ( { open_order_id } ): RESP { cr } ' )
logging . info ( f ' TIMING - cancel_aster_order: { ( time . time ( ) - start ) * 1000 : .2f } ' )
async def post_aster_order (
symbol : str ,
side : str ,
qty : Decimal ,
price : Decimal ,
reduceOnly : bool ,
postOnly : bool
) :
global ASTER_OPEN_ORDERS
global Just_Rejected_Or_Expired
if postOnly :
timeInForce = ' GTX '
else :
timeInForce = ' GTC '
post_order = {
" url " : " /fapi/v3/order " ,
" method " : " POST " ,
" params " : {
' symbol ' : symbol ,
' side ' : side ,
' type ' : ' LIMIT ' ,
' timeInForce ' : timeInForce ,
' quantity ' : qty ,
' price ' : price ,
' reduceOnly ' : reduceOnly
}
}
order_resp : dict = await aster_auth . post_authenticated_url ( post_order ) # ty:ignore[invalid-assignment]
if order_resp . get ( ' orderId ' , None ) is not None :
order_resp [ ' original_price ' ] = price
order_resp [ ' order_status ' ] = order_resp [ ' status ' ]
ASTER_OPEN_ORDERS . append ( order_resp )
Just_Rejected_Or_Expired = False
utils . send_tg_alert ( f ' FR_ALGO - ASTER Order ( { order_resp [ ' orderId ' ] } ). Start_$: { ASTER . notional_position : .4f } ; Value: { float ( ASTER_TGT_TAIL_BASE_QTY ) * float ( price ) : .4f } ; Price: { float ( price ) : .4f } ' )
logging . info ( f ' ASTER ORDER PLACED SUCCESS: { order_resp } ' )
print_summary ( use_logging = True )
else :
logging . critical ( f ' *** Aster Order Response Abnormal: { order_resp } ; post_order: { post_order } ' )
await kill_algo ( )
2026-04-29 16:18:42 +00:00
2026-05-05 16:38:45 +00:00
async def cancel_extend_order ( order_id : str ) :
r = EXTEND_CLIENT . orders . cancel_order ( order_id = order_id )
r = dict ( r )
if r . get ( ' status ' , None ) == ' OK ' :
logging . info ( f ' EXTEND ORDER CANCELLED: { order_id } ' )
else :
logging . warning ( f ' EXTEND ORDER FAILED TO CANCEL DURING CR ( { order_id } ): RESP { r } ' )
async def post_extend_order (
symbol : str ,
side : str ,
qty : Decimal ,
price : Decimal ,
reduceOnly : bool ,
postOnly : bool ,
cxl_prev_order_id : str | None = None ,
) :
global EXTEND_OPEN_ORDERS
global Just_Rejected_Or_Expired
side = OrderSide . BUY if side == ' BUY ' else OrderSide . SELL
taker_fee = Decimal ( " 0.00025 " )
try :
order_resp : WrappedApiResponse [ PlacedOrderModel ] = await EXTEND_CLIENT . place_order (
market_name = symbol ,
amount_of_synthetic = qty ,
price = price ,
side = side ,
taker_fee = taker_fee ,
previous_order_id = cxl_prev_order_id ,
post_only = postOnly ,
reduce_only = reduceOnly
)
except Exception as e :
logging . critical ( e )
order_resp_dict = dict ( order_resp )
if order_resp_dict . get ( ' status ' , None ) == ' ERROR ' :
if order_resp_dict [ ' error ' ] [ ' code ' ] == 1142 :
logging . info ( ' Cant find edit order for Extend, skipping cancel. ' )
else :
logging . critical ( f ' *** Extend Order Response Abnormal: { order_resp } ; ' )
await kill_algo ( )
if order_resp_dict . get ( ' status ' , None ) == ' OK ' :
if EXTEND_OPEN_ORDERS :
EXTEND_OPEN_ORDERS . pop ( 0 )
order_dict = dict ( order_resp_dict [ ' data ' ] )
order_dict [ ' status ' ] = ' NEW '
order_dict [ ' price ' ] = str ( price )
order_dict [ ' qty ' ] = str ( qty )
order_dict [ ' filled_qty ' ] = str ( 0 )
order_dict [ ' side ' ] = str ( side )
EXTEND_OPEN_ORDERS . append ( order_dict )
Just_Rejected_Or_Expired = False
utils . send_tg_alert ( f ' FR_ALGO - EXTEND Order ( { order_dict . get ( ' id ' , None ) } ). Start_$: { EXTEND . notional_position : .2f } ; Value: { float ( EXTEND_TGT_TAIL_BASE_QTY ) * float ( price ) : .2f } ; Price: { float ( price ) : .2f } ' )
logging . info ( f ' EXTEND ORDER PLACED SUCCESS: { order_dict } ' )
print_summary ( use_logging = True )
else :
logging . critical ( f ' *** Extend Order Response Abnormal: { order_resp } ; ' )
await kill_algo ( )
### ASTER
if ALGO_CONFIG . Overrides . Allow_Ordering_Aster and ASTER_TGT_TAIL_ORDERABLE : # Tier 1 Overrides
if alpha_signal or ALGO_CONFIG . Overrides . Flatten_Open_Positions : # Tier 2 Overrides / Alpha
skip = False
2026-04-28 15:02:32 +00:00
side = ' BUY ' if ASTER_TGT_TAIL_BASE_QTY > 0.00 else ' SELL '
2026-05-01 20:45:26 +00:00
qty = Decimal ( value = str ( abs ( ASTER_TGT_TAIL_BASE_QTY ) ) )
price = ASTER_TOB_PX - ( float ( ASTER . min_price ) * int ( ALGO_CONFIG . Config . Price_Worsener_Aster ) ) if side == ' BUY ' else ASTER_TOB_PX + ( float ( ASTER . min_price ) * int ( ALGO_CONFIG . Config . Price_Worsener_Aster ) )
2026-04-28 15:02:32 +00:00
2026-05-05 16:38:45 +00:00
if abs ( ( float ( ASTER_TGT_TAIL_BASE_QTY ) * float ( price ) ) + ASTER . notional_position ) > ALGO_CONFIG . Config . Max_Target_Notional * ALGO_CONFIG . Config . Max_Order_Over_Notional_Ratio :
logging . info ( f ' TRYING TO ORDER OVER MAX NOTIOANL - ASTER: { ASTER . notional_position } + { float ( ASTER_TGT_TAIL_BASE_QTY ) * float ( price ) } (qty: { float ( ASTER_TGT_TAIL_BASE_QTY ) : .2f } ; px: { float ( price ) : .2f } ) ' )
2026-04-28 15:02:32 +00:00
await kill_algo ( )
2026-05-05 16:38:45 +00:00
if ASTER_OPEN_ORDERS : # Cancel Open Order?
2026-04-28 15:02:32 +00:00
open_order_id = ASTER_OPEN_ORDERS [ 0 ] . get ( ' order_id ' ) if ASTER_OPEN_ORDERS [ 0 ] . get ( ' order_id ' ) is not None else ASTER_OPEN_ORDERS [ 0 ] [ ' orderId ' ]
2026-05-05 16:38:45 +00:00
open_order_px = float ( ASTER_OPEN_ORDERS [ 0 ] . get ( ' price ' , 0 ) ) if ASTER_OPEN_ORDERS [ 0 ] . get ( ' price ' ) is not None else float ( ASTER_OPEN_ORDERS [ 0 ] [ ' original_price ' ] )
open_order_dict = dict ( ASTER_OPEN_ORDERS [ 0 ] )
open_order_id = str ( open_order_dict [ ' order_id ' ] )
open_order_px = float ( open_order_dict [ ' price ' ] )
2026-05-01 20:45:26 +00:00
min_price = ASTER . min_price
min_price = int ( min_price ) if min_price == int ( min_price ) else min_price
if Decimal ( str ( float ( open_order_px ) - float ( price ) ) ) . quantize ( Decimal ( str ( min_price ) ) , rounding = ROUND_HALF_UP ) == 0.00 :
if ALGO_CONFIG . Logging . Print_Summary_Each_Loop :
print ( ' ASTER OPEN ORDER NO PX CHG; SKIPPING ' )
2026-05-05 16:38:45 +00:00
skip = True
2026-04-28 15:02:32 +00:00
else :
2026-05-05 16:38:45 +00:00
await cancel_aster_order ( open_order_id ) # ty:ignore[invalid-argument-type]
2026-04-28 15:02:32 +00:00
if ASTER_TGT_TAIL_BASE_QTY == 0.00 :
logging . info ( ' ASTER TRYNG TO ORDER 0.00 BASE QTY, SKIPPING ' )
2026-05-05 16:38:45 +00:00
skip = True
2026-04-28 15:02:32 +00:00
2026-05-05 16:38:45 +00:00
if not skip :
2026-05-01 20:45:26 +00:00
min_price = ASTER . min_price
min_price = int ( min_price ) if min_price == int ( min_price ) else min_price
price : Decimal = Decimal ( str ( price ) ) . quantize ( Decimal ( str ( min_price ) ) , rounding = ROUND_HALF_UP )
2026-05-04 18:04:45 +00:00
if price == Decimal ( str ( 0.00 ) ) . quantize ( Decimal ( str ( min_price ) ) , rounding = ROUND_HALF_UP ) :
logging . info ( ' ASTER TRYNG TO ORDER with A PRICE OF 0.00, SKIPPING ' )
continue
2026-05-01 20:45:26 +00:00
2026-05-05 16:38:45 +00:00
if qty > = ASTER . min_order_size and ( ( qty * price ) > ASTER . min_notional ) :
2026-05-01 20:45:26 +00:00
reduceOnly = False
else :
reduceOnly = True
2026-05-05 16:38:45 +00:00
await post_aster_order (
symbol = ASTER . symbol ,
side = side ,
qty = qty ,
price = price ,
reduceOnly = reduceOnly ,
postOnly = True ,
)
2026-04-28 15:02:32 +00:00
else :
pass
2026-05-05 16:38:45 +00:00
elif not ( ASTER_TGT_TAIL_ORDERABLE ) and ASTER_OPEN_ORDERS :
logging . info ( ' ASTER HAS NO TAIL BUT OPEN ORDERS - CANCELLING OPEN ORDERS ' )
await extend_cancel_all_orders ( )
### EXTEND ###
if ALGO_CONFIG . Overrides . Allow_Ordering_Extend and EXTEND_TGT_TAIL_ORDERABLE : # Tier 1 Overrides
if alpha_signal or ALGO_CONFIG . Overrides . Flatten_Open_Positions : # Tier 2 Overrides / Alpha
skip = False
side = ' BUY ' if EXTEND_TGT_TAIL_BASE_QTY > 0.00 else ' SELL '
2026-05-01 20:45:26 +00:00
qty = Decimal ( value = str ( abs ( EXTEND_TGT_TAIL_BASE_QTY ) ) )
2026-05-05 16:38:45 +00:00
price = EXTEND_TOB_PX - ( float ( EXTEND . min_price ) * int ( ALGO_CONFIG . Config . Price_Worsener_Extend ) ) if side == ' BUY ' else EXTEND_TOB_PX + ( float ( EXTEND . min_price ) * int ( ALGO_CONFIG . Config . Price_Worsener_Extend ) ) # ty:ignore[invalid-assignment]
if abs ( ( float ( EXTEND_TGT_TAIL_BASE_QTY ) * float ( price ) ) + EXTEND . notional_position ) > ALGO_CONFIG . Config . Max_Target_Notional * ALGO_CONFIG . Config . Max_Order_Over_Notional_Ratio :
logging . info ( f ' TRYING TO ORDER OVER MAX NOTIOANL - EXTEND: { EXTEND . notional_position } + { float ( EXTEND_TGT_TAIL_BASE_QTY ) * float ( price ) } (qty: { float ( EXTEND_TGT_TAIL_BASE_QTY ) : .2f } ; px: { float ( price ) : .2f } ) ' )
2026-04-28 15:02:32 +00:00
await kill_algo ( )
2026-05-05 16:38:45 +00:00
if EXTEND_OPEN_ORDERS : # Cancel Open Order?
2026-04-28 15:02:32 +00:00
open_order_dict = dict ( EXTEND_OPEN_ORDERS [ 0 ] )
2026-05-01 20:45:26 +00:00
open_order_id = str ( open_order_dict [ ' external_id ' ] )
2026-04-28 15:02:32 +00:00
open_order_px = float ( open_order_dict [ ' price ' ] )
2026-05-05 16:38:45 +00:00
min_price = EXTEND . min_price
min_price = int ( min_price ) if min_price == int ( min_price ) else min_price
if Decimal ( str ( float ( open_order_px ) - float ( price ) ) ) . quantize ( Decimal ( str ( min_price ) ) , rounding = ROUND_HALF_UP ) == 0.00 :
if ALGO_CONFIG . Logging . Print_Summary_Each_Loop :
print ( ' EXTEND OPEN ORDER NO PX CHG; SKIPPING ' )
skip = True
2026-04-24 07:29:26 +00:00
else :
2026-04-28 15:02:32 +00:00
open_order_id = None
2026-05-05 16:38:45 +00:00
if EXTEND_TGT_TAIL_BASE_QTY == 0.00 :
logging . info ( ' EXTEND TRYNG TO ORDER 0.00 BASE QTY, SKIPPING ' )
skip = True
if not skip :
min_price = EXTEND . min_price
min_price = int ( min_price ) if min_price == int ( min_price ) else min_price
2026-05-01 20:45:26 +00:00
price : Decimal = Decimal ( str ( price ) ) . quantize ( Decimal ( str ( min_price ) ) , rounding = ROUND_HALF_UP )
2026-05-05 16:38:45 +00:00
if price == Decimal ( str ( 0.00 ) ) . quantize ( Decimal ( str ( min_price ) ) , rounding = ROUND_HALF_UP ) :
logging . info ( ' EXTEND TRYNG TO ORDER with A PRICE OF 0.00, SKIPPING ' )
continue
if qty > = EXTEND . min_order_size and ( ( qty * price ) > EXTEND . min_notional ) :
reduceOnly = False
2026-04-26 06:10:18 +00:00
else :
2026-05-05 16:38:45 +00:00
reduceOnly = True
await post_extend_order (
symbol = EXTEND . symbol ,
side = side ,
qty = qty ,
price = price ,
reduceOnly = reduceOnly ,
postOnly = True ,
cxl_prev_order_id = open_order_id
)
2026-04-28 15:02:32 +00:00
else :
2026-05-05 16:38:45 +00:00
pass
elif not ( EXTEND_TGT_TAIL_ORDERABLE ) and EXTEND_OPEN_ORDERS :
logging . info ( ' EXTEND HAS NO TAIL BUT OPEN ORDERS - CANCELLING OPEN ORDERS ' )
await extend_cancel_all_orders ( )
2026-04-24 07:29:26 +00:00
2026-05-05 16:38:45 +00:00
### Continue immediately or sleep ###
2026-04-28 15:02:32 +00:00
if ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS :
2026-05-05 16:38:45 +00:00
if ALGO_CONFIG . Logging . Print_Summary_Each_Loop :
print ( f ' _____ Open Orders _____ (Algo Engine ms: { ( time . time ( ) - loop_start ) * 1000 : .2f } ); Continuing... ' )
2026-04-28 15:02:32 +00:00
continue
else :
2026-05-04 18:04:45 +00:00
if ALGO_CONFIG . Logging . Print_Summary_Each_Loop :
print ( f ' _____ End No Open Orders _____ (Algo Engine ms: { ( time . time ( ) - loop_start ) * 1000 : .2f } ); Sleeping for sec: { ALGO_CONFIG . Config . Loop_Sleep_Sec : .0f } ' )
2026-05-05 16:38:45 +00:00
time . sleep ( ALGO_CONFIG . Config . Loop_Sleep_Sec )
2026-04-21 20:22:33 +00:00
except KeyboardInterrupt :
2026-04-24 07:29:26 +00:00
logging . info ( ' CANCELLING OPEN ORDERS ' )
await kill_algo ( )
2026-04-21 20:22:33 +00:00
except Exception as e :
logging . error ( traceback . format_exc ( ) )
2026-04-24 07:29:26 +00:00
logging . critical ( f ' *** ALGO ENGINE CRASHED: { e } ' )
logging . info ( ' CANCELLING OPEN ORDERS ' )
utils . send_tg_alert ( f ' FR_ALGO_CRASHED: { str ( e ) } ' )
await kill_algo ( )
2026-04-23 06:39:51 +00:00
2026-04-23 16:34:47 +00:00
### MAIN STARTUP ###
2026-04-21 20:22:33 +00:00
async def main ( ) :
2026-04-23 06:39:51 +00:00
global EXTEND_CLIENT
2026-04-21 20:22:33 +00:00
global VAL_KEY
global CON
2026-04-25 23:43:28 +00:00
global ALGO_CONFIG
2026-05-01 20:45:26 +00:00
global ASTER
global EXTEND
global Open_Symbols
2026-04-21 20:22:33 +00:00
2026-04-23 06:39:51 +00:00
_ , EXTEND_CLIENT = await extend_auth . create_auth_account_and_trading_client ( )
2026-04-21 20:22:33 +00:00
VAL_KEY = valkey . Valkey ( host = ' localhost ' , port = 6379 , db = 0 , decode_responses = True )
engine = create_async_engine ( ' mysql+asyncmy://root:pwd@localhost/fund_rate ' )
2026-05-01 20:45:26 +00:00
await set_comb_open_symbols ( )
2026-05-04 18:04:45 +00:00
best_symbol_by_exchange : dict = json . loads ( s = VAL_KEY . get ( name = ' fr_engine_best_fund_rate_output ' ) ) # ty:ignore[invalid-argument-type]
2026-05-01 20:45:26 +00:00
if Open_Symbols :
2026-05-04 18:04:45 +00:00
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 ]
ASTER = structs . Perpetual_Exchange (
mult = int ( current_pos_master_ast [ ' max_leverage_ast ' ] ) ,
lh_asset = current_pos_master_ast [ ' lh_asset_ast ' ] ,
rh_asset = current_pos_master_ast [ ' rh_asset_ast ' ] ,
symbol_asset_separator = ' ' ,
initial_funding_rate = float ( current_pos_master_ast [ ' funding_rate_ast ' ] ) ,
min_price = float ( current_pos_master_ast [ ' min_price_ast ' ] ) ,
min_order_size = float ( current_pos_master_ast [ ' min_order_size_ast ' ] ) ,
min_lot_size = float ( current_pos_master_ast [ ' min_lot_size_ast ' ] ) ,
min_notional = float ( current_pos_master_ast [ ' min_notional_ast ' ] ) ,
2026-05-05 16:38:45 +00:00
buy_ratio = float ( current_pos_master_ast [ ' buy_ratio_ast ' ] ) ,
2026-05-04 18:04:45 +00:00
)
EXTEND = structs . Perpetual_Exchange (
mult = int ( current_pos_master_ast [ ' max_leverage_ext ' ] ) ,
lh_asset = current_pos_master_ast [ ' lh_asset_ext ' ] ,
rh_asset = current_pos_master_ast [ ' rh_asset_ext ' ] ,
symbol_asset_separator = ' - ' ,
initial_funding_rate = float ( current_pos_master_ast [ ' funding_rate_ext ' ] ) ,
min_price = float ( current_pos_master_ast [ ' min_price_ext ' ] ) ,
min_order_size = float ( current_pos_master_ast [ ' min_order_size_ext ' ] ) ,
min_lot_size = float ( current_pos_master_ast [ ' min_lot_size_ext ' ] ) ,
min_notional = float ( current_pos_master_ast [ ' min_notional_ext ' ] ) ,
2026-05-05 16:38:45 +00:00
buy_ratio = float ( current_pos_master_ast [ ' buy_ratio_ext ' ] ) ,
2026-05-04 18:04:45 +00:00
)
2026-05-01 20:45:26 +00:00
Open_Symbols . pop ( 0 )
2026-05-04 18:04:45 +00:00
else :
ASTER = structs . Perpetual_Exchange ( * * best_symbol_by_exchange [ ' ASTER ' ] )
EXTEND = structs . Perpetual_Exchange ( * * best_symbol_by_exchange [ ' EXTEND ' ] )
# await get_aster_exch_info(symbol_override=Open_Symbols[0])
# await get_extend_exch_info(symbol_override=Open_Symbols[0])
2026-05-01 20:45:26 +00:00
2026-04-30 04:32:49 +00:00
with open ( ' algo_config.json ' , mode = ' r ' , encoding = ' utf-8 ' ) as file :
2026-04-29 16:18:42 +00:00
ALGO_CONFIG = json . load ( file )
ALGO_CONFIG = structs . Algo_Config ( * * ALGO_CONFIG )
2026-04-30 04:32:49 +00:00
2026-04-29 16:18:42 +00:00
ALGO_CONFIG . Config . Max_Target_Notional = float ( min ( [ ASTER . mult , EXTEND . mult ] ) * ALGO_CONFIG . Config . Target_Open_Cash_Position )
2026-05-04 18:04:45 +00:00
# logging.info(f'Initial Algo Config: {ALGO_CONFIG}')
2026-04-29 16:18:42 +00:00
2026-04-30 04:32:49 +00:00
VAL_KEY . set ( name = ' fr_orchestrator_output ' , value = json . dumps ( obj = ALGO_CONFIG . model_dump ( ) ) )
VAL_KEY . set ( name = ' fr_algo_working_symbol ' , value = json . dumps ( obj = { ' ASTER ' : asdict ( obj = ASTER ) , ' EXTEND ' : asdict ( obj = EXTEND ) } ) )
2026-04-25 23:43:28 +00:00
2026-04-21 20:22:33 +00:00
async with engine . connect ( ) as CON :
2026-04-23 16:34:47 +00:00
### ASTER SETUP ###
2026-05-01 20:45:26 +00:00
# await get_aster_collateral()
2026-04-23 06:39:51 +00:00
await get_aster_notional_position ( )
2026-04-23 16:34:47 +00:00
await get_aster_exch_info ( )
await get_aster_open_orders ( )
### EXTEND SETUP ###
2026-05-01 20:45:26 +00:00
# await get_extend_collateral()
2026-04-23 06:39:51 +00:00
await get_extend_notional ( )
2026-04-23 16:34:47 +00:00
await get_extend_exch_info ( )
await get_extend_open_orders ( )
2026-04-23 06:39:51 +00:00
2026-04-21 20:22:33 +00:00
await run_algo ( )
if __name__ == ' __main__ ' :
START_TIME = round ( datetime . now ( ) . timestamp ( ) * 1000 )
logging . info ( 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 ( f " STARTED: { START_TIME } " )
asyncio . run ( main ( ) )