added coin cooldown to stop coin chase
This commit is contained in:
52
portfolio.py
52
portfolio.py
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user