Compare commits

..
Author SHA1 Message Date
google-labs-jules[bot]andmaghdam 5c24d2d72d test: add error path test for live trading get_data
- Add check for `mt5.copy_rates_from_pos` returning `None` in `live_trading/multi_bar.py:82`
- Add test in `tests/test_multi_bar.py` verifying that when `mt5.copy_rates_from_pos` returns `None`, the function returns `None` and an error is logged.

Co-authored-by: maghdam <63883156+maghdam@users.noreply.github.com>
2026-03-11 18:33:09 +00:00
11 changed files with 27 additions and 64 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3
View File
@@ -82,6 +82,9 @@ class TradingApp:
Fetch 'n' bars of historical data for the given symbol and timeframe.
"""
rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, n)
if rates is None:
log_and_print(f"Could not retrieve data for {symbol}", is_error=True)
return None
rates_frame = pd.DataFrame(rates)
rates_frame['time'] = pd.to_datetime(rates_frame['time'], unit='s')
rates_frame.set_index('time', inplace=True)
View File
-64
View File
@@ -1,64 +0,0 @@
import pandas as pd
import numpy as np
from features.labeling_schemes import create_labels_multi_bar
def test_create_labels_multi_bar():
# Toy dataframe with close prices
df = pd.DataFrame({
"close": [100.0, 102.0, 99.0, 99.0, 105.0]
})
# horizon = 1, threshold = 0.01
# row 0: close = 100, future = 102, return = 0.02 >= 0.01 -> label = 1
# row 1: close = 102, future = 99, return = -3/102 = -0.0294 <= -0.01 -> label = -1
# row 2: close = 99, future = 99, return = 0.00 -> label = 0
# row 3: close = 99, future = 105, return = 6/99 = 0.0606 >= 0.01 -> label = 1
# row 4: close = 105, future = NaN
res = create_labels_multi_bar(df, horizon=1, threshold=0.01)
# Should have 4 rows because the last row is dropped due to NaN future return
assert len(res) == 4
expected_labels = [1, -1, 0, 1]
np.testing.assert_array_equal(res["multi_bar_label"].values, expected_labels)
# Check returns
expected_returns = [0.02, -3/102, 0.0, 6/99]
np.testing.assert_array_almost_equal(res["future_return_h"].values, expected_returns)
def test_create_labels_multi_bar_custom_horizon():
# Test with horizon=2, threshold=0.05
df = pd.DataFrame({
"close": [100.0, 101.0, 105.0, 90.0, 95.0, 100.0]
})
# horizon = 2
# row 0: close 100, future 105 (idx 2), return 0.05 >= 0.05 -> 1
# row 1: close 101, future 90 (idx 3), return -11/101 = -0.1089 <= -0.05 -> -1
# row 2: close 105, future 95 (idx 4), return -10/105 = -0.0952 <= -0.05 -> -1
# row 3: close 90, future 100 (idx 5), return 10/90 = 0.1111 >= 0.05 -> 1
# row 4: NaN
# row 5: NaN
res = create_labels_multi_bar(df, horizon=2, threshold=0.05)
assert len(res) == 4
expected_labels = [1, -1, -1, 1]
np.testing.assert_array_equal(res["multi_bar_label"].values, expected_labels)
def test_create_labels_multi_bar_exact_threshold():
# Check boundary condition where return is exactly the threshold
df = pd.DataFrame({
"close": [100.0, 105.0, 95.0]
})
# threshold = 0.05, horizon = 1
# row 0: return 0.05 -> 1
# row 1: return -10/105 = -0.0952 -> -1
res = create_labels_multi_bar(df, horizon=1, threshold=0.05)
assert res.iloc[0]["multi_bar_label"] == 1
res = create_labels_multi_bar(df, horizon=1, threshold=0.1)
# return is 0.05, which is < 0.1 and > -0.1
assert res.iloc[0]["multi_bar_label"] == 0
+24
View File
@@ -0,0 +1,24 @@
import sys
from unittest.mock import MagicMock
# Mock out MetaTrader5 before importing our module
sys.modules['MetaTrader5'] = MagicMock()
import unittest
from unittest.mock import patch
import pandas as pd
from live_trading.multi_bar import TradingApp, log_and_print
class TestTradingApp(unittest.TestCase):
def setUp(self):
self.app = TradingApp(symbol="EURUSD", lot_size=0.01, magic_number=123456)
@patch("live_trading.multi_bar.mt5.copy_rates_from_pos")
@patch("live_trading.multi_bar.log_and_print")
def test_get_data_returns_none(self, mock_log, mock_copy_rates):
mock_copy_rates.return_value = None
# Test what happens when mt5.copy_rates_from_pos returns None
result = self.app.get_data("EURUSD", 100, 16408)
self.assertIsNone(result)
mock_log.assert_called_once_with("Could not retrieve data for EURUSD", is_error=True)