v1 working switch symbols w volume filter
This commit is contained in:
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/rust/
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,5 +5,5 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
# Rust
|
# Rust
|
||||||
/rust_test/test_world/target/
|
/rust/
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
312
algo.ipynb
312
algo.ipynb
@@ -2,7 +2,7 @@
|
|||||||
"cells": [
|
"cells": [
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 4,
|
"execution_count": 1,
|
||||||
"id": "d1eed397",
|
"id": "d1eed397",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
"from dataclasses import dataclass, asdict\n",
|
"from dataclasses import dataclass, asdict\n",
|
||||||
"import valkey\n",
|
"import valkey\n",
|
||||||
"import modules.utils as utils\n",
|
"import modules.utils as utils\n",
|
||||||
|
"from decimal import Decimal, ROUND_DOWN\n",
|
||||||
"\n",
|
"\n",
|
||||||
"with open('algo_config.json', 'r', encoding='utf-8') as file:\n",
|
"with open('algo_config.json', 'r', encoding='utf-8') as file:\n",
|
||||||
" ALGO_CONFIG = json.load(file)\n",
|
" ALGO_CONFIG = json.load(file)\n",
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 5,
|
"execution_count": 2,
|
||||||
"id": "c6151613",
|
"id": "c6151613",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
@@ -30,7 +31,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 38,
|
"execution_count": null,
|
||||||
"id": "d83c61e5",
|
"id": "d83c61e5",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -40,31 +41,25 @@
|
|||||||
"1"
|
"1"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 38,
|
"execution_count": 7,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"config_update = {\n",
|
"config_update = {\n",
|
||||||
" 'Config': {\n",
|
" # 'Config': {\n",
|
||||||
" # 'Loop_Sleep_Sec': 0.00,\n",
|
" # 'Price_Worsener_Aster': 0,\n",
|
||||||
" # 'Min_Time_To_Funding_Minutes': 60,\n",
|
" # 'Price_Worsener_Extend': -1\n",
|
||||||
" # 'Min_Fund_Rate_Pct_To_Trade': 0.0002,\n",
|
" # },\n",
|
||||||
" # 'Price_Worsener_Extend': 0.0,\n",
|
|
||||||
" # 'Price_Worsener_Aster': 0.0,\n",
|
|
||||||
" # 'Switch_To_Taker_Seconds': 1,\n",
|
|
||||||
" },\n",
|
|
||||||
" 'Logging': {\n",
|
" 'Logging': {\n",
|
||||||
" # 'Log_Summary_Each_Loop': False,\n",
|
" 'Log_Summary_Each_Loop': False,\n",
|
||||||
" 'Print_Summary_Each_Loop': False,\n",
|
" 'Print_Summary_Each_Loop': True,\n",
|
||||||
" },\n",
|
" },\n",
|
||||||
" # 'Overrides': {\n",
|
" # 'Overrides': {\n",
|
||||||
" # # 'Allow_Ordering_Aster': True,\n",
|
" # 'Allow_Ordering_Aster': True,\n",
|
||||||
" # # 'Allow_Ordering_Extend': True,\n",
|
" # 'Allow_Ordering_Extend': True,\n",
|
||||||
" # # 'Allow_Symbol_Change': False,\n",
|
" # 'Allow_Symbol_Change': True,\n",
|
||||||
" # # 'Flip_Side_For_Testing': False,\n",
|
|
||||||
" # # 'Flatten_Open_Positions': False,\n",
|
|
||||||
" # },\n",
|
" # },\n",
|
||||||
"}\n",
|
"}\n",
|
||||||
"VAL_KEY.publish('fr_orchestrator_input', json.dumps(config_update))"
|
"VAL_KEY.publish('fr_orchestrator_input', json.dumps(config_update))"
|
||||||
@@ -94,27 +89,55 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 6,
|
||||||
"id": "940586bb",
|
"id": "940586bb",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
"source": []
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"'{\"stream\":\"btcusdt@bookTicker\",\"data\":{\"event_type\":\"bookTicker\",\"update_id\":458028589204,\"symbol\":\"BTCUSDT\",\"best_bid_price\":\"76414.9\",\"best_bid_qty\":\"0.507\",\"best_ask_price\":\"76415.0\",\"best_ask_qty\":\"0.878\",\"transaction_time\":1777565596460,\"event_time\":1777565596488}}'"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 6,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"VAL_KEY.get('test_key')"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 13,
|
||||||
"id": "cd600e0e",
|
"id": "cd600e0e",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": []
|
"source": [
|
||||||
|
"from decimal import Decimal"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 16,
|
||||||
"id": "db52edf9",
|
"id": "db52edf9",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
"source": []
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 16,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"int(Decimal(0.56))"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
@@ -146,17 +169,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 57,
|
"execution_count": 18,
|
||||||
"id": "5f7535df",
|
"id": "5f7535df",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"'{\"ASTER\": {\"mult\": 150, \"lh_asset\": \"ETH\", \"rh_asset\": \"USD\", \"symbol_asset_separator\": \"\"}, \"EXTEND\": {\"mult\": 50, \"lh_asset\": \"ETH\", \"rh_asset\": \"USD\", \"symbol_asset_separator\": \"-\"}}'"
|
"'{\"ASTER\": {\"lh_asset\": \"ZEC\", \"rh_asset\": \"USDT\", \"symbol_asset_separator\": \"\", \"mult\": 75, \"initial_funding_rate\": -8.836e-05, \"min_price\": 0.01, \"min_order_size\": 0.01}, \"EXTEND\": {\"lh_asset\": \"ZEC\", \"rh_asset\": \"USD\", \"symbol_asset_separator\": \"-\", \"mult\": 10, \"initial_funding_rate\": 1.3e-05, \"min_price\": 0.01, \"min_order_size\": 0.01}}'"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 57,
|
"execution_count": 18,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@@ -188,29 +211,50 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 52,
|
"execution_count": 3,
|
||||||
"id": "b417adad",
|
"id": "b417adad",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_algo_working_symbol')) # ty:ignore[invalid-argument-type]\n",
|
"best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_engine_best_fund_rate_output')) # ty:ignore[invalid-argument-type]\n",
|
||||||
"best_symbol_by_exchange_aster = structs.Perpetual_Exchange(**asdict(ASTER))\n",
|
"best_symbol_by_exchange_aster = structs.Perpetual_Exchange(**best_symbol_by_exchange['ASTER'])\n",
|
||||||
"best_symbol_by_exchange_extend = structs.Perpetual_Exchange(**asdict(EXTEND))"
|
"best_symbol_by_exchange_extend = structs.Perpetual_Exchange(**best_symbol_by_exchange['EXTEND'])"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 53,
|
"execution_count": 4,
|
||||||
|
"id": "ba98754e",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Perpetual_Exchange(lh_asset='ZEC', rh_asset='USDT', symbol_asset_separator='', mult=75, initial_funding_rate=-8.836e-05, min_price=0.01, min_order_size=0.001)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 4,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"best_symbol_by_exchange_aster"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 5,
|
||||||
"id": "fa5a8e85",
|
"id": "fa5a8e85",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"Perpetual_Exchange(mult=50, lh_asset='ETH', rh_asset='USD', symbol_asset_separator='-')"
|
"Perpetual_Exchange(lh_asset='ZEC', rh_asset='USD', symbol_asset_separator='-', mult=10, initial_funding_rate=1.3e-05, min_price=0.001, min_order_size=0.1)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 53,
|
"execution_count": 5,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@@ -221,30 +265,203 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 54,
|
"execution_count": null,
|
||||||
"id": "fb81441a",
|
"id": "09571e38",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"True"
|
"Decimal('1.0')"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 54,
|
"execution_count": 13,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
|
"Decimal('1.0').quantize()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 16,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"1.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 16,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"x = 1.0\n",
|
||||||
|
"x"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 8,
|
||||||
|
"id": "70b57870",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"f = ['b','a','z','e']"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 9,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"['b', 'a', 'z', 'e']"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 9,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"f"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 10,
|
||||||
|
"id": "9fd60c6e",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"f.sort()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 11,
|
||||||
|
"id": "cd8b41de",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"['a', 'b', 'e', 'z']"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 11,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"f"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 46,
|
||||||
|
"id": "4c566e14",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"price = float(0.9066)\n",
|
||||||
|
"min_price = float(0.0001)\n",
|
||||||
"\n",
|
"\n",
|
||||||
"VAL_KEY.set('fr_algo_working_symbol', json.dumps({'ASTER': asdict(best_symbol_by_exchange_aster), 'EXTEND': asdict(best_symbol_by_exchange_extend)}))"
|
"min_price = int(min_price) if min_price == int(min_price) else min_price\n",
|
||||||
|
"price: Decimal = Decimal(str(price)).quantize(Decimal(str(min_price)), rounding=ROUND_DOWN)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 49,
|
||||||
|
"id": "ad6444a5",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"6"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 49,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"len(str(price))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 50,
|
||||||
|
"id": "74a227cc",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Decimal('0.9066')"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 50,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"price"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
"id": "09571e38",
|
"id": "57fac02c",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "2331e29f",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 33,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Decimal('0.000100000000000000004792173602385929598312941379845142364501953125')"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 33,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"Decimal(min_price)\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "1c139413",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": []
|
"source": []
|
||||||
@@ -259,6 +476,15 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
|
"id": "a938b2e0",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "39667bd8",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": []
|
"source": []
|
||||||
|
|||||||
1
algo/.dockerignore
Normal file
1
algo/.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
../rust/
|
||||||
@@ -1,25 +1,26 @@
|
|||||||
{
|
{
|
||||||
"Updated_Timestamp": 1777496051056,
|
"Updated_Timestamp": 1777667398908,
|
||||||
"Config": {
|
"Config": {
|
||||||
"Loop_Sleep_Sec": 0.0,
|
"Loop_Sleep_Sec": 0.0,
|
||||||
"Max_Order_Over_Notional_Ratio": 1.05,
|
"Max_Order_Over_Notional_Ratio": 1.05,
|
||||||
"Max_Target_Notional": 0.0,
|
"Max_Target_Notional": 0.0,
|
||||||
"Min_Time_To_Funding_Minutes": 60,
|
"Min_Time_To_Funding_Minutes": 60,
|
||||||
"Min_Fund_Rate_Pct_To_Trade": 0.0,
|
"Min_Fund_Rate_Pct_To_Trade": 0.0,
|
||||||
"Price_Worsener_Aster": 0.0,
|
"Price_Worsener_Aster": 0,
|
||||||
"Price_Worsener_Extend": -0.1,
|
"Price_Worsener_Extend": -1,
|
||||||
"Switch_To_Taker_Seconds": 1,
|
"Switch_To_Taker_Seconds": 3,
|
||||||
"Target_Open_Cash_Position": 10
|
"Target_Open_Cash_Position": 10
|
||||||
},
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"Log_Summary_Each_Loop": false,
|
"Log_Summary_Each_Loop": false,
|
||||||
"Print_Summary_Each_Loop": false
|
"Print_Summary_Each_Loop": true
|
||||||
},
|
},
|
||||||
"Overrides": {
|
"Overrides": {
|
||||||
"Allow_Ordering_Aster": true,
|
"Allow_Ordering_Aster": true,
|
||||||
"Allow_Ordering_Extend": true,
|
"Allow_Ordering_Extend": true,
|
||||||
"Allow_Symbol_Change": false,
|
"Allow_Symbol_Change": true,
|
||||||
"Flatten_Open_Positions": false,
|
"Flatten_Open_Positions": false,
|
||||||
|
"Flatten_Open_Positions_Opportunistic": false,
|
||||||
"Flip_Side_For_Testing": false
|
"Flip_Side_For_Testing": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,8 +18,8 @@ TO DO:
|
|||||||
VK_IN: str = 'fr_orchestrator_input'
|
VK_IN: str = 'fr_orchestrator_input'
|
||||||
VK_OUT: str = 'fr_orchestrator_output'
|
VK_OUT: str = 'fr_orchestrator_output'
|
||||||
|
|
||||||
# CONFIG_FILEPATH: str = '/algo_local_drive/algo_config.json'
|
CONFIG_FILEPATH: str = '/algo_local_drive/algo_config.json'
|
||||||
CONFIG_FILEPATH: str = 'algo_config.json'
|
# CONFIG_FILEPATH: str = 'algo_config.json'
|
||||||
|
|
||||||
### Logging ###
|
### Logging ###
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|||||||
1
algo_orchestrator/.dockerignore
Normal file
1
algo_orchestrator/.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
../rust/
|
||||||
33209
aster.ipynb
33209
aster.ipynb
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
# tail -f Fund_Rate_Algo_Orchestrator.log Fund_Rate_Algo.log Fund_Rate_Aster_User.log Fund_Rate_Aster.log Fund_Rate_Extended_FR.log Fund_Rate_Extended_OB.log Fund_Rate_Extended_Trades.log Fund_Rate_Extended_User.log
|
# tail -f Fund_Rate_Aster_FR_ALL.log Fund_Rate_Extended_FR_ALL.log Fund_Rate_Engine_BFR.log Fund_Rate_Algo_Orchestrator.log Fund_Rate_Algo.log Fund_Rate_Aster_User.log Fund_Rate_Aster.log Fund_Rate_Extended_FR.log Fund_Rate_Extended_OB.log Fund_Rate_Extended_User.log
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# algo:
|
# algo:
|
||||||
@@ -30,6 +30,36 @@ services:
|
|||||||
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
|
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
|
||||||
- ./:/algo_local_drive:rw # Read-write access to data
|
- ./:/algo_local_drive:rw # Read-write access to data
|
||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
|
engine_best_funding_rate:
|
||||||
|
container_name: engine_best_funding_rate
|
||||||
|
restart: "unless-stopped"
|
||||||
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: ./engine_best_funding_rate/Dockerfile
|
||||||
|
volumes:
|
||||||
|
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data
|
||||||
|
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
|
||||||
|
network_mode: "host"
|
||||||
|
ws_extended_fund_rate_all:
|
||||||
|
container_name: ws_extended_fund_rate_all
|
||||||
|
restart: "unless-stopped"
|
||||||
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: ./ws_extended_fund_rate_all/Dockerfile
|
||||||
|
volumes:
|
||||||
|
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data
|
||||||
|
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
|
||||||
|
network_mode: "host"
|
||||||
|
ws_aster_fund_rate_all:
|
||||||
|
container_name: ws_aster_fund_rate_all
|
||||||
|
restart: "unless-stopped"
|
||||||
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: ./ws_aster_fund_rate_all/Dockerfile
|
||||||
|
volumes:
|
||||||
|
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data
|
||||||
|
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
|
||||||
|
network_mode: "host"
|
||||||
ws_aster:
|
ws_aster:
|
||||||
container_name: ws_aster
|
container_name: ws_aster
|
||||||
restart: "unless-stopped"
|
restart: "unless-stopped"
|
||||||
@@ -70,16 +100,16 @@ services:
|
|||||||
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to dataw
|
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to dataw
|
||||||
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
|
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
|
||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
ws_extended_trades:
|
# ws_extended_trades:
|
||||||
container_name: ws_extended_trades
|
# container_name: ws_extended_trades
|
||||||
restart: "unless-stopped"
|
# restart: "unless-stopped"
|
||||||
build:
|
# build:
|
||||||
context: ./
|
# context: ./
|
||||||
dockerfile: ./ws_extended_trades/Dockerfile
|
# dockerfile: ./ws_extended_trades/Dockerfile
|
||||||
volumes:
|
# volumes:
|
||||||
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to dataw
|
# - /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to dataw
|
||||||
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
|
# - /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
|
||||||
network_mode: "host"
|
# network_mode: "host"
|
||||||
ws_extended_user:
|
ws_extended_user:
|
||||||
container_name: ws_extended_user
|
container_name: ws_extended_user
|
||||||
restart: "unless-stopped"
|
restart: "unless-stopped"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@ import requests
|
|||||||
import valkey
|
import valkey
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
import modules.manual_leverage as leverage
|
import modules.manual_leverage as leverage
|
||||||
|
import modules.aster_auth as aster_auth
|
||||||
|
|
||||||
### MANUAL LEVERAGE DATA ###
|
### MANUAL LEVERAGE DATA ###
|
||||||
df_leverage_by_exch = pd.DataFrame(data=leverage.LEVERAGE_BY_EXCH)
|
df_leverage_by_exch = pd.DataFrame(data=leverage.LEVERAGE_BY_EXCH)
|
||||||
@@ -32,35 +33,61 @@ REFRESH_MKT_INFO_EVERY_SEC: int = 90
|
|||||||
REFRESH_MKT_VOLUME_EVERY_SEC: int = 30
|
REFRESH_MKT_VOLUME_EVERY_SEC: int = 30
|
||||||
|
|
||||||
### GLOBALS ###
|
### GLOBALS ###
|
||||||
Mkt_Info_Last_Refresh_TS_ms: int
|
Mkt_Info_Last_Refresh_TS_ms: int = 0
|
||||||
Mkt_Volume_Last_Refresh_TS_ms: int
|
Mkt_Volume_Last_Refresh_TS_ms: int = 0
|
||||||
|
|
||||||
### Funcs - Load Data ###
|
### Funcs - Load Data ###
|
||||||
def get_extended_markets_info() -> pd.DataFrame:
|
async def get_extended_markets_info() -> pd.DataFrame:
|
||||||
global Mkt_Info_Last_Refresh_TS_ms
|
|
||||||
|
|
||||||
r: dict = json.loads(s=requests.get(url='https://api.starknet.extended.exchange/api/v1/info/markets').text)
|
r: dict = json.loads(s=requests.get(url='https://api.starknet.extended.exchange/api/v1/info/markets').text)
|
||||||
|
|
||||||
df: pd.DataFrame = pd.DataFrame(data=r['data'])
|
df: pd.DataFrame = pd.DataFrame(data=r['data'])
|
||||||
df['funding_rate'] = df['marketStats'].apply(lambda x: x.get('fundingRate',{}))
|
df['funding_rate'] = df['marketStats'].apply(lambda x: x.get('fundingRate',{}))
|
||||||
df['funding_rate_ts'] = df['marketStats'].apply(lambda x: x.get('nextFundingRate',{}))
|
df['funding_rate_ts'] = df['marketStats'].apply(lambda x: x.get('nextFundingRate',{}))
|
||||||
|
df['daily_volume'] = df['marketStats'].apply(lambda x: x.get('dailyVolume',{})).astype(float)
|
||||||
df['min_order_size'] = df['tradingConfig'].apply(lambda x: x.get('minOrderSize',{}))
|
df['min_order_size'] = df['tradingConfig'].apply(lambda x: x.get('minOrderSize',{}))
|
||||||
df['min_price_change'] = df['tradingConfig'].apply(lambda x: x.get('minPriceChange',{}))
|
df['min_price'] = df['tradingConfig'].apply(lambda x: x.get('minPriceChange',{}))
|
||||||
df['max_leverage'] = df['tradingConfig'].apply(lambda x: x.get('maxLeverage',{}))
|
df['max_leverage'] = df['tradingConfig'].apply(lambda x: x.get('maxLeverage',{}))
|
||||||
|
|
||||||
Mkt_Info_Last_Refresh_TS_ms = round(datetime.now().timestamp() * 1000)
|
|
||||||
|
|
||||||
print('Extend markets info refreshed successfully')
|
print('Extend markets info refreshed successfully')
|
||||||
|
|
||||||
return df
|
return df
|
||||||
|
|
||||||
def load_aster_current_fr() -> pd.DataFrame:
|
async def get_aster_exch_info() -> pd.DataFrame:
|
||||||
|
### ASTER EXCHANGE INFO ###
|
||||||
|
fut_acct_exchangeInfo: dict = {
|
||||||
|
"url": "/fapi/v3/exchangeInfo",
|
||||||
|
"method": "GET",
|
||||||
|
"params": {}
|
||||||
|
}
|
||||||
|
r: dict = await aster_auth.post_authenticated_url(fut_acct_exchangeInfo) # ty:ignore[invalid-assignment]
|
||||||
|
df = pd.DataFrame(r['symbols'])
|
||||||
|
df['min_order_size'] = df['filters'].apply(lambda x: [f for f in x if f.get('filterType', None) == 'LOT_SIZE'][0]['minQty'] )
|
||||||
|
df['min_price'] = df['filters'].apply(lambda x: [f for f in x if f.get('filterType', None) == 'PRICE_FILTER'][0]['minPrice'] )
|
||||||
|
|
||||||
|
fut_acct_ticker_stats: dict = {
|
||||||
|
"url": "/fapi/v3/ticker/24hr",
|
||||||
|
"method": "GET",
|
||||||
|
"params": {}
|
||||||
|
}
|
||||||
|
r: dict = await aster_auth.post_authenticated_url(fut_acct_ticker_stats) # ty:ignore[invalid-assignment]
|
||||||
|
df_stats = pd.DataFrame(r)
|
||||||
|
|
||||||
|
df = df.merge(df_stats[['symbol','quoteVolume']].rename({'quoteVolume':'daily_volume'}, axis=1), on='symbol', how='left')
|
||||||
|
df['daily_volume'] = df['daily_volume'].astype(float)
|
||||||
|
|
||||||
|
|
||||||
|
print('Aster markets info refreshed successfully')
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
|
def load_aster_current_fr(df_aster_exch_info: pd.DataFrame) -> pd.DataFrame:
|
||||||
df = pd.DataFrame(data=json.loads(s=VAL_KEY.get(name='fund_rate_aster_all'))) # ty:ignore[invalid-argument-type]
|
df = pd.DataFrame(data=json.loads(s=VAL_KEY.get(name='fund_rate_aster_all'))) # ty:ignore[invalid-argument-type]
|
||||||
|
|
||||||
df: pd.DataFrame = df[['s','E','r','T']].rename({'s':'symbol','E':'funding_rate_updated_ts_ms','r':'funding_rate','T':'next_funding_ts'}, axis=1)
|
df: pd.DataFrame = df[['s','E','r','T']].rename({'s':'symbol','E':'funding_rate_updated_ts_ms','r':'funding_rate','T':'next_funding_ts'}, axis=1)
|
||||||
df['funding_rate_updated_dt'] = pd.to_datetime(df['funding_rate_updated_ts_ms'], unit='ms')
|
df['funding_rate_updated_dt'] = pd.to_datetime(df['funding_rate_updated_ts_ms'], unit='ms')
|
||||||
df['funding_rate'] = df['funding_rate'].astype(float)
|
df['funding_rate'] = df['funding_rate'].astype(float)
|
||||||
df['time_delta_to_next_funding'] = pd.to_datetime(df['next_funding_ts'], unit='ms') - pd.Timestamp.now()
|
df['time_delta_to_next_funding'] = pd.to_datetime(df['next_funding_ts'], unit='ms') - pd.Timestamp.now()
|
||||||
|
df = df.merge(df_aster_exch_info[['symbol','daily_volume','min_order_size','min_price']], on='symbol', how='left')
|
||||||
|
|
||||||
return df
|
return df
|
||||||
|
|
||||||
@@ -71,7 +98,7 @@ def load_extend_current_fr(df_mkt_stats: pd.DataFrame) -> pd.DataFrame:
|
|||||||
df['funding_rate_updated_dt'] = pd.to_datetime(df['funding_rate_updated_ts_ms'], unit='ms')
|
df['funding_rate_updated_dt'] = pd.to_datetime(df['funding_rate_updated_ts_ms'], unit='ms')
|
||||||
df['funding_rate'] = df['funding_rate'].astype(float)
|
df['funding_rate'] = df['funding_rate'].astype(float)
|
||||||
|
|
||||||
df: pd.DataFrame = df.merge(df_mkt_stats[['name','assetName','status', 'funding_rate_ts']].rename({'name':'symbol','funding_rate_ts':'next_funding_ts'}, axis=1), on='symbol', how='left')
|
df: pd.DataFrame = df.merge(df_mkt_stats[['name','assetName','status','funding_rate_ts','daily_volume','min_order_size','min_price']].rename({'name':'symbol','funding_rate_ts':'next_funding_ts'}, axis=1), on='symbol', how='left')
|
||||||
df: pd.DataFrame = df.loc[df['status']=='ACTIVE',:]
|
df: pd.DataFrame = df.loc[df['status']=='ACTIVE',:]
|
||||||
df['USDT_Symbol'] = df['assetName'] + 'USDT'
|
df['USDT_Symbol'] = df['assetName'] + 'USDT'
|
||||||
|
|
||||||
@@ -81,15 +108,15 @@ def load_extend_current_fr(df_mkt_stats: pd.DataFrame) -> pd.DataFrame:
|
|||||||
|
|
||||||
async def loop() -> None:
|
async def loop() -> None:
|
||||||
global Mkt_Info_Last_Refresh_TS_ms
|
global Mkt_Info_Last_Refresh_TS_ms
|
||||||
|
|
||||||
df_extend_mkt_stats = get_extended_markets_info()
|
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
ts_arrival = round(datetime.now().timestamp() * 1000)
|
ts_arrival = round(datetime.now().timestamp() * 1000)
|
||||||
if ( ts_arrival - Mkt_Info_Last_Refresh_TS_ms ) > ( REFRESH_MKT_INFO_EVERY_SEC * 1000 ):
|
if ( ts_arrival - Mkt_Info_Last_Refresh_TS_ms ) > ( REFRESH_MKT_INFO_EVERY_SEC * 1000 ):
|
||||||
df_extend_mkt_stats = get_extended_markets_info()
|
df_extend_mkt_stats = await get_extended_markets_info()
|
||||||
|
df_aster_exch_info = await get_aster_exch_info()
|
||||||
|
Mkt_Info_Last_Refresh_TS_ms = round(datetime.now().timestamp() * 1000)
|
||||||
|
|
||||||
df_aster_fr = load_aster_current_fr()
|
df_aster_fr = load_aster_current_fr(df_aster_exch_info=df_aster_exch_info)
|
||||||
df_extend_fr = load_extend_current_fr(df_mkt_stats=df_extend_mkt_stats)
|
df_extend_fr = load_extend_current_fr(df_mkt_stats=df_extend_mkt_stats)
|
||||||
|
|
||||||
df_comb_fr = df_extend_fr.merge(df_aster_fr, left_on='USDT_Symbol', right_on='symbol', how='inner', suffixes=('_ext', '_ast'))
|
df_comb_fr = df_extend_fr.merge(df_aster_fr, left_on='USDT_Symbol', right_on='symbol', how='inner', suffixes=('_ext', '_ast'))
|
||||||
@@ -103,26 +130,35 @@ async def loop() -> None:
|
|||||||
df_comb_fr['net_mult'] = df_comb_fr['net_mult'].round(2)
|
df_comb_fr['net_mult'] = df_comb_fr['net_mult'].round(2)
|
||||||
df_comb_fr['net_mult_x_net_fr_abs'] = df_comb_fr['net_funding_rate_abs'] * df_comb_fr['net_mult']
|
df_comb_fr['net_mult_x_net_fr_abs'] = df_comb_fr['net_funding_rate_abs'] * df_comb_fr['net_mult']
|
||||||
|
|
||||||
df_best_fr_rate: pd.DataFrame = df_comb_fr[['symbol_ext','symbol_ast','max_leverage_ext','max_leverage_ast','lh_asset_ext','lh_asset_ast','rh_asset_ext','rh_asset_ast','net_mult_x_net_fr_abs','net_funding_rate_abs','net_funding_rate','next_funding_at_same_time']].sort_values(by='net_mult_x_net_fr_abs', ascending=False).reset_index(drop=True)
|
|
||||||
|
df_best_fr_rate: pd.DataFrame = df_comb_fr[['symbol_ext','symbol_ast','daily_volume_ext','daily_volume_ast','funding_rate_ext','funding_rate_ast','min_price_ext','min_price_ast','min_order_size_ext','min_order_size_ast','max_leverage_ext','max_leverage_ast','lh_asset_ext','lh_asset_ast','rh_asset_ext','rh_asset_ast','net_mult_x_net_fr_abs','net_funding_rate_abs','net_funding_rate','next_funding_at_same_time']].sort_values(by='net_mult_x_net_fr_abs', ascending=False).reset_index(drop=True)
|
||||||
|
|
||||||
|
min_daily_volume = 100_000
|
||||||
|
df_best_fr_rate = df_best_fr_rate.loc[ (df_best_fr_rate['daily_volume_ast']>=min_daily_volume) & (df_best_fr_rate['daily_volume_ext']>min_daily_volume) ,:].reset_index(drop=True)
|
||||||
|
|
||||||
ASTER = structs.Perpetual_Exchange(
|
ASTER = structs.Perpetual_Exchange(
|
||||||
mult = int(df_best_fr_rate['max_leverage_ast'][0]),
|
mult = int(df_best_fr_rate['max_leverage_ast'][0]),
|
||||||
lh_asset = df_best_fr_rate['lh_asset_ast'][0],
|
lh_asset = df_best_fr_rate['lh_asset_ast'][0],
|
||||||
rh_asset = df_best_fr_rate['rh_asset_ast'][0],
|
rh_asset = df_best_fr_rate['rh_asset_ast'][0],
|
||||||
symbol_asset_separator = '',
|
symbol_asset_separator = '',
|
||||||
|
initial_funding_rate=float(df_best_fr_rate['funding_rate_ast'][0]),
|
||||||
|
min_price=float(df_best_fr_rate['min_price_ast'][0]),
|
||||||
|
min_order_size=float(df_best_fr_rate['min_order_size_ast'][0]),
|
||||||
)
|
)
|
||||||
|
|
||||||
EXTEND = structs.Perpetual_Exchange(
|
EXTEND = structs.Perpetual_Exchange(
|
||||||
mult = int(df_best_fr_rate['max_leverage_ext'][0]),
|
mult = int(df_best_fr_rate['max_leverage_ext'][0]),
|
||||||
lh_asset = df_best_fr_rate['lh_asset_ext'][0],
|
lh_asset = df_best_fr_rate['lh_asset_ext'][0],
|
||||||
rh_asset = df_best_fr_rate['rh_asset_ext'][0],
|
rh_asset = df_best_fr_rate['rh_asset_ext'][0],
|
||||||
symbol_asset_separator = '-',
|
symbol_asset_separator = '-',
|
||||||
|
initial_funding_rate=float(df_best_fr_rate['funding_rate_ext'][0]),
|
||||||
|
min_price=float(df_best_fr_rate['min_price_ext'][0]),
|
||||||
|
min_order_size=float(df_best_fr_rate['min_order_size_ext'][0]),
|
||||||
)
|
)
|
||||||
|
|
||||||
best_next_funding_pair: dict[str, dict] = {'ASTER': asdict(obj=ASTER), 'EXTEND': asdict(obj=EXTEND)}
|
best_next_funding_pair: dict[str, dict] = {'ASTER': asdict(obj=ASTER), 'EXTEND': asdict(obj=EXTEND)}
|
||||||
|
|
||||||
VAL_KEY.set(name=VK_OUT, value=json.dumps(obj=best_next_funding_pair))
|
VAL_KEY.set(name=VK_OUT, value=json.dumps(obj=best_next_funding_pair))
|
||||||
# print(best_next_funding_pair)
|
print(df_best_fr_rate[['symbol_ext','max_leverage_ext','funding_rate_ast','funding_rate_ext','net_funding_rate','daily_volume_ast']].head(10))
|
||||||
time.sleep(LOOP_SLEEP_SEC)
|
time.sleep(LOOP_SLEEP_SEC)
|
||||||
continue
|
continue
|
||||||
except valkey.exceptions.ConnectionError as e:
|
except valkey.exceptions.ConnectionError as e:
|
||||||
|
|||||||
1
engine_best_funding_rate/.dockerignore
Normal file
1
engine_best_funding_rate/.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
../rust/
|
||||||
19
engine_best_funding_rate/Dockerfile
Normal file
19
engine_best_funding_rate/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
FROM python:3.13-slim
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y build-essential
|
||||||
|
|
||||||
|
RUN gcc --version
|
||||||
|
RUN rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Finally, run gunicorn.
|
||||||
|
CMD [ "python", "engine_best_funding_rate.py"]
|
||||||
|
# CMD [ "gunicorn", "--workers=5", "--threads=1", "-b 0.0.0.0:8000", "app:server"]
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 98,
|
"execution_count": null,
|
||||||
"id": "cd9ecfde",
|
"id": "cd9ecfde",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
626
extended.ipynb
626
extended.ipynb
File diff suppressed because one or more lines are too long
407
main.py
407
main.py
@@ -1,3 +1,4 @@
|
|||||||
|
from orjson import JSONDecodeError
|
||||||
from x10.utils.http import WrappedApiResponse
|
from x10.utils.http import WrappedApiResponse
|
||||||
from x10.perpetual.trading_client.trading_client import PerpetualTradingClient
|
from x10.perpetual.trading_client.trading_client import PerpetualTradingClient
|
||||||
import asyncio
|
import asyncio
|
||||||
@@ -8,7 +9,7 @@ import os
|
|||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from decimal import ROUND_DOWN, Decimal
|
from decimal import ROUND_DOWN, ROUND_HALF_UP, Decimal
|
||||||
from typing import AsyncContextManager
|
from typing import AsyncContextManager
|
||||||
from dataclasses import dataclass, asdict
|
from dataclasses import dataclass, asdict
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@@ -42,29 +43,14 @@ ALGO_CONFIG: structs.Algo_Config
|
|||||||
MIN_TIME_TO_FUNDING: int
|
MIN_TIME_TO_FUNDING: int
|
||||||
|
|
||||||
### EXCHANGES ###
|
### EXCHANGES ###
|
||||||
ASTER = structs.Perpetual_Exchange(
|
ASTER: structs.Perpetual_Exchange
|
||||||
mult = 150,
|
EXTEND: structs.Perpetual_Exchange
|
||||||
lh_asset = 'ETH',
|
|
||||||
rh_asset = 'USDT',
|
|
||||||
symbol_asset_separator = '',
|
|
||||||
)
|
|
||||||
EXTEND = structs.Perpetual_Exchange(
|
|
||||||
mult = 50,
|
|
||||||
lh_asset = 'ETH',
|
|
||||||
rh_asset = 'USD',
|
|
||||||
symbol_asset_separator = '-',
|
|
||||||
)
|
|
||||||
|
|
||||||
### GLOBALS ###
|
### GLOBALS ###
|
||||||
|
Open_Symbols: list[str] = []
|
||||||
Last_Aster_Fill_Time_Ts: float = 0.00
|
Last_Aster_Fill_Time_Ts: float = 0.00
|
||||||
Just_Rejected_Or_Expired: bool = False
|
Just_Rejected_Or_Expired: bool = False
|
||||||
|
|
||||||
# ASTER_MULT = 150
|
|
||||||
# EXTEND_MULT = 50
|
|
||||||
|
|
||||||
ASTER_MIN_ORDER_QTY = 0.001
|
|
||||||
EXTEND_MIN_ORDER_QTY = 0.01
|
|
||||||
|
|
||||||
ASTER_AVAIL_COLLATERAL = 0
|
ASTER_AVAIL_COLLATERAL = 0
|
||||||
EXTEND_AVAIL_COLLATERAL = 0
|
EXTEND_AVAIL_COLLATERAL = 0
|
||||||
|
|
||||||
@@ -89,11 +75,11 @@ EXTEND_OPEN_ORDERS = []
|
|||||||
Flags = structs.Flags()
|
Flags = structs.Flags()
|
||||||
|
|
||||||
### UTILS ###
|
### UTILS ###
|
||||||
def round_decimal_down(value, decimal_places):
|
# def round_decimal_down(value, decimal_places):
|
||||||
# Construct precision string like '0.01' for 2 places
|
# # Construct precision string like '0.01' for 2 places
|
||||||
fmt = f'0.{"0" * decimal_places}' if decimal_places > 0 else '0'
|
# fmt = f'0.{"0" * decimal_places}' if decimal_places > 0 else '0'
|
||||||
precision = Decimal(fmt)
|
# precision = Decimal(fmt)
|
||||||
return Decimal(str(value)).quantize(precision, rounding=ROUND_DOWN)
|
# return Decimal(str(value)).quantize(precision, rounding=ROUND_HALF_UP)
|
||||||
|
|
||||||
### OPEN ORDERS ###
|
### OPEN ORDERS ###
|
||||||
async def get_aster_open_orders():
|
async def get_aster_open_orders():
|
||||||
@@ -112,16 +98,28 @@ async def get_extend_open_orders():
|
|||||||
EXTEND_OPEN_ORDERS = list(dict(await EXTEND_CLIENT.account.get_open_orders()).get('data', 0))
|
EXTEND_OPEN_ORDERS = list(dict(await EXTEND_CLIENT.account.get_open_orders()).get('data', 0))
|
||||||
|
|
||||||
### WALLLET ###
|
### WALLLET ###
|
||||||
async def get_aster_collateral():
|
# async def get_aster_collateral():
|
||||||
global ASTER_AVAIL_COLLATERAL
|
# global ASTER_AVAIL_COLLATERAL
|
||||||
|
|
||||||
fut_acct_balances = {
|
# fut_acct_balances = {
|
||||||
"url": "/fapi/v3/balance",
|
# "url": "/fapi/v3/balance",
|
||||||
"method": "GET",
|
# "method": "GET",
|
||||||
"params": {}
|
# "params": {}
|
||||||
}
|
# }
|
||||||
r = await aster_auth.post_authenticated_url(fut_acct_balances)
|
# r = await aster_auth.post_authenticated_url(fut_acct_balances)
|
||||||
ASTER_AVAIL_COLLATERAL = float([d for d in r if d.get('asset')==ASTER.rh_asset][0].get('availableBalance'))
|
# ASTER_AVAIL_COLLATERAL = float([d for d in r if d.get('asset')==ASTER.rh_asset][0].get('availableBalance'))
|
||||||
|
|
||||||
|
async def get_aster_account_open_symbols() -> list[str]:
|
||||||
|
fut_acct_positionRisk: dict = {
|
||||||
|
"url": "/fapi/v3/positionRisk",
|
||||||
|
"method": "GET",
|
||||||
|
"params": {
|
||||||
|
'symbol': ASTER.symbol,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resp: list = await aster_auth.post_authenticated_url(req=fut_acct_positionRisk) # ty:ignore[invalid-assignment]
|
||||||
|
ld = [ utils.symbol_to_extend_fmt(x['symbol']) for x in resp if abs(float(x.get('positionAmt', 0))) > 0]
|
||||||
|
return ld
|
||||||
|
|
||||||
async def get_aster_notional_position(resp: list | None = None):
|
async def get_aster_notional_position(resp: list | None = None):
|
||||||
global ASTER_NOTIONAL_OBJ
|
global ASTER_NOTIONAL_OBJ
|
||||||
@@ -130,8 +128,13 @@ async def get_aster_notional_position(resp: list | None = None):
|
|||||||
global ASTER
|
global ASTER
|
||||||
|
|
||||||
previous_notional_obj = ASTER_NOTIONAL_OBJ
|
previous_notional_obj = ASTER_NOTIONAL_OBJ
|
||||||
|
previous_notional_position = ASTER_NOTIONAL_POSITION
|
||||||
|
|
||||||
if not resp:
|
if resp:
|
||||||
|
d = [x for x in resp if x.get('symbol', None) == ASTER.symbol]
|
||||||
|
d = d[0] if d else {}
|
||||||
|
|
||||||
|
if ( not resp ) or ( not d ):
|
||||||
fut_acct_positionRisk: dict = {
|
fut_acct_positionRisk: dict = {
|
||||||
"url": "/fapi/v3/positionRisk",
|
"url": "/fapi/v3/positionRisk",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
@@ -139,11 +142,14 @@ async def get_aster_notional_position(resp: list | None = None):
|
|||||||
'symbol': ASTER.symbol,
|
'symbol': ASTER.symbol,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resp: list = await aster_auth.post_authenticated_url(req=fut_acct_positionRisk) # ty:ignore[invalid-assignment]
|
try:
|
||||||
|
resp: list = await aster_auth.post_authenticated_url(req=fut_acct_positionRisk) # ty:ignore[invalid-assignment]
|
||||||
|
except JSONDecodeError as e:
|
||||||
|
logging.warning(f'JSONDecodeError trying to get Aster notional: {e}; resp: {resp}')
|
||||||
|
time.sleep(0.1)
|
||||||
|
resp: list = await aster_auth.post_authenticated_url(req=fut_acct_positionRisk) # ty:ignore[invalid-assignment]
|
||||||
d = [x for x in resp if x.get('symbol', None) == ASTER.symbol][0]
|
d = [x for x in resp if x.get('symbol', None) == ASTER.symbol][0]
|
||||||
d['timestamp_arrival'] = round(datetime.now().timestamp()*1000)
|
d['timestamp_arrival'] = round(datetime.now().timestamp()*1000)
|
||||||
else:
|
|
||||||
d = [x for x in resp if x.get('symbol', None) == ASTER.symbol][0]
|
|
||||||
|
|
||||||
if previous_notional_obj is not None:
|
if previous_notional_obj is not None:
|
||||||
if previous_notional_obj['timestamp_arrival'] > d['timestamp_arrival']:
|
if previous_notional_obj['timestamp_arrival'] > d['timestamp_arrival']:
|
||||||
@@ -162,8 +168,6 @@ async def get_aster_notional_position(resp: list | None = None):
|
|||||||
ASTER_NOTIONAL_POSITION = float(d['notional']) - ASTER_UNREALIZED_PNL
|
ASTER_NOTIONAL_POSITION = float(d['notional']) - ASTER_UNREALIZED_PNL
|
||||||
else:
|
else:
|
||||||
ASTER_NOTIONAL_POSITION = float(d['position_amount'])*float(d['entry_price'])
|
ASTER_NOTIONAL_POSITION = float(d['position_amount'])*float(d['entry_price'])
|
||||||
|
|
||||||
previous_notional_position = ASTER_NOTIONAL_POSITION
|
|
||||||
# if not resp: # this can never evaluate
|
# if not resp: # this can never evaluate
|
||||||
# ASTER.mult = float(d['leverage'])
|
# ASTER.mult = float(d['leverage'])
|
||||||
if abs(ASTER_NOTIONAL_POSITION) > ALGO_CONFIG.Config.Max_Target_Notional*ALGO_CONFIG.Config.Max_Order_Over_Notional_Ratio:
|
if abs(ASTER_NOTIONAL_POSITION) > ALGO_CONFIG.Config.Max_Target_Notional*ALGO_CONFIG.Config.Max_Order_Over_Notional_Ratio:
|
||||||
@@ -172,11 +176,25 @@ async def get_aster_notional_position(resp: list | None = None):
|
|||||||
if ASTER_NOTIONAL_POSITION != previous_notional_position:
|
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():
|
# async def get_extend_collateral():
|
||||||
global EXTEND_AVAIL_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
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
async def get_extend_notional(resp: list | None = None):
|
async def get_extend_notional(resp: list | None = None):
|
||||||
global EXTEND_NOTIONAL_OBJ
|
global EXTEND_NOTIONAL_OBJ
|
||||||
@@ -207,8 +225,9 @@ async def get_extend_notional(resp: list | None = None):
|
|||||||
pos_dict = {}
|
pos_dict = {}
|
||||||
pos_dict['side'] = 'LONG'
|
pos_dict['side'] = 'LONG'
|
||||||
pos_dict['value'] = 0.00
|
pos_dict['value'] = 0.00
|
||||||
logging.info('get_extend_notional - No Positions')
|
# logging.info('get_extend_notional - No Positions')
|
||||||
|
|
||||||
|
pos_dict['timestamp_arrival'] = round(datetime.now().timestamp()*1000)
|
||||||
|
|
||||||
if previous_notional_obj is not None:
|
if previous_notional_obj is not None:
|
||||||
if previous_notional_obj['timestamp_arrival'] > pos_dict['timestamp_arrival']:
|
if previous_notional_obj['timestamp_arrival'] > pos_dict['timestamp_arrival']:
|
||||||
@@ -228,17 +247,20 @@ async def get_extend_notional(resp: list | None = None):
|
|||||||
logging.info(f'EXTEND BAD SIDE ON POSITION UPDATE: {pos_dict}')
|
logging.info(f'EXTEND BAD SIDE ON POSITION UPDATE: {pos_dict}')
|
||||||
|
|
||||||
EXTEND_NOTIONAL_POSITION = notional_pos_sided - float(EXTEND_UNREALIZED_PNL)
|
EXTEND_NOTIONAL_POSITION = notional_pos_sided - float(EXTEND_UNREALIZED_PNL)
|
||||||
EXTEND.mult = pos_dict.get('leverage', EXTEND)
|
EXTEND.mult = pos_dict.get('leverage', EXTEND.mult)
|
||||||
if abs(EXTEND_NOTIONAL_POSITION) > ALGO_CONFIG.Config.Max_Target_Notional*ALGO_CONFIG.Config.Max_Order_Over_Notional_Ratio:
|
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}')
|
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()
|
await kill_algo()
|
||||||
if EXTEND_NOTIONAL_POSITION != previous_notional_position:
|
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}; resp: {bool(resp)}')
|
||||||
|
|
||||||
### EXCHANGE INFO ###
|
### EXCHANGE INFO ###
|
||||||
async def get_aster_exch_info():
|
async def get_aster_exch_info(symbol_override: str | None = None):
|
||||||
global ASTER_MIN_ORDER_QTY
|
global ASTER
|
||||||
|
|
||||||
|
if symbol_override:
|
||||||
|
ASTER.symbol = utils.symbol_to_aster_fmt(symbol_override)
|
||||||
|
|
||||||
fut_acct_exchangeInfo: dict = {
|
fut_acct_exchangeInfo: dict = {
|
||||||
"url": "/fapi/v3/exchangeInfo",
|
"url": "/fapi/v3/exchangeInfo",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
@@ -246,15 +268,27 @@ async def get_aster_exch_info():
|
|||||||
}
|
}
|
||||||
r: dict = await aster_auth.post_authenticated_url(fut_acct_exchangeInfo) # ty:ignore[invalid-assignment]
|
r: dict = await aster_auth.post_authenticated_url(fut_acct_exchangeInfo) # ty:ignore[invalid-assignment]
|
||||||
s: list = r['symbols']
|
s: list = r['symbols']
|
||||||
d: dict = [d for d in s if d.get('symbol', None) == 'ETHUSDT'][0]
|
d: dict = [d for d in s if d.get('symbol', None) == ASTER.symbol][0]
|
||||||
f: dict = [f for f in d['filters'] if f.get('filterType', None) == 'LOT_SIZE'][0]
|
f: dict = [f for f in d['filters'] if f.get('filterType', None) == 'LOT_SIZE'][0]
|
||||||
ASTER_MIN_ORDER_QTY = float(f['minQty'])
|
q: dict = [f for f in d['filters'] if f.get('filterType', None) == 'PRICE_FILTER'][0]
|
||||||
|
|
||||||
async def get_extend_exch_info():
|
|
||||||
global EXTEND_MIN_ORDER_QTY
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
async def get_extend_exch_info(symbol_override: str | None = None):
|
||||||
|
global EXTEND
|
||||||
|
|
||||||
|
if symbol_override:
|
||||||
|
EXTEND.symbol = utils.symbol_to_extend_fmt(symbol_override)
|
||||||
|
|
||||||
r = await EXTEND_CLIENT.markets_info.get_markets_dict()
|
r = await EXTEND_CLIENT.markets_info.get_markets_dict()
|
||||||
EXTEND_MIN_ORDER_QTY = float(r['ETH-USD'].trading_config.min_order_size)
|
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)
|
||||||
|
|
||||||
### CANCEL ORDERS ###
|
### CANCEL ORDERS ###
|
||||||
async def aster_cancel_all_orders():
|
async def aster_cancel_all_orders():
|
||||||
@@ -262,7 +296,7 @@ async def aster_cancel_all_orders():
|
|||||||
"url": "/fapi/v3/allOpenOrders",
|
"url": "/fapi/v3/allOpenOrders",
|
||||||
"method": "DELETE",
|
"method": "DELETE",
|
||||||
"params": {
|
"params": {
|
||||||
'symbol': 'ETHUSDT',
|
'symbol': ASTER.symbol,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r = await aster_auth.post_authenticated_url(cancel_all_open_orders)
|
r = await aster_auth.post_authenticated_url(cancel_all_open_orders)
|
||||||
@@ -310,28 +344,60 @@ async def run_algo():
|
|||||||
best_symbol_by_exchange_aster = structs.Perpetual_Exchange(**best_symbol_by_exchange['ASTER'])
|
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'])
|
best_symbol_by_exchange_extend = structs.Perpetual_Exchange(**best_symbol_by_exchange['EXTEND'])
|
||||||
|
|
||||||
ASTER_FUND_RATE_DICT = json.loads(VAL_KEY.get('fund_rate_aster')) # ty:ignore[invalid-argument-type]
|
|
||||||
EXTENDED_FUND_RATE_DICT = json.loads(VAL_KEY.get('fund_rate_extended')) # ty:ignore[invalid-argument-type]
|
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 {}
|
||||||
ASTER_FUND_RATE: float = float(ASTER_FUND_RATE_DICT.get('funding_rate', 0))
|
if ASTER_FUND_RATE_DICT.get('symbol', None) != ASTER.symbol:
|
||||||
EXTEND_FUND_RATE: float = float(EXTENDED_FUND_RATE_DICT.get('funding_rate', 0))
|
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))
|
||||||
|
|
||||||
if ALGO_CONFIG.Overrides.Flip_Side_For_Testing:
|
if ALGO_CONFIG.Overrides.Flip_Side_For_Testing:
|
||||||
ASTER_FUND_RATE = ASTER_FUND_RATE * -1
|
ASTER_FUND_RATE = ASTER_FUND_RATE * -1
|
||||||
EXTEND_FUND_RATE = EXTEND_FUND_RATE * -1
|
EXTEND_FUND_RATE = EXTEND_FUND_RATE * -1
|
||||||
|
|
||||||
|
if ALGO_CONFIG.Overrides.Flatten_Open_Positions or ALGO_CONFIG.Overrides.Flatten_Open_Positions_Opportunistic:
|
||||||
|
ROUNDING = ROUND_HALF_UP
|
||||||
|
else:
|
||||||
|
ROUNDING = ROUND_DOWN
|
||||||
|
|
||||||
ASTER_FUND_RATE_TIME = float(ASTER_FUND_RATE_DICT.get('next_funding_time_ts_ms', 0))
|
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([float(EXTENDED_FUND_RATE_DICT.get('next_funding_time_ts_ms', 0)), 0])
|
||||||
EXTEND_FUND_RATE_TIME = max([EXTEND_FUND_RATE_TIME, 0])
|
|
||||||
|
|
||||||
ASTER_TICKER_DICT = json.loads(VAL_KEY.get('fut_ticker_aster')) # ty:ignore[invalid-argument-type]
|
ASTER_TICKER_DICT: Any = VAL_KEY.get('fut_ticker_aster')
|
||||||
EXTENDED_TICKER_DICT = json.loads(VAL_KEY.get('fut_ticker_extended')) # ty:ignore[invalid-argument-type]
|
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}')
|
||||||
|
|
||||||
### Manage Local Collateral Using Updates from WS ###
|
### Manage Local Collateral Using Updates from WS ###
|
||||||
ASTER_WS_COLLATERAL_UPDATES = VAL_KEY.get('fr_aster_user_positions')
|
# ASTER_WS_COLLATERAL_UPDATES = VAL_KEY.get('fr_aster_user_positions')
|
||||||
ASTER_WS_COLLATERAL_UPDATES = json.loads(ASTER_WS_COLLATERAL_UPDATES) if ASTER_WS_COLLATERAL_UPDATES is not None else [] # ty:ignore[invalid-argument-type]
|
# ASTER_WS_COLLATERAL_UPDATES = json.loads(ASTER_WS_COLLATERAL_UPDATES) if ASTER_WS_COLLATERAL_UPDATES is not None else [] # ty:ignore[invalid-argument-type]
|
||||||
EXTEND_WS_COLLATERAL_UPDATES = VAL_KEY.get('fr_extended_user_positions')
|
# EXTEND_WS_COLLATERAL_UPDATES = VAL_KEY.get('fr_extended_user_positions')
|
||||||
EXTEND_WS_COLLATERAL_UPDATES = json.loads(EXTEND_WS_COLLATERAL_UPDATES) if EXTEND_WS_COLLATERAL_UPDATES is not None else [] # ty:ignore[invalid-argument-type]
|
# EXTEND_WS_COLLATERAL_UPDATES = json.loads(EXTEND_WS_COLLATERAL_UPDATES) if EXTEND_WS_COLLATERAL_UPDATES is not None else [] # ty:ignore[invalid-argument-type]
|
||||||
|
|
||||||
### Manage Local Notionals Using Updates from WS ###
|
### Manage Local Notionals Using Updates from WS ###
|
||||||
ASTER_WS_POS_UPDATES: Any = VAL_KEY.get(name='fr_aster_user_positions')
|
ASTER_WS_POS_UPDATES: Any = VAL_KEY.get(name='fr_aster_user_positions')
|
||||||
@@ -426,32 +492,42 @@ async def run_algo():
|
|||||||
utils.send_tg_alert(f'FR_ALGO - EXTEND REJECTED ({order_id})')
|
utils.send_tg_alert(f'FR_ALGO - EXTEND REJECTED ({order_id})')
|
||||||
elif order_update_status in ['PARTIALLY_FILLED']:
|
elif order_update_status in ['PARTIALLY_FILLED']:
|
||||||
logging.info(f'EXTEND ORDER PARTIALLY FILLED: {order_id}')
|
logging.info(f'EXTEND ORDER PARTIALLY FILLED: {order_id}')
|
||||||
await get_extend_collateral()
|
# await get_extend_collateral()
|
||||||
await get_extend_notional()
|
await get_extend_notional()
|
||||||
utils.send_tg_alert(f'FR_ALGO - EXTEND PARTIALLY FILLED ({order_id})')
|
utils.send_tg_alert(f'FR_ALGO - EXTEND PARTIALLY FILLED ({order_id})')
|
||||||
elif order_update_status in ['FILLED']:
|
elif order_update_status in ['FILLED']:
|
||||||
logging.info(f'EXTEND ORDER FILLED: {order_id}')
|
logging.info(f'EXTEND ORDER FILLED: {order_id}')
|
||||||
EXTEND_OPEN_ORDERS.pop(idx)
|
EXTEND_OPEN_ORDERS.pop(idx)
|
||||||
await get_extend_collateral()
|
# await get_extend_collateral()
|
||||||
await get_extend_notional()
|
await get_extend_notional()
|
||||||
utils.send_tg_alert(f'FR_ALGO - EXTEND FILLED ({order_id})')
|
utils.send_tg_alert(f'FR_ALGO - EXTEND FILLED ({order_id})')
|
||||||
else:
|
else:
|
||||||
logging.critical(f'EXTEND ORDER STATUS CHG TO UNEXPECTED VALUE, KILLING... ({order_id}): {order_orig_status} -> {order_update_status}')
|
logging.critical(f'EXTEND ORDER STATUS CHG TO UNEXPECTED VALUE, KILLING... ({order_id}): {order_orig_status} -> {order_update_status}')
|
||||||
|
|
||||||
|
|
||||||
# if (best_symbol_by_exchange_aster.symbol != ASTER.symbol) or (best_symbol_by_exchange_extend.symbol != EXTEND.symbol):
|
if ALGO_CONFIG.Overrides.Allow_Symbol_Change:
|
||||||
# if abs( ASTER_NOTIONAL_POSITION ) > 0.00 or abs( EXTEND_NOTIONAL_POSITION ) > 0.00:
|
if (best_symbol_by_exchange_aster.symbol != ASTER.symbol) or (best_symbol_by_exchange_extend.symbol != EXTEND.symbol):
|
||||||
# print('Symbol switch - Flattening Positions')
|
if abs( ASTER_NOTIONAL_POSITION ) > 0.00 or abs( EXTEND_NOTIONAL_POSITION ) > 0.00:
|
||||||
# ALGO_CONFIG.Overrides.Flatten_Open_Positions = True
|
if ALGO_CONFIG.Logging.Print_Summary_Each_Loop:
|
||||||
# else:
|
print(f'Symbol switch [{ASTER.symbol} > {best_symbol_by_exchange_aster.symbol}] - Flattening Positions')
|
||||||
# print('Balances Flattened - Updating to Trade New Symbols:')
|
ALGO_CONFIG.Overrides.Flatten_Open_Positions = True
|
||||||
# print(f' ASTER.symbol -> {best_symbol_by_exchange_aster.symbol}')
|
else:
|
||||||
# print(f' EXTEND.symbol -> {best_symbol_by_exchange_extend.symbol}')
|
logging.info('Balances Flattened - Updating to Trade New Symbols:')
|
||||||
# ALGO_CONFIG.Overrides.Flatten_Open_Positions = False
|
logging.info(f' ASTER.symbol -> {best_symbol_by_exchange_aster.symbol}')
|
||||||
# ASTER = best_symbol_by_exchange_aster
|
logging.info(f' EXTEND.symbol -> {best_symbol_by_exchange_extend.symbol}')
|
||||||
# EXTEND = best_symbol_by_exchange_extend
|
ALGO_CONFIG.Overrides.Flatten_Open_Positions = False
|
||||||
# VAL_KEY.set(name='fr_algo_working_symbol', value=json.dumps(obj={'ASTER': asdict(obj=ASTER), 'EXTEND': asdict(obj=EXTEND)}))
|
if Open_Symbols:
|
||||||
|
logging.info(f'OPEN SYMBOLS TO CLOSE: {Open_Symbols}')
|
||||||
|
await get_aster_exch_info(symbol_override=Open_Symbols[0])
|
||||||
|
await get_extend_exch_info(symbol_override=Open_Symbols[0])
|
||||||
|
Open_Symbols.pop(0)
|
||||||
|
await get_aster_notional_position()
|
||||||
|
await get_extend_notional()
|
||||||
|
else:
|
||||||
|
ASTER = best_symbol_by_exchange_aster
|
||||||
|
EXTEND = best_symbol_by_exchange_extend
|
||||||
|
|
||||||
|
VAL_KEY.set(name='fr_algo_working_symbol', value=json.dumps(obj={'ASTER': asdict(obj=ASTER), 'EXTEND': asdict(obj=EXTEND)}))
|
||||||
|
|
||||||
|
|
||||||
min_between_fundings = round((abs(ASTER_FUND_RATE_TIME - EXTEND_FUND_RATE_TIME) / 1000 / 60))
|
min_between_fundings = round((abs(ASTER_FUND_RATE_TIME - EXTEND_FUND_RATE_TIME) / 1000 / 60))
|
||||||
@@ -479,7 +555,7 @@ async def run_algo():
|
|||||||
|
|
||||||
NEXT_NET_FUNDING_RATE = calc_next_net_fund_rate(FUNDINGS_AT_SAME_TIME_NEXT_HR)
|
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.Config.Min_Fund_Rate_Pct_To_Trade*-1) / 100) ) and ( NEXT_NET_FUNDING_RATE <= ( ALGO_CONFIG.Config.Min_Fund_Rate_Pct_To_Trade / 100 ) )
|
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:
|
if Flags.NET_FUNDING_IS_ZERO or ALGO_CONFIG.Overrides.Flatten_Open_Positions or ALGO_CONFIG.Overrides.Flatten_Open_Positions_Opportunistic:
|
||||||
ALPHA_TGT_NOTIONAL = 0.00
|
ALPHA_TGT_NOTIONAL = 0.00
|
||||||
# if ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS:
|
# if ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS:
|
||||||
# logging.info('NET FUNDING = 0.00; Cancelling Open Orders! then Waiting...')
|
# logging.info('NET FUNDING = 0.00; Cancelling Open Orders! then Waiting...')
|
||||||
@@ -536,16 +612,22 @@ async def run_algo():
|
|||||||
EXTEND_TGT_TAIL = EXTEND_TGT_NOTIONAL - ( float(EXTEND_NOTIONAL_POSITION) + float(EXTEND_UNREALIZED_PNL) )
|
EXTEND_TGT_TAIL = EXTEND_TGT_NOTIONAL - ( float(EXTEND_NOTIONAL_POSITION) + float(EXTEND_UNREALIZED_PNL) )
|
||||||
# EXTEND_TGT_TAIL = float(ASTER_NOTIONAL_POSITION)*-1
|
# EXTEND_TGT_TAIL = float(ASTER_NOTIONAL_POSITION)*-1
|
||||||
|
|
||||||
ASTER_TGT_TAIL_BASE_QTY = Decimal(str(float(ASTER_TGT_TAIL) / float(ASTER_TOB_PX))).quantize(Decimal(str(0.001)), rounding=ROUND_DOWN)
|
min_order_size = ASTER.min_order_size
|
||||||
EXTEND_TGT_TAIL_BASE_QTY = Decimal(str(float(EXTEND_TGT_TAIL) / float(EXTEND_TOB_PX))).quantize(Decimal(str(0.001)), rounding=ROUND_DOWN)
|
min_order_size = int(min_order_size) if min_order_size == int(min_order_size) else min_order_size
|
||||||
|
ASTER_TGT_TAIL_BASE_QTY = Decimal(str(float(ASTER_TGT_TAIL) / float(ASTER_TOB_PX))).quantize(Decimal(str(min_order_size)), rounding=ROUNDING)
|
||||||
|
|
||||||
MAX_MIN_ORDER_QTY = max([ASTER_MIN_ORDER_QTY, EXTEND_MIN_ORDER_QTY])
|
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
|
||||||
|
EXTEND_TGT_TAIL_BASE_QTY = Decimal(str(float(EXTEND_TGT_TAIL) / float(EXTEND_TOB_PX))).quantize(Decimal(str(min_order_size)), rounding=ROUNDING)
|
||||||
|
|
||||||
ASTER_TGT_TAIL_ORDERABLE = abs(ASTER_TGT_TAIL_BASE_QTY) >= MAX_MIN_ORDER_QTY
|
# MAX_MIN_ORDER_QTY = max([ASTER.min_order_size, EXTEND.min_order_size])
|
||||||
EXTEND_TGT_TAIL_ORDERABLE = abs(EXTEND_TGT_TAIL_BASE_QTY) >= MAX_MIN_ORDER_QTY
|
ASTER_TGT_TAIL_ORDERABLE = Decimal(str(abs(ASTER_TGT_TAIL_BASE_QTY))) >= Decimal(str(abs(ASTER.min_order_size)))
|
||||||
|
EXTEND_TGT_TAIL_ORDERABLE = Decimal(str(abs(EXTEND_TGT_TAIL_BASE_QTY))) >= Decimal(str(abs(EXTEND.min_order_size)))
|
||||||
|
# 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
|
Hedge_Ratio = abs( ( EXTEND_NOTIONAL_POSITION + ASTER_NOTIONAL_POSITION ) / max([ASTER_NOTIONAL_POSITION, 0.01]) ) * 100
|
||||||
Currently_Hedged = Hedge_Ratio < 1.00
|
Currently_Hedged = Hedge_Ratio < 1.00
|
||||||
|
|
||||||
def print_summary(use_logging: bool = False):
|
def print_summary(use_logging: bool = False):
|
||||||
@@ -555,25 +637,25 @@ async def run_algo():
|
|||||||
LOOP SLEEP (SEC): {ALGO_CONFIG.Config.Loop_Sleep_Sec}
|
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}
|
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()):})
|
{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_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: {'LONG PAYS SHORT' if ASTER_FUND_RATE > 0 else 'SHORT PAYS LONG'} | EXTEND: {'LONG PAYS SHORT' if EXTEND_FUND_RATE > 0 else 'SHORT PAYS LONG'}
|
||||||
ASTER: [ Notional Position $ : {ASTER_NOTIONAL_POSITION:.4f} ] | EXTEND: [ Notional Position $ : {EXTEND_NOTIONAL_POSITION:.4f} ]
|
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} ]
|
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.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}]
|
ALPHA SIDE : {ALPHA_EXCH} [{ALPHA_CARRY_SIDE}]
|
||||||
|
|
||||||
TGT NOTIONAL: $ {abs(ALPHA_TGT_NOTIONAL):.2f}; Flatten Open Positions Flag? {ALGO_CONFIG.Overrides.Flatten_Open_Positions}
|
TGT NOTIONAL: $ {abs(ALPHA_TGT_NOTIONAL):.2f}; Flatten Open Positions Flag? {ALGO_CONFIG.Overrides.Flatten_Open_Positions}; Opportunistic? {ALGO_CONFIG.Overrides.Flatten_Open_Positions_Opportunistic}
|
||||||
|
|
||||||
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_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_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_TAIL_BASE_QTY:.4f} > {ASTER.min_order_size:.4f} min [ Order: {ASTER_TGT_TAIL_ORDERABLE} ] | EXTEND: {EXTEND_TGT_TAIL_BASE_QTY:.4f} > {EXTEND.min_order_size:.4f} min [ Order: {EXTEND_TGT_TAIL_ORDERABLE} ]
|
||||||
|
|
||||||
ALPHA: {ALPHA_RATIO:.8f} ALPHA_RATIO: {Alpha_Nominator:_.2f} / {Alpha_Denominator:_.2f} (Px Diff: {abs(Alpha_Nominator-Alpha_Denominator):.2f}); Expected_Alpha = {Expected_Alpha:.6f} + FR[{NEXT_NET_FUNDING_RATE:.6f}] = * {Expected_Alpha_Net_FR:.6f} *
|
ALPHA: {ALPHA_RATIO:.8f} ALPHA_RATIO: {Alpha_Nominator:_.6f} / {Alpha_Denominator:_.6f} (Px Diff: {abs(Alpha_Nominator-Alpha_Denominator):.2f}); Expected_Alpha = {Expected_Alpha:.6f} + FR[{NEXT_NET_FUNDING_RATE:.6f}] = * {Expected_Alpha_Net_FR:.6f} *
|
||||||
FEES : TAKER: {0.0002:.2%}; Expected Alpha w Taker = {Expected_Alpha_Net_FR-0.0002:.6f} [w/o FR: {Expected_Alpha_w_Taker:.6f}]
|
FEES : TAKER: {0.0002:.2%}; Expected Alpha w Taker = {Expected_Alpha_Net_FR-0.0002:.6f} [w/o FR: {Expected_Alpha_w_Taker:.6f}]
|
||||||
HEDGE: {Hedge_Ratio:.2f}% <= {1:.2f}%: {Currently_Hedged} [{EXTEND_NOTIONAL_POSITION:.2f} / {ASTER_NOTIONAL_POSITION:.2f}]
|
HEDGE: {Hedge_Ratio:.2f}% <= {1:.2f}%: {Currently_Hedged} [{EXTEND_NOTIONAL_POSITION:.2f} / {ASTER_NOTIONAL_POSITION:.2f}]
|
||||||
|
|
||||||
MKT : Aster: {ASTER.symbol} (best: {best_symbol_by_exchange_aster.symbol}) | Extend: {ASTER.symbol} (best: {best_symbol_by_exchange_extend.symbol})
|
MKT : Aster: {ASTER.symbol} (best: {best_symbol_by_exchange_aster.symbol}) | Extend: {ASTER.symbol} (best: {best_symbol_by_exchange_extend.symbol})
|
||||||
|
|
||||||
--- ASTER OPEN ORDERS ---
|
--- ASTER OPEN ORDERS ---
|
||||||
{ASTER_OPEN_ORDERS}
|
{ASTER_OPEN_ORDERS}
|
||||||
@@ -591,14 +673,15 @@ async def run_algo():
|
|||||||
|
|
||||||
|
|
||||||
### ROUTES ###
|
### ROUTES ###
|
||||||
MIN_EXPECTED_ALPHA_TO_TRADE = 0.0001
|
# Just_Rejected_Or_Expired
|
||||||
|
# MIN_EXPECTED_ALPHA_TO_TRADE = 0.0001
|
||||||
|
MIN_EXPECTED_ALPHA_TO_TRADE = abs(NEXT_NET_FUNDING_RATE)*-1
|
||||||
# MIN_EXPECTED_ALPHA_TO_TRADE = -0.000001
|
# MIN_EXPECTED_ALPHA_TO_TRADE = -0.000001
|
||||||
# ALPHA RATIO CHECK
|
# ALPHA RATIO CHECK
|
||||||
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 not( ( Expected_Alpha_Net_FR_w_Taker > MIN_EXPECTED_ALPHA_TO_TRADE ) or ( ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS or ALGO_CONFIG.Overrides.Flatten_Open_Positions) ) and Currently_Hedged:
|
||||||
if ALGO_CONFIG.Logging.Print_Summary_Each_Loop:
|
if ALGO_CONFIG.Logging.Print_Summary_Each_Loop:
|
||||||
print(f'Alpha Ratio too low ({ALPHA_RATIO:.8f}) and no Open Orders...')
|
print(f'Alpha Ratio too low ({ALPHA_RATIO:.8f}) and no Open Orders...')
|
||||||
pass
|
elif ( Expected_Alpha_Net_FR_w_Taker <= MIN_EXPECTED_ALPHA_TO_TRADE ) and ( ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS ) and Currently_Hedged and not(ALGO_CONFIG.Overrides.Flatten_Open_Positions):
|
||||||
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 aster_cancel_all_orders()
|
||||||
await extend_cancel_all_orders()
|
await extend_cancel_all_orders()
|
||||||
logging.info('Expected_Alpha went away with open orders...cancelling since we are currently hedged...')
|
logging.info('Expected_Alpha went away with open orders...cancelling since we are currently hedged...')
|
||||||
@@ -607,10 +690,12 @@ async def run_algo():
|
|||||||
# logging.info(f'*** Alpha Ratio HIT - LETS ORDER: {ALPHA_RATIO:.8f}')
|
# logging.info(f'*** Alpha Ratio HIT - LETS ORDER: {ALPHA_RATIO:.8f}')
|
||||||
# ASTER
|
# ASTER
|
||||||
if ASTER_TGT_TAIL_ORDERABLE and ALGO_CONFIG.Overrides.Allow_Ordering_Aster:
|
if ASTER_TGT_TAIL_ORDERABLE and ALGO_CONFIG.Overrides.Allow_Ordering_Aster:
|
||||||
|
# if ALGO_CONFIG.Overrides.Allow_Ordering_Aster:
|
||||||
symbol = ASTER.symbol
|
symbol = ASTER.symbol
|
||||||
side = 'BUY' if ASTER_TGT_TAIL_BASE_QTY > 0.00 else 'SELL'
|
side = 'BUY' if ASTER_TGT_TAIL_BASE_QTY > 0.00 else 'SELL'
|
||||||
qty = str(abs(ASTER_TGT_TAIL_BASE_QTY))
|
# qty = str(abs(ASTER_TGT_TAIL_BASE_QTY))
|
||||||
price = ASTER_TOB_PX - ALGO_CONFIG.Config.Price_Worsener_Aster if side == 'BUY' else ASTER_TOB_PX + ALGO_CONFIG.Config.Price_Worsener_Aster
|
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) )
|
||||||
|
|
||||||
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:
|
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})')
|
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})')
|
||||||
@@ -618,8 +703,12 @@ async def run_algo():
|
|||||||
if ASTER_OPEN_ORDERS:
|
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_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'])
|
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:
|
min_price = ASTER.min_price
|
||||||
logging.info('ASTER OPEN ORDER NO PX CHG; SKIPPING')
|
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 round(open_order_px - float(price), len(str(ASTER.min_price)) - 2 ) == 0.00:
|
||||||
|
if ALGO_CONFIG.Logging.Print_Summary_Each_Loop:
|
||||||
|
print('ASTER OPEN ORDER NO PX CHG; SKIPPING')
|
||||||
place_order = False
|
place_order = False
|
||||||
else:
|
else:
|
||||||
cancel_order: dict = {
|
cancel_order: dict = {
|
||||||
@@ -644,8 +733,17 @@ async def run_algo():
|
|||||||
place_order = False
|
place_order = False
|
||||||
logging.info('ASTER TRYNG TO ORDER 0.00 BASE QTY, SKIPPING')
|
logging.info('ASTER TRYNG TO ORDER 0.00 BASE QTY, SKIPPING')
|
||||||
|
|
||||||
if place_order:
|
if place_order:
|
||||||
price: Decimal = Decimal(str(price)).quantize(Decimal(str(0.01)), rounding=ROUND_DOWN)
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
if qty >= ASTER.min_order_size:
|
||||||
|
reduceOnly = False
|
||||||
|
else:
|
||||||
|
reduceOnly = True
|
||||||
|
|
||||||
post_order = {
|
post_order = {
|
||||||
"url": "/fapi/v3/order",
|
"url": "/fapi/v3/order",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
@@ -656,6 +754,7 @@ async def run_algo():
|
|||||||
'timeInForce': 'GTX',
|
'timeInForce': 'GTX',
|
||||||
'quantity': qty,
|
'quantity': qty,
|
||||||
'price': price,
|
'price': price,
|
||||||
|
'reduceOnly': reduceOnly
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
order_resp: dict = await aster_auth.post_authenticated_url(post_order) # ty:ignore[invalid-assignment]
|
order_resp: dict = await aster_auth.post_authenticated_url(post_order) # ty:ignore[invalid-assignment]
|
||||||
@@ -667,69 +766,93 @@ async def run_algo():
|
|||||||
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}')
|
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}')
|
logging.info(f'ASTER ORDER PLACED SUCCESS: {order_resp}')
|
||||||
print_summary(use_logging=True)
|
print_summary(use_logging=True)
|
||||||
|
else:
|
||||||
|
logging.critical(f'*** Aster Order Response Abnormal: {order_resp}; post_order: {post_order}')
|
||||||
|
await kill_algo()
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
# logging.warning('ASTER PLACE ORDER CHECKS FAILED, SKIPPING')
|
# logging.warning('ASTER PLACE ORDER CHECKS FAILED, SKIPPING')
|
||||||
|
|
||||||
elif not(ASTER_TGT_TAIL_ORDERABLE) and ASTER_OPEN_ORDERS:
|
elif not(ASTER_TGT_TAIL_ORDERABLE) and ASTER_OPEN_ORDERS:
|
||||||
|
### Add code to flatten small balances
|
||||||
logging.info('ASTER HAS NO TAIL BUT OPEN ORDERS - CANCELLING OPEN ORDERS')
|
logging.info('ASTER HAS NO TAIL BUT OPEN ORDERS - CANCELLING OPEN ORDERS')
|
||||||
await aster_cancel_all_orders()
|
await aster_cancel_all_orders()
|
||||||
|
|
||||||
# EXTEND
|
# EXTEND
|
||||||
if EXTEND_TGT_TAIL_ORDERABLE and ALGO_CONFIG.Overrides.Allow_Ordering_Extend:
|
if EXTEND_TGT_TAIL_ORDERABLE and ALGO_CONFIG.Overrides.Allow_Ordering_Extend:
|
||||||
|
# if ALGO_CONFIG.Overrides.Allow_Ordering_Extend:
|
||||||
|
side = OrderSide.BUY if EXTEND_TGT_TAIL_BASE_QTY > 0.00 else OrderSide.SELL
|
||||||
|
symbol = EXTEND.symbol
|
||||||
|
qty = Decimal(value=str(abs(EXTEND_TGT_TAIL_BASE_QTY)))
|
||||||
Time_Since_Last_Aster_Fill_ms = ( datetime.now().timestamp()*1000 ) - Last_Aster_Fill_Time_Ts
|
Time_Since_Last_Aster_Fill_ms = ( datetime.now().timestamp()*1000 ) - Last_Aster_Fill_Time_Ts
|
||||||
|
min_price = EXTEND.min_price
|
||||||
|
min_price = int(min_price) if min_price == int(min_price) else min_price
|
||||||
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
|
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
|
post_only = False
|
||||||
price: Decimal = Decimal(value=str(EXTEND_TOB_PX - ALGO_CONFIG.Config.Price_Worsener_Extend if side == 'BUY' else EXTEND_TOB_PX + ALGO_CONFIG.Config.Price_Worsener_Extend)).quantize(Decimal(str(0.1)), rounding=ROUND_DOWN)
|
price: Decimal = Decimal(value=str(EXTEND_TOB_PX - ( float(min_price)*int(ALGO_CONFIG.Config.Price_Worsener_Extend) ) if side == 'BUY' else EXTEND_TOB_PX + ( float(min_price)*int(ALGO_CONFIG.Config.Price_Worsener_Extend) ) )).quantize(Decimal(str(min_price)), rounding=ROUND_HALF_UP)
|
||||||
else:
|
else:
|
||||||
post_only = True
|
post_only = True
|
||||||
price: Decimal = Decimal(value=str(EXTEND_TOB_PX)).quantize(Decimal(str(0.1)), rounding=ROUND_DOWN)
|
price: Decimal = Decimal(value=str(EXTEND_TOB_PX)).quantize(Decimal(str(min_price)), rounding=ROUND_HALF_UP)
|
||||||
|
|
||||||
symbol = EXTEND.symbol
|
|
||||||
side = OrderSide.BUY if EXTEND_TGT_TAIL_BASE_QTY > 0.00 else OrderSide.SELL
|
|
||||||
qty = Decimal(value=str(abs(EXTEND_TGT_TAIL_BASE_QTY)))
|
|
||||||
|
|
||||||
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:
|
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})')
|
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()
|
await kill_algo()
|
||||||
if EXTEND_OPEN_ORDERS:
|
if EXTEND_OPEN_ORDERS:
|
||||||
open_order_dict = dict(EXTEND_OPEN_ORDERS[0])
|
open_order_dict = dict(EXTEND_OPEN_ORDERS[0])
|
||||||
open_order_id = open_order_dict['external_id']
|
open_order_id = str(open_order_dict['external_id'])
|
||||||
open_order_px = float(open_order_dict['price'])
|
open_order_px = float(open_order_dict['price'])
|
||||||
open_order_filled_qty = float(open_order_dict['filled_qty'])
|
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.
|
if qty >= EXTEND.min_order_size:
|
||||||
# qty = Decimal(str(qty))
|
|
||||||
|
|
||||||
if qty >= MAX_MIN_ORDER_QTY:
|
|
||||||
place_order = True
|
place_order = True
|
||||||
|
place_residual_order = False
|
||||||
else:
|
else:
|
||||||
place_order = False
|
if int(qty) == 0:
|
||||||
logging.info(f'EXTEND NOT ORDERING DUE TO FILLED QTY RESIDUAL < MIN ORDER; Filled: {float(open_order_filled_qty):.4f}; Residual: {qty:.4f}')
|
place_order = False
|
||||||
|
place_residual_order = False
|
||||||
|
logging.info(f'EXTEND NOT ORDERING DUE TO NOTIONAL QTY == 0; Filled: {float(open_order_filled_qty):.4f}; Residual: {qty:.4f}')
|
||||||
|
else:
|
||||||
|
place_order = True
|
||||||
|
place_residual_order = True
|
||||||
|
logging.info(f'Ordering RESIDUAL market order for remaining small amount: {qty}')
|
||||||
else:
|
else:
|
||||||
open_order_id = None
|
open_order_id = None
|
||||||
open_order_px = 0
|
open_order_px = 0
|
||||||
place_order = True
|
place_order = True
|
||||||
if place_order:
|
if qty >= EXTEND.min_order_size:
|
||||||
price: Decimal = Decimal(str(price)).quantize(Decimal(str(0.1)), rounding=ROUND_DOWN)
|
place_residual_order = False
|
||||||
if round(open_order_px - float(price), 2) == 0.00:
|
else:
|
||||||
logging.info('EXTEND OPEN ORDER NO PX CHG; SKIPPING')
|
place_residual_order = True
|
||||||
|
if place_order:
|
||||||
|
price: Decimal = Decimal(str(price)).quantize(Decimal(str(min_price)), rounding=ROUND_HALF_UP)
|
||||||
|
if round(open_order_px - float(price), len(str(min_price)) - 2 ) == 0.00:
|
||||||
|
if ALGO_CONFIG.Logging.Print_Summary_Each_Loop:
|
||||||
|
print('EXTEND OPEN ORDER NO PX CHG; SKIPPING')
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
if place_residual_order:
|
||||||
|
post_only = False
|
||||||
|
reduce_only = True
|
||||||
|
else:
|
||||||
|
# post_only = SEE ABOVE
|
||||||
|
reduce_only = False
|
||||||
|
|
||||||
taker_fee = taker_fee=Decimal("0.00000") if post_only else Decimal("0.00025")
|
taker_fee = taker_fee=Decimal("0.00000") if post_only else Decimal("0.00025")
|
||||||
order_resp: WrappedApiResponse[PlacedOrderModel] = await EXTEND_CLIENT.place_order(
|
order_resp: WrappedApiResponse[PlacedOrderModel] = await EXTEND_CLIENT.place_order(
|
||||||
market_name=symbol,
|
market_name=symbol,
|
||||||
amount_of_synthetic=qty,
|
amount_of_synthetic=Decimal(str(qty)),
|
||||||
price=price,
|
price=Decimal(str(price)),
|
||||||
side=side,
|
side=side,
|
||||||
taker_fee=taker_fee,
|
taker_fee=taker_fee,
|
||||||
previous_order_id=open_order_id,
|
previous_order_id=open_order_id,
|
||||||
post_only=post_only,
|
post_only=post_only
|
||||||
|
# reduce_only=reduce_only
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f'EXTEND ORDER PLACEMENT FAILED - RESP: {order_resp}')
|
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: {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(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}; reduce_only:{reduce_only}')
|
||||||
logging.error(traceback.format_exc())
|
logging.error(traceback.format_exc())
|
||||||
|
|
||||||
order_resp_dict = dict(order_resp)
|
order_resp_dict = dict(order_resp)
|
||||||
@@ -751,15 +874,16 @@ async def run_algo():
|
|||||||
logging.info(f'EXTEND ORDER PLACED SUCCESS: {order_dict}')
|
logging.info(f'EXTEND ORDER PLACED SUCCESS: {order_dict}')
|
||||||
print_summary(use_logging=True)
|
print_summary(use_logging=True)
|
||||||
else:
|
else:
|
||||||
order_resp_dict.get
|
logging.critical(f'*** Extend Order Response Abnormal: {order_resp};')
|
||||||
|
await kill_algo()
|
||||||
else:
|
else:
|
||||||
logging.warning('EXTEND PLACE ORDER CHECKS FAILED, SKIPPING')
|
logging.warning('EXTEND PLACE ORDER CHECKS FAILED, SKIPPING')
|
||||||
|
|
||||||
elif not(EXTEND_TGT_TAIL_ORDERABLE) and EXTEND_OPEN_ORDERS:
|
elif not(EXTEND_TGT_TAIL_ORDERABLE) and EXTEND_OPEN_ORDERS:
|
||||||
logging.info('EXTEND HAS NO TAIL BUT OPEN ORDERS - CANCELLING OPEN ORDERS')
|
logging.info('EXTEND HAS NO TAIL BUT OPEN ORDERS - CANCELLING OPEN ORDERS')
|
||||||
await extend_cancel_all_orders()
|
await extend_cancel_all_orders()
|
||||||
|
|
||||||
|
|
||||||
if ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS:
|
if ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
@@ -783,11 +907,27 @@ async def main():
|
|||||||
global VAL_KEY
|
global VAL_KEY
|
||||||
global CON
|
global CON
|
||||||
global ALGO_CONFIG
|
global ALGO_CONFIG
|
||||||
|
global ASTER
|
||||||
|
global EXTEND
|
||||||
|
global Open_Symbols
|
||||||
|
|
||||||
|
|
||||||
_, EXTEND_CLIENT = await extend_auth.create_auth_account_and_trading_client()
|
_, EXTEND_CLIENT = await extend_auth.create_auth_account_and_trading_client()
|
||||||
VAL_KEY = valkey.Valkey(host='localhost', port=6379, db=0, decode_responses=True)
|
VAL_KEY = valkey.Valkey(host='localhost', port=6379, db=0, decode_responses=True)
|
||||||
engine = create_async_engine('mysql+asyncmy://root:pwd@localhost/fund_rate')
|
engine = create_async_engine('mysql+asyncmy://root:pwd@localhost/fund_rate')
|
||||||
|
|
||||||
|
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_engine_best_fund_rate_output')) # ty:ignore[invalid-argument-type]
|
||||||
|
ASTER = structs.Perpetual_Exchange(**best_symbol_by_exchange['ASTER'])
|
||||||
|
EXTEND = structs.Perpetual_Exchange(**best_symbol_by_exchange['EXTEND'])
|
||||||
|
|
||||||
|
await set_comb_open_symbols()
|
||||||
|
|
||||||
|
if Open_Symbols:
|
||||||
|
logging.info(f'OPEN SYMBOLS TO CLOSE: {Open_Symbols}')
|
||||||
|
await get_aster_exch_info(symbol_override=Open_Symbols[0])
|
||||||
|
await get_extend_exch_info(symbol_override=Open_Symbols[0])
|
||||||
|
Open_Symbols.pop(0)
|
||||||
|
|
||||||
with open('algo_config.json', mode='r', encoding='utf-8') as file:
|
with open('algo_config.json', mode='r', encoding='utf-8') as file:
|
||||||
ALGO_CONFIG = json.load(file)
|
ALGO_CONFIG = json.load(file)
|
||||||
ALGO_CONFIG = structs.Algo_Config(**ALGO_CONFIG)
|
ALGO_CONFIG = structs.Algo_Config(**ALGO_CONFIG)
|
||||||
@@ -799,12 +939,12 @@ async def main():
|
|||||||
|
|
||||||
async with engine.connect() as CON:
|
async with engine.connect() as CON:
|
||||||
### ASTER SETUP ###
|
### ASTER SETUP ###
|
||||||
await get_aster_collateral()
|
# await get_aster_collateral()
|
||||||
await get_aster_notional_position()
|
await get_aster_notional_position()
|
||||||
await get_aster_exch_info()
|
await get_aster_exch_info()
|
||||||
await get_aster_open_orders()
|
await get_aster_open_orders()
|
||||||
### EXTEND SETUP ###
|
### EXTEND SETUP ###
|
||||||
await get_extend_collateral()
|
# await get_extend_collateral()
|
||||||
await get_extend_notional()
|
await get_extend_notional()
|
||||||
await get_extend_exch_info()
|
await get_extend_exch_info()
|
||||||
await get_extend_open_orders()
|
await get_extend_open_orders()
|
||||||
@@ -826,4 +966,3 @@ if __name__ == '__main__':
|
|||||||
logging.info(f"STARTED: {START_TIME}")
|
logging.info(f"STARTED: {START_TIME}")
|
||||||
|
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ LEVERAGE_BY_EXCH: list[Asset_Leverage] = [
|
|||||||
Asset_Leverage('ASTER', 'LIT' , 'USDT', 50 , 2_500 ), Asset_Leverage('EXTEND', 'LIT' , 'USD', 25, 400_000 ),
|
Asset_Leverage('ASTER', 'LIT' , 'USDT', 50 , 2_500 ), Asset_Leverage('EXTEND', 'LIT' , 'USD', 25, 400_000 ),
|
||||||
Asset_Leverage('ASTER', 'SOL' , 'USDT', 100, 50_000 ), Asset_Leverage('EXTEND', 'SOL' , 'USD', 50, 1_000_000),
|
Asset_Leverage('ASTER', 'SOL' , 'USDT', 100, 50_000 ), Asset_Leverage('EXTEND', 'SOL' , 'USD', 50, 1_000_000),
|
||||||
Asset_Leverage('ASTER', 'SUI' , 'USDT', 75 , 5_416 ), Asset_Leverage('EXTEND', 'SUI' , 'USD', 50, 500_000 ),
|
Asset_Leverage('ASTER', 'SUI' , 'USDT', 75 , 5_416 ), Asset_Leverage('EXTEND', 'SUI' , 'USD', 50, 500_000 ),
|
||||||
Asset_Leverage('ASTER', 'TRUMP', 'USDT', 50 , 5_567 ), Asset_Leverage('EXTEND', 'TRUMP', 'USD', 25, 400_000 ),
|
Asset_Leverage('ASTER', 'TRUMP', 'USDT', 10 , 60_000 ), Asset_Leverage('EXTEND', 'TRUMP', 'USD', 25, 400_000 ),
|
||||||
Asset_Leverage('ASTER', 'WLFI' , 'USDT', 25 , 104_869), Asset_Leverage('EXTEND', 'WLFI' , 'USD', 10, 250_000 ),
|
Asset_Leverage('ASTER', 'WLFI' , 'USDT', 25 , 104_869), Asset_Leverage('EXTEND', 'WLFI' , 'USD', 10, 250_000 ),
|
||||||
Asset_Leverage('ASTER', 'XAG' , 'USDT', 100, 50_000 ), Asset_Leverage('EXTEND', 'XAG' , 'USD', 10, 1_000_000),
|
Asset_Leverage('ASTER', 'XAG' , 'USDT', 100, 50_000 ), Asset_Leverage('EXTEND', 'XAG' , 'USD', 10, 1_000_000),
|
||||||
Asset_Leverage('ASTER', 'XAU' , 'USDT', 75 , 2_500 ), Asset_Leverage('EXTEND', 'XAU' , 'USD', 25, 2_000_000),
|
Asset_Leverage('ASTER', 'XAU' , 'USDT', 75 , 2_500 ), Asset_Leverage('EXTEND', 'XAU' , 'USD', 25, 2_000_000),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from rel.rel import init
|
||||||
import json
|
import json
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@@ -11,6 +12,7 @@ class Algo_Config_Overrides(BaseModel):
|
|||||||
Allow_Ordering_Extend: bool
|
Allow_Ordering_Extend: bool
|
||||||
Allow_Symbol_Change: bool
|
Allow_Symbol_Change: bool
|
||||||
Flatten_Open_Positions: bool
|
Flatten_Open_Positions: bool
|
||||||
|
Flatten_Open_Positions_Opportunistic: bool
|
||||||
Flip_Side_For_Testing: bool
|
Flip_Side_For_Testing: bool
|
||||||
|
|
||||||
|
|
||||||
@@ -21,8 +23,8 @@ class Algo_Config_Config(BaseModel):
|
|||||||
Max_Target_Notional: float
|
Max_Target_Notional: float
|
||||||
Min_Time_To_Funding_Minutes: int
|
Min_Time_To_Funding_Minutes: int
|
||||||
Min_Fund_Rate_Pct_To_Trade: float
|
Min_Fund_Rate_Pct_To_Trade: float
|
||||||
Price_Worsener_Aster: float
|
Price_Worsener_Aster: int
|
||||||
Price_Worsener_Extend: float
|
Price_Worsener_Extend: int
|
||||||
Switch_To_Taker_Seconds: int
|
Switch_To_Taker_Seconds: int
|
||||||
Target_Open_Cash_Position: int
|
Target_Open_Cash_Position: int
|
||||||
|
|
||||||
@@ -159,10 +161,14 @@ class Perpetual_Exchange:
|
|||||||
# Collateral_Updates: Collateral
|
# Collateral_Updates: Collateral
|
||||||
# Funding_Rate: Funding_Rate
|
# Funding_Rate: Funding_Rate
|
||||||
# Markets: Markets_Details
|
# Markets: Markets_Details
|
||||||
mult: int
|
|
||||||
lh_asset: str
|
lh_asset: str
|
||||||
rh_asset: str
|
rh_asset: str
|
||||||
|
symbol: str = ''
|
||||||
symbol_asset_separator: str = ''
|
symbol_asset_separator: str = ''
|
||||||
|
mult: int
|
||||||
|
initial_funding_rate: float = 0
|
||||||
|
min_price: float = 0
|
||||||
|
min_order_size: float = 0
|
||||||
|
|
||||||
# async def update(self):
|
# async def update(self):
|
||||||
# await self.Collateral_Updates.update()
|
# await self.Collateral_Updates.update()
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import os
|
|||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
def upsert_list_of_dicts_by_id(list_of_dicts, new_dict, id='id', seq_check_field: str | None = None) -> list[dict]:
|
def upsert_list_of_dicts_by_id(list_of_dicts, new_dict, id='id', seq_check_field: str | None = None, reset_seq_id: bool = False) -> list[dict]:
|
||||||
for index, item in enumerate(list_of_dicts):
|
for index, item in enumerate(list_of_dicts):
|
||||||
if item.get(id) == new_dict.get(id):
|
if item.get(id) == new_dict.get(id):
|
||||||
if seq_check_field is not None:
|
if ( seq_check_field is not None ) and ( not(reset_seq_id) ):
|
||||||
if item.get(seq_check_field) > new_dict.get(seq_check_field):
|
if item.get(seq_check_field) > new_dict.get(seq_check_field):
|
||||||
logging.info('Skipping out of sequence msg')
|
logging.info('Skipping out of sequence msg')
|
||||||
return list_of_dicts
|
return list_of_dicts
|
||||||
@@ -40,4 +40,10 @@ def rec_set_dict(orig_dict, new_dict, allow_new_fields: bool = False) -> dict:
|
|||||||
else:
|
else:
|
||||||
logging.warning(msg=f'rec_set_dict: encountered nonexistent key: "{k}"; skipping')
|
logging.warning(msg=f'rec_set_dict: encountered nonexistent key: "{k}"; skipping')
|
||||||
|
|
||||||
return orig_dict
|
return orig_dict
|
||||||
|
|
||||||
|
def symbol_to_aster_fmt(symbol: str) -> str:
|
||||||
|
return (symbol+'T' if symbol[-1].upper()!='T' else symbol).replace('-','').upper()
|
||||||
|
|
||||||
|
def symbol_to_extend_fmt(symbol: str) -> str:
|
||||||
|
return (symbol[0:-1] if symbol[-1].upper()=='T' else symbol).replace('-','').upper().split('USD')[0]+'-'+'USD'
|
||||||
2
pyproject.toml
Normal file
2
pyproject.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[tool.ty.src]
|
||||||
|
exclude = ["*.ipynb"]
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "test_world"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
tokio = { version = "1", features = ["full"] }
|
|
||||||
tokio-tungstenite = { version = "0.24", features = ["native-tls"] }
|
|
||||||
futures-util = "0.3"
|
|
||||||
url = "2"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
|
||||||
serde_json = "1.0"
|
|
||||||
redis = "0.25.0"
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message};
|
|
||||||
use futures_util::StreamExt;
|
|
||||||
use url::Url;
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
use redis::Commands;
|
|
||||||
|
|
||||||
// Aster Book Ticker (TOB)
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
pub struct BookTickerResponse {
|
|
||||||
pub stream: String,
|
|
||||||
pub data: BookTickerData,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
pub struct BookTickerData {
|
|
||||||
#[serde(rename(deserialize = "e"))]
|
|
||||||
pub event_type: String,
|
|
||||||
#[serde(rename(deserialize = "u"))]
|
|
||||||
pub update_id: u64,
|
|
||||||
#[serde(rename(deserialize = "s"))]
|
|
||||||
pub symbol: String,
|
|
||||||
#[serde(rename(deserialize = "b"))]
|
|
||||||
pub best_bid_price: String,
|
|
||||||
#[serde(rename(deserialize = "B"))]
|
|
||||||
pub best_bid_qty: String,
|
|
||||||
#[serde(rename(deserialize = "a"))]
|
|
||||||
pub best_ask_price: String,
|
|
||||||
#[serde(rename(deserialize = "A"))]
|
|
||||||
pub best_ask_qty: String,
|
|
||||||
#[serde(rename(deserialize = "T"))]
|
|
||||||
pub transaction_time: u64,
|
|
||||||
#[serde(rename(deserialize = "E"))]
|
|
||||||
pub event_time: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() {
|
|
||||||
// Connect to a local Valkey instance
|
|
||||||
let vk_client = redis::Client::open("redis://localhost:6379/0").unwrap();
|
|
||||||
let mut con = vk_client.get_connection().unwrap();
|
|
||||||
|
|
||||||
let url = Url::parse("wss://fstream.asterdex.com/stream?streams=btcusdt@bookTicker").unwrap();
|
|
||||||
let (ws_stream, _) = connect_async(url.as_str()).await.expect("Failed to connect");
|
|
||||||
println!("WebSocket client connected");
|
|
||||||
|
|
||||||
let (_, mut read) = ws_stream.split();
|
|
||||||
|
|
||||||
while let Some(msg) = read.next().await {
|
|
||||||
match msg {
|
|
||||||
Ok(Message::Text(text)) => {
|
|
||||||
let parsed: BookTickerResponse = serde_json::from_str(&text).expect("Failed to parse JSON");
|
|
||||||
println!("Symbol: {} - Bid: {}", parsed.data.symbol, parsed.data.best_bid_price);
|
|
||||||
let serialized: String = serde_json::to_string(&parsed).map_err(|_| "Serialization failed").expect("Failed to serialize struct");
|
|
||||||
// println!("{:?}", serialized);
|
|
||||||
let _: () = con.set("test_key", serialized).unwrap();
|
|
||||||
},
|
|
||||||
Ok(Message::Binary(bin)) => println!("[binary] {} bytes", bin.len()),
|
|
||||||
Ok(Message::Ping(_)) => println!("[ping]"),
|
|
||||||
Ok(Message::Pong(_)) => println!("[pong]"),
|
|
||||||
Ok(Message::Close(frame)) => {
|
|
||||||
println!("[close] {:?}", frame);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Ok(Message::Frame(_)) => {}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("[error] {e}");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
36
ws_aster.py
36
ws_aster.py
@@ -45,7 +45,7 @@ STREAM_TRADES: str = f'{SYMBOL.lower()}@aggTrade'
|
|||||||
|
|
||||||
### Globals ###
|
### Globals ###
|
||||||
WSS_URL: str = f"wss://fstream.asterdex.com/stream?streams={STREAM_MARKPRICE}/{STREAM_BOOKTICKER}/{STREAM_TRADES}"
|
WSS_URL: str = f"wss://fstream.asterdex.com/stream?streams={STREAM_MARKPRICE}/{STREAM_BOOKTICKER}/{STREAM_TRADES}"
|
||||||
ALLOW_SYMBOL_CHG: bool = False
|
ALLOW_SYMBOL_CHG: bool = True
|
||||||
|
|
||||||
### Funcs ###
|
### Funcs ###
|
||||||
async def subscribe_streams(websocket, streams: list[str]) -> None:
|
async def subscribe_streams(websocket, streams: list[str]) -> None:
|
||||||
@@ -84,7 +84,7 @@ async def ws_stream():
|
|||||||
### Update Symbol if Algo Outputs Change ###
|
### Update Symbol if Algo Outputs Change ###
|
||||||
if ALLOW_SYMBOL_CHG:
|
if ALLOW_SYMBOL_CHG:
|
||||||
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_algo_working_symbol')) # ty:ignore[invalid-argument-type]
|
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_algo_working_symbol')) # ty:ignore[invalid-argument-type]
|
||||||
best_symbol: str = f'{best_symbol_by_exchange['ASTER']['lh_asset']}{best_symbol_by_exchange['ASTER']['rh_asset']}'
|
best_symbol: str = best_symbol_by_exchange['ASTER']['symbol']
|
||||||
if best_symbol != SYMBOL:
|
if best_symbol != SYMBOL:
|
||||||
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
|
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
|
||||||
SYMBOL = best_symbol
|
SYMBOL = best_symbol
|
||||||
@@ -133,22 +133,22 @@ async def ws_stream():
|
|||||||
VAL_KEY.set(VK_TICKER, VAL_KEY_OBJ)
|
VAL_KEY.set(VK_TICKER, VAL_KEY_OBJ)
|
||||||
continue
|
continue
|
||||||
case c if c == STREAM_TRADES:
|
case c if c == STREAM_TRADES:
|
||||||
# print(f'MKT_TRADE: {data}')
|
# # print(f'MKT_TRADE: {data}')
|
||||||
trade_obj = {
|
# trade_obj = {
|
||||||
'timestamp_arrival': ts_arrival,
|
# 'timestamp_arrival': ts_arrival,
|
||||||
'timestamp_msg': data['data']['E'],
|
# 'timestamp_msg': data['data']['E'],
|
||||||
'timestamp_trade': data['data']['T'],
|
# 'timestamp_trade': data['data']['T'],
|
||||||
'symbol': data['data']['s'],
|
# 'symbol': data['data']['s'],
|
||||||
'aggregate_trade_id': data['data']['a'],
|
# 'aggregate_trade_id': data['data']['a'],
|
||||||
'price': float(data['data']['p']),
|
# 'price': float(data['data']['p']),
|
||||||
'qty': float(data['data']['q']),
|
# 'qty': float(data['data']['q']),
|
||||||
'first_trade_id': data['data']['f'],
|
# 'first_trade_id': data['data']['f'],
|
||||||
'last_trade_id': data['data']['l'],
|
# 'last_trade_id': data['data']['l'],
|
||||||
'is_buyer_mkt_maker': bool(data['data']['m']),
|
# 'is_buyer_mkt_maker': bool(data['data']['m']),
|
||||||
}
|
# }
|
||||||
# VAL_KEY.set(VK_LAST_TRADE, json.dumps(trade_obj))
|
# # VAL_KEY.set(VK_LAST_TRADE, json.dumps(trade_obj))
|
||||||
if USE_DB:
|
# if USE_DB:
|
||||||
await db.insert_df_to_mysql(table_name='fr_aster_mkt_trades', params=trade_obj, CON=CON)
|
# await db.insert_df_to_mysql(table_name='fr_aster_mkt_trades', params=trade_obj, CON=CON)
|
||||||
continue
|
continue
|
||||||
case _:
|
case _:
|
||||||
logging.warning(f'UNMATCHED OTHER MSG: {data}')
|
logging.warning(f'UNMATCHED OTHER MSG: {data}')
|
||||||
|
|||||||
1
ws_aster/.dockerignore
Normal file
1
ws_aster/.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
../rust/
|
||||||
1
ws_aster_fund_rate_all/.dockerignore
Normal file
1
ws_aster_fund_rate_all/.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
../rust/
|
||||||
19
ws_aster_fund_rate_all/Dockerfile
Normal file
19
ws_aster_fund_rate_all/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
FROM python:3.13-slim
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y build-essential
|
||||||
|
|
||||||
|
RUN gcc --version
|
||||||
|
RUN rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Finally, run gunicorn.
|
||||||
|
CMD [ "python", "ws_aster_fund_rate_all.py"]
|
||||||
|
# CMD [ "gunicorn", "--workers=5", "--threads=1", "-b 0.0.0.0:8000", "app:server"]
|
||||||
1
ws_aster_user/.dockerignore
Normal file
1
ws_aster_user/.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
../rust/
|
||||||
@@ -39,7 +39,7 @@ LOG_FILEPATH: str = f'{os.getenv("LOGS_PATH")}/Fund_Rate_Extended_FR.log'
|
|||||||
SYMBOL: str = 'ETH-USD'
|
SYMBOL: str = 'ETH-USD'
|
||||||
|
|
||||||
### Globals ###
|
### Globals ###
|
||||||
ALLOW_SYMBOL_CHG: bool = False
|
ALLOW_SYMBOL_CHG: bool = True
|
||||||
|
|
||||||
def time_round_down(dt, interval_mins=5) -> int: # returns timestamp in seconds
|
def time_round_down(dt, interval_mins=5) -> int: # returns timestamp in seconds
|
||||||
interval_secs = interval_mins * 60
|
interval_secs = interval_mins * 60
|
||||||
@@ -64,7 +64,7 @@ async def ws_stream():
|
|||||||
### Update Symbol if Algo Outputs Change ###
|
### Update Symbol if Algo Outputs Change ###
|
||||||
if ALLOW_SYMBOL_CHG:
|
if ALLOW_SYMBOL_CHG:
|
||||||
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_algo_working_symbol')) # ty:ignore[invalid-argument-type]
|
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_algo_working_symbol')) # ty:ignore[invalid-argument-type]
|
||||||
best_symbol: str = f'{best_symbol_by_exchange['EXTEND']['lh_asset']}-{best_symbol_by_exchange['EXTEND']['rh_asset']}'
|
best_symbol: str = best_symbol_by_exchange['EXTEND']['symbol']
|
||||||
if best_symbol != SYMBOL:
|
if best_symbol != SYMBOL:
|
||||||
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
|
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
|
||||||
SYMBOL = best_symbol
|
SYMBOL = best_symbol
|
||||||
|
|||||||
1
ws_extended_fund_rate/.dockerignore
Normal file
1
ws_extended_fund_rate/.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
../rust/
|
||||||
@@ -26,7 +26,7 @@ urllib3_cn.allowed_gai_family = allowed_gai_family
|
|||||||
### Database ###
|
### Database ###
|
||||||
USE_DB: bool = False
|
USE_DB: bool = False
|
||||||
USE_VK: bool = True
|
USE_VK: bool = True
|
||||||
VK_FUND_RATE = 'fund_rate_extended'
|
# VK_FUND_RATE = 'fund_rate_extended'
|
||||||
VK_FUND_RATE_ALL = 'fund_rate_extended_all'
|
VK_FUND_RATE_ALL = 'fund_rate_extended_all'
|
||||||
|
|
||||||
CON: AsyncContextManager | None = None
|
CON: AsyncContextManager | None = None
|
||||||
@@ -71,7 +71,7 @@ async def ws_stream():
|
|||||||
'funding_rate_updated_ts_ms': data['data']['T'],
|
'funding_rate_updated_ts_ms': data['data']['T'],
|
||||||
# 'next_funding_time_ts_ms': fr_next_update_ts,
|
# 'next_funding_time_ts_ms': fr_next_update_ts,
|
||||||
}
|
}
|
||||||
LOCAL_FUNDING_RATES = utils.upsert_list_of_dicts_by_id(LOCAL_FUNDING_RATES, fr_update, id='symbol', seq_check_field='sequence_id')
|
LOCAL_FUNDING_RATES = utils.upsert_list_of_dicts_by_id(LOCAL_FUNDING_RATES, fr_update, id='symbol', seq_check_field=None)
|
||||||
VAL_KEY.set(VK_FUND_RATE_ALL, json.dumps(LOCAL_FUNDING_RATES))
|
VAL_KEY.set(VK_FUND_RATE_ALL, json.dumps(LOCAL_FUNDING_RATES))
|
||||||
# print(f'VK_SAVED: {data}')
|
# print(f'VK_SAVED: {data}')
|
||||||
continue
|
continue
|
||||||
|
|||||||
1
ws_extended_fund_rate_all/.dockerignore
Normal file
1
ws_extended_fund_rate_all/.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
../rust/
|
||||||
19
ws_extended_fund_rate_all/Dockerfile
Normal file
19
ws_extended_fund_rate_all/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
FROM python:3.13-slim
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y build-essential
|
||||||
|
|
||||||
|
RUN gcc --version
|
||||||
|
RUN rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Finally, run gunicorn.
|
||||||
|
CMD [ "python", "ws_extended_fund_rate_all.py"]
|
||||||
|
# CMD [ "gunicorn", "--workers=5", "--threads=1", "-b 0.0.0.0:8000", "app:server"]
|
||||||
@@ -38,7 +38,7 @@ LOG_FILEPATH: str = f'{os.getenv("LOGS_PATH")}/Fund_Rate_Extended_OB.log'
|
|||||||
SYMBOL: str = 'ETH-USD'
|
SYMBOL: str = 'ETH-USD'
|
||||||
|
|
||||||
### Globals ###
|
### Globals ###
|
||||||
ALLOW_SYMBOL_CHG: bool = False
|
ALLOW_SYMBOL_CHG: bool = True
|
||||||
|
|
||||||
### Websocket ###
|
### Websocket ###
|
||||||
async def ws_stream():
|
async def ws_stream():
|
||||||
@@ -56,7 +56,7 @@ async def ws_stream():
|
|||||||
### Update Symbol if Algo Outputs Change ###
|
### Update Symbol if Algo Outputs Change ###
|
||||||
if ALLOW_SYMBOL_CHG:
|
if ALLOW_SYMBOL_CHG:
|
||||||
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_algo_working_symbol')) # ty:ignore[invalid-argument-type]
|
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_algo_working_symbol')) # ty:ignore[invalid-argument-type]
|
||||||
best_symbol: str = f'{best_symbol_by_exchange['EXTEND']['lh_asset']}-{best_symbol_by_exchange['EXTEND']['rh_asset']}'
|
best_symbol: str = best_symbol_by_exchange['EXTEND']['symbol']
|
||||||
if best_symbol != SYMBOL:
|
if best_symbol != SYMBOL:
|
||||||
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
|
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
|
||||||
SYMBOL = best_symbol
|
SYMBOL = best_symbol
|
||||||
|
|||||||
2
ws_extended_orderbook/.dockerignore
Normal file
2
ws_extended_orderbook/.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
../rust/
|
||||||
|
/rust/
|
||||||
@@ -40,7 +40,7 @@ LOG_FILEPATH: str = f'{os.getenv("LOGS_PATH")}/Fund_Rate_Extended_Trades.log'
|
|||||||
SYMBOL: str = 'ETH-USD'
|
SYMBOL: str = 'ETH-USD'
|
||||||
|
|
||||||
### Globals ###
|
### Globals ###
|
||||||
ALLOW_SYMBOL_CHG: bool = False
|
ALLOW_SYMBOL_CHG: bool = True
|
||||||
|
|
||||||
|
|
||||||
### Websocket ###
|
### Websocket ###
|
||||||
@@ -59,7 +59,7 @@ async def ws_stream():
|
|||||||
### Update Symbol if Algo Outputs Change ###
|
### Update Symbol if Algo Outputs Change ###
|
||||||
if ALLOW_SYMBOL_CHG:
|
if ALLOW_SYMBOL_CHG:
|
||||||
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_algo_working_symbol')) # ty:ignore[invalid-argument-type]
|
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_algo_working_symbol')) # ty:ignore[invalid-argument-type]
|
||||||
best_symbol: str = f'{best_symbol_by_exchange['EXTEND']['lh_asset']}-{best_symbol_by_exchange['EXTEND']['rh_asset']}'
|
best_symbol: str = best_symbol_by_exchange['EXTEND']['symbol']
|
||||||
if best_symbol != SYMBOL:
|
if best_symbol != SYMBOL:
|
||||||
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
|
logging.info(f'Symbol Change: {SYMBOL} -> {best_symbol}')
|
||||||
SYMBOL = best_symbol
|
SYMBOL = best_symbol
|
||||||
|
|||||||
1
ws_extended_trades/.dockerignore
Normal file
1
ws_extended_trades/.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
../rust/
|
||||||
@@ -50,12 +50,15 @@ LOCAL_RECENT_TRADES: list = []
|
|||||||
LOCAL_RECENT_BALANCES: list = []
|
LOCAL_RECENT_BALANCES: list = []
|
||||||
LOCAL_RECENT_POSITIONS: list = []
|
LOCAL_RECENT_POSITIONS: list = []
|
||||||
|
|
||||||
|
RESET_SEQ: bool = False
|
||||||
|
|
||||||
### Websocket ###
|
### Websocket ###
|
||||||
async def ws_stream():
|
async def ws_stream():
|
||||||
global LOCAL_RECENT_ORDERS
|
global LOCAL_RECENT_ORDERS
|
||||||
global LOCAL_RECENT_TRADES
|
global LOCAL_RECENT_TRADES
|
||||||
global LOCAL_RECENT_BALANCES
|
global LOCAL_RECENT_BALANCES
|
||||||
global LOCAL_RECENT_POSITIONS
|
global LOCAL_RECENT_POSITIONS
|
||||||
|
global RESET_SEQ
|
||||||
|
|
||||||
async for websocket in websockets.connect(WSS_URL, extra_headers={'X-Api-Key': API_KEY}):
|
async for websocket in websockets.connect(WSS_URL, extra_headers={'X-Api-Key': API_KEY}):
|
||||||
logging.info(f"Connected to {WSS_URL}")
|
logging.info(f"Connected to {WSS_URL}")
|
||||||
@@ -63,7 +66,7 @@ async def ws_stream():
|
|||||||
async for message in websocket:
|
async for message in websocket:
|
||||||
ts_arrival = round(datetime.now().timestamp()*1000)
|
ts_arrival = round(datetime.now().timestamp()*1000)
|
||||||
if isinstance(message, str):
|
if isinstance(message, str):
|
||||||
try:
|
try:
|
||||||
data = json.loads(message)
|
data = json.loads(message)
|
||||||
channel = data.get('type', None)
|
channel = data.get('type', None)
|
||||||
if channel is not None:
|
if channel is not None:
|
||||||
@@ -101,14 +104,13 @@ async def ws_stream():
|
|||||||
'expire_time_ts': o['expireTime'],
|
'expire_time_ts': o['expireTime'],
|
||||||
}
|
}
|
||||||
list_for_df.append(order_update)
|
list_for_df.append(order_update)
|
||||||
LOCAL_RECENT_ORDERS = utils.upsert_list_of_dicts_by_id(LOCAL_RECENT_ORDERS, order_update, id='order_id', seq_check_field='sequence_id')
|
LOCAL_RECENT_ORDERS = utils.upsert_list_of_dicts_by_id(LOCAL_RECENT_ORDERS, order_update, id='order_id', seq_check_field='sequence_id', reset_seq_id=RESET_SEQ)
|
||||||
LOCAL_RECENT_ORDERS = [t for t in LOCAL_RECENT_ORDERS if t.get('timestamp_arrival', 0) >= LOOKBACK_MIN_TS_MS]
|
LOCAL_RECENT_ORDERS = [t for t in LOCAL_RECENT_ORDERS if t.get('timestamp_arrival', 0) >= LOOKBACK_MIN_TS_MS]
|
||||||
|
|
||||||
VAL_KEY_OBJ = json.dumps(LOCAL_RECENT_ORDERS)
|
VAL_KEY_OBJ = json.dumps(LOCAL_RECENT_ORDERS)
|
||||||
VAL_KEY.publish(channel=VK_ORDERS, message=VAL_KEY_OBJ)
|
VAL_KEY.publish(channel=VK_ORDERS, message=VAL_KEY_OBJ)
|
||||||
VAL_KEY.set(name=VK_ORDERS, value=VAL_KEY_OBJ)
|
VAL_KEY.set(name=VK_ORDERS, value=VAL_KEY_OBJ)
|
||||||
await db.insert_df_to_mysql(table_name='fr_extended_user_order', params=list_for_df, CON=CON)
|
await db.insert_df_to_mysql(table_name='fr_extended_user_order', params=list_for_df, CON=CON)
|
||||||
continue
|
|
||||||
case 'TRADE':
|
case 'TRADE':
|
||||||
list_for_df = []
|
list_for_df = []
|
||||||
for t in data['data']['trades']:
|
for t in data['data']['trades']:
|
||||||
@@ -132,13 +134,12 @@ async def ws_stream():
|
|||||||
'is_taker': t['isTaker'],
|
'is_taker': t['isTaker'],
|
||||||
}
|
}
|
||||||
list_for_df.append(trade_update)
|
list_for_df.append(trade_update)
|
||||||
LOCAL_RECENT_TRADES = utils.upsert_list_of_dicts_by_id(LOCAL_RECENT_TRADES, trade_update, id='trade_id', seq_check_field='sequence_id')
|
LOCAL_RECENT_TRADES = utils.upsert_list_of_dicts_by_id(LOCAL_RECENT_TRADES, trade_update, id='trade_id', seq_check_field='sequence_id', reset_seq_id=RESET_SEQ)
|
||||||
LOCAL_RECENT_TRADES = [t for t in LOCAL_RECENT_TRADES if t.get('timestamp_arrival', 0) >= LOOKBACK_MIN_TS_MS]
|
LOCAL_RECENT_TRADES = [t for t in LOCAL_RECENT_TRADES if t.get('timestamp_arrival', 0) >= LOOKBACK_MIN_TS_MS]
|
||||||
|
|
||||||
VAL_KEY_OBJ = json.dumps(LOCAL_RECENT_TRADES)
|
VAL_KEY_OBJ = json.dumps(LOCAL_RECENT_TRADES)
|
||||||
VAL_KEY.set(VK_TRADES, VAL_KEY_OBJ)
|
VAL_KEY.set(VK_TRADES, VAL_KEY_OBJ)
|
||||||
await db.insert_df_to_mysql(table_name='fr_extended_user_trade', params=list_for_df, CON=CON)
|
await db.insert_df_to_mysql(table_name='fr_extended_user_trade', params=list_for_df, CON=CON)
|
||||||
continue
|
|
||||||
case 'BALANCE':
|
case 'BALANCE':
|
||||||
balance_update = {
|
balance_update = {
|
||||||
'sequence_id': data['seq'],
|
'sequence_id': data['seq'],
|
||||||
@@ -157,13 +158,12 @@ async def ws_stream():
|
|||||||
'exposure': float(data['data']['balance']['exposure']),
|
'exposure': float(data['data']['balance']['exposure']),
|
||||||
'leverage': float(data['data']['balance']['leverage']),
|
'leverage': float(data['data']['balance']['leverage']),
|
||||||
}
|
}
|
||||||
LOCAL_RECENT_BALANCES = utils.upsert_list_of_dicts_by_id(LOCAL_RECENT_BALANCES, balance_update, id='collateral_name', seq_check_field='sequence_id')
|
LOCAL_RECENT_BALANCES = utils.upsert_list_of_dicts_by_id(LOCAL_RECENT_BALANCES, balance_update, id='collateral_name', seq_check_field='sequence_id', reset_seq_id=RESET_SEQ)
|
||||||
LOCAL_RECENT_BALANCES = [t for t in LOCAL_RECENT_BALANCES if t.get('timestamp_arrival', 0) >= LOOKBACK_MIN_TS_MS]
|
LOCAL_RECENT_BALANCES = [t for t in LOCAL_RECENT_BALANCES if t.get('timestamp_arrival', 0) >= LOOKBACK_MIN_TS_MS]
|
||||||
|
|
||||||
VAL_KEY_OBJ = json.dumps(LOCAL_RECENT_BALANCES)
|
VAL_KEY_OBJ = json.dumps(LOCAL_RECENT_BALANCES)
|
||||||
VAL_KEY.set(VK_BALANCES, VAL_KEY_OBJ)
|
VAL_KEY.set(VK_BALANCES, VAL_KEY_OBJ)
|
||||||
await db.insert_df_to_mysql(table_name='fr_extended_user_balance', params=balance_update, CON=CON)
|
await db.insert_df_to_mysql(table_name='fr_extended_user_balance', params=balance_update, CON=CON)
|
||||||
continue
|
|
||||||
case 'POSITION':
|
case 'POSITION':
|
||||||
list_for_df = []
|
list_for_df = []
|
||||||
for p in data['data']['positions']:
|
for p in data['data']['positions']:
|
||||||
@@ -194,31 +194,40 @@ async def ws_stream():
|
|||||||
'updated_at_ts': p['updatedAt'],
|
'updated_at_ts': p['updatedAt'],
|
||||||
}
|
}
|
||||||
list_for_df.append(position_update)
|
list_for_df.append(position_update)
|
||||||
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 = utils.upsert_list_of_dicts_by_id(LOCAL_RECENT_POSITIONS, position_update, id='market', seq_check_field='sequence_id', reset_seq_id=RESET_SEQ)
|
||||||
LOCAL_RECENT_POSITIONS = [t for t in LOCAL_RECENT_POSITIONS if t.get('timestamp_arrival', 0) >= LOOKBACK_MIN_TS_MS]
|
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)
|
VAL_KEY_OBJ = json.dumps(LOCAL_RECENT_POSITIONS)
|
||||||
VAL_KEY.publish(channel=VK_POSITIONS, message=VAL_KEY_OBJ)
|
VAL_KEY.publish(channel=VK_POSITIONS, message=VAL_KEY_OBJ)
|
||||||
VAL_KEY.set(name=VK_POSITIONS, value=VAL_KEY_OBJ)
|
VAL_KEY.set(name=VK_POSITIONS, value=VAL_KEY_OBJ)
|
||||||
await db.insert_df_to_mysql(table_name='fr_extended_user_position', params=list_for_df, CON=CON)
|
await db.insert_df_to_mysql(table_name='fr_extended_user_position', params=list_for_df, CON=CON)
|
||||||
continue
|
|
||||||
case _:
|
case _:
|
||||||
logging.warning(f'UNMATCHED OTHER MSG: {data}')
|
logging.warning(f'UNMATCHED OTHER MSG: {data}')
|
||||||
|
RESET_SEQ = True
|
||||||
|
|
||||||
|
### END OF GOOD MAIN LOOP - SEQ WILL HAVE BEEN RESET IF A FAILURE HAD OCCURRED; FLIPPING BOOL BACK TO NORMAL ###
|
||||||
|
RESET_SEQ = False
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
logging.info(f'Initial or unexpected data struct, skipping: {data}')
|
logging.info(f'Initial or unexpected data struct, skipping: {data}')
|
||||||
|
RESET_SEQ = True
|
||||||
continue
|
continue
|
||||||
except (json.JSONDecodeError, ValueError):
|
except (json.JSONDecodeError, ValueError):
|
||||||
logging.warning(f'Message not in JSON format, skipping: {message}')
|
logging.warning(f'Message not in JSON format, skipping: {message}')
|
||||||
|
RESET_SEQ = True
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
|
RESET_SEQ = True
|
||||||
raise ValueError(f'Type: {type(data)} not expected: {message}')
|
raise ValueError(f'Type: {type(data)} not expected: {message}')
|
||||||
except websockets.ConnectionClosed as e:
|
except websockets.ConnectionClosed as e:
|
||||||
logging.error(f'Connection closed: {e}')
|
logging.error(f'Connection closed: {e}')
|
||||||
logging.error(traceback.format_exc())
|
logging.error(traceback.format_exc())
|
||||||
|
RESET_SEQ = True
|
||||||
continue
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f'Connection closed: {e}')
|
logging.error(f'Connection closed: {e}')
|
||||||
logging.error(traceback.format_exc())
|
logging.error(traceback.format_exc())
|
||||||
|
RESET_SEQ = True
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
|
|||||||
1
ws_extended_user/.dockerignore
Normal file
1
ws_extended_user/.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
../rust/
|
||||||
Reference in New Issue
Block a user