File size: 3,126 Bytes
d53dc44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
"""

tests/test_order_manager.py — Order routing and management tests.



Covers:

 - Fractional → 3-step flow

 - Whole → bracket order routing

 - DRY_RUN simulation

 - Spread check threshold

 - Limit price computation

"""
from __future__ import annotations

import datetime
from unittest.mock import patch, MagicMock

import pytest

from contracts import OrderRequest
from execution.order_manager import (
    submit_order,
    check_spread,
    compute_limit_price,
)


@pytest.fixture
def buy_order():
    return OrderRequest(
        symbol="AAPL",
        side="buy",
        qty=10.0,
        limit_price=150.50,
        stop_price=148.00,
        tp_price=155.00,
        is_fractional=False,
        dry_run=True,
    )


@pytest.fixture
def fractional_order():
    return OrderRequest(
        symbol="AAPL",
        side="buy",
        qty=0.5,
        limit_price=150.50,
        stop_price=148.00,
        tp_price=155.00,
        is_fractional=True,
        dry_run=True,
    )


class TestOrderRouting:
    def test_dry_run_whole_order(self, buy_order):
        """DRY_RUN whole-share order should return a simulated fill."""
        result = submit_order(buy_order, alert_callback=lambda *a, **kw: None)
        assert result is not None

    def test_dry_run_fractional_order(self, fractional_order):
        """DRY_RUN fractional order should return a simulated fill."""
        result = submit_order(fractional_order, alert_callback=lambda *a, **kw: None)
        assert result is not None


class TestSpreadCheck:
    def test_dry_run_spread_always_passes(self):
        """In DRY_RUN, spread check always passes."""
        ok, bid, ask, spread_pct = check_spread("AAPL")
        assert ok

    @patch("execution.order_manager.config.DRY_RUN", False)
    @patch("execution.order_manager.broker.get_latest_quote")
    def test_acceptable_spread(self, mock_quote):
        mock_quote.return_value = {"bp": 150.00, "ap": 150.10}
        ok, bid, ask, spread_pct = check_spread("AAPL")
        assert ok
        assert spread_pct < 0.5  # 0.067% spread

    @patch("execution.order_manager.config.DRY_RUN", False)
    @patch("execution.order_manager.broker.get_latest_quote")
    def test_wide_spread_rejected(self, mock_quote):
        mock_quote.return_value = {"bp": 150.00, "ap": 155.00}
        ok, bid, ask, spread_pct = check_spread("AAPL")
        assert not ok
        assert spread_pct > 0.5


class TestLimitPrice:
    def test_buy_limit_within_spread(self):
        price = compute_limit_price("buy", bid=150.00, ask=150.10)
        assert price >= 150.00
        assert price <= 150.10

    def test_sell_limit_within_spread(self):
        price = compute_limit_price("sell", bid=150.00, ask=150.10)
        assert price >= 150.00
        assert price <= 150.10


class TestDryRunSimulation:
    def test_dry_run_returns_fill(self, buy_order):
        buy_order.dry_run = True
        result = submit_order(buy_order, alert_callback=lambda *a, **kw: None)
        assert result is not None