import unittest
import os
import tempfile
import json
from pathlib import Path
try:
import market_data_source as mds
except ImportError:
import sys
print("Warning: market_data_source not installed. Run 'maturin develop' first.")
sys.exit(1)
class TestMarketDataGenerator(unittest.TestCase):
def setUp(self):
self.generator = mds.MarketDataGenerator(
initial_price=100.0,
volatility=0.02,
seed=42
)
self.temp_dir = tempfile.mkdtemp()
def tearDown(self):
import shutil
if os.path.exists(self.temp_dir):
shutil.rmtree(self.temp_dir)
def test_generator_creation(self):
gen1 = mds.MarketDataGenerator()
self.assertIsNotNone(gen1)
gen2 = mds.MarketDataGenerator(
initial_price=200.0,
volatility=0.03,
trend=0.001,
min_price=150.0,
max_price=250.0,
volume_base=5000, volume_volatility=0.2,
interval="5m",
seed=123
)
self.assertIsNotNone(gen2)
config = gen2.config
self.assertAlmostEqual(config.initial_price, 200.0, places=2)
self.assertAlmostEqual(config.volatility, 0.03, places=4)
def test_generate_series(self):
data = self.generator.generate_series(100)
self.assertEqual(len(data), 100)
self.assertIsInstance(data, list)
self.assertIsInstance(data[0], dict)
required_fields = ['timestamp', 'open', 'high', 'low', 'close', 'volume']
for field in required_fields:
self.assertIn(field, data[0])
for bar in data:
self.assertGreaterEqual(bar['high'], bar['low'])
self.assertGreaterEqual(bar['high'], bar['open'])
self.assertGreaterEqual(bar['high'], bar['close'])
self.assertLessEqual(bar['low'], bar['open'])
self.assertLessEqual(bar['low'], bar['close'])
self.assertGreater(bar['volume'], 0)
def test_generate_ticks(self):
ticks = self.generator.generate_ticks(50)
self.assertEqual(len(ticks), 50)
self.assertIsInstance(ticks, list)
self.assertIsInstance(ticks[0], dict)
required_fields = ['timestamp', 'bid', 'ask', 'spread', 'volume']
for field in required_fields:
self.assertIn(field, ticks[0])
for tick in ticks:
self.assertGreater(tick['ask'], tick['bid'])
self.assertGreater(tick['spread'], 0)
self.assertAlmostEqual(tick['spread'], tick['ask'] - tick['bid'], places=6)
self.assertGreater(tick['volume'], 0)
def test_generate_series_between(self):
import time
start = int(time.time())
end = start + 3600
data = self.generator.generate_series_between(start, end)
self.assertIsInstance(data, list)
self.assertGreater(len(data), 0)
for bar in data:
self.assertGreaterEqual(bar['timestamp'], start)
self.assertLessEqual(bar['timestamp'], end)
def test_deterministic_generation(self):
gen1 = mds.MarketDataGenerator(seed=123)
gen2 = mds.MarketDataGenerator(seed=123)
data1 = gen1.generate_series(10)
data2 = gen2.generate_series(10)
self.assertEqual(len(data1), len(data2))
for bar1, bar2 in zip(data1, data2):
self.assertAlmostEqual(bar1['open'], bar2['open'], places=10)
self.assertAlmostEqual(bar1['close'], bar2['close'], places=10)
def test_set_seed(self):
data1 = self.generator.generate_series(5)
self.generator.set_seed(999)
data2 = self.generator.generate_series(5)
self.generator.set_seed(999)
data3 = self.generator.generate_series(5)
self.assertNotEqual(data1[0]['close'], data2[0]['close'])
self.assertAlmostEqual(data2[0]['close'], data3[0]['close'], places=10)
def test_reset(self):
initial_data = self.generator.generate_series(5)
self.generator.reset()
reset_data = self.generator.generate_series(5)
self.assertAlmostEqual(
initial_data[0]['open'],
reset_data[0]['open'],
places=2
)
def test_preset_configs(self):
volatile = mds.volatile_config()
self.assertIsNotNone(volatile)
config = volatile.config
self.assertGreater(config.volatility, 0.03)
stable = mds.stable_config()
self.assertIsNotNone(stable)
config = stable.config
self.assertLess(config.volatility, 0.01)
bull = mds.bull_market_config()
self.assertIsNotNone(bull)
config = bull.config
self.assertGreater(config.trend_strength, 0)
bear = mds.bear_market_config()
self.assertIsNotNone(bear)
config = bear.config
self.assertGreater(config.trend_strength, 0) self.assertIn("Bearish", config.trend_direction)
def test_price_boundaries(self):
gen = mds.MarketDataGenerator(
initial_price=100.0,
min_price=90.0,
max_price=110.0,
volatility=0.1, seed=42
)
data = gen.generate_series(1000)
for bar in data:
self.assertGreaterEqual(bar['low'], 90.0)
self.assertLessEqual(bar['high'], 110.0)
def test_different_intervals(self):
intervals = ["1m", "5m", "15m", "30m", "1h", "4h", "1d"]
for interval in intervals:
gen = mds.MarketDataGenerator(interval=interval)
data = gen.generate_series(10)
self.assertEqual(len(data), 10)
if len(data) > 1:
expected_seconds = {
"1m": 60,
"5m": 300,
"15m": 900,
"30m": 1800,
"1h": 3600,
"4h": 14400,
"1d": 86400
}[interval]
actual_ms = data[1]['timestamp'] - data[0]['timestamp']
expected_ms = expected_seconds * 1000
self.assertEqual(actual_ms, expected_ms)
class TestDataQuality(unittest.TestCase):
def test_price_continuity(self):
gen = mds.MarketDataGenerator(seed=42)
data = gen.generate_series(100)
for i in range(1, len(data)):
prev_close = data[i-1]['close']
curr_open = data[i]['open']
self.assertAlmostEqual(prev_close, curr_open, places=10)
def test_volume_properties(self):
gen = mds.MarketDataGenerator(
volume_base=1000000, volume_volatility=0.2,
seed=42
)
data = gen.generate_series(1000)
volumes = [bar['volume'] for bar in data]
self.assertTrue(all(v > 0 for v in volumes))
avg_volume = sum(volumes) / len(volumes)
self.assertAlmostEqual(avg_volume, 1000000.0, delta=100000)
def test_trend_effect(self):
gen_up = mds.MarketDataGenerator(
initial_price=100.0,
trend=0.001,
volatility=0.001, seed=42
)
data_up = gen_up.generate_series(100)
gen_down = mds.MarketDataGenerator(
initial_price=100.0,
trend=-0.001,
volatility=0.001,
seed=42
)
data_down = gen_down.generate_series(100)
final_up = data_up[-1]['close']
final_down = data_down[-1]['close']
self.assertGreater(final_up, 100.0)
self.assertLess(final_down, 100.0)
if __name__ == "__main__":
unittest.main()