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.
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/PKG-INFO +1 -1
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/cli.py +0 -13
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/conf.py +315 -309
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/database.py +16 -33
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/marketdata/tick_repository.py +14 -23
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/marketdata/tick_streamer.py +0 -13
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/pyproject.toml +1 -1
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/LICENSE +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/README.md +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/commands/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/commands/bars.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/commands/conf.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/commands/doctor.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/commands/migrate.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/conecta.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/config_registre.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/data/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/data/base.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/data/csv.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/data/mt5.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/domain/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/domain/timeframe.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/logger.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/marketdata/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/marketdata/tick_cache.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/marketdata/tick_engine.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/migrations/001_initial_schema.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/migrations/002_ticks_time_msc.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/migrations/003_optimize_ticks_without_rowid.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/migrations/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/migrations/runner.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/bar_model.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/bars_model.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/chart_model.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/conf_model.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/consecutive_bars_model.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/rates_model.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/signals_model.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/models/unconsecutive_bar_model.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/mt5_context.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugin.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugin_loader.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugin_manager.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/exemplo.py-dist +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/cli.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/conf.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/models/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/models/model_media_movel.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/tests/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/tests/test_mm.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/tests/test_model_media_movel.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/cli.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/conf.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/models/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/models/average_range_model.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/tests/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/tests/test_rm.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/cli.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/conf.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/models/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/models/model_average_volume.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/tests/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/tests/test_vm.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/__init__.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/close_view.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/full_view.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/high_view.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/low_view.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/min_view.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/open_view.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/ranges_view.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/rates_view.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/vars_view.py +0 -0
- {mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/views/volumes_view.py +0 -0
|
@@ -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
|
-
|
|
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
|
-
|
|
24
|
-
contínua de ticks
|
|
25
|
-
|
|
26
|
-
Configurações aplicadas:
|
|
27
|
+
Retorna conexão singleton SQLite otimizada para ingestão
|
|
28
|
+
contínua de ticks.
|
|
29
|
+
"""
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
- synchronous=NORMAL
|
|
30
|
-
- temp_store em memória
|
|
31
|
-
- mmap para leitura rápida
|
|
32
|
-
- cache expandido
|
|
31
|
+
global _connection
|
|
33
32
|
|
|
34
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/media_movel/tests/test_model_media_movel.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/range_medio/models/average_range_model.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mtcli-3.8.0.dev2 → mtcli-3.8.0.dev4}/mtcli/plugins/volume_medio/models/model_average_volume.py
RENAMED
|
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
|