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.
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/PKG-INFO +1 -1
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/marketdata/tick_engine.py +46 -17
- mtcli-3.8.0.dev11/mtcli/marketdata/tick_writer.py +98 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/pyproject.toml +1 -1
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/LICENSE +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/README.md +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/__main__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/cli.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/cli_dev.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/commands/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/commands/bars.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/commands/conf.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/commands/doctor.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/commands/ticks.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/commands_dev/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/commands_dev/migrate.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/conecta.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/conf.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/config_registre.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/data/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/data/base.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/data/csv.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/data/mt5.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/database.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/domain/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/domain/timeframe.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/logger.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/marketdata/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/marketdata/tick_cache.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/marketdata/tick_repository.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/001_initial_schema.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/002_ticks_time_msc.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/003_optimize_ticks_without_rowid.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/004_ticks_pk_time_msc.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/005_tick_price_compression.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/006_add_index_ticks_symbol_time.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/007_deduplicate_ticks_and_add_unique_index.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/__main__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/runner.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/bar_model.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/bars_model.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/chart_model.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/conf_model.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/consecutive_bars_model.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/rates_model.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/signals_model.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/models/unconsecutive_bar_model.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/mt5_context.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugin.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugin_loader.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugin_manager.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/exemplo.py-dist +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/cli.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/conf.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/models/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/models/model_media_movel.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/tests/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/tests/test_mm.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/tests/test_model_media_movel.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/range_medio/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/range_medio/cli.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/range_medio/conf.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/range_medio/models/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/range_medio/models/average_range_model.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/range_medio/tests/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/range_medio/tests/test_rm.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/volume_medio/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/volume_medio/cli.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/volume_medio/conf.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/volume_medio/models/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/volume_medio/models/model_average_volume.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/volume_medio/tests/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/volume_medio/tests/test_vm.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/__init__.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/close_view.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/full_view.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/high_view.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/low_view.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/min_view.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/open_view.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/ranges_view.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/rates_view.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/vars_view.py +0 -0
- {mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/mtcli/views/volumes_view.py +0 -0
|
@@ -5,7 +5,8 @@ Responsável por:
|
|
|
5
5
|
|
|
6
6
|
- sincronizar histórico inicial
|
|
7
7
|
- capturar ticks em tempo real
|
|
8
|
-
-
|
|
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
|
-
|
|
126
|
+
# ==========================================================
|
|
127
|
+
# CAPTURA POR SÍMBOLO
|
|
128
|
+
# ==========================================================
|
|
88
129
|
|
|
89
|
-
|
|
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
|
-
|
|
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] = []
|
|
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.dev10 → mtcli-3.8.0.dev11}/mtcli/migrations/003_optimize_ticks_without_rowid.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
|
|
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.dev10 → mtcli-3.8.0.dev11}/mtcli/plugins/media_movel/models/model_media_movel.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mtcli-3.8.0.dev10 → mtcli-3.8.0.dev11}/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.dev10 → mtcli-3.8.0.dev11}/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.dev10 → mtcli-3.8.0.dev11}/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
|