mtcli 3.5.0.dev2__tar.gz → 3.5.1__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 (68) hide show
  1. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/PKG-INFO +2 -2
  2. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/database.py +54 -54
  3. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/marketdata/tick_cache.py +24 -24
  4. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/marketdata/tick_repository.py +142 -126
  5. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/mt.py +45 -45
  6. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/pyproject.toml +2 -2
  7. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/LICENSE +0 -0
  8. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/README.md +0 -0
  9. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/__init__.py +0 -0
  10. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/commands/__init__.py +0 -0
  11. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/commands/bars.py +0 -0
  12. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/commands/conf.py +0 -0
  13. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/commands/logs.py +0 -0
  14. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/conecta.py +0 -0
  15. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/conf.py +0 -0
  16. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/data/__init__.py +0 -0
  17. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/data/base.py +0 -0
  18. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/data/csv.py +0 -0
  19. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/data/mt5.py +0 -0
  20. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/domain/__init__.py +0 -0
  21. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/domain/timeframe.py +0 -0
  22. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/logger.py +0 -0
  23. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/marketdata/__init__.py +0 -0
  24. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/models/__init__.py +0 -0
  25. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/models/bar_model.py +0 -0
  26. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/models/bars_model.py +0 -0
  27. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/models/chart_model.py +0 -0
  28. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/models/conf_model.py +0 -0
  29. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/models/consecutive_bars_model.py +0 -0
  30. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/models/rates_model.py +0 -0
  31. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/models/signals_model.py +0 -0
  32. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/models/unconsecutive_bar_model.py +0 -0
  33. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/mt5_context.py +0 -0
  34. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugin.py +0 -0
  35. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/__init__.py +0 -0
  36. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/media_movel/__init__.py +0 -0
  37. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/media_movel/command.py +0 -0
  38. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/media_movel/conf.py +0 -0
  39. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/media_movel/models/__init__.py +0 -0
  40. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/media_movel/models/model_media_movel.py +0 -0
  41. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/media_movel/tests/__init__.py +0 -0
  42. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/media_movel/tests/test_mm.py +0 -0
  43. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/media_movel/tests/test_model_media_movel.py +0 -0
  44. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/range_medio/__init__.py +0 -0
  45. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/range_medio/command.py +0 -0
  46. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/range_medio/conf.py +0 -0
  47. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/range_medio/models/__init__.py +0 -0
  48. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/range_medio/models/average_range_model.py +0 -0
  49. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/range_medio/tests/__init__.py +0 -0
  50. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/range_medio/tests/test_rm.py +0 -0
  51. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/volume_medio/__init__.py +0 -0
  52. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/volume_medio/command.py +0 -0
  53. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/volume_medio/conf.py +0 -0
  54. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/volume_medio/models/__init__.py +0 -0
  55. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/volume_medio/models/model_average_volume.py +0 -0
  56. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/volume_medio/tests/__init__.py +0 -0
  57. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/plugins/volume_medio/tests/test_vm.py +0 -0
  58. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/views/__init__.py +0 -0
  59. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/views/close_view.py +0 -0
  60. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/views/full_view.py +0 -0
  61. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/views/high_view.py +0 -0
  62. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/views/low_view.py +0 -0
  63. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/views/min_view.py +0 -0
  64. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/views/open_view.py +0 -0
  65. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/views/ranges_view.py +0 -0
  66. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/views/rates_view.py +0 -0
  67. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/views/vars_view.py +0 -0
  68. {mtcli-3.5.0.dev2 → mtcli-3.5.1}/mtcli/views/volumes_view.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mtcli
3
- Version: 3.5.0.dev2
3
+ Version: 3.5.1
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
@@ -8,7 +8,7 @@ Keywords: trading,candlestick,metatrader5,mt5,cli,price-action
8
8
  Author: Valmir França da Silva
9
9
  Author-email: vfranca3@gmail.com
10
10
  Requires-Python: >=3.10,<3.14.0
