Relocate Python EA helpers under MQL5

This commit is contained in:
Hiroaki86
2026-05-28 03:11:33 +09:00
parent fa3394415d
commit 3d853fa67a
13 changed files with 1303 additions and 31 deletions
+2 -2
View File
@@ -2,7 +2,7 @@
## 位置づけ
- このファイルは、`C:\ea_py` プロジェクトで作業するAIエージェント共通の正本とする。
- このファイルは、MT5データフォルダ配下の `MQL5\python_for_ea` プロジェクトで作業するAIエージェント共通の正本とする。
- GitHub Copilot 固有の動作指示は、必要になった時点で `.github/copilot-instructions.md` に分離する。
- ファイル種別・技術領域ごとの詳細指示は、必要に応じて `.github/instructions/``.cursor/rules/``.agents/skills/` に分離する。
- 長文ルールを複数ファイルへ重複記載しない。共通ルールはこのファイルを優先する。
@@ -101,7 +101,7 @@
- 変更後は、影響範囲に応じて以下を確認する。
```bash
uv run python -m py_compile C:/ea_py/get_trend_reply.py C:/ea_py/get_entry_reply.py
uv run python -m py_compile get_trend_reply.py get_entry_reply.py
uv run ruff check .
uv run ty check
uv run pytest
+9 -9
View File
@@ -9,7 +9,7 @@ Python 側は注文送信を行いません。発注、注文管理、M15 確定
MT5 EA からは `bat/` 経由でルート直下の互換スクリプトを起動します。
```text
C:/ea_py/
<TerminalDataPath>/MQL5/python_for_ea/
├─ get_trend_reply.py
├─ get_entry_reply.py
├─ bat/
@@ -21,20 +21,20 @@ C:/ea_py/
- `get_trend_reply.py`: `src/ea_py/pipelines/trend_pipeline.py` を起動する薄い入口。
- `get_entry_reply.py`: `src/ea_py/pipelines/entry_pipeline.py` を起動する薄い入口。
- `bat/get_trend_reply.bat`: `uv run python C:\ea_py\get_trend_reply.py` を実行。
- `bat/get_entry_reply.bat`: `uv run python C:\ea_py\get_entry_reply.py` を実行。
- `bat/get_trend_reply.bat`: bat自身の親フォルダをプロジェクトルートとして `uv run python get_trend_reply.py` を実行。
- `bat/get_entry_reply.bat`: bat自身の親フォルダをプロジェクトルートとして `uv run python get_entry_reply.py` を実行。
入口ファイル名や配置を変える場合は、`bat/` と MQL5 EA 側の呼び出し設定も合わせて更新してください。
## MT5 連携ファイル
既定の連携先は次の MT5 `MQL5/Files` ディレクトリです。
既定の連携先は、このプロジェクトの親にある MT5 `MQL5/Files` ディレクトリです。
```text
C:/Users/new/AppData/Roaming/MetaQuotes/Terminal/5BDB0B60344C088C2FA5CA35699BAAFD/MQL5/Files/
<TerminalDataPath>/MQL5/Files/
```
パスの組み立ては `src/ea_py/paths.py` で定義しています。
パスの組み立ては `src/ea_py/paths.py` で定義しています。特殊な配置では `MT5_FILES_DIR` または `MT5_DATA_PATH` 環境変数で上書きできます。
### 入力
@@ -174,14 +174,14 @@ uv sync
個別実行:
```powershell
uv run python C:/ea_py/get_trend_reply.py
uv run python C:/ea_py/get_entry_reply.py
uv run python get_trend_reply.py
uv run python get_entry_reply.py
```
品質チェック:
```powershell
uv run python -m py_compile C:/ea_py/get_trend_reply.py C:/ea_py/get_entry_reply.py
uv run python -m py_compile get_trend_reply.py get_entry_reply.py
uv run ruff check .
uv run ty check
uv run pytest
+4 -1
View File
@@ -1,8 +1,11 @@
@echo off
setlocal
set APP_DIR=C:\ea_py
chcp 65001 >nul
set PYTHONIOENCODING=utf-8
set "APP_DIR=%~dp0.."
set PY_FILE=get_entry_reply.py
for %%I in ("%APP_DIR%") do set "APP_DIR=%%~fI"
cd /d "%APP_DIR%"
+4 -1
View File
@@ -1,8 +1,11 @@
@echo off
setlocal
set APP_DIR=C:\ea_py
chcp 65001 >nul
set PYTHONIOENCODING=utf-8
set "APP_DIR=%~dp0.."
set PY_FILE=get_trend_reply.py
for %%I in ("%APP_DIR%") do set "APP_DIR=%%~fI"
cd /d "%APP_DIR%"
@@ -2,7 +2,7 @@
## 目的
このドキュメントは、`C:\ea_py` のフォルダ構成に関する正本である。
このドキュメントは、MT5データフォルダ配下の `MQL5\python_for_ea` のフォルダ構成に関する正本である。
現在のプロジェクトは、MT5 HIT-EAから出力されたOHLC CSVをPythonで解析し、OpenAI APIを使ってGOLD/XAUUSD向けの相場環境とエントリー候補価格を返す補助アプリケーションである。
@@ -11,7 +11,7 @@ MT5 EAから `bat/` 経由でルート直下のPythonスクリプトが呼ばれ
## 現在のフォルダ構成
```text
C:/ea_py/
<TerminalDataPath>/MQL5/python_for_ea/
├─ AGENTS.md
├─ README.md
├─ pyproject.toml
@@ -105,7 +105,7 @@ C:/Users/new/AppData/Roaming/MetaQuotes/Terminal/{terminal_ID}/MQL5/Files/
└─ tmp_chart_long.png
```
これらは実行時生成物であり、原則として `C:\ea_py` 配下へコピーして正本化しない。
これらは実行時生成物であり、原則として `MQL5\python_for_ea` 配下へコピーして正本化しない。
## MQL5 EAファイル
@@ -127,7 +127,7 @@ EAファイルはこのPythonプロジェクトの外部連携先として扱う
ルート直下の `get_trend_reply.py``get_entry_reply.py` は、MT5/bat互換のため薄いエントリーポイントとして残す。
```text
C:/ea_py/
<TerminalDataPath>/MQL5/python_for_ea/
├─ AGENTS.md
├─ README.md
├─ pyproject.toml
+69 -5
View File
@@ -3,19 +3,24 @@
from __future__ import annotations
from dataclasses import dataclass
import os
from pathlib import Path
DEFAULT_USER_NAME = "new"
DEFAULT_TERMINAL_ID = "5BDB0B60344C088C2FA5CA35699BAAFD"
ENV_MT5_FILES_DIR = "MT5_FILES_DIR"
ENV_MT5_DATA_PATH = "MT5_DATA_PATH"
@dataclass(frozen=True)
class Mt5PathSettings:
"""MT5データフォルダを特定するための設定。"""
user_name: str = DEFAULT_USER_NAME
terminal_id: str = DEFAULT_TERMINAL_ID
files_dir: Path | None = None
data_path: Path | None = None
user_name: str | None = None
terminal_id: str | None = None
@dataclass(frozen=True)
@@ -43,17 +48,76 @@ class EntryPaths:
debug_reason: Path
def discover_mql5_dir(start_path: Path | None = None) -> Path | None:
"""このPythonプロジェクトの配置からMQL5ルートを探す。"""
path = (start_path or Path(__file__)).resolve()
candidates = [path, *path.parents]
for candidate in candidates:
if candidate.name.lower() == "mql5":
return candidate
return None
def files_dir_from_data_path(data_path: Path) -> Path:
"""MT5データフォルダまたはMQL5フォルダからFilesパスを作る。"""
resolved = data_path.resolve()
if resolved.name.lower() == "mql5":
return resolved / "Files"
return resolved / "MQL5" / "Files"
def terminal_files_dir(settings: Mt5PathSettings | None = None) -> Path:
"""MT5のMQL5/Filesディレクトリを返す。"""
"""MT5のMQL5/Filesディレクトリを返す。
既定では `MQL5/python_for_ea` 配置から親の `MQL5/Files` を発見する。
テストや特殊運用では `MT5_FILES_DIR` または `MT5_DATA_PATH` で上書きできる。
"""
if settings and settings.files_dir is not None:
return settings.files_dir
env_files_dir = os.getenv(ENV_MT5_FILES_DIR)
if env_files_dir:
return Path(env_files_dir)
if settings and settings.data_path is not None:
return files_dir_from_data_path(settings.data_path)
env_data_path = os.getenv(ENV_MT5_DATA_PATH)
if env_data_path:
return files_dir_from_data_path(Path(env_data_path))
mt5_settings = settings or Mt5PathSettings()
if mt5_settings.user_name is not None or mt5_settings.terminal_id is not None:
user_name = mt5_settings.user_name or DEFAULT_USER_NAME
terminal_id = mt5_settings.terminal_id or DEFAULT_TERMINAL_ID
return (
Path("C:/Users")
/ user_name
/ "AppData"
/ "Roaming"
/ "MetaQuotes"
/ "Terminal"
/ terminal_id
/ "MQL5"
/ "Files"
)
discovered_mql5_dir = discover_mql5_dir()
if discovered_mql5_dir is not None:
return discovered_mql5_dir / "Files"
user_name = mt5_settings.user_name or DEFAULT_USER_NAME
terminal_id = mt5_settings.terminal_id or DEFAULT_TERMINAL_ID
return (
Path("C:/Users")
/ mt5_settings.user_name
/ user_name
/ "AppData"
/ "Roaming"
/ "MetaQuotes"
/ "Terminal"
/ mt5_settings.terminal_id
/ terminal_id
/ "MQL5"
/ "Files"
)
+91
View File
@@ -0,0 +1,91 @@
"""MT5 path discovery tests."""
from __future__ import annotations
from pathlib import Path
import pytest
from ea_py.paths import (
ENV_MT5_DATA_PATH,
ENV_MT5_FILES_DIR,
Mt5PathSettings,
discover_mql5_dir,
files_dir_from_data_path,
terminal_files_dir,
)
def test_discover_mql5_dir_from_nested_python_project(tmp_path: Path) -> None:
"""MQL5/python_for_ea 配置から親の MQL5 ディレクトリを見つける。"""
nested = tmp_path / "Terminal" / "ABC" / "MQL5" / "python_for_ea" / "src" / "ea_py" / "paths.py"
nested.parent.mkdir(parents=True)
nested.write_text("", encoding="utf-8")
actual = discover_mql5_dir(nested)
assert actual == tmp_path / "Terminal" / "ABC" / "MQL5"
def test_terminal_files_dir_accepts_explicit_files_dir(tmp_path: Path) -> None:
"""Tests and special deployments can pass the Files directory directly."""
expected = tmp_path / "Files"
actual = terminal_files_dir(Mt5PathSettings(files_dir=expected))
assert actual == expected
def test_terminal_files_dir_prefers_environment_files_dir(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""MT5_FILES_DIR overrides automatic discovery."""
expected = tmp_path / "CustomFiles"
monkeypatch.setenv(ENV_MT5_FILES_DIR, str(expected))
actual = terminal_files_dir()
assert actual == expected
def test_terminal_files_dir_accepts_legacy_user_and_terminal_settings() -> None:
"""Existing tests and tools can still pass user and terminal settings."""
settings = Mt5PathSettings(user_name="alice", terminal_id="TERMINAL123")
actual = terminal_files_dir(settings)
assert actual == Path(
"C:/Users/alice/AppData/Roaming/MetaQuotes/Terminal/TERMINAL123/MQL5/Files"
)
def test_files_dir_from_data_path_accepts_terminal_data_path(tmp_path: Path) -> None:
"""Terminal data paths are converted to MQL5/Files."""
data_path = tmp_path / "Terminal" / "ABC"
actual = files_dir_from_data_path(data_path)
assert actual == data_path / "MQL5" / "Files"
def test_files_dir_from_data_path_accepts_mql5_path(tmp_path: Path) -> None:
"""MQL5 paths are converted to their adjacent Files directory."""
mql5_path = tmp_path / "Terminal" / "ABC" / "MQL5"
actual = files_dir_from_data_path(mql5_path)
assert actual == mql5_path / "Files"
def test_terminal_files_dir_accepts_environment_data_path(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""MT5_DATA_PATH can point at the terminal data folder."""
data_path = tmp_path / "Terminal" / "ABC"
monkeypatch.setenv(ENV_MT5_DATA_PATH, str(data_path))
actual = terminal_files_dir()
assert actual == data_path / "MQL5" / "Files"