mt5cli 1.0.2__tar.gz → 1.0.3__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.2 → mt5cli-1.0.3}/PKG-INFO +28 -28
  2. {mt5cli-1.0.2 → mt5cli-1.0.3}/README.md +27 -27
  3. {mt5cli-1.0.2 → mt5cli-1.0.3}/mt5cli/trading.py +10 -2
  4. {mt5cli-1.0.2 → mt5cli-1.0.3}/pyproject.toml +1 -1
  5. {mt5cli-1.0.2 → mt5cli-1.0.3}/tests/test_trading.py +88 -0
  6. {mt5cli-1.0.2 → mt5cli-1.0.3}/uv.lock +1 -1
  7. {mt5cli-1.0.2 → mt5cli-1.0.3}/.agents/skills/local-qa/SKILL.md +0 -0
  8. {mt5cli-1.0.2 → mt5cli-1.0.3}/.agents/skills/local-qa/scripts/qa.sh +0 -0
  9. {mt5cli-1.0.2 → mt5cli-1.0.3}/.agents/skills/mt5cli/SKILL.md +0 -0
  10. {mt5cli-1.0.2 → mt5cli-1.0.3}/.agents/skills/pr-feedback-triage/SKILL.md +0 -0
  11. {mt5cli-1.0.2 → mt5cli-1.0.3}/.claude/agents/codex.md +0 -0
  12. {mt5cli-1.0.2 → mt5cli-1.0.3}/.claude/settings.json +0 -0
  13. {mt5cli-1.0.2 → mt5cli-1.0.3}/.github/FUNDING.yml +0 -0
  14. {mt5cli-1.0.2 → mt5cli-1.0.3}/.github/dependabot.yml +0 -0
  15. {mt5cli-1.0.2 → mt5cli-1.0.3}/.github/renovate.json +0 -0
  16. {mt5cli-1.0.2 → mt5cli-1.0.3}/.github/workflows/ci.yml +0 -0
  17. {mt5cli-1.0.2 → mt5cli-1.0.3}/.github/workflows/claude.yml +0 -0
  18. {mt5cli-1.0.2 → mt5cli-1.0.3}/.github/workflows/release.yml +0 -0
  19. {mt5cli-1.0.2 → mt5cli-1.0.3}/.gitignore +0 -0
  20. {mt5cli-1.0.2 → mt5cli-1.0.3}/AGENTS.md +0 -0
  21. {mt5cli-1.0.2 → mt5cli-1.0.3}/CLAUDE.md +0 -0
  22. {mt5cli-1.0.2 → mt5cli-1.0.3}/LICENSE +0 -0
  23. {mt5cli-1.0.2 → mt5cli-1.0.3}/docs/api/cli.md +0 -0
  24. {mt5cli-1.0.2 → mt5cli-1.0.3}/docs/api/client.md +0 -0
  25. {mt5cli-1.0.2 → mt5cli-1.0.3}/docs/api/converters.md +0 -0
  26. {mt5cli-1.0.2 → mt5cli-1.0.3}/docs/api/exceptions.md +0 -0
  27. {mt5cli-1.0.2 → mt5cli-1.0.3}/docs/api/history.md +0 -0
  28. {mt5cli-1.0.2 → mt5cli-1.0.3}/docs/api/index.md +0 -0
  29. {mt5cli-1.0.2 → mt5cli-1.0.3}/docs/api/public-contract.md +0 -0
  30. {mt5cli-1.0.2 → mt5cli-1.0.3}/docs/api/schemas.md +0 -0
  31. {mt5cli-1.0.2 → mt5cli-1.0.3}/docs/api/sdk.md +0 -0
  32. {mt5cli-1.0.2 → mt5cli-1.0.3}/docs/api/trading.md +0 -0
  33. {mt5cli-1.0.2 → mt5cli-1.0.3}/docs/api/utils.md +0 -0
  34. {mt5cli-1.0.2 → mt5cli-1.0.3}/docs/index.md +0 -0
  35. {mt5cli-1.0.2 → mt5cli-1.0.3}/mkdocs.yml +0 -0
  36. {mt5cli-1.0.2 → mt5cli-1.0.3}/mt5cli/__init__.py +0 -0
  37. {mt5cli-1.0.2 → mt5cli-1.0.3}/mt5cli/__main__.py +0 -0
  38. {mt5cli-1.0.2 → mt5cli-1.0.3}/mt5cli/cli.py +0 -0
  39. {mt5cli-1.0.2 → mt5cli-1.0.3}/mt5cli/client.py +0 -0
  40. {mt5cli-1.0.2 → mt5cli-1.0.3}/mt5cli/contract.py +0 -0
  41. {mt5cli-1.0.2 → mt5cli-1.0.3}/mt5cli/converters.py +0 -0
  42. {mt5cli-1.0.2 → mt5cli-1.0.3}/mt5cli/exceptions.py +0 -0
  43. {mt5cli-1.0.2 → mt5cli-1.0.3}/mt5cli/history.py +0 -0
  44. {mt5cli-1.0.2 → mt5cli-1.0.3}/mt5cli/retry.py +0 -0
  45. {mt5cli-1.0.2 → mt5cli-1.0.3}/mt5cli/schemas.py +0 -0
  46. {mt5cli-1.0.2 → mt5cli-1.0.3}/mt5cli/sdk.py +0 -0
  47. {mt5cli-1.0.2 → mt5cli-1.0.3}/mt5cli/utils.py +0 -0
  48. {mt5cli-1.0.2 → mt5cli-1.0.3}/tests/__init__.py +0 -0
  49. {mt5cli-1.0.2 → mt5cli-1.0.3}/tests/conftest.py +0 -0
  50. {mt5cli-1.0.2 → mt5cli-1.0.3}/tests/test_cli.py +0 -0
  51. {mt5cli-1.0.2 → mt5cli-1.0.3}/tests/test_contracts.py +0 -0
  52. {mt5cli-1.0.2 → mt5cli-1.0.3}/tests/test_history.py +0 -0
  53. {mt5cli-1.0.2 → mt5cli-1.0.3}/tests/test_sdk.py +0 -0
  54. {mt5cli-1.0.2 → mt5cli-1.0.3}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mt5cli
