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.
- {mt5cli-0.9.5 → mt5cli-0.9.7}/PKG-INFO +13 -3
- {mt5cli-0.9.5 → mt5cli-0.9.7}/README.md +10 -1
- {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/public-contract.md +14 -1
- {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/index.md +6 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/__init__.py +2 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/cli.py +93 -2
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/contract.py +1 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/trading.py +34 -10
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/utils.py +9 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/pyproject.toml +5 -2
- {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/test_cli.py +300 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/test_contracts.py +52 -36
- {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/test_history.py +19 -54
- {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/test_sdk.py +31 -43
- {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/test_trading.py +469 -589
- {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/test_utils.py +12 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/uv.lock +10 -3
- {mt5cli-0.9.5 → mt5cli-0.9.7}/.agents/skills/local-qa/SKILL.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/.agents/skills/local-qa/scripts/qa.sh +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/.agents/skills/mt5cli/SKILL.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/.agents/skills/pr-feedback-triage/SKILL.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/.claude/agents/codex.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/.claude/settings.json +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/.github/FUNDING.yml +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/.github/dependabot.yml +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/.github/renovate.json +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/.github/workflows/ci.yml +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/.github/workflows/claude.yml +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/.github/workflows/release.yml +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/.gitignore +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/AGENTS.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/CLAUDE.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/LICENSE +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/cli.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/client.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/converters.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/exceptions.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/history.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/index.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/schemas.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/sdk.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/storage.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/trading.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/docs/api/utils.md +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mkdocs.yml +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/__main__.py +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/client.py +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/converters.py +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/exceptions.py +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/history.py +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/retry.py +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/schemas.py +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/sdk.py +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/mt5cli/storage.py +0 -0
- {mt5cli-0.9.5 → mt5cli-0.9.7}/tests/__init__.py +0 -0
- {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.
|
|
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`
|
|
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,
|
|
@@ -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
|
|
937
|
-
|
|
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
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|