Strengthen Python timeout recovery and candidate freshness
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
"""OHLC CSV reader tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from ea_py.io.ohlc_csv import read_ohlc_csv
|
||||
|
||||
|
||||
def write_csv(path: Path, body: str) -> None:
|
||||
"""Write a small MT5-style OHLC CSV for tests."""
|
||||
path.write_text("Time,Open,High,Low,Close\n" + body, encoding="utf-8")
|
||||
|
||||
|
||||
def test_read_ohlc_csv_rejects_non_finite_prices(tmp_path: Path) -> None:
|
||||
"""NaN/inf values are rejected before market calculations."""
|
||||
csv_path = tmp_path / "ohlc.csv"
|
||||
write_csv(csv_path, "2026.05.24 13:00,1900.0,nan,1890.0,1895.0\n")
|
||||
|
||||
with pytest.raises(ValueError, match="non-finite"):
|
||||
read_ohlc_csv(csv_path)
|
||||
|
||||
|
||||
def test_read_ohlc_csv_rejects_inconsistent_high_low(tmp_path: Path) -> None:
|
||||
"""High/Low must contain Open and Close."""
|
||||
csv_path = tmp_path / "ohlc.csv"
|
||||
write_csv(csv_path, "2026.05.24 13:00,1900.0,1899.0,1890.0,1895.0\n")
|
||||
|
||||
with pytest.raises(ValueError, match="High below Open/Close"):
|
||||
read_ohlc_csv(csv_path)
|
||||
|
||||
|
||||
def test_read_ohlc_csv_accepts_valid_mt5_rows(tmp_path: Path) -> None:
|
||||
"""Valid MT5 rows are converted to the internal DateTime schema."""
|
||||
csv_path = tmp_path / "ohlc.csv"
|
||||
write_csv(csv_path, "2026.05.24 13:00,1900.0,1902.0,1890.0,1895.0\n")
|
||||
|
||||
actual = read_ohlc_csv(csv_path)
|
||||
|
||||
assert actual == [
|
||||
{
|
||||
"DateTime": "2026.05.24 13:00",
|
||||
"Open": 1900.0,
|
||||
"High": 1902.0,
|
||||
"Low": 1890.0,
|
||||
"Close": 1895.0,
|
||||
}
|
||||
]
|
||||
@@ -10,13 +10,16 @@ from ea_py.paths import (
|
||||
ENV_MT5_EA_FILE_PREFIX,
|
||||
ENV_MT5_DATA_PATH,
|
||||
ENV_MT5_FILES_DIR,
|
||||
ENV_MT5_PRICE_DIGITS,
|
||||
Mt5PathSettings,
|
||||
build_entry_paths,
|
||||
build_trend_paths,
|
||||
discover_mql5_dir,
|
||||
files_dir_from_data_path,
|
||||
mt5_price_digits,
|
||||
prefixed_file_name,
|
||||
sanitize_file_prefix,
|
||||
sanitize_price_digits,
|
||||
terminal_files_dir,
|
||||
)
|
||||
|
||||
@@ -135,3 +138,25 @@ def test_build_paths_apply_file_prefix_from_environment(
|
||||
actual = build_entry_paths()
|
||||
|
||||
assert actual.done_entry == tmp_path / "HIT_GOLD_10001_process_done_entry.txt"
|
||||
|
||||
|
||||
def test_sanitize_price_digits_accepts_mt5_symbol_digits() -> None:
|
||||
"""MT5のSYMBOL_DIGITSは安全な範囲だけ受け入れる。"""
|
||||
assert sanitize_price_digits("3") == 3
|
||||
assert sanitize_price_digits(10) == 10
|
||||
assert sanitize_price_digits("-1") is None
|
||||
assert sanitize_price_digits("abc") is None
|
||||
|
||||
|
||||
def test_mt5_price_digits_reads_environment(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""EA/batから渡された価格桁数をPython側で参照できる。"""
|
||||
monkeypatch.setenv(ENV_MT5_PRICE_DIGITS, "3")
|
||||
|
||||
assert mt5_price_digits() == 3
|
||||
|
||||
|
||||
def test_mt5_price_digits_accepts_settings() -> None:
|
||||
"""テストや手動実行では設定オブジェクトから価格桁数を渡せる。"""
|
||||
settings = Mt5PathSettings(price_digits=2)
|
||||
|
||||
assert mt5_price_digits(settings) == 2
|
||||
|
||||
@@ -111,7 +111,7 @@ def test_format_target_zones_includes_schema_candidate_and_all_strategies() -> N
|
||||
parsed = parse_lines_to_entry_zones_allow_subset("2,1895.00,1910.00,1885.00,1892.00,1898.00")
|
||||
sanitized = sanitize_entry_zones(parsed, selected_strategies=[2], current_price=1900.0)
|
||||
|
||||
actual = format_target_zones(sanitized, "202605241300")
|
||||
actual = format_target_zones(sanitized, "202605241300", price_digits=2)
|
||||
|
||||
assert actual.splitlines() == [
|
||||
"2",
|
||||
@@ -124,6 +124,16 @@ def test_format_target_zones_includes_schema_candidate_and_all_strategies() -> N
|
||||
]
|
||||
|
||||
|
||||
def test_format_target_zones_uses_mt5_price_digits() -> None:
|
||||
"""MT5から渡された価格桁数に合わせてゾーン価格を出力する。"""
|
||||
parsed = parse_lines_to_entry_zones_allow_subset("2,1895.1234,1910.5678,1885.2345,1892.1234,1898.5678")
|
||||
sanitized = sanitize_entry_zones(parsed, selected_strategies=[2], current_price=1900.0)
|
||||
|
||||
actual = format_target_zones(sanitized, "202605241300", price_digits=3)
|
||||
|
||||
assert actual.splitlines()[4] == "2,1892.123,1898.568,1910.568,1885.235"
|
||||
|
||||
|
||||
def test_build_candidate_id_datetime_text_returns_compact_digits() -> None:
|
||||
"""H1確定足時刻からスペースなしの候補IDを作る。"""
|
||||
actual = build_candidate_id("2026.05.24 13:00")
|
||||
|
||||
Reference in New Issue
Block a user