fix: add 24h sell cooldown to prevent immediate re-entry after profit-taking
After selling a position (profit-taking, stop-loss, or recommendation), the engine would immediately buy it back on the next poll cycle if a buy recommendation existed. Now sets a 24h Redis cooldown key per ticker on any sell, and checks it before entering a new buy position.
This commit is contained in:
@@ -32,6 +32,7 @@ except ImportError:
|
|||||||
from services.shared.config import TradingConfig
|
from services.shared.config import TradingConfig
|
||||||
from services.shared.redis_keys import (
|
from services.shared.redis_keys import (
|
||||||
QUEUE_BROKER,
|
QUEUE_BROKER,
|
||||||
|
TRADING_DEDUPE_PREFIX,
|
||||||
queue_key,
|
queue_key,
|
||||||
trading_dedupe_key,
|
trading_dedupe_key,
|
||||||
)
|
)
|
||||||
@@ -735,6 +736,9 @@ class TradingEngine:
|
|||||||
if self.redis is not None:
|
if self.redis is not None:
|
||||||
broker_queue = queue_key(QUEUE_BROKER)
|
broker_queue = queue_key(QUEUE_BROKER)
|
||||||
await self.redis.rpush(broker_queue, json.dumps(order_job))
|
await self.redis.rpush(broker_queue, json.dumps(order_job))
|
||||||
|
# Set 24h cooldown to prevent immediate re-entry
|
||||||
|
cooldown_key = f"{TRADING_DEDUPE_PREFIX}:sell_cooldown:{ticker}"
|
||||||
|
await self.redis.set(cooldown_key, "1", ex=86400)
|
||||||
logger.info(
|
logger.info(
|
||||||
"Pushed sell order for %s (%d shares, ~$%.2f) to broker queue",
|
"Pushed sell order for %s (%d shares, ~$%.2f) to broker queue",
|
||||||
ticker, sell_qty, estimated_proceeds,
|
ticker, sell_qty, estimated_proceeds,
|
||||||
@@ -779,6 +783,16 @@ class TradingEngine:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# --- Buy path ---
|
# --- Buy path ---
|
||||||
|
# Check sell cooldown — don't re-enter a position we just sold
|
||||||
|
if self.redis is not None:
|
||||||
|
cooldown_key = f"{TRADING_DEDUPE_PREFIX}:sell_cooldown:{ticker}"
|
||||||
|
if await self.redis.get(cooldown_key):
|
||||||
|
logger.info(
|
||||||
|
"Decision for %s: skip (reason=sell_cooldown)",
|
||||||
|
ticker,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
# Check if we already hold this ticker — don't double up
|
# Check if we already hold this ticker — don't double up
|
||||||
try:
|
try:
|
||||||
existing_pos = await self.pool.fetchrow(
|
existing_pos = await self.pool.fetchrow(
|
||||||
@@ -1852,6 +1866,9 @@ class TradingEngine:
|
|||||||
try:
|
try:
|
||||||
broker_queue = queue_key(QUEUE_BROKER)
|
broker_queue = queue_key(QUEUE_BROKER)
|
||||||
await self.redis.rpush(broker_queue, json.dumps(order_job))
|
await self.redis.rpush(broker_queue, json.dumps(order_job))
|
||||||
|
# Set 24h cooldown to prevent immediate re-entry
|
||||||
|
cooldown_key = f"{TRADING_DEDUPE_PREFIX}:sell_cooldown:{ticker}"
|
||||||
|
await self.redis.set(cooldown_key, "1", ex=86400)
|
||||||
logger.info("Submitted sell order for %s (%d shares): %s", ticker, quantity, reason)
|
logger.info("Submitted sell order for %s (%d shares): %s", ticker, quantity, reason)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Failed to push sell order for %s", ticker)
|
logger.exception("Failed to push sell order for %s", ticker)
|
||||||
|
|||||||
Reference in New Issue
Block a user