added coin cooldown to stop coin chase

This commit is contained in:
2026-05-04 11:09:17 -04:00
parent 2ce401c487
commit 61987482e1
4 changed files with 51 additions and 13 deletions

View File

@@ -42,21 +42,29 @@ class Position:
class Portfolio:
def __init__(self, filepath: str):
self._path = Path(filepath)
self._cooldown_path = self._path.with_suffix(".cooldown.json")
self.positions: dict[str, Position] = {}
self._cooldowns: dict[str, str] = {} # pair → ISO sell time
self._load()
def _load(self) -> None:
if not self._path.exists():
return
try:
content = self._path.read_text().strip()
if not content:
return
data = json.loads(content)
self.positions = {pair: Position(**fields) for pair, fields in data.items()}
log.info("Loaded %d position(s) from %s", len(self.positions), self._path)
except Exception as exc:
log.error("Could not load positions file: %s", exc)
if self._path.exists():
try:
content = self._path.read_text().strip()
if content:
data = json.loads(content)
self.positions = {pair: Position(**fields) for pair, fields in data.items()}
log.info("Loaded %d position(s) from %s", len(self.positions), self._path)
except Exception as exc:
log.error("Could not load positions file: %s", exc)
if self._cooldown_path.exists():
try:
content = self._cooldown_path.read_text().strip()
if content:
self._cooldowns = json.loads(content)
except Exception as exc:
log.error("Could not load cooldown file: %s", exc)
def _save(self) -> None:
try:
@@ -69,6 +77,12 @@ class Portfolio:
except Exception as exc:
log.error("Could not save positions file: %s", exc)
def _save_cooldowns(self) -> None:
try:
self._cooldown_path.write_text(json.dumps(self._cooldowns, indent=2))
except Exception as exc:
log.error("Could not save cooldown file: %s", exc)
def add(self, position: Position) -> None:
self.positions[position.pair] = position
self._save()
@@ -81,8 +95,24 @@ class Portfolio:
pos = self.positions.pop(pair, None)
if pos:
self._save()
self._cooldowns[pair] = datetime.now(tz=timezone.utc).isoformat()
self._save_cooldowns()
return pos
def on_cooldown(self, pair: str, cooldown_hours: float) -> bool:
sell_time_str = self._cooldowns.get(pair)
if not sell_time_str:
return False
sell_time = datetime.fromisoformat(sell_time_str)
elapsed = (datetime.now(tz=timezone.utc) - sell_time).total_seconds() / 3600
if elapsed < cooldown_hours:
log.debug("%s on cooldown: %.1fh of %.1fh elapsed", pair, elapsed, cooldown_hours)
return True
# Cooldown expired — clean it up
del self._cooldowns[pair]
self._save_cooldowns()
return False
def get(self, pair: str) -> Position | None:
return self.positions.get(pair)