mt5cli 0.9.5__tar.gz → 0.9.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. {mt5cli-0.9.5 → mt5cli-0.9.7}/PKG-INFO +13 -3
  2. {mt5cli-0.9.5 → mt5cli-0.9.7}/README.md +10 -1
  3. {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/public-contract.md +14 -1
  4. {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/index.md +6 -0
  5. {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/__init__.py +2 -0
  6. {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/cli.py +93 -2
  7. {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/contract.py +1 -0
  8. {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/trading.py +34 -10
  9. {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/utils.py +9 -0
  10. {mt5cli-0.9.5 → mt5cli-0.9.7}/pyproject.toml +5 -2
  11. {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/test_cli.py +300 -0
  12. {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/test_contracts.py +52 -36
  13. {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/test_history.py +19 -54
  14. {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/test_sdk.py +31 -43
  15. {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/test_trading.py +469 -589
  16. {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/test_utils.py +12 -0
  17. {mt5cli-0.9.5 → mt5cli-0.9.7}/uv.lock +10 -3
  18. {mt5cli-0.9.5 → mt5cli-0.9.7}/.agents/skills/local-qa/SKILL.md +0 -0
  19. {mt5cli-0.9.5 → mt5cli-0.9.7}/.agents/skills/local-qa/scripts/qa.sh +0 -0
  20. {mt5cli-0.9.5 → mt5cli-0.9.7}/.agents/skills/mt5cli/SKILL.md +0 -0
  21. {mt5cli-0.9.5 → mt5cli-0.9.7}/.agents/skills/pr-feedback-triage/SKILL.md +0 -0
  22. {mt5cli-0.9.5 → mt5cli-0.9.7}/.claude/agents/codex.md +0 -0
  23. {mt5cli-0.9.5 → mt5cli-0.9.7}/.claude/settings.json +0 -0
  24. {mt5cli-0.9.5 → mt5cli-0.9.7}/.github/FUNDING.yml +0 -0
  25. {mt5cli-0.9.5 → mt5cli-0.9.7}/.github/dependabot.yml +0 -0
  26. {mt5cli-0.9.5 → mt5cli-0.9.7}/.github/renovate.json +0 -0
  27. {mt5cli-0.9.5 → mt5cli-0.9.7}/.github/workflows/ci.yml +0 -0
  28. {mt5cli-0.9.5 → mt5cli-0.9.7}/.github/workflows/claude.yml +0 -0
  29. {mt5cli-0.9.5 → mt5cli-0.9.7}/.github/workflows/release.yml +0 -0
  30. {mt5cli-0.9.5 → mt5cli-0.9.7}/.gitignore +0 -0
  31. {mt5cli-0.9.5 → mt5cli-0.9.7}/AGENTS.md +0 -0
  32. {mt5cli-0.9.5 → mt5cli-0.9.7}/CLAUDE.md +0 -0
  33. {mt5cli-0.9.5 → mt5cli-0.9.7}/LICENSE +0 -0
  34. {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/cli.md +0 -0
  35. {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/client.md +0 -0
  36. {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/converters.md +0 -0
  37. {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/exceptions.md +0 -0
  38. {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/history.md +0 -0
  39. {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/index.md +0 -0
  40. {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/schemas.md +0 -0
  41. {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/sdk.md +0 -0
  42. {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/storage.md +0 -0
  43. {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/trading.md +0 -0
  44. {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/utils.md +0 -0
  45. {mt5cli-0.9.5 → mt5cli-0.9.7}/mkdocs.yml +0 -0
  46. {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/__main__.py +0 -0
  47. {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/client.py +0 -0
  48. {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/converters.py +0 -0
  49. {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/exceptions.py +0 -0
  50. {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/history.py +0 -0
  51. {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/retry.py +0 -0
  52. {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/schemas.py +0 -0
  53. {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/sdk.py +0 -0
  54. {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/storage.py +0 -0
  55. {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/__init__.py +0 -0
  56. {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/conftest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mt5cli
3
- Version: 0.9.5
3
+ Version: 0.9.7
4
4
  Summary: Generic MT5 data and execution infrastructure for Python applications
5
5
  Project-URL: Repository, https://github.com/dceoy/mt5cli.git
6
6
  Author-email: dceoy <dceoy@users.noreply.github.com>
@@ -18,8 +18,9 @@ Classifier: Topic :: Office/Business :: Financial :: Investment
18
18
  Requires-Python: <3.14,>=3.11
19
19
  Requires-Dist: click>=8.1.0
20
20
  Requires-Dist: pdmt5>=0.3.0
21
- Requires-Dist: pyarrow>=19.0.0
22
21
  Requires-Dist: typer>=0.15.0
22
+ Provides-Extra: parquet
23
+ Requires-Dist: pyarrow>=19.0.0; extra == 'parquet'
23
24
  Description-Content-Type: text/markdown
24
25
 
25
26
  # mt5cli
@@ -53,6 +54,12 @@ Built on top of [pdmt5](https://github.com/dceoy/pdmt5), a pandas-based data han
53
54
  pip install -U mt5cli MetaTrader5
54
55
  ```
55
56
 
57
+ Parquet export is not included by default. To enable it, install the `parquet` extra:
58
+
59
+ ```bash
60
+ pip install -U "mt5cli[parquet]" MetaTrader5
61
+ ```
62
+
56
63
  ## Python API (downstream packages)
57
64
 
58
65
  Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives.
@@ -202,10 +209,13 @@ python -m mt5cli -o account.csv account-info
202
209
  | `recent-history-deals` | Export historical deals from a recent trailing window |
203
210
  | `mt5-summary` | Export terminal/account status summary |
204
211
  | `order-check` | Check funds sufficiency for a trade request |
205
- | `order-send` | Send a trade request to the trade server (`--yes` required) |
212
+ | `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path) |
213
+ | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available) |
206
214
  | `collect-history` | Bundle rates, ticks, history-orders, and history-deals for one or more symbols into a single SQLite database |
207
215
 
208
216
  Use `order-check` to validate a request payload before running `order-send --yes`.
217
+ `close-positions` is the safer high-level alternative that builds correct close
218
+ requests automatically. At least one `--symbol` or `--ticket` must be provided.
209
219
 
210
220
  ### `collect-history`
211
221
 
@@ -29,6 +29,12 @@ Built on top of [pdmt5](https://github.com/dceoy/pdmt5), a pandas-based data han
29
29
  pip install -U mt5cli MetaTrader5
30
30
  ```
31
31
 
32
+ Parquet export is not included by default. To enable it, install the `parquet` extra:
33
+
34
+ ```bash
35
+ pip install -U "mt5cli[parquet]" MetaTrader5
36
+ ```
37
+
32
38
  ## Python API (downstream packages)
33
39
 
34
40
  Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives.
@@ -178,10 +184,13 @@ python -m mt5cli -o account.csv account-info
178
184
  | `recent-history-deals` | Export historical deals from a recent trailing window |
179
185
  | `mt5-summary` | Export terminal/account status summary |
180
186
  | `order-check` | Check funds sufficiency for a trade request |
181
- | `order-send` | Send a trade request to the trade server (`--yes` required) |
187
+ | `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path) |
188
+ | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available) |
182
189
  | `collect-history` | Bundle rates, ticks, history-orders, and history-deals for one or more symbols into a single SQLite database |
183
190
 
184
191
  Use `order-check` to validate a request payload before running `order-send --yes`.
192
+ `close-positions` is the safer high-level alternative that builds correct close
193
+ requests automatically. At least one `--symbol` or `--ticket` must be provided.
185
194
 
186
195
  ### `collect-history`
187
196
 
@@ -111,6 +111,14 @@ strategy entries, exits, Kelly sizing, or signal logic.
111
111
  | `place_market_order`, `close_open_positions`, `update_sltp_for_open_positions`, `update_trailing_stop_loss_for_open_positions` | Order execution helpers (`dry_run` supported) |
112
112
  | `MarginVolume`, `OrderLimits`, `OrderExecutionResult` | Typed return contracts for order helpers |
113
113
  | `OrderSide`, `OrderFillingMode`, `OrderTimeMode`, `PositionSide`, `ExecutionStatus` | Typed enums for order helpers |
114
+ | `ProjectionMode` | Literal type for `calculate_symbol_group_margin_ratio` projection |
115
+
116
+ `calculate_symbol_group_margin_ratio` accepts an optional `projection_mode`
117
+ parameter (`"add"` by default). Pass `projection_mode="replace_symbol"` to
118
+ subtract current exposure for `new_symbol` before adding the candidate margin —
119
+ useful for reversal-style projections. mt5cli only calculates broker-facing
120
+ exposure; downstream applications own thresholds, risk guard actions, and
121
+ strategy policy.
114
122
 
115
123
  `MT5Client.order_send()` and CLI `order-send --yes` are live execution paths.
116
124
 
@@ -182,7 +190,12 @@ The Typer application in `mt5cli.cli` exposes file-export commands documented in
182
190
  - Delegate to the same Python APIs described here; they are not duplicated
183
191
  business logic.
184
192
 
185
- `order-send` requires `--yes` before placing live trades.
193
+ `order-send` is the expert raw-request path; it requires `--yes` and a fully
194
+ constructed request payload. `close-positions` is the safer high-level helper
195
+ that closes open positions by `--symbol` or `--ticket` using
196
+ `close_open_positions()`. Both `order-send --yes` and `close-positions --yes`
197
+ are live execution paths. `close-positions --dry-run` previews close orders
198
+ without placing them and does not require `--yes`.
186
199
 
187
200
  ## Internal helpers (not stable)
188
201
 
@@ -27,6 +27,12 @@ mt5cli provides a stable `MT5Client` Python API, standardized dataset schemas, s
27
27
  pip install mt5cli
28
28
  ```
29
29
 
30
+ Parquet export is not included by default. To enable it, install the `parquet` extra:
31
+
32
+ ```bash
33
+ pip install "mt5cli[parquet]"
34
+ ```
35
+
30
36
  ## Python API for downstream packages
31
37
 
32
38
  Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives.
@@ -120,6 +120,7 @@ from .trading import (
120
120
  OrderSide,
121
121
  OrderTimeMode,
122
122
  PositionSide,
123
+ ProjectionMode,
123
124
  calculate_account_projected_margin_ratio,
124
125
  calculate_margin_and_volume,
125
126
  calculate_new_position_margin_ratio,
@@ -192,6 +193,7 @@ __all__ = [
192
193
  "OrderSide",
193
194
  "OrderTimeMode",
194
195
  "PositionSide",
196
+ "ProjectionMode",
195
197
  "RateTarget",
196
198
  "ThrottledHistoryUpdater",
197
199
  "account_info",
@@ -2,17 +2,20 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import json
5
6
  import logging
6
7
  from dataclasses import dataclass
7
8
  from datetime import datetime # noqa: TC003
8
9
  from pathlib import Path # noqa: TC003
9
10
  from typing import TYPE_CHECKING, Annotated, Any, cast
10
11
 
12
+ import pandas as pd
11
13
  import typer
12
14
  from pdmt5 import Mt5Config
13
15
 
14
16
  from . import sdk
15
17
  from .client import MT5Client
18
+ from .trading import OrderExecutionResult, close_open_positions, create_trading_client
16
19
  from .utils import (
17
20
  DATETIME_TYPE,
18
21
  REQUEST_TYPE,
@@ -29,8 +32,6 @@ from .utils import (
29
32
  if TYPE_CHECKING:
30
33
  from collections.abc import Callable
31
34
 
32
- import pandas as pd
33
-
34
35
  logger = logging.getLogger(__name__)
35
36
 
36
37
  # ---------------------------------------------------------------------------
@@ -600,6 +601,96 @@ def order_send(
600
601
  _export_command(ctx, lambda client: client.order_send(request))
601
602
 
602
603
 
604
+ _EXECUTION_RESULT_COLUMNS: list[str] = [
605
+ "status",
606
+ "symbol",
607
+ "order_side",
608
+ "volume",
609
+ "retcode",
610
+ "comment",
611
+ "request",
612
+ "response",
613
+ "dry_run",
614
+ ]
615
+
616
+
617
+ def _execution_results_to_df(results: list[OrderExecutionResult]) -> pd.DataFrame:
618
+ if not results:
619
+ return pd.DataFrame(columns=_EXECUTION_RESULT_COLUMNS)
620
+ rows = [
621
+ {
622
+ **r,
623
+ "request": json.dumps(r["request"]),
624
+ "response": json.dumps(r["response"]),
625
+ }
626
+ for r in results
627
+ ]
628
+ return pd.DataFrame(rows)
629
+
630
+
631
+ @app.command()
632
+ def close_positions(
633
+ ctx: typer.Context,
634
+ symbol: Annotated[
635
+ list[str] | None,
636
+ typer.Option(
637
+ "--symbol",
638
+ "-s",
639
+ help="Symbol to close (repeat for multiple symbols).",
640
+ ),
641
+ ] = None,
642
+ ticket: Annotated[
643
+ list[int] | None,
644
+ typer.Option(
645
+ "--ticket",
646
+ "-t",
647
+ help="Position ticket to close (repeat for multiple tickets).",
648
+ ),
649
+ ] = None,
650
+ dry_run: Annotated[
651
+ bool,
652
+ typer.Option("--dry-run", help="Preview close orders without executing them."),
653
+ ] = False,
654
+ yes: Annotated[
655
+ bool,
656
+ typer.Option("--yes", help="Confirm live position closing."),
657
+ ] = False,
658
+ ) -> None:
659
+ """Close open positions by symbol or ticket.
660
+
661
+ Delegates to :func:`mt5cli.trading.close_open_positions`. At least one
662
+ ``--symbol`` or ``--ticket`` must be provided to avoid accidentally closing
663
+ all positions. Use ``--dry-run`` to preview without executing; ``--yes`` is
664
+ required for live execution.
665
+
666
+ ``order-send`` is the expert raw-request path. ``close-positions`` is the
667
+ safer high-level helper that builds correct close requests automatically.
668
+
669
+ Raises:
670
+ typer.BadParameter: If neither ``--symbol`` nor ``--ticket`` is given,
671
+ or if ``--yes`` is missing for a live (non-dry-run) run.
672
+ """
673
+ if not symbol and not ticket:
674
+ msg = "Provide at least one --symbol or --ticket to close positions."
675
+ raise typer.BadParameter(msg)
676
+ if not dry_run and not yes:
677
+ msg = "Pass --yes to close live positions."
678
+ raise typer.BadParameter(msg, param_hint="--yes")
679
+ export_ctx = _get_export_context(ctx)
680
+ client = create_trading_client(config=export_ctx.config)
681
+ try:
682
+ results = close_open_positions(
683
+ client,
684
+ symbols=list(symbol) if symbol else None,
685
+ tickets=list(ticket) if ticket else None,
686
+ dry_run=dry_run,
687
+ )
688
+ finally:
689
+ client.shutdown()
690
+ df = _execution_results_to_df(results)
691
+ _execute_export(ctx, lambda: df)
692
+
693
+
603
694
  @app.command()
604
695
  def collect_history(
605
696
  ctx: typer.Context,
@@ -17,6 +17,7 @@ STABLE_SDK_EXPORTS: frozenset[str] = frozenset({
17
17
  "OrderSide",
18
18
  "OrderTimeMode",
19
19
  "PositionSide",
20
+ "ProjectionMode",
20
21
  "ExecutionStatus",
21
22
  "MarginVolume",
22
23
  "OrderExecutionResult",
@@ -25,6 +25,7 @@ OrderSide = Literal["BUY", "SELL"]
25
25
  OrderFillingMode = Literal["IOC", "FOK", "RETURN"]
26
26
  OrderTimeMode = Literal["GTC", "DAY", "SPECIFIED", "SPECIFIED_DAY"]
27
27
  ExecutionStatus = Literal["executed", "dry_run", "skipped", "failed"]
28
+ ProjectionMode = Literal["add", "replace_symbol"]
28
29
 
29
30
 
30
31
  class MarginVolume(TypedDict):
@@ -127,6 +128,7 @@ __all__ = [
127
128
  "OrderSide",
128
129
  "OrderTimeMode",
129
130
  "PositionSide",
131
+ "ProjectionMode",
130
132
  "calculate_account_projected_margin_ratio",
131
133
  "calculate_margin_and_volume",
132
134
  "calculate_new_position_margin_ratio",
@@ -920,6 +922,16 @@ def calculate_projected_margin_ratio(
920
922
  return margin / equity
921
923
 
922
924
 
925
+ def _validate_projection_mode(projection_mode: str) -> ProjectionMode:
926
+ if projection_mode not in {"add", "replace_symbol"}:
927
+ msg = (
928
+ f"Unsupported projection mode: {projection_mode!r}. "
929
+ "Expected 'add' or 'replace_symbol'."
930
+ )
931
+ raise ValueError(msg)
932
+ return cast("ProjectionMode", projection_mode)
933
+
934
+
923
935
  def calculate_symbol_group_margin_ratio(
924
936
  client: Mt5TradingClient,
925
937
  *,
@@ -928,13 +940,22 @@ def calculate_symbol_group_margin_ratio(
928
940
  new_position_side: OrderSide | None = None,
929
941
  new_position_volume: float = 0.0,
930
942
  suppress_errors: bool = True,
943
+ projection_mode: ProjectionMode = "add",
931
944
  ) -> float:
932
945
  """Return estimated symbol-group margin over account equity.
933
946
 
934
947
  Per-symbol current exposure is summed with
935
948
  :func:`calculate_positions_margin_by_symbol`. When ``new_symbol`` is inside
936
- the input symbol group, optional projected order margin is added for that
937
- symbol. Invalid equity always raises to fail closed.
949
+ the input symbol group and candidate side/volume are provided, projected order
950
+ margin is applied according to ``projection_mode``:
951
+
952
+ - ``"add"`` (default): adds candidate margin to the group total.
953
+ - ``"replace_symbol"``: subtracts current margin for ``new_symbol``, then
954
+ adds candidate margin. Useful for reversal-style projections where the new
955
+ order is intended to replace existing exposure for that symbol.
956
+
957
+ If the candidate margin estimation fails, the subtraction is also skipped so
958
+ the operation is atomic. Invalid equity always raises to fail closed.
938
959
 
939
960
  Raises:
940
961
  AttributeError: When symbol margin lookup or projected margin lookup
@@ -945,23 +966,22 @@ def calculate_symbol_group_margin_ratio(
945
966
  lookup or projected margin lookup fails and ``suppress_errors`` is
946
967
  ``False``.
947
968
  """
969
+ projection_mode = _validate_projection_mode(projection_mode)
948
970
  equity = _account_equity(client)
949
971
  unique_symbols = list(dict.fromkeys(symbols))
950
- margin = sum(
951
- calculate_positions_margin_by_symbol(
952
- client,
953
- symbols=unique_symbols,
954
- suppress_errors=suppress_errors,
955
- ).values(),
956
- 0.0,
972
+ per_symbol = calculate_positions_margin_by_symbol(
973
+ client,
974
+ symbols=unique_symbols,
975
+ suppress_errors=suppress_errors,
957
976
  )
977
+ margin = sum(per_symbol.values(), 0.0)
958
978
  if (
959
979
  new_symbol in unique_symbols
960
980
  and new_position_side is not None
961
981
  and new_position_volume > 0
962
982
  ):
963
983
  try:
964
- margin += estimate_order_margin(
984
+ candidate_margin = estimate_order_margin(
965
985
  client,
966
986
  new_symbol,
967
987
  new_position_side,
@@ -971,6 +991,10 @@ def calculate_symbol_group_margin_ratio(
971
991
  if not suppress_errors:
972
992
  raise
973
993
  _logger.warning("Skipping projected margin for %r.", new_symbol)
994
+ else:
995
+ if projection_mode == "replace_symbol":
996
+ margin = max(0.0, margin - per_symbol.get(new_symbol, 0.0))
997
+ margin += candidate_margin
974
998
  return margin / equity
975
999
 
976
1000
 
@@ -314,6 +314,7 @@ def export_dataframe(
314
314
  table_name: Table name for SQLite3 output.
315
315
 
316
316
  Raises:
317
+ ImportError: If the parquet format is requested but pyarrow is not installed.
317
318
  ValueError: If the output format is not supported.
318
319
  """
319
320
  if output_format == "csv":
@@ -326,6 +327,14 @@ def export_dataframe(
326
327
  indent=2,
327
328
  )
328
329
  elif output_format == "parquet":
330
+ try:
331
+ __import__("pyarrow")
332
+ except ImportError as exc:
333
+ msg = (
334
+ "Parquet export requires the optional dependency pyarrow. "
335
+ 'Install it with: pip install "mt5cli[parquet]"'
336
+ )
337
+ raise ImportError(msg) from exc
329
338
  df.to_parquet(output_path, index=False)
330
339
  elif output_format == "sqlite3":
331
340
  export_dataframe_to_sqlite(
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mt5cli"
3
- version = "0.9.5"
3
+ version = "0.9.7"
4
4
  description = "Generic MT5 data and execution infrastructure for Python applications"
5
5
  authors = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
6
6
  maintainers = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
@@ -11,7 +11,6 @@ requires-python = ">= 3.11, < 3.14"
11
11
  dependencies = [
12
12
  "pdmt5>=0.3.0",
13
13
  "click >= 8.1.0",
14
- "pyarrow >= 19.0.0",
15
14
  "typer >= 0.15.0",
16
15
  ]
17
16
  classifiers = [
@@ -25,6 +24,9 @@ classifiers = [
25
24
  "Topic :: Office/Business :: Financial :: Investment",
26
25
  ]
27
26
 
27
+ [project.optional-dependencies]
28
+ parquet = ["pyarrow >= 19.0.0"]
29
+
28
30
  [project.scripts]
29
31
  mt5cli = "mt5cli.cli:main"
30
32
 
@@ -42,6 +44,7 @@ dev = [
42
44
  "pytest-mock >= 3.12.0",
43
45
  "pytest-cov >= 5.0.0",
44
46
  "pandas-stubs >= 2.2.3.250527",
47
+ "pyarrow >= 19.0.0",
45
48
  "mkdocs >= 1.6.1",
46
49
  "mkdocs-material >= 9.7.6",
47
50
  "mkdocstrings[python] >= 1.0.4",