testing ordering as taker

This commit is contained in:
2026-04-28 15:02:32 +00:00
parent 484fe4ba0b
commit 7d55712278
7 changed files with 5937 additions and 162 deletions

View File

@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
"execution_count": 76,
"execution_count": 8,
"id": "d1eed397",
"metadata": {},
"outputs": [],
@@ -18,7 +18,7 @@
},
{
"cell_type": "code",
"execution_count": 77,
"execution_count": 9,
"id": "c6151613",
"metadata": {},
"outputs": [],
@@ -38,21 +38,22 @@
"1"
]
},
"execution_count": 131,
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"config_update = {\n",
" 'Min_Time_To_Funding_Minutes': 7,\n",
" # 'Allow_Ordering_Aster': True,\n",
" # 'Allow_Ordering_Extend': True,\n",
" # 'Min_Time_To_Funding_Minutes': 60,\n",
" # 'Allow_Ordering_Aster': False,\n",
" # 'Allow_Ordering_Extend': False,\n",
" 'Loop_Sleep_Sec': 0.00,\n",
"# 'Flip_Side_For_Testing': False,\n",
" 'Min_Fund_Rate_Pct_To_Trade': 0.0001,\n",
" # 'Flip_Side_For_Testing': False,\n",
"# 'Price_Worsener_Extend': 0.0,\n",
" 'Log_Summary_Each_Loop': False,\n",
" 'Print_Summary_Each_Loop': False,\n",
" # 'Log_Summary_Each_Loop': False,\n",
" # 'Print_Summary_Each_Loop': False,\n",
"}\n",
"VAL_KEY.publish('fr_orchestrator_input', json.dumps(config_update))"
]

View File

@@ -1,14 +1,16 @@
{
"Config_Updated_Timestamp": 1777312620005,
"Config_Updated_Timestamp": 1777388081818,
"Allow_Ordering_Aster": true,
"Allow_Ordering_Extend": true,
"Loop_Sleep_Sec": 0.0,
"Max_Target_Notional": 0.0,
"Min_Time_To_Funding_Minutes": 7,
"Price_Worsener_Aster": 0.0,
"Price_Worsener_Extend": 0.0,
"Min_Time_To_Funding_Minutes": 60,
"Min_Fund_Rate_Pct_To_Trade": 0.000,
"Price_Worsener_Aster": 0.01,
"Price_Worsener_Extend": -0.1,
"Target_Open_Cash_Position": 10,
"Log_Summary_Each_Loop": false,
"Print_Summary_Each_Loop": false,
"Print_Summary_Each_Loop": true,
"Flatten_Open_Positions": true,
"Flip_Side_For_Testing": false
}

View File

@@ -594,7 +594,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.12"
"version": "3.13.13"
}
},
"nbformat": 4,

5701
engine_dispersion.ipynb Normal file

File diff suppressed because one or more lines are too long

View File

