Files

120 lines
4.2 KiB
Python
Raw Permalink Normal View History

2026-05-27 07:02:58 +01:00
"""
actions.py — Action handler for dry/confirm/live modes.
========================================================
Every action the bot takes (place order, cancel order, update TP)
goes through this handler. The mode controls what actually happens.
MODES:
------
dryrun → Logs what it would do. Returns None. No API calls made.
confirm → Prints the action, waits for y/n input. Only proceeds on 'y'.
live → Executes immediately, no prompts.
All three modes log the action clearly so you can audit what happened.
WHY THIS MATTERS:
-----------------
Before trusting the bot with live orders, run in dryrun and confirm modes.
Watch the logs for several days. Only switch to live when you're confident
the logic is correct and the orders it wants to place match your expectations.
"""
import logging
from typing import Optional
from client import CapitalClient
import config
log = logging.getLogger("maxbot.actions")
class ActionHandler:
def __init__(self, mode: str, client: CapitalClient):
self.mode = mode
self.client = client
self.actions_taken = 0
self.orders_placed = 0
self.orders_cancelled = 0
self.tp_updates = 0
def _log_action(self, symbol: str, description: str):
"""Print a highly visible action log line."""
log.warning(f"")
log.warning(f"{'' * 50}")
log.warning(f" {symbol} {description}")
log.warning(f"{'' * 50}")
def _confirm(self, description: str) -> bool:
if self.mode == "dryrun":
log.info(f"[DRY RUN] Would: {description}")
return False
if self.mode == "confirm":
print(f"\n ACTION: {description}")
try:
answer = input(" Proceed? (y/n): ").strip().lower()
except (EOFError, KeyboardInterrupt):
return False
return answer == "y"
return True # live
# ─────────────────────────────────────────
# ACTIONS
# ─────────────────────────────────────────
def place_limit_order(self, size: float, limit_price: float,
take_profit: float) -> Optional[dict]:
desc = (
f"PLACE ORDER entry ${limit_price:.2f} "
f"size {size} TP ${take_profit:.2f}"
)
if self._confirm(desc):
result = self.client.place_limit_order(size, limit_price, take_profit)
self._log_action("", desc)
self.actions_taken += 1
self.orders_placed += 1
return result
return None
def cancel_order(self, deal_id: str, price: float) -> Optional[dict]:
desc = f"CANCEL ORDER ${price:.2f}"
if self._confirm(desc):
result = self.client.cancel_order(deal_id)
self._log_action("", desc)
self.actions_taken += 1
self.orders_cancelled += 1
return result
return None
def enable_tp(self, deal_id: str, entry_price: float,
take_profit: float) -> Optional[dict]:
desc = (
f"ENABLE TP position ${entry_price:.2f} "
f"→ TP ${take_profit:.2f}"
)
if self._confirm(desc):
result = self.client.update_position_tp(deal_id, take_profit)
self._log_action("", desc)
self.actions_taken += 1
self.tp_updates += 1
return result
return None
def disable_tp(self, deal_id: str, entry_price: float) -> Optional[dict]:
desc = f"DISABLE TP position ${entry_price:.2f} (manual close)"
if self._confirm(desc):
result = self.client.update_position_tp(deal_id, None)
self._log_action("", desc)
self.actions_taken += 1
self.tp_updates += 1
return result
return None
def summary(self) -> str:
return (
f"Session summary: "
f"{self.actions_taken} total actions | "
f"{self.orders_placed} placed | "
f"{self.orders_cancelled} cancelled | "
f"{self.tp_updates} TP updates"
)