bug fixes
This commit is contained in:
13
_On_Ice/algo_config_backup.json
Normal file
13
_On_Ice/algo_config_backup.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"Config_Updated_Timestamp": 1777098091913,
|
||||||
|
"Allow_Ordering_Aster": false,
|
||||||
|
"Allow_Ordering_Extend": false,
|
||||||
|
"Loop_Sleep_Sec": 5.00,
|
||||||
|
"Max_Target_Notional": 0.00,
|
||||||
|
"Min_Time_To_Funding_Minutes": 10,
|
||||||
|
"Price_Worsener_Aster": 0.0,
|
||||||
|
"Price_Worsener_Extend": 0.0,
|
||||||
|
"Target_Open_Cash_Position": 10,
|
||||||
|
"Print_Summary_Each_Loop" : true,
|
||||||
|
"Flip_Side_For_Testing": false
|
||||||
|
}
|
||||||
194
algo.ipynb
194
algo.ipynb
@@ -2,7 +2,7 @@
|
|||||||
"cells": [
|
"cells": [
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 7,
|
"execution_count": 76,
|
||||||
"id": "d1eed397",
|
"id": "d1eed397",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 8,
|
"execution_count": 77,
|
||||||
"id": "c6151613",
|
"id": "c6151613",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 31,
|
"execution_count": null,
|
||||||
"id": "d83c61e5",
|
"id": "d83c61e5",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -38,87 +38,50 @@
|
|||||||
"1"
|
"1"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 31,
|
"execution_count": 131,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"config_update = {\n",
|
"config_update = {\n",
|
||||||
" 'Min_Time_To_Funding_Minutes': 60,\n",
|
" 'Min_Time_To_Funding_Minutes': 7,\n",
|
||||||
" 'Print_Summary_Each_Loop': True,\n",
|
" # 'Allow_Ordering_Aster': True,\n",
|
||||||
" 'Allow_Ordering_Aster': True,\n",
|
" # 'Allow_Ordering_Extend': True,\n",
|
||||||
" 'Allow_Ordering_Extend': True,\n",
|
" 'Loop_Sleep_Sec': 0.00,\n",
|
||||||
" 'Loop_Sleep_Sec': 0.0,\n",
|
"# 'Flip_Side_For_Testing': False,\n",
|
||||||
" 'Flip_Side_For_Testing': False,\n",
|
"# 'Price_Worsener_Extend': 0.0,\n",
|
||||||
" 'Price_Worsener_Extend': 0.0,\n",
|
" 'Log_Summary_Each_Loop': False,\n",
|
||||||
|
" 'Print_Summary_Each_Loop': False,\n",
|
||||||
"}\n",
|
"}\n",
|
||||||
"VAL_KEY.publish('fr_orchestrator_input', json.dumps(config_update))"
|
"VAL_KEY.publish('fr_orchestrator_input', json.dumps(config_update))"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 93,
|
||||||
"id": "d2fdd7d2",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 11,
|
|
||||||
"id": "45fae761",
|
"id": "45fae761",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"Algo_Config(Config_Updated_Timestamp=1777151524162, Allow_Ordering_Aster=True, Allow_Ordering_Extend=True, Loop_Sleep_Sec=1, Max_Target_Notional=0.0, Min_Time_To_Funding_Minutes=60, Price_Worsener_Aster=0.0, Price_Worsener_Extend=0.0, Target_Open_Cash_Position=10)"
|
"5.0"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 11,
|
"execution_count": 93,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"json.loads(VAL_KEY.get('fr_orchestrator_output'), object_hook=lambda d: structs.Algo_Config(**d))"
|
"json.loads(VAL_KEY.get('fr_orchestrator_output'), object_hook=lambda d: structs.Algo_Config(**d)).Loop_Sleep_Sec"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 9,
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"data": {
|
|
||||||
"text/plain": [
|
|
||||||
"{'Config_Updated_Timestamp': 1777098091913,\n",
|
|
||||||
" 'Allow_Ordering_Aster': False,\n",
|
|
||||||
" 'Allow_Ordering_Extend': False,\n",
|
|
||||||
" 'Loop_Sleep_Sec': 1,\n",
|
|
||||||
" 'Max_Target_Notional': 0.0,\n",
|
|
||||||
" 'Min_Time_To_Funding_Minutes': 60,\n",
|
|
||||||
" 'Price_Worsener_Aster': 0.0,\n",
|
|
||||||
" 'Price_Worsener_Extend': 0.0,\n",
|
|
||||||
" 'Target_Open_Cash_Position': 10,\n",
|
|
||||||
" 'Print_Summary_Each_Loop': False,\n",
|
|
||||||
" 'Flip_Side_For_Testing': False}"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"execution_count": 9,
|
|
||||||
"metadata": {},
|
|
||||||
"output_type": "execute_result"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
|
||||||
"asdict(ALGO_CONFIG)"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
"id": "d2e26271",
|
"id": "98c500cc",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": []
|
"source": []
|
||||||
@@ -126,9 +89,130 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
|
"id": "f2cf3325",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 31,
|
||||||
"id": "a0df43de",
|
"id": "a0df43de",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"pos = json.loads(VAL_KEY.get('fr_aster_user_positions'))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 32,
|
||||||
|
"id": "ca526c8a",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"[{'timestamp_arrival': 1777303258987,\n",
|
||||||
|
" 'timestamp_msg': 1777303258979,\n",
|
||||||
|
" 'timestamp_transaction': 1777303258950,\n",
|
||||||
|
" 'event_reason_type': 'ORDER',\n",
|
||||||
|
" 'symbol': 'ETHUSDT',\n",
|
||||||
|
" 'position_amount': 0.226,\n",
|
||||||
|
" 'entry_price': 2284.28,\n",
|
||||||
|
" 'accumulated_realized_pre_fees': 8.24392002,\n",
|
||||||
|
" 'unrealized_pnl': 0.0,\n",
|
||||||
|
" 'margin_type': 'cross',\n",
|
||||||
|
" 'isolated_wallet': 0.0,\n",
|
||||||
|
" 'position_side': 'BOTH'}]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 32,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"pos"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 33,
|
||||||
|
"id": "f788b6df",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Timestamp('2026-04-27 15:20:58.987000')"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 33,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"import pandas as pd\n",
|
||||||
|
"pd.to_datetime(1777303258987, unit='ms')"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 34,
|
||||||
|
"id": "855f980b",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Timestamp('2026-04-27 15:20:58.979000')"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 34,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"import pandas as pd\n",
|
||||||
|
"pd.to_datetime(1777303258979, unit='ms')"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 35,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Timestamp('2026-04-27 15:20:58.950000')"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 35,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"import pandas as pd\n",
|
||||||
|
"pd.to_datetime(1777303258950, unit='ms')"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
"source": []
|
"source": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -155,7 +239,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.13.12"
|
"version": "3.13.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
|
|||||||
@@ -15,5 +15,5 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Finally, run gunicorn.
|
# Finally, run gunicorn.
|
||||||
CMD [ "python", "-u" ,"main.py"]
|
CMD [ "python", "main.py"]
|
||||||
# CMD [ "gunicorn", "--workers=5", "--threads=1", "-b 0.0.0.0:8000", "app:server"]
|
# CMD [ "gunicorn", "--workers=5", "--threads=1", "-b 0.0.0.0:8000", "app:server"]
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
{
|
{
|
||||||
"Config_Updated_Timestamp": 1777098091913,
|
"Config_Updated_Timestamp": 1777312620005,
|
||||||
"Allow_Ordering_Aster": true,
|
"Allow_Ordering_Aster": true,
|
||||||
"Allow_Ordering_Extend": true,
|
"Allow_Ordering_Extend": true,
|
||||||
"Loop_Sleep_Sec": 0.00,
|
"Loop_Sleep_Sec": 0.0,
|
||||||
"Max_Target_Notional": 0.00,
|
"Max_Target_Notional": 0.0,
|
||||||
"Min_Time_To_Funding_Minutes": 10,
|
"Min_Time_To_Funding_Minutes": 7,
|
||||||
"Price_Worsener_Aster": 0.0,
|
"Price_Worsener_Aster": 0.0,
|
||||||
"Price_Worsener_Extend": 0.0,
|
"Price_Worsener_Extend": 0.0,
|
||||||
"Target_Open_Cash_Position": 10,
|
"Target_Open_Cash_Position": 10,
|
||||||
"Print_Summary_Each_Loop" : false,
|
"Log_Summary_Each_Loop": false,
|
||||||
|
"Print_Summary_Each_Loop": false,
|
||||||
"Flip_Side_For_Testing": false
|
"Flip_Side_For_Testing": false
|
||||||
}
|
}
|
||||||
@@ -42,14 +42,19 @@ async def orchestrator() -> None:
|
|||||||
timestamp = round(datetime.now().timestamp()*1000)
|
timestamp = round(datetime.now().timestamp()*1000)
|
||||||
data = json.loads(message['data'])
|
data = json.loads(message['data'])
|
||||||
# channel = message['channel']
|
# channel = message['channel']
|
||||||
|
|
||||||
|
with open('/algo_local_drive/algo_config.json', 'r', encoding='utf-8') as f:
|
||||||
|
# ALGO_CONFIG = json.load(f, object_hook=lambda d: Algo_Config(**d))
|
||||||
|
ALGO_CONFIG = json.load(f)
|
||||||
|
ALGO_CONFIG['Config_Updated_Timestamp'] = timestamp
|
||||||
|
|
||||||
for k, v in data.items():
|
for k, v in data.items():
|
||||||
if ALGO_CONFIG.get(k, None) is not None:
|
if ALGO_CONFIG.get(k, None) is not None:
|
||||||
ALGO_CONFIG[k] = v
|
ALGO_CONFIG[k] = v
|
||||||
|
|
||||||
ALGO_CONFIG['Config_Updated_Timestamp'] = timestamp
|
|
||||||
VAL_KEY.set(VK_OUT, json.dumps(ALGO_CONFIG))
|
VAL_KEY.set(VK_OUT, json.dumps(ALGO_CONFIG))
|
||||||
with open('algo_config.json', 'w', encoding='utf-8') as f:
|
with open('/algo_local_drive/algo_config.json', 'w', encoding='utf-8') as f:
|
||||||
|
# print('SAVING FILE')
|
||||||
json.dump(ALGO_CONFIG, f, indent=4)
|
json.dump(ALGO_CONFIG, f, indent=4)
|
||||||
print(f"Algo Config Updated @ {timestamp}; {data}")
|
print(f"Algo Config Updated @ {timestamp}; {data}")
|
||||||
|
|
||||||
@@ -70,7 +75,7 @@ async def main() -> None:
|
|||||||
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')
|
||||||
|
|
||||||
with open('algo_config.json', 'r', encoding='utf-8') as f:
|
with open('/algo_local_drive/algo_config.json', 'r', encoding='utf-8') as f:
|
||||||
# ALGO_CONFIG = json.load(f, object_hook=lambda d: Algo_Config(**d))
|
# ALGO_CONFIG = json.load(f, object_hook=lambda d: Algo_Config(**d))
|
||||||
ALGO_CONFIG = json.load(f)
|
ALGO_CONFIG = json.load(f)
|
||||||
ALGO_CONFIG['Config_Updated_Timestamp'] = round(datetime.now().timestamp()*1000)
|
ALGO_CONFIG['Config_Updated_Timestamp'] = round(datetime.now().timestamp()*1000)
|
||||||
|
|||||||
@@ -15,5 +15,5 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Finally, run gunicorn.
|
# Finally, run gunicorn.
|
||||||
CMD [ "python", "-u" ,"algo_orchestrator.py"]
|
CMD [ "python", "algo_orchestrator.py"]
|
||||||
# CMD [ "gunicorn", "--workers=5", "--threads=1", "-b 0.0.0.0:8000", "app:server"]
|
# CMD [ "gunicorn", "--workers=5", "--threads=1", "-b 0.0.0.0:8000", "app:server"]
|
||||||
15
docker-compose-algo.yml
Normal file
15
docker-compose-algo.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# tail -f Fund_Rate_Algo.log
|
||||||
|
# docker compose -f docker-compose-algo.yml up --build
|
||||||
|
|
||||||
|
services:
|
||||||
|
algo:
|
||||||
|
container_name: algo
|
||||||
|
restart: "no"
|
||||||
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: ./algo/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
|
||||||
|
- ./:/algo_local_drive:rw # Read-write access to data
|
||||||
|
network_mode: "host"
|
||||||
@@ -1,23 +1,24 @@
|
|||||||
# tail -f 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
|
# tail -f 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
|
||||||
|
|
||||||
services:
|
services:
|
||||||
algo:
|
# algo:
|
||||||
container_name: algo
|
# container_name: algo
|
||||||
restart: "no"
|
# restart: "no"
|
||||||
build:
|
# build:
|
||||||
context: ./
|
# context: ./
|
||||||
dockerfile: ./algo/Dockerfile
|
# dockerfile: ./algo/Dockerfile
|
||||||
depends_on:
|
# depends_on:
|
||||||
- algo_orchestrator
|
# - algo_orchestrator
|
||||||
- ws_aster
|
# - ws_aster
|
||||||
- ws_aster_user
|
# - ws_aster_user
|
||||||
- ws_extended_fund_rate
|
# - ws_extended_fund_rate
|
||||||
- ws_extended_orderbook
|
# - ws_extended_orderbook
|
||||||
- ws_extended_user
|
# - ws_extended_trades
|
||||||
volumes:
|
# - ws_extended_user
|
||||||
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data
|
# volumes:
|
||||||
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
|
# - /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data
|
||||||
network_mode: "host"
|
# - /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
|
||||||
|
# network_mode: "host"
|
||||||
algo_orchestrator:
|
algo_orchestrator:
|
||||||
container_name: algo_orchestrator
|
container_name: algo_orchestrator
|
||||||
restart: "unless-stopped"
|
restart: "unless-stopped"
|
||||||
@@ -27,6 +28,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data
|
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to data
|
||||||
- /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
|
||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
ws_aster:
|
ws_aster:
|
||||||
container_name: ws_aster
|
container_name: ws_aster
|
||||||
@@ -68,6 +70,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:
|
||||||
|
container_name: ws_extended_trades
|
||||||
|
restart: "unless-stopped"
|
||||||
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: ./ws_extended_trades/Dockerfile
|
||||||
|
volumes:
|
||||||
|
- /home/ubuntu/data:/home/ubuntu/data:rw # Read-write access to dataw
|
||||||
|
- /home/ubuntu/logs:/home/ubuntu/logs:rw # Read-write access to data
|
||||||
|
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"
|
||||||
|
|||||||
0
engine_health.py
Normal file
0
engine_health.py
Normal file
76
main.py
76
main.py
@@ -38,6 +38,7 @@ LOG_FILEPATH: str = os.getenv("LOGS_PATH") + '/Fund_Rate_Algo.log'
|
|||||||
|
|
||||||
### Algo Config ###
|
### Algo Config ###
|
||||||
ALGO_CONFIG: structs.Algo_Config = None
|
ALGO_CONFIG: structs.Algo_Config = None
|
||||||
|
MIN_TIME_TO_FUNDING: int
|
||||||
|
|
||||||
### CONSTANTS ###
|
### CONSTANTS ###
|
||||||
ASTER = structs.Perpetual_Exchange(
|
ASTER = structs.Perpetual_Exchange(
|
||||||
@@ -64,6 +65,12 @@ EXTEND_AVAIL_COLLATERAL = 0
|
|||||||
ASTER_NOTIONAL_POSITION = 0
|
ASTER_NOTIONAL_POSITION = 0
|
||||||
EXTEND_NOTIONAL_POSITION = 0
|
EXTEND_NOTIONAL_POSITION = 0
|
||||||
|
|
||||||
|
ASTER_NOTIONAL_OBJ: dict | None = None
|
||||||
|
EXTEND_NOTIONAL_OBJ: dict | None = None
|
||||||
|
|
||||||
|
ASTER_UNREALIZED_PNL = 0
|
||||||
|
EXTEND_UNREALIZED_PNL = 0
|
||||||
|
|
||||||
ASTER_OPEN_ORDERS = []
|
ASTER_OPEN_ORDERS = []
|
||||||
EXTEND_OPEN_ORDERS = []
|
EXTEND_OPEN_ORDERS = []
|
||||||
|
|
||||||
@@ -111,9 +118,13 @@ async def get_aster_collateral():
|
|||||||
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_notional_position(resp: dict | None = None):
|
async def get_aster_notional_position(resp: dict | None = None):
|
||||||
|
global ASTER_NOTIONAL_OBJ
|
||||||
global ASTER_NOTIONAL_POSITION
|
global ASTER_NOTIONAL_POSITION
|
||||||
|
global ASTER_UNREALIZED_PNL
|
||||||
global ASTER_MULT
|
global ASTER_MULT
|
||||||
|
|
||||||
|
previous_notional_obj = ASTER_NOTIONAL_OBJ
|
||||||
|
|
||||||
if not resp:
|
if not resp:
|
||||||
fut_acct_positionRisk = {
|
fut_acct_positionRisk = {
|
||||||
"url": "/fapi/v3/positionRisk",
|
"url": "/fapi/v3/positionRisk",
|
||||||
@@ -123,29 +134,38 @@ async def get_aster_notional_position(resp: dict | None = None):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
resp = await aster_auth.post_authenticated_url(fut_acct_positionRisk)
|
resp = await aster_auth.post_authenticated_url(fut_acct_positionRisk)
|
||||||
|
d = [x for x in resp if x.get('symbol', None) == ASTER.symbol][0]
|
||||||
|
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['timestamp_arrival'] > d['timestamp_arrival']:
|
||||||
|
# logging.info(f'ASTER NOTIONAL: prev timestamp ({pd.to_datetime(previous_notional_obj['timestamp_arrival'], unit='ms')}) > new timestamp ({pd.to_datetime(d['timestamp_arrival'], unit='ms')}); skipping')
|
||||||
|
return
|
||||||
|
|
||||||
d = [x for x in resp if x.get('symbol', None) == ASTER.symbol][0]
|
ASTER_NOTIONAL_OBJ = d
|
||||||
|
|
||||||
if len(d) < 1:
|
if len(d) < 1:
|
||||||
logging.info(f'BAD NOTIONAL - ASTER CHANGE: Empty d: {d}; resp: {resp}')
|
logging.info(f'BAD NOTIONAL - ASTER CHANGE: Empty d: {d}; resp: {resp}')
|
||||||
await kill_algo()
|
await kill_algo()
|
||||||
|
|
||||||
aster_unrealized_pnl = float(d['unrealized_pnl']) if d.get('unrealized_pnl') is not None else float(d['unRealizedProfit'])
|
ASTER_UNREALIZED_PNL = float(d['unrealized_pnl']) if d.get('unrealized_pnl') is not None else float(d['unRealizedProfit'])
|
||||||
|
|
||||||
if d.get('notional') is not None:
|
if d.get('notional') is not None:
|
||||||
notional = float(d['notional'])
|
ASTER_NOTIONAL_POSITION = float(d['notional']) - ASTER_UNREALIZED_PNL
|
||||||
else:
|
else:
|
||||||
notional = 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
|
previous_notional_position = ASTER_NOTIONAL_POSITION
|
||||||
ASTER_NOTIONAL_POSITION = notional - aster_unrealized_pnl
|
# if not resp: # this can never evaluate
|
||||||
if not resp:
|
# ASTER_MULT = float(d['leverage'])
|
||||||
ASTER_MULT = float(d['leverage'])
|
# if abs(ASTER_NOTIONAL_POSITION) > ALGO_CONFIG.Max_Target_Notional*1.01:
|
||||||
if abs(ASTER_NOTIONAL_POSITION) > ALGO_CONFIG.Max_Target_Notional*1.01:
|
if abs(ASTER_NOTIONAL_POSITION) > ALGO_CONFIG.Max_Target_Notional*2.01:
|
||||||
logging.info(f'BAD NOTIONAL - ASTER CHANGE: {ASTER_NOTIONAL_POSITION}; UR PNL: {aster_unrealized_pnl}; MULT: {ASTER_MULT}; d: {d}; resp: {resp}')
|
logging.info(f'BAD NOTIONAL - ASTER CHANGE: {previous_notional_position} -> {ASTER_NOTIONAL_POSITION}; UR PNL: {ASTER_UNREALIZED_PNL}; MULT: {ASTER_MULT}; d: {d}; resp: {resp}')
|
||||||
await kill_algo()
|
await kill_algo()
|
||||||
if ASTER_NOTIONAL_POSITION != previous_notional_position:
|
# if ASTER_NOTIONAL_POSITION != previous_notional_position:
|
||||||
logging.info(f'ASTER NOTIONAL CHANGE: {previous_notional_position} -> {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} -> {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
|
||||||
@@ -155,6 +175,7 @@ async def get_extend_collateral():
|
|||||||
|
|
||||||
async def get_extend_notional(resp: dict | None = None):
|
async def get_extend_notional(resp: dict | None = None):
|
||||||
global EXTEND_NOTIONAL_POSITION
|
global EXTEND_NOTIONAL_POSITION
|
||||||
|
global EXTEND_UNREALIZED_PNL
|
||||||
global EXTEND_MULT
|
global EXTEND_MULT
|
||||||
|
|
||||||
if not resp:
|
if not resp:
|
||||||
@@ -166,7 +187,7 @@ async def get_extend_notional(resp: dict | None = None):
|
|||||||
logging.info('get_extend_notional - No Positions')
|
logging.info('get_extend_notional - No Positions')
|
||||||
else:
|
else:
|
||||||
pos_dict = pos_dict[0]
|
pos_dict = pos_dict[0]
|
||||||
unrealized_pnl = pos_dict.get('unrealised_pnl', 0)
|
EXTEND_UNREALIZED_PNL = pos_dict.get('unrealised_pnl', 0)
|
||||||
previous_notional_position = EXTEND_NOTIONAL_POSITION
|
previous_notional_position = EXTEND_NOTIONAL_POSITION
|
||||||
position_side = pos_dict['side'] # LONG or SHORT
|
position_side = pos_dict['side'] # LONG or SHORT
|
||||||
notional_pos_abs = abs(float(pos_dict['value']))
|
notional_pos_abs = abs(float(pos_dict['value']))
|
||||||
@@ -177,10 +198,13 @@ async def get_extend_notional(resp: dict | None = None):
|
|||||||
else:
|
else:
|
||||||
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(unrealized_pnl)
|
EXTEND_NOTIONAL_POSITION = notional_pos_sided - float(EXTEND_UNREALIZED_PNL)
|
||||||
EXTEND_MULT = pos_dict.get('leverage', EXTEND_MULT)
|
EXTEND_MULT = pos_dict.get('leverage', EXTEND_MULT)
|
||||||
|
if abs(EXTEND_NOTIONAL_POSITION) > ALGO_CONFIG.Max_Target_Notional*2.01:
|
||||||
|
logging.info(f'BAD NOTIONAL - EXTEND CHANGE: {previous_notional_position} -> {EXTEND_NOTIONAL_POSITION}; UR PNL: {EXTEND_UNREALIZED_PNL}; MULT: {EXTEND_MULT}; d: {pos_dict}; resp: {resp}')
|
||||||
|
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: {unrealized_pnl:.2f}; MULT: {EXTEND_MULT:.0f}; resp: {bool(resp)}')
|
logging.info(f'EXTEND NOTIONAL CHANGE: {previous_notional_position} -> {EXTEND_NOTIONAL_POSITION:.2f}; UR PNL: {EXTEND_UNREALIZED_PNL:.2f}; MULT: {EXTEND_MULT:.0f}; resp: {bool(resp)}')
|
||||||
|
|
||||||
### EXCHANGE INFO ###
|
### EXCHANGE INFO ###
|
||||||
async def get_aster_exch_info():
|
async def get_aster_exch_info():
|
||||||
@@ -226,9 +250,11 @@ async def kill_algo():
|
|||||||
logging.info('ALGO KILL FLAG ACTIVATED; CANCELLING OPEN ORDERS AND SHUTTING DOWN')
|
logging.info('ALGO KILL FLAG ACTIVATED; CANCELLING OPEN ORDERS AND SHUTTING DOWN')
|
||||||
raise ValueError('KILL FLAG ACTIVATED')
|
raise ValueError('KILL FLAG ACTIVATED')
|
||||||
|
|
||||||
|
|
||||||
### ALGO LOOP ###
|
### ALGO LOOP ###
|
||||||
async def run_algo():
|
async def run_algo():
|
||||||
global ALGO_CONFIG
|
global ALGO_CONFIG
|
||||||
|
global MIN_TIME_TO_FUNDING
|
||||||
global ASTER_OPEN_ORDERS
|
global ASTER_OPEN_ORDERS
|
||||||
global EXTEND_OPEN_ORDERS
|
global EXTEND_OPEN_ORDERS
|
||||||
|
|
||||||
@@ -288,13 +314,15 @@ async def run_algo():
|
|||||||
now_ms = round(datetime.now().timestamp()*1000)
|
now_ms = round(datetime.now().timestamp()*1000)
|
||||||
time_to_funding_ms = min([ASTER_FUND_RATE_TIME, EXTEND_FUND_RATE_TIME]) - now_ms
|
time_to_funding_ms = min([ASTER_FUND_RATE_TIME, EXTEND_FUND_RATE_TIME]) - now_ms
|
||||||
if ( time_to_funding_ms > MIN_TIME_TO_FUNDING ) and (not ASTER_OPEN_ORDERS) and (not EXTEND_OPEN_ORDERS):
|
if ( time_to_funding_ms > MIN_TIME_TO_FUNDING ) and (not ASTER_OPEN_ORDERS) and (not EXTEND_OPEN_ORDERS):
|
||||||
print(f'Outside action window (minutes) and no active order (sleeping for 5 sec): {pd.to_datetime(time_to_funding_ms, unit='ms').minute} > {pd.to_datetime(MIN_TIME_TO_FUNDING, unit='ms').minute}')
|
logging.info(f'Outside action window (minutes) and no active order (sleeping for 5 sec): {pd.to_datetime(time_to_funding_ms, unit='ms').minute} > {pd.to_datetime(MIN_TIME_TO_FUNDING, unit='ms').minute}')
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if len(ASTER_WS_POS_UPDATES) > 0:
|
if len(ASTER_WS_POS_UPDATES) > 0:
|
||||||
|
# await get_aster_notional_position()
|
||||||
await get_aster_notional_position(resp=ASTER_WS_POS_UPDATES)
|
await get_aster_notional_position(resp=ASTER_WS_POS_UPDATES)
|
||||||
###### *** returned 0 notional even though had a position, need to handle and safety check to not order above max notional.
|
###### *** returned 0 notional even though had a position, need to handle and safety check to not order above max notional.
|
||||||
|
##### NEED TO UPDATE SO IT TAKES THE LATEST MSG, ie drop the WS msg if its older than the exisiting one from the API.
|
||||||
|
|
||||||
if len(EXTEND_WS_POS_UPDATES) > 0:
|
if len(EXTEND_WS_POS_UPDATES) > 0:
|
||||||
await get_extend_notional(resp=EXTEND_WS_POS_UPDATES)
|
await get_extend_notional(resp=EXTEND_WS_POS_UPDATES)
|
||||||
@@ -304,7 +332,7 @@ async def run_algo():
|
|||||||
if ASTER_WS_ORDER_UPDATES is not None:
|
if ASTER_WS_ORDER_UPDATES is not None:
|
||||||
for idx, o in enumerate(ASTER_OPEN_ORDERS):
|
for idx, o in enumerate(ASTER_OPEN_ORDERS):
|
||||||
order_id = o.get('order_id') if o.get('order_id') is not None else o['orderId']
|
order_id = o.get('order_id') if o.get('order_id') is not None else o['orderId']
|
||||||
order_orig_status = o['status'] ### Got a keyerror on this
|
order_orig_status = o.get('status') if o.get('status') is not None else o['order_status']
|
||||||
order_update = [ou for ou in ASTER_WS_ORDER_UPDATES if ou.get('order_id', None) == order_id]
|
order_update = [ou for ou in ASTER_WS_ORDER_UPDATES if ou.get('order_id', None) == order_id]
|
||||||
|
|
||||||
if len(order_update) > 0:
|
if len(order_update) > 0:
|
||||||
@@ -411,8 +439,8 @@ async def run_algo():
|
|||||||
EXTEND_TOB_PX = float(EXTENDED_TICKER_DICT['best_bid_px'])
|
EXTEND_TOB_PX = float(EXTENDED_TICKER_DICT['best_bid_px'])
|
||||||
|
|
||||||
|
|
||||||
ASTER_TGT_TAIL = ASTER_TGT_NOTIONAL - ASTER_NOTIONAL_POSITION
|
ASTER_TGT_TAIL = ASTER_TGT_NOTIONAL - ( float(ASTER_NOTIONAL_POSITION) + float(ASTER_UNREALIZED_PNL) )
|
||||||
EXTEND_TGT_TAIL = EXTEND_TGT_NOTIONAL - EXTEND_NOTIONAL_POSITION
|
EXTEND_TGT_TAIL = EXTEND_TGT_NOTIONAL - ( float(EXTEND_NOTIONAL_POSITION) + float(EXTEND_UNREALIZED_PNL) )
|
||||||
|
|
||||||
ASTER_TGT_TAIL_BASE_QTY = Decimal(str(float(ASTER_TGT_TAIL) / float(ASTER_TOB_PX))).quantize(Decimal(str(0.001)), rounding=ROUND_DOWN)
|
ASTER_TGT_TAIL_BASE_QTY = Decimal(str(float(ASTER_TGT_TAIL) / float(ASTER_TOB_PX))).quantize(Decimal(str(0.001)), rounding=ROUND_DOWN)
|
||||||
EXTEND_TGT_TAIL_BASE_QTY = Decimal(str(float(EXTEND_TGT_TAIL) / float(EXTEND_TOB_PX))).quantize(Decimal(str(0.001)), rounding=ROUND_DOWN)
|
EXTEND_TGT_TAIL_BASE_QTY = Decimal(str(float(EXTEND_TGT_TAIL) / float(EXTEND_TOB_PX))).quantize(Decimal(str(0.001)), rounding=ROUND_DOWN)
|
||||||
@@ -426,9 +454,10 @@ async def run_algo():
|
|||||||
OUT: print | logging.info = logging.info if use_logging else print
|
OUT: print | logging.info = logging.info if use_logging else print
|
||||||
|
|
||||||
OUT(f'''
|
OUT(f'''
|
||||||
|
LOOP SLEEP (SEC): {ALGO_CONFIG.Loop_Sleep_Sec}
|
||||||
FLIP SIDES FOR TESTING?: {ALGO_CONFIG.Flip_Side_For_Testing}
|
FLIP SIDES FOR TESTING?: {ALGO_CONFIG.Flip_Side_For_Testing}
|
||||||
{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: [ Available Collateral: {ASTER_AVAIL_COLLATERAL:.4f} ] | EXTEND: [ Available Collateral: {EXTEND_AVAIL_COLLATERAL:.4f} ]
|
ASTER: [ Available Collateral: {ASTER_AVAIL_COLLATERAL:.4f} ] | EXTEND: [ Available Collateral: {EXTEND_AVAIL_COLLATERAL:.4f} ]
|
||||||
ASTER: [ Notional Position $ : {ASTER_NOTIONAL_POSITION:.4f} ] | EXTEND: [ Notional Position $ : {EXTEND_NOTIONAL_POSITION:.4f} ]
|
ASTER: [ Notional Position $ : {ASTER_NOTIONAL_POSITION:.4f} ] | EXTEND: [ Notional Position $ : {EXTEND_NOTIONAL_POSITION:.4f} ]
|
||||||
@@ -440,7 +469,7 @@ async def run_algo():
|
|||||||
TGT NOTIONAL: $ {ALGO_CONFIG.Max_Target_Notional if not Flags.NET_FUNDING_IS_ZERO else 0.00}
|
TGT NOTIONAL: $ {ALGO_CONFIG.Max_Target_Notional if not Flags.NET_FUNDING_IS_ZERO else 0.00}
|
||||||
|
|
||||||
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:.4f} - {ASTER_NOTIONAL_POSITION:.4f} = Tail: {ASTER_TGT_TAIL:4f} | EXTEND: {EXTEND_TGT_NOTIONAL:.4f} - {EXTEND_NOTIONAL_POSITION:.4f} = Tail: {EXTEND_TGT_TAIL:4f}
|
ASTER: {ASTER_TGT_NOTIONAL:.2f} - {ASTER_NOTIONAL_POSITION:.2f} + {ASTER_UNREALIZED_PNL:.2f} = {ASTER_TGT_TAIL:2f} | EXTEND: {EXTEND_TGT_NOTIONAL:.2f} - {EXTEND_NOTIONAL_POSITION:.2f} + {EXTEND_UNREALIZED_PNL:.2f} = {EXTEND_TGT_TAIL:2f}
|
||||||
ASTER: {ASTER_TGT_TAIL_BASE_QTY:.4f} > {MAX_MIN_ORDER_QTY:.4f} min [ Order: {ASTER_TGT_TAIL_ORDERABLE} ] | EXTEND: {EXTEND_TGT_TAIL_BASE_QTY:.4f} > {MAX_MIN_ORDER_QTY:.4f} min [ Order: {EXTEND_TGT_TAIL_ORDERABLE} ]
|
ASTER: {ASTER_TGT_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 OPEN ORDERS ---
|
--- ASTER OPEN ORDERS ---
|
||||||
@@ -449,8 +478,10 @@ async def run_algo():
|
|||||||
--- EXTEND OPEN ORDERS ---
|
--- EXTEND OPEN ORDERS ---
|
||||||
{EXTEND_OPEN_ORDERS}
|
{EXTEND_OPEN_ORDERS}
|
||||||
''')
|
''')
|
||||||
|
if ALGO_CONFIG.Log_Summary_Each_Loop:
|
||||||
|
print_summary(use_logging=True)
|
||||||
if ALGO_CONFIG.Print_Summary_Each_Loop:
|
if ALGO_CONFIG.Print_Summary_Each_Loop:
|
||||||
print_summary()
|
print_summary(use_logging=False)
|
||||||
# print_summary()
|
# print_summary()
|
||||||
|
|
||||||
### ROUTES ###
|
### ROUTES ###
|
||||||
@@ -510,6 +541,7 @@ async def run_algo():
|
|||||||
order_resp = await aster_auth.post_authenticated_url(post_order)
|
order_resp = await aster_auth.post_authenticated_url(post_order)
|
||||||
if order_resp.get('orderId', None) is not None:
|
if order_resp.get('orderId', None) is not None:
|
||||||
order_resp['original_price'] = price
|
order_resp['original_price'] = price
|
||||||
|
order_resp['order_status'] = order_resp['status']
|
||||||
ASTER_OPEN_ORDERS.append(order_resp)
|
ASTER_OPEN_ORDERS.append(order_resp)
|
||||||
utils.send_tg_alert(f'FR_ALGO - ASTER Order. Start_$: {ASTER_NOTIONAL_POSITION:.2f}; Value: {float(ASTER_TGT_TAIL_BASE_QTY)*float(price):.2f}; Price: {float(price):.2f}')
|
utils.send_tg_alert(f'FR_ALGO - ASTER Order. Start_$: {ASTER_NOTIONAL_POSITION:.2f}; Value: {float(ASTER_TGT_TAIL_BASE_QTY)*float(price):.2f}; Price: {float(price):.2f}')
|
||||||
logging.info(f'ASTER ORDER PLACED SUCCESS: {order_resp}')
|
logging.info(f'ASTER ORDER PLACED SUCCESS: {order_resp}')
|
||||||
@@ -594,7 +626,7 @@ async def run_algo():
|
|||||||
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()
|
||||||
|
|
||||||
print(f'__________ End ___________ (Algo Engine ms: {(time.time() - loop_start)*1000})')
|
# print(f'__________ End ___________ (Algo Engine ms: {(time.time() - loop_start)*1000}); Sleeping for sec: {ALGO_CONFIG.Loop_Sleep_Sec}')
|
||||||
|
|
||||||
time.sleep(ALGO_CONFIG.Loop_Sleep_Sec)
|
time.sleep(ALGO_CONFIG.Loop_Sleep_Sec)
|
||||||
|
|
||||||
|
|||||||
@@ -139,9 +139,30 @@ async def create_fr_aster_user_account_pos(
|
|||||||
else:
|
else:
|
||||||
raise ValueError('Only MySQL engine is implemented')
|
raise ValueError('Only MySQL engine is implemented')
|
||||||
|
|
||||||
|
### Mkt Trades Table ####
|
||||||
|
async def create_fr_aster_mkt_trades(
|
||||||
|
CON: AsyncContextManager,
|
||||||
|
engine: str = 'mysql', # mysql | duckdb
|
||||||
|
) -> None:
|
||||||
|
if CON is None:
|
||||||
|
logging.info("NO DB CONNECTION, SKIPPING Create Statements")
|
||||||
|
else:
|
||||||
|
if engine == 'mysql':
|
||||||
|
logging.info('Creating Table if Does Not Exist: fr_aster_mkt_trades')
|
||||||
|
await CON.execute(text("""
|
||||||
|
CREATE TABLE IF NOT EXISTS fr_aster_mkt_trades (
|
||||||
|
timestamp_arrival BIGINT,
|
||||||
|
timestamp_msg BIGINT,
|
||||||
|
timestamp_trade BIGINT,
|
||||||
|
symbol VARCHAR(20),
|
||||||
|
aggregate_trade_id VARCHAR(100),
|
||||||
|
price DOUBLE,
|
||||||
|
qty DOUBLE,
|
||||||
|
first_trade_id VARCHAR(100),
|
||||||
|
last_trade_id VARCHAR(100),
|
||||||
|
is_buyer_mkt_maker BOOL
|
||||||
|
);
|
||||||
|
"""))
|
||||||
|
await CON.commit()
|
||||||
|
else:
|
||||||
|
raise ValueError('Only MySQL engine is implemented')
|
||||||
|
|||||||
@@ -150,3 +150,32 @@ async def create_fr_extended_user_position(
|
|||||||
else:
|
else:
|
||||||
raise ValueError('Only MySQL engine is implemented')
|
raise ValueError('Only MySQL engine is implemented')
|
||||||
|
|
||||||
|
### Market Trades Table ####
|
||||||
|
async def create_fr_extended_mkt_trades(
|
||||||
|
CON: AsyncContextManager,
|
||||||
|
engine: str = 'mysql', # mysql | duckdb
|
||||||
|
) -> None:
|
||||||
|
if CON is None:
|
||||||
|
logging.info("NO DB CONNECTION, SKIPPING Create Statements")
|
||||||
|
else:
|
||||||
|
if engine == 'mysql':
|
||||||
|
logging.info('Creating Table if Does Not Exist: fr_extended_mkt_trades')
|
||||||
|
await CON.execute(text("""
|
||||||
|
CREATE TABLE IF NOT EXISTS fr_extended_mkt_trades (
|
||||||
|
sequence_id INT,
|
||||||
|
timestamp_arrival BIGINT,
|
||||||
|
timestamp_msg BIGINT,
|
||||||
|
timestamp_trade BIGINT,
|
||||||
|
symbol VARCHAR(20),
|
||||||
|
side_taker VARCHAR(20),
|
||||||
|
trade_type VARCHAR(20),
|
||||||
|
price DOUBLE,
|
||||||
|
qty DOUBLE,
|
||||||
|
trade_id VARCHAR(100),
|
||||||
|
is_buyer_mkt_maker BOOL
|
||||||
|
);
|
||||||
|
"""))
|
||||||
|
await CON.commit()
|
||||||
|
else:
|
||||||
|
raise ValueError('Only MySQL engine is implemented')
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class Algo_Config:
|
|||||||
Price_Worsener_Extend: float
|
Price_Worsener_Extend: float
|
||||||
Target_Open_Cash_Position: int
|
Target_Open_Cash_Position: int
|
||||||
|
|
||||||
|
Log_Summary_Each_Loop: bool = False
|
||||||
Print_Summary_Each_Loop: bool = False
|
Print_Summary_Each_Loop: bool = False
|
||||||
Flip_Side_For_Testing: bool = False
|
Flip_Side_For_Testing: bool = False
|
||||||
|
|
||||||
|
|||||||
272
order_engine.ipynb
Normal file
272
order_engine.ipynb
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 32,
|
||||||
|
"id": "68966247",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import requests\n",
|
||||||
|
"import pandas as pd\n",
|
||||||
|
"import numpy as np\n",
|
||||||
|
"import json\n",
|
||||||
|
"import pandas as pd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "02cd5305",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"### Extended Trades History ###\n",
|
||||||
|
"candleType = 'trades'\n",
|
||||||
|
"market = 'ETH-USD'\n",
|
||||||
|
"params = {\n",
|
||||||
|
" 'interval': \"1m\",\n",
|
||||||
|
" 'limit': 100,\n",
|
||||||
|
"}\n",
|
||||||
|
"r = requests.get(f'https://api.starknet.extended.exchange/api/v1/info/candles/{market}/{candleType}', params=params)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 33,
|
||||||
|
"id": "5603b04d",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"### Aster Trades History ###\n",
|
||||||
|
"params = {\n",
|
||||||
|
" 'symbol': \"ETHUSDT\",\n",
|
||||||
|
" 'limit': 1000,\n",
|
||||||
|
"}\n",
|
||||||
|
"r = requests.get('https://fapi.asterdex.com/fapi/v3/trades', params=params)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 34,
|
||||||
|
"id": "a3ad1819",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/html": [
|
||||||
|
"<div>\n",
|
||||||
|
"<style scoped>\n",
|
||||||
|
" .dataframe tbody tr th:only-of-type {\n",
|
||||||
|
" vertical-align: middle;\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" .dataframe tbody tr th {\n",
|
||||||
|
" vertical-align: top;\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" .dataframe thead th {\n",
|
||||||
|
" text-align: right;\n",
|
||||||
|
" }\n",
|
||||||
|
"</style>\n",
|
||||||
|
"<table border=\"1\" class=\"dataframe\">\n",
|
||||||
|
" <thead>\n",
|
||||||
|
" <tr style=\"text-align: right;\">\n",
|
||||||
|
" <th></th>\n",
|
||||||
|
" <th>id</th>\n",
|
||||||
|
" <th>price</th>\n",
|
||||||
|
" <th>qty</th>\n",
|
||||||
|
" <th>quoteQty</th>\n",
|
||||||
|
" <th>time</th>\n",
|
||||||
|
" <th>isBuyerMaker</th>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" </thead>\n",
|
||||||
|
" <tbody>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>0</th>\n",
|
||||||
|
" <td>74506547</td>\n",
|
||||||
|
" <td>2311.02</td>\n",
|
||||||
|
" <td>0.044</td>\n",
|
||||||
|
" <td>101.68</td>\n",
|
||||||
|
" <td>2026-04-27 14:22:45.650</td>\n",
|
||||||
|
" <td>True</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>1</th>\n",
|
||||||
|
" <td>74506548</td>\n",
|
||||||
|
" <td>2311.00</td>\n",
|
||||||
|
" <td>0.004</td>\n",
|
||||||
|
" <td>9.24</td>\n",
|
||||||
|
" <td>2026-04-27 14:22:45.650</td>\n",
|
||||||
|
" <td>True</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>2</th>\n",
|
||||||
|
" <td>74506549</td>\n",
|
||||||
|
" <td>2310.91</td>\n",
|
||||||
|
" <td>0.003</td>\n",
|
||||||
|
" <td>6.93</td>\n",
|
||||||
|
" <td>2026-04-27 14:22:45.650</td>\n",
|
||||||
|
" <td>True</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>3</th>\n",
|
||||||
|
" <td>74506550</td>\n",
|
||||||
|
" <td>2310.90</td>\n",
|
||||||
|
" <td>0.004</td>\n",
|
||||||
|
" <td>9.24</td>\n",
|
||||||
|
" <td>2026-04-27 14:22:45.650</td>\n",
|
||||||
|
" <td>True</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>4</th>\n",
|
||||||
|
" <td>74506551</td>\n",
|
||||||
|
" <td>2310.80</td>\n",
|
||||||
|
" <td>0.004</td>\n",
|
||||||
|
" <td>9.24</td>\n",
|
||||||
|
" <td>2026-04-27 14:22:45.700</td>\n",
|
||||||
|
" <td>True</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>...</th>\n",
|
||||||
|
" <td>...</td>\n",
|
||||||
|
" <td>...</td>\n",
|
||||||
|
" <td>...</td>\n",
|
||||||
|
" <td>...</td>\n",
|
||||||
|
" <td>...</td>\n",
|
||||||
|
" <td>...</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>995</th>\n",
|
||||||
|
" <td>74507542</td>\n",
|
||||||
|
" <td>2312.10</td>\n",
|
||||||
|
" <td>0.004</td>\n",
|
||||||
|
" <td>9.24</td>\n",
|
||||||
|
" <td>2026-04-27 14:34:12.500</td>\n",
|
||||||
|
" <td>True</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>996</th>\n",
|
||||||
|
" <td>74507543</td>\n",
|
||||||
|
" <td>2312.18</td>\n",
|
||||||
|
" <td>2.442</td>\n",
|
||||||
|
" <td>5646.34</td>\n",
|
||||||
|
" <td>2026-04-27 14:34:13.443</td>\n",
|
||||||
|
" <td>True</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>997</th>\n",
|
||||||
|
" <td>74507544</td>\n",
|
||||||
|
" <td>2312.24</td>\n",
|
||||||
|
" <td>10.099</td>\n",
|
||||||
|
" <td>23351.31</td>\n",
|
||||||
|
" <td>2026-04-27 14:34:13.600</td>\n",
|
||||||
|
" <td>True</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>998</th>\n",
|
||||||
|
" <td>74507545</td>\n",
|
||||||
|
" <td>2312.13</td>\n",
|
||||||
|
" <td>3.120</td>\n",
|
||||||
|
" <td>7213.84</td>\n",
|
||||||
|
" <td>2026-04-27 14:34:14.568</td>\n",
|
||||||
|
" <td>True</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>999</th>\n",
|
||||||
|
" <td>74507546</td>\n",
|
||||||
|
" <td>2312.19</td>\n",
|
||||||
|
" <td>6.228</td>\n",
|
||||||
|
" <td>14400.31</td>\n",
|
||||||
|
" <td>2026-04-27 14:34:15.988</td>\n",
|
||||||
|
" <td>True</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" </tbody>\n",
|
||||||
|
"</table>\n",
|
||||||
|
"<p>1000 rows × 6 columns</p>\n",
|
||||||
|
"</div>"
|
||||||
|
],
|
||||||
|
"text/plain": [
|
||||||
|
" id price qty quoteQty time isBuyerMaker\n",
|
||||||
|
"0 74506547 2311.02 0.044 101.68 2026-04-27 14:22:45.650 True\n",
|
||||||
|
"1 74506548 2311.00 0.004 9.24 2026-04-27 14:22:45.650 True\n",
|
||||||
|
"2 74506549 2310.91 0.003 6.93 2026-04-27 14:22:45.650 True\n",
|
||||||
|
"3 74506550 2310.90 0.004 9.24 2026-04-27 14:22:45.650 True\n",
|
||||||
|
"4 74506551 2310.80 0.004 9.24 2026-04-27 14:22:45.700 True\n",
|
||||||
|
".. ... ... ... ... ... ...\n",
|
||||||
|
"995 74507542 2312.10 0.004 9.24 2026-04-27 14:34:12.500 True\n",
|
||||||
|
"996 74507543 2312.18 2.442 5646.34 2026-04-27 14:34:13.443 True\n",
|
||||||
|
"997 74507544 2312.24 10.099 23351.31 2026-04-27 14:34:13.600 True\n",
|
||||||
|
"998 74507545 2312.13 3.120 7213.84 2026-04-27 14:34:14.568 True\n",
|
||||||
|
"999 74507546 2312.19 6.228 14400.31 2026-04-27 14:34:15.988 True\n",
|
||||||
|
"\n",
|
||||||
|
"[1000 rows x 6 columns]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 34,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"l = json.loads(r.text)\n",
|
||||||
|
"df = pd.DataFrame(l)\n",
|
||||||
|
"df['time'] = pd.to_datetime(df['time'], unit='ms')\n",
|
||||||
|
"df"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "3c908942",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "60f4608a",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "76624896",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "5ade3c15",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "py_313",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython3",
|
||||||
|
"version": "3.13.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
||||||
100
ws_aster.py
100
ws_aster.py
@@ -15,7 +15,8 @@ from sqlalchemy.ext.asyncio import create_async_engine
|
|||||||
import valkey
|
import valkey
|
||||||
import os
|
import os
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
import modules.db as db
|
||||||
|
import modules.aster_db as aster_db
|
||||||
|
|
||||||
### Allow only ipv4 ###
|
### Allow only ipv4 ###
|
||||||
def allowed_gai_family():
|
def allowed_gai_family():
|
||||||
@@ -23,10 +24,11 @@ def allowed_gai_family():
|
|||||||
urllib3_cn.allowed_gai_family = allowed_gai_family
|
urllib3_cn.allowed_gai_family = allowed_gai_family
|
||||||
|
|
||||||
### Database ###
|
### Database ###
|
||||||
USE_DB: bool = False
|
USE_DB: bool = True
|
||||||
USE_VK: bool = True
|
USE_VK: bool = True
|
||||||
VK_FUND_RATE = 'fund_rate_aster'
|
VK_FUND_RATE = 'fund_rate_aster'
|
||||||
VK_TICKER = 'fut_ticker_aster'
|
VK_TICKER = 'fut_ticker_aster'
|
||||||
|
VK_LAST_TRADE = 'fut_last_trade_aster'
|
||||||
CON: AsyncContextManager | None = None
|
CON: AsyncContextManager | None = None
|
||||||
VAL_KEY = None
|
VAL_KEY = None
|
||||||
|
|
||||||
@@ -38,80 +40,10 @@ LOG_FILEPATH: str = os.getenv("LOGS_PATH") + '/Fund_Rate_Aster.log'
|
|||||||
SYMBOL: str = 'ETHUSDT'
|
SYMBOL: str = 'ETHUSDT'
|
||||||
STREAM_MARKPRICE: str = f'{SYMBOL.lower()}@markPrice@1s'
|
STREAM_MARKPRICE: str = f'{SYMBOL.lower()}@markPrice@1s'
|
||||||
STREAM_BOOKTICKER: str = f'{SYMBOL.lower()}@bookTicker'
|
STREAM_BOOKTICKER: str = f'{SYMBOL.lower()}@bookTicker'
|
||||||
|
STREAM_TRADES: str = f'{SYMBOL.lower()}@aggTrade'
|
||||||
|
|
||||||
### Globals ###
|
### Globals ###
|
||||||
WSS_URL = f"wss://fstream.asterdex.com/stream?streams={STREAM_MARKPRICE}/{STREAM_BOOKTICKER}"
|
WSS_URL = f"wss://fstream.asterdex.com/stream?streams={STREAM_MARKPRICE}/{STREAM_BOOKTICKER}/{STREAM_TRADES}"
|
||||||
# WSS_URL = f"wss://fstream.asterdex.com/stream?streams={STREAM_MARKPRICE}"
|
|
||||||
|
|
||||||
# HIST_TRADES = np.empty((0, 3))
|
|
||||||
# HIST_TRADES_LOOKBACK_SEC = 6
|
|
||||||
|
|
||||||
# ### Database Funcs ###
|
|
||||||
# async def create_rtds_btcusd_table(
|
|
||||||
# CON: AsyncContextManager,
|
|
||||||
# engine: str = 'mysql', # mysql | duckdb
|
|
||||||
# ) -> None:
|
|
||||||
# if CON is None:
|
|
||||||
# logging.info("NO DB CONNECTION, SKIPPING Create Statements")
|
|
||||||
# else:
|
|
||||||
# if engine == 'mysql':
|
|
||||||
# logging.info('Creating Table if Does Not Exist: binance_btcusd_trades')
|
|
||||||
# await CON.execute(text("""
|
|
||||||
# CREATE TABLE IF NOT EXISTS binance_btcusd_trades (
|
|
||||||
# timestamp_arrival BIGINT,
|
|
||||||
# timestamp_msg BIGINT,
|
|
||||||
# timestamp_value BIGINT,
|
|
||||||
# value DOUBLE,
|
|
||||||
# qty DOUBLE
|
|
||||||
# );
|
|
||||||
# """))
|
|
||||||
# await CON.commit()
|
|
||||||
# else:
|
|
||||||
# raise ValueError('Only MySQL engine is implemented')
|
|
||||||
|
|
||||||
# async def insert_rtds_btcusd_table(
|
|
||||||
# timestamp_arrival: int,
|
|
||||||
# timestamp_msg: int,
|
|
||||||
# timestamp_value: int,
|
|
||||||
# value: float,
|
|
||||||
# qty: float,
|
|
||||||
# CON: AsyncContextManager,
|
|
||||||
# engine: str = 'mysql', # mysql | duckdb
|
|
||||||
# ) -> None:
|
|
||||||
# params={
|
|
||||||
# 'timestamp_arrival': timestamp_arrival,
|
|
||||||
# 'timestamp_msg': timestamp_msg,
|
|
||||||
# 'timestamp_value': timestamp_value,
|
|
||||||
# 'value': value,
|
|
||||||
# 'qty': qty,
|
|
||||||
# }
|
|
||||||
# if CON is None:
|
|
||||||
# logging.info("NO DB CONNECTION, SKIPPING Insert Statements")
|
|
||||||
# else:
|
|
||||||
# if engine == 'mysql':
|
|
||||||
# await CON.execute(text("""
|
|
||||||
# INSERT INTO binance_btcusd_trades
|
|
||||||
# (
|
|
||||||
# timestamp_arrival,
|
|
||||||
# timestamp_msg,
|
|
||||||
# timestamp_value,
|
|
||||||
# value,
|
|
||||||
# qty
|
|
||||||
# )
|
|
||||||
# VALUES
|
|
||||||
# (
|
|
||||||
# :timestamp_arrival,
|
|
||||||
# :timestamp_msg,
|
|
||||||
# :timestamp_value,
|
|
||||||
# :value,
|
|
||||||
# :qty
|
|
||||||
# )
|
|
||||||
# """),
|
|
||||||
# parameters=params
|
|
||||||
# )
|
|
||||||
# await CON.commit()
|
|
||||||
# else:
|
|
||||||
# raise ValueError('Only MySQL engine is implemented')
|
|
||||||
|
|
||||||
### Websocket ###
|
### Websocket ###
|
||||||
async def ws_stream():
|
async def ws_stream():
|
||||||
@@ -156,6 +88,24 @@ 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:
|
||||||
|
# print(f'MKT_TRADE: {data}')
|
||||||
|
trade_obj = {
|
||||||
|
'timestamp_arrival': ts_arrival,
|
||||||
|
'timestamp_msg': data['data']['E'],
|
||||||
|
'timestamp_trade': data['data']['T'],
|
||||||
|
'symbol': data['data']['s'],
|
||||||
|
'aggregate_trade_id': data['data']['a'],
|
||||||
|
'price': float(data['data']['p']),
|
||||||
|
'qty': float(data['data']['q']),
|
||||||
|
'first_trade_id': data['data']['f'],
|
||||||
|
'last_trade_id': data['data']['l'],
|
||||||
|
'is_buyer_mkt_maker': bool(data['data']['m']),
|
||||||
|
}
|
||||||
|
# VAL_KEY.set(VK_LAST_TRADE, json.dumps(trade_obj))
|
||||||
|
if USE_DB:
|
||||||
|
await db.insert_df_to_mysql(table_name='fr_aster_mkt_trades', params=trade_obj, CON=CON)
|
||||||
|
continue
|
||||||
case _:
|
case _:
|
||||||
logging.warning(f'UNMATCHED OTHER MSG: {data}')
|
logging.warning(f'UNMATCHED OTHER MSG: {data}')
|
||||||
else:
|
else:
|
||||||
@@ -188,7 +138,7 @@ async def main():
|
|||||||
if USE_DB:
|
if USE_DB:
|
||||||
engine = create_async_engine('mysql+asyncmy://root:pwd@localhost/fund_rate')
|
engine = create_async_engine('mysql+asyncmy://root:pwd@localhost/fund_rate')
|
||||||
async with engine.connect() as CON:
|
async with engine.connect() as CON:
|
||||||
# await create_rtds_btcusd_table(CON=CON)
|
await aster_db.create_fr_aster_mkt_trades(CON=CON)
|
||||||
await ws_stream()
|
await ws_stream()
|
||||||
else:
|
else:
|
||||||
CON = None
|
CON = None
|
||||||
|
|||||||
134
ws_extended_trades.py
Normal file
134
ws_extended_trades.py
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import traceback
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import AsyncContextManager
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import requests.packages.urllib3.util.connection as urllib3_cn # type: ignore
|
||||||
|
import valkey
|
||||||
|
import websockets
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from sqlalchemy.ext.asyncio import create_async_engine
|
||||||
|
|
||||||
|
import modules.db as db
|
||||||
|
import modules.extended_db as extended_db
|
||||||
|
|
||||||
|
|
||||||
|
### Allow only ipv4 ###
|
||||||
|
def allowed_gai_family():
|
||||||
|
return socket.AF_INET
|
||||||
|
urllib3_cn.allowed_gai_family = allowed_gai_family
|
||||||
|
|
||||||
|
### Database ###
|
||||||
|
USE_DB: bool = True
|
||||||
|
USE_VK: bool = True
|
||||||
|
|
||||||
|
VK_LAST_TRADE = 'fut_last_trade_extended'
|
||||||
|
CON: AsyncContextManager | None = None
|
||||||
|
VAL_KEY = None
|
||||||
|
|
||||||
|
### Logging ###
|
||||||
|
load_dotenv()
|
||||||
|
LOG_FILEPATH: str = os.getenv("LOGS_PATH") + '/Fund_Rate_Extended_Trades.log'
|
||||||
|
|
||||||
|
### CONSTANTS ###
|
||||||
|
WS_SYMBOL: str = 'ETH-USD'
|
||||||
|
|
||||||
|
### Globals ###
|
||||||
|
WSS_URL = f"wss://api.starknet.extended.exchange/stream.extended.exchange/v1/publicTrades/{WS_SYMBOL}"
|
||||||
|
|
||||||
|
### Websocket ###
|
||||||
|
async def ws_stream():
|
||||||
|
async for websocket in websockets.connect(WSS_URL):
|
||||||
|
logging.info(f"Connected to {WSS_URL}")
|
||||||
|
try:
|
||||||
|
async for message in websocket:
|
||||||
|
ts_arrival = round(datetime.now().timestamp()*1000)
|
||||||
|
if isinstance(message, str):
|
||||||
|
try:
|
||||||
|
data = json.loads(message)
|
||||||
|
if data.get('data', None) is not None:
|
||||||
|
if data['seq'] == 1: # Skip first msg that has historical trades
|
||||||
|
continue
|
||||||
|
list_for_df = []
|
||||||
|
for t in data['data']:
|
||||||
|
trade_obj = {
|
||||||
|
'sequence_id': data['seq'],
|
||||||
|
'timestamp_arrival': ts_arrival,
|
||||||
|
'timestamp_msg': data['ts'],
|
||||||
|
'timestamp_trade': t['T'],
|
||||||
|
'symbol': t['m'],
|
||||||
|
'side_taker': t['S'],
|
||||||
|
'trade_type': t['tT'],
|
||||||
|
'price': float(t['p']),
|
||||||
|
'qty': float(t['q']),
|
||||||
|
'trade_id': t['i'],
|
||||||
|
'is_buyer_mkt_maker': True if t['S']=='SELL' else False,
|
||||||
|
}
|
||||||
|
list_for_df.append(trade_obj)
|
||||||
|
# VAL_KEY.set(VK_LAST_TRADE, json.dumps(trade_obj))
|
||||||
|
if USE_DB:
|
||||||
|
await db.insert_df_to_mysql(table_name='fr_extended_mkt_trades', params=list_for_df, CON=CON)
|
||||||
|
pass
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
logging.info(f'Initial or unexpected data struct, skipping: {data}')
|
||||||
|
continue
|
||||||
|
except (json.JSONDecodeError, ValueError):
|
||||||
|
logging.warning(f'Message not in JSON format, skipping: {message}')
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise ValueError(f'Type: {type(data)} not expected: {message}')
|
||||||
|
except websockets.ConnectionClosed as e:
|
||||||
|
logging.error(f'Connection closed: {e}')
|
||||||
|
logging.error(traceback.format_exc())
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f'Connection closed: {e}')
|
||||||
|
logging.error(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
global VAL_KEY
|
||||||
|
global CON
|
||||||
|
|
||||||
|
if USE_VK:
|
||||||
|
VAL_KEY = valkey.Valkey(host='localhost', port=6379, db=0)
|
||||||
|
else:
|
||||||
|
VAL_KEY = None
|
||||||
|
logging.warning("VALKEY NOT BEING USED, NO DATA WILL BE PUBLISHED")
|
||||||
|
|
||||||
|
if USE_DB:
|
||||||
|
engine = create_async_engine('mysql+asyncmy://root:pwd@localhost/fund_rate')
|
||||||
|
async with engine.connect() as CON:
|
||||||
|
await extended_db.create_fr_extended_mkt_trades(CON=CON)
|
||||||
|
await ws_stream()
|
||||||
|
else:
|
||||||
|
CON = None
|
||||||
|
logging.warning("DATABASE NOT BEING USED, NO DATA WILL BE RECORDED")
|
||||||
|
await ws_stream()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
START_TIME = round(datetime.now().timestamp()*1000)
|
||||||
|
|
||||||
|
logging.info(f'Log FilePath: {LOG_FILEPATH}')
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
force=True,
|
||||||
|
filename=LOG_FILEPATH,
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||||
|
filemode='w'
|
||||||
|
)
|
||||||
|
logging.info(f"STARTED: {START_TIME}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
asyncio.run(main())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
logging.info("Stream stopped")
|
||||||
19
ws_extended_trades/Dockerfile
Normal file
19
ws_extended_trades/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_trades.py"]
|
||||||
|
# CMD [ "gunicorn", "--workers=5", "--threads=1", "-b 0.0.0.0:8000", "app:server"]
|
||||||
@@ -198,7 +198,6 @@ async def ws_stream():
|
|||||||
|
|
||||||
VAL_KEY_OBJ = json.dumps(LOCAL_RECENT_POSITIONS)
|
VAL_KEY_OBJ = json.dumps(LOCAL_RECENT_POSITIONS)
|
||||||
VAL_KEY.set(VK_POSITIONS, VAL_KEY_OBJ)
|
VAL_KEY.set(VK_POSITIONS, 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
|
continue
|
||||||
case _:
|
case _:
|
||||||
|
|||||||
Reference in New Issue
Block a user