mtcli 3.8.0.dev10__tar.gz → 3.8.0.dev11__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 (89) hide show
  1. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/PKG-INFO +1 -1
  2. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/marketdata/tick_engine.py +46 -17
  3. mtcli-3.8.0.dev11/mtcli/marketdata/tick_writer.py +98 -0
  4. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/pyproject.toml +1 -1
  5. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/LICENSE +0 -0
  6. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/README.md +0 -0
  7. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/__init__.py +0 -0
  8. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/__main__.py +0 -0
  9. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/cli.py +0 -0
  10. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/cli_dev.py +0 -0
  11. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/commands/__init__.py +0 -0
  12. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/commands/bars.py +0 -0
  13. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/commands/conf.py +0 -0
  14. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/commands/doctor.py +0 -0
  15. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/commands/ticks.py +0 -0
  16. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/commands_dev/__init__.py +0 -0
  17. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/commands_dev/migrate.py +0 -0
  18. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/conecta.py +0 -0
  19. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/conf.py +0 -0
  20. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/config_registre.py +0 -0
  21. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/data/__init__.py +0 -0
  22. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/data/base.py +0 -0
  23. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/data/csv.py +0 -0
  24. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/data/mt5.py +0 -0
  25. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/database.py +0 -0
  26. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/domain/__init__.py +0 -0
  27. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/domain/timeframe.py +0 -0
  28. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/logger.py +0 -0
  29. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/marketdata/__init__.py +0 -0
  30. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/marketdata/tick_cache.py +0 -0
  31. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/marketdata/tick_repository.py +0 -0
  32. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/001_initial_schema.py +0 -0
  33. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/002_ticks_time_msc.py +0 -0
  34. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/003_optimize_ticks_without_rowid.py +0 -0
  35. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/004_ticks_pk_time_msc.py +0 -0
  36. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/005_tick_price_compression.py +0 -0
  37. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/006_add_index_ticks_symbol_time.py +0 -0
  38. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/007_deduplicate_ticks_and_add_unique_index.py +0 -0
  39. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/__init__.py +0 -0
  40. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/__main__.py +0 -0
  41. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/runner.py +0 -0
  42. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/__init__.py +0 -0
  43. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/bar_model.py +0 -0
  44. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/bars_model.py +0 -0
  45. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/chart_model.py +0 -0
  46. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/conf_model.py +0 -0
  47. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/consecutive_bars_model.py +0 -0
  48. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/rates_model.py +0 -0
  49. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/signals_model.py +0 -0
  50. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/unconsecutive_bar_model.py +0 -0
  51. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/mt5_context.py +0 -0
  52. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugin.py +0 -0
  53. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugin_loader.py +0 -0
  54. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugin_manager.py +0 -0
  55. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/__init__.py +0 -0
  56. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/exemplo.py-dist +0 -0
  57. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/__init__.py +0 -0
  58. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/cli.py +0 -0
  59. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/conf.py +0 -0
  60. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/models/__init__.py +0 -0
  61. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/models/model_media_movel.py +0 -0
  62. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/tests/__init__.py +0 -0
  63. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/tests/test_mm.py +0 -0
  64. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/tests/test_model_media_movel.py +0 -0
  65. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/range_medio/__init__.py +0 -0
  66. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/range_medio/cli.py +0 -0
  67. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/range_medio/conf.py +0 -0
  68. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/range_medio/models/__init__.py +0 -0
  69. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/range_medio/models/average_range_model.py +0 -0
  70. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/range_medio/tests/__init__.py +0 -0
  71. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/range_medio/tests/test_rm.py +0 -0
  72. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/volume_medio/__init__.py +0 -0
  73. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/volume_medio/cli.py +0 -0
  74. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/volume_medio/conf.py +0 -0
  75. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/volume_medio/models/__init__.py +0 -0
  76. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/volume_medio/models/model_average_volume.py +0 -0
  77. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/volume_medio/tests/__init__.py +0 -0
  78. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/volume_medio/tests/test_vm.py +0 -0
  79. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/__init__.py +0 -0
  80. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/close_view.py +0 -0
  81. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/full_view.py +0 -0
  82. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/high_view.py +0 -0
  83. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/low_view.py +0 -0
  84. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/min_view.py +0 -0
  85. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/open_view.py +0 -0
  86. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/ranges_view.py +0 -0
  87. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/rates_view.py +0 -0
  88. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/vars_view.py +0 -0
  89. {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/volumes_view.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mtcli
3
- Version: 3.8.0.dev10
3
+ Version: 3.8.0.dev11
4
4
  Summary: Aplicativo CLI para exibir gráficos do MetaTrader 5 screen reader friendly
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -5,7 +5,8 @@ Responsável por:
5
5
 
6
6
  - sincronizar histórico inicial
7
7
  - capturar ticks em tempo real
8
- - persistir ticks via TickRepository
8
+ - enviar ticks para o TickWriter
9
+ - manter posição de leitura por símbolo
9
10
  """
10
11
 
11
12
  import time
@@ -16,33 +17,53 @@ from datetime import datetime
16
17
 
17
18
  from mtcli.mt5_context import mt5_conexao
18
19
  from .tick_repository import TickRepository
20
+ from .tick_writer import TickWriter
19
21
 
20
22
 
21
23
  class TickEngine:
22
24
 
25
+ # intervalo entre ciclos de polling
23
26
  POLL_INTERVAL = 0.05
27
+
28
+ # tamanho máximo retornado pelo MT5
24
29
  BATCH_SIZE = 1000
30
+
31
+ # sobreposição para evitar perda de ticks
25
32
  OVERLAP_MS = 20
26
33
 
27
34
  def __init__(self, symbols):
28
35
 
29
36
  self.symbols = symbols
30
37
 
38
+ # repositórios por símbolo
31
39
  self.repositories = {
32
40
  symbol: TickRepository()
33
41
  for symbol in symbols
34
42
  }
35
43
 
44
+ # writer assíncrono
45
+ self.writer = TickWriter(self.repositories)
46
+
36
47
  self.running = False
37
48
  self.thread = None
38
49
 
50
+ # ==========================================================
51
+ # CONTROLE DO ENGINE
52
+ # ==========================================================
53
+
39
54
  def start(self):
55
+ """
56
+ Inicia o motor de captura.
57
+ """
40
58
 
41
59
  if self.running:
42
60
  return
43
61
 
44
62
  self.running = True
45
63
 
64
+ # inicia writer
65
+ self.writer.start()
66
+
46
67
  self.thread = threading.Thread(
47
68
  target=self._run,
48
69
  daemon=True,
@@ -52,18 +73,32 @@ class TickEngine:
52
73
  self.thread.start()
53
74
 
54
75
  def stop(self):
76
+ """
77
+ Interrompe captura de ticks.
78
+ """
55
79
 
56
80
  self.running = False
57
81
 
58
82
  if self.thread:
59
83
  self.thread.join()
60
84
 
85
+ # garante flush final
86
+ self.writer.stop()
87
+
88
+ # ==========================================================
89
+ # LOOP PRINCIPAL
90
+ # ==========================================================
91
+
61
92
  def _run(self):
62
93
 
63
94
  with mt5_conexao():
64
95
 
65
96
  last_positions = {}
66
97
 
98
+ # --------------------------------------------------
99
+ # sincronização inicial
100
+ # --------------------------------------------------
101
+
67
102
  for symbol in self.symbols:
68
103
 
69
104
  repo = self.repositories[symbol]
@@ -77,6 +112,10 @@ class TickEngine:
77
112
  else:
78
113
  last_positions[symbol] = int(time.time() * 1000)
79
114
 
115
+ # --------------------------------------------------
116
+ # loop contínuo
117
+ # --------------------------------------------------
118
+
80
119
  while self.running:
81
120
 
82
121
  for symbol in self.symbols:
@@ -84,9 +123,11 @@ class TickEngine:
84
123
 
85
124
  time.sleep(self.POLL_INTERVAL)
86
125
 
87
- def _drain_symbol(self, symbol, last_positions):
126
+ # ==========================================================
127
+ # CAPTURA POR SÍMBOLO
128
+ # ==========================================================
88
129
 
89
- repo = self.repositories[symbol]
130
+ def _drain_symbol(self, symbol, last_positions):
90
131
 
91
132
  last_msc = last_positions[symbol]
92
133
 
@@ -106,20 +147,8 @@ class TickEngine:
106
147
  if ticks is None or len(ticks) == 0:
107
148
  break
108
149
 
109
- repo.conn.execute("BEGIN")
110
-
111
- try:
112
-
113
- repo._insert_ticks(symbol, ticks)
114
-
115
- repo.cache.add_many(ticks)
116
-
117
- repo.conn.commit()
118
-
119
- except Exception:
120
-
121
- repo.conn.rollback()
122
- raise
150
+ # envia ticks para o writer assíncrono
151
+ self.writer.push(symbol, ticks)
123
152
 
124
153
  last_msc = int(ticks[-1]["time_msc"])
125
154
 
@@ -0,0 +1,98 @@
1
+ """
2
+ TickWriter.
3
+
4
+ Thread dedicada para escrita de ticks no SQLite.
5
+ Recebe ticks via Queue e grava em batches grandes
6
+ para maximizar performance.
7
+ """
8
+
9
+ import threading
10
+ from queue import Queue, Empty
11
+ from time import sleep
12
+
13
+
14
+ class TickWriter:
15
+
16
+ FLUSH_INTERVAL = 0.2
17
+ MAX_BATCH = 5000
18
+
19
+ def __init__(self, repositories):
20
+
21
+ self.repositories = repositories
22
+ self.queue = Queue(maxsize=100000)
23
+
24
+ self.running = False
25
+ self.thread = None
26
+
27
+ def start(self):
28
+
29
+ if self.running:
30
+ return
31
+
32
+ self.running = True
33
+
34
+ self.thread = threading.Thread(
35
+ target=self._run,
36
+ daemon=True,
37
+ name="mtcli-tick-writer"
38
+ )
39
+
40
+ self.thread.start()
41
+
42
+ def stop(self):
43
+
44
+ self.running = False
45
+
46
+ if self.thread:
47
+ self.thread.join()
48
+
49
+ def push(self, symbol, ticks):
50
+
51
+ self.queue.put((symbol, ticks))
52
+
53
+ def _run(self):
54
+
55
+ buffers = {}
56
+
57
+ while self.running:
58
+
59
+ try:
60
+
61
+ symbol, ticks = self.queue.get(timeout=self.FLUSH_INTERVAL)
62
+
63
+ buffers.setdefault(symbol, []).extend(ticks)
64
+
65
+ if len(buffers[symbol]) >= self.MAX_BATCH:
66
+
67
+ self._flush(symbol, buffers)
68
+
69
+ except Empty:
70
+
71
+ for symbol in list(buffers.keys()):
72
+ self._flush(symbol, buffers)
73
+
74
+ def _flush(self, symbol, buffers):
75
+
76
+ ticks = buffers.get(symbol)
77
+
78
+ if not ticks:
79
+ return
80
+
81
+ repo = self.repositories[symbol]
82
+
83
+ repo.conn.execute("BEGIN")
84
+
85
+ try:
86
+
87
+ repo._insert_ticks(symbol, ticks)
88
+
89
+ repo.cache.add_many(ticks)
90
+
91
+ repo.conn.commit()
92
+
93
+ except Exception:
94
+
95
+ repo.conn.rollback()
96
+ raise
97
+
98
+ buffers[symbol] = []
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mtcli"
3
- version = "3.8.0.dev10"
3
+ version = "3.8.0.dev11"
4
4
  description = "Aplicativo CLI para exibir gráficos do MetaTrader 5 screen reader friendly"
5
5
  authors = [
6
6
  {name = "Valmir França",email = "vfranca3@gmail.com"}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes