refactor algo orchestrator and hedge ratio bug fix

This commit is contained in:
2026-04-29 16:18:42 +00:00
parent 8f3f7c6667
commit dc3409ac40
14 changed files with 1475 additions and 11128 deletions

176
main.py
View File

@@ -5,7 +5,6 @@ import math
import os
import time
import traceback
from dataclasses import asdict, dataclass, field
from datetime import datetime, timezone
from decimal import ROUND_DOWN, Decimal
from typing import AsyncContextManager
@@ -37,24 +36,30 @@ load_dotenv()
LOG_FILEPATH: str = os.getenv("LOGS_PATH") + '/Fund_Rate_Algo.log'
### Algo Config ###
ALGO_CONFIG: structs.Algo_Config = None
ALGO_CONFIG: structs.Algo_Config | None = None
MIN_TIME_TO_FUNDING: int
### CONSTANTS ###
### EXCHANGES ###
ASTER = structs.Perpetual_Exchange(
mult = 150,
lh_asset = 'ETH',
rh_asset = 'USDT',
symbol_asset_separator = '',
)
EXTEND_LH_ASSET: str = 'ETH'
EXTEND_RH_ASSET: str = 'USD'
EXTEND_TICKER: str = EXTEND_LH_ASSET + '-' + EXTEND_RH_ASSET
EXTEND = structs.Perpetual_Exchange(
mult = 50,
lh_asset = 'ETH',
rh_asset = 'USD',
symbol_asset_separator = '-',
)
### GLOBALS ###
ASTER_MULT = 150
EXTEND_MULT = 50
Last_Aster_Fill_Time_Ts: float = 0.00
Just_Rejected_Or_Expired: bool = False
Best_Symbol_by_Exchange: dict = {}
# ASTER_MULT = 150
# EXTEND_MULT = 50
ASTER_MIN_ORDER_QTY = 0.001
EXTEND_MIN_ORDER_QTY = 0.01
@@ -121,7 +126,7 @@ async def get_aster_notional_position(resp: dict | None = None):
global ASTER_NOTIONAL_OBJ
global ASTER_NOTIONAL_POSITION
global ASTER_UNREALIZED_PNL
global ASTER_MULT
global ASTER
previous_notional_obj = ASTER_NOTIONAL_OBJ
@@ -159,32 +164,31 @@ async def get_aster_notional_position(resp: dict | None = None):
previous_notional_position = ASTER_NOTIONAL_POSITION
# if not resp: # this can never evaluate
# ASTER_MULT = float(d['leverage'])
# if abs(ASTER_NOTIONAL_POSITION) > ALGO_CONFIG.Max_Target_Notional*1.01:
if abs(ASTER_NOTIONAL_POSITION) > ALGO_CONFIG.Max_Target_Notional*2.01:
logging.info(f'BAD NOTIONAL - ASTER CHANGE: {previous_notional_position} -> {ASTER_NOTIONAL_POSITION}; UR PNL: {ASTER_UNREALIZED_PNL}; MULT: {ASTER_MULT}; d: {d}; resp: {resp}')
# ASTER.mult = float(d['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}; d: {d}; resp: {resp}')
await kill_algo()
# 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)}')
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)}')
async def get_extend_collateral():
global EXTEND_AVAIL_COLLATERAL
get_bals = dict(dict(await EXTEND_CLIENT.account.get_balance()).get('data', {}))
EXTEND_AVAIL_COLLATERAL = get_bals.get('available_for_trade', 0) if get_bals.get('collateral_name', None)==EXTEND_RH_ASSET else 0
EXTEND_AVAIL_COLLATERAL = get_bals.get('available_for_trade', 0) if get_bals.get('collateral_name', None)==EXTEND.rh_asset else 0
async def get_extend_notional(resp: dict | None = None):
global EXTEND_NOTIONAL_OBJ
global EXTEND_NOTIONAL_POSITION
global EXTEND_UNREALIZED_PNL
global EXTEND_MULT
global EXTEND
previous_notional_obj = EXTEND_NOTIONAL_OBJ
previous_notional_position = EXTEND_NOTIONAL_POSITION
if not resp:
resp = dict(await EXTEND_CLIENT.account.get_positions()).get('data', [])
pos_dict = [dict(d) for d in resp if dict(d).get('market') == EXTEND_TICKER]
pos_dict = [dict(d) for d in resp if dict(d).get('market') == EXTEND.symbol]
if pos_dict:
pos_dict = pos_dict[0]
else:
@@ -195,7 +199,7 @@ async def get_extend_notional(resp: dict | None = None):
pos_dict['timestamp_arrival'] = round(datetime.now().timestamp()*1000)
else:
pos_dict = [dict(d) for d in resp if dict(d).get('market') == EXTEND_TICKER]
pos_dict = [dict(d) for d in resp if dict(d).get('market') == EXTEND.symbol]
if pos_dict:
pos_dict = pos_dict[0]
else:
@@ -223,12 +227,12 @@ async def get_extend_notional(resp: dict | None = None):
logging.info(f'EXTEND BAD SIDE ON POSITION UPDATE: {pos_dict}')
EXTEND_NOTIONAL_POSITION = notional_pos_sided - float(EXTEND_UNREALIZED_PNL)
EXTEND_MULT = pos_dict.get('leverage', EXTEND_MULT)
if abs(EXTEND_NOTIONAL_POSITION) > ALGO_CONFIG.Max_Target_Notional*2.01:
logging.info(f'BAD NOTIONAL - EXTEND CHANGE: {previous_notional_position} -> {EXTEND_NOTIONAL_POSITION}; UR PNL: {EXTEND_UNREALIZED_PNL}; MULT: {EXTEND_MULT}; d: {pos_dict}; resp: {resp}')
EXTEND.mult = pos_dict.get('leverage', EXTEND)
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}; d: {pos_dict}; resp: {resp}')
await kill_algo()
if EXTEND_NOTIONAL_POSITION != previous_notional_position:
logging.info(f'EXTEND NOTIONAL CHANGE: {previous_notional_position} -> {EXTEND_NOTIONAL_POSITION:.2f}; UR PNL: {EXTEND_UNREALIZED_PNL:.2f}; MULT: {EXTEND_MULT:.0f}; resp: {bool(resp)}')
logging.info(f'EXTEND NOTIONAL CHANGE: {previous_notional_position} -> {EXTEND_NOTIONAL_POSITION:.2f}; UR PNL: {EXTEND_UNREALIZED_PNL:.2f}; MULT: {EXTEND.mult:.0f}; resp: {bool(resp)}')
### EXCHANGE INFO ###
async def get_aster_exch_info():
@@ -264,7 +268,7 @@ async def aster_cancel_all_orders():
logging.info(f'ASTER CANCEL ALL OPEN ORDERS RESP: {r}')
async def extend_cancel_all_orders():
r = await EXTEND_CLIENT.orders.mass_cancel(markets=[EXTEND_TICKER])
r = await EXTEND_CLIENT.orders.mass_cancel(markets=[EXTEND.symbol])
logging.info(f'EXTEND CANCEL ALL OPEN ORDERS RESP: {r}')
### KILL ALGO ###
@@ -277,29 +281,39 @@ async def kill_algo():
### ALGO LOOP ###
async def run_algo():
global ASTER
global EXTEND
global ALGO_CONFIG
global MIN_TIME_TO_FUNDING
global ASTER_OPEN_ORDERS
global EXTEND_OPEN_ORDERS
global Last_Aster_Fill_Time_Ts
global Just_Rejected_Or_Expired
global Best_Symbol_by_Exchange
try:
while True:
loop_start = time.time()
# print('__________Start___________')
### ALGO CONIFG ###
ALGO_CONFIG = json.loads(VAL_KEY.get('fr_orchestrator_output'), object_hook=lambda d: structs.Algo_Config(**d))
ALGO_CONFIG.Max_Target_Notional = float(min([ASTER_MULT, EXTEND_MULT]) * ALGO_CONFIG.Target_Open_Cash_Position)
ALGO_CONFIG = json.loads(VAL_KEY.get('fr_orchestrator_output'))
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)
MIN_TIME_TO_FUNDING = ALGO_CONFIG.Min_Time_To_Funding_Minutes * 60 * 1000
MIN_TIME_TO_FUNDING = ALGO_CONFIG.Config.Min_Time_To_Funding_Minutes * 60 * 1000
### Load Data from Feedhandlers ###
Best_Symbol_by_Exchange = json.loads(VAL_KEY.get('fr_engine_best_fund_rate_output'))
ASTER_FUND_RATE_DICT = json.loads(VAL_KEY.get('fund_rate_aster'))
EXTENDED_FUND_RATE_DICT = json.loads(VAL_KEY.get('fund_rate_extended'))
ASTER_FUND_RATE = float(ASTER_FUND_RATE_DICT.get('funding_rate', 0))
EXTEND_FUND_RATE = float(EXTENDED_FUND_RATE_DICT.get('funding_rate', 0))
if ALGO_CONFIG.Flip_Side_For_Testing:
if ALGO_CONFIG.Overrides.Flip_Side_For_Testing:
ASTER_FUND_RATE = ASTER_FUND_RATE * -1
EXTEND_FUND_RATE = EXTEND_FUND_RATE * -1
@@ -370,16 +384,20 @@ async def run_algo():
if order_update_status in ['CANCELED','EXPIRED']:
logging.info(f'ASTER ORDER CANCELLED or EXPIRED: {order_id}')
ASTER_OPEN_ORDERS.pop(idx)
Just_Rejected_Or_Expired = True
utils.send_tg_alert(f'FR_ALGO - ASTER REJECTED ({order_id})')
elif order_update_status in ['PARTIALLY_FILLED']:
logging.info(f'ASTER ORDER PARTIALLY FILLED: {order_id}')
await get_aster_collateral()
await get_aster_notional_position()
# await get_aster_collateral()
await get_aster_notional_position(resp=ASTER_WS_POS_UPDATES)
Last_Aster_Fill_Time_Ts = datetime.now().timestamp()*1000
utils.send_tg_alert(f'FR_ALGO - ASTER PARTIALLY FILLED ({order_id})')
elif order_update_status in ['FILLED']:
logging.info(f'ASTER ORDER FILLED: {order_id}')
ASTER_OPEN_ORDERS.pop(idx)
await get_aster_collateral()
await get_aster_notional_position()
# await get_aster_collateral()
await get_aster_notional_position(resp=ASTER_WS_POS_UPDATES)
Last_Aster_Fill_Time_Ts = datetime.now().timestamp()*1000
utils.send_tg_alert(f'FR_ALGO - ASTER FILLED ({order_id})')
else:
logging.critical(f'EXTEND ORDER STATUS CHG TO UNEXPECTED VALUE, KILLING... ({order_id}): {order_orig_status} -> {order_update_status}')
@@ -401,6 +419,8 @@ async def run_algo():
if order_update_status in ['CANCELLED','EXPIRED','REJECTED']:
logging.info(f'EXTEND ORDER CANCELLED, REJECTED or EXPIRED: {order_id}')
EXTEND_OPEN_ORDERS.pop(idx)
Just_Rejected_Or_Expired = True
utils.send_tg_alert(f'FR_ALGO - EXTEND REJECTED ({order_id})')
elif order_update_status in ['PARTIALLY_FILLED']:
logging.info(f'EXTEND ORDER PARTIALLY FILLED: {order_id}')
await get_extend_collateral()
@@ -416,10 +436,20 @@ async def run_algo():
logging.critical(f'EXTEND ORDER STATUS CHG TO UNEXPECTED VALUE, KILLING... ({order_id}): {order_orig_status} -> {order_update_status}')
# if Best_Symbol_by_Exchange['symbol_aster'] != ASTER.symbol:
# if abs( ASTER_NOTIONAL_POSITION ) > 0 or abs( EXTEND_NOTIONAL_POSITION ) > 0:
# print('Symbol switch - Flattening Positions')
# ALGO_CONFIG.Overrides.Flatten_Open_Positions = True
# else:
# print(f'Balances Flattened - Updating to Trade New Symbol: ASTER.symbol -> {Best_Symbol_by_Exchange['symbol_aster']}')
# # ASTER.symbol = Best_Symbol_by_Exchange['symbol_aster']
# # EXTEND.symbol = Best_Symbol_by_Exchange['symbol_extended']
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
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
@@ -429,20 +459,20 @@ async def run_algo():
if ALPHA_FUND_RATE < 0:
ALPHA_CARRY_SIDE = 'BUY'
ALPHA_TGT_NOTIONAL = ALGO_CONFIG.Max_Target_Notional
ALPHA_TGT_NOTIONAL = ALGO_CONFIG.Config.Max_Target_Notional
else:
ALPHA_CARRY_SIDE = 'SELL'
ALPHA_TGT_NOTIONAL = ALGO_CONFIG.Max_Target_Notional*-1
ALPHA_TGT_NOTIONAL = ALGO_CONFIG.Config.Max_Target_Notional*-1
def calc_next_net_fund_rate(FUNDINGS_AT_SAME_TIME_NEXT_HR: bool) -> float:
if FUNDINGS_AT_SAME_TIME_NEXT_HR:
return ASTER_FUND_RATE + EXTEND_FUND_RATE
return max([ASTER_FUND_RATE, EXTEND_FUND_RATE]) - min([ASTER_FUND_RATE, EXTEND_FUND_RATE])
else:
return EXTEND_FUND_RATE
NEXT_NET_FUNDING_RATE = calc_next_net_fund_rate(FUNDINGS_AT_SAME_TIME_NEXT_HR)
Flags.NET_FUNDING_IS_ZERO = ( NEXT_NET_FUNDING_RATE >= ( (ALGO_CONFIG.Min_Fund_Rate_Pct_To_Trade*-1) / 100) ) and ( NEXT_NET_FUNDING_RATE <= ( ALGO_CONFIG.Min_Fund_Rate_Pct_To_Trade / 100 ) )
if Flags.NET_FUNDING_IS_ZERO or ALGO_CONFIG.Flatten_Open_Positions:
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 ) )
if Flags.NET_FUNDING_IS_ZERO or ALGO_CONFIG.Overrides.Flatten_Open_Positions:
ALPHA_TGT_NOTIONAL = 0.00
# if ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS:
# logging.info('NET FUNDING = 0.00; Cancelling Open Orders! then Waiting...')
@@ -507,26 +537,26 @@ async def run_algo():
ASTER_TGT_TAIL_ORDERABLE = abs(ASTER_TGT_TAIL_BASE_QTY) >= MAX_MIN_ORDER_QTY
EXTEND_TGT_TAIL_ORDERABLE = abs(EXTEND_TGT_TAIL_BASE_QTY) >= MAX_MIN_ORDER_QTY
Hedge_Ratio = abs(( abs( max([abs(float(EXTEND_NOTIONAL_POSITION)), 0.01]) / max([abs(float(ASTER_NOTIONAL_POSITION)), 0.01]) ) - 1 ) * 100)
# Hedge_Ratio = abs(( abs( max([abs(float(EXTEND_NOTIONAL_POSITION)), 0.01]) / max([abs(float(ASTER_NOTIONAL_POSITION)), 0.01]) ) - 1 ) * 100)
Hedge_Ratio = abs( ( EXTEND_NOTIONAL_POSITION + ASTER_NOTIONAL_POSITION ) / ASTER_NOTIONAL_POSITION ) * 100
Currently_Hedged = Hedge_Ratio < 1.00
def print_summary(use_logging: bool = False):
OUT: print | logging.info = logging.info if use_logging else print
OUT(f'''
LOOP SLEEP (SEC): {ALGO_CONFIG.Loop_Sleep_Sec}
FLIP SIDES FOR TESTING?: {ALGO_CONFIG.Flip_Side_For_Testing}; ASTER ORDER ENABLED? {ALGO_CONFIG.Allow_Ordering_Aster}; EXTEND ORDER ENABLED? {ALGO_CONFIG.Allow_Ordering_Extend}
LOOP SLEEP (SEC): {ALGO_CONFIG.Config.Loop_Sleep_Sec}
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}
{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:.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: [ Available Collateral: {ASTER_AVAIL_COLLATERAL:.4f} ] | EXTEND: [ Available Collateral: {EXTEND_AVAIL_COLLATERAL:.4f} ]
ASTER: [ Notional Position $ : {ASTER_NOTIONAL_POSITION:.4f} ] | EXTEND: [ Notional Position $ : {EXTEND_NOTIONAL_POSITION:.4f} ]
SAME TIME? : {FUNDINGS_AT_SAME_TIME_NEXT_HR} [ Minutes Between Fundings: {min_between_fundings} ]
NET FUNDING : {NEXT_NET_FUNDING_RATE:.6%} [{NEXT_NET_FUNDING_RATE*10_000:.2f}bps] [{NEXT_NET_FUNDING_RATE*1_000_000:.0f}pips]; Is Zero?: {Flags.NET_FUNDING_IS_ZERO} [Min: {ALGO_CONFIG.Min_Fund_Rate_Pct_To_Trade}]
NET FUNDING : {NEXT_NET_FUNDING_RATE:.6%} [{NEXT_NET_FUNDING_RATE*10_000:.2f}bps] [{NEXT_NET_FUNDING_RATE*1_000_000:.0f}pips]; Is Zero?: {Flags.NET_FUNDING_IS_ZERO} [Min: {ALGO_CONFIG.Config.Min_Fund_Rate_Pct_To_Trade}]
ALPHA SIDE : {ALPHA_EXCH} [{ALPHA_CARRY_SIDE}]
TGT NOTIONAL: $ {abs(ALPHA_TGT_NOTIONAL):.2f}; Flatten Open Positions Flag? {ALGO_CONFIG.Flatten_Open_Positions}
TGT NOTIONAL: $ {abs(ALPHA_TGT_NOTIONAL):.2f}; Flatten Open Positions Flag? {ALGO_CONFIG.Overrides.Flatten_Open_Positions}
ASTER: {ASTER_NOTIONAL_POSITION:.4f} -> {ASTER_TGT_NOTIONAL:.2f} [ Remain: {ASTER_TGT_TAIL:.4f} ] | EXTEND: {EXTEND_NOTIONAL_POSITION:.4f} -> {EXTEND_TGT_NOTIONAL:.2f} [ Remain: {EXTEND_TGT_TAIL:.4f} ]
ASTER: {ASTER_TGT_NOTIONAL:.2f} - {ASTER_NOTIONAL_POSITION:.2f} + {ASTER_UNREALIZED_PNL:.2f} = {ASTER_TGT_TAIL:2f} | EXTEND: {EXTEND_TGT_NOTIONAL:.2f} - {EXTEND_NOTIONAL_POSITION:.2f} + {EXTEND_UNREALIZED_PNL:.2f} = {EXTEND_TGT_TAIL:2f}
@@ -543,35 +573,38 @@ async def run_algo():
--- EXTEND OPEN ORDERS ---
{EXTEND_OPEN_ORDERS}
''')
# ASTER: [ Available Collateral: {ASTER_AVAIL_COLLATERAL:.4f} ] | EXTEND: [ Available Collateral: {EXTEND_AVAIL_COLLATERAL:.4f} ]
# Try Making Hedge Order Contingent on Alpha Order Fills (Basically Hedge has to wait for sig Diff in Balance to order.) would improve when extended is thin (Overnight).
if ALGO_CONFIG.Log_Summary_Each_Loop:
if ALGO_CONFIG.Logging.Log_Summary_Each_Loop:
print_summary(use_logging=True)
if ALGO_CONFIG.Print_Summary_Each_Loop:
if ALGO_CONFIG.Logging.Print_Summary_Each_Loop:
print_summary(use_logging=False)
# print_summary()
### ROUTES ###
# MIN_EXPECTED_ALPHA_TO_TRADE = 0.0001
MIN_EXPECTED_ALPHA_TO_TRADE = -0.000001
# ALPHA RATIO CHECK
if not( ( Expected_Alpha_w_Taker > MIN_EXPECTED_ALPHA_TO_TRADE ) or ( ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS ) or ALGO_CONFIG.Flatten_Open_Positions ):
# logging.info(f'Alpha Ratio too low ({ALPHA_RATIO:.8f}) and no Open Orders...')
if not( ( Expected_Alpha_Net_FR_w_Taker > MIN_EXPECTED_ALPHA_TO_TRADE ) or ( ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS or Just_Rejected_Or_Expired or ALGO_CONFIG.Overrides.Flatten_Open_Positions) ):
if ALGO_CONFIG.Logging.Print_Summary_Each_Loop:
print(f'Alpha Ratio too low ({ALPHA_RATIO:.8f}) and no Open Orders...')
pass
elif ( Expected_Alpha_w_Taker <= MIN_EXPECTED_ALPHA_TO_TRADE ) and ( ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS ) and Currently_Hedged:
elif ( Expected_Alpha_Net_FR_w_Taker <= MIN_EXPECTED_ALPHA_TO_TRADE ) and ( ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS ) and Currently_Hedged:
await aster_cancel_all_orders()
await extend_cancel_all_orders()
logging.info('Expected_Alpha went away with open orders...cancelling since we are currently hedged...')
time.sleep( (1/1000)*100 ) # 100ms wait for ws cancel response
# time.sleep( (1/1000)*100 ) # 100ms wait for ws cancel response
else:
# logging.info(f'*** Alpha Ratio HIT - LETS ORDER: {ALPHA_RATIO:.8f}')
# ASTER
if ASTER_TGT_TAIL_ORDERABLE and ALGO_CONFIG.Allow_Ordering_Aster:
if ASTER_TGT_TAIL_ORDERABLE and ALGO_CONFIG.Overrides.Allow_Ordering_Aster:
symbol = ASTER.symbol
side = 'BUY' if ASTER_TGT_TAIL_BASE_QTY > 0.00 else 'SELL'
qty = str(abs(ASTER_TGT_TAIL_BASE_QTY))
price = ASTER_TOB_PX - ALGO_CONFIG.Price_Worsener_Aster if side == 'BUY' else ASTER_TOB_PX + ALGO_CONFIG.Price_Worsener_Aster
price = ASTER_TOB_PX - ALGO_CONFIG.Config.Price_Worsener_Aster if side == 'BUY' else ASTER_TOB_PX + ALGO_CONFIG.Config.Price_Worsener_Aster
if abs( ( float(ASTER_TGT_TAIL_BASE_QTY)*float(price) ) + ASTER_NOTIONAL_POSITION ) > ALGO_CONFIG.Max_Target_Notional*1.01:
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})')
await kill_algo()
if ASTER_OPEN_ORDERS:
@@ -622,6 +655,7 @@ async def run_algo():
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:.2f}; Value: {float(ASTER_TGT_TAIL_BASE_QTY)*float(price):.2f}; Price: {float(price):.2f}')
logging.info(f'ASTER ORDER PLACED SUCCESS: {order_resp}')
print_summary(use_logging=True)
@@ -634,13 +668,20 @@ async def run_algo():
await aster_cancel_all_orders()
# EXTEND
if EXTEND_TGT_TAIL_ORDERABLE and ALGO_CONFIG.Allow_Ordering_Extend:
symbol = EXTEND_TICKER
if EXTEND_TGT_TAIL_ORDERABLE and ALGO_CONFIG.Overrides.Allow_Ordering_Extend:
Time_Since_Last_Aster_Fill_ms = ( datetime.now().timestamp()*1000 ) - Last_Aster_Fill_Time_Ts
if Time_Since_Last_Aster_Fill_ms > ( 1000 * ALGO_CONFIG.Config.Switch_To_Taker_Seconds ): # Change to allow taker orders if its been more than x seconds
post_only = False
price = EXTEND_TOB_PX - ALGO_CONFIG.Config.Price_Worsener_Extend if side == 'BUY' else EXTEND_TOB_PX + ALGO_CONFIG.Config.Price_Worsener_Extend
else:
post_only = True
price = EXTEND_TOB_PX
symbol = EXTEND.symbol
side = OrderSide.BUY if EXTEND_TGT_TAIL_BASE_QTY > 0.00 else OrderSide.SELL
qty = Decimal(str(abs(EXTEND_TGT_TAIL_BASE_QTY)))
price = EXTEND_TOB_PX - ALGO_CONFIG.Price_Worsener_Extend if side == 'BUY' else EXTEND_TOB_PX + ALGO_CONFIG.Price_Worsener_Extend
if abs( ( float(EXTEND_TGT_TAIL_BASE_QTY)*float(price) ) + EXTEND_NOTIONAL_POSITION ) > ALGO_CONFIG.Max_Target_Notional*1.01:
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:.2f} + {float(EXTEND_TGT_TAIL_BASE_QTY)*float(price):.2f} (qty: {float(EXTEND_TGT_TAIL_BASE_QTY):.2f}; px: {float(price):.2f})')
await kill_algo()
if EXTEND_OPEN_ORDERS:
@@ -667,17 +708,20 @@ async def run_algo():
logging.info('EXTEND OPEN ORDER NO PX CHG; SKIPPING')
else:
try:
taker_fee = taker_fee=Decimal("0.00000") if post_only else Decimal("0.00025")
order_resp = await EXTEND_CLIENT.place_order(
market_name=symbol,
amount_of_synthetic=qty,
price=price,
side=side,
taker_fee=Decimal("0.00025"),
taker_fee=taker_fee,
previous_order_id=open_order_id,
post_only=post_only,
)
except Exception as e:
logging.error(f'EXTEND ORDER PLACEMENT FAILED - RESP: {order_resp}')
logging.error(f'EXTEND ORDER PLACEMENT FAILED: {e}')
logging.error(f'EXTEND ORDER PLACEMENT FAILED - POSTED: market_name:{symbol}, amount_of_synthetic:{qty}, price:{price}, side:{side},taker_fee:{taker_fee}, previous_order_id:{open_order_id}, post_only:{post_only}')
logging.error(traceback.format_exc())
order_resp_dict = dict(order_resp)
@@ -694,6 +738,7 @@ async def run_algo():
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)
@@ -710,8 +755,8 @@ async def run_algo():
if ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS:
continue
else:
time.sleep(ALGO_CONFIG.Loop_Sleep_Sec)
# print(f'_____ End No Open Orders _____ (Algo Engine ms: {(time.time() - loop_start)*1000:.2f}); Sleeping for sec: {ALGO_CONFIG.Loop_Sleep_Sec:.0f}')
time.sleep(ALGO_CONFIG.Config.Loop_Sleep_Sec)
# 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}')
except KeyboardInterrupt:
logging.info('CANCELLING OPEN ORDERS')
@@ -736,10 +781,15 @@ async def main():
engine = create_async_engine('mysql+asyncmy://root:pwd@localhost/fund_rate')
with open('algo_config.json', 'r', encoding='utf-8') as file:
ALGO_CONFIG = json.load(file, object_hook=lambda d: structs.Algo_Config(**d))
ALGO_CONFIG.Max_Target_Notional = float(min([ASTER_MULT, EXTEND_MULT]) * ALGO_CONFIG.Target_Open_Cash_Position)
ALGO_CONFIG = json.load(file)
ALGO_CONFIG = structs.Algo_Config(**ALGO_CONFIG)
VAL_KEY.set('fr_orchestrator_output', json.dumps(asdict(ALGO_CONFIG)))
# with open('algo_config.json', 'r', encoding='utf-8') as file:
# ALGO_CONFIG = json.load(file, object_hook=lambda d: structs.Algo_Config(**d))
ALGO_CONFIG.Config.Max_Target_Notional = float(min([ASTER.mult, EXTEND.mult]) * ALGO_CONFIG.Config.Target_Open_Cash_Position)
VAL_KEY.set('fr_orchestrator_output', json.dumps(ALGO_CONFIG.model_dump()))
async with engine.connect() as CON:
### ASTER SETUP ###