b977c371e5
Adjust .gitignore to keep src/multi_swarm/data/ tracked (only top-level /data/ cache directory remains ignored). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
65 lines
2.1 KiB
Python
65 lines
2.1 KiB
Python
from datetime import UTC, datetime
|
|
from pathlib import Path
|
|
|
|
import pandas as pd
|
|
import pytest
|
|
|
|
from multi_swarm.data.ohlcv_loader import OHLCVLoader, OHLCVRequest
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_ohlcv_rows():
|
|
base_ts = int(datetime(2024, 1, 1, tzinfo=UTC).timestamp() * 1000)
|
|
rows = []
|
|
for i in range(48):
|
|
rows.append(
|
|
[base_ts + i * 3600 * 1000, 40000 + i, 40100 + i, 39900 + i, 40050 + i, 100.0 + i]
|
|
)
|
|
return rows
|
|
|
|
|
|
def test_loader_fetches_and_caches(tmp_path: Path, mocker, sample_ohlcv_rows):
|
|
fake_exchange = mocker.MagicMock()
|
|
fake_exchange.fetch_ohlcv.return_value = sample_ohlcv_rows
|
|
mocker.patch("multi_swarm.data.ohlcv_loader.ccxt.binance", return_value=fake_exchange)
|
|
|
|
loader = OHLCVLoader(cache_dir=tmp_path)
|
|
req = OHLCVRequest(
|
|
symbol="BTC/USDT",
|
|
timeframe="1h",
|
|
start=datetime(2024, 1, 1, tzinfo=UTC),
|
|
end=datetime(2024, 1, 3, tzinfo=UTC),
|
|
)
|
|
df = loader.load(req)
|
|
|
|
assert isinstance(df, pd.DataFrame)
|
|
assert list(df.columns) == ["open", "high", "low", "close", "volume"]
|
|
assert len(df) == 48
|
|
assert df.index.is_monotonic_increasing
|
|
cache_files = list(tmp_path.glob("*.parquet"))
|
|
assert len(cache_files) == 1
|
|
|
|
|
|
def test_loader_uses_cache_on_second_call(tmp_path: Path, mocker, sample_ohlcv_rows):
|
|
fake_exchange = mocker.MagicMock()
|
|
fake_exchange.fetch_ohlcv.return_value = sample_ohlcv_rows
|
|
mocker.patch("multi_swarm.data.ohlcv_loader.ccxt.binance", return_value=fake_exchange)
|
|
|
|
loader = OHLCVLoader(cache_dir=tmp_path)
|
|
req = OHLCVRequest(
|
|
symbol="BTC/USDT",
|
|
timeframe="1h",
|
|
start=datetime(2024, 1, 1, tzinfo=UTC),
|
|
end=datetime(2024, 1, 3, tzinfo=UTC),
|
|
)
|
|
df1 = loader.load(req)
|
|
df2 = loader.load(req)
|
|
|
|
assert fake_exchange.fetch_ohlcv.call_count == 2 # paginazione interna, non caching
|
|
pd.testing.assert_frame_equal(df1, df2)
|
|
# Seconda chiamata legge da cache, non chiama exchange
|
|
fake_exchange.fetch_ohlcv.reset_mock()
|
|
df3 = loader.load(req)
|
|
assert fake_exchange.fetch_ohlcv.call_count == 0
|
|
pd.testing.assert_frame_equal(df1, df3)
|