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.
Source code in mt5cli/history.py
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
load_rate_data ¶
load_rate_data(
conn_or_path: SqliteConnOrPath,
table: str,
count: int | None = None,
) -> DataFrame
Load rate-like data from a SQLite database path or connection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conn_or_path
|
SqliteConnOrPath
|
SQLite database path or open connection. |
required |
table
|
str
|
Source table or view name. |
required |
count
|
int | None
|
Optional number of most recent rows to load. |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame indexed by ascending |
Source code in mt5cli/history.py
load_rate_data_from_connection ¶
load_rate_data_from_connection(
connection: Connection,
table: str,
count: int | None = None,
) -> DataFrame
Load rate-like data from a SQLite table or view.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
connection
|
Connection
|
Open SQLite connection. |
required |
table
|
str
|
Source table or view name. |
required |
count
|
int | None
|
Optional number of most recent rows to load. |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame indexed by ascending |
Raises:
| Type | Description |
|---|---|
ValueError
|
If inputs, schema, timestamps are invalid, or the table or view contains no rows. |
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
1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 | |
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
1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 | |
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.
Rate data loading¶
Use load_rate_data() to load a table or view from a SQLite path, or
load_rate_data_from_connection() when you already have a connection:
from pathlib import Path
from mt5cli import load_rate_data
from mt5cli.history import resolve_rate_view_name
view = resolve_rate_view_name(Path("history.db"), "EURUSD", "M1", require_existing=True)
rates = load_rate_data(Path("history.db"), view, count=1000)
The loader accepts close-based OHLC rate data or tick-like bid/ask data. It
validates that time exists, parses timestamps with pandas, and returns a
DataFrame indexed by ascending DatetimeIndex named time.