- Extract 7 focused services from smart_executor.py: - trade_common: constants, timezone, logging, dry-run state - file_utils: file locking + atomic JSON helpers - smart_executor_parser: argparse + legacy argument compatibility - execution_state: decision deduplication (executions.json) - portfolio_service: positions.json + exchange reconciliation - exchange_service: ccxt wrapper, balances, order prep - trade_execution: buy/sell/rebalance/hold actions - Turn smart_executor.py into a thin backward-compatible facade - Fix critical dry-run bug: module-level DRY_RUN copy caused real orders in dry-run mode; replace with mutable dict + is_dry_run() function - Fix dry-run polluting positions.json: skip save_positions() when dry-run - Fix rebalance dry-run budget: use sell_order cost instead of real balance - Add full legacy CLI compatibility for old --decision HOLD --dry-run style
41 lines
1.1 KiB
Python
41 lines
1.1 KiB
Python
"""File locking and atomic JSON helpers."""
|
|
import fcntl
|
|
import json
|
|
import os
|
|
from contextlib import contextmanager
|
|
from pathlib import Path
|
|
|
|
|
|
@contextmanager
|
|
def locked_file(path: Path):
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
with open(path, "a+", encoding="utf-8") as f:
|
|
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
|
|
f.seek(0)
|
|
yield f
|
|
f.flush()
|
|
os.fsync(f.fileno())
|
|
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
|
|
|
|
|
|
def atomic_write_json(path: Path, data: dict):
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
tmp = path.with_suffix(path.suffix + ".tmp")
|
|
tmp.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8")
|
|
os.replace(tmp, path)
|
|
|
|
|
|
def load_json_locked(path: Path, lock_path: Path, default):
|
|
with locked_file(lock_path):
|
|
if not path.exists():
|
|
return default
|
|
try:
|
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
except Exception:
|
|
return default
|
|
|
|
|
|
def save_json_locked(path: Path, lock_path: Path, data: dict):
|
|
with locked_file(lock_path):
|
|
atomic_write_json(path, data)
|