Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
251aa900dd |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -204,8 +204,11 @@ def market_regime_dc(df: pd.DataFrame, threshold: float = 0.01) -> pd.DataFrame:
|
||||
dc_up, dc_down = calculate_dc(dfc, threshold=threshold)
|
||||
t_down, t_up = calculate_trend(dc_up, dc_down, dfc)
|
||||
dfc["market_regime"] = np.nan
|
||||
dfc.loc[t_up, "market_regime"] = 1
|
||||
dfc.loc[t_down, "market_regime"] = 0
|
||||
# Map index locations to actual index labels since calculate_dc returns integer indices
|
||||
t_up_labels = dfc.index[t_up] if len(t_up) > 0 else []
|
||||
t_down_labels = dfc.index[t_down] if len(t_down) > 0 else []
|
||||
dfc.loc[t_up_labels, "market_regime"] = 1
|
||||
dfc.loc[t_down_labels, "market_regime"] = 0
|
||||
dfc["market_regime"] = dfc["market_regime"].ffill().bfill()
|
||||
return dfc
|
||||
|
||||
@@ -266,7 +269,7 @@ def displacement_detection(
|
||||
dfc["displacement"] = 0
|
||||
mask = dfc["candle_range"] > strenght * dfc["STD"]
|
||||
dfc.loc[mask, "displacement"] = 1
|
||||
dfc["red_displacement"] = (dfc["displacement"] & dfc["displacement"].shift(1).fillna(0)).astype(int)
|
||||
dfc["red_displacement"] = (dfc["displacement"].astype(int) & dfc["displacement"].shift(1).fillna(0).astype(int)).astype(int)
|
||||
return dfc
|
||||
|
||||
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
import sys
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
# Mock MetaTrader5 before importing data_loader
|
||||
mt5_mock = MagicMock()
|
||||
sys.modules['MetaTrader5'] = mt5_mock
|
||||
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from data.data_loader import get_data_mt5
|
||||
|
||||
def test_get_data_mt5_live_trading():
|
||||
"""Test get_data_mt5 when start_pos is None (live trading)."""
|
||||
# Arrange
|
||||
symbol = "BTCUSD"
|
||||
n_bars = 100
|
||||
timeframe = mt5_mock.TIMEFRAME_H1
|
||||
|
||||
# Mock return value of copy_rates_from_pos
|
||||
mock_rates = [
|
||||
{"time": 1600000000, "open": 1.0, "high": 2.0, "low": 0.5, "close": 1.5},
|
||||
{"time": 1600003600, "open": 1.5, "high": 2.5, "low": 1.0, "close": 2.0},
|
||||
]
|
||||
mt5_mock.copy_rates_from_pos.return_value = mock_rates
|
||||
|
||||
# Act
|
||||
df = get_data_mt5(symbol, n_bars, timeframe)
|
||||
|
||||
# Assert
|
||||
mt5_mock.copy_rates_from_pos.assert_called_once_with(symbol, timeframe, 0, n_bars)
|
||||
assert isinstance(df, pd.DataFrame)
|
||||
assert df.index.name == 'time'
|
||||
assert len(df) == 2
|
||||
assert "open" in df.columns
|
||||
assert df.index[0] == pd.to_datetime(1600000000, unit='s')
|
||||
|
||||
def test_get_data_mt5_backtesting():
|
||||
"""Test get_data_mt5 when start_pos is provided (backtesting)."""
|
||||
# Arrange
|
||||
mt5_mock.copy_rates_from_pos.reset_mock()
|
||||
symbol = "EURUSD"
|
||||
n_bars = 50
|
||||
timeframe = mt5_mock.TIMEFRAME_M15
|
||||
start_pos = 10
|
||||
|
||||
mock_rates = [
|
||||
{"time": 1600000000, "open": 1.1, "high": 1.2, "low": 1.0, "close": 1.15},
|
||||
]
|
||||
mt5_mock.copy_rates_from_pos.return_value = mock_rates
|
||||
|
||||
# Act
|
||||
df = get_data_mt5(symbol, n_bars, timeframe, start_pos=start_pos)
|
||||
|
||||
# Assert
|
||||
mt5_mock.copy_rates_from_pos.assert_called_once_with(symbol, timeframe, start_pos, n_bars)
|
||||
assert isinstance(df, pd.DataFrame)
|
||||
assert df.index.name == 'time'
|
||||
assert len(df) == 1
|
||||
|
||||
def test_get_data_mt5_no_data():
|
||||
"""Test get_data_mt5 when copy_rates_from_pos returns None."""
|
||||
# Arrange
|
||||
mt5_mock.copy_rates_from_pos.reset_mock()
|
||||
symbol = "INVALID"
|
||||
n_bars = 10
|
||||
timeframe = mt5_mock.TIMEFRAME_H1
|
||||
|
||||
mt5_mock.copy_rates_from_pos.return_value = None
|
||||
|
||||
# Act & Assert
|
||||
with pytest.raises(ValueError, match=f"Could not retrieve data for {symbol}"):
|
||||
get_data_mt5(symbol, n_bars, timeframe)
|
||||
|
||||
mt5_mock.copy_rates_from_pos.assert_called_once_with(symbol, timeframe, 0, n_bars)
|
||||
@@ -0,0 +1,171 @@
|
||||
import pytest
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from features.feature_engineering import (
|
||||
spread,
|
||||
candle_information,
|
||||
log_transform,
|
||||
mathematical_derivatives,
|
||||
parkinson_estimator,
|
||||
yang_zhang_estimator,
|
||||
market_regime_dc,
|
||||
gap_detection,
|
||||
displacement_detection,
|
||||
set_double_barrier_label
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def sample_df():
|
||||
"""Create a sample dataframe with OHLCV data for testing."""
|
||||
dates = pd.date_range("2023-01-01", periods=10)
|
||||
data = {
|
||||
"open": [100.0, 102.0, 101.0, 105.0, 104.0, 106.0, 108.0, 107.0, 109.0, 110.0],
|
||||
"high": [105.0, 106.0, 104.0, 108.0, 107.0, 109.0, 110.0, 111.0, 112.0, 115.0],
|
||||
"low": [95.0, 100.0, 99.0, 102.0, 101.0, 104.0, 105.0, 106.0, 107.0, 108.0],
|
||||
"close": [102.0, 101.0, 105.0, 104.0, 106.0, 108.0, 107.0, 109.0, 110.0, 112.0],
|
||||
"tick_volume": [1000, 1200, 1100, 1500, 1300, 1400, 1600, 1700, 1800, 2000]
|
||||
}
|
||||
return pd.DataFrame(data, index=dates)
|
||||
|
||||
def test_spread(sample_df):
|
||||
"""Test spread calculation."""
|
||||
df_result = spread(sample_df)
|
||||
|
||||
# Check if 'spread' column exists
|
||||
assert "spread" in df_result.columns
|
||||
|
||||
# Check calculation
|
||||
expected_spread = sample_df["high"] - sample_df["low"]
|
||||
pd.testing.assert_series_equal(df_result["spread"], expected_spread, check_names=False)
|
||||
|
||||
# Check that original df is not modified
|
||||
assert "spread" not in sample_df.columns
|
||||
|
||||
def test_candle_information(sample_df):
|
||||
"""Test candle_information features."""
|
||||
df_result = candle_information(sample_df)
|
||||
|
||||
# Check if columns are added
|
||||
expected_cols = ["candle_way", "fill", "amplitude"]
|
||||
for col in expected_cols:
|
||||
assert col in df_result.columns
|
||||
|
||||
# Check logic of candle_way
|
||||
expected_candle_way = (sample_df["close"] > sample_df["open"]).astype(int)
|
||||
pd.testing.assert_series_equal(df_result["candle_way"], expected_candle_way, check_names=False)
|
||||
|
||||
# Check fill calculation
|
||||
rng = (sample_df["high"] - sample_df["low"]).replace(0, np.nan)
|
||||
expected_fill = (sample_df["close"] - sample_df["open"]).abs() / (rng + 1e-5)
|
||||
pd.testing.assert_series_equal(df_result["fill"], expected_fill, check_names=False)
|
||||
|
||||
def test_log_transform(sample_df):
|
||||
"""Test log_transform logic."""
|
||||
col_to_transform = "close"
|
||||
n_period = 2
|
||||
df_result = log_transform(sample_df, col_to_transform, n_period)
|
||||
|
||||
# Check columns
|
||||
assert f"log_{col_to_transform}" in df_result.columns
|
||||
assert f"log_ret_{n_period}" in df_result.columns
|
||||
|
||||
# Check log calculation
|
||||
expected_log = np.log(sample_df[col_to_transform].clip(lower=1e-12))
|
||||
pd.testing.assert_series_equal(df_result[f"log_{col_to_transform}"], expected_log, check_names=False)
|
||||
|
||||
# Check diff calculation
|
||||
expected_diff = expected_log.diff(n_period)
|
||||
pd.testing.assert_series_equal(df_result[f"log_ret_{n_period}"], expected_diff, check_names=False)
|
||||
|
||||
def test_mathematical_derivatives(sample_df):
|
||||
"""Test velocity and acceleration logic."""
|
||||
col_to_derive = "close"
|
||||
df_result = mathematical_derivatives(sample_df, col_to_derive)
|
||||
|
||||
# Check columns
|
||||
assert "velocity" in df_result.columns
|
||||
assert "acceleration" in df_result.columns
|
||||
|
||||
# Check calculation
|
||||
expected_velocity = sample_df[col_to_derive].diff()
|
||||
expected_acceleration = expected_velocity.diff()
|
||||
|
||||
pd.testing.assert_series_equal(df_result["velocity"], expected_velocity, check_names=False)
|
||||
pd.testing.assert_series_equal(df_result["acceleration"], expected_acceleration, check_names=False)
|
||||
|
||||
def test_parkinson_estimator(sample_df):
|
||||
"""Test Parkinson estimator."""
|
||||
# Test with valid window
|
||||
vol = parkinson_estimator(sample_df)
|
||||
assert not np.isnan(vol)
|
||||
assert vol > 0
|
||||
|
||||
# Test with empty window
|
||||
empty_df = pd.DataFrame()
|
||||
empty_vol = parkinson_estimator(empty_df)
|
||||
assert np.isnan(empty_vol)
|
||||
|
||||
def test_yang_zhang_estimator(sample_df):
|
||||
"""Test Yang Zhang estimator."""
|
||||
# Test with valid window
|
||||
vol = yang_zhang_estimator(sample_df)
|
||||
assert not np.isnan(vol)
|
||||
assert vol > 0
|
||||
|
||||
# Test with empty window
|
||||
empty_df = pd.DataFrame()
|
||||
empty_vol = yang_zhang_estimator(empty_df)
|
||||
assert np.isnan(empty_vol)
|
||||
|
||||
def test_market_regime_dc(sample_df):
|
||||
"""Test market regime calculation."""
|
||||
df_result = market_regime_dc(sample_df, threshold=0.01)
|
||||
|
||||
# Check column
|
||||
assert "market_regime" in df_result.columns
|
||||
|
||||
# Check values
|
||||
assert df_result["market_regime"].isin([0, 1, np.nan]).all()
|
||||
|
||||
def test_gap_detection(sample_df):
|
||||
"""Test gap detection calculation."""
|
||||
df_result = gap_detection(sample_df)
|
||||
|
||||
# Check expected columns
|
||||
expected_cols = [
|
||||
"Bullish_gap_inf",
|
||||
"Bullish_gap_sup",
|
||||
"Bullish_gap_size",
|
||||
"Bearish_gap_inf",
|
||||
"Bearish_gap_sup",
|
||||
"Bearish_gap_size",
|
||||
]
|
||||
for col in expected_cols:
|
||||
assert col in df_result.columns
|
||||
|
||||
def test_displacement_detection(sample_df):
|
||||
"""Test displacement detection."""
|
||||
df_result = displacement_detection(sample_df, period=2)
|
||||
|
||||
# Check added columns
|
||||
assert "candle_range" in df_result.columns
|
||||
assert "Variation" in df_result.columns
|
||||
assert "STD" in df_result.columns
|
||||
assert "displacement" in df_result.columns
|
||||
assert "red_displacement" in df_result.columns
|
||||
|
||||
# Check values of displacement
|
||||
assert df_result["displacement"].isin([0, 1]).all()
|
||||
assert df_result["red_displacement"].isin([0, 1]).all()
|
||||
|
||||
def test_set_double_barrier_label(sample_df):
|
||||
"""Test set double barrier label."""
|
||||
# Since our sample_df only has 10 rows and we drop NAs,
|
||||
# we need a small horizon to not drop all rows.
|
||||
df_result = set_double_barrier_label(sample_df, up=0.01, down=0.01, horizon=2)
|
||||
|
||||
# Check columns
|
||||
assert "barrier_label" in df_result.columns
|
||||
|
||||
# Check values
|
||||
assert df_result["barrier_label"].isin([0, 1]).all()
|
||||
Reference in New Issue
Block a user