pdmt5 0.1.9__tar.gz → 0.2.0__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 (33) hide show
  1. {pdmt5-0.1.9 → pdmt5-0.2.0}/.github/workflows/ci.yml +4 -13
  2. pdmt5-0.2.0/.github/workflows/pr-agent.yml +34 -0
  3. {pdmt5-0.1.9 → pdmt5-0.2.0}/PKG-INFO +2 -3
  4. {pdmt5-0.1.9 → pdmt5-0.2.0}/README.md +1 -2
  5. {pdmt5-0.1.9 → pdmt5-0.2.0}/pdmt5/trading.py +70 -15
  6. {pdmt5-0.1.9 → pdmt5-0.2.0}/pyproject.toml +1 -1
  7. {pdmt5-0.1.9 → pdmt5-0.2.0}/uv.lock +2 -2
  8. {pdmt5-0.1.9 → pdmt5-0.2.0}/.claude/settings.json +0 -0
  9. {pdmt5-0.1.9 → pdmt5-0.2.0}/.github/FUNDING.yml +0 -0
  10. {pdmt5-0.1.9 → pdmt5-0.2.0}/.github/copilot-instructions.md +0 -0
  11. {pdmt5-0.1.9 → pdmt5-0.2.0}/.github/dependabot.yml +0 -0
  12. {pdmt5-0.1.9 → pdmt5-0.2.0}/.github/workflows/claude.yml +0 -0
  13. {pdmt5-0.1.9 → pdmt5-0.2.0}/.gitignore +0 -0
  14. {pdmt5-0.1.9 → pdmt5-0.2.0}/CLAUDE.md +0 -0
  15. {pdmt5-0.1.9 → pdmt5-0.2.0}/LICENSE +0 -0
  16. {pdmt5-0.1.9 → pdmt5-0.2.0}/docs/api/dataframe.md +0 -0
  17. {pdmt5-0.1.9 → pdmt5-0.2.0}/docs/api/index.md +0 -0
  18. {pdmt5-0.1.9 → pdmt5-0.2.0}/docs/api/mt5.md +0 -0
  19. {pdmt5-0.1.9 → pdmt5-0.2.0}/docs/api/trading.md +0 -0
  20. {pdmt5-0.1.9 → pdmt5-0.2.0}/docs/api/utils.md +0 -0
  21. {pdmt5-0.1.9 → pdmt5-0.2.0}/docs/index.md +0 -0
  22. {pdmt5-0.1.9 → pdmt5-0.2.0}/mkdocs.yml +0 -0
  23. {pdmt5-0.1.9 → pdmt5-0.2.0}/pdmt5/__init__.py +0 -0
  24. {pdmt5-0.1.9 → pdmt5-0.2.0}/pdmt5/dataframe.py +0 -0
  25. {pdmt5-0.1.9 → pdmt5-0.2.0}/pdmt5/mt5.py +0 -0
  26. {pdmt5-0.1.9 → pdmt5-0.2.0}/pdmt5/utils.py +0 -0
  27. {pdmt5-0.1.9 → pdmt5-0.2.0}/renovate.json +0 -0
  28. {pdmt5-0.1.9 → pdmt5-0.2.0}/tests/__init__.py +0 -0
  29. {pdmt5-0.1.9 → pdmt5-0.2.0}/tests/test_dataframe.py +0 -0
  30. {pdmt5-0.1.9 → pdmt5-0.2.0}/tests/test_init.py +0 -0
  31. {pdmt5-0.1.9 → pdmt5-0.2.0}/tests/test_mt5.py +0 -0
  32. {pdmt5-0.1.9 → pdmt5-0.2.0}/tests/test_trading.py +0 -0
  33. {pdmt5-0.1.9 → pdmt5-0.2.0}/tests/test_utils.py +0 -0
@@ -30,25 +30,16 @@ jobs:
30
30
  uses: dceoy/gh-actions-for-devops/.github/workflows/python-package-lint-and-scan.yml@main
