Files
DOLPHIN/nautilus_dolphin/tests/test_position_manager.py

112 lines
3.7 KiB
Python
Raw Normal View History

"""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()