@@ -27,7 +27,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "ff971ca9",
"metadata": {},
"outputs": [],
@@ -44,7 +44,7 @@
"ORDER_MARKET = \"ETH-USD\"\n",
"ORDER_SIDE = OrderSide.BUY\n",
"ORDER_QTY = Decimal(\"0.01\")\n",
"ORDER_PRICE = Decimal(\"2200\")"
"ORDER_PRICE = Decimal(\"2200.1\")"
]
},
{
@@ -59,10 +59,33 @@
},
{
"cell_type": "code",
"execution_count": 41,
"execution_count": 4,
"id": "c366706f",
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Error response from https://api.starknet.extended.exchange/api/v1/user/order: {\"status\":\"ERROR\",\"error\":{\"code\":1125,\"message\":\"Invalid price precision\"}}\n"
]
},
{
"ename": "ValueError",
"evalue": "Error response from https://api.starknet.extended.exchange/api/v1/user/order: code 400 - {\"status\":\"ERROR\",\"error\":{\"code\":1125,\"message\":\"Invalid price precision\"}}",
"output_type": "error",
"traceback": [
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
"\u001b[31mValueError\u001b[39m Traceback (most recent call last)",
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m placed_order = await trading_client.place_order(\n\u001b[32m 2\u001b[39m market_name=ORDER_MARKET,\n\u001b[32m 3\u001b[39m amount_of_synthetic=ORDER_QTY,\n\u001b[32m 4\u001b[39m price=ORDER_PRICE,\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/py_313/lib/python3.13/site-packages/x10/perpetual/trading_client/trading_client.py:101\u001b[39m, in \u001b[36mPerpetualTradingClient.place_order\u001b[39m\u001b[34m(self, market_name, amount_of_synthetic, price, side, taker_fee, post_only, previous_order_id, expire_time, time_in_force, self_trade_protection_level, external_id, builder_fee, builder_id, reduce_only, tp_sl_type, take_profit, stop_loss)\u001b[39m\n\u001b[32m 78\u001b[39m expire_time = utc_now() + timedelta(hours=\u001b[32m1\u001b[39m)\n\u001b[32m 80\u001b[39m order = create_order_object(\n\u001b[32m 81\u001b[39m account=\u001b[38;5;28mself\u001b[39m.__stark_account,\n\u001b[32m 82\u001b[39m market=market,\n\u001b[32m (...)\u001b[39m\u001b[32m 99\u001b[39m stop_loss=stop_loss,\n\u001b[32m 100\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m101\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;01mawait\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__order_management_module.place_order(order)\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/py_313/lib/python3.13/site-packages/x10/perpetual/trading_client/order_management_module.py:31\u001b[39m, in \u001b[36mOrderManagementModule.place_order\u001b[39m\u001b[34m(self, order)\u001b[39m\n\u001b[32m 28\u001b[39m LOGGER.debug(\u001b[33m\"\u001b[39m\u001b[33mPlacing an order: id=\u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[33m\"\u001b[39m, order.id)\n\u001b[32m 30\u001b[39m url = \u001b[38;5;28mself\u001b[39m._get_url(\u001b[33m\"\u001b[39m\u001b[33m/user/order\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m---> \u001b[39m\u001b[32m31\u001b[39m response = \u001b[38;5;28;01mawait\u001b[39;00m send_post_request(\n\u001b[32m 32\u001b[39m \u001b[38;5;28;01mawait\u001b[39;00m \u001b[38;5;28mself\u001b[39m.get_session(),\n\u001b[32m 33\u001b[39m url,\n\u001b[32m 34\u001b[39m PlacedOrderModel,\n\u001b[32m 35\u001b[39m json=order.to_api_request_json(exclude_none=\u001b[38;5;28;01mTrue\u001b[39;00m),\n\u001b[32m 36\u001b[39m api_key=\u001b[38;5;28mself\u001b[39m._get_api_key(),\n\u001b[32m 37\u001b[39m )\n\u001b[32m 38\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m response\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/py_313/lib/python3.13/site-packages/x10/utils/http.py:173\u001b[39m, in \u001b[36msend_post_request\u001b[39m\u001b[34m(session, url, model_class, json, api_key, request_headers, response_code_to_exception)\u001b[39m\n\u001b[32m 171\u001b[39m \u001b[38;5;28;01masync\u001b[39;00m \u001b[38;5;28;01mwith\u001b[39;00m session.post(url, json=json, headers=headers) \u001b[38;5;28;01mas\u001b[39;00m response:\n\u001b[32m 172\u001b[39m response_text = \u001b[38;5;28;01mawait\u001b[39;00m response.text()\n\u001b[32m--> \u001b[39m\u001b[32m173\u001b[39m \u001b[30;43mhandle_known_errors\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43murl\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mresponse_code_to_exception\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mresponse\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mresponse_text\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 174\u001b[39m response_model = parse_response_to_model(response_text, model_class)\n\u001b[32m 176\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m (response_model.status != ResponseStatus.OK) \u001b[38;5;129;01mor\u001b[39;00m (response_model.error \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m):\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/py_313/lib/python3.13/site-packages/x10/utils/http.py:243\u001b[39m, in \u001b[36mhandle_known_errors\u001b[39m\u001b[34m(url, response_code_handler, response, response_text)\u001b[39m\n\u001b[32m 241\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m response.status > \u001b[32m299\u001b[39m:\n\u001b[32m 242\u001b[39m LOGGER.error(\u001b[33m\"\u001b[39m\u001b[33mError response from \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[33m: \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[33m\"\u001b[39m, url, response_text)\n\u001b[32m--> \u001b[39m\u001b[32m243\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mError response from \u001b[39m\u001b[38;5;132;01m{\u001b[39;00murl\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m: code \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse.status\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m - \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse_text\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n",
"\u001b[31mValueError\u001b[39m: Error response from https://api.starknet.extended.exchange/api/v1/user/order: code 400 - {\"status\":\"ERROR\",\"error\":{\"code\":1125,\"message\":\"Invalid price precision\"}}"
]
}
],
"source": [
"placed_order = await trading_client.place_order(\n",
" market_name=ORDER_MARKET,\n",
@@ -70,7 +93,7 @@
" price=ORDER_PRICE,\n",
" side=ORDER_SIDE,\n",
" taker_fee=Decimal(\"0.00025\"),\n",
" previous_order_id='1295034892466447624365619416628580523728221205816494340545831832663414858661'\n",
" # previous_order_id='1295034892466447624365619416628580523728221205816494340545831832663414858661'\n",
")"
]
},
@@ -789,7 +812,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.12"
"version": "3.13.13"
}
},
"nbformat": 4,

328
main.py
View File

@@ -350,11 +350,13 @@ async def run_algo():
logging.info(f'ASTER ORDER PARTIALLY FILLED: {order_id}')
await get_aster_collateral()
await get_aster_notional_position()
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()
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}')
if EXTEND_WS_ORDER_UPDATES is not None:
@@ -379,11 +381,13 @@ async def run_algo():
logging.info(f'EXTEND ORDER PARTIALLY FILLED: {order_id}')
await get_extend_collateral()
await get_extend_notional()
utils.send_tg_alert(f'FR_ALGO - EXTEND PARTIALLY FILLED ({order_id})')
elif order_update_status in ['FILLED']:
logging.info(f'EXTEND ORDER FILLED: {order_id}')
EXTEND_OPEN_ORDERS.pop(idx)
await get_extend_collateral()
await get_extend_notional()
utils.send_tg_alert(f'FR_ALGO - EXTEND FILLED ({order_id})')
else:
logging.critical(f'EXTEND ORDER STATUS CHG TO UNEXPECTED VALUE, KILLING... ({order_id}): {order_orig_status} -> {order_update_status}')
@@ -414,10 +418,18 @@ async def run_algo():
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 == 0.00
if Flags.NET_FUNDING_IS_ZERO:
logging.info('NET FUNDING = 0.00; Cancelling Open Orders; Wait Until Non-Zero.')
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:
ALPHA_TGT_NOTIONAL = 0.00
# if ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS:
# logging.info('NET FUNDING = 0.00; Cancelling Open Orders! then Waiting...')
# aster_cancel_all_orders()
# extend_cancel_all_orders()
# time.sleep(5)
# else:
# logging.info('NET FUNDING = 0.00; NO OPEN ORDERS; Waiting...')
# time.sleep(5)
if ALPHA_EXCH == 'EXTEND':
ASTER_TGT_NOTIONAL = ALPHA_TGT_NOTIONAL*-1
@@ -425,19 +437,38 @@ async def run_algo():
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'])
Alpha_Nominator = ASTER_TOB_PX
Alpha_Denominator = EXTEND_TOB_PX
ALPHA_RATIO = ASTER_TOB_PX / EXTEND_TOB_PX
Expected_Alpha = ( ( ASTER_TOB_PX - EXTEND_TOB_PX ) / (( ASTER_TOB_PX + EXTEND_TOB_PX ) / 2) )
else:
ASTER_TOB_PX = float(ASTER_TICKER_DICT['best_bid_px'])
EXTEND_TOB_PX = float(EXTENDED_TICKER_DICT['best_ask_px'])
Alpha_Nominator = EXTEND_TOB_PX
Alpha_Denominator = ASTER_TOB_PX
ALPHA_RATIO = EXTEND_TOB_PX / ASTER_TOB_PX
Expected_Alpha = ( ( EXTEND_TOB_PX - ASTER_TOB_PX ) / (( ASTER_TOB_PX + EXTEND_TOB_PX ) / 2) )
else:
ASTER_TGT_NOTIONAL = ALPHA_TGT_NOTIONAL
EXTEND_TGT_NOTIONAL = ALPHA_TGT_NOTIONAL*-1
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'])
Alpha_Nominator = EXTEND_TOB_PX
Alpha_Denominator = ASTER_TOB_PX
ALPHA_RATIO = EXTEND_TOB_PX / ASTER_TOB_PX
Expected_Alpha = ( ( EXTEND_TOB_PX - ASTER_TOB_PX ) / (( EXTEND_TOB_PX + ASTER_TOB_PX ) / 2) )
else:
ASTER_TOB_PX = float(ASTER_TICKER_DICT['best_ask_px'])
EXTEND_TOB_PX = float(EXTENDED_TICKER_DICT['best_bid_px'])
Alpha_Nominator = ASTER_TOB_PX
Alpha_Denominator = EXTEND_TOB_PX
ALPHA_RATIO = ASTER_TOB_PX / EXTEND_TOB_PX
Expected_Alpha = ( ( ASTER_TOB_PX - EXTEND_TOB_PX ) / (( ASTER_TOB_PX + EXTEND_TOB_PX ) / 2) )
Expected_Alpha_Net_FR = abs(NEXT_NET_FUNDING_RATE) + Expected_Alpha
Expected_Alpha_Net_FR_w_Taker = Expected_Alpha_Net_FR-0.0002
Expected_Alpha_w_Taker = Expected_Alpha-0.0002
ASTER_TGT_TAIL = ASTER_TGT_NOTIONAL - ( float(ASTER_NOTIONAL_POSITION) + float(ASTER_UNREALIZED_PNL) )
EXTEND_TGT_TAIL = EXTEND_TGT_NOTIONAL - ( float(EXTEND_NOTIONAL_POSITION) + float(EXTEND_UNREALIZED_PNL) )
@@ -455,22 +486,26 @@ async def run_algo():
OUT(f'''
LOOP SLEEP (SEC): {ALGO_CONFIG.Loop_Sleep_Sec}
FLIP SIDES FOR TESTING?: {ALGO_CONFIG.Flip_Side_For_Testing}
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}
{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} ]
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}
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}]
ALPHA SIDE : {ALPHA_EXCH} [{ALPHA_CARRY_SIDE}]
TGT NOTIONAL: $ {ALGO_CONFIG.Max_Target_Notional if not Flags.NET_FUNDING_IS_ZERO else 0.00}
TGT NOTIONAL: $ {ALGO_CONFIG.Max_Target_Notional if not Flags.NET_FUNDING_IS_ZERO else 0.00}; Flatten Open Positions Flag? {ALGO_CONFIG.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}
ASTER: {ASTER_TGT_TAIL_BASE_QTY:.4f} > {MAX_MIN_ORDER_QTY:.4f} min [ Order: {ASTER_TGT_TAIL_ORDERABLE} ] | EXTEND: {EXTEND_TGT_TAIL_BASE_QTY:.4f} > {MAX_MIN_ORDER_QTY:.4f} min [ Order: {EXTEND_TGT_TAIL_ORDERABLE} ]
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}
ASTER: {ASTER_TGT_TAIL_BASE_QTY:.4f} > {MAX_MIN_ORDER_QTY:.4f} min [ Order: {ASTER_TGT_TAIL_ORDERABLE} ] | EXTEND: {EXTEND_TGT_TAIL_BASE_QTY:.4f} > {MAX_MIN_ORDER_QTY:.4f} min [ Order: {EXTEND_TGT_TAIL_ORDERABLE} ]
ALPHA: {ALPHA_RATIO:.8f} ALPHA_RATIO: {Alpha_Nominator:_.2f} / {Alpha_Denominator:_.2f}; Expected_Alpha = {Expected_Alpha:.6f} + FR[{NEXT_NET_FUNDING_RATE:.6f}] = * {Expected_Alpha_Net_FR:.6f} *
FEES : TAKER: {0.02:.2%}; Expected Alpha w Taker = {Expected_Alpha_Net_FR-0.0002:.6f} [w/o FR: {Expected_Alpha_w_Taker:.6f}]
--- ASTER OPEN ORDERS ---
{ASTER_OPEN_ORDERS}
@@ -478,6 +513,7 @@ async def run_algo():
--- EXTEND OPEN ORDERS ---
{EXTEND_OPEN_ORDERS}
''')
# 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:
print_summary(use_logging=True)
if ALGO_CONFIG.Print_Summary_Each_Loop:
@@ -485,150 +521,160 @@ async def run_algo():
# print_summary()
### ROUTES ###
# ASTER
if ASTER_TGT_TAIL_ORDERABLE and ALGO_CONFIG.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
if abs( ( float(ASTER_TGT_TAIL_BASE_QTY)*float(price) ) + ASTER_NOTIONAL_POSITION ) > ALGO_CONFIG.Max_Target_Notional*1.01:
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:
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']
open_order_px = float(ASTER_OPEN_ORDERS[0].get('price')) if ASTER_OPEN_ORDERS[0].get('price') is not None else float(ASTER_OPEN_ORDERS[0]['original_price'])
if round(open_order_px - float(price), 2) == 0.00:
logging.info('ASTER OPEN ORDER NO PX CHG; SKIPPING')
place_order = False
# ALPHA RATIO CHECK
if not( ( Expected_Alpha_w_Taker > 0 ) or ( ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS ) ):
# logging.info(f'Alpha Ratio too low ({ALPHA_RATIO:.8f}) and no Open Orders...')
pass
else:
# logging.info(f'*** Alpha Ratio HIT - LETS ORDER: {ALPHA_RATIO:.8f}')
# ASTER
if ASTER_TGT_TAIL_ORDERABLE and ALGO_CONFIG.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
if abs( ( float(ASTER_TGT_TAIL_BASE_QTY)*float(price) ) + ASTER_NOTIONAL_POSITION ) > ALGO_CONFIG.Max_Target_Notional*1.01:
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:
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']
open_order_px = float(ASTER_OPEN_ORDERS[0].get('price')) if ASTER_OPEN_ORDERS[0].get('price') is not None else float(ASTER_OPEN_ORDERS[0]['original_price'])
if round(open_order_px - float(price), 2) == 0.00:
logging.info('ASTER OPEN ORDER NO PX CHG; SKIPPING')
place_order = False
else:
cancel_order = {
"url": "/fapi/v3/order",
"method": "DELETE",
"params": {
'symbol': ASTER.symbol,
'orderId': open_order_id,
}
}
cr = await aster_auth.post_authenticated_url(cancel_order)
if cr.get('status', None) == 'CANCELED':
ASTER_OPEN_ORDERS.pop(0)
place_order = True
else:
logging.warning(f'ASTER ORDER FAILED TO CANCEL DURING CR ({open_order_id}): RESP {cr}')
place_order = False
else:
cancel_order = {
place_order = True
if ASTER_TGT_TAIL_BASE_QTY == 0.00:
place_order = False
logging.info('ASTER TRYNG TO ORDER 0.00 BASE QTY, SKIPPING')
if place_order:
price = Decimal(str(price)).quantize(Decimal(str(0.01)), rounding=ROUND_DOWN)
post_order = {
"url": "/fapi/v3/order",
"method": "DELETE",
"method": "POST",
"params": {
'symbol': ASTER.symbol,
'orderId': open_order_id,
'symbol': symbol,
'side': side,
'type': 'LIMIT',
'timeInForce': 'GTC',
'quantity': qty,
'price': price,
}
}
cr = await aster_auth.post_authenticated_url(cancel_order)
if cr.get('status', None) == 'CANCELED':
ASTER_OPEN_ORDERS.pop(0)
order_resp = await aster_auth.post_authenticated_url(post_order)
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)
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)
else:
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')
await aster_cancel_all_orders()
# EXTEND
if EXTEND_TGT_TAIL_ORDERABLE and ALGO_CONFIG.Allow_Ordering_Extend:
symbol = EXTEND_TICKER
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:
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:
open_order_dict = dict(EXTEND_OPEN_ORDERS[0])
open_order_id = open_order_dict['external_id']
open_order_px = float(open_order_dict['price'])
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:
logging.warning(f'ASTER ORDER FAILED TO CANCEL DURING CR ({open_order_id}): RESP {cr}')
place_order = False
else:
place_order = True
if ASTER_TGT_TAIL_BASE_QTY == 0.00:
place_order = False
logging.info('ASTER TRYNG TO ORDER 0.00 BASE QTY, SKIPPING')
if place_order:
price = Decimal(str(price)).quantize(Decimal(str(0.01)), rounding=ROUND_DOWN)
post_order = {
"url": "/fapi/v3/order",
"method": "POST",
"params": {
'symbol': symbol,
'side': side,
'type': 'LIMIT',
'timeInForce': 'GTC',
'quantity': qty,
'price': price,
}
}
order_resp = await aster_auth.post_authenticated_url(post_order)
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)
utils.send_tg_alert(f'FR_ALGO - ASTER Order. 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)
else:
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')
await aster_cancel_all_orders()
# EXTEND
if EXTEND_TGT_TAIL_ORDERABLE and ALGO_CONFIG.Allow_Ordering_Extend:
symbol = EXTEND_TICKER
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:
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:
open_order_dict = dict(EXTEND_OPEN_ORDERS[0])
open_order_id = open_order_dict['external_id']
open_order_px = float(open_order_dict['price'])
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:
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
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
place_order = True
if place_order:
price = Decimal(str(price)).quantize(Decimal(str(0.01)), rounding=ROUND_DOWN)
if round(open_order_px - float(price), 2) == 0.00:
logging.info('EXTEND OPEN ORDER NO PX CHG; SKIPPING')
else:
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)
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)
if place_order:
price = Decimal(str(price)).quantize(Decimal(str(0.01)), rounding=ROUND_DOWN)
if round(open_order_px - float(price), 2) == 0.00:
logging.info('EXTEND OPEN ORDER NO PX CHG; SKIPPING')
else:
order_resp_dict.get
else:
logging.warning('EXTEND PLACE ORDER CHECKS FAILED, SKIPPING')
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())
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()
order_resp_dict = dict(order_resp)
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)
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:
order_resp_dict.get
else:
logging.warning('EXTEND PLACE ORDER CHECKS FAILED, SKIPPING')
# print(f'__________ End ___________ (Algo Engine ms: {(time.time() - loop_start)*1000}); Sleeping for sec: {ALGO_CONFIG.Loop_Sleep_Sec}')
time.sleep(ALGO_CONFIG.Loop_Sleep_Sec)
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()
if ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS:
continue
else:
time.sleep(ALGO_CONFIG.Loop_Sleep_Sec)
# logging.info(f'_____ End No Open Orders _____ (Algo Engine ms: {(time.time() - loop_start)*1000:.2f}); Sleeping for sec: {ALGO_CONFIG.Loop_Sleep_Sec:.0f}')
except KeyboardInterrupt:
logging.info('CANCELLING OPEN ORDERS')

View File

@@ -13,12 +13,14 @@ class Algo_Config:
Loop_Sleep_Sec: int
Max_Target_Notional: float
Min_Time_To_Funding_Minutes: int
Min_Fund_Rate_Pct_To_Trade: float
Price_Worsener_Aster: float
Price_Worsener_Extend: float
Target_Open_Cash_Position: int
Log_Summary_Each_Loop: bool = False
Print_Summary_Each_Loop: bool = False
Flatten_Open_Positions: bool = False
Flip_Side_For_Testing: bool = False
@dataclass(kw_only=True)