Spaces:
Running
Running
Daniel Alnawaz commited on
indent fixes
Browse files- autonomous_bot.py +131 -131
autonomous_bot.py
CHANGED
|
@@ -963,30 +963,30 @@ class AutonomousTradingBot:
|
|
| 963 |
logger.info(f"Running {len(self.slots)} slot(s): " +
|
| 964 |
", ".join(f"{s.coin}/{s.timeframe}({s.strategy_name})" for s in self.slots))
|
| 965 |
|
| 966 |
-
|
| 967 |
-
|
| 968 |
-
|
| 969 |
-
|
| 970 |
-
|
| 971 |
-
|
| 972 |
-
|
| 973 |
-
|
| 974 |
-
|
| 975 |
-
|
| 976 |
-
|
| 977 |
-
|
| 978 |
-
|
| 979 |
-
|
| 980 |
-
|
| 981 |
-
|
| 982 |
-
|
| 983 |
-
|
| 984 |
-
|
| 985 |
-
|
| 986 |
-
|
| 987 |
-
|
| 988 |
-
|
| 989 |
-
|
| 990 |
reconnect_delay = min(reconnect_delay * 2, 60)
|
| 991 |
logger.error(f"Reconnect failed: {e}. Retry in {reconnect_delay}sβ¦")
|
| 992 |
self.tg.send_error("WebSocket reconnect", str(e))
|
|
@@ -1251,113 +1251,113 @@ def _start_health_server(port: int = 7860, bot_instance: Optional[AutonomousBot]
|
|
| 1251 |
except Exception as e:
|
| 1252 |
return 500, json.dumps({"error": str(e)}).encode()
|
| 1253 |
|
| 1254 |
-
|
| 1255 |
-
|
| 1256 |
-
|
| 1257 |
-
|
| 1258 |
-
|
| 1259 |
-
|
| 1260 |
-
|
| 1261 |
-
|
| 1262 |
-
|
| 1263 |
-
|
| 1264 |
-
|
| 1265 |
-
|
| 1266 |
-
|
| 1267 |
-
|
| 1268 |
-
|
| 1269 |
-
|
| 1270 |
-
|
| 1271 |
-
|
| 1272 |
-
|
| 1273 |
-
|
| 1274 |
-
|
| 1275 |
-
|
| 1276 |
-
|
| 1277 |
-
|
| 1278 |
-
|
| 1279 |
-
|
| 1280 |
-
|
| 1281 |
-
|
| 1282 |
-
|
| 1283 |
-
|
| 1284 |
-
|
| 1285 |
-
|
| 1286 |
-
|
| 1287 |
-
|
| 1288 |
-
|
| 1289 |
-
|
| 1290 |
-
|
| 1291 |
-
|
| 1292 |
-
|
| 1293 |
-
|
| 1294 |
-
|
| 1295 |
-
|
| 1296 |
-
|
| 1297 |
-
|
| 1298 |
-
|
| 1299 |
-
|
| 1300 |
-
|
| 1301 |
-
|
| 1302 |
-
|
| 1303 |
-
|
| 1304 |
-
|
| 1305 |
-
|
| 1306 |
-
|
| 1307 |
-
|
| 1308 |
-
|
| 1309 |
-
|
| 1310 |
-
|
| 1311 |
-
|
| 1312 |
-
|
| 1313 |
-
|
| 1314 |
-
|
| 1315 |
-
|
| 1316 |
-
|
| 1317 |
-
|
| 1318 |
-
|
| 1319 |
-
|
| 1320 |
-
|
| 1321 |
-
|
| 1322 |
-
|
| 1323 |
-
|
| 1324 |
-
|
| 1325 |
-
|
| 1326 |
-
|
| 1327 |
-
|
| 1328 |
-
|
| 1329 |
-
|
| 1330 |
-
|
| 1331 |
-
|
| 1332 |
-
|
| 1333 |
-
|
| 1334 |
-
|
| 1335 |
-
|
| 1336 |
-
|
| 1337 |
-
|
| 1338 |
-
|
| 1339 |
-
|
| 1340 |
-
|
| 1341 |
-
|
| 1342 |
-
|
| 1343 |
-
|
| 1344 |
-
|
| 1345 |
-
|
| 1346 |
-
|
| 1347 |
-
|
| 1348 |
-
|
| 1349 |
-
|
| 1350 |
-
|
| 1351 |
-
|
| 1352 |
-
|
| 1353 |
-
|
| 1354 |
-
|
| 1355 |
-
|
| 1356 |
-
|
| 1357 |
-
|
| 1358 |
-
|
| 1359 |
-
|
| 1360 |
-
|
| 1361 |
|
| 1362 |
class _Handler(BaseHTTPRequestHandler):
|
| 1363 |
def do_GET(self):
|
|
|
|
| 963 |
logger.info(f"Running {len(self.slots)} slot(s): " +
|
| 964 |
", ".join(f"{s.coin}/{s.timeframe}({s.strategy_name})" for s in self.slots))
|
| 965 |
|
| 966 |
+
# ββ Keep-alive + auto-reconnect βββββββββββββββββββββββββββββββ
|
| 967 |
+
reconnect_delay = 5
|
| 968 |
+
try:
|
| 969 |
+
while True:
|
| 970 |
+
# Check if shutdown was requested (e.g., via /api/restart)
|
| 971 |
+
if self._shutdown_event.is_set():
|
| 972 |
+
logger.info("Shutdown event received. Exiting gracefully...")
|
| 973 |
+
break
|
| 974 |
+
|
| 975 |
+
time.sleep(1)
|
| 976 |
+
ws_alive = info.ws_manager is not None and info.ws_manager.is_alive()
|
| 977 |
+
if not ws_alive:
|
| 978 |
+
logger.warning("WebSocket disconnected. Reconnectingβ¦")
|
| 979 |
+
time.sleep(reconnect_delay)
|
| 980 |
+
try:
|
| 981 |
+
clients = self.api.create_live_execution_clients(
|
| 982 |
+
private_key, account_address=account_address, skip_ws=False
|
| 983 |
+
)
|
| 984 |
+
info = clients["info"]
|
| 985 |
+
subscribed.clear()
|
| 986 |
+
subscribe_all()
|
| 987 |
+
reconnect_delay = 5
|
| 988 |
+
logger.info("Reconnected.")
|
| 989 |
+
except Exception as e:
|
| 990 |
reconnect_delay = min(reconnect_delay * 2, 60)
|
| 991 |
logger.error(f"Reconnect failed: {e}. Retry in {reconnect_delay}sβ¦")
|
| 992 |
self.tg.send_error("WebSocket reconnect", str(e))
|
|
|
|
| 1251 |
except Exception as e:
|
| 1252 |
return 500, json.dumps({"error": str(e)}).encode()
|
| 1253 |
|
| 1254 |
+
elif path == "/api/config":
|
| 1255 |
+
try:
|
| 1256 |
+
from pathlib import Path
|
| 1257 |
+
config_path = Path(__file__).parent / "bot_config.json"
|
| 1258 |
+
if config_path.exists():
|
| 1259 |
+
with open(config_path) as f:
|
| 1260 |
+
config = json.load(f)
|
| 1261 |
+
return 200, json.dumps(config).encode()
|
| 1262 |
+
return 404, json.dumps({"error": "Config not found"}).encode()
|
| 1263 |
+
except Exception as e:
|
| 1264 |
+
return 500, json.dumps({"error": str(e)}).encode()
|
| 1265 |
+
|
| 1266 |
+
elif path == "/api/portfolio/backtest":
|
| 1267 |
+
try:
|
| 1268 |
+
from pathlib import Path
|
| 1269 |
+
backtest_file = Path(__file__).parent / "reports" / "backtest_results.json"
|
| 1270 |
+
if backtest_file.exists():
|
| 1271 |
+
with open(backtest_file) as f:
|
| 1272 |
+
results = json.load(f)
|
| 1273 |
+
return 200, json.dumps(results).encode()
|
| 1274 |
+
return 200, json.dumps({"status": "No backtest results yet", "results": []}).encode()
|
| 1275 |
+
except Exception as e:
|
| 1276 |
+
return 500, json.dumps({"error": str(e)}).encode()
|
| 1277 |
+
|
| 1278 |
+
elif path == "/api/scripts/ml-learn":
|
| 1279 |
+
# POST endpoint to start ML tracker - runs track_ml_learning.py
|
| 1280 |
+
try:
|
| 1281 |
+
logger.info("ML learning started via API - executing track_ml_learning.py")
|
| 1282 |
+
import subprocess
|
| 1283 |
+
script_path = os.path.join(os.path.dirname(__file__), "track_ml_learning.py")
|
| 1284 |
+
# Run in background
|
| 1285 |
+
subprocess.Popen([sys.executable, script_path],
|
| 1286 |
+
stdout=subprocess.DEVNULL,
|
| 1287 |
+
stderr=subprocess.DEVNULL)
|
| 1288 |
+
return 200, json.dumps({"status": "ML learning started", "script": "track_ml_learning.py"}).encode()
|
| 1289 |
+
except Exception as e:
|
| 1290 |
+
logger.error(f"ML learning failed: {e}")
|
| 1291 |
+
return 500, json.dumps({"error": str(e)}).encode()
|
| 1292 |
+
|
| 1293 |
+
elif path == "/api/scripts/ml-optimize":
|
| 1294 |
+
# POST endpoint to find best coins - runs _scan_coins.py then optimize_ml_coins.py
|
| 1295 |
+
try:
|
| 1296 |
+
logger.info("ML optimization started via API - executing _scan_coins.py then optimize_ml_coins.py")
|
| 1297 |
+
import subprocess
|
| 1298 |
+
|
| 1299 |
+
# First run _scan_coins.py
|
| 1300 |
+
scan_path = os.path.join(os.path.dirname(__file__), "_scan_coins.py")
|
| 1301 |
+
logger.info(f"Starting _scan_coins.py")
|
| 1302 |
+
subprocess.Popen([sys.executable, scan_path],
|
| 1303 |
+
stdout=subprocess.DEVNULL,
|
| 1304 |
+
stderr=subprocess.DEVNULL)
|
| 1305 |
+
|
| 1306 |
+
# Then run optimize_ml_coins.py (will run after scan completes)
|
| 1307 |
+
optimize_path = os.path.join(os.path.dirname(__file__), "optimize_ml_coins.py")
|
| 1308 |
+
logger.info(f"Optimize will start after scan completes")
|
| 1309 |
+
|
| 1310 |
+
return 200, json.dumps({
|
| 1311 |
+
"status": "ML optimization started",
|
| 1312 |
+
"scripts": ["_scan_coins.py", "optimize_ml_coins.py"],
|
| 1313 |
+
"note": "optimize_ml_coins.py will run after _scan_coins.py completes"
|
| 1314 |
+
}).encode()
|
| 1315 |
+
except Exception as e:
|
| 1316 |
+
logger.error(f"ML optimization failed: {e}")
|
| 1317 |
+
return 500, json.dumps({"error": str(e)}).encode()
|
| 1318 |
+
|
| 1319 |
+
elif path == "/api/scripts/backtest":
|
| 1320 |
+
# POST endpoint to start backtest - runs backtest.py then track_ml_learning.py
|
| 1321 |
+
try:
|
| 1322 |
+
logger.info("Backtest started via API - executing backtest.py")
|
| 1323 |
+
import subprocess
|
| 1324 |
+
|
| 1325 |
+
# Run backtest.py
|
| 1326 |
+
backtest_path = os.path.join(os.path.dirname(__file__), "backtest.py")
|
| 1327 |
+
logger.info(f"Starting backtest.py")
|
| 1328 |
+
subprocess.Popen([sys.executable, backtest_path],
|
| 1329 |
+
stdout=subprocess.DEVNULL,
|
| 1330 |
+
stderr=subprocess.DEVNULL)
|
| 1331 |
+
|
| 1332 |
+
return 200, json.dumps({
|
| 1333 |
+
"status": "Backtest started",
|
| 1334 |
+
"scripts": ["backtest.py"],
|
| 1335 |
+
"note": "track_ml_learning.py can be run separately with /api/scripts/ml-learn"
|
| 1336 |
+
}).encode()
|
| 1337 |
+
except Exception as e:
|
| 1338 |
+
logger.error(f"Backtest failed: {e}")
|
| 1339 |
+
return 500, json.dumps({"error": str(e)}).encode()
|
| 1340 |
+
|
| 1341 |
+
elif path == "/api/restart":
|
| 1342 |
+
# POST endpoint to gracefully restart the bot with new config
|
| 1343 |
+
try:
|
| 1344 |
+
logger.info("Bot restart requested via API")
|
| 1345 |
+
logger.info("Shutting down gracefully...")
|
| 1346 |
+
# Signal to exit the live loop
|
| 1347 |
+
if bot_instance and hasattr(bot_instance, '_shutdown_event'):
|
| 1348 |
+
bot_instance._shutdown_event.set()
|
| 1349 |
+
# Give it a moment to shut down
|
| 1350 |
+
import time as time_module
|
| 1351 |
+
time_module.sleep(2)
|
| 1352 |
+
# Exit the process so it can be restarted by HF Spaces or supervisor
|
| 1353 |
+
logger.info("Exiting bot process for restart")
|
| 1354 |
+
os._exit(0)
|
| 1355 |
+
except Exception as e:
|
| 1356 |
+
logger.error(f"Restart failed: {e}")
|
| 1357 |
+
return 500, json.dumps({"error": str(e)}).encode()
|
| 1358 |
+
|
| 1359 |
+
else:
|
| 1360 |
+
return 404, json.dumps({"error": "Endpoint not found"}).encode()
|
| 1361 |
|
| 1362 |
class _Handler(BaseHTTPRequestHandler):
|
| 1363 |
def do_GET(self):
|