mt5cli 1.0.0__tar.gz → 1.0.1__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 (54) hide show
  1. {mt5cli-1.0.0 → mt5cli-1.0.1}/PKG-INFO +1 -1
  2. {mt5cli-1.0.0 → mt5cli-1.0.1}/docs/index.md +25 -13
  3. {mt5cli-1.0.0 → mt5cli-1.0.1}/mt5cli/cli.py +41 -30
  4. {mt5cli-1.0.0 → mt5cli-1.0.1}/pyproject.toml +1 -1
  5. {mt5cli-1.0.0 → mt5cli-1.0.1}/tests/test_cli.py +60 -0
  6. {mt5cli-1.0.0 → mt5cli-1.0.1}/uv.lock +1 -1
  7. {mt5cli-1.0.0 → mt5cli-1.0.1}/.agents/skills/local-qa/SKILL.md +0 -0
  8. {mt5cli-1.0.0 → mt5cli-1.0.1}/.agents/skills/local-qa/scripts/qa.sh +0 -0
  9. {mt5cli-1.0.0 → mt5cli-1.0.1}/.agents/skills/mt5cli/SKILL.md +0 -0
  10. {mt5cli-1.0.0 → mt5cli-1.0.1}/.agents/skills/pr-feedback-triage/SKILL.md +0 -0
  11. {mt5cli-1.0.0 → mt5cli-1.0.1}/.claude/agents/codex.md +0 -0
  12. {mt5cli-1.0.0 → mt5cli-1.0.1}/.claude/settings.json +0 -0
  13. {mt5cli-1.0.0 → mt5cli-1.0.1}/.github/FUNDING.yml +0 -0
  14. {mt5cli-1.0.0 → mt5cli-1.0.1}/.github/dependabot.yml +0 -0
  15. {mt5cli-1.0.0 → mt5cli-1.0.1}/.github/renovate.json +0 -0
  16. {mt5cli-1.0.0 → mt5cli-1.0.1}/.github/workflows/ci.yml +0 -0
  17. {mt5cli-1.0.0 → mt5cli-1.0.1}/.github/workflows/claude.yml +0 -0
  18. {mt5cli-1.0.0 → mt5cli-1.0.1}/.github/workflows/release.yml +0 -0
  19. {mt5cli-1.0.0 → mt5cli-1.0.1}/.gitignore +0 -0
  20. {mt5cli-1.0.0 → mt5cli-1.0.1}/AGENTS.md +0 -0
  21. {mt5cli-1.0.0 → mt5cli-1.0.1}/CLAUDE.md +0 -0
  22. {mt5cli-1.0.0 → mt5cli-1.0.1}/LICENSE +0 -0
  23. {mt5cli-1.0.0 → mt5cli-1.0.1}/README.md +0 -0
  24. {mt5cli-1.0.0 → mt5cli-1.0.1}/docs/api/cli.md +0 -0
  25. {mt5cli-1.0.0 → mt5cli-1.0.1}/docs/api/client.md +0 -0
  26. {mt5cli-1.0.0 → mt5cli-1.0.1}/docs/api/converters.md +0 -0
  27. {mt5cli-1.0.0 → mt5cli-1.0.1}/docs/api/exceptions.md +0 -0
  28. {mt5cli-1.0.0 → mt5cli-1.0.1}/docs/api/history.md +0 -0
  29. {mt5cli-1.0.0 → mt5cli-1.0.1}/docs/api/index.md +0 -0
  30. {mt5cli-1.0.0 → mt5cli-1.0.1}/docs/api/public-contract.md +0 -0
  31. {mt5cli-1.0.0 → mt5cli-1.0.1}/docs/api/schemas.md +0 -0
  32. {mt5cli-1.0.0 → mt5cli-1.0.1}/docs/api/sdk.md +0 -0
  33. {mt5cli-1.0.0 → mt5cli-1.0.1}/docs/api/trading.md +0 -0
  34. {mt5cli-1.0.0 → mt5cli-1.0.1}/docs/api/utils.md +0 -0
  35. {mt5cli-1.0.0 → mt5cli-1.0.1}/mkdocs.yml +0 -0
  36. {mt5cli-1.0.0 → mt5cli-1.0.1}/mt5cli/__init__.py +0 -0
  37. {mt5cli-1.0.0 → mt5cli-1.0.1}/mt5cli/__main__.py +0 -0
  38. {mt5cli-1.0.0 → mt5cli-1.0.1}/mt5cli/client.py +0 -0
  39. {mt5cli-1.0.0 → mt5cli-1.0.1}/mt5cli/contract.py +0 -0
  40. {mt5cli-1.0.0 → mt5cli-1.0.1}/mt5cli/converters.py +0 -0
  41. {mt5cli-1.0.0 → mt5cli-1.0.1}/mt5cli/exceptions.py +0 -0
  42. {mt5cli-1.0.0 → mt5cli-1.0.1}/mt5cli/history.py +0 -0
  43. {mt5cli-1.0.0 → mt5cli-1.0.1}/mt5cli/retry.py +0 -0
  44. {mt5cli-1.0.0 → mt5cli-1.0.1}/mt5cli/schemas.py +0 -0
  45. {mt5cli-1.0.0 → mt5cli-1.0.1}/mt5cli/sdk.py +0 -0
  46. {mt5cli-1.0.0 → mt5cli-1.0.1}/mt5cli/trading.py +0 -0
  47. {mt5cli-1.0.0 → mt5cli-1.0.1}/mt5cli/utils.py +0 -0
  48. {mt5cli-1.0.0 → mt5cli-1.0.1}/tests/__init__.py +0 -0
  49. {mt5cli-1.0.0 → mt5cli-1.0.1}/tests/conftest.py +0 -0
  50. {mt5cli-1.0.0 → mt5cli-1.0.1}/tests/test_contracts.py +0 -0
  51. {mt5cli-1.0.0 → mt5cli-1.0.1}/tests/test_history.py +0 -0
  52. {mt5cli-1.0.0 → mt5cli-1.0.1}/tests/test_sdk.py +0 -0
  53. {mt5cli-1.0.0 → mt5cli-1.0.1}/tests/test_trading.py +0 -0
  54. {mt5cli-1.0.0 → mt5cli-1.0.1}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mt5cli
3
- Version: 1.0.0
3
+ Version: 1.0.1
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>
@@ -147,20 +147,32 @@ mt5cli --login 12345 --password mypass --server MyBroker-Demo \
147
147
  | `minimum-margins` | Export minimum-volume margin summary |
148
148
  | `market-book` | Export market depth (order book) |
149
149
 
150
- ### Trading
150
+ ### Trading State
151
151
 
152
- | Command | Description |
153
- | ---------------------- | ----------------------------------------------------------- |
154
- | `orders` | Export active orders |
155
- | `positions` | Export open positions |
156
- | `history-orders` | Export historical orders |
157
- | `history-deals` | Export historical deals |
158
- | `recent-history-deals` | Export historical deals from a trailing window |
159
- | `mt5-summary` | Export terminal/account status summary |
160
- | `order-check` | Check funds sufficiency for a trade request |
161
- | `order-send` | Send a trade request to the trade server (`--yes` required) |
152
+ | Command | Description |
153
+ | ---------------------- | --------------------------------------------------------------------- |
154
+ | `orders` | Export active orders |
155
+ | `positions` | Export open positions |
156
+ | `history-orders` | Export historical orders |
157
+ | `history-deals` | Export historical deals |
158
+ | `recent-history-deals` | Export historical deals from a trailing window |
159
+ | `mt5-summary` | Export terminal/account status summary |
160
+ | `order-check` | Check funds sufficiency for a trade request (read-only, no `--yes`) |
162
161
 
163
- Use `order-check` to validate a request payload before running `order-send --yes`.
162
+ ### Execution (live / mutating)
163
+
164
+ These commands send requests to the live trade server and can place or close
165
+ real trades. Both require `--yes` for live execution.
166
+
167
+ | Command | Description |
168
+ | ----------------- | ---------------------------------------------------------------------------------------------------- |
169
+ | `order-send` | Send a **raw** trade request directly to MT5 (`--yes` required; expert path — no extra validation) |
170
+ | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` to preview) |
171
+
172
+ Use `order-check` (Trading State) to validate funds before running `order-send --yes`.
173
+ `close-positions` is the safer high-level alternative that builds correct close
174
+ requests automatically. `order-send` is the expert raw path — downstream
175
+ applications should prefer dedicated closing helpers or their own risk controls.
164
176
 
165
177
  ### Bulk Collection
166
178
 
@@ -217,7 +229,7 @@ See the [History schema diagram](api/history.md#entity-relationship-diagram) for
217
229
 
218
230
  Browse the API documentation for detailed module information:
219
231
 
220
- - [CLI Module](api/cli.md) - CLI application with export commands
232
+ - [CLI Module](api/cli.md) - CLI application with data export and execution commands
221
233
  - [SDK Module](api/sdk.md) - Programmatic read-only data collection API
222
234
  - [Utils Module](api/utils.md) - Constants, parameter types, parsers, and export utilities
223
235
 
@@ -1,4 +1,4 @@
1
- """Command-line interface for MetaTrader 5 data export."""
1
+ """Command-line interface for MetaTrader 5 data and execution utilities."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -55,7 +55,12 @@ class _ExportContext:
55
55
 
56
56
  app = typer.Typer(
57
57
  name="mt5cli",
58
- help="Export MetaTrader5 data to CSV, JSON, Parquet, or SQLite3.",
58
+ help=(
59
+ "MT5 data and execution utilities — read market data, inspect account"
60
+ " state, and send trade requests. Data commands write to CSV, JSON,"
61
+ " Parquet, or SQLite3. Execution commands (order-send, close-positions)"
62
+ " require --yes for live mutations."
63
+ ),
59
64
  )
60
65
 
61
66
  _REQUEST_OPTION_HELP = (
@@ -151,7 +156,7 @@ def _callback( # pyright: ignore[reportUnusedFunction]
151
156
  typer.Option("--log-level", help="Logging level."),
152
157
  ] = LogLevel.WARNING,
153
158
  ) -> None:
154
- """Configure shared options for all export commands.
159
+ """Configure shared connection and output options.
155
160
 
156
161
  Raises:
157
162
  typer.BadParameter: If the output format cannot be determined.
@@ -183,7 +188,7 @@ def _callback( # pyright: ignore[reportUnusedFunction]
183
188
  # ---------------------------------------------------------------------------
184
189
 
185
190
 
186
- @app.command()
191
+ @app.command(rich_help_panel="Data / Export")
187
192
  def rates_from(
188
193
  ctx: typer.Context,
189
194
  symbol: Annotated[str, typer.Option(help="Symbol name.")],
@@ -210,7 +215,7 @@ def rates_from(
210
215
  )
211
216
 
212
217
 
213
- @app.command()
218
+ @app.command(rich_help_panel="Data / Export")
214
219
  def rates_from_pos(
215
220
  ctx: typer.Context,
216
221
  symbol: Annotated[str, typer.Option(help="Symbol name.")],
@@ -236,7 +241,7 @@ def rates_from_pos(
236
241
  )
237
242
 
238
243
 
239
- @app.command()
244
+ @app.command(rich_help_panel="Data / Export")
240
245
  def latest_rates(
241
246
  ctx: typer.Context,
242
247
  symbol: Annotated[str, typer.Option(help="Symbol name.")],
@@ -265,7 +270,7 @@ def latest_rates(
265
270
  )
266
271
 
267
272
 
268
- @app.command()
273
+ @app.command(rich_help_panel="Data / Export")
269
274
  def rates_range(
270
275
  ctx: typer.Context,
271
276
  symbol: Annotated[str, typer.Option(help="Symbol name.")],
@@ -292,7 +297,7 @@ def rates_range(
292
297
  )
293
298
 
294
299
 
295
- @app.command()
300
+ @app.command(rich_help_panel="Data / Export")
296
301
  def ticks_from(
297
302
  ctx: typer.Context,
298
303
  symbol: Annotated[str, typer.Option(help="Symbol name.")],
@@ -316,7 +321,7 @@ def ticks_from(
316
321
  )
317
322
 
318
323
 
319
- @app.command()
324
+ @app.command(rich_help_panel="Data / Export")
320
325
  def ticks_range(
321
326
  ctx: typer.Context,
322
327
  symbol: Annotated[str, typer.Option(help="Symbol name.")],
@@ -340,7 +345,7 @@ def ticks_range(
340
345
  )
341
346
 
342
347
 
343
- @app.command()
348
+ @app.command(rich_help_panel="Data / Export")
344
349
  def ticks_recent(
345
350
  ctx: typer.Context,
346
351
  symbol: Annotated[str, typer.Option(help="Symbol name.")],
@@ -377,19 +382,19 @@ def ticks_recent(
377
382
  )
378
383
 
379
384
 
380
- @app.command()
385
+ @app.command(rich_help_panel="Data / Export")
381
386
  def account_info(ctx: typer.Context) -> None:
382
387
  """Export account information."""
383
388
  _export_command(ctx, lambda client: client.account_info())
384
389
 
385
390
 
386
- @app.command()
391
+ @app.command(rich_help_panel="Data / Export")
387
392
  def terminal_info(ctx: typer.Context) -> None:
388
393
  """Export terminal information."""
389
394
  _export_command(ctx, lambda client: client.terminal_info())
390
395
 
391
396
 
392
- @app.command()
397
+ @app.command(rich_help_panel="Data / Export")
393
398
  def symbols(
394
399
  ctx: typer.Context,
395
400
  group: Annotated[
@@ -401,7 +406,7 @@ def symbols(
401
406
  _export_command(ctx, lambda client: client.symbols(group=group))
402
407
 
403
408
 
404
- @app.command()
409
+ @app.command(rich_help_panel="Data / Export")
405
410
  def symbol_info(
406
411
  ctx: typer.Context,
407
412
  symbol: Annotated[str, typer.Option(help="Symbol name.")],
@@ -410,7 +415,7 @@ def symbol_info(
410
415
  _export_command(ctx, lambda client: client.symbol_info(symbol))
411
416
 
412
417
 
413
- @app.command()
418
+ @app.command(rich_help_panel="Data / Export")
414
419
  def minimum_margins(
415
420
  ctx: typer.Context,
416
421
  symbol: Annotated[str, typer.Option(help="Symbol name.")],
@@ -419,7 +424,7 @@ def minimum_margins(
419
424
  _export_command(ctx, lambda client: client.minimum_margins(symbol))
420
425
 
421
426
 
422
- @app.command()
427
+ @app.command(rich_help_panel="Data / Export")
423
428
  def orders(
424
429
  ctx: typer.Context,
425
430
  symbol: Annotated[str | None, typer.Option(help="Symbol filter.")] = None,
@@ -433,7 +438,7 @@ def orders(
433
438
  )
434
439
 
435
440
 
436
- @app.command()
441
+ @app.command(rich_help_panel="Data / Export")
437
442
  def positions(
438
443
  ctx: typer.Context,
439
444
  symbol: Annotated[str | None, typer.Option(help="Symbol filter.")] = None,
@@ -447,7 +452,7 @@ def positions(
447
452
  )
448
453
 
449
454
 
450
- @app.command()
455
+ @app.command(rich_help_panel="Data / Export")
451
456
  def history_orders(
452
457
  ctx: typer.Context,
453
458
  date_from: Annotated[
@@ -477,7 +482,7 @@ def history_orders(
477
482
  )
478
483
 
479
484
 
480
- @app.command()
485
+ @app.command(rich_help_panel="Data / Export")
481
486
  def history_deals(
482
487
  ctx: typer.Context,
483
488
  date_from: Annotated[
@@ -507,7 +512,7 @@ def history_deals(
507
512
  )
508
513
 
509
514
 
510
- @app.command()
515
+ @app.command(rich_help_panel="Data / Export")
511
516
  def recent_history_deals(
512
517
  ctx: typer.Context,
513
518
  hours: Annotated[float, typer.Option(help="Lookback window in hours.")],
@@ -530,25 +535,25 @@ def recent_history_deals(
530
535
  )
531
536
 
532
537
 
533
- @app.command()
538
+ @app.command(rich_help_panel="Data / Export")
534
539
  def mt5_summary(ctx: typer.Context) -> None:
535
540
  """Export a compact terminal/account status summary."""
536
541
  _export_command(ctx, lambda client: client.mt5_summary_as_df())
537
542
 
538
543
 
539
- @app.command()
544
+ @app.command(rich_help_panel="Data / Export")
540
545
  def version(ctx: typer.Context) -> None:
541
546
  """Export MetaTrader5 version information."""
542
547
  _export_command(ctx, lambda client: client.version())
543
548
 
544
549
 
545
- @app.command()
550
+ @app.command(rich_help_panel="Data / Export")
546
551
  def last_error(ctx: typer.Context) -> None:
547
552
  """Export the last error information."""
548
553
  _export_command(ctx, lambda client: client.last_error())
549
554
 
550
555
 
551
- @app.command()
556
+ @app.command(rich_help_panel="Data / Export")
552
557
  def symbol_info_tick(
553
558
  ctx: typer.Context,
554
559
  symbol: Annotated[str, typer.Option(help="Symbol name.")],
@@ -557,7 +562,7 @@ def symbol_info_tick(
557
562
  _export_command(ctx, lambda client: client.symbol_info_tick(symbol))
558
563
 
559
564
 
560
- @app.command()
565
+ @app.command(rich_help_panel="Data / Export")
561
566
  def market_book(
562
567
  ctx: typer.Context,
563
568
  symbol: Annotated[str, typer.Option(help="Symbol name.")],
@@ -566,7 +571,7 @@ def market_book(
566
571
  _export_command(ctx, lambda client: client.market_book(symbol))
567
572
 
568
573
 
569
- @app.command()
574
+ @app.command(rich_help_panel="Data / Export")
570
575
  def order_check(
571
576
  ctx: typer.Context,
572
577
  request: Annotated[
@@ -578,7 +583,7 @@ def order_check(
578
583
  _export_command(ctx, lambda client: client.order_check(request))
579
584
 
580
585
 
581
- @app.command()
586
+ @app.command(rich_help_panel="Execution")
582
587
  def order_send(
583
588
  ctx: typer.Context,
584
589
  request: Annotated[
@@ -590,7 +595,13 @@ def order_send(
590
595
  typer.Option("--yes", help="Confirm the live trade request."),
591
596
  ] = False,
592
597
  ) -> None:
593
- """Send a trading operation request to the trade server.
598
+ """Send a raw trade request to the trade server (expert path, live execution).
599
+
600
+ Passes the request JSON directly to MT5 ``order_send``. This is the
601
+ low-level expert path — it places real trades on the connected account
602
+ with no additional validation beyond what MT5 itself performs. Use
603
+ ``order-check`` first to validate funds sufficiency. Prefer
604
+ ``close-positions`` for closing open positions. ``--yes`` is required.
594
605
 
595
606
  Raises:
596
607
  typer.BadParameter: If --yes is not provided.
@@ -628,7 +639,7 @@ def _execution_results_to_df(results: list[OrderExecutionResult]) -> pd.DataFram
628
639
  return pd.DataFrame(rows)
629
640
 
630
641
 
631
- @app.command()
642
+ @app.command(rich_help_panel="Execution")
632
643
  def close_positions(
633
644
  ctx: typer.Context,
634
645
  symbol: Annotated[
@@ -691,7 +702,7 @@ def close_positions(
691
702
  _execute_export(ctx, lambda: df)
692
703
 
693
704
 
694
- @app.command()
705
+ @app.command(rich_help_panel="Collection")
695
706
  def collect_history(
696
707
  ctx: typer.Context,
697
708
  symbol: Annotated[
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mt5cli"
3
- version = "1.0.0"
3
+ version = "1.0.1"
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"}]
@@ -740,6 +740,66 @@ class TestCommands:
740
740
  assert "must be a JSON object" in normalize_cli_output(result.output)
741
741
 
742
742
 
743
+ # ---------------------------------------------------------------------------
744
+ # Help text / scope tests
745
+ # ---------------------------------------------------------------------------
746
+
747
+
748
+ class TestHelpText:
749
+ """Tests verifying CLI help text matches the documented scope."""
750
+
751
+ def test_top_level_help_mentions_execution(self) -> None:
752
+ """Top-level help must describe execution utilities, not export only."""
753
+ result = runner.invoke(app, ["--help"])
754
+ assert result.exit_code == 0
755
+ output = normalize_cli_output(result.output)
756
+ assert "execution" in output.lower()
757
+
758
+ def test_top_level_help_has_execution_panel(self) -> None:
759
+ """Top-level help must show an Execution command group."""
760
+ result = runner.invoke(app, ["--help"])
761
+ assert result.exit_code == 0
762
+ assert "Execution" in result.output
763
+
764
+ def test_top_level_help_has_data_export_panel(self) -> None:
765
+ """Top-level help must show a Data / Export command group."""
766
+ result = runner.invoke(app, ["--help"])
767
+ assert result.exit_code == 0
768
+ assert "Data / Export" in result.output
769
+
770
+ def test_order_send_help_mentions_expert_and_raw(self) -> None:
771
+ """order-send help must communicate it is the expert raw-request path."""
772
+ result2 = runner.invoke(
773
+ app,
774
+ ["-o", "out.csv", "order-send", "--help"],
775
+ )
776
+ assert result2.exit_code == 0
777
+ output = normalize_cli_output(result2.output)
778
+ assert "raw" in output.lower()
779
+ assert "expert" in output.lower()
780
+
781
+ def test_order_send_help_mentions_live_execution(self) -> None:
782
+ """order-send help must warn about live execution."""
783
+ result = runner.invoke(
784
+ app,
785
+ ["-o", "out.csv", "order-send", "--help"],
786
+ )
787
+ assert result.exit_code == 0
788
+ output = normalize_cli_output(result.output)
789
+ assert "live" in output.lower()
790
+
791
+ def test_close_positions_help_mentions_dry_run_and_yes(self) -> None:
792
+ """close-positions help must document both safety gates."""
793
+ result = runner.invoke(
794
+ app,
795
+ ["-o", "out.csv", "close-positions", "--help"],
796
+ )
797
+ assert result.exit_code == 0
798
+ output = normalize_cli_output(result.output)
799
+ assert "--dry-run" in output
800
+ assert "--yes" in output
801
+
802
+
743
803
  # ---------------------------------------------------------------------------
744
804
  # close-positions command
745
805
  # ---------------------------------------------------------------------------
@@ -487,7 +487,7 @@ wheels = [
487
487
 
488
488
  [[package]]
489
489
  name = "mt5cli"
490
- version = "1.0.0"
490
+ version = "1.0.1"
491
491
  source = { editable = "." }
492
492
  dependencies = [
493
493
  { name = "click" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes