Close stale paper positions outside trend universe
This commit is contained in:
+27
-1
@@ -7,7 +7,7 @@ from crypto_spot_bot.config import Settings
|
||||
from crypto_spot_bot.execution import LiveBroker, PaperBroker
|
||||
from crypto_spot_bot.learning import TradeLearner
|
||||
from crypto_spot_bot.market_data import MarketData
|
||||
from crypto_spot_bot.models import BotStatus, Signal, utc_now
|
||||
from crypto_spot_bot.models import BotStatus, Signal, Ticker, utc_now
|
||||
from crypto_spot_bot.patterns import PatternAnalyzer
|
||||
from crypto_spot_bot.strategy import SpotStrategy
|
||||
from crypto_spot_bot.storage import Storage
|
||||
@@ -50,6 +50,7 @@ class CryptoSpotBot:
|
||||
self.market.reset_stop()
|
||||
if not self.market.symbols:
|
||||
await self.market.bootstrap()
|
||||
self._close_paper_positions_outside_symbol_universe()
|
||||
self._update_patterns()
|
||||
self._update_forecasts()
|
||||
self.learner.refresh()
|
||||
@@ -198,6 +199,31 @@ class CryptoSpotBot:
|
||||
enriched["over_target_exposure"] = current_exposure > target_total + self.settings.min_position_usdt
|
||||
return enriched
|
||||
|
||||
def _close_paper_positions_outside_symbol_universe(self) -> None:
|
||||
if self.settings.strategy_mode != "trend_macd" or self.settings.trading_mode != "paper":
|
||||
return
|
||||
allowed_symbols = set(self.market.symbols or self.settings.symbols)
|
||||
for position in list(self.broker.open_positions()):
|
||||
if position.symbol in allowed_symbols:
|
||||
continue
|
||||
synthetic_ticker = Ticker(
|
||||
symbol=position.symbol,
|
||||
last_price=position.entry_price,
|
||||
bid=position.entry_price,
|
||||
ask=position.entry_price,
|
||||
turnover_24h=0.0,
|
||||
volume_24h=0.0,
|
||||
change_24h=0.0,
|
||||
)
|
||||
self.broker.sell(
|
||||
position,
|
||||
synthetic_ticker,
|
||||
"trend_macd: закрыта старая paper-позиция вне списка разрешенных пар",
|
||||
)
|
||||
self.storage.event(
|
||||
f"{position.symbol}: старая paper-позиция закрыта при переходе на trend_macd"
|
||||
)
|
||||
|
||||
def _reduction_candidate_id(self, prices: dict[str, float]) -> int | None:
|
||||
rules = self._with_exposure_context(self.learner.state.adaptive_rules or {})
|
||||
if not rules.get("reduce_exposure") or not rules.get("over_target_exposure"):
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
from crypto_spot_bot.bybit import Instrument
|
||||
from crypto_spot_bot.bot import CryptoSpotBot
|
||||
from crypto_spot_bot.execution import PaperBroker
|
||||
from crypto_spot_bot.models import Signal, Ticker
|
||||
from crypto_spot_bot.storage import Storage
|
||||
from crypto_spot_bot.strategy import SpotStrategy
|
||||
|
||||
|
||||
def test_paper_broker_buy_and_sell_records_trade(make_settings, tmp_path) -> None:
|
||||
@@ -154,3 +158,47 @@ def test_trend_macd_broker_blocks_dca_for_same_symbol(make_settings, tmp_path) -
|
||||
assert first is not None
|
||||
assert second is None
|
||||
assert len(broker.open_positions()) == 1
|
||||
|
||||
|
||||
def test_trend_macd_closes_old_paper_positions_outside_symbol_universe(make_settings, tmp_path) -> None:
|
||||
settings = make_settings(
|
||||
tmp_path,
|
||||
strategy_mode="trend_macd",
|
||||
trading_mode="paper",
|
||||
symbols=("BTCUSDT", "ETHUSDT", "SOLUSDT"),
|
||||
min_position_usdt=1,
|
||||
max_position_usdt=20,
|
||||
max_symbol_exposure_usdt=20,
|
||||
max_total_exposure_usdt=80,
|
||||
max_open_positions=3,
|
||||
max_entries_per_minute=0,
|
||||
)
|
||||
storage = Storage(settings.database_path)
|
||||
broker = PaperBroker(settings, storage)
|
||||
ticker = Ticker("HYPEUSDT", 10, 9.99, 10.01, 10_000_000, 100, 0)
|
||||
instrument = Instrument("HYPEUSDT", "HYPE", "USDT", "Trading", 0.001, 0.001, 0.001, 1)
|
||||
position = broker.buy(
|
||||
Signal("HYPEUSDT", "BUY", 0.8, "old", {"position_notional_usdt": 5}),
|
||||
ticker,
|
||||
instrument,
|
||||
{"HYPEUSDT": 10},
|
||||
)
|
||||
assert position is not None
|
||||
|
||||
bot = CryptoSpotBot(
|
||||
settings,
|
||||
storage,
|
||||
SimpleNamespace(symbols=["BTCUSDT", "ETHUSDT", "SOLUSDT"]),
|
||||
broker,
|
||||
SpotStrategy(settings),
|
||||
object(),
|
||||
object(),
|
||||
)
|
||||
bot._close_paper_positions_outside_symbol_universe()
|
||||
|
||||
assert broker.open_positions() == []
|
||||
assert storage.open_positions() == []
|
||||
trade = storage.recent_trades(limit=1)[0]
|
||||
assert trade["side"] == "SELL"
|
||||
assert trade["symbol"] == "HYPEUSDT"
|
||||
assert "trend_macd" in trade["reason"]
|
||||
|
||||
Reference in New Issue
Block a user