31
31
  with:
32
32
  package-path: .
33
- python-version: 3.x
34
33
  runs-on: windows-latest
35
34
  python-test:
36
35
  if: >
37
36
  github.event_name == 'push'
38
37
  || github.event_name == 'pull_request'
39
38
  || (github.event_name == 'workflow_dispatch' && inputs.workflow == 'lint-and-test')
40
- runs-on: windows-latest
41
- steps:
42
- - name: Checkout repository
43
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
44
- - name: Set up uv
45
- uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
46
- - name: Install the package
47
- run: >
48
- uv sync
49
- - name: Run unit tests with pytest
50
- run: >
51
- uv run pytest
39
+ uses: dceoy/gh-actions-for-devops/.github/workflows/python-package-test.yml@main
40
+ with:
41
+ package-path: .
42
+ runs-on: windows-latest
52
43
  dependabot-auto-merge:
53
44
  if: >
54
45
  github.event_name == 'pull_request' && github.actor == 'dependabot[bot]'
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: PR-agent
3
+ on:
4
+ pull_request:
5
+ types:
6
+ - opened
7
+ - reopened
8
+ - ready_for_review
9
+ issue_comment:
10
+ types:
11
+ - created
12
+ - edited
13
+ - deleted
14
+ jobs:
15
+ pr-agent:
16
+ if: >
17
+ github.event.sender.type != 'Bot'
18
+ && (
19
+ github.event_name == 'pull_request'
20
+ || github.event_name == 'issue_comment'
21
+ )
22
+ uses: dceoy/gh-actions-for-devops/.github/workflows/pr-agent.yml@main
23
+ permissions:
24
+ contents: write
25
+ pull-requests: write
26
+ issues: write
27
+ id-token: write
28
+ with:
29
+ auto-describe: true
30
+ auto-review: true
31
+ auto-improve: true
32
+ secrets:
33
+ GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
34
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pdmt5
3
- Version: 0.1.9
3
+ Version: 0.2.0
4
4
  Summary: Pandas-based data handler for MetaTrader 5
5
5
  Project-URL: Repository, https://github.com/dceoy/pdmt5.git
6
6
  Author-email: dceoy <dceoy@users.noreply.github.com>
@@ -56,8 +56,7 @@ Pandas-based data handler for MetaTrader 5
56
56
  ### Using pip
57
57
 
58
58
  ```bash
59
- pip install -U pdmt5
60
- pip install -U MetaTrader5
59
+ pip install -U pdmt5 MetaTrader5
61
60
  ```
62
61
 
63
62
  ### Using uv
@@ -33,8 +33,7 @@ Pandas-based data handler for MetaTrader 5
33
33
  ### Using pip
34
34
 
35
35
  ```bash
36
- pip install -U pdmt5
37
- pip install -U MetaTrader5
36
+ pip install -U pdmt5 MetaTrader5
38
37
  ```
39
38
 
40
39
  ### Using uv
@@ -243,6 +243,7 @@ class Mt5TradingClient(Mt5DataClient):
243
243
  Returns:
244
244
  Dictionary with operation result.
