mtcli 3.8.0.dev2__tar.gz → 3.8.0.dev4__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 (80) hide show
  1. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/PKG-INFO +1 -1
  2. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/cli.py +0 -13
  3. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/conf.py +315 -309
  4. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/database.py +16 -33
  5. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/marketdata/tick_repository.py +14 -23
  6. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/marketdata/tick_streamer.py +0 -13
  7. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/pyproject.toml +1 -1
  8. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/LICENSE +0 -0
  9. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/README.md +0 -0
  10. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/__init__.py +0 -0
  11. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/commands/__init__.py +0 -0
  12. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/commands/bars.py +0 -0
  13. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/commands/conf.py +0 -0
  14. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/commands/doctor.py +0 -0
  15. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/commands/migrate.py +0 -0
  16. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/conecta.py +0 -0
  17. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/config_registre.py +0 -0
  18. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/data/__init__.py +0 -0
  19. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/data/base.py +0 -0
  20. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/data/csv.py +0 -0
  21. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/data/mt5.py +0 -0
  22. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/domain/__init__.py +0 -0
  23. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/domain/timeframe.py +0 -0
  24. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/logger.py +0 -0
  25. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/marketdata/__init__.py +0 -0
  26. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/marketdata/tick_cache.py +0 -0
  27. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/marketdata/tick_engine.py +0 -0
  28. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/migrations/001_initial_schema.py +0 -0
  29. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/migrations/002_ticks_time_msc.py +0 -0
  30. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/migrations/003_optimize_ticks_without_rowid.py +0 -0
  31. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/migrations/__init__.py +0 -0
  32. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/migrations/runner.py +0 -0
  33. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/__init__.py +0 -0
  34. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/bar_model.py +0 -0
  35. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/bars_model.py +0 -0
  36. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/chart_model.py +0 -0
  37. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/conf_model.py +0 -0
  38. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/consecutive_bars_model.py +0 -0
  39. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/rates_model.py +0 -0
  40. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/signals_model.py +0 -0
  41. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/unconsecutive_bar_model.py +0 -0
  42. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/mt5_context.py +0 -0
  43. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugin.py +0 -0
  44. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugin_loader.py +0 -0
  45. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugin_manager.py +0 -0
  46. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/__init__.py +0 -0
  47. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/exemplo.py-dist +0 -0
  48. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/__init__.py +0 -0
  49. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/cli.py +0 -0
  50. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/conf.py +0 -0
  51. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/models/__init__.py +0 -0
  52. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/models/model_media_movel.py +0 -0
  53. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/tests/__init__.py +0 -0
  54. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/tests/test_mm.py +0 -0
  55. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/tests/test_model_media_movel.py +0 -0
  56. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/__init__.py +0 -0
  57. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/cli.py +0 -0
  58. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/conf.py +0 -0
  59. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/models/__init__.py +0 -0
  60. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/models/average_range_model.py +0 -0
  61. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/tests/__init__.py +0 -0
  62. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/tests/test_rm.py +0 -0
  63. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/__init__.py +0 -0
  64. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/cli.py +0 -0
  65. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/conf.py +0 -0
  66. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/models/__init__.py +0 -0
  67. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/models/model_average_volume.py +0 -0
  68. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/tests/__init__.py +0 -0
  69. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/tests/test_vm.py +0 -0
  70. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/__init__.py +0 -0
  71. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/close_view.py +0 -0
  72. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/full_view.py +0 -0
  73. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/high_view.py +0 -0
  74. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/low_view.py +0 -0
  75. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/min_view.py +0 -0
  76. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/open_view.py +0 -0
  77. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/ranges_view.py +0 -0
  78. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/rates_view.py +0 -0
  79. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/vars_view.py +0 -0
  80. {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/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.dev2
3
+ Version: 3.8.0.dev4
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
@@ -21,13 +21,6 @@ _tick_streamer = None
21
21
 
22
22
 
23
23
  def start_tick_capture():
24
- """
25
- Inicia captura contínua de ticks em background.
26
-
27
- O símbolo deve ser definido via variável:
28
-
29
- MTCLI_SYMBOL=WINJ26
30
- """
31
24
 
32
25
  global _tick_streamer
33
26
 
@@ -65,9 +58,6 @@ def start_tick_capture():
65
58
  @click.version_option(package_name="mtcli")
66
59
  @click.pass_context
67
60
  def mt(ctx):
68
- """
69
- CLI principal do mtcli.
70
- """
71
61
 
72
62
  start_tick_capture()
73
63
 
@@ -87,9 +77,6 @@ logger.info("Plugins carregados: %s", loaded_plugins)
87
77
 
88
78
  @mt.command(name="plugins")
89
79
  def list_plugins():
90
- """
91
- Lista os plugins carregados.
92
- """
93
80
 
94
81
  if not loaded_plugins:
95
82
  click.echo("Nenhum plugin carregado.")
@@ -1,309 +1,315 @@
1
- """
2
- Sistema central de configuração do mtcli.
3
-
4
- Fornece leitura de configuração a partir de:
5
-
6
- 1. Variáveis de ambiente
7
- 2. Arquivo mtcli.ini
8
- 3. Valores default
9
-
10
- Também oferece utilidades usadas por plugins como:
11
-
12
- - descoberta do diretório MQL5/Files
13
- - seleção da fonte de dados (CSV ou MT5)
14
-
15
- Plugins novos devem acessar a configuração através do objeto global `conf`.
16
-
17
- Compatibilidade retroativa:
18
- ---------------------------
19
- Plugins antigos utilizavam:
20
-
21
- from mtcli.conf import config
22
-
23
- onde `config` era um objeto `configparser.ConfigParser`.
24
-
25
- Para manter compatibilidade com plugins já publicados,
26
- o objeto `config` continua sendo exposto.
27
-
28
- Essa API é considerada **deprecated** e poderá ser removida
29
- em versões futuras do mtcli.
30
- """
31
-
32
- import os
33
- import configparser
34
-
35
- import MetaTrader5 as mt5
36
-
37
- from mtcli.mt5_context import mt5_conexao
38
-
39
-
40
- class Config:
41
- """
42
- Gerenciador central de configurações do mtcli.
43
-
44
- Permite acessar valores a partir de:
45
-
46
- - variáveis de ambiente
47
- - arquivo mtcli.ini
48
- - valores default
49
-
50
- Plugins devem preferencialmente usar:
51
-
52
- from mtcli.conf import conf
53
- conf.get(...)
54
- """
55
-
56
- def __init__(self, filename="mtcli.ini"):
57
- self.config = configparser.ConfigParser()
58
- self.config.read(filename)
59
-
60
- # ---------------------------------------------------------
61
- # leitura de valores
62
- # ---------------------------------------------------------
63
-
64
- def get(self, key, section="DEFAULT", cast=None, default=None):
65
- """
66
- Retorna um valor de configuração.
67
-
68
- Prioridade:
69
-
70
- 1. Variável de ambiente SECTION_KEY
71
- 2. Variável de ambiente KEY
72
- 3. mtcli.ini [section]
73
- 4. mtcli.ini [DEFAULT]
74
- 5. default
75
-
76
- Args:
77
- key (str):
78
- Nome da configuração.
79
-
80
- section (str):
81
- Seção do arquivo mtcli.ini.
82
-
83
- cast (type | None):
84
- Tipo para conversão do valor.
85
-
86
- default (Any):
87
- Valor padrão.
88
-
89
- Returns:
90
- Any
91
- """
92
-
93
- env_key = f"{section.upper()}_{key.upper()}"
94
-
95
- value = os.getenv(env_key) or os.getenv(key.upper())
96
-
97
- if value is None:
98
-
99
- if self.config.has_option(section, key):
100
- value = self.config.get(section, key)
101
-
102
- elif self.config.has_option("DEFAULT", key):
103
- value = self.config.get("DEFAULT", key)
104
-
105
- else:
106
- value = default
107
-
108
- if cast and value is not None:
109
-
110
- try:
111
-
112
- if cast is bool:
113
- value = str(value).lower() in ("1", "true", "yes")
114
-
115
- else:
116
- value = cast(value)
117
-
118
- except ValueError:
119
- value = default
120
-
121
- return value
122
-
123
- # ---------------------------------------------------------
124
- # seção helper
125
- # ---------------------------------------------------------
126
-
127
- def section(self, section):
128
- """
129
- Retorna um helper para acessar uma seção específica.
130
-
131
- Example:
132
-
133
- renko = conf.section("renko")
134
- brick = renko.get("brick", cast=int, default=10)
135
- """
136
-
137
- class Section:
138
- def __init__(self, parent, section):
139
- self.parent = parent
140
- self.section = section
141
-
142
- def get(self, key, cast=None, default=None):
143
- return self.parent.get(key, self.section, cast, default)
144
-
145
- return Section(self, section)
146
-
147
- # ---------------------------------------------------------
148
- # caminho MT5
149
- # ---------------------------------------------------------
150
-
151
- def get_csv_path(self):
152
- """
153
- Retorna o caminho da pasta MQL5/Files do MetaTrader 5.
154
-
155
- A prioridade é:
156
-
157
- 1. mtcli.ini -> mt5_pasta
158
- 2. descoberta automática via MT5
159
-
160
- Returns:
161
- str: caminho normalizado da pasta Files.
162
- """
163
-
164
- path = self.get("mt5_pasta")
165
-
166
- if path:
167
- return os.path.normpath(path) + os.sep
168
-
169
- with mt5_conexao():
170
-
171
- info = mt5.terminal_info()
172
-
173
- if info is None:
174
- raise RuntimeError(
175
- "Não foi possível obter informações do terminal MT5."
176
- )
177
-
178
- path = os.path.join(info.data_path, "MQL5", "Files")
179
-
180
- return os.path.normpath(path) + os.sep
181
-
182
- # ---------------------------------------------------------
183
- # data source
184
- # ---------------------------------------------------------
185
-
186
- def get_data_source(self, source=None):
187
- """
188
- Retorna a fonte de dados configurada.
189
-
190
- Args:
191
- source (str | None):
192
- Fonte explícita ("csv" ou "mt5").
193
-
194
- Returns:
195
- DataSource
196
- """
197
-
198
- from mtcli.data import CsvDataSource, MT5DataSource
199
-
200
- src = (source or self.get("dados", default="mt5")).lower()
201
-
202
- if src == "csv":
203
- return CsvDataSource()
204
-
205
- if src == "mt5":
206
- return MT5DataSource()
207
-
208
- raise ValueError(f"Fonte de dados desconhecida: {src}")
209
-
210
-
211
- # ---------------------------------------------------------
212
- # instância global usada por todo o sistema
213
- # ---------------------------------------------------------
214
-
215
- conf = Config()
216
-
217
- # ---------------------------------------------------------
218
- # compatibilidade com plugins antigos
219
- # ---------------------------------------------------------
220
- #
221
- # Plugins antigos utilizam:
222
- #
223
- # from mtcli.conf import config
224
- #
225
- # onde `config` era um ConfigParser.
226
- #
227
- # Mantemos esse objeto apontando para o ConfigParser interno
228
- # para evitar quebra de compatibilidade.
229
- #
230
- # API DEPRECATED – usar `conf.get()` em novos plugins.
231
- #
232
-
233
- config = conf.config
234
-
235
-
236
- # ---------------------------------------------------------
237
- # timeframes suportados
238
- # ---------------------------------------------------------
239
-
240
- _HOURS = [12, 8, 6, 4, 3, 2, 1]
241
- _MINUTES = [30, 20, 15, 12, 10, 6, 5, 4, 3, 2, 1]
242
-
243
- TIMEFRAMES = (
244
- ["mn1", "w1", "d1"]
245
- + [f"h{i}" for i in _HOURS]
246
- + [f"m{i}" for i in _MINUTES]
247
- )
248
-
249
- # ---------------------------------------------------------
250
- # Configurações gerais
251
- # ---------------------------------------------------------
252
-
253
- SYMBOL = conf.get("symbol", default="WIN$N")
254
- DIGITOS = conf.get("digitos", cast=int, default=2)
255
- PERIOD = conf.get("period", default="D1")
256
- BARS = conf.get("count", cast=int, default=999)
257
-
258
- VIEW = conf.get("view", default="ch")
259
- VOLUME = conf.get("volume", default="tick")
260
- DATE = conf.get("date", default="")
261
-
262
- # ---------------------------------------------------------
263
- # Configurações de leitura de candles
264
- # ---------------------------------------------------------
265
-
266
- LATERAL = conf.get("lateral", default="doji")
267
- ALTA = conf.get("alta", default="verde")
268
- BAIXA = conf.get("baixa", default="vermelho")
269
-
270
- ROMPIMENTO_ALTA = conf.get("rompimento_alta", default="c")
271
- ROMPIMENTO_BAIXA = conf.get("rompimento_baixa", default="v")
272
-
273
- PERCENTUAL_ROMPIMENTO = conf.get(
274
- "percentual_rompimento",
275
- cast=int,
276
- default=50,
277
- )
278
-
279
- PERCENTUAL_DOJI = conf.get(
280
- "percentual_doji",
281
- cast=int,
282
- default=10,
283
- )
284
-
285
- # ---------------------------------------------------------
286
- # Configurações de padrões de barra
287
- # ---------------------------------------------------------
288
-
289
- UP_BAR = conf.get("up_bar", default="asc")
290
- DOWN_BAR = conf.get("down_bar", default="desc")
291
-
292
- INSIDE_BAR = conf.get("inside_bar", default="ib")
293
- OUTSIDE_BAR = conf.get("outside_bar", default="ob")
294
-
295
- SOMBRA_SUPERIOR = conf.get("sombra_superior", default="top")
296
- SOMBRA_INFERIOR = conf.get("sombra_inferior", default="bottom")
297
-
298
- # ---------------------------------------------------------
299
- # Fonte de dados
300
- # ---------------------------------------------------------
301
-
302
- DATA_SOURCE_NAME = conf.get("dados", default="mt5").lower()
303
- DATA_SOURCE = conf.get_data_source()
304
-
305
- # ---------------------------------------------------------
306
- # caminho inicial do CSV (pode vir do ini/env)
307
- # ---------------------------------------------------------
308
-
309
- _INITIAL_CSV_PATH = conf.get_csv_path()
1
+ """
2
+ Sistema central de configuração do mtcli.
3
+
4
+ Fornece leitura de configuração a partir de:
5
+
6
+ 1. Variáveis de ambiente
7
+ 2. Arquivo mtcli.ini
8
+ 3. Valores default
9
+
10
+ Também oferece utilidades usadas por plugins como:
11
+
12
+ - descoberta do diretório MQL5/Files
13
+ - seleção da fonte de dados (CSV ou MT5)
14
+
15
+ Plugins novos devem acessar a configuração através do objeto global `conf`.
16
+
17
+ Compatibilidade retroativa:
18
+ ---------------------------
19
+ Plugins antigos utilizavam:
20
+
21
+ from mtcli.conf import config
22
+
23
+ onde `config` era um objeto `configparser.ConfigParser`.
24
+
25
+ Para manter compatibilidade com plugins já publicados,
26
+ o objeto `config` continua sendo exposto.
27
+
28
+ Essa API é considerada **deprecated** e poderá ser removida
29
+ em versões futuras do mtcli.
30
+ """
31
+
32
+ import os
33
+ import configparser
34
+
35
+ import MetaTrader5 as mt5
36
+
37
+ from mtcli.mt5_context import mt5_conexao
38
+
39
+
40
+ class Config:
41
+ """
42
+ Gerenciador central de configurações do mtcli.
43
+
44
+ Permite acessar valores a partir de:
45
+
46
+ - variáveis de ambiente
47
+ - arquivo mtcli.ini
48
+ - valores default
49
+
50
+ Plugins devem preferencialmente usar:
51
+
52
+ from mtcli.conf import conf
53
+ conf.get(...)
54
+ """
55
+
56
+ def __init__(self, filename="mtcli.ini"):
57
+ self.config = configparser.ConfigParser()
58
+ self.config.read(filename)
59
+
60
+ # ---------------------------------------------------------
61
+ # leitura de valores
62
+ # ---------------------------------------------------------
63
+
64
+ def get(self, key, section="DEFAULT", cast=None, default=None):
65
+ """
66
+ Retorna um valor de configuração.
67
+
68
+ Prioridade:
69
+
70
+ 1. Variável de ambiente SECTION_KEY
71
+ 2. Variável de ambiente KEY
72
+ 3. mtcli.ini [section]
73
+ 4. mtcli.ini [DEFAULT]
74
+ 5. default
75
+
76
+ Args:
77
+ key (str):
78
+ Nome da configuração.
79
+
80
+ section (str):
81
+ Seção do arquivo mtcli.ini.
82
+
83
+ cast (type | None):
84
+ Tipo para conversão do valor.
85
+
86
+ default (Any):
87
+ Valor padrão.
88
+
89
+ Returns:
90
+ Any
91
+ """
92
+
93
+ env_key = f"{section.upper()}_{key.upper()}"
94
+
95
+ value = os.getenv(env_key) or os.getenv(key.upper())
96
+
97
+ if value is None:
98
+
99
+ if self.config.has_option(section, key):
100
+ value = self.config.get(section, key)
101
+
102
+ elif self.config.has_option("DEFAULT", key):
103
+ value = self.config.get("DEFAULT", key)
104
+
105
+ else:
106
+ value = default
107
+
108
+ if cast and value is not None:
109
+
110
+ try:
111
+
112
+ if cast is bool:
113
+ value = str(value).lower() in ("1", "true", "yes")
114
+
115
+ else:
116
+ value = cast(value)
117
+
118
+ except ValueError:
119
+ value = default
120
+
121
+ return value
122
+
123
+ # ---------------------------------------------------------
124
+ # seção helper
125
+ # ---------------------------------------------------------
126
+
127
+ def section(self, section):
128
+ """
129
+ Retorna um helper para acessar uma seção específica.
130
+
131
+ Example:
132
+
133
+ renko = conf.section("renko")
134
+ brick = renko.get("brick", cast=int, default=10)
135
+ """
136
+
137
+ class Section:
138
+ def __init__(self, parent, section):
139
+ self.parent = parent
140
+ self.section = section
141
+
142
+ def get(self, key, cast=None, default=None):
143
+ return self.parent.get(key, self.section, cast, default)
144
+
145
+ return Section(self, section)
146
+
147
+ # ---------------------------------------------------------
148
+ # caminho MT5
149
+ # ---------------------------------------------------------
150
+
151
+ def get_csv_path(self):
152
+ """
153
+ Retorna o caminho da pasta MQL5/Files do MetaTrader 5.
154
+
155
+ A prioridade é:
156
+
157
+ 1. mtcli.ini -> mt5_pasta
158
+ 2. descoberta automática via MT5
159
+
160
+ Returns:
161
+ str: caminho normalizado da pasta Files.
162
+ """
163
+
164
+ path = self.get("mt5_pasta")
165
+
166
+ if path:
167
+ return os.path.normpath(path) + os.sep
168
+
169
+ with mt5_conexao():
170
+
171
+ info = mt5.terminal_info()
172
+
173
+ if info is None:
174
+ raise RuntimeError(
175
+ "Não foi possível obter informações do terminal MT5."
176
+ )
177
+
178
+ path = os.path.join(info.data_path, "MQL5", "Files")
179
+
180
+ return os.path.normpath(path) + os.sep
181
+
182
+ # ---------------------------------------------------------
183
+ # data source
184
+ # ---------------------------------------------------------
185
+
186
+ def get_data_source(self, source=None):
187
+ """
188
+ Retorna a fonte de dados configurada.
189
+
190
+ Args:
191
+ source (str | None):
192
+ Fonte explícita ("csv" ou "mt5").
193
+
194
+ Returns:
195
+ DataSource
196
+ """
197
+
198
+ from mtcli.data import CsvDataSource, MT5DataSource
199
+
200
+ src = (source or self.get("dados", default="mt5")).lower()
201
+
202
+ if src == "csv":
203
+ return CsvDataSource()
204
+
205
+ if src == "mt5":
206
+ return MT5DataSource()
207
+
208
+ raise ValueError(f"Fonte de dados desconhecida: {src}")
209
+
210
+
211
+ # ---------------------------------------------------------
212
+ # instância global usada por todo o sistema
213
+ # ---------------------------------------------------------
214
+
215
+ conf = Config()
216
+
217
+ # ---------------------------------------------------------
218
+ # compatibilidade com plugins antigos
219
+ # ---------------------------------------------------------
220
+ #
221
+ # Plugins antigos utilizam:
222
+ #
223
+ # from mtcli.conf import config
224
+ #
225
+ # onde `config` era um ConfigParser.
226
+ #
227
+ # Mantemos esse objeto apontando para o ConfigParser interno
228
+ # para evitar quebra de compatibilidade.
229
+ #
230
+ # API DEPRECATED – usar `conf.get()` em novos plugins.
231
+ #
232
+
233
+ config = conf.config
234
+
235
+
236
+ # ---------------------------------------------------------
237
+ # timeframes suportados
238
+ # ---------------------------------------------------------
239
+
240
+ _HOURS = [12, 8, 6, 4, 3, 2, 1]
241
+ _MINUTES = [30, 20, 15, 12, 10, 6, 5, 4, 3, 2, 1]
242
+
243
+ TIMEFRAMES = (
244
+ ["mn1", "w1", "d1"]
245
+ + [f"h{i}" for i in _HOURS]
246
+ + [f"m{i}" for i in _MINUTES]
247
+ )
248
+
249
+ # ---------------------------------------------------------
250
+ # Configurações gerais
251
+ # ---------------------------------------------------------
252
+
253
+ SYMBOL = conf.get("symbol", default="WIN$N")
254
+ DIGITOS = conf.get("digitos", cast=int, default=2)
255
+ PERIOD = conf.get("period", default="D1")
256
+ BARS = conf.get("count", cast=int, default=999)
257
+
258
+ VIEW = conf.get("view", default="ch")
259
+ VOLUME = conf.get("volume", default="tick")
260
+ DATE = conf.get("date", default="")
261
+
262
+ # ---------------------------------------------------------
263
+ # Configurações de leitura de candles
264
+ # ---------------------------------------------------------
265
+
266
+ LATERAL = conf.get("lateral", default="doji")
267
+ ALTA = conf.get("alta", default="verde")
268
+ BAIXA = conf.get("baixa", default="vermelho")
269
+
270
+ ROMPIMENTO_ALTA = conf.get("rompimento_alta", default="c")
271
+ ROMPIMENTO_BAIXA = conf.get("rompimento_baixa", default="v")
272
+
273
+ PERCENTUAL_ROMPIMENTO = conf.get(
274
+ "percentual_rompimento",
275
+ cast=int,
276
+ default=50,
277
+ )
278
+
279
+ PERCENTUAL_DOJI = conf.get(
280
+ "percentual_doji",
281
+ cast=int,
282
+ default=10,
283
+ )
284
+
285
+ # ---------------------------------------------------------
286
+ # Configurações de padrões de barra
287
+ # ---------------------------------------------------------
288
+
289
+ UP_BAR = conf.get("up_bar", default="asc")
290
+ DOWN_BAR = conf.get("down_bar", default="desc")
291
+
292
+ INSIDE_BAR = conf.get("inside_bar", default="ib")
293
+ OUTSIDE_BAR = conf.get("outside_bar", default="ob")
294
+
295
+ SOMBRA_SUPERIOR = conf.get("sombra_superior", default="top")
296
+ SOMBRA_INFERIOR = conf.get("sombra_inferior", default="bottom")
297
+
298
+ # ---------------------------------------------------------
299
+ # Fonte de dados
300
+ # ---------------------------------------------------------
301
+
302
+ DATA_SOURCE_NAME = conf.get("dados", default="mt5").lower()
303
+ DATA_SOURCE = conf.get_data_source()
304
+
305
+ # ---------------------------------------------------------
306
+ # caminho inicial do CSV (pode vir do ini/env)
307
+ # ---------------------------------------------------------
308
+
309
+ _INITIAL_CSV_PATH = conf.get_csv_path()
310
+
311
+ # ---------------------------------------------------------
312
+ # nome do banco de dados de marketdata
313
+ # ---------------------------------------------------------
314
+
315
+ DB_NAME = conf.get("db_name", default="marketdata.db")
@@ -13,29 +13,25 @@ Responsável por:
13
13
  import sqlite3
14
14
  from pathlib import Path
15
15
  from datetime import datetime
16
+ from .conf import DB_NAME
16
17
 
17
- DB_PATH = Path.home() / ".mtcli" / "marketdata.db"
18
+
19
+ DB_PATH = Path.home() / ".mtcli" / DB_NAME
18
20
  BACKUP_DIR = Path.home() / ".mtcli" / "backups"
19
21
 
22
+ _connection = None
23
+
20
24
 
21
25
  def get_connection():
22
26
  """
23
- Cria ou retorna uma conexão SQLite otimizada para ingestão
24
- contínua de ticks de mercado.
25
-
26
- Configurações aplicadas:
27
+ Retorna conexão singleton SQLite otimizada para ingestão
28
+ contínua de ticks.
29
+ """
27
30
 
28
- - WAL (Write Ahead Logging)
29
- - synchronous=NORMAL
30
- - temp_store em memória
31
- - mmap para leitura rápida
32
- - cache expandido
31
+ global _connection
33
32
 
34
- Returns
35
- -------
36
- sqlite3.Connection
37
- Conexão ativa com o banco SQLite.
38
- """
33
+ if _connection:
34
+ return _connection
39
35
 
40
36
  DB_PATH.parent.mkdir(parents=True, exist_ok=True)
41
37
  BACKUP_DIR.mkdir(parents=True, exist_ok=True)
@@ -49,6 +45,9 @@ def get_connection():
49
45
  conn.execute("PRAGMA cache_size=-200000")
50
46
  conn.execute("PRAGMA journal_size_limit=67108864")
51
47
 
48
+ # checkpoint automático
49
+ conn.execute("PRAGMA wal_autocheckpoint=1000")
50
+
52
51
  conn.execute("""
53
52
  CREATE TABLE IF NOT EXISTS schema_migrations(
54
53
  version INTEGER PRIMARY KEY,
@@ -58,22 +57,9 @@ def get_connection():
58
57
 
59
58
  conn.commit()
60
59
 
61
- return conn
62
-
60
+ _connection = conn
63
61
 
64
- # ==========================================================
65
- # CHECKPOINT
66
- # ==========================================================
67
-
68
- def wal_checkpoint(conn):
69
- """
70
- Executa checkpoint do WAL.
71
-
72
- Move os dados do arquivo `.wal` para o banco principal
73
- e reduz seu tamanho.
74
- """
75
-
76
- conn.execute("PRAGMA wal_checkpoint(TRUNCATE)")
62
+ return conn
77
63
 
78
64
 
79
65
  # ==========================================================
@@ -83,9 +69,6 @@ def wal_checkpoint(conn):
83
69
  def backup_database(conn):
84
70
  """
85
71
  Realiza backup diário seguro do banco SQLite.
86
-
87
- O backup utiliza a API nativa do SQLite,
88
- permitindo cópia consistente mesmo com o banco em uso.
89
72
  """
90
73
 
91
74
  now = datetime.now().strftime("%Y%m%d")
@@ -5,13 +5,13 @@ Responsável por:
5
5
 
6
6
  - Persistir ticks no SQLite
7
7
  - Sincronizar histórico inicial
8
- - Consultas rápidas para engines (Renko etc)
8
+ - Consultas rápidas para engines
9
9
  """
10
10
 
11
11
  import MetaTrader5 as mt5
12
12
  from datetime import datetime, timedelta
13
13
 
14
- from ..database import get_connection, wal_checkpoint, backup_database
14
+ from ..database import get_connection, backup_database
15
15
  from .tick_cache import TickCache
16
16
  from mtcli.mt5_context import mt5_conexao
17
17
 
@@ -25,7 +25,6 @@ class TickRepository:
25
25
  self.conn = get_connection()
26
26
  self.cache = TickCache()
27
27
 
28
- self.insert_counter = 0
29
28
  self.last_backup_day = None
30
29
 
31
30
  # ==========================================================
@@ -33,9 +32,6 @@ class TickRepository:
33
32
  # ==========================================================
34
33
 
35
34
  def sync(self, symbol: str, days_back: int = 1):
36
- """
37
- Sincroniza histórico de ticks a partir do broker.
38
- """
39
35
 
40
36
  total_inserted = 0
41
37
 
@@ -84,6 +80,8 @@ class TickRepository:
84
80
  self.conn.rollback()
85
81
  raise
86
82
 
83
+ self._daily_backup()
84
+
87
85
  return total_inserted
88
86
 
89
87
  # ==========================================================
@@ -119,23 +117,7 @@ class TickRepository:
119
117
  data,
120
118
  )
121
119
 
122
- inserted = len(data)
123
-
124
- self.insert_counter += inserted
125
-
126
- if self.insert_counter >= 200000:
127
-
128
- wal_checkpoint(self.conn)
129
- self.insert_counter = 0
130
-
131
- today = datetime.now().date()
132
-
133
- if self.last_backup_day != today:
134
-
135
- backup_database(self.conn)
136
- self.last_backup_day = today
137
-
138
- return inserted
120
+ return len(data)
139
121
 
140
122
  # ==========================================================
141
123
  # CONSULTAS
@@ -199,3 +181,12 @@ class TickRepository:
199
181
  result = cursor.fetchone()
200
182
 
201
183
  return result[0] if result and result[0] else None
184
+
185
+ def _daily_backup(self):
186
+
187
+ today = datetime.now().date()
188
+
189
+ if self.last_backup_day != today:
190
+
191
+ backup_database(self.conn)
192
+ self.last_backup_day = today
@@ -1,10 +1,5 @@
1
1
  """
2
2
  Captura contínua de ticks.
3
-
4
- Objetivo:
5
-
6
- - eliminar dependência do histórico do broker
7
- - manter histórico próprio
8
3
  """
9
4
 
10
5
  import time
@@ -21,13 +16,9 @@ class TickStreamer:
21
16
 
22
17
  self.symbol = symbol
23
18
  self.repo = TickRepository()
24
-
25
19
  self.running = False
26
20
 
27
21
  def start(self):
28
- """
29
- Inicia captura contínua de ticks.
30
- """
31
22
 
32
23
  self.running = True
33
24
 
@@ -69,7 +60,3 @@ class TickStreamer:
69
60
  self.repo.conn.rollback()
70
61
 
71
62
  start = int(ticks[-1]["time_msc"]) + 1
72
-
73
- def stop(self):
74
-
75
- self.running = False
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mtcli"
3
- version = "3.8.0.dev2"
3
+ version = "3.8.0.dev4"
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
File without changes
File without changes