3
- Version: 1.0.2
3
+ Version: 1.0.3
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>
@@ -182,33 +182,33 @@ python -m mt5cli -o account.csv account-info
182
182
 
183
183
  ## Commands
184
184
 
185
- | Command | Description |
186
- | ---------------------- | ------------------------------------------------------------------------------------------------------------ |
187
- | `rates-from` | Export rates from a start date |
188
- | `rates-from-pos` | Export rates from a start position |
189
- | `latest-rates` | Export latest rates from a start position |
190
- | `rates-range` | Export rates for a date range |
191
- | `ticks-from` | Export ticks from a start date |
192
- | `ticks-range` | Export ticks for a date range |
193
- | `ticks-recent` | Export ticks from a recent trailing window |
194
- | `account-info` | Export account information |
195
- | `terminal-info` | Export terminal information |
196
- | `version` | Export MetaTrader 5 version information |
197
- | `last-error` | Export the last error information |
198
- | `symbols` | Export symbol list |
199
- | `symbol-info` | Export symbol details |
200
- | `symbol-info-tick` | Export the last tick for a symbol |
201
- | `minimum-margins` | Export minimum-volume buy and sell margin requirements |
202
- | `market-book` | Export market depth (order book) |
203
- | `orders` | Export active orders |
204
- | `positions` | Export open positions |
205
- | `history-orders` | Export historical orders |
206
- | `history-deals` | Export historical deals |
207
- | `recent-history-deals` | Export historical deals from a recent trailing window |
208
- | `mt5-summary` | Export terminal/account status summary |
209
- | `order-check` | Check funds sufficiency for a trade request |
210
- | `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path) |
211
- | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available) |
185
+ | Command | Description |
186
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
187
+ | `rates-from` | Export rates from a start date |
188
+ | `rates-from-pos` | Export rates from a start position |
189
+ | `latest-rates` | Export latest rates from a start position |
190
+ | `rates-range` | Export rates for a date range |
191
+ | `ticks-from` | Export ticks from a start date |
192
+ | `ticks-range` | Export ticks for a date range |
193
+ | `ticks-recent` | Export ticks from a recent trailing window |
194
+ | `account-info` | Export account information |
195
+ | `terminal-info` | Export terminal information |
196
+ | `version` | Export MetaTrader 5 version information |
197
+ | `last-error` | Export the last error information |
198
+ | `symbols` | Export symbol list |
199
+ | `symbol-info` | Export symbol details |
200
+ | `symbol-info-tick` | Export the last tick for a symbol |
201
+ | `minimum-margins` | Export minimum-volume buy and sell margin requirements |
202
+ | `market-book` | Export market depth (order book) |
203
+ | `orders` | Export active orders |
204
+ | `positions` | Export open positions |
205
+ | `history-orders` | Export historical orders |
206
+ | `history-deals` | Export historical deals |
207
+ | `recent-history-deals` | Export historical deals from a recent trailing window |
208
+ | `mt5-summary` | Export terminal/account status summary |
209
+ | `order-check` | Check funds sufficiency for a trade request |
210
+ | `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path) |
211
+ | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available) |
212
212
  | `collect-history` | Collect rates, history-orders, and history-deals for one or more symbols into a single SQLite database (ticks opt-in via `--dataset ticks`) |
