deploy: 2026-03-20 07:49

This commit is contained in:
ufo6849
2026-03-20 07:49:42 +09:00
commit d14a8bab04
73 changed files with 76534 additions and 0 deletions

0
tests/__init__.py Normal file
View File

60
tests/test_ict_engine.py Normal file
View File

@@ -0,0 +1,60 @@
"""Tests for the ICT indicator engine."""
import pytest
import pandas as pd
import numpy as np
from indicators.ict_engine import ICTEngine, ICTSignals
def generate_sample_ohlc(n: int = 200) -> pd.DataFrame:
"""Generate synthetic OHLC data for testing."""
np.random.seed(42)
dates = pd.date_range("2025-01-01", periods=n, freq="1h")
close = 50000 + np.cumsum(np.random.randn(n) * 100)
high = close + np.abs(np.random.randn(n) * 50)
low = close - np.abs(np.random.randn(n) * 50)
open_ = close + np.random.randn(n) * 30
volume = np.random.randint(100, 10000, n).astype(float)
return pd.DataFrame(
{"open": open_, "high": high, "low": low, "close": close, "volume": volume},
index=dates,
)
class TestICTEngine:
"""Tests for ICTEngine."""
def test_init_default_swing_length(self):
engine = ICTEngine()
assert engine.swing_length == 50
def test_init_custom_swing_length(self):
engine = ICTEngine(swing_length=30)
assert engine.swing_length == 30
def test_analyze_returns_ict_signals(self):
engine = ICTEngine(swing_length=10)
df = generate_sample_ohlc(100)
try:
result = engine.analyze(df)
assert isinstance(result, ICTSignals)
assert isinstance(result.swing_highs_lows, pd.DataFrame)
assert isinstance(result.fvg, pd.DataFrame)
assert isinstance(result.bos_choch, pd.DataFrame)
assert isinstance(result.order_blocks, pd.DataFrame)
except ImportError:
pytest.skip("smartmoneyconcepts not installed")
def test_analyze_too_few_candles(self):
engine = ICTEngine(swing_length=50)
df = generate_sample_ohlc(10)
with pytest.raises(ValueError, match="Need at least"):
engine.analyze(df)
def test_analyze_empty_dataframe(self):
engine = ICTEngine(swing_length=10)
df = pd.DataFrame()
with pytest.raises(ValueError):
engine.analyze(df)

View File

@@ -0,0 +1,37 @@
"""Tests for the order manager."""
import pytest
from execution.order_manager import Order, OrderStatus
class TestOrder:
"""Tests for Order dataclass."""
def test_order_defaults(self):
order = Order(
id="test-001",
symbol="BTC/USDT",
side="buy",
order_type="market",
amount=0.01,
)
assert order.status == OrderStatus.PENDING
assert order.filled_amount == 0.0
assert order.fee == 0.0
assert order.exchange_order_id is None
def test_order_status_transitions(self):
order = Order(
id="test-002",
symbol="ETH/USDT",
side="sell",
order_type="limit",
amount=0.5,
price=3000.0,
)
order.status = OrderStatus.FILLED
assert order.status == OrderStatus.FILLED
order.status = OrderStatus.CANCELLED
assert order.status == OrderStatus.CANCELLED

View File

@@ -0,0 +1,70 @@
"""Tests for the risk management engine."""
import pytest
from risk.risk_manager import RiskManager
class TestRiskManager:
"""Tests for RiskManager."""
def setup_method(self):
self.rm = RiskManager(
max_risk_per_trade=0.02,
max_daily_loss=0.05,
max_concurrent_positions=3,
max_leverage=3,
max_drawdown=0.15,
)
def test_calculate_position_size(self):
size = self.rm.calculate_position_size(
balance=1000, entry_price=50000, stop_loss=49000, risk_pct=0.02
)
# risk = 1000 * 0.02 = 20, price_risk = 1000
# size = 20 / 1000 = 0.02
assert size == 0.02
def test_position_size_zero_risk(self):
size = self.rm.calculate_position_size(
balance=1000, entry_price=50000, stop_loss=50000
)
assert size == 0.0
def test_approve_trade_success(self):
approval = self.rm.approve_trade(
entry_price=50000, stop_loss=49000, balance=1000
)
assert approval.approved
assert approval.position_size > 0
def test_approve_trade_max_positions(self):
for _ in range(3):
self.rm.on_position_opened()
approval = self.rm.approve_trade(
entry_price=50000, stop_loss=49000, balance=1000
)
assert not approval.approved
assert "concurrent" in approval.reason.lower()
def test_emergency_stop(self):
self.rm.emergency_stop()
assert self.rm.is_stopped
approval = self.rm.approve_trade(
entry_price=50000, stop_loss=49000, balance=1000
)
assert not approval.approved
def test_reset_emergency(self):
self.rm.emergency_stop()
self.rm.reset_emergency()
assert not self.rm.is_stopped
def test_daily_pnl_tracking(self):
self.rm.update_daily_pnl(50)
self.rm.update_daily_pnl(-30)
assert self.rm.get_daily_pnl() == 20
def test_drawdown_check(self):
self.rm._peak_equity = 1000
assert not self.rm.check_drawdown(900) # 10% < 15%
assert self.rm.check_drawdown(800) # 20% >= 15%

View File

@@ -0,0 +1,52 @@
"""Tests for the signal generator module."""
import pytest
from datetime import datetime
from indicators.multi_timeframe import TradeDirection
from strategy.signal_generator import TradeSignal
class TestTradeSignal:
"""Tests for TradeSignal dataclass."""
def test_risk_reward_ratio(self):
signal = TradeSignal(
symbol="BTC/USDT",
direction=TradeDirection.LONG,
entry_price=50000,
stop_loss=49000,
take_profit=52000,
confidence=4,
timeframe="15m",
)
assert signal.risk_reward_ratio == 2.0
def test_risk_reward_zero_risk(self):
signal = TradeSignal(
symbol="BTC/USDT",
direction=TradeDirection.LONG,
entry_price=50000,
stop_loss=50000,
take_profit=52000,
confidence=3,
timeframe="15m",
)
assert signal.risk_reward_ratio == 0.0
def test_to_dict(self):
signal = TradeSignal(
symbol="ETH/USDT",
direction=TradeDirection.SHORT,
entry_price=3000,
stop_loss=3100,
take_profit=2800,
confidence=5,
timeframe="1h",
reasons=["BOS", "OB", "FVG"],
)
d = signal.to_dict()
assert d["symbol"] == "ETH/USDT"
assert d["direction"] == "SHORT"
assert d["confidence"] == 5
assert len(d["reasons"]) == 3