test_risk_assessment_engine.pyโข24.2 kB
"""์ํ ํ๊ฐ ์์ง ํ
์คํธ"""
import pytest
import asyncio
import time
from datetime import datetime, timedelta
from unittest.mock import AsyncMock, MagicMock, patch
from typing import Dict, List, Any
from src.ai.risk_assessment_engine import RiskAssessmentEngine
from src.exceptions import ModelNotTrainedError, InsufficientDataError, PredictionError
class TestRiskAssessmentEngine:
"""์ํ ํ๊ฐ ์์ง ํ
์คํธ"""
@pytest.fixture
def engine_config(self):
"""์์ง ์ค์ """
return {
"risk_models": ["var", "cvar", "drawdown", "volatility", "beta"],
"confidence_levels": [0.95, 0.99],
"time_horizons": [1, 5, 10, 30], # days
"lookback_period": 252, # trading days
"monte_carlo_simulations": 10000,
"risk_factors": {
"market_risk": 0.4,
"credit_risk": 0.2,
"liquidity_risk": 0.15,
"operational_risk": 0.15,
"model_risk": 0.1
},
"stress_scenarios": ["market_crash", "interest_rate_shock", "currency_crisis"],
"portfolio_constraints": {
"max_position_size": 0.1, # 10%
"max_sector_exposure": 0.3, # 30%
"min_diversification_score": 0.7
}
}
@pytest.fixture
def risk_engine(self, engine_config):
"""์ํ ํ๊ฐ ์์ง ์ธ์คํด์ค"""
return RiskAssessmentEngine(engine_config)
@pytest.fixture
def sample_portfolio_data(self):
"""์ํ ํฌํธํด๋ฆฌ์ค ๋ฐ์ดํฐ"""
return {
"positions": [
{
"symbol": "005930", # ์ผ์ฑ์ ์
"weight": 0.25,
"market_value": 25000000,
"sector": "technology",
"beta": 1.2,
"volatility": 0.25
},
{
"symbol": "000660", # SKํ์ด๋์ค
"weight": 0.20,
"market_value": 20000000,
"sector": "technology",
"beta": 1.5,
"volatility": 0.35
},
{
"symbol": "035420", # NAVER
"weight": 0.15,
"market_value": 15000000,
"sector": "internet",
"beta": 1.1,
"volatility": 0.30
},
{
"symbol": "051910", # LGํํ
"weight": 0.20,
"market_value": 20000000,
"sector": "chemicals",
"beta": 0.9,
"volatility": 0.28
},
{
"symbol": "028260", # ์ผ์ฑ๋ฌผ์ฐ
"weight": 0.20,
"market_value": 20000000,
"sector": "construction",
"beta": 0.8,
"volatility": 0.22
}
],
"total_value": 100000000, # 1์ต์
"currency": "KRW",
"benchmark": "KOSPI",
"creation_date": "2024-01-01"
}
@pytest.fixture
def sample_market_data(self):
"""์ํ ์์ฅ ๋ฐ์ดํฐ"""
return {
"005930": {
"prices": [75000 + i * 100 for i in range(252)], # 1๋
์น ๋ฐ์ดํฐ
"returns": [0.001 + i * 0.0001 for i in range(252)],
"volumes": [1000000 + i * 1000 for i in range(252)]
},
"000660": {
"prices": [80000 + i * 150 for i in range(252)],
"returns": [0.002 + i * 0.0002 for i in range(252)],
"volumes": [800000 + i * 800 for i in range(252)]
},
"KOSPI": {
"prices": [2500 + i * 2 for i in range(252)],
"returns": [0.0008 + i * 0.00008 for i in range(252)]
}
}
def test_engine_initialization(self, risk_engine, engine_config):
"""์์ง ์ด๊ธฐํ ํ
์คํธ"""
assert risk_engine.risk_models == engine_config["risk_models"]
assert risk_engine.confidence_levels == engine_config["confidence_levels"]
assert risk_engine.time_horizons == engine_config["time_horizons"]
assert risk_engine.lookback_period == engine_config["lookback_period"]
assert risk_engine.monte_carlo_simulations == engine_config["monte_carlo_simulations"]
assert risk_engine.risk_factors == engine_config["risk_factors"]
assert risk_engine.is_calibrated == False
@pytest.mark.asyncio
async def test_model_calibration(self, risk_engine, sample_market_data):
"""๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
ํ
์คํธ"""
# ์บ๋ฆฌ๋ธ๋ ์ด์
์ ์ํ
assert risk_engine.is_calibrated == False
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
calibration_result = await risk_engine.calibrate_models(sample_market_data)
# ์บ๋ฆฌ๋ธ๋ ์ด์
ํ ์ํ
assert risk_engine.is_calibrated == True
assert "calibrated_models" in calibration_result
assert "calibration_metrics" in calibration_result
assert "model_parameters" in calibration_result
# ๊ฐ ์ํ ๋ชจ๋ธ์ด ์บ๋ฆฌ๋ธ๋ ์ด์
๋์๋์ง ํ์ธ
calibrated_models = calibration_result["calibrated_models"]
for model in risk_engine.risk_models:
assert model in calibrated_models
@pytest.mark.asyncio
async def test_var_calculation(self, risk_engine, sample_portfolio_data, sample_market_data):
"""VaR ๊ณ์ฐ ํ
์คํธ"""
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
await risk_engine.calibrate_models(sample_market_data)
# VaR ๊ณ์ฐ
var_results = await risk_engine.calculate_var(
sample_portfolio_data,
confidence_level=0.95,
time_horizon=1
)
# ๊ฒฐ๊ณผ ๊ฒ์ฆ
assert "var_amount" in var_results
assert "var_percentage" in var_results
assert "confidence_level" in var_results
assert "time_horizon" in var_results
assert "methodology" in var_results
# VaR ๊ฐ์ด ํฉ๋ฆฌ์ ์ธ ๋ฒ์์ธ์ง ํ์ธ
var_amount = var_results["var_amount"]
portfolio_value = sample_portfolio_data["total_value"]
var_percentage = abs(var_amount) / portfolio_value
assert 0 < var_percentage < 0.2 # 0-20% ๋ฒ์
assert var_results["confidence_level"] == 0.95
assert var_results["time_horizon"] == 1
@pytest.mark.asyncio
async def test_cvar_calculation(self, risk_engine, sample_portfolio_data, sample_market_data):
"""CVaR (Conditional VaR) ๊ณ์ฐ ํ
์คํธ"""
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
await risk_engine.calibrate_models(sample_market_data)
# CVaR ๊ณ์ฐ
cvar_results = await risk_engine.calculate_cvar(
sample_portfolio_data,
confidence_level=0.95,
time_horizon=1
)
# ๊ฒฐ๊ณผ ๊ฒ์ฆ
assert "cvar_amount" in cvar_results
assert "cvar_percentage" in cvar_results
assert "var_amount" in cvar_results # CVaR๋ VaR๋ ํฌํจ
assert "expected_shortfall" in cvar_results
# CVaR์ ํญ์ VaR๋ณด๋ค ํฌ๊ฑฐ๋ ๊ฐ์์ผ ํจ
assert abs(cvar_results["cvar_amount"]) >= abs(cvar_results["var_amount"])
@pytest.mark.asyncio
async def test_maximum_drawdown_analysis(self, risk_engine, sample_portfolio_data, sample_market_data):
"""์ต๋ ๋ํญ ๋ถ์ ํ
์คํธ"""
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
await risk_engine.calibrate_models(sample_market_data)
# ์ต๋ ๋ํญ ๋ถ์
drawdown_results = await risk_engine.analyze_maximum_drawdown(
sample_portfolio_data,
sample_market_data
)
# ๊ฒฐ๊ณผ ๊ฒ์ฆ
assert "max_drawdown" in drawdown_results
assert "max_drawdown_duration" in drawdown_results
assert "recovery_time" in drawdown_results
assert "drawdown_periods" in drawdown_results
assert "current_drawdown" in drawdown_results
# ๋ํญ์ ์์์ด๊ฑฐ๋ 0์ด์ด์ผ ํจ
assert drawdown_results["max_drawdown"] <= 0
assert drawdown_results["max_drawdown_duration"] >= 0
@pytest.mark.asyncio
async def test_volatility_analysis(self, risk_engine, sample_portfolio_data, sample_market_data):
"""๋ณ๋์ฑ ๋ถ์ ํ
์คํธ"""
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
await risk_engine.calibrate_models(sample_market_data)
# ๋ณ๋์ฑ ๋ถ์
volatility_results = await risk_engine.analyze_volatility(
sample_portfolio_data,
sample_market_data
)
# ๊ฒฐ๊ณผ ๊ฒ์ฆ
assert "portfolio_volatility" in volatility_results
assert "annualized_volatility" in volatility_results
assert "volatility_breakdown" in volatility_results
assert "garch_forecast" in volatility_results
assert "realized_volatility" in volatility_results
# ๋ณ๋์ฑ์ ์์์ฌ์ผ ํจ
assert volatility_results["portfolio_volatility"] > 0
assert volatility_results["annualized_volatility"] > 0
@pytest.mark.asyncio
async def test_beta_analysis(self, risk_engine, sample_portfolio_data, sample_market_data):
"""๋ฒ ํ ๋ถ์ ํ
์คํธ"""
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
await risk_engine.calibrate_models(sample_market_data)
# ๋ฒ ํ ๋ถ์
beta_results = await risk_engine.analyze_beta(
sample_portfolio_data,
sample_market_data,
benchmark="KOSPI"
)
# ๊ฒฐ๊ณผ ๊ฒ์ฆ
assert "portfolio_beta" in beta_results
assert "systematic_risk" in beta_results
assert "idiosyncratic_risk" in beta_results
assert "correlation_with_market" in beta_results
assert "r_squared" in beta_results
# ๋ฒ ํ ๊ฐ์ด ํฉ๋ฆฌ์ ์ธ ๋ฒ์์ธ์ง ํ์ธ
portfolio_beta = beta_results["portfolio_beta"]
assert -2 < portfolio_beta < 3 # ์ผ๋ฐ์ ์ธ ๋ฒ ํ ๋ฒ์
# R-squared๋ 0-1 ๋ฒ์
assert 0 <= beta_results["r_squared"] <= 1
@pytest.mark.asyncio
async def test_stress_testing(self, risk_engine, sample_portfolio_data, sample_market_data):
"""์คํธ๋ ์ค ํ
์คํธ"""
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
await risk_engine.calibrate_models(sample_market_data)
# ์คํธ๋ ์ค ํ
์คํธ ์คํ
stress_results = await risk_engine.run_stress_tests(
sample_portfolio_data,
scenarios=["market_crash", "interest_rate_shock"]
)
# ๊ฒฐ๊ณผ ๊ฒ์ฆ
assert "stress_test_results" in stress_results
assert "worst_case_scenario" in stress_results
assert "scenario_rankings" in stress_results
stress_test_results = stress_results["stress_test_results"]
# ๊ฐ ์๋๋ฆฌ์ค์ ๋ํ ๊ฒฐ๊ณผ ํ์ธ
for scenario in ["market_crash", "interest_rate_shock"]:
assert scenario in stress_test_results
scenario_result = stress_test_results[scenario]
assert "portfolio_loss" in scenario_result
assert "loss_percentage" in scenario_result
assert "affected_positions" in scenario_result
@pytest.mark.asyncio
async def test_monte_carlo_simulation(self, risk_engine, sample_portfolio_data, sample_market_data):
"""๋ชฌํ
์นด๋ฅผ๋ก ์๋ฎฌ๋ ์ด์
ํ
์คํธ"""
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
await risk_engine.calibrate_models(sample_market_data)
# ๋ชฌํ
์นด๋ฅผ๋ก ์๋ฎฌ๋ ์ด์
mc_results = await risk_engine.run_monte_carlo_simulation(
sample_portfolio_data,
time_horizon=30,
num_simulations=1000 # ํ
์คํธ์ฉ์ผ๋ก ์ถ์
)
# ๊ฒฐ๊ณผ ๊ฒ์ฆ
assert "simulation_results" in mc_results
assert "statistical_summary" in mc_results
assert "percentiles" in mc_results
assert "probability_of_loss" in mc_results
# ์๋ฎฌ๋ ์ด์
๊ฒฐ๊ณผ ์ ํ์ธ
simulation_results = mc_results["simulation_results"]
assert len(simulation_results) == 1000
# ํต๊ณ ์์ฝ ํ์ธ
statistical_summary = mc_results["statistical_summary"]
assert "mean" in statistical_summary
assert "std" in statistical_summary
assert "min" in statistical_summary
assert "max" in statistical_summary
@pytest.mark.asyncio
async def test_portfolio_optimization(self, risk_engine, sample_portfolio_data, sample_market_data):
"""ํฌํธํด๋ฆฌ์ค ์ต์ ํ ํ
์คํธ"""
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
await risk_engine.calibrate_models(sample_market_data)
# ํฌํธํด๋ฆฌ์ค ์ต์ ํ
optimization_results = await risk_engine.optimize_portfolio(
sample_portfolio_data,
objective="minimize_risk",
constraints={"max_position_size": 0.3}
)
# ๊ฒฐ๊ณผ ๊ฒ์ฆ
assert "optimized_weights" in optimization_results
assert "risk_reduction" in optimization_results
assert "expected_return" in optimization_results
assert "optimization_method" in optimization_results
# ๊ฐ์ค์น ํฉ์ด 1์ธ์ง ํ์ธ
optimized_weights = optimization_results["optimized_weights"]
total_weight = sum(optimized_weights.values())
assert abs(total_weight - 1.0) < 0.01 # 1์ ๊ฐ๊น์
# ์ ์ฝ์กฐ๊ฑด ํ์ธ
for weight in optimized_weights.values():
assert weight <= 0.3 # ์ต๋ ํฌ์ง์
ํฌ๊ธฐ ์ ์ฝ
@pytest.mark.asyncio
async def test_risk_attribution(self, risk_engine, sample_portfolio_data, sample_market_data):
"""์ํ ๊ธฐ์ฌ๋ ๋ถ์ ํ
์คํธ"""
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
await risk_engine.calibrate_models(sample_market_data)
# ์ํ ๊ธฐ์ฌ๋ ๋ถ์
attribution_results = await risk_engine.analyze_risk_attribution(
sample_portfolio_data,
sample_market_data
)
# ๊ฒฐ๊ณผ ๊ฒ์ฆ
assert "position_contributions" in attribution_results
assert "sector_contributions" in attribution_results
assert "factor_contributions" in attribution_results
assert "diversification_ratio" in attribution_results
# ํฌ์ง์
๋ณ ๊ธฐ์ฌ๋ ํ์ธ
position_contributions = attribution_results["position_contributions"]
for position in sample_portfolio_data["positions"]:
symbol = position["symbol"]
assert symbol in position_contributions
assert "risk_contribution" in position_contributions[symbol]
assert "marginal_risk" in position_contributions[symbol]
@pytest.mark.asyncio
async def test_liquidity_risk_assessment(self, risk_engine, sample_portfolio_data, sample_market_data):
"""์ ๋์ฑ ์ํ ํ๊ฐ ํ
์คํธ"""
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
await risk_engine.calibrate_models(sample_market_data)
# ์ ๋์ฑ ์ํ ํ๊ฐ
liquidity_results = await risk_engine.assess_liquidity_risk(
sample_portfolio_data,
sample_market_data
)
# ๊ฒฐ๊ณผ ๊ฒ์ฆ
assert "liquidity_score" in liquidity_results
assert "time_to_liquidate" in liquidity_results
assert "liquidity_cost" in liquidity_results
assert "position_liquidity" in liquidity_results
# ์ ๋์ฑ ์ ์๋ 0-1 ๋ฒ์
liquidity_score = liquidity_results["liquidity_score"]
assert 0 <= liquidity_score <= 1
# ์ฒญ์ฐ ์๊ฐ์ ์์
time_to_liquidate = liquidity_results["time_to_liquidate"]
assert time_to_liquidate > 0
@pytest.mark.asyncio
async def test_concentration_risk_analysis(self, risk_engine, sample_portfolio_data):
"""์ง์ค๋ ์ํ ๋ถ์ ํ
์คํธ"""
# ์ง์ค๋ ์ํ ๋ถ์
concentration_results = await risk_engine.analyze_concentration_risk(sample_portfolio_data)
# ๊ฒฐ๊ณผ ๊ฒ์ฆ
assert "herfindahl_index" in concentration_results
assert "max_position_weight" in concentration_results
assert "sector_concentration" in concentration_results
assert "concentration_score" in concentration_results
assert "diversification_recommendations" in concentration_results
# ํํ๋ฌ ์ง์๋ 0-1 ๋ฒ์
herfindahl_index = concentration_results["herfindahl_index"]
assert 0 < herfindahl_index <= 1
# ์ต๋ ํฌ์ง์
๋น์ค ํ์ธ
max_position_weight = concentration_results["max_position_weight"]
actual_max_weight = max(pos["weight"] for pos in sample_portfolio_data["positions"])
assert abs(max_position_weight - actual_max_weight) < 0.01
@pytest.mark.asyncio
async def test_risk_reporting(self, risk_engine, sample_portfolio_data, sample_market_data):
"""์ํ ๋ณด๊ณ ์ ์์ฑ ํ
์คํธ"""
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
await risk_engine.calibrate_models(sample_market_data)
# ์ข
ํฉ ์ํ ๋ณด๊ณ ์ ์์ฑ
risk_report = await risk_engine.generate_risk_report(
sample_portfolio_data,
sample_market_data,
report_date=datetime.now()
)
# ๋ณด๊ณ ์ ๊ตฌ์กฐ ํ์ธ
assert "executive_summary" in risk_report
assert "var_analysis" in risk_report
assert "stress_test_results" in risk_report
assert "risk_attribution" in risk_report
assert "recommendations" in risk_report
assert "report_metadata" in risk_report
# ์์ ์์ฝ ํ์ธ
executive_summary = risk_report["executive_summary"]
assert "overall_risk_level" in executive_summary
assert "key_risk_factors" in executive_summary
assert "immediate_actions" in executive_summary
@pytest.mark.asyncio
async def test_real_time_risk_monitoring(self, risk_engine, sample_portfolio_data, sample_market_data):
"""์ค์๊ฐ ์ํ ๋ชจ๋ํฐ๋ง ํ
์คํธ"""
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
await risk_engine.calibrate_models(sample_market_data)
# ์ค์๊ฐ ์์ฅ ๋ฐ์ดํฐ ์๋ฎฌ๋ ์ด์
real_time_data = {
"005930": {"price": 77000, "volume": 1500000, "timestamp": datetime.now().isoformat()},
"000660": {"price": 82000, "volume": 1200000, "timestamp": datetime.now().isoformat()}
}
# ์ค์๊ฐ ์ํ ๋ชจ๋ํฐ๋ง
monitoring_results = await risk_engine.monitor_real_time_risk(
sample_portfolio_data,
real_time_data
)
# ๊ฒฐ๊ณผ ๊ฒ์ฆ
assert "current_var" in monitoring_results
assert "risk_alerts" in monitoring_results
assert "position_changes" in monitoring_results
assert "monitoring_timestamp" in monitoring_results
# ์ํ ์๋ฆผ ๊ตฌ์กฐ ํ์ธ
risk_alerts = monitoring_results["risk_alerts"]
for alert in risk_alerts:
assert "alert_type" in alert
assert "severity" in alert
assert "message" in alert
@pytest.mark.asyncio
async def test_backtesting_validation(self, risk_engine, sample_portfolio_data, sample_market_data):
"""๋ฐฑํ
์คํ
๊ฒ์ฆ ํ
์คํธ"""
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
await risk_engine.calibrate_models(sample_market_data)
# ๋ฐฑํ
์คํ
์คํ
backtest_results = await risk_engine.run_backtest_validation(
sample_portfolio_data,
sample_market_data,
start_date="2023-01-01",
end_date="2023-12-31"
)
# ๊ฒฐ๊ณผ ๊ฒ์ฆ
assert "var_accuracy" in backtest_results
assert "kupiec_test" in backtest_results
assert "christoffersen_test" in backtest_results
assert "model_performance" in backtest_results
# VaR ์ ํ๋ ํ์ธ
var_accuracy = backtest_results["var_accuracy"]
assert "hit_rate" in var_accuracy
assert "expected_exceptions" in var_accuracy
assert "actual_exceptions" in var_accuracy
@pytest.mark.asyncio
async def test_error_handling(self, risk_engine):
"""์ค๋ฅ ์ฒ๋ฆฌ ํ
์คํธ"""
# ์บ๋ฆฌ๋ธ๋ ์ด์
๋์ง ์์ ๋ชจ๋ธ๋ก ๊ณ์ฐ ์๋
with pytest.raises(ModelNotTrainedError):
await risk_engine.calculate_var({}, confidence_level=0.95)
# ๋ถ์ถฉ๋ถํ ๋ฐ์ดํฐ๋ก ์บ๋ฆฌ๋ธ๋ ์ด์
์๋
insufficient_data = {"symbol": [100, 101, 102]} # ๋๋ฌด ์ ์ ๋ฐ์ดํฐ
with pytest.raises(InsufficientDataError):
await risk_engine.calibrate_models(insufficient_data)
# ์๋ชป๋ ์ ๋ขฐ์์ค
await risk_engine.calibrate_models({"symbol": list(range(300))})
with pytest.raises(ValueError):
await risk_engine.calculate_var({}, confidence_level=1.5) # > 1
@pytest.mark.asyncio
async def test_performance_benchmarking(self, risk_engine, sample_portfolio_data, sample_market_data):
"""์ฑ๋ฅ ๋ฒค์น๋งํน ํ
์คํธ"""
# ๋ชจ๋ธ ์บ๋ฆฌ๋ธ๋ ์ด์
await risk_engine.calibrate_models(sample_market_data)
# ๋๋ ๊ณ์ฐ ์ฑ๋ฅ ์ธก์
start_time = time.time()
# ์ฌ๋ฌ ๊ณ์ฐ์ ๋ณ๋ ฌ๋ก ์คํ
tasks = [
risk_engine.calculate_var(sample_portfolio_data, 0.95, 1),
risk_engine.calculate_cvar(sample_portfolio_data, 0.95, 1),
risk_engine.analyze_volatility(sample_portfolio_data, sample_market_data)
]
results = await asyncio.gather(*tasks)
end_time = time.time()
# ์ฑ๋ฅ ํ์ธ
processing_time = end_time - start_time
assert processing_time < 5.0 # 5์ด ์ด๋ด
assert len(results) == 3
# ๊ฐ ๊ฒฐ๊ณผ๊ฐ ์ ํจํ์ง ํ์ธ
for result in results:
assert result is not None
assert isinstance(result, dict)
def test_risk_metrics_calculation(self, risk_engine):
"""์ํ ๋ฉํธ๋ฆญ ๊ณ์ฐ ํ
์คํธ"""
# ์ํ ์์ต๋ฅ ๋ฐ์ดํฐ
returns = [0.01, -0.02, 0.015, -0.008, 0.003, -0.012, 0.007, -0.005]
# ํ์คํธ์ฐจ ๊ณ์ฐ
volatility = risk_engine._calculate_volatility(returns)
assert volatility > 0
# VaR ๊ณ์ฐ (ํ์คํ ๋ฆฌ์ปฌ ๋ฐฉ๋ฒ)
var_95 = risk_engine._calculate_historical_var(returns, 0.95)
var_99 = risk_engine._calculate_historical_var(returns, 0.99)
# 99% VaR์ด 95% VaR๋ณด๋ค ํฌ๊ฑฐ๋ ๊ฐ์์ผ ํจ (์ ๋๊ฐ)
assert abs(var_99) >= abs(var_95)
# ์คํ ๋น์จ ๊ณ์ฐ
sharpe_ratio = risk_engine._calculate_sharpe_ratio(returns, risk_free_rate=0.02)
assert isinstance(sharpe_ratio, (int, float))
def test_correlation_analysis(self, risk_engine):
"""์๊ด๊ด๊ณ ๋ถ์ ํ
์คํธ"""
# ์ํ ์์ต๋ฅ ๋ฐ์ดํฐ
returns_a = [0.01, -0.02, 0.015, -0.008, 0.003]
returns_b = [0.005, -0.015, 0.012, -0.006, 0.002]
# ์๊ด๊ณ์ ๊ณ์ฐ
correlation = risk_engine._calculate_correlation(returns_a, returns_b)
# ์๊ด๊ณ์๋ -1๊ณผ 1 ์ฌ์ด
assert -1 <= correlation <= 1
# ๊ณต๋ถ์ฐ ๊ณ์ฐ
covariance = risk_engine._calculate_covariance(returns_a, returns_b)
assert isinstance(covariance, (int, float))