Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8da5ee9242 | |||
| 9dbb46fbb1 |
@@ -29,6 +29,12 @@ Built on top of [pdmt5](https://github.com/dceoy/pdmt5), a pandas-based data han
|
|||||||
pip install -U mt5cli MetaTrader5
|
pip install -U mt5cli MetaTrader5
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Parquet export is not included by default. To enable it, install the `parquet` extra:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -U "mt5cli[parquet]" MetaTrader5
|
||||||
|
```
|
||||||
|
|
||||||
## Python API (downstream packages)
|
## Python API (downstream packages)
|
||||||
|
|
||||||
Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives.
|
Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives.
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ mt5cli provides a stable `MT5Client` Python API, standardized dataset schemas, s
|
|||||||
pip install mt5cli
|
pip install mt5cli
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Parquet export is not included by default. To enable it, install the `parquet` extra:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install "mt5cli[parquet]"
|
||||||
|
```
|
||||||
|
|
||||||
## Python API for downstream packages
|
## Python API for downstream packages
|
||||||
|
|
||||||
Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives.
|
Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives.
|
||||||
|
|||||||
@@ -314,6 +314,7 @@ def export_dataframe(
|
|||||||
table_name: Table name for SQLite3 output.
|
table_name: Table name for SQLite3 output.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
|
ImportError: If the parquet format is requested but pyarrow is not installed.
|
||||||
ValueError: If the output format is not supported.
|
ValueError: If the output format is not supported.
|
||||||
"""
|
"""
|
||||||
if output_format == "csv":
|
if output_format == "csv":
|
||||||
@@ -326,6 +327,14 @@ def export_dataframe(
|
|||||||
indent=2,
|
indent=2,
|
||||||
)
|
)
|
||||||
elif output_format == "parquet":
|
elif output_format == "parquet":
|
||||||
|
try:
|
||||||
|
__import__("pyarrow")
|
||||||
|
except ImportError as exc:
|
||||||
|
msg = (
|
||||||
|
"Parquet export requires the optional dependency pyarrow. "
|
||||||
|
'Install it with: pip install "mt5cli[parquet]"'
|
||||||
|
)
|
||||||
|
raise ImportError(msg) from exc
|
||||||
df.to_parquet(output_path, index=False)
|
df.to_parquet(output_path, index=False)
|
||||||
elif output_format == "sqlite3":
|
elif output_format == "sqlite3":
|
||||||
export_dataframe_to_sqlite(
|
export_dataframe_to_sqlite(
|
||||||
|
|||||||
+5
-2
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "mt5cli"
|
name = "mt5cli"
|
||||||
version = "0.9.6"
|
version = "0.9.7"
|
||||||
description = "Generic MT5 data and execution infrastructure for Python applications"
|
description = "Generic MT5 data and execution infrastructure for Python applications"
|
||||||
authors = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
|
authors = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
|
||||||
maintainers = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
|
maintainers = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
|
||||||
@@ -11,7 +11,6 @@ requires-python = ">= 3.11, < 3.14"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"pdmt5>=0.3.0",
|
"pdmt5>=0.3.0",
|
||||||
"click >= 8.1.0",
|
"click >= 8.1.0",
|
||||||
"pyarrow >= 19.0.0",
|
|
||||||
"typer >= 0.15.0",
|
"typer >= 0.15.0",
|
||||||
]
|
]
|
||||||
classifiers = [
|
classifiers = [
|
||||||
@@ -25,6 +24,9 @@ classifiers = [
|
|||||||
"Topic :: Office/Business :: Financial :: Investment",
|
"Topic :: Office/Business :: Financial :: Investment",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
parquet = ["pyarrow >= 19.0.0"]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
mt5cli = "mt5cli.cli:main"
|
mt5cli = "mt5cli.cli:main"
|
||||||
|
|
||||||
@@ -42,6 +44,7 @@ dev = [
|
|||||||
"pytest-mock >= 3.12.0",
|
"pytest-mock >= 3.12.0",
|
||||||
"pytest-cov >= 5.0.0",
|
"pytest-cov >= 5.0.0",
|
||||||
"pandas-stubs >= 2.2.3.250527",
|
"pandas-stubs >= 2.2.3.250527",
|
||||||
|
"pyarrow >= 19.0.0",
|
||||||
"mkdocs >= 1.6.1",
|
"mkdocs >= 1.6.1",
|
||||||
"mkdocs-material >= 9.7.6",
|
"mkdocs-material >= 9.7.6",
|
||||||
"mkdocstrings[python] >= 1.0.4",
|
"mkdocstrings[python] >= 1.0.4",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|||||||
import re
|
import re
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
|
from importlib.metadata import requires
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import get_type_hints
|
from typing import get_type_hints
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
@@ -832,3 +833,24 @@ class TestStableSdkContract:
|
|||||||
assert result.index.tz is not None
|
assert result.index.tz is not None
|
||||||
assert "time" not in result.columns
|
assert "time" not in result.columns
|
||||||
assert "close" in result.columns
|
assert "close" in result.columns
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Packaging metadata
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_parquet_extra_declares_pyarrow() -> None:
|
||||||
|
"""Package metadata lists pyarrow under the parquet optional extra."""
|
||||||
|
reqs = requires("mt5cli") or []
|
||||||
|
parquet_reqs = [r for r in reqs if "pyarrow" in r and "parquet" in r]
|
||||||
|
assert parquet_reqs, "pyarrow not found in parquet optional extra"
|
||||||
|
|
||||||
|
|
||||||
|
def test_pyarrow_not_in_core_dependencies() -> None:
|
||||||
|
"""Pyarrow is not a core dependency; it belongs only in the parquet extra."""
|
||||||
|
reqs = requires("mt5cli") or []
|
||||||
|
core_reqs = [r for r in reqs if "extra ==" not in r]
|
||||||
|
assert not any("pyarrow" in r for r in core_reqs), (
|
||||||
|
"pyarrow should not appear in core dependencies"
|
||||||
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import sys
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
@@ -111,6 +112,17 @@ class TestExportDataframe:
|
|||||||
result = pd.read_parquet(output)
|
result = pd.read_parquet(output)
|
||||||
pd.testing.assert_frame_equal(result, sample_df)
|
pd.testing.assert_frame_equal(result, sample_df)
|
||||||
|
|
||||||
|
def test_export_parquet_without_pyarrow(
|
||||||
|
self,
|
||||||
|
tmp_path: Path,
|
||||||
|
sample_df: pd.DataFrame,
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
"""Test that a clear error is raised when pyarrow is not installed."""
|
||||||
|
monkeypatch.setitem(sys.modules, "pyarrow", None)
|
||||||
|
with pytest.raises(ImportError, match="mt5cli\\[parquet\\]"):
|
||||||
|
export_dataframe(sample_df, tmp_path / "out.parquet", "parquet")
|
||||||
|
|
||||||
def test_export_sqlite3(self, tmp_path: Path, sample_df: pd.DataFrame) -> None:
|
def test_export_sqlite3(self, tmp_path: Path, sample_df: pd.DataFrame) -> None:
|
||||||
"""Test SQLite3 export."""
|
"""Test SQLite3 export."""
|
||||||
output = tmp_path / "out.db"
|
output = tmp_path / "out.db"
|
||||||
|
|||||||
@@ -487,21 +487,26 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mt5cli"
|
name = "mt5cli"
|
||||||
version = "0.9.6"
|
version = "0.9.7"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "click" },
|
{ name = "click" },
|
||||||
{ name = "pdmt5" },
|
{ name = "pdmt5" },
|
||||||
{ name = "pyarrow" },
|
|
||||||
{ name = "typer" },
|
{ name = "typer" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[package.optional-dependencies]
|
||||||
|
parquet = [
|
||||||
|
{ name = "pyarrow" },
|
||||||
|
]
|
||||||
|
|
||||||
[package.dev-dependencies]
|
[package.dev-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
{ name = "mkdocs" },
|
{ name = "mkdocs" },
|
||||||
{ name = "mkdocs-material" },
|
{ name = "mkdocs-material" },
|
||||||
{ name = "mkdocstrings", extra = ["python"] },
|
{ name = "mkdocstrings", extra = ["python"] },
|
||||||
{ name = "pandas-stubs" },
|
{ name = "pandas-stubs" },
|
||||||
|
{ name = "pyarrow" },
|
||||||
{ name = "pymdown-extensions" },
|
{ name = "pymdown-extensions" },
|
||||||
{ name = "pyright" },
|
{ name = "pyright" },
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
@@ -514,9 +519,10 @@ dev = [
|
|||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "click", specifier = ">=8.1.0" },
|
{ name = "click", specifier = ">=8.1.0" },
|
||||||
{ name = "pdmt5", specifier = ">=0.3.0" },
|
{ name = "pdmt5", specifier = ">=0.3.0" },
|
||||||
{ name = "pyarrow", specifier = ">=19.0.0" },
|
{ name = "pyarrow", marker = "extra == 'parquet'", specifier = ">=19.0.0" },
|
||||||
{ name = "typer", specifier = ">=0.15.0" },
|
{ name = "typer", specifier = ">=0.15.0" },
|
||||||
]
|
]
|
||||||
|
provides-extras = ["parquet"]
|
||||||
|
|
||||||
[package.metadata.requires-dev]
|
[package.metadata.requires-dev]
|
||||||
dev = [
|
dev = [
|
||||||
@@ -524,6 +530,7 @@ dev = [
|
|||||||
{ name = "mkdocs-material", specifier = ">=9.7.6" },
|
{ name = "mkdocs-material", specifier = ">=9.7.6" },
|
||||||
{ name = "mkdocstrings", extras = ["python"], specifier = ">=1.0.4" },
|
{ name = "mkdocstrings", extras = ["python"], specifier = ">=1.0.4" },
|
||||||
{ name = "pandas-stubs", specifier = ">=2.2.3.250527" },
|
{ name = "pandas-stubs", specifier = ">=2.2.3.250527" },
|
||||||
|
{ name = "pyarrow", specifier = ">=19.0.0" },
|
||||||
{ name = "pymdown-extensions", specifier = ">=10.21.2" },
|
{ name = "pymdown-extensions", specifier = ">=10.21.2" },
|
||||||
{ name = "pyright", specifier = ">=1.1.407" },
|
{ name = "pyright", specifier = ">=1.1.407" },
|
||||||
{ name = "pytest", specifier = ">=9.0.3" },
|
{ name = "pytest", specifier = ">=9.0.3" },
|
||||||
|
|||||||
Reference in New Issue
Block a user