245
245
  """
246
+ self.logger.info("Placing market order: %s %s %s", order_side, volume, symbol)
246
247
  return self._send_or_check_order(
247
248
  request={
248
249
  "action": self.mt5.TRADE_ACTION_DEAL,
@@ -315,6 +316,13 @@ class Mt5TradingClient(Mt5DataClient):
315
316
  if sl != p["sl"] or tp != p["tp"]
316
317
  ]
317
318
  if order_requests:
319
+ self.logger.info(
320
+ "Updating SL/TP for %d positions for %s: %s/%s",
321
+ len(order_requests),
322
+ symbol,
323
+ sl,
324
+ tp,
325
+ )
318
326
  return [
319
327
  self._send_or_check_order(request=r, dry_run=dry_run)
320
328
  for r in order_requests
@@ -354,15 +362,22 @@ class Mt5TradingClient(Mt5DataClient):
354
362
  else symbol_info_tick["ask"]
355
363
  ),
356
364
  )
365
+ result = {"volume": symbol_info["volume_min"], "margin": margin}
357
366
  if margin:
358
- return {"volume": symbol_info["volume_min"], "margin": margin}
367
+ self.logger.info(
368
+ "Calculated minimum %s order margin for %s: %s",
369
+ order_side,
370
+ symbol,
371
+ result,
372
+ )
359
373
  else:
360
374
  self.logger.warning(
361
- "No margin available for symbol: %s with order side: %s",
362
- symbol,
375
+ "Calculated minimum order margin to %s %s: %s",
363
376
  order_side,
377
+ symbol,
378
+ result,
364
379
  )
365
- return {"volume": symbol_info["volume_min"], "margin": 0.0}
380
+ return result
366
381
 
367
382
  def calculate_volume_by_margin(
368
383
  self,
@@ -385,12 +400,19 @@ class Mt5TradingClient(Mt5DataClient):
385
400
  order_side=order_side,
386
401
  )
387
402
  if min_order_margin_dict["margin"]:
388
- return (
403
+ result = (
389
404
  floor(margin / min_order_margin_dict["margin"])
390
405
  * min_order_margin_dict["volume"]
391
406
  )
392
407
  else:
393
- return 0.0
408
+ result = 0.0
409
+ self.logger.info(
410
+ "Calculated volume by margin to %s %s: %s",
411
+ order_side,
412
+ symbol,
413
+ result,
414
+ )
415
+ return result
394
416
 
395
417
  def calculate_spread_ratio(
396
418
  self,
@@ -405,11 +427,13 @@ class Mt5TradingClient(Mt5DataClient):
405
427
  Spread ratio as a float.
406
428
  """
407
429
  symbol_info_tick = self.symbol_info_tick_as_dict(symbol=symbol)
