diff options
| author | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-01 15:56:35 +0900 |
|---|---|---|
| committer | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-01 15:56:35 +0900 |
| commit | 33b14aaa2344b0fd95d1629627c3d135b24ae102 (patch) | |
| tree | 90b214758bc3b076baa7711226a1a1be6268e72e /services/portfolio-manager/tests | |
| parent | 9360f1a800aa29b40399a2f3bfbfcf215a04e279 (diff) | |
feat: initial trading platform implementation
Binance spot crypto trading platform with microservices architecture:
- shared: Pydantic models, Redis Streams broker, asyncpg DB layer
- data-collector: Binance WebSocket/REST market data collection
- strategy-engine: Plugin-based strategy execution (RSI, Grid)
- order-executor: Order execution with risk management
- portfolio-manager: Position tracking and PnL calculation
- backtester: Historical strategy testing with simulator
- cli: Click-based CLI for all operations
- Docker Compose orchestration with Redis and PostgreSQL
- 24 test files covering all modules
Diffstat (limited to 'services/portfolio-manager/tests')
| -rw-r--r-- | services/portfolio-manager/tests/__init__.py | 0 | ||||
| -rw-r--r-- | services/portfolio-manager/tests/test_pnl.py | 32 | ||||
| -rw-r--r-- | services/portfolio-manager/tests/test_portfolio.py | 57 |
3 files changed, 89 insertions, 0 deletions
diff --git a/services/portfolio-manager/tests/__init__.py b/services/portfolio-manager/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/services/portfolio-manager/tests/__init__.py diff --git a/services/portfolio-manager/tests/test_pnl.py b/services/portfolio-manager/tests/test_pnl.py new file mode 100644 index 0000000..4462adc --- /dev/null +++ b/services/portfolio-manager/tests/test_pnl.py @@ -0,0 +1,32 @@ +"""Tests for PnL calculation functions.""" +from decimal import Decimal + +from portfolio_manager.pnl import calculate_realized_pnl, calculate_unrealized_pnl + + +def test_unrealized_pnl_profit() -> None: + result = calculate_unrealized_pnl( + quantity=Decimal("0.1"), + avg_entry_price=Decimal("50000"), + current_price=Decimal("55000"), + ) + assert result == Decimal("500") + + +def test_unrealized_pnl_loss() -> None: + result = calculate_unrealized_pnl( + quantity=Decimal("0.1"), + avg_entry_price=Decimal("50000"), + current_price=Decimal("45000"), + ) + assert result == Decimal("-500") + + +def test_realized_pnl_single_trade() -> None: + result = calculate_realized_pnl( + buy_price=Decimal("50000"), + sell_price=Decimal("55000"), + quantity=Decimal("0.1"), + fee=Decimal("5.5"), + ) + assert result == Decimal("494.5") diff --git a/services/portfolio-manager/tests/test_portfolio.py b/services/portfolio-manager/tests/test_portfolio.py new file mode 100644 index 0000000..26319ca --- /dev/null +++ b/services/portfolio-manager/tests/test_portfolio.py @@ -0,0 +1,57 @@ +"""Tests for PortfolioTracker.""" +from decimal import Decimal + +from shared.models import Order, OrderSide, OrderStatus, OrderType +from portfolio_manager.portfolio import PortfolioTracker + + +def make_order(side: OrderSide, price: str, quantity: str) -> Order: + """Helper to create a filled Order.""" + return Order( + signal_id="test-signal", + symbol="BTC/USDT", + side=side, + type=OrderType.MARKET, + price=Decimal(price), + quantity=Decimal(quantity), + status=OrderStatus.FILLED, + ) + + +def test_portfolio_add_buy_order() -> None: + tracker = PortfolioTracker() + order = make_order(OrderSide.BUY, "50000", "0.1") + tracker.apply_order(order) + + position = tracker.get_position("BTC/USDT") + assert position is not None + assert position.quantity == Decimal("0.1") + assert position.avg_entry_price == Decimal("50000") + + +def test_portfolio_add_multiple_buys() -> None: + tracker = PortfolioTracker() + tracker.apply_order(make_order(OrderSide.BUY, "50000", "0.1")) + tracker.apply_order(make_order(OrderSide.BUY, "52000", "0.1")) + + position = tracker.get_position("BTC/USDT") + assert position is not None + assert position.quantity == Decimal("0.2") + assert position.avg_entry_price == Decimal("51000") + + +def test_portfolio_sell_reduces_position() -> None: + tracker = PortfolioTracker() + tracker.apply_order(make_order(OrderSide.BUY, "50000", "0.2")) + tracker.apply_order(make_order(OrderSide.SELL, "55000", "0.1")) + + position = tracker.get_position("BTC/USDT") + assert position is not None + assert position.quantity == Decimal("0.1") + assert position.avg_entry_price == Decimal("50000") + + +def test_portfolio_no_position_returns_none() -> None: + tracker = PortfolioTracker() + position = tracker.get_position("ETH/USDT") + assert position is None |
