History Collection (SQLite)¶
mt5cli.history ¶
SQLite storage helpers for the collect-history incremental data pipeline.
DEFAULT_HISTORY_TIMEFRAMES
module-attribute
¶
DEFAULT_HISTORY_TIMEFRAMES: tuple[str, ...] = tuple(
TIMEFRAME_MAP
)
append_dataframe ¶
append_dataframe(
conn: Connection,
frame: DataFrame,
table_name: str,
if_exists: IfExists,
) -> bool
Append a DataFrame to SQLite when it has a schema.
Returns:
| Type | Description |
|---|---|
bool
|
True if a table was written, False if the frame had no columns. |
Source code in mt5cli/history.py
augment_written_columns_from_sqlite ¶
augment_written_columns_from_sqlite(
conn: Connection,
datasets: set[Dataset],
written_columns: dict[Dataset, set[str]],
) -> None
Add existing table columns to the written column map.
Source code in mt5cli/history.py
build_rate_view_name ¶
build_rate_view_name(
*,
symbol: str,
granularity: str,
granularity_count: int,
timeframe: int,
) -> str
Return a collision-free offline optimize view name.
View names always include the timeframe integer after a __ separator so
a symbol such as EURUSD_M1 cannot collide with EURUSD at timeframe
M1.
Source code in mt5cli/history.py
create_cash_events_view ¶
Create the cash_events SQLite view derived from history_deals.
Returns:
| Type | Description |
|---|---|
bool
|
True if the view was created, False if required columns are missing. |
Source code in mt5cli/history.py
create_history_indexes ¶
create_history_indexes(
conn: Connection,
written_columns: dict[Dataset, set[str]],
) -> None
Create useful indexes for collected history tables when present.
Source code in mt5cli/history.py
create_positions_reconstructed_view ¶
Create the positions_reconstructed SQLite view derived from history_deals.
Returns:
| Type | Description |
|---|---|
bool
|
True if the view was created, False if required columns are missing. |
Source code in mt5cli/history.py
create_rate_compatibility_views ¶
Create rate compatibility views from the normalized rates table.
Source code in mt5cli/history.py
deduplicate_history_tables ¶
deduplicate_history_tables(
conn: Connection,
written_columns: dict[Dataset, set[str]],
written_tables: set[Dataset],
dedup_scopes: dict[Dataset, list[DedupScope]]
| None = None,
) -> None
Deduplicate appended history tables by stable identifiers.
Source code in mt5cli/history.py
drop_duplicates_in_table ¶
drop_duplicates_in_table(
cursor: Cursor,
table: str,
ids: list[str],
*,
keep: Literal["first", "last"] = "last",
scope_where: str | None = None,
scope_params: tuple[object, ...] = (),
) -> None
Remove duplicate rows, keeping the first or last ROWID per key group.
Raises:
| Type | Description |
|---|---|
ValueError
|
If the table or column names are invalid. |
Source code in mt5cli/history.py
drop_rate_compatibility_views ¶
Drop all mt5cli-managed rate_* compatibility views.
Source code in mt5cli/history.py
filter_incremental_history_deals_frame ¶
filter_incremental_history_deals_frame(
frame: DataFrame,
symbols: Sequence[str],
start_by_symbol: dict[str, datetime],
account_event_start: datetime,
) -> DataFrame
Filter incrementally fetched history_deals by symbol and event start times.
Returns:
| Type | Description |
|---|---|
DataFrame
|
Rows for selected symbols at or after each symbol start, plus account |
DataFrame
|
events at or after |
Source code in mt5cli/history.py
filter_trade_history_frame ¶
filter_trade_history_frame(
frame: DataFrame,
symbols: Sequence[str],
*,
include_account_events: bool,
) -> DataFrame
Filter trade history rows to selected symbols and account events.
Returns:
| Type | Description |
|---|---|
DataFrame
|
Filtered history rows. |
Source code in mt5cli/history.py
get_history_deals_account_event_start_datetime ¶
get_history_deals_account_event_start_datetime(
conn: Connection, *, fallback_start: datetime
) -> datetime
Return the next update start for account-level history_deals rows.
Source code in mt5cli/history.py
get_incremental_start_datetime ¶
get_incremental_start_datetime(
conn: Connection,
dataset: Dataset,
*,
symbol: str,
timeframe: int | None,
fallback_start: datetime,
) -> datetime
Return the next update start datetime from existing MAX(time).
Source code in mt5cli/history.py
get_table_columns ¶
Return existing SQLite columns for a table.
load_incremental_start_datetimes ¶
load_incremental_start_datetimes(
conn: Connection,
dataset: Dataset,
*,
symbols: Sequence[str],
timeframes: Sequence[int] | None = None,
fallback_start: datetime,
) -> dict[tuple[str, int | None], datetime]
Return next update start datetimes keyed by symbol and optional timeframe.
Source code in mt5cli/history.py
parse_sqlite_timestamp ¶
Parse a SQLite history timestamp value.
Returns:
| Type | Description |
|---|---|
datetime | None
|
Parsed timezone-aware datetime, or None when parsing fails. |
Source code in mt5cli/history.py
quote_sqlite_identifier ¶
record_written_columns ¶
record_written_columns(
written_columns: dict[Dataset, set[str]],
dataset: Dataset,
frame: DataFrame,
) -> None
Remember columns for datasets written during collection.
Source code in mt5cli/history.py
resolve_granularity_name ¶
Return a granularity name for a timeframe integer when known.
resolve_history_datasets ¶
resolve_history_tick_flags ¶
Resolve tick copy flags from an integer or name.
Returns:
| Type | Description |
|---|---|
int
|
Integer tick flag value. |
resolve_history_timeframes ¶
Resolve rate timeframes, deduplicating aliases for the same integer.
Returns:
| Type | Description |
|---|---|
list[int]
|
Ordered list of unique timeframe integers. |
Source code in mt5cli/history.py
resolve_rate_view_name ¶
resolve_rate_view_name(
conn_or_path: SqliteConnOrPath,
symbol: str,
granularity: str,
*,
require_existing: bool = False,
) -> str
Resolve the mt5cli-managed rate compatibility view name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conn_or_path
|
SqliteConnOrPath
|
SQLite database path or open connection. |
required |
symbol
|
str
|
Symbol stored in the normalized |
required |
granularity
|
str
|
Timeframe name (for example |
required |
require_existing
|
bool
|
When True, require the database and a managed view to exist. |
False
|
Returns:
| Type | Description |
|---|---|
str
|
View name such as |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in mt5cli/history.py
resolve_rate_view_names ¶
resolve_rate_view_names(
conn_or_path: SqliteConnOrPath,
symbols: Sequence[str],
granularities: Sequence[str],
*,
require_existing: bool = False,
) -> list[str]
Resolve rate compatibility view names for symbol and granularity pairs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conn_or_path
|
SqliteConnOrPath
|
SQLite database path or open connection. |
required |
symbols
|
Sequence[str]
|
Symbols stored in the normalized |
required |
granularities
|
Sequence[str]
|
Timeframe names (for example |
required |
require_existing
|
bool
|
When True, require the database and managed views to exist. |
False
|
Returns:
| Type | Description |
|---|---|
list[str]
|
View names in row-major order: every |
list[str]
|
symbol, then every granularity for the next symbol, and so on. |
Source code in mt5cli/history.py
write_collected_datasets ¶
write_collected_datasets(
conn: Connection,
client: Mt5DataClient,
symbols: Sequence[str],
datasets: set[Dataset],
timeframe: int,
flags: int,
date_from: datetime,
date_to: datetime,
if_exists: IfExists,
) -> tuple[set[Dataset], dict[Dataset, set[str]]]
Collect selected datasets and stream each symbol frame into SQLite.
Returns:
| Type | Description |
|---|---|
tuple[set[Dataset], dict[Dataset, set[str]]]
|
Written datasets and their columns. |
Source code in mt5cli/history.py
1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 | |
write_history_dataset ¶
write_history_dataset(
conn: Connection,
fetch: Callable[..., DataFrame],
dataset: Dataset,
symbols: Sequence[str],
date_from: datetime,
date_to: datetime,
if_exists: IfExists,
written_columns: dict[Dataset, set[str]],
*,
include_account_events: bool = False,
) -> bool
Stream a history dataset into SQLite.
Returns:
| Type | Description |
|---|---|
bool
|
True if the target table was written. |
Source code in mt5cli/history.py
write_incremental_datasets ¶
write_incremental_datasets(
conn: Connection,
client: Mt5DataClient,
symbols: Sequence[str],
selected_datasets: set[Dataset],
resolved_timeframes: list[int],
resolved_tick_flags: int,
fallback_start: datetime,
end_date: datetime,
*,
deduplicate: bool,
create_rate_views: bool,
with_views: bool,
include_account_events: bool,
) -> tuple[set[Dataset], dict[Dataset, set[str]]]
Append selected datasets incrementally and refresh indexes and views.
Returns:
| Type | Description |
|---|---|
tuple[set[Dataset], dict[Dataset, set[str]]]
|
Written datasets and their columns. |
Source code in mt5cli/history.py
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 | |
write_rates_dataset ¶
write_rates_dataset(
conn: Connection,
client: Mt5DataClient,
symbols: Sequence[str],
timeframe: int,
date_from: datetime,
date_to: datetime,
if_exists: IfExists,
written_columns: dict[Dataset, set[str]],
) -> bool
Stream rates frames into SQLite.
Returns:
| Type | Description |
|---|---|
bool
|
True if the rates table was written. |
Source code in mt5cli/history.py
write_streamed_frame ¶
write_streamed_frame(
conn: Connection,
frame: DataFrame,
dataset: Dataset,
table_exists: bool,
if_exists: IfExists,
written_columns: dict[Dataset, set[str]],
) -> bool
Write one streamed dataset frame and track table state.
Returns:
| Type | Description |
|---|---|
bool
|
True if the dataset table exists after this write attempt. |
Source code in mt5cli/history.py
write_ticks_dataset ¶
write_ticks_dataset(
conn: Connection,
client: Mt5DataClient,
symbols: Sequence[str],
flags: int,
date_from: datetime,
date_to: datetime,
if_exists: IfExists,
written_columns: dict[Dataset, set[str]],
) -> bool
Stream ticks frames into SQLite.
Returns:
| Type | Description |
|---|---|
bool
|
True if the ticks table was written. |
Source code in mt5cli/history.py
collect-history schema¶
The collect-history command (and the matching collect_history SDK function) writes
selected MT5 datasets into one SQLite database. Each dataset becomes a table; column
names and types mirror the pdmt5 DataFrame schema for that export, with two additions:
symbolis prepended on every table.timeframeis prepended onratesso appended runs at different bar sizes stay distinguishable.
SQLite does not declare foreign keys. Rows are linked logically by symbol, time
windows, and (for deals) position_id / order. Duplicate rows are removed on
append using dataset-specific keys (for example ticket on history tables, or
(symbol, timeframe, time) on rates).
Optional views are created when --with-views is set and the history-deals dataset
was written.
Entity-relationship diagram¶
Sample layout for a full collection with --with-views:
erDiagram
rates {
TEXT symbol "dedup key"
INTEGER timeframe "dedup key"
TEXT time "dedup key"
REAL open
REAL high
REAL low
REAL close
INTEGER tick_volume
INTEGER spread
INTEGER real_volume
}
ticks {
TEXT symbol "dedup key"
TEXT time "dedup key"
INTEGER time_msc "dedup key (preferred)"
REAL bid
REAL ask
REAL last
INTEGER volume
INTEGER flags
REAL volume_real
}
history_orders {
INTEGER ticket "dedup key"
TEXT symbol
TEXT time
INTEGER type
INTEGER state
REAL volume_initial
REAL price_open
REAL price_current
INTEGER magic
}
history_deals {
INTEGER ticket "dedup key"
INTEGER order
INTEGER position_id "groups position view"
TEXT symbol
TEXT time
INTEGER type "0/1 trade, else cash event"
INTEGER entry "0 IN, 1 OUT, 2 INOUT, 3 OUT_BY"
REAL volume
REAL price
REAL profit
REAL commission
REAL swap
REAL fee
}
cash_events {
INTEGER ticket
TEXT symbol
TEXT time
INTEGER type
REAL profit
}
positions_reconstructed {
INTEGER position_id
TEXT symbol
TEXT open_time
TEXT close_time
INTEGER direction
REAL volume_open
REAL volume_close
REAL volume_reversal
REAL open_price
REAL close_price
REAL total_profit
INTEGER reversal_count
INTEGER deals_count
}
rates ||--o{ history_deals : "symbol (logical)"
ticks ||--o{ history_deals : "symbol (logical)"
history_orders ||--o{ history_deals : "order ~ ticket (logical)"
history_deals ||--|| cash_events : "VIEW: type NOT IN (0,1)"
history_deals ||--o{ positions_reconstructed : "VIEW: GROUP BY position_id"
Tables and views¶
| Object | Kind | Source | Notes |
|---|---|---|---|
rates |
table | copy_rates_range |
Indexed on (symbol, timeframe, time) when columns exist. |
ticks |
table | copy_ticks_range |
Indexed on (symbol, time) when columns exist. |
history_orders |
table | history_orders_get |
Fetched per --symbol, then concatenated. |
history_deals |
table | history_deals_get |
Fetched per --symbol, then concatenated. Indexed on (position_id, symbol) when present. |
cash_events |
view | history_deals |
Non-trade deal types (deposits, balance ops, etc.). Requires type column. |
positions_reconstructed |
view | history_deals |
One row per closed position_id; volume-weighted prices and reversal stats. |
Column sets can vary with terminal and pdmt5 version. Views are skipped with a warning when required columns are missing.
Incremental collection¶
The update_history SDK path uses the same base tables and optional
cash_events / positions_reconstructed views. It additionally maintains
rate_<symbol>__<timeframe> compatibility views when create_rate_views=True.
Rate view resolution¶
Downstream tools can resolve mt5cli-managed compatibility view names from an existing SQLite history database without creating files or guessing legacy naming schemes:
from pathlib import Path
from mt5cli.history import resolve_rate_view_name, resolve_rate_view_names
# Single symbol and granularity
view = resolve_rate_view_name(Path("history.db"), "EURUSD", "M1")
# Batch resolution in row-major order
views = resolve_rate_view_names(
Path("history.db"),
["EURUSD", "GBPUSD"],
["M1", "H1"],
)
Resolution rules:
- Returns
rate_<symbol>__<timeframe>when a symbol stores one timeframe. - Returns
rate_<symbol>__<granularity>_<timeframe>when multiple timeframes are stored for the same symbol. - When multiple naming candidates apply, prefers an existing managed
rate_*__*view from the candidate list. - Falls back to single-timeframe naming when the database path is missing or
ratesmetadata is unavailable. - Pass
require_existing=Trueto raiseValueErrorinstead of returning a best-guess name when the database or view is missing. - Accepts either a SQLite path or an open
sqlite3.Connection.