408
- return (
430
+ result = (
409
431
  (symbol_info_tick["ask"] - symbol_info_tick["bid"])
410
432
  / (symbol_info_tick["ask"] + symbol_info_tick["bid"])
411
433
  * 2
412
434
  )
435
+ self.logger.info("Calculated spread ratio for %s: %s", symbol, result)
436
+ return result
413
437
 
414
438
  def fetch_latest_rates_as_df(
415
439
  self,
@@ -440,13 +464,20 @@ class Mt5TradingClient(Mt5DataClient):
440
464
  )
441
465
  raise Mt5TradingError(error_message) from e
442
466
  else:
443
- return self.copy_rates_from_pos_as_df(
467
+ result = self.copy_rates_from_pos_as_df(
444
468
  symbol=symbol,
445
469
  timeframe=timeframe,
446
470
  start_pos=0,
447
471
  count=count,
448
472
  index_keys=index_keys,
449
473
  )
474
+ self.logger.info(
475
+ "Fetched latest %s rates for %s: %d rows",
476
+ granularity,
477
+ symbol,
478
+ result.shape[0],
479
+ )
480
+ return result
450
481
 
451
482
  def fetch_latest_ticks_as_df(
452
483
  self,
@@ -465,13 +496,19 @@ class Mt5TradingClient(Mt5DataClient):
465
496
  pd.DataFrame: Tick data with time index.
466
497
  """
467
498
  last_tick_time = self.symbol_info_tick_as_dict(symbol=symbol)["time"]
468
- return self.copy_ticks_range_as_df(
499
+ result = self.copy_ticks_range_as_df(
469
500
  symbol=symbol,
470
501
  date_from=(last_tick_time - timedelta(seconds=seconds)),
471
502
  date_to=(last_tick_time + timedelta(seconds=seconds)),
472
503
  flags=self.mt5.COPY_TICKS_ALL,
473
504
  index_keys=index_keys,
474
505
  )
506
+ self.logger.info(
507
+ "Fetched latest ticks for %s: %d rows",
508
+ symbol,
509
+ result.shape[0],
510
+ )
511
+ return result
475
512
 
476
513
  def collect_entry_deals_as_df(
477
514
  self,
@@ -497,14 +534,20 @@ class Mt5TradingClient(Mt5DataClient):
497
534
  index_keys=index_keys,
498
535
  )
499
536
  if deals_df.empty:
500
- return deals_df
537
+ result = deals_df
501
538
  else:
502
- return deals_df.pipe(
539
+ result = deals_df.pipe(
503
540
  lambda d: d[
504
541
  d["entry"]
505
542
  & d["type"].isin({self.mt5.DEAL_TYPE_BUY, self.mt5.DEAL_TYPE_SELL})
506
543
  ]
507
544
  )
545
+ self.logger.info(
546
+ "Collected entry deals for %s: %d rows",
547
+ symbol,
548
+ result.shape[0],
549
+ )
550
+ return result
508
551
 
509
552
  def fetch_positions_with_metrics_as_df(
510
553
  self,
@@ -520,7 +563,7 @@ class Mt5TradingClient(Mt5DataClient):
520
563
  """
521
564
  positions_df = self.positions_get_as_df(symbol=symbol)
522
565
  if positions_df.empty:
523
- return positions_df
566
+ result = positions_df
524
567
  else:
525
568
  symbol_info_tick = self.symbol_info_tick_as_dict(symbol=symbol)
526
569
  ask_margin = self.order_calc_margin(
@@ -535,7 +578,7 @@ class Mt5TradingClient(Mt5DataClient):
535
578
  volume=1,
536
579
  price=symbol_info_tick["bid"],
537
580
  )
538
- return (
581
+ result = (
539
582
  positions_df.assign(
540
583
  elapsed_seconds=lambda d: (
541
584
  symbol_info_tick["time"] - d["time"]
@@ -566,6 +609,12 @@ class Mt5TradingClient(Mt5DataClient):
566
609
  )
567
610
  .drop(columns=["buy_i", "sell_i", "sign", "underlier_increase_ratio"])
568
611
  )
612
+ self.logger.info(
613
+ "Fetched positions with metrics for %s: %d rows",
614
+ symbol,
615
+ result.shape[0],
616
+ )
617
+ return result
569
618
 
570
619
  def calculate_new_position_margin_ratio(
571
620
  self,
@@ -585,7 +634,7 @@ class Mt5TradingClient(Mt5DataClient):
585
634
  """
586
635
  account_info = self.account_info_as_dict()
587
636
  if not account_info["equity"]:
588
- return 0.0
637
+ result = 0.0
589
638
  else:
590
639
  positions_df = self.fetch_positions_with_metrics_as_df(symbol=symbol)
591
640
  current_signed_margin = (
@@ -610,6 +659,12 @@ class Mt5TradingClient(Mt5DataClient):
610
659
  )
611
660
  else:
612
661
  new_signed_margin = 0
613
- return abs(
662
+ result = abs(
614
663
  (new_signed_margin + current_signed_margin) / account_info["equity"]
615
664
  )
665
+ self.logger.info(
666
+ "Calculated new position margin ratio for %s: %s",
667
+ symbol,
668
+ result,
669
+ )
670
+ return result
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pdmt5"
3
- version = "0.1.9"
3
+ version = "0.2.0"
4
4
  description = "Pandas-based data handler for MetaTrader 5"
5
5
  authors = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
6
6
  maintainers = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
@@ -1,5 +1,5 @@
1
1
  version = 1
2
- revision = 2
2
+ revision = 3
3
3
  requires-python = ">=3.11"
4
4
  resolution-markers = [
5
5
  "python_full_version >= '3.12'",
@@ -613,7 +613,7 @@ wheels = [
613
613
 
614
614
  [[package]]
615
615
  name = "pdmt5"
616
- version = "0.1.9"
616
+ version = "0.2.0"
617
617
  source = { editable = "." }
618
618
  dependencies = [
619
619
  { name = "metatrader5", marker = "sys_platform == 'win32'" },
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