mtcli 3.8.0.dev14__tar.gz → 3.8.0.dev15__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.dev14 → mtcli-3.8.0.dev15}/PKG-INFO +1 -1
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/commands/backfill.py +70 -68
- mtcli-3.8.0.dev15/mtcli/marketdata/backfill.py +166 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/marketdata/tick_bus.py +61 -45
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/marketdata/tick_repository.py +330 -330
- mtcli-3.8.0.dev15/mtcli/marketdata/tick_writer.py +44 -0
- mtcli-3.8.0.dev15/mtcli/migrations/008_fix_ticks_unique_index.py +98 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/pyproject.toml +1 -1
- mtcli-3.8.0.dev14/mtcli/marketdata/backfill_engine.py +0 -201
- mtcli-3.8.0.dev14/mtcli/marketdata/tick_writer.py +0 -38
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/LICENSE +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/README.md +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/__main__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/cli.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/cli_dev.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/commands/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/commands/bars.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/commands/conf.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/commands/doctor.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/commands/ticks.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/commands_dev/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/commands_dev/migrate.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/conecta.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/conf.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/config_registre.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/data/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/data/base.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/data/csv.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/data/mt5.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/database.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/domain/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/domain/timeframe.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/logger.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/marketdata/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/marketdata/tick_cache.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/marketdata/tick_engine.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/migrations/001_initial_schema.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/migrations/002_ticks_time_msc.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/migrations/003_optimize_ticks_without_rowid.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/migrations/004_ticks_pk_time_msc.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/migrations/005_tick_price_compression.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/migrations/006_add_index_ticks_symbol_time.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/migrations/007_deduplicate_ticks_and_add_unique_index.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/migrations/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/migrations/__main__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/migrations/runner.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/models/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/models/bar_model.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/models/bars_model.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/models/chart_model.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/models/conf_model.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/models/consecutive_bars_model.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/models/rates_model.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/models/signals_model.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/models/unconsecutive_bar_model.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/mt5_context.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugin.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugin_loader.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugin_manager.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/exemplo.py-dist +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/media_movel/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/media_movel/cli.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/media_movel/conf.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/media_movel/models/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/media_movel/models/model_media_movel.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/media_movel/tests/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/media_movel/tests/test_mm.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/media_movel/tests/test_model_media_movel.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/range_medio/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/range_medio/cli.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/range_medio/conf.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/range_medio/models/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/range_medio/models/average_range_model.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/range_medio/tests/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/range_medio/tests/test_rm.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/volume_medio/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/volume_medio/cli.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/volume_medio/conf.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/volume_medio/models/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/volume_medio/models/model_average_volume.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/volume_medio/tests/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/plugins/volume_medio/tests/test_vm.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/services/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/services/maintenance_service.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/services/tick_service.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/views/__init__.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/views/close_view.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/views/full_view.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/views/high_view.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/views/low_view.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/views/min_view.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/views/open_view.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/views/ranges_view.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/views/rates_view.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/views/vars_view.py +0 -0
- {mtcli-3.8.0.dev14 → mtcli-3.8.0.dev15}/mtcli/views/volumes_view.py +0 -0
|
@@ -1,68 +1,70 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Comando CLI: backfill
|
|
3
|
-
|
|
4
|
-
Responsável por carregar histórico de ticks do MetaTrader5
|
|
5
|
-
para o banco SQLite do mtcli utilizando o BackfillEngine.
|
|
6
|
-
|
|
7
|
-
Fluxo:
|
|
8
|
-
|
|
9
|
-
BackfillEngine
|
|
10
|
-
↓
|
|
11
|
-
|
|
12
|
-
↓
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
from mtcli.marketdata.
|
|
23
|
-
from mtcli.marketdata.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@click.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
repo = TickRepository()
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
engine
|
|
1
|
+
"""
|
|
2
|
+
Comando CLI: backfill
|
|
3
|
+
|
|
4
|
+
Responsável por carregar histórico de ticks do MetaTrader5
|
|
5
|
+
para o banco SQLite do mtcli utilizando o BackfillEngine.
|
|
6
|
+
|
|
7
|
+
Fluxo:
|
|
8
|
+
|
|
9
|
+
BackfillEngine
|
|
10
|
+
↓
|
|
11
|
+
TickBus
|
|
12
|
+
↓
|
|
13
|
+
TickWriter
|
|
14
|
+
↓
|
|
15
|
+
TickRepository
|
|
16
|
+
↓
|
|
17
|
+
SQLite
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import click
|
|
21
|
+
|
|
22
|
+
from mtcli.marketdata.backfill import BackfillEngine
|
|
23
|
+
from mtcli.marketdata.tick_bus import TickBus
|
|
24
|
+
from mtcli.marketdata.tick_repository import TickRepository
|
|
25
|
+
from mtcli.marketdata.tick_writer import TickWriter
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@click.command()
|
|
29
|
+
@click.argument("symbol")
|
|
30
|
+
@click.option(
|
|
31
|
+
"--days",
|
|
32
|
+
default=5,
|
|
33
|
+
show_default=True,
|
|
34
|
+
help="Número de dias de histórico a carregar caso não exista histórico local.",
|
|
35
|
+
)
|
|
36
|
+
def backfill(symbol: str, days: int):
|
|
37
|
+
"""
|
|
38
|
+
Executa backfill histórico de ticks.
|
|
39
|
+
|
|
40
|
+
Este comando baixa ticks históricos diretamente do MetaTrader5
|
|
41
|
+
e os grava no banco local SQLite do mtcli.
|
|
42
|
+
|
|
43
|
+
O processo é incremental:
|
|
44
|
+
|
|
45
|
+
- se já existirem ticks no banco, o backfill continua do último tick
|
|
46
|
+
- caso contrário, carrega o número de dias definido em --days
|
|
47
|
+
|
|
48
|
+
Examples
|
|
49
|
+
--------
|
|
50
|
+
|
|
51
|
+
Carregar 5 dias:
|
|
52
|
+
|
|
53
|
+
mt fill WINJ26
|
|
54
|
+
|
|
55
|
+
Carregar 30 dias:
|
|
56
|
+
|
|
57
|
+
mt fill WINJ26 --days 30
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
bus = TickBus()
|
|
61
|
+
|
|
62
|
+
repo = TickRepository()
|
|
63
|
+
|
|
64
|
+
writer = TickWriter(symbol, repo)
|
|
65
|
+
|
|
66
|
+
bus.subscribe(writer)
|
|
67
|
+
|
|
68
|
+
engine = BackfillEngine(symbol, bus, repo)
|
|
69
|
+
|
|
70
|
+
engine.run(days)
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BackfillEngine
|
|
3
|
+
|
|
4
|
+
Carrega ticks históricos do MetaTrader5 de forma determinística
|
|
5
|
+
utilizando paginação por janelas de tempo.
|
|
6
|
+
|
|
7
|
+
Vantagens sobre copy_ticks_from():
|
|
8
|
+
|
|
9
|
+
• evita duplicação
|
|
10
|
+
• evita buracos de ticks
|
|
11
|
+
• não entra em loop
|
|
12
|
+
• desempenho previsível
|
|
13
|
+
• seguro para reexecução (idempotente)
|
|
14
|
+
|
|
15
|
+
Arquitetura:
|
|
16
|
+
|
|
17
|
+
MT5
|
|
18
|
+
↓
|
|
19
|
+
BackfillEngine
|
|
20
|
+
↓
|
|
21
|
+
TickBus
|
|
22
|
+
↓
|
|
23
|
+
Subscribers (TickWriter, plugins, etc)
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import datetime
|
|
27
|
+
import MetaTrader5 as mt5
|
|
28
|
+
|
|
29
|
+
from mtcli.logger import setup_logger
|
|
30
|
+
from mtcli.mt5_context import mt5_conexao
|
|
31
|
+
|
|
32
|
+
logger = setup_logger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class BackfillEngine:
|
|
36
|
+
"""
|
|
37
|
+
Engine responsável pelo carregamento histórico de ticks.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
WINDOW_MINUTES = 10
|
|
41
|
+
|
|
42
|
+
def __init__(self, symbol, tick_bus, repository):
|
|
43
|
+
|
|
44
|
+
self.symbol = symbol
|
|
45
|
+
self.tick_bus = tick_bus
|
|
46
|
+
self.repository = repository
|
|
47
|
+
|
|
48
|
+
self.last_time_msc = None
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
def _get_last_stored(self):
|
|
53
|
+
|
|
54
|
+
last = self.repository._get_last_tick_msc(self.symbol)
|
|
55
|
+
|
|
56
|
+
if last:
|
|
57
|
+
logger.info(
|
|
58
|
+
"Backfill retomando do último tick armazenado: %s",
|
|
59
|
+
last,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return last
|
|
63
|
+
|
|
64
|
+
# ---------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
def run(self, days=5):
|
|
67
|
+
|
|
68
|
+
logger.info(
|
|
69
|
+
"Backfill iniciado (%s) — até %s dias de histórico",
|
|
70
|
+
self.symbol,
|
|
71
|
+
days,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
with mt5_conexao():
|
|
75
|
+
|
|
76
|
+
mt5.symbol_select(self.symbol, True)
|
|
77
|
+
|
|
78
|
+
now = datetime.datetime.now()
|
|
79
|
+
|
|
80
|
+
last = self._get_last_stored()
|
|
81
|
+
|
|
82
|
+
if last:
|
|
83
|
+
|
|
84
|
+
self.last_time_msc = last
|
|
85
|
+
|
|
86
|
+
start = datetime.datetime.fromtimestamp(
|
|
87
|
+
(last + 1) / 1000
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
else:
|
|
91
|
+
|
|
92
|
+
start = now - datetime.timedelta(days=days)
|
|
93
|
+
|
|
94
|
+
end = now
|
|
95
|
+
|
|
96
|
+
total_loaded = 0
|
|
97
|
+
|
|
98
|
+
while start < end:
|
|
99
|
+
|
|
100
|
+
chunk_end = start + datetime.timedelta(
|
|
101
|
+
minutes=self.WINDOW_MINUTES
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if chunk_end > end:
|
|
105
|
+
chunk_end = end
|
|
106
|
+
|
|
107
|
+
ticks = mt5.copy_ticks_range(
|
|
108
|
+
self.symbol,
|
|
109
|
+
start,
|
|
110
|
+
chunk_end,
|
|
111
|
+
mt5.COPY_TICKS_ALL,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
if ticks is None or len(ticks) == 0:
|
|
115
|
+
|
|
116
|
+
start = chunk_end
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
# ---------------------------------------------
|
|
120
|
+
# filtro extra de segurança
|
|
121
|
+
# ---------------------------------------------
|
|
122
|
+
|
|
123
|
+
if self.last_time_msc:
|
|
124
|
+
|
|
125
|
+
mask = ticks["time_msc"] > self.last_time_msc
|
|
126
|
+
ticks = ticks[mask]
|
|
127
|
+
|
|
128
|
+
if len(ticks) == 0:
|
|
129
|
+
|
|
130
|
+
start = chunk_end
|
|
131
|
+
continue
|
|
132
|
+
|
|
133
|
+
# ---------------------------------------------
|
|
134
|
+
# publica ticks
|
|
135
|
+
# ---------------------------------------------
|
|
136
|
+
|
|
137
|
+
self.tick_bus.publish_many(ticks)
|
|
138
|
+
|
|
139
|
+
last_msc = int(ticks["time_msc"][-1])
|
|
140
|
+
|
|
141
|
+
if last_msc == self.last_time_msc:
|
|
142
|
+
|
|
143
|
+
logger.warning(
|
|
144
|
+
"Proteção de loop ativada — encerrando backfill"
|
|
145
|
+
)
|
|
146
|
+
break
|
|
147
|
+
|
|
148
|
+
self.last_time_msc = last_msc
|
|
149
|
+
|
|
150
|
+
total_loaded += len(ticks)
|
|
151
|
+
|
|
152
|
+
if total_loaded % 1_000_000 < len(ticks):
|
|
153
|
+
|
|
154
|
+
logger.info(
|
|
155
|
+
"Backfill progresso (%s): %s ticks",
|
|
156
|
+
self.symbol,
|
|
157
|
+
f"{total_loaded:,}",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
start = chunk_end
|
|
161
|
+
|
|
162
|
+
logger.info(
|
|
163
|
+
"Backfill concluído (%s) — %s ticks processados",
|
|
164
|
+
self.symbol,
|
|
165
|
+
f"{total_loaded:,}",
|
|
166
|
+
)
|
|
@@ -1,45 +1,61 @@
|
|
|
1
|
-
"""
|
|
2
|
-
TickBus
|
|
3
|
-
|
|
4
|
-
Event Bus simples para distribuição de ticks.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
logger =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class TickBus:
|
|
13
|
-
"""
|
|
14
|
-
Implementa um Event Bus simples para ticks.
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
def __init__(self):
|
|
18
|
-
self.subscribers = []
|
|
19
|
-
|
|
20
|
-
# ---------------------------------------------------------
|
|
21
|
-
|
|
22
|
-
def subscribe(self, handler):
|
|
23
|
-
"""
|
|
24
|
-
Registra um subscriber para receber ticks.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
self.subscribers.append(handler)
|
|
28
|
-
|
|
29
|
-
name = getattr(handler, "__qualname__", handler.__class__.__name__)
|
|
30
|
-
|
|
31
|
-
logger.debug("Subscriber registrado: %s", name)
|
|
32
|
-
|
|
33
|
-
# ---------------------------------------------------------
|
|
34
|
-
|
|
35
|
-
def publish(self, tick):
|
|
36
|
-
"""
|
|
37
|
-
Publica um tick para todos os subscribers.
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
for handler in self.subscribers:
|
|
41
|
-
try:
|
|
42
|
-
handler(tick)
|
|
43
|
-
|
|
44
|
-
except Exception:
|
|
45
|
-
logger.exception("Erro em subscriber")
|
|
1
|
+
"""
|
|
2
|
+
TickBus
|
|
3
|
+
|
|
4
|
+
Event Bus simples para distribuição de ticks.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from mtcli.logger import setup_logger
|
|
8
|
+
|
|
9
|
+
logger = setup_logger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TickBus:
|
|
13
|
+
"""
|
|
14
|
+
Implementa um Event Bus simples para ticks.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
self.subscribers = []
|
|
19
|
+
|
|
20
|
+
# ---------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
def subscribe(self, handler):
|
|
23
|
+
"""
|
|
24
|
+
Registra um subscriber para receber ticks.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
self.subscribers.append(handler)
|
|
28
|
+
|
|
29
|
+
name = getattr(handler, "__qualname__", handler.__class__.__name__)
|
|
30
|
+
|
|
31
|
+
logger.debug("Subscriber registrado: %s", name)
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
def publish(self, tick):
|
|
36
|
+
"""
|
|
37
|
+
Publica um único tick para todos os subscribers.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
for handler in self.subscribers:
|
|
41
|
+
try:
|
|
42
|
+
handler(tick)
|
|
43
|
+
|
|
44
|
+
except Exception:
|
|
45
|
+
logger.exception("Erro em subscriber")
|
|
46
|
+
|
|
47
|
+
# ---------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
def publish_many(self, ticks):
|
|
50
|
+
"""
|
|
51
|
+
Publica um lote de ticks para todos os subscribers.
|
|
52
|
+
|
|
53
|
+
Usado principalmente por mecanismos de backfill.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
for handler in self.subscribers:
|
|
57
|
+
try:
|
|
58
|
+
handler(ticks)
|
|
59
|
+
|
|
60
|
+
except Exception:
|
|
61
|
+
logger.exception("Erro em subscriber")
|