11
- Classifier: Development Status :: 4 - Beta
11
+ Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Financial and Insurance Industry
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Programming Language :: Python :: 3.10
@@ -1,54 +1,54 @@
1
- """
2
- Database core para mtcli.
3
-
4
- Responsável por:
5
- - Criar conexão SQLite
6
- - Ativar WAL
7
- - Garantir schema
8
- """
9
-
10
- import sqlite3
11
- from pathlib import Path
12
-
13
-
14
- DB_PATH = Path.home() / ".mtcli" / "marketdata.db"
15
-
16
-
17
- def get_connection():
18
- """
19
- Retorna conexão SQLite configurada.
20
- """
21
- DB_PATH.parent.mkdir(parents=True, exist_ok=True)
22
-
23
- conn = sqlite3.connect(DB_PATH)
24
- conn.execute("PRAGMA journal_mode=WAL;")
25
- conn.execute("PRAGMA synchronous=NORMAL;")
26
-
27
- _ensure_schema(conn)
28
- return conn
29
-
30
-
31
- def _ensure_schema(conn):
32
- conn.execute(
33
- """
34
- CREATE TABLE IF NOT EXISTS ticks (
35
- symbol TEXT NOT NULL,
36
- time INTEGER NOT NULL,
37
- bid REAL,
38
- ask REAL,
39
- last REAL,
40
- volume REAL,
41
- flags INTEGER,
42
- PRIMARY KEY (symbol, time)
43
- );
44
- """
45
- )
46
-
47
- conn.execute(
48
- """
49
- CREATE INDEX IF NOT EXISTS idx_ticks_symbol_time
50
- ON ticks(symbol, time);
51
- """
52
- )
53
-
54
- conn.commit()
1
+ """
2
+ Database core para mtcli.
3
+
4
+ Responsável por:
5
+ - Criar conexão SQLite
6
+ - Ativar WAL
7
+ - Garantir schema
8
+ """
9
+
10
+ import sqlite3
11
+ from pathlib import Path
12
+
13
+
14
+ DB_PATH = Path.home() / ".mtcli" / "marketdata.db"
15
+
16
+
17
+ def get_connection():
18
+ """
19
+ Retorna conexão SQLite configurada.
20
+ """
21
+ DB_PATH.parent.mkdir(parents=True, exist_ok=True)
22
+
23
+ conn = sqlite3.connect(DB_PATH)
24
+ conn.execute("PRAGMA journal_mode=WAL;")
25
+ conn.execute("PRAGMA synchronous=NORMAL;")
26
+
27
+ _ensure_schema(conn)
28
+ return conn
29
+
30
+
31
+ def _ensure_schema(conn):
32
+ conn.execute(
33
+ """
34
+ CREATE TABLE IF NOT EXISTS ticks (
35
+ symbol TEXT NOT NULL,
36
+ time INTEGER NOT NULL,
37
+ bid REAL,
38
+ ask REAL,
39
+ last REAL,
40
+ volume REAL,
41
+ flags INTEGER,
42
+ PRIMARY KEY (symbol, time)
43
+ );
44
+ """
45
+ )
46
+
47
+ conn.execute(
48
+ """
49
+ CREATE INDEX IF NOT EXISTS idx_ticks_symbol_time
50
+ ON ticks(symbol, time);
51
+ """
52
+ )
53
+
54
+ conn.commit()
@@ -1,24 +1,24 @@
1
- """
2
- Cache de ticks em memória.
3
- """
4
-
5
- from collections import deque
6
-
7
-
8
- class TickCache:
9
- """
10
- Mantém janela recente de ticks em memória.
11
- """
12
-
13
- def __init__(self, max_size=10000):
14
- self.buffer = deque(maxlen=max_size)
15
-
16
- def add_many(self, ticks):
17
- for t in ticks:
18
- self.buffer.append(t)
19
-
20
- def get_all(self):
21
- return list(self.buffer)
22
-
23
- def clear(self):
24
- self.buffer.clear()
1
+ """
2
+ Cache de ticks em memória.
3
+ """
4
+
5
+ from collections import deque
6
+
7
+
8
+ class TickCache:
9
+ """
10
+ Mantém janela recente de ticks em memória.
11
+ """
12
+
13
+ def __init__(self, max_size=10000):
14
+ self.buffer = deque(maxlen=max_size)
15
+
16
+ def add_many(self, ticks):
17
+ for t in ticks:
18
+ self.buffer.append(t)
19
+
20
+ def get_all(self):
21
+ return list(self.buffer)
22
+
23
+ def clear(self):
24
+ self.buffer.clear()
@@ -1,126 +1,142 @@
1
- """
2
- TickRepository centralizado.
3
-
4
- Responsável por:
5
- - Sincronizar com MT5 via mt5_conexao
6
- - Persistir ticks corretamente (structured array numpy)
7
- - Fornecer ticks para modelos
8
- """
9
-
10
- import MetaTrader5 as mt5
11
- from datetime import datetime, timedelta
12
-
13
- from ..database import get_connection
14
- from .tick_cache import TickCache
15
- from mtcli.mt5_context import mt5_conexao
16
-
17
-
18
- class TickRepository:
19
-
20
- def __init__(self):
21
- self.conn = get_connection()
22
- self.cache = TickCache()
23
-
24
- # ============================================================
25
- # SINCRONIZAÇÃO INCREMENTAL
26
- # ============================================================
27
-
28
- def sync(self, symbol: str, days_back: int = 1):
29
- """
30
- Sincroniza banco com MT5.
31
-
32
- Busca apenas ticks após o último armazenado.
33
- """
34
-
35
- with mt5_conexao():
36
-
37
- last_time = self._get_last_tick_time(symbol)
38
-
39
- if last_time:
40
- start = datetime.fromtimestamp(last_time)
41
- else:
42
- start = datetime.now() - timedelta(days=days_back)
43
-
44
- ticks = mt5.copy_ticks_from(
45
- symbol,
46
- start,
47
- 200000,
48
- mt5.COPY_TICKS_ALL,
49
- )
50
-
51
- if ticks is None or len(ticks) == 0:
52
- return 0
53
-
54
- inserted = self._insert_ticks(symbol, ticks)
55
- self.cache.add_many(ticks)
56
-
57
- return inserted
58
-
59
- # ============================================================
60
- # INSERÇÃO
61
- # ============================================================
62
-
63
- def _insert_ticks(self, symbol, ticks):
64
- """
65
- Insere ticks no SQLite.
66
-
67
- O retorno do MT5 é numpy structured array.
68
- Acesso deve ser por campo: t["time"], t["last"], etc.
69
- """
70
-
71
- cursor = self.conn.cursor()
72
-
73
- data = [
74
- (
75
- symbol,
76
- int(t["time"]),
77
- float(t["bid"]),
78
- float(t["ask"]),
79
- float(t["last"]),
80
- float(t["volume"]),
81
- int(t["flags"]),
82
- )
83
- for t in ticks
84
- ]
85
-
86
- cursor.executemany(
87
- """
88
- INSERT OR IGNORE INTO ticks
89
- VALUES (?, ?, ?, ?, ?, ?, ?)
90
- """,
91
- data,
92
- )
93
-
94
- self.conn.commit()
95
- return cursor.rowcount
96
-
97
- # ============================================================
98
- # CONSULTAS
99
- # ============================================================
100
-
101
- def get_ticks_between(self, symbol, start_ts, end_ts):
102
- cursor = self.conn.cursor()
103
- cursor.execute(
104
- """
105
- SELECT time, bid, ask, last, volume, flags
106
- FROM ticks
107
- WHERE symbol = ?
108
- AND time BETWEEN ? AND ?
109
- ORDER BY time ASC
110
- """,
111
- (symbol, start_ts, end_ts),
112
- )
113
- return cursor.fetchall()
114
-
115
- def _get_last_tick_time(self, symbol):
116
- cursor = self.conn.cursor()
117
- cursor.execute(
118
- """
119
- SELECT MAX(time)
120
- FROM ticks
121
- WHERE symbol = ?
122
- """,
123
- (symbol,),
124
- )
125
- result = cursor.fetchone()
126
- return result[0] if result and result[0] else None
1
+ """
2
+ TickRepository profissional com:
3
+
4
+ - Paginação automática
5
+ - Sincronização incremental robusta
6
+ - Integração com mt5_conexao
7
+ - Proteção contra loops infinitos
8
+ """
9
+
10
+ import MetaTrader5 as mt5
11
+ from datetime import datetime, timedelta
12
+
13
+ from ..database import get_connection
14
+ from .tick_cache import TickCache
15
+ from mtcli.mt5_context import mt5_conexao
16
+
17
+
18
+ class TickRepository:
19
+
20
+ BATCH_SIZE = 200000 # tamanho seguro por lote
21
+
22
+ def __init__(self):
23
+ self.conn = get_connection()
24
+ self.cache = TickCache()
25
+
26
+ # ============================================================
27
+ # SINCRONIZAÇÃO COM PAGINAÇÃO
28
+ # ============================================================
29
+
30
+ def sync(self, symbol: str, days_back: int = 1):
31
+ """
32
+ Sincroniza banco com MT5 usando paginação.
33
+
34
+ Busca todos os ticks disponíveis desde o último timestamp.
35
+ """
36
+
37
+ total_inserted = 0
38
+
39
+ with mt5_conexao():
40
+
41
+ last_time = self._get_last_tick_time(symbol)
42
+
43
+ if last_time:
44
+ start = datetime.fromtimestamp(last_time + 1)
45
+ else:
46
+ start = datetime.now() - timedelta(days=days_back)
47
+
48
+ while True:
49
+
50
+ ticks = mt5.copy_ticks_from(
51
+ symbol,
52
+ start,
53
+ self.BATCH_SIZE,
54
+ mt5.COPY_TICKS_ALL,
55
+ )
56
+
57
+ if ticks is None or len(ticks) == 0:
58
+ break
59
+
60
+ inserted = self._insert_ticks(symbol, ticks)
61
+ total_inserted += inserted
62
+
63
+ self.cache.add_many(ticks)
64
+
65
+ # Atualiza início para próximo tick
66
+ ultimo_timestamp = int(ticks[-1]["time"])
67
+ novo_start = datetime.fromtimestamp(ultimo_timestamp + 1)
68
+
69
+ # Proteção contra loop infinito
70
+ if novo_start <= start:
71
+ break
72
+
73
+ start = novo_start
74
+
75
+ # Se retornou menos que o batch, acabou histórico
76
+ if len(ticks) < self.BATCH_SIZE:
77
+ break
78
+
79
+ return total_inserted
80
+
81
+ # ============================================================
82
+ # INSERÇÃO
83
+ # ============================================================
84
+
85
+ def _insert_ticks(self, symbol, ticks):
86
+
87
+ cursor = self.conn.cursor()
88
+
89
+ data = [
90
+ (
91
+ symbol,
92
+ int(t["time"]),
93
+ float(t["bid"]),
94
+ float(t["ask"]),
95
+ float(t["last"]),
96
+ float(t["volume"]),
97
+ int(t["flags"]),
98
+ )
99
+ for t in ticks
100
+ ]
101
+
102
+ cursor.executemany(
103
+ """
104
+ INSERT OR IGNORE INTO ticks
105
+ VALUES (?, ?, ?, ?, ?, ?, ?)
106
+ """,
107
+ data,
108
+ )
109
+
110
+ self.conn.commit()
111
+ return cursor.rowcount
112
+
113
+ # ============================================================
114
+ # CONSULTAS
115
+ # ============================================================
116
+
117
+ def get_ticks_between(self, symbol, start_ts, end_ts):
118
+ cursor = self.conn.cursor()
119
+ cursor.execute(
120
+ """
121
+ SELECT time, bid, ask, last, volume, flags
122
+ FROM ticks
123
+ WHERE symbol = ?
124
+ AND time BETWEEN ? AND ?
125
+ ORDER BY time ASC
126
+ """,
127
+ (symbol, start_ts, end_ts),
128
+ )
129
+ return cursor.fetchall()
130
+
131
+ def _get_last_tick_time(self, symbol):
132
+ cursor = self.conn.cursor()
133
+ cursor.execute(
134
+ """
135
+ SELECT MAX(time)
136
+ FROM ticks
137
+ WHERE symbol = ?
138
+ """,
139
+ (symbol,),
140
+ )
141
+ result = cursor.fetchone()
142
+ return result[0] if result and result[0] else None
@@ -1,45 +1,45 @@
1
- """Módulo principal do aplicativo."""
2
-
3
- import click
4
-
5
- try:
6
- from importlib.metadata import entry_points
7
- except ImportError:
8
- from importlib_metadata import entry_points # Para Python < 3.8
9
-
10
-
11
- @click.group(context_settings={"max_content_width": 120})
12
- @click.version_option(package_name="mtcli")
13
- def mt():
14
- """Exibe o grafico candlestick screen reader friendly."""
15
- pass
16
-
17
-
18
- def load_plugins():
19
- eps = entry_points()
20
- plugins = (
21
- eps.select(group="mtcli.plugins")
22
- if hasattr(eps, "select")
23
- else eps.get("mtcli.plugins", [])
24
- )
25
-
26
- for ep in plugins:
27
- plugin = ep.load()
28
-
29
- # Caso seja uma função register(cli)
30
- if callable(plugin) and not isinstance(plugin, click.Command):
31
- plugin(mt) # chama register(cli)
32
- # Caso seja um comando Click diretamente
33
- elif isinstance(plugin, click.Command):
34
- mt.add_command(plugin)
35
- else:
36
- raise TypeError(
37
- f"Plugin {ep.name} inválido: não é um comando nem função register."
38
- )
39
-
40
-
41
- load_plugins()
42
-
43
-
44
- if __name__ == "__main__":
45
- mt()
1
+ """Módulo principal do aplicativo."""
2
+
3
+ import click
4
+
5
+ try:
6
+ from importlib.metadata import entry_points
7
+ except ImportError:
8
+ from importlib_metadata import entry_points # Para Python < 3.8
9
+
10
+
11
+ @click.group(context_settings={"max_content_width": 120})
12
+ @click.version_option(package_name="mtcli")
13
+ def mt():
14
+ """Exibe o grafico candlestick screen reader friendly."""
15
+ pass
16
+
17
+
18
+ def load_plugins():
19
+ eps = entry_points()
20
+ plugins = (
21
+ eps.select(group="mtcli.plugins")
22
+ if hasattr(eps, "select")
23
+ else eps.get("mtcli.plugins", [])
24
+ )
25
+
26
+ for ep in plugins:
27
+ plugin = ep.load()
28
+
29
+ # Caso seja uma função register(cli)
30
+ if callable(plugin) and not isinstance(plugin, click.Command):
31
+ plugin(mt) # chama register(cli)
32
+ # Caso seja um comando Click diretamente
33
+ elif isinstance(plugin, click.Command):
34
+ mt.add_command(plugin)
35
+ else:
36
+ raise TypeError(
37
+ f"Plugin {ep.name} inválido: não é um comando nem função register."
38
+ )
39
+
40
+
41
+ load_plugins()
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mt()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mtcli"
3
- version = "3.5.0.dev2"
3
+ version = "3.5.1"
4
4
  description = "Aplicativo CLI para exibir gráficos do MetaTrader 5 screen reader friendly"
5
5
  authors = [
6
6
  {name = "Valmir França da Silva",email = "vfranca3@gmail.com"}
@@ -20,7 +20,7 @@ keywords = [
20
20
  ]
21
21
 
22
22
  classifiers = [
23
- "Development Status :: 4 - Beta",
23
+ "Development Status :: 5 - Production/Stable",
24
24
  "Intended Audience :: Financial and Insurance Industry",
25
25
  "Programming Language :: Python :: 3",
26
26
  "Programming Language :: Python :: 3.10",
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