initial: import DOLPHIN baseline 2026-04-21 from dolphinng5_predict working tree
Includes core prod + GREEN/BLUE subsystems: - prod/ (BLUE harness, configs, scripts, docs) - nautilus_dolphin/ (GREEN Nautilus-native impl + dvae/ preserved) - adaptive_exit/ (AEM engine + models/bucket_assignments.pkl) - Observability/ (EsoF advisor, TUI, dashboards) - external_factors/ (EsoF producer) - mc_forewarning_qlabs_fork/ (MC regime/envelope) Excludes runtime caches, logs, backups, and reproducible artifacts per .gitignore.
This commit is contained in:
111
nautilus_dolphin/tests/test_position_manager.py
Executable file
111
nautilus_dolphin/tests/test_position_manager.py
Executable file
@@ -0,0 +1,111 @@
|
||||
"""Tests for PositionManager."""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock
|
||||
from nautilus_dolphin.nautilus.position_manager import PositionManager
|
||||
from nautilus_trader.model.enums import PositionSide
|
||||
|
||||
|
||||
class TestPositionManager:
|
||||
|
||||
def test_on_position_opened_calculates_tp(self):
|
||||
"""Test TP price calculation on position open."""
|
||||
strategy = Mock()
|
||||
pm = PositionManager(strategy, tp_bps=99, max_hold_bars=120)
|
||||
|
||||
position = Mock()
|
||||
position.id = "POS-001"
|
||||
position.instrument_id = "BTCUSDT.BINANCE"
|
||||
position.avg_px_open = 50000.0
|
||||
position.side = PositionSide.SHORT
|
||||
|
||||
pm.on_position_opened(position)
|
||||
|
||||
assert "BTCUSDT.BINANCE" in pm.positions
|
||||
metadata = pm.positions["BTCUSDT.BINANCE"]
|
||||
|
||||
# TP should be entry * (1 - 99/10000) = 50000 * 0.9901
|
||||
expected_tp = 50000.0 * 0.9901
|
||||
assert abs(metadata['tp_price'] - expected_tp) < 0.01
|
||||
|
||||
def test_on_bar_increments_bars_held(self):
|
||||
"""Test bars_held counter increments."""
|
||||
strategy = Mock()
|
||||
strategy.cache = Mock()
|
||||
pm = PositionManager(strategy)
|
||||
|
||||
position = Mock()
|
||||
position.id = "POS-001"
|
||||
position.instrument_id = "BTCUSDT.BINANCE"
|
||||
position.avg_px_open = 50000.0
|
||||
position.side = PositionSide.SHORT
|
||||
position.is_closed = False
|
||||
|
||||
pm.on_position_opened(position)
|
||||
strategy.cache.position.return_value = position
|
||||
|
||||
bar = Mock()
|
||||
bar.instrument_id = "BTCUSDT.BINANCE"
|
||||
bar.close = 50000.0
|
||||
|
||||
pm.on_bar(bar)
|
||||
assert pm.positions["BTCUSDT.BINANCE"]['bars_held'] == 1
|
||||
|
||||
pm.on_bar(bar)
|
||||
assert pm.positions["BTCUSDT.BINANCE"]['bars_held'] == 2
|
||||
|
||||
def test_tp_exit_triggered(self):
|
||||
"""Test TP exit when price hits target."""
|
||||
strategy = Mock()
|
||||
strategy.cache = Mock()
|
||||
strategy.order_factory = Mock()
|
||||
pm = PositionManager(strategy, tp_bps=99)
|
||||
|
||||
position = Mock()
|
||||
position.id = "POS-001"
|
||||
position.instrument_id = "BTCUSDT.BINANCE"
|
||||
position.avg_px_open = 50000.0
|
||||
position.side = PositionSide.SHORT
|
||||
position.is_closed = False
|
||||
position.quantity = 1.0
|
||||
|
||||
pm.on_position_opened(position)
|
||||
strategy.cache.position.return_value = position
|
||||
|
||||
bar = Mock()
|
||||
bar.instrument_id = "BTCUSDT.BINANCE"
|
||||
bar.close = 49505.0 # Below TP
|
||||
|
||||
pm.on_bar(bar)
|
||||
|
||||
# Should have called order_factory.market
|
||||
strategy.order_factory.market.assert_called_once()
|
||||
|
||||
def test_max_hold_exit_triggered(self):
|
||||
"""Test max hold exit after 120 bars."""
|
||||
strategy = Mock()
|
||||
strategy.cache = Mock()
|
||||
strategy.order_factory = Mock()
|
||||
pm = PositionManager(strategy, max_hold_bars=3)
|
||||
|
||||
position = Mock()
|
||||
position.id = "POS-001"
|
||||
position.instrument_id = "BTCUSDT.BINANCE"
|
||||
position.avg_px_open = 50000.0
|
||||
position.side = PositionSide.SHORT
|
||||
position.is_closed = False
|
||||
position.quantity = 1.0
|
||||
|
||||
pm.on_position_opened(position)
|
||||
strategy.cache.position.return_value = position
|
||||
|
||||
bar = Mock()
|
||||
bar.instrument_id = "BTCUSDT.BINANCE"
|
||||
bar.close = 50100.0 # Above TP
|
||||
|
||||
# Trigger max hold
|
||||
for _ in range(3):
|
||||
pm.on_bar(bar)
|
||||
|
||||
# Should have called order_factory.market
|
||||
strategy.order_factory.market.assert_called_once()
|
||||
Reference in New Issue
Block a user