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.execution import LiveBroker, PaperBroker
|
||||||
from crypto_spot_bot.learning import TradeLearner
|
from crypto_spot_bot.learning import TradeLearner
|
||||||
from crypto_spot_bot.market_data import MarketData
|
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.patterns import PatternAnalyzer
|
||||||
from crypto_spot_bot.strategy import SpotStrategy
|
from crypto_spot_bot.strategy import SpotStrategy
|
||||||
from crypto_spot_bot.storage import Storage
|
from crypto_spot_bot.storage import Storage
|
||||||
@@ -50,6 +50,7 @@ class CryptoSpotBot:
|
|||||||
self.market.reset_stop()
|
self.market.reset_stop()
|
||||||
if not self.market.symbols:
|
if not self.market.symbols:
|
||||||
await self.market.bootstrap()
|
await self.market.bootstrap()
|
||||||
|
self._close_paper_positions_outside_symbol_universe()
|
||||||
self._update_patterns()
|
self._update_patterns()
|
||||||
self._update_forecasts()
|
self._update_forecasts()
|
||||||
self.learner.refresh()
|
self.learner.refresh()
|
||||||
@@ -198,6 +199,31 @@ class CryptoSpotBot:
|
|||||||
enriched["over_target_exposure"] = current_exposure > target_total + self.settings.min_position_usdt
|
enriched["over_target_exposure"] = current_exposure > target_total + self.settings.min_position_usdt
|
||||||
return enriched
|
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:
|
def _reduction_candidate_id(self, prices: dict[str, float]) -> int | None:
|
||||||
rules = self._with_exposure_context(self.learner.state.adaptive_rules or {})
|
rules = self._with_exposure_context(self.learner.state.adaptive_rules or {})
|
||||||
if not rules.get("reduce_exposure") or not rules.get("over_target_exposure"):
|
if not rules.get("reduce_exposure") or not rules.get("over_target_exposure"):
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
from crypto_spot_bot.bybit import Instrument
|
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.execution import PaperBroker
|
||||||
from crypto_spot_bot.models import Signal, Ticker
|
from crypto_spot_bot.models import Signal, Ticker
|
||||||
from crypto_spot_bot.storage import Storage
|
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:
|
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 first is not None
|
||||||
assert second is None
|
assert second is None
|
||||||
assert len(broker.open_positions()) == 1
|
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