213
213
 
214
214
  Use `order-check` to validate a request payload before running `order-send --yes`.
@@ -157,33 +157,33 @@ python -m mt5cli -o account.csv account-info
157
157
 
158
158
  ## Commands
159
159
 
160
- | Command | Description |
161
- | ---------------------- | ------------------------------------------------------------------------------------------------------------ |
162
- | `rates-from` | Export rates from a start date |
163
- | `rates-from-pos` | Export rates from a start position |
164
- | `latest-rates` | Export latest rates from a start position |
165
- | `rates-range` | Export rates for a date range |
166
- | `ticks-from` | Export ticks from a start date |
167
- | `ticks-range` | Export ticks for a date range |
168
- | `ticks-recent` | Export ticks from a recent trailing window |
169
- | `account-info` | Export account information |
170
- | `terminal-info` | Export terminal information |
171
- | `version` | Export MetaTrader 5 version information |
172
- | `last-error` | Export the last error information |
173
- | `symbols` | Export symbol list |
174
- | `symbol-info` | Export symbol details |
175
- | `symbol-info-tick` | Export the last tick for a symbol |
176
- | `minimum-margins` | Export minimum-volume buy and sell margin requirements |
177
- | `market-book` | Export market depth (order book) |
178
- | `orders` | Export active orders |
179
- | `positions` | Export open positions |
180
- | `history-orders` | Export historical orders |
181
- | `history-deals` | Export historical deals |
182
- | `recent-history-deals` | Export historical deals from a recent trailing window |
183
- | `mt5-summary` | Export terminal/account status summary |
184
- | `order-check` | Check funds sufficiency for a trade request |
185
- | `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path) |
186
- | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available) |
160
+ | Command | Description |
161
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
162
+ | `rates-from` | Export rates from a start date |
163
+ | `rates-from-pos` | Export rates from a start position |
164
+ | `latest-rates` | Export latest rates from a start position |
165
+ | `rates-range` | Export rates for a date range |
166
+ | `ticks-from` | Export ticks from a start date |
167
+ | `ticks-range` | Export ticks for a date range |
168
+ | `ticks-recent` | Export ticks from a recent trailing window |
169
+ | `account-info` | Export account information |
170
+ | `terminal-info` | Export terminal information |
171
+ | `version` | Export MetaTrader 5 version information |
172
+ | `last-error` | Export the last error information |
173
+ | `symbols` | Export symbol list |
174
+ | `symbol-info` | Export symbol details |
175
+ | `symbol-info-tick` | Export the last tick for a symbol |
176
+ | `minimum-margins` | Export minimum-volume buy and sell margin requirements |
177
+ | `market-book` | Export market depth (order book) |
178
+ | `orders` | Export active orders |
179
+ | `positions` | Export open positions |
180
+ | `history-orders` | Export historical orders |
181
+ | `history-deals` | Export historical deals |
182
+ | `recent-history-deals` | Export historical deals from a recent trailing window |
183
+ | `mt5-summary` | Export terminal/account status summary |
184
+ | `order-check` | Check funds sufficiency for a trade request |
185
+ | `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path) |
186
+ | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available) |
187
187
  | `collect-history` | Collect rates, history-orders, and history-deals for one or more symbols into a single SQLite database (ticks opt-in via `--dataset ticks`) |
