extend positions bug fix

This commit is contained in:
2026-04-26 06:10:18 +00:00
parent 73c4eb1bf8
commit f1839d0779
8 changed files with 167 additions and 44 deletions

View File

@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 7,
"id": "d1eed397",
"metadata": {},
"outputs": [],
@@ -18,7 +18,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 8,
"id": "c6151613",
"metadata": {},
"outputs": [],
@@ -28,7 +28,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 31,
"id": "d83c61e5",
"metadata": {},
"outputs": [
@@ -38,16 +38,32 @@
"1"
]
},
"execution_count": 3,
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"config_update = {'Min_Time_To_Funding_Minutes': 7}\n",
"config_update = {\n",
" 'Min_Time_To_Funding_Minutes': 60,\n",
" 'Print_Summary_Each_Loop': True,\n",
" 'Allow_Ordering_Aster': True,\n",
" 'Allow_Ordering_Extend': True,\n",
" 'Loop_Sleep_Sec': 0.0,\n",
" 'Flip_Side_For_Testing': False,\n",
" 'Price_Worsener_Extend': 0.0,\n",
"}\n",
"VAL_KEY.publish('fr_orchestrator_input', json.dumps(config_update))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d2fdd7d2",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 11,
@@ -71,24 +87,26 @@
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'Config_Updated_Timestamp': 1777098091913,\n",
" 'Allow_Ordering_Aster': True,\n",
" 'Allow_Ordering_Extend': True,\n",
" 'Allow_Ordering_Aster': False,\n",
" 'Allow_Ordering_Extend': False,\n",
" 'Loop_Sleep_Sec': 1,\n",
" 'Max_Target_Notional': 0.0,\n",
" 'Min_Time_To_Funding_Minutes': 60,\n",
" 'Price_Worsener_Aster': 0.0,\n",
" 'Price_Worsener_Extend': 0.0,\n",
" 'Target_Open_Cash_Position': 10}"
" 'Target_Open_Cash_Position': 10,\n",
" 'Print_Summary_Each_Loop': False,\n",
" 'Flip_Side_For_Testing': False}"
]
},
"execution_count": 16,
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}

View File

@@ -2,11 +2,12 @@
"Config_Updated_Timestamp": 1777098091913,
"Allow_Ordering_Aster": true,
"Allow_Ordering_Extend": true,
"Loop_Sleep_Sec": 1,
"Loop_Sleep_Sec": 0.00,
"Max_Target_Notional": 0.00,
"Min_Time_To_Funding_Minutes": 60,
"Min_Time_To_Funding_Minutes": 10,
"Price_Worsener_Aster": 0.0,
"Price_Worsener_Extend": 0.0,
"Target_Open_Cash_Position": 10,
"Print_Summary_Each_Loop" : false
"Print_Summary_Each_Loop" : false,
"Flip_Side_For_Testing": false
}

File diff suppressed because one or more lines are too long

64
main.py
View File

@@ -229,6 +229,8 @@ async def kill_algo():
### ALGO LOOP ###
async def run_algo():
global ALGO_CONFIG
global ASTER_OPEN_ORDERS
global EXTEND_OPEN_ORDERS
try:
while True:
@@ -246,8 +248,14 @@ async def run_algo():
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:
ASTER_FUND_RATE = ASTER_FUND_RATE * -1
EXTEND_FUND_RATE = EXTEND_FUND_RATE * -1
ASTER_FUND_RATE_TIME = float(ASTER_FUND_RATE_DICT.get('next_funding_time_ts_ms', 0))
EXTEND_FUND_RATE_TIME = float(EXTENDED_FUND_RATE_DICT.get('next_funding_time_ts_ms', 0))
EXTEND_FUND_RATE_TIME = max([EXTEND_FUND_RATE_TIME, 0])
ASTER_TICKER_DICT = json.loads(VAL_KEY.get('fut_ticker_aster'))
EXTENDED_TICKER_DICT = json.loads(VAL_KEY.get('fut_ticker_extended'))
@@ -290,11 +298,13 @@ async def run_algo():
if len(EXTEND_WS_POS_UPDATES) > 0:
await get_extend_notional(resp=EXTEND_WS_POS_UPDATES)
# await get_extend_notional() # ************** NOT USING WEBSOCKET FEED DUE TO ISSUES WITH IT OVERWRITING API DATA ie the WS just statically shows last update and doesnt pull new when you start the algo.
### Also WS was just stale and caused issues where it sees a fill then gets new API Collateral (correct) and then the next loop would be the old incorrect collateral in the WS, causing bad orders. Do not have issue on ASTER.
if ASTER_WS_ORDER_UPDATES is not None:
for idx, o in enumerate(ASTER_OPEN_ORDERS):
order_id = o.get('order_id') if o.get('order_id') is not None else o.get('orderId')
order_orig_status = o['status']
order_id = o.get('order_id') if o.get('order_id') is not None else o['orderId']
order_orig_status = o['status'] ### Got a keyerror on this
order_update = [ou for ou in ASTER_WS_ORDER_UPDATES if ou.get('order_id', None) == order_id]
if len(order_update) > 0:
@@ -349,9 +359,6 @@ async def run_algo():
else:
logging.critical(f'EXTEND ORDER STATUS CHG TO UNEXPECTED VALUE, KILLING... ({order_id}): {order_orig_status} -> {order_update_status}')
ASTER_PAYOUT_DIRECTION_STR = 'LONG PAYS SHORT' if ASTER_FUND_RATE > 0 else 'SHORT PAYS LONG'
EXTEND_PAYOUT_DIRECTION_STR = 'LONG PAYS SHORT' if EXTEND_FUND_RATE > 0 else 'SHORT PAYS LONG'
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
@@ -382,7 +389,6 @@ async def run_algo():
Flags.NET_FUNDING_IS_ZERO = NEXT_NET_FUNDING_RATE == 0.00
if Flags.NET_FUNDING_IS_ZERO:
logging.info('NET FUNDING = 0.00; Cancelling Open Orders; Wait Until Non-Zero.')
ALPHA_TGT_NOTIONAL = 0.00
if ALPHA_EXCH == 'EXTEND':
@@ -420,9 +426,10 @@ async def run_algo():
OUT: print | logging.info = logging.info if use_logging else print
OUT(f'''
FLIP SIDES FOR TESTING?: {ALGO_CONFIG.Flip_Side_For_Testing}
{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: {ASTER_PAYOUT_DIRECTION_STR} | EXTEND: {EXTEND_PAYOUT_DIRECTION_STR}
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} ]
@@ -508,7 +515,8 @@ async def run_algo():
logging.info(f'ASTER ORDER PLACED SUCCESS: {order_resp}')
print_summary(use_logging=True)
else:
logging.warning('ASTER PLACE ORDER CHECKS FAILED, SKIPPING')
pass
# logging.warning('ASTER PLACE ORDER CHECKS FAILED, SKIPPING')
elif not(ASTER_TGT_TAIL_ORDERABLE) and ASTER_OPEN_ORDERS:
logging.info('ASTER HAS NO TAIL BUT OPEN ORDERS - CANCELLING OPEN ORDERS')
@@ -528,7 +536,16 @@ async def run_algo():
open_order_dict = dict(EXTEND_OPEN_ORDERS[0])
open_order_id = open_order_dict['external_id']
open_order_px = float(open_order_dict['price'])
place_order = True
open_order_filled_qty = float(open_order_dict['filled_qty'])
# qty = abs(float(qty)) - abs(float(open_order_filled_qty)) # Was trying to account for partial fills but thats not necessary, handled by position change so qty is correct w/o further adj.
# qty = Decimal(str(qty))
if qty >= MAX_MIN_ORDER_QTY:
place_order = True
else:
place_order = False
logging.info(f'EXTEND NOT ORDERING DUE TO FILLED QTY RESIDUAL < MIN ORDER; Filled: {float(open_order_filled_qty):.4f}; Residual: {qty:.4f}')
else:
open_order_id = None
open_order_px = 0
@@ -538,15 +555,22 @@ async def run_algo():
if round(open_order_px - float(price), 2) == 0.00:
logging.info('EXTEND OPEN ORDER NO PX CHG; SKIPPING')
else:
order_resp = await EXTEND_CLIENT.place_order(
market_name=symbol,
amount_of_synthetic=qty,
price=price,
side=side,
taker_fee=Decimal("0.00025"),
previous_order_id=open_order_id,
)
try:
order_resp = await EXTEND_CLIENT.place_order(
market_name=symbol,
amount_of_synthetic=qty,
price=price,
side=side,
taker_fee=Decimal("0.00025"),
previous_order_id=open_order_id,
)
except Exception as e:
logging.error(f'EXTEND ORDER PLACEMENT FAILED - RESP: {order_resp}')
logging.error(f'EXTEND ORDER PLACEMENT FAILED: {e}')
logging.error(traceback.format_exc())
order_resp_dict = dict(order_resp)
if order_resp_dict.get('status', None) == 'OK':
if EXTEND_OPEN_ORDERS:
EXTEND_OPEN_ORDERS.pop(0)
@@ -554,11 +578,15 @@ async def run_algo():
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)
EXTEND_OPEN_ORDERS.append(order_dict)
utils.send_tg_alert(f'FR_ALGO - EXTEND Order. 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:
order_resp_dict.get
else:
logging.warning('EXTEND PLACE ORDER CHECKS FAILED, SKIPPING')
@@ -595,6 +623,8 @@ async def main():
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)
VAL_KEY.set('fr_orchestrator_output', json.dumps(asdict(ALGO_CONFIG)))
async with engine.connect() as CON:
### ASTER SETUP ###

View File

@@ -20,7 +20,6 @@ async def insert_df_to_mysql(
df = pd.DataFrame(params)
else:
df = params
print(f'DB INSERT: table: {table_name}; CON: {CON}; params: {params}')
await CON.run_sync(
lambda sync_conn: df.to_sql(name=table_name, con=sync_conn, if_exists='append', index=False)
)

View File

@@ -18,6 +18,7 @@ class Algo_Config:
Target_Open_Cash_Position: int
Print_Summary_Each_Loop: bool = False
Flip_Side_For_Testing: bool = False
@dataclass(kw_only=True)
class Flags:

View File

@@ -193,7 +193,7 @@ async def ws_stream():
'updated_at_ts': p['updatedAt'],
}
list_for_df.append(position_update)
LOCAL_RECENT_POSITIONS = utils.upsert_list_of_dicts_by_id(LOCAL_RECENT_POSITIONS, position_update, id='position_id', seq_check_field='sequence_id')
LOCAL_RECENT_POSITIONS = utils.upsert_list_of_dicts_by_id(LOCAL_RECENT_POSITIONS, position_update, id='market', seq_check_field='sequence_id')
LOCAL_RECENT_POSITIONS = [t for t in LOCAL_RECENT_POSITIONS if t.get('timestamp_arrival', 0) >= LOOKBACK_MIN_TS_MS]
VAL_KEY_OBJ = json.dumps(LOCAL_RECENT_POSITIONS)