From 2446214389fb8f4644d1a24a19e5e3d7b55e8651 Mon Sep 17 00:00:00 2001 From: TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:05:25 +0900 Subject: refactor: replace Binance/ccxt with Alpaca API client for US stocks --- shared/tests/test_alpaca.py | 67 +++++++++++++++++++++++++++++++++++++++++++ shared/tests/test_exchange.py | 55 ----------------------------------- shared/tests/test_models.py | 10 ++----- 3 files changed, 69 insertions(+), 63 deletions(-) create mode 100644 shared/tests/test_alpaca.py delete mode 100644 shared/tests/test_exchange.py (limited to 'shared/tests') diff --git a/shared/tests/test_alpaca.py b/shared/tests/test_alpaca.py new file mode 100644 index 0000000..7c8eab1 --- /dev/null +++ b/shared/tests/test_alpaca.py @@ -0,0 +1,67 @@ +"""Tests for Alpaca API client.""" +import pytest +from unittest.mock import AsyncMock, MagicMock +from shared.alpaca import AlpacaClient + + +@pytest.fixture +def client(): + return AlpacaClient(api_key="test-key", api_secret="test-secret", paper=True) + + +def test_client_uses_paper_url(client): + assert "paper" in client._base_url + + +def test_client_uses_live_url(): + c = AlpacaClient(api_key="k", api_secret="s", paper=False) + assert "paper" not in c._base_url + + +def test_client_headers(client): + h = client.headers + assert h["APCA-API-KEY-ID"] == "test-key" + assert h["APCA-API-SECRET-KEY"] == "test-secret" + + +@pytest.mark.asyncio +async def test_get_buying_power(client): + mock_response = AsyncMock() + mock_response.status = 200 + mock_response.json = AsyncMock(return_value={"buying_power": "10000.00"}) + mock_response.__aenter__ = AsyncMock(return_value=mock_response) + mock_response.__aexit__ = AsyncMock(return_value=False) + + mock_session = MagicMock() + mock_session.closed = False + mock_session.request = MagicMock(return_value=mock_response) + mock_session.close = AsyncMock() + client._session = mock_session + + result = await client.get_buying_power() + from decimal import Decimal + assert result == Decimal("10000.00") + await client.close() + + +@pytest.mark.asyncio +async def test_submit_moc_order(client): + mock_response = AsyncMock() + mock_response.status = 200 + mock_response.json = AsyncMock(return_value={"id": "order-1", "status": "accepted"}) + mock_response.__aenter__ = AsyncMock(return_value=mock_response) + mock_response.__aexit__ = AsyncMock(return_value=False) + + mock_session = MagicMock() + mock_session.closed = False + mock_session.request = MagicMock(return_value=mock_response) + mock_session.close = AsyncMock() + client._session = mock_session + + result = await client.submit_moc_order("AAPL", qty=10, side="buy") + assert result["id"] == "order-1" + + # Verify the request was made with correct params + call_args = mock_session.request.call_args + assert call_args[0][0] == "POST" + await client.close() diff --git a/shared/tests/test_exchange.py b/shared/tests/test_exchange.py deleted file mode 100644 index 95dc7d7..0000000 --- a/shared/tests/test_exchange.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Tests for the exchange factory.""" - -from unittest.mock import patch - -import ccxt.async_support as ccxt -import pytest - -from shared.exchange import create_exchange - - -def test_create_exchange_binance(): - """Verify create_exchange returns a ccxt.binance instance.""" - exchange = create_exchange( - exchange_id="binance", - api_key="test-key", - api_secret="test-secret", - ) - assert isinstance(exchange, ccxt.binance) - assert exchange.apiKey == "test-key" - assert exchange.secret == "test-secret" - assert exchange.enableRateLimit is True - - -def test_create_exchange_unknown(): - """Verify create_exchange raises ValueError for unknown exchange.""" - with pytest.raises(ValueError, match="Unknown exchange 'not_a_real_exchange'"): - create_exchange( - exchange_id="not_a_real_exchange", - api_key="key", - api_secret="secret", - ) - - -def test_create_exchange_with_sandbox(): - """Verify sandbox mode is activated when sandbox=True.""" - with patch.object(ccxt.binance, "set_sandbox_mode") as mock_sandbox: - exchange = create_exchange( - exchange_id="binance", - api_key="key", - api_secret="secret", - sandbox=True, - ) - mock_sandbox.assert_called_once_with(True) - assert isinstance(exchange, ccxt.binance) - - -def test_create_exchange_no_sandbox_by_default(): - """Verify sandbox mode is not set when sandbox=False (default).""" - with patch.object(ccxt.binance, "set_sandbox_mode") as mock_sandbox: - create_exchange( - exchange_id="binance", - api_key="key", - api_secret="secret", - ) - mock_sandbox.assert_not_called() diff --git a/shared/tests/test_models.py b/shared/tests/test_models.py index e3b9f12..2b8cd5e 100644 --- a/shared/tests/test_models.py +++ b/shared/tests/test_models.py @@ -8,15 +8,9 @@ from unittest.mock import patch def test_settings_defaults(): """Test that Settings has correct defaults.""" - with patch.dict( - os.environ, - { - "BINANCE_API_KEY": "test_key", - "BINANCE_API_SECRET": "test_secret", - }, - ): - from shared.config import Settings + from shared.config import Settings + with patch.dict(os.environ, {}, clear=False): settings = Settings() assert settings.redis_url == "redis://localhost:6379" assert settings.database_url == "postgresql://trading:trading@localhost:5432/trading" -- cgit v1.2.3