mtcli 3.8.0.dev0__py3-none-any.whl → 3.8.0.dev2__py3-none-any.whl
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/cli.py +16 -46
- mtcli/database.py +40 -3
- mtcli/marketdata/tick_cache.py +24 -1
- mtcli/marketdata/tick_engine.py +93 -0
- mtcli/marketdata/tick_repository.py +35 -9
- mtcli/marketdata/tick_streamer.py +4 -4
- {mtcli-3.8.0.dev0.dist-info → mtcli-3.8.0.dev2.dist-info}/METADATA +1 -1
- {mtcli-3.8.0.dev0.dist-info → mtcli-3.8.0.dev2.dist-info}/RECORD +11 -10
- {mtcli-3.8.0.dev0.dist-info → mtcli-3.8.0.dev2.dist-info}/WHEEL +0 -0
- {mtcli-3.8.0.dev0.dist-info → mtcli-3.8.0.dev2.dist-info}/entry_points.txt +0 -0
- {mtcli-3.8.0.dev0.dist-info → mtcli-3.8.0.dev2.dist-info}/licenses/LICENSE +0 -0
mtcli/cli.py
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
2
|
CLI principal do mtcli.
|
|
3
|
-
|
|
4
|
-
Este módulo define o grupo principal `mt`
|
|
5
|
-
e inicializa o carregamento de plugins.
|
|
6
|
-
|
|
7
|
-
Também inicia a captura contínua de ticks para manter
|
|
8
|
-
um histórico próprio independente do broker.
|
|
9
3
|
"""
|
|
10
4
|
|
|
11
5
|
import os
|
|
@@ -23,11 +17,6 @@ from .commands.migrate import migrate
|
|
|
23
17
|
|
|
24
18
|
logger = setup_logger(__name__)
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
# ---------------------------------------------------------
|
|
28
|
-
# Configuração de captura de ticks
|
|
29
|
-
# ---------------------------------------------------------
|
|
30
|
-
|
|
31
20
|
_tick_streamer = None
|
|
32
21
|
|
|
33
22
|
|
|
@@ -35,7 +24,7 @@ def start_tick_capture():
|
|
|
35
24
|
"""
|
|
36
25
|
Inicia captura contínua de ticks em background.
|
|
37
26
|
|
|
38
|
-
O símbolo
|
|
27
|
+
O símbolo deve ser definido via variável:
|
|
39
28
|
|
|
40
29
|
MTCLI_SYMBOL=WINJ26
|
|
41
30
|
"""
|
|
@@ -45,7 +34,7 @@ def start_tick_capture():
|
|
|
45
34
|
if _tick_streamer:
|
|
46
35
|
return
|
|
47
36
|
|
|
48
|
-
symbol = os.getenv("
|
|
37
|
+
symbol = os.getenv("MTCLI_SYMBOL")
|
|
49
38
|
|
|
50
39
|
if not symbol:
|
|
51
40
|
logger.info("Captura de ticks desativada (MTCLI_SYMBOL não definido).")
|
|
@@ -53,22 +42,24 @@ def start_tick_capture():
|
|
|
53
42
|
|
|
54
43
|
logger.info("Iniciando captura contínua de ticks para %s", symbol)
|
|
55
44
|
|
|
56
|
-
|
|
45
|
+
try:
|
|
46
|
+
|
|
47
|
+
_tick_streamer = TickStreamer(symbol)
|
|
48
|
+
|
|
49
|
+
thread = threading.Thread(
|
|
50
|
+
target=_tick_streamer.start,
|
|
51
|
+
daemon=True,
|
|
52
|
+
name="mtcli-tick-streamer",
|
|
53
|
+
)
|
|
57
54
|
|
|
58
|
-
|
|
59
|
-
target=_tick_streamer.start,
|
|
60
|
-
daemon=True,
|
|
61
|
-
name="mtcli-tick-streamer",
|
|
62
|
-
)
|
|
55
|
+
thread.start()
|
|
63
56
|
|
|
64
|
-
|
|
57
|
+
logger.info("Captura de ticks iniciada em background.")
|
|
65
58
|
|
|
66
|
-
|
|
59
|
+
except Exception:
|
|
67
60
|
|
|
61
|
+
logger.exception("Falha ao iniciar captura de ticks")
|
|
68
62
|
|
|
69
|
-
# ---------------------------------------------------------
|
|
70
|
-
# CLI principal
|
|
71
|
-
# ---------------------------------------------------------
|
|
72
63
|
|
|
73
64
|
@click.group(context_settings={"max_content_width": 120}, invoke_without_command=True)
|
|
74
65
|
@click.version_option(package_name="mtcli")
|
|
@@ -76,45 +67,28 @@ def start_tick_capture():
|
|
|
76
67
|
def mt(ctx):
|
|
77
68
|
"""
|
|
78
69
|
CLI principal do mtcli.
|
|
79
|
-
|
|
80
|
-
Exibe gráficos e informações de mercado
|
|
81
|
-
em formato textual compatível com leitores de tela.
|
|
82
70
|
"""
|
|
83
71
|
|
|
84
|
-
# inicia captura de ticks
|
|
85
72
|
start_tick_capture()
|
|
86
73
|
|
|
87
74
|
if ctx.invoked_subcommand is None:
|
|
88
75
|
click.echo(ctx.get_help())
|
|
89
76
|
|
|
90
77
|
|
|
91
|
-
# ---------------------------------------------------------
|
|
92
|
-
# Comandos internos
|
|
93
|
-
# ---------------------------------------------------------
|
|
94
|
-
|
|
95
78
|
mt.add_command(doctor, name="doctor")
|
|
96
79
|
mt.add_command(bars, name="bars")
|
|
97
80
|
mt.add_command(doctor, name="dr")
|
|
98
81
|
mt.add_command(migrate)
|
|
99
82
|
|
|
100
|
-
|
|
101
|
-
# ---------------------------------------------------------
|
|
102
|
-
# Carregamento de plugins
|
|
103
|
-
# ---------------------------------------------------------
|
|
104
|
-
|
|
105
83
|
loaded_plugins = load_plugins(mt)
|
|
106
84
|
|
|
107
85
|
logger.info("Plugins carregados: %s", loaded_plugins)
|
|
108
86
|
|
|
109
87
|
|
|
110
|
-
# ---------------------------------------------------------
|
|
111
|
-
# Comando utilitário: listar plugins
|
|
112
|
-
# ---------------------------------------------------------
|
|
113
|
-
|
|
114
88
|
@mt.command(name="plugins")
|
|
115
89
|
def list_plugins():
|
|
116
90
|
"""
|
|
117
|
-
Lista os plugins
|
|
91
|
+
Lista os plugins carregados.
|
|
118
92
|
"""
|
|
119
93
|
|
|
120
94
|
if not loaded_plugins:
|
|
@@ -127,9 +101,5 @@ def list_plugins():
|
|
|
127
101
|
click.echo(f" {name}")
|
|
128
102
|
|
|
129
103
|
|
|
130
|
-
# ---------------------------------------------------------
|
|
131
|
-
# Entry point
|
|
132
|
-
# ---------------------------------------------------------
|
|
133
|
-
|
|
134
104
|
if __name__ == "__main__":
|
|
135
105
|
mt()
|
mtcli/database.py
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core de acesso ao banco SQLite do mtcli.
|
|
3
|
+
|
|
4
|
+
Responsável por:
|
|
5
|
+
|
|
6
|
+
- Criar conexão SQLite
|
|
7
|
+
- Aplicar otimizações de performance
|
|
8
|
+
- Ativar WAL
|
|
9
|
+
- Gerenciar migrations
|
|
10
|
+
- Backup e manutenção do banco
|
|
11
|
+
"""
|
|
12
|
+
|
|
1
13
|
import sqlite3
|
|
2
14
|
from pathlib import Path
|
|
3
15
|
from datetime import datetime
|
|
@@ -7,6 +19,23 @@ BACKUP_DIR = Path.home() / ".mtcli" / "backups"
|
|
|
7
19
|
|
|
8
20
|
|
|
9
21
|
def get_connection():
|
|
22
|
+
"""
|
|
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
|
+
|
|
28
|
+
- WAL (Write Ahead Logging)
|
|
29
|
+
- synchronous=NORMAL
|
|
30
|
+
- temp_store em memória
|
|
31
|
+
- mmap para leitura rápida
|
|
32
|
+
- cache expandido
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
sqlite3.Connection
|
|
37
|
+
Conexão ativa com o banco SQLite.
|
|
38
|
+
"""
|
|
10
39
|
|
|
11
40
|
DB_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
12
41
|
BACKUP_DIR.mkdir(parents=True, exist_ok=True)
|
|
@@ -16,8 +45,9 @@ def get_connection():
|
|
|
16
45
|
conn.execute("PRAGMA journal_mode=WAL")
|
|
17
46
|
conn.execute("PRAGMA synchronous=NORMAL")
|
|
18
47
|
conn.execute("PRAGMA temp_store=MEMORY")
|
|
19
|
-
conn.execute("PRAGMA mmap_size=
|
|
48
|
+
conn.execute("PRAGMA mmap_size=268435456")
|
|
20
49
|
conn.execute("PRAGMA cache_size=-200000")
|
|
50
|
+
conn.execute("PRAGMA journal_size_limit=67108864")
|
|
21
51
|
|
|
22
52
|
conn.execute("""
|
|
23
53
|
CREATE TABLE IF NOT EXISTS schema_migrations(
|
|
@@ -37,8 +67,12 @@ def get_connection():
|
|
|
37
67
|
|
|
38
68
|
def wal_checkpoint(conn):
|
|
39
69
|
"""
|
|
40
|
-
|
|
70
|
+
Executa checkpoint do WAL.
|
|
71
|
+
|
|
72
|
+
Move os dados do arquivo `.wal` para o banco principal
|
|
73
|
+
e reduz seu tamanho.
|
|
41
74
|
"""
|
|
75
|
+
|
|
42
76
|
conn.execute("PRAGMA wal_checkpoint(TRUNCATE)")
|
|
43
77
|
|
|
44
78
|
|
|
@@ -48,7 +82,10 @@ def wal_checkpoint(conn):
|
|
|
48
82
|
|
|
49
83
|
def backup_database(conn):
|
|
50
84
|
"""
|
|
51
|
-
|
|
85
|
+
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.
|
|
52
89
|
"""
|
|
53
90
|
|
|
54
91
|
now = datetime.now().strftime("%Y%m%d")
|
mtcli/marketdata/tick_cache.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Cache de ticks em memória.
|
|
3
|
+
|
|
4
|
+
Mantém uma janela recente de ticks para acesso rápido
|
|
5
|
+
sem necessidade de consultar o banco SQLite.
|
|
3
6
|
"""
|
|
4
7
|
|
|
5
8
|
from collections import deque
|
|
@@ -7,26 +10,43 @@ from collections import deque
|
|
|
7
10
|
|
|
8
11
|
class TickCache:
|
|
9
12
|
"""
|
|
10
|
-
Mantém janela
|
|
13
|
+
Mantém uma janela deslizante de ticks em memória.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
max_size : int
|
|
18
|
+
Número máximo de ticks armazenados no cache.
|
|
11
19
|
"""
|
|
12
20
|
|
|
13
21
|
def __init__(self, max_size=10000):
|
|
14
22
|
self.buffer = deque(maxlen=max_size)
|
|
15
23
|
|
|
16
24
|
def add_many(self, ticks):
|
|
25
|
+
"""
|
|
26
|
+
Adiciona múltiplos ticks ao cache.
|
|
27
|
+
"""
|
|
17
28
|
|
|
18
29
|
for t in ticks:
|
|
19
30
|
self.buffer.append(t)
|
|
20
31
|
|
|
21
32
|
def add(self, tick):
|
|
33
|
+
"""
|
|
34
|
+
Adiciona um único tick ao cache.
|
|
35
|
+
"""
|
|
22
36
|
|
|
23
37
|
self.buffer.append(tick)
|
|
24
38
|
|
|
25
39
|
def get_all(self):
|
|
40
|
+
"""
|
|
41
|
+
Retorna todos os ticks do cache.
|
|
42
|
+
"""
|
|
26
43
|
|
|
27
44
|
return list(self.buffer)
|
|
28
45
|
|
|
29
46
|
def get_last(self):
|
|
47
|
+
"""
|
|
48
|
+
Retorna o último tick armazenado.
|
|
49
|
+
"""
|
|
30
50
|
|
|
31
51
|
if self.buffer:
|
|
32
52
|
return self.buffer[-1]
|
|
@@ -34,5 +54,8 @@ class TickCache:
|
|
|
34
54
|
return None
|
|
35
55
|
|
|
36
56
|
def clear(self):
|
|
57
|
+
"""
|
|
58
|
+
Limpa o cache.
|
|
59
|
+
"""
|
|
37
60
|
|
|
38
61
|
self.buffer.clear()
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Motor de captura de ticks multi-ativo.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
import threading
|
|
7
|
+
import MetaTrader5 as mt5
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
|
|
10
|
+
from mtcli.mt5_context import mt5_conexao
|
|
11
|
+
from .tick_repository import TickRepository
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TickEngine:
|
|
15
|
+
|
|
16
|
+
POLL_INTERVAL = 0.2
|
|
17
|
+
|
|
18
|
+
def __init__(self, symbols):
|
|
19
|
+
|
|
20
|
+
self.symbols = symbols
|
|
21
|
+
|
|
22
|
+
self.repositories = {
|
|
23
|
+
symbol: TickRepository()
|
|
24
|
+
for symbol in symbols
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
self.running = False
|
|
28
|
+
|
|
29
|
+
def start(self):
|
|
30
|
+
|
|
31
|
+
thread = threading.Thread(
|
|
32
|
+
target=self._run,
|
|
33
|
+
daemon=True
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
self.running = True
|
|
37
|
+
thread.start()
|
|
38
|
+
|
|
39
|
+
def stop(self):
|
|
40
|
+
|
|
41
|
+
self.running = False
|
|
42
|
+
|
|
43
|
+
def _run(self):
|
|
44
|
+
|
|
45
|
+
with mt5_conexao():
|
|
46
|
+
|
|
47
|
+
last_positions = {}
|
|
48
|
+
|
|
49
|
+
for symbol in self.symbols:
|
|
50
|
+
|
|
51
|
+
repo = self.repositories[symbol]
|
|
52
|
+
last_msc = repo._get_last_tick_msc(symbol)
|
|
53
|
+
|
|
54
|
+
if last_msc:
|
|
55
|
+
last_positions[symbol] = last_msc + 1
|
|
56
|
+
else:
|
|
57
|
+
last_positions[symbol] = int(time.time() * 1000)
|
|
58
|
+
|
|
59
|
+
while self.running:
|
|
60
|
+
|
|
61
|
+
for symbol in self.symbols:
|
|
62
|
+
|
|
63
|
+
repo = self.repositories[symbol]
|
|
64
|
+
|
|
65
|
+
start = last_positions[symbol]
|
|
66
|
+
|
|
67
|
+
ticks = mt5.copy_ticks_from(
|
|
68
|
+
symbol,
|
|
69
|
+
datetime.fromtimestamp(start / 1000),
|
|
70
|
+
1000,
|
|
71
|
+
mt5.COPY_TICKS_ALL
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if ticks is None or len(ticks) == 0:
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
repo.conn.execute("BEGIN")
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
|
|
81
|
+
repo._insert_ticks(symbol, ticks)
|
|
82
|
+
|
|
83
|
+
repo.cache.add_many(ticks)
|
|
84
|
+
|
|
85
|
+
repo.conn.commit()
|
|
86
|
+
|
|
87
|
+
except Exception:
|
|
88
|
+
|
|
89
|
+
repo.conn.rollback()
|
|
90
|
+
|
|
91
|
+
last_positions[symbol] = int(ticks[-1]["time_msc"]) + 1
|
|
92
|
+
|
|
93
|
+
time.sleep(self.POLL_INTERVAL)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
|
-
TickRepository
|
|
2
|
+
TickRepository.
|
|
3
3
|
|
|
4
4
|
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
9
|
"""
|
|
9
10
|
|
|
10
11
|
import MetaTrader5 as mt5
|
|
@@ -32,6 +33,9 @@ class TickRepository:
|
|
|
32
33
|
# ==========================================================
|
|
33
34
|
|
|
34
35
|
def sync(self, symbol: str, days_back: int = 1):
|
|
36
|
+
"""
|
|
37
|
+
Sincroniza histórico de ticks a partir do broker.
|
|
38
|
+
"""
|
|
35
39
|
|
|
36
40
|
total_inserted = 0
|
|
37
41
|
|
|
@@ -40,7 +44,7 @@ class TickRepository:
|
|
|
40
44
|
last_msc = self._get_last_tick_msc(symbol)
|
|
41
45
|
|
|
42
46
|
if last_msc:
|
|
43
|
-
start = datetime.fromtimestamp(last_msc / 1000)
|
|
47
|
+
start = datetime.fromtimestamp((last_msc + 1) / 1000)
|
|
44
48
|
else:
|
|
45
49
|
start = datetime.now() - timedelta(days=days_back)
|
|
46
50
|
|
|
@@ -68,7 +72,7 @@ class TickRepository:
|
|
|
68
72
|
|
|
69
73
|
last_msc = int(ticks[-1]["time_msc"])
|
|
70
74
|
|
|
71
|
-
start = datetime.fromtimestamp(last_msc / 1000)
|
|
75
|
+
start = datetime.fromtimestamp((last_msc + 1) / 1000)
|
|
72
76
|
|
|
73
77
|
if len(ticks) < self.BATCH_SIZE:
|
|
74
78
|
break
|
|
@@ -106,23 +110,24 @@ class TickRepository:
|
|
|
106
110
|
|
|
107
111
|
cursor.executemany(
|
|
108
112
|
"""
|
|
109
|
-
INSERT
|
|
110
|
-
|
|
113
|
+
INSERT INTO ticks(
|
|
114
|
+
symbol,time,time_msc,bid,ask,last,volume,flags
|
|
115
|
+
)
|
|
116
|
+
VALUES (?,?,?,?,?,?,?,?)
|
|
117
|
+
ON CONFLICT(symbol,time) DO NOTHING
|
|
111
118
|
""",
|
|
112
119
|
data,
|
|
113
120
|
)
|
|
114
121
|
|
|
115
|
-
inserted =
|
|
122
|
+
inserted = len(data)
|
|
116
123
|
|
|
117
124
|
self.insert_counter += inserted
|
|
118
125
|
|
|
119
|
-
# checkpoint periódico
|
|
120
126
|
if self.insert_counter >= 200000:
|
|
121
127
|
|
|
122
128
|
wal_checkpoint(self.conn)
|
|
123
129
|
self.insert_counter = 0
|
|
124
130
|
|
|
125
|
-
# backup diário
|
|
126
131
|
today = datetime.now().date()
|
|
127
132
|
|
|
128
133
|
if self.last_backup_day != today:
|
|
@@ -136,13 +141,34 @@ class TickRepository:
|
|
|
136
141
|
# CONSULTAS
|
|
137
142
|
# ==========================================================
|
|
138
143
|
|
|
144
|
+
def get_last_ticks(self, symbol, limit=5000):
|
|
145
|
+
|
|
146
|
+
cursor = self.conn.cursor()
|
|
147
|
+
|
|
148
|
+
cursor.execute(
|
|
149
|
+
"""
|
|
150
|
+
SELECT time_msc,bid,ask,last,volume,flags
|
|
151
|
+
FROM ticks
|
|
152
|
+
WHERE symbol = ?
|
|
153
|
+
ORDER BY time_msc DESC
|
|
154
|
+
LIMIT ?
|
|
155
|
+
""",
|
|
156
|
+
(symbol, limit),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
rows = cursor.fetchall()
|
|
160
|
+
|
|
161
|
+
rows.reverse()
|
|
162
|
+
|
|
163
|
+
return rows
|
|
164
|
+
|
|
139
165
|
def get_ticks_between(self, symbol, start_msc, end_msc):
|
|
140
166
|
|
|
141
167
|
cursor = self.conn.cursor()
|
|
142
168
|
|
|
143
169
|
cursor.execute(
|
|
144
170
|
"""
|
|
145
|
-
SELECT time_msc,
|
|
171
|
+
SELECT time_msc,bid,ask,last,volume,flags
|
|
146
172
|
FROM ticks
|
|
147
173
|
WHERE symbol = ?
|
|
148
174
|
AND time_msc BETWEEN ? AND ?
|
|
@@ -26,7 +26,7 @@ class TickStreamer:
|
|
|
26
26
|
|
|
27
27
|
def start(self):
|
|
28
28
|
"""
|
|
29
|
-
Inicia captura contínua.
|
|
29
|
+
Inicia captura contínua de ticks.
|
|
30
30
|
"""
|
|
31
31
|
|
|
32
32
|
self.running = True
|
|
@@ -36,7 +36,7 @@ class TickStreamer:
|
|
|
36
36
|
last_msc = self.repo._get_last_tick_msc(self.symbol)
|
|
37
37
|
|
|
38
38
|
if last_msc:
|
|
39
|
-
start = last_msc
|
|
39
|
+
start = last_msc + 1
|
|
40
40
|
else:
|
|
41
41
|
start = int(time.time() * 1000)
|
|
42
42
|
|
|
@@ -51,7 +51,7 @@ class TickStreamer:
|
|
|
51
51
|
|
|
52
52
|
if ticks is None or len(ticks) == 0:
|
|
53
53
|
|
|
54
|
-
time.sleep(0.
|
|
54
|
+
time.sleep(0.1)
|
|
55
55
|
continue
|
|
56
56
|
|
|
57
57
|
self.repo.conn.execute("BEGIN")
|
|
@@ -68,7 +68,7 @@ class TickStreamer:
|
|
|
68
68
|
|
|
69
69
|
self.repo.conn.rollback()
|
|
70
70
|
|
|
71
|
-
start = int(ticks[-1]["time_msc"])
|
|
71
|
+
start = int(ticks[-1]["time_msc"]) + 1
|
|
72
72
|
|
|
73
73
|
def stop(self):
|
|
74
74
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
mtcli/__init__.py,sha256=guFO5EmCfuH8MNujJH8hw_oPg8Ee__QlQgVd-4A3N6o,84
|
|
2
|
-
mtcli/cli.py,sha256=
|
|
2
|
+
mtcli/cli.py,sha256=RIrJZgpwqR_1ciTdg_Vs92PF1tuVvHaMG-IgYMqGkmY,2202
|
|
3
3
|
mtcli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
mtcli/commands/bars.py,sha256=JXXq9HIeFU-VKf8e2rOKTMXx_h5SRcuvKER-mlOjxjM,4205
|
|
5
5
|
mtcli/commands/conf.py,sha256=nYN3dk9hZVagkf3mP41yBdKUuCKlvzgaF8DHGKbG8OU,866
|
|
@@ -12,14 +12,15 @@ mtcli/data/__init__.py,sha256=NMzgUoiyY5OUFJkmR-OCFF2fOTY9vp-64z8-bh7HEcs,64
|
|
|
12
12
|
mtcli/data/base.py,sha256=Hv1r1IlIhFVEhINWz90FZptYIdhyBKxhMBn5odJfT_8,314
|
|
13
13
|
mtcli/data/csv.py,sha256=P007I9SoK51-lD_OXXjZu4NkKaGgNr2HRQ3pc_pS1XQ,1008
|
|
14
14
|
mtcli/data/mt5.py,sha256=wsF14Vm5xXw0QbjHjC-tvXjjMADQWCrhkJ6ozbDLbzk,2880
|
|
15
|
-
mtcli/database.py,sha256=
|
|
15
|
+
mtcli/database.py,sha256=P4MFZ2ytfhwN2rOe3jMSWxE26Eki8UHxa--a--0SayE,2480
|
|
16
16
|
mtcli/domain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
mtcli/domain/timeframe.py,sha256=nG7vB01d1TyhInHaIZgCPYyVhbiklMIOSgxZnJ7MI6Q,2034
|
|
18
18
|
mtcli/logger.py,sha256=fEj_meH9-H7CBm1qf1FYhs6QodP7KuYO4ptCyr8WlOc,3677
|
|
19
19
|
mtcli/marketdata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
-
mtcli/marketdata/tick_cache.py,sha256=
|
|
21
|
-
mtcli/marketdata/
|
|
22
|
-
mtcli/marketdata/
|
|
20
|
+
mtcli/marketdata/tick_cache.py,sha256=T-auTfkhk5s-TZkaVaTuJGLEnaoxyED7yPr6CGyUDfM,1191
|
|
21
|
+
mtcli/marketdata/tick_engine.py,sha256=EySvnBVAG2kHu5NMT3fOajzV5GDR6i3Wb3mAUMzvQ0w,2156
|
|
22
|
+
mtcli/marketdata/tick_repository.py,sha256=SfQplU73ScSLQ0v7Uy-OAx7tQIWp2I7G7DBap64k768,5042
|
|
23
|
+
mtcli/marketdata/tick_streamer.py,sha256=K0zcpNEqc1zpFfc4YKl9tJcvOdWzXWO78D9C6A1zDuo,1647
|
|
23
24
|
mtcli/migrations/001_initial_schema.py,sha256=Xve9r9RRAOtoZQ2iOaD-gdTfxSjaqoaCwGHn_ZSUtlg,439
|
|
24
25
|
mtcli/migrations/002_ticks_time_msc.py,sha256=OKpxZZlb5QEKzVaYAt3kgi2vHu5CRoxLHQ4RRzYTX4E,420
|
|
25
26
|
mtcli/migrations/003_optimize_ticks_without_rowid.py,sha256=AyOUJA1SvEhNVXnFRpz2FI4hKk1PutHuC6FSneceR4s,1234
|
|
@@ -73,8 +74,8 @@ mtcli/views/ranges_view.py,sha256=5gA2bAJuco-Xj964nsjs87tZ-079O7xKvaeif1UJ-PQ,12
|
|
|
73
74
|
mtcli/views/rates_view.py,sha256=Jkdbg-Q_OVJuID-Q9HFUVICCF1jE82d-qfZWNm1JJ_4,1214
|
|
74
75
|
mtcli/views/vars_view.py,sha256=Vpcej41qnZSvvSyvXYyW8W7CEFdqTxBd8NczV84XPJY,1635
|
|
75
76
|
mtcli/views/volumes_view.py,sha256=d_6YxM0vA1revwoPwnwUjV19KcqnddVgdpBHTt5Vqms,1575
|
|
76
|
-
mtcli-3.8.0.
|
|
77
|
-
mtcli-3.8.0.
|
|
78
|
-
mtcli-3.8.0.
|
|
79
|
-
mtcli-3.8.0.
|
|
80
|
-
mtcli-3.8.0.
|
|
77
|
+
mtcli-3.8.0.dev2.dist-info/entry_points.txt,sha256=EH8lMNy2L-TCJB3E3EJHF5QySz_b_86oOhcAKAN20-Q,103
|
|
78
|
+
mtcli-3.8.0.dev2.dist-info/licenses/LICENSE,sha256=Z-2ANeRgM9IjXnHeg9mA2gillM6eTQj8sIExAGNe2-8,1092
|
|
79
|
+
mtcli-3.8.0.dev2.dist-info/METADATA,sha256=znrOc5VBOdqB47AIvdoK0twd9YhgJRngCLZqSagHS6k,3654
|
|
80
|
+
mtcli-3.8.0.dev2.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
81
|
+
mtcli-3.8.0.dev2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|