mirror of
https://github.com/triqbit/mt5-ai-xauusd-trader.git
synced 2026-11-02 06:56:15 +00:00
* feat: 🗄️ Jules02: Database reliability improvement — Slow query logging and SQLite hardening Implement enterprise-grade database observability and reliability: - Add SLOW_QUERY_THRESHOLD = 1.0s and SQLAlchemy event listeners for query timing. - Harden SQLite with busy_timeout=5000 to reduce lock contention errors. - Optimize SQLite with synchronous=NORMAL for better WAL mode performance. - Add comprehensive integration tests in tests/test_database_performance.py. Co-authored-by: xnessom <255288032+xnessom@users.noreply.github.com> * feat: 🗄️ Jules02: Database reliability improvement — Slow query logging and SQLite hardening Implement enterprise-grade database observability and reliability: - Add SLOW_QUERY_THRESHOLD = 1.0s and SQLAlchemy event listeners for query timing. - Harden SQLite with busy_timeout=5000 to reduce lock contention errors. - Optimize SQLite with synchronous=NORMAL for better WAL mode performance. - Document database improvements in DATABASE_STANDARDS.md to satisfy auto-merge policy. - Fix linting violations in main.py (SIM201, SIM105) and tests. - Add comprehensive integration tests in tests/test_database_performance.py. Co-authored-by: xnessom <255288032+xnessom@users.noreply.github.com> --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: xnessom <255288032+xnessom@users.noreply.github.com>
396 lines
13 KiB
Markdown
396 lines
13 KiB
Markdown
# Database Standards for MT5 AI/ML Trading Bot
|
|
|
|
## 1. Schema Design & Architecture
|
|
|
|
### 1.1 Database Technology Stack
|
|
- **Primary Database**: PostgreSQL 13+ (ACID compliance, JSON support, range types)
|
|
- **Time-Series Data**: TimescaleDB extension for high-frequency trading data
|
|
- **Caching Layer**: Redis 6.0+ for real-time market data and model predictions
|
|
- **Data Warehouse**: ClickHouse for analytics and backtesting data
|
|
- **Backup**: AWS S3 or equivalent for disaster recovery
|
|
|
|
### 1.2 Schema Normalization
|
|
- **Normalization Level**: 3rd Normal Form (3NF) minimum
|
|
- **Denormalization**: Only for proven performance bottlenecks with documented justification
|
|
- **Key Constraints**:
|
|
- All tables must have primary keys
|
|
- Foreign key constraints enforced at database level
|
|
- Unique constraints on natural keys where applicable
|
|
|
|
### 1.3 Audit Trail
|
|
Every table must include:
|
|
```sql
|
|
CREATE TABLE market_data (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
symbol VARCHAR(20) NOT NULL,
|
|
timeframe VARCHAR(10) NOT NULL,
|
|
open_price DECIMAL(12,5) NOT NULL,
|
|
close_price DECIMAL(12,5) NOT NULL,
|
|
volume BIGINT NOT NULL,
|
|
|
|
-- Audit columns (mandatory)
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by VARCHAR(100),
|
|
updated_by VARCHAR(100),
|
|
|
|
-- Soft delete
|
|
deleted_at TIMESTAMPTZ,
|
|
is_deleted BOOLEAN DEFAULT FALSE,
|
|
|
|
CONSTRAINT check_prices CHECK (open_price > 0 AND close_price > 0),
|
|
CONSTRAINT check_volume CHECK (volume >= 0)
|
|
);
|
|
|
|
CREATE INDEX idx_market_data_symbol_timeframe
|
|
ON market_data(symbol, timeframe, created_at DESC);
|
|
CREATE INDEX idx_market_data_created_at
|
|
ON market_data(created_at DESC);
|
|
```
|
|
|
|
### 1.4 Table Partitioning Strategy
|
|
For high-volume tables:
|
|
- **Partition by**: Time (daily for OHLCV, hourly for ticks)
|
|
- **Retention Policy**:
|
|
- Tick data: 3 months
|
|
- OHLCV data: 5+ years
|
|
- Trade logs: 7 years (regulatory)
|
|
- Model training data: Indefinite
|
|
|
|
## 2. Query Standards & Performance
|
|
|
|
### 2.1 Query Best Practices
|
|
```sql
|
|
-- GOOD: Explicit columns, parameterized queries
|
|
SELECT symbol, timeframe, close_price, volume
|
|
FROM market_data
|
|
WHERE symbol = $1
|
|
AND timeframe = $2
|
|
AND created_at >= $3
|
|
AND is_deleted = FALSE
|
|
ORDER BY created_at DESC
|
|
LIMIT 1000;
|
|
|
|
-- BAD: SELECT *, implicit joins, missing indexes
|
|
SELECT * FROM market_data, trades
|
|
WHERE market_data.id = trades.market_data_id;
|
|
```
|
|
|
|
### 2.2 Index Strategy
|
|
- **Query Analysis**: Use EXPLAIN ANALYZE for all new queries
|
|
- **Index Types**:
|
|
- B-tree: Default for equality and range queries
|
|
- Hash: For exact equality matches only
|
|
- GiST: For geographic/range queries
|
|
- BRIN: For sorted data (time-series)
|
|
- **Avoid Over-indexing**: Maximum 5-7 indexes per table
|
|
|
|
### 2.3 Connection Management
|
|
```python
|
|
# Best Practice: Use centralized engine factory
|
|
from src.core.database import get_engine, get_session_factory
|
|
|
|
# Centralized management ensures consistent pooling and resilience
|
|
engine = get_engine(database_url)
|
|
SessionFactory = get_session_factory(engine)
|
|
|
|
# Use with context manager
|
|
with SessionFactory() as session:
|
|
# database operations
|
|
...
|
|
```
|
|
|
|
The system uses `src/core/database.py` to manage SQLAlchemy engines. It automatically configures:
|
|
- `StaticPool` for in-memory SQLite (shares same DB across connections).
|
|
- `NullPool` for file-based SQLite (prevents "database is locked" errors).
|
|
- `QueuePool` for PostgreSQL with `pool_pre_ping=True` and `pool_recycle=3600`.
|
|
|
|
### 2.4 Transaction Handling
|
|
```python
|
|
def save_trade_execution(trade_data):
|
|
"""Save trade with transaction integrity."""
|
|
try:
|
|
with engine.begin() as conn:
|
|
# Insert trade record
|
|
trade_id = conn.execute(
|
|
"INSERT INTO trades (symbol, side, volume, price, status) "
|
|
"VALUES ($1, $2, $3, $4, $5) RETURNING id",
|
|
[trade_data.symbol, trade_data.side,
|
|
trade_data.volume, trade_data.price, 'PENDING']
|
|
).scalar()
|
|
|
|
# Update account balance (atomic with trade)
|
|
conn.execute(
|
|
"UPDATE accounts SET balance = balance - $1 "
|
|
"WHERE id = $2",
|
|
[trade_data.cost, trade_data.account_id]
|
|
)
|
|
|
|
return trade_id
|
|
except Exception as e:
|
|
# Transaction auto-rolled back by context manager
|
|
logger.error(f"Trade execution failed: {str(e)}")
|
|
raise
|
|
```
|
|
|
|
## 3. Data Integrity & Validation
|
|
|
|
### 3.1 Constraint Types
|
|
```sql
|
|
-- Domain constraints
|
|
ALTER TABLE trading_orders ADD CONSTRAINT check_volume_positive
|
|
CHECK (volume > 0);
|
|
|
|
-- Not null constraints
|
|
ALTER TABLE market_data ALTER COLUMN close_price SET NOT NULL;
|
|
|
|
-- Unique constraints for business rules
|
|
ALTER TABLE api_keys ADD CONSTRAINT uq_api_key_user
|
|
UNIQUE (user_id, provider);
|
|
|
|
-- Referential integrity
|
|
ALTER TABLE trades ADD CONSTRAINT fk_trades_account
|
|
FOREIGN KEY (account_id) REFERENCES accounts(id)
|
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
```
|
|
|
|
### 3.2 Data Validation Rules
|
|
- **Prices**: Must be positive, reasonable bounds (e.g., 0.01 - 10,000 for XAUUSD)
|
|
- **Volumes**: Must be positive, comply with broker limits
|
|
- **Timestamps**: Must be in valid range, not in future
|
|
- **Status Values**: Must be from predefined enum set
|
|
- **Risk Metrics**: Must comply with position sizing rules
|
|
|
|
### 3.3 Regular Consistency Checks
|
|
```sql
|
|
-- Check for orphaned records
|
|
SELECT t.id FROM trades t
|
|
LEFT JOIN accounts a ON t.account_id = a.id
|
|
WHERE a.id IS NULL;
|
|
|
|
-- Verify account balance calculation
|
|
SELECT account_id, SUM(profit_loss) as calculated_balance
|
|
FROM trade_history
|
|
GROUP BY account_id
|
|
HAVING SUM(profit_loss) != (
|
|
SELECT balance FROM accounts WHERE id = account_id
|
|
);
|
|
|
|
-- Check for duplicate trades
|
|
SELECT symbol, entry_time, COUNT(*)
|
|
FROM trades
|
|
GROUP BY symbol, entry_time
|
|
HAVING COUNT(*) > 1;
|
|
```
|
|
|
|
## 4. Backup & Disaster Recovery
|
|
|
|
### 4.1 Backup Strategy
|
|
- **Frequency**:
|
|
- Full backup: Daily (non-business hours)
|
|
- Incremental: Every 6 hours
|
|
- Transaction logs: Continuous archiving
|
|
- **Retention**:
|
|
- Daily backups: 30 days
|
|
- Weekly backups: 90 days
|
|
- Monthly backups: 1 year
|
|
- **Location**: Geographically distributed (primary + 2 replicas)
|
|
|
|
### 4.2 Backup Procedure
|
|
```bash
|
|
#!/bin/bash
|
|
# Daily PostgreSQL backup script
|
|
|
|
BACKUP_DIR="/backups/trading_db"
|
|
DB_NAME="mt5_trading"
|
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
|
|
|
# Full backup
|
|
pg_dump -Fc -v -U postgres $DB_NAME > \
|
|
$BACKUP_DIR/full_backup_$TIMESTAMP.dump
|
|
|
|
# Compress
|
|
gzip $BACKUP_DIR/full_backup_$TIMESTAMP.dump
|
|
|
|
# Upload to S3
|
|
aws s3 cp $BACKUP_DIR/full_backup_$TIMESTAMP.dump.gz \
|
|
s3://trading-backups/daily/
|
|
|
|
# Verify backup integrity
|
|
pg_restore -l $BACKUP_DIR/full_backup_$TIMESTAMP.dump | wc -l
|
|
|
|
# Cleanup old backups (keep 30 days)
|
|
find $BACKUP_DIR -name "*.dump.gz" -mtime +30 -delete
|
|
```
|
|
|
|
### 4.3 Recovery Testing
|
|
- **Frequency**: Monthly full recovery test
|
|
- **Procedure**: Restore to test environment, verify data integrity
|
|
- **Documentation**: Maintain recovery runbook with tested commands
|
|
- **RTO Target**: < 4 hours
|
|
- **RPO Target**: < 1 hour
|
|
|
|
## 5. Database Monitoring & Maintenance
|
|
|
|
### 5.1 Performance Monitoring Metrics
|
|
```sql
|
|
-- Monitor table sizes
|
|
SELECT
|
|
schemaname,
|
|
tablename,
|
|
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size,
|
|
n_live_tup as row_count,
|
|
last_vacuum,
|
|
last_autovacuum
|
|
FROM pg_stat_user_tables
|
|
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
|
|
|
|
-- Monitor index performance
|
|
SELECT
|
|
schemaname,
|
|
tablename,
|
|
indexname,
|
|
idx_scan,
|
|
idx_tup_read,
|
|
idx_tup_fetch,
|
|
pg_size_pretty(pg_relation_size(indexrelid)) AS size
|
|
FROM pg_stat_user_indexes
|
|
ORDER BY idx_scan DESC;
|
|
|
|
-- Monitor slow queries
|
|
SELECT
|
|
query,
|
|
calls,
|
|
mean_time,
|
|
max_time,
|
|
total_time
|
|
FROM pg_stat_statements
|
|
ORDER BY mean_time DESC
|
|
LIMIT 20;
|
|
```
|
|
|
|
### 5.2 Maintenance Tasks
|
|
- **VACUUM**: Daily (auto-enabled), full vacuum weekly
|
|
- **ANALYZE**: After bulk loads, updates to pg_stat
|
|
- **REINDEX**: Monthly check for bloat, rebuild if > 30%
|
|
- **CLUSTER**: Quarterly reorganization of heavily queried tables
|
|
|
|
### 5.3 Alert Thresholds
|
|
- **Disk Space**: Alert at 80%, critical at 95%
|
|
- **Connection Count**: Alert at 80% of max_connections
|
|
- **Query Time**: Alert if slow query > 10 seconds
|
|
- **Lock Wait**: Alert if transaction wait > 5 seconds
|
|
- **Replication Lag**: Alert if > 1 second
|
|
|
|
## 6. Security & Access Control
|
|
|
|
### 6.1 Database Users & Privileges
|
|
```sql
|
|
-- Application user (restricted)
|
|
CREATE ROLE app_user WITH LOGIN PASSWORD 'secure_password';
|
|
GRANT CONNECT ON DATABASE mt5_trading TO app_user;
|
|
GRANT USAGE ON SCHEMA public TO app_user;
|
|
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO app_user;
|
|
REVOKE DELETE, TRUNCATE ON ALL TABLES IN SCHEMA public FROM app_user;
|
|
|
|
-- Read-only user for analytics
|
|
CREATE ROLE analytics_user WITH LOGIN PASSWORD 'secure_password';
|
|
GRANT SELECT ON ALL TABLES IN SCHEMA public TO analytics_user;
|
|
|
|
-- Admin user (restricted access, audit all actions)
|
|
CREATE ROLE admin_user WITH LOGIN PASSWORD 'secure_password' SUPERUSER;
|
|
```
|
|
|
|
### 6.2 Data Encryption
|
|
- **Encryption at Rest**: Enable in PostgreSQL or storage layer
|
|
- **Encryption in Transit**: TLS 1.2+ for all connections
|
|
- **Sensitive Fields**: Encrypt API keys, account numbers, passwords
|
|
```python
|
|
from cryptography.fernet import Fernet
|
|
|
|
cipher = Fernet(encryption_key)
|
|
|
|
def save_api_key(user_id, api_key, provider):
|
|
encrypted_key = cipher.encrypt(api_key.encode())
|
|
db.insert('api_keys', {
|
|
'user_id': user_id,
|
|
'provider': provider,
|
|
'encrypted_key': encrypted_key
|
|
})
|
|
```
|
|
|
|
### 6.3 Access Audit Logging
|
|
```sql
|
|
-- Create audit table
|
|
CREATE TABLE audit_log (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
table_name VARCHAR(100),
|
|
operation VARCHAR(10), -- SELECT, INSERT, UPDATE, DELETE
|
|
user_name VARCHAR(100),
|
|
timestamp TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
old_values JSONB,
|
|
new_values JSONB,
|
|
query TEXT
|
|
);
|
|
|
|
-- Enable auditing
|
|
CREATE TRIGGER audit_trades_changes
|
|
AFTER INSERT OR UPDATE OR DELETE ON trades
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION audit_trigger_func();
|
|
```
|
|
|
|
## 7. Data Quality Standards
|
|
|
|
### 7.1 Data Completeness
|
|
- **Required Fields**: All NOT NULL fields must have values
|
|
- **Historical Data**: Must be validated before import
|
|
- **Real-Time Data**: Validation on receipt from data provider
|
|
|
|
### 7.2 Data Accuracy
|
|
- **Price Validation**: Spot check against market feed
|
|
- **Volume Validation**: Compare with exchange reports
|
|
- **Reconciliation**: Daily reconciliation of trades with MT5 platform
|
|
|
|
### 7.3 Data Freshness
|
|
- **Market Data**: Updated within 1-5 seconds
|
|
- **Trade Data**: Updated immediately upon execution
|
|
- **Account Data**: Updated within 10 seconds
|
|
- **Stale Data Alert**: Trigger if no update received in 60 seconds
|
|
|
|
## 8. Query Optimization Checklist
|
|
|
|
- [ ] EXPLAIN ANALYZE reviewed for all queries
|
|
- [ ] Appropriate indexes created and tested
|
|
- [ ] Query execution time < 1 second (typical)
|
|
- [ ] Connection pooling configured
|
|
- [ ] N+1 query problem identified and resolved
|
|
- [ ] Prepared statements used for all dynamic queries
|
|
- [ ] No SELECT * without WHERE clause
|
|
- [ ] Batch operations used for bulk inserts/updates
|
|
- [ ] Cache strategy implemented for frequently accessed data
|
|
|
|
---
|
|
|
|
**Last Updated**: May 2026
|
|
**Version**: 1.1
|
|
**Maintained By**: Infrastructure Team
|
|
**Review Frequency**: Quarterly
|
|
|
|
### Implementation Note (v1.1)
|
|
The trade logging system has been implemented in `src/core/trade_logger.py` using SQLAlchemy 2.0 and Alembic. It enforces the following:
|
|
- Mandatory audit columns via `AuditMixin`.
|
|
- SQLite compatibility using `render_as_batch=True`.
|
|
- Automated performance reporting (Sharpe, Profit Factor, Max Drawdown).
|
|
|
|
### Implementation Note (v1.2) - SQLite Hardening
|
|
To ensure enterprise-grade reliability when using SQLite:
|
|
- **Foreign Key Enforcement**: Enabled via SQLAlchemy event listeners (`PRAGMA foreign_keys=ON`). This ensures relational integrity that is disabled by default in SQLite.
|
|
- **Write-Ahead Logging (WAL)**: Enabled via `PRAGMA journal_mode=WAL` to improve concurrency (allowing multiple readers and one writer) and provide better durability and performance.
|
|
- **File Permissions**: Enforced `0o600` (owner read/write only) on file-based SQLite databases to prevent unauthorized local access to sensitive trade and audit data.
|
|
- **Lock Contention**: `PRAGMA busy_timeout=5000` is used to allow connections to wait up to 5 seconds for a lock to clear, reducing "database is locked" errors.
|
|
- **Optimized Durability**: `PRAGMA synchronous=NORMAL` is used in WAL mode to provide high performance for writes while maintaining excellent durability.
|
|
|
|
### Implementation Note (v1.3) - Observability
|
|
- **Slow Query Logging**: The system implements automated slow query detection via SQLAlchemy event listeners. Queries exceeding `SLOW_QUERY_THRESHOLD` (default: 1.0s) are logged as warnings for performance monitoring.
|