188
188
 
189
189
  Use `order-check` to validate a request payload before running `order-send --yes`.
@@ -15,6 +15,7 @@ from .exceptions import Mt5OperationError
15
15
  from .history import drop_forming_rate_bar
16
16
  from .sdk import build_config
17
17
  from .utils import coerce_login as _coerce_login
18
+ from .utils import parse_timeframe
18
19
 
19
20
  if TYPE_CHECKING:
20
21
  from collections.abc import Iterator, Mapping, Sequence
@@ -1624,11 +1625,18 @@ def fetch_latest_closed_rates_for_trading_client(
1624
1625
  if count <= 0:
1625
1626
  msg = "count must be positive."
1626
1627
  raise ValueError(msg)
1628
+ timeframe = parse_timeframe(granularity)
1627
1629
  fetch_method = getattr(client, "fetch_latest_rates_as_df", None)
1628
- if not callable(fetch_method):
1630
+ copy_method = getattr(client, "copy_rates_from_pos_as_df", None)
1631
+ if callable(fetch_method):
1632
+ fetched = fetch_method(symbol, granularity, count + 1)
1633
+ elif callable(copy_method):
1634
+ fetched = copy_method(
1635
+ symbol=symbol, timeframe=timeframe, start_pos=0, count=count + 1
1636
+ )
1637
+ else:
1629
1638
  msg = "MT5 trading client cannot fetch rate data."
1630
1639
  raise Mt5OperationError(msg)
1631
- fetched = fetch_method(symbol, granularity, count + 1)
1632
1640
  if not isinstance(fetched, pd.DataFrame):
1633
1641
  msg = (
1634
1642
  f"Malformed rate data for {symbol!r} at granularity {granularity!r}: "
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mt5cli"
3
- version = "1.0.2"
3
+ version = "1.0.3"
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"}]
@@ -3198,6 +3198,71 @@ class TestFetchLatestClosedRatesForTradingClient:
3198
3198
  count=2,
3199
3199
  )
3200
3200
 
3201
+ def test_copy_rates_from_pos_fallback_drops_forming_bar(self) -> None:
3202
+ """Regression: client with only copy_rates_from_pos_as_df works end-to-end."""
3203
+ client = MagicMock(spec=["copy_rates_from_pos_as_df"])
3204
+ client.copy_rates_from_pos_as_df.return_value = pd.DataFrame(
3205
+ {
3206
+ "time": [1700000000, 1700000060, 1700000120],
3207
+ "close": [1.0, 1.1, 1.2],
3208
+ },
3209
+ )
3210
+
3211
+ result = fetch_latest_closed_rates_for_trading_client(
3212
+ client,
3213
+ symbol="EURUSD",
3214
+ granularity="M1",
3215
+ count=2,
3216
+ )
3217
+
3218
+ client.copy_rates_from_pos_as_df.assert_called_once_with(
3219
+ symbol="EURUSD", timeframe=1, start_pos=0, count=3
3220
+ )
3221
+ assert list(result["close"]) == [1.0, 1.1]
3222
+ assert list(result["time"]) == [1700000000, 1700000060]
3223
+
3224
+ def test_copy_rates_from_pos_fallback_resolves_granularity(self) -> None:
3225
+ """Fallback path resolves granularity string to integer timeframe."""
3226
+ client = MagicMock(spec=["copy_rates_from_pos_as_df"])
3227
+ client.copy_rates_from_pos_as_df.return_value = pd.DataFrame(
3228
+ {"time": [1, 2, 3, 4], "close": [1.0, 1.1, 1.2, 1.3]},
3229
+ )
3230
+
3231
+ fetch_latest_closed_rates_for_trading_client(
3232
+ client, symbol="USDJPY", granularity="H1", count=3
3233
+ )
3234
+
3235
+ call_kwargs = client.copy_rates_from_pos_as_df.call_args.kwargs
3236
+ assert call_kwargs["symbol"] == "USDJPY"
3237
+ assert call_kwargs["timeframe"] == 16385
3238
+ assert call_kwargs["start_pos"] == 0
3239
+ assert call_kwargs["count"] == 4
3240
+
3241
+ def test_copy_rates_from_pos_fallback_returns_count_closed_rows(self) -> None:
3242
+ """Fallback path trims to exactly count closed rows after forming-bar drop."""
3243
+ client = MagicMock(spec=["copy_rates_from_pos_as_df"])
3244
+ client.copy_rates_from_pos_as_df.return_value = pd.DataFrame(
3245
+ {"time": list(range(6)), "close": [float(i) for i in range(6)]},
3246
+ )
3247
+
3248
+ result = fetch_latest_closed_rates_for_trading_client(
3249
+ client, symbol="EURUSD", granularity="M1", count=4
3250
+ )
3251
+
3252
+ assert len(result) == 4
3253
+ assert list(result["close"]) == [1.0, 2.0, 3.0, 4.0]
3254
+
3255
+ def test_copy_rates_from_pos_fallback_raises_on_invalid_granularity(self) -> None:
3256
+ """Invalid granularity raises ValueError before calling the fallback method."""
3257
+ client = MagicMock(spec=["copy_rates_from_pos_as_df"])
3258
+
3259
+ with pytest.raises(ValueError, match="Invalid timeframe"):
3260
+ fetch_latest_closed_rates_for_trading_client(
3261
+ client, symbol="EURUSD", granularity="BADGRAN", count=1
3262
+ )
3263
+
3264
+ client.copy_rates_from_pos_as_df.assert_not_called()
3265
+
3201
3266
  def test_raises_when_trading_client_cannot_fetch_rates(self) -> None:
3202
3267
  """Test missing rate-fetch methods raise Mt5TradingError."""
3203
3268
  client = MagicMock(spec=[])
@@ -3528,6 +3593,29 @@ class TestFetchLatestClosedRatesIndexed:
3528
3593
  assert "close" in result.columns
3529
3594
  assert isinstance(result.index, pd.DatetimeIndex)
3530
3595
 
3596
+ def test_copy_rates_from_pos_fallback_produces_utc_datetime_index(self) -> None:
3597
+ """Fallback path via copy_rates_from_pos_as_df produces a UTC DatetimeIndex."""
3598
+ client = MagicMock(spec=["copy_rates_from_pos_as_df"])
3599
+ client.copy_rates_from_pos_as_df.return_value = pd.DataFrame(
3600
+ {
3601
+ "time": [1700000000, 1700003600, 1700007200],
3602
+ "close": [1.1, 1.2, 1.3],
3603
+ },
3604
+ )
3605
+
3606
+ result = fetch_latest_closed_rates_indexed(
3607
+ client,
3608
+ symbol="EURUSD",
3609
+ granularity="M1",
3610
+ count=2,
3611
+ )
3612
+
3613
+ assert isinstance(result.index, pd.DatetimeIndex)
3614
+ assert result.index.name == "time"
3615
+ assert str(result.index.tz) == "UTC"
3616
+ assert "time" not in result.columns
3617
+ assert list(result["close"]) == [1.1, 1.2]
3618
+
3531
3619
 
3532
3620
  class TestExtractTickPrice:
3533
3621
  """Tests for the public extract_tick_price helper."""
@@ -487,7 +487,7 @@ wheels = [
487
487
 
488
488
  [[package]]
489
489
  name = "mt5cli"
490
- version = "1.0.2"
490
+ version = "1.0.3"
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