Relocate Python EA helpers under MQL5
This commit is contained in:
@@ -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,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
|
||||
|
||||
@@ -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%"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user