A machine-learning trading system built with security engineering discipline — not an ML toy retrofitted with security later.
Most cryptocurrency trading bots treat security as a checkbox exercise. The ML pipeline gets all the engineering attention — feature selection, hyperparameter tuning, backtesting — and then someone adds python-dotenv at the end and calls it secure. The problem is that these systems hold API keys with withdrawal permissions, execute financial transactions against live exchanges, and run unattended for hours. They are attractive targets, and "add encryption later" is not a security posture.
This project was designed from the start as a single system where the trading logic and the security architecture are one design problem, not two. Every component — from how credentials are isolated, to how untrusted market data enters the pipeline, to how a model's prediction becomes a real order — was built with the assumption that the system is operating in a hostile environment and that any component could be compromised.
The ML layer uses ICT/SMC (Inner Circle Trader / Smart Money Concepts) as its feature engineering framework, trained on BTC/USDT historical data with walk-forward validation. The primary model is a LightGBM gradient-boosted classifier with Optuna-optimized hyperparameters, filtered through a meta-labeling layer. But the interesting engineering decisions are not in the model — they are in the boundaries between the model and the things the model can cause to happen.
graph TB
subgraph External["External - Untrusted"]
BINANCE["Binance API<br/>Spot + Futures"]
end
subgraph Ingestion["Data Ingestion"]
WS["REST / WebSocket Client<br/>HTTPS - TLS 1.2+"]
DP["Data Pipeline<br/>Dedup + Integrity Checks<br/>500-Candle Warmup Buffer"]
end
subgraph ML["ML Pipeline"]
FE["Feature Engineering<br/>70+ ICT/SMC Features"]
MODEL["LightGBM Classifier<br/>Optuna-Optimized"]
META["Meta-Labeler<br/>Secondary Confidence Filter"]
end
subgraph Execution["Execution Layer"]
SIG["Signal Engine<br/>Session + Conflict Gating"]
RISK["Risk Manager<br/>Daily Loss Circuit Breaker<br/>90% Margin Cap"]
ORD["Order Manager<br/>Input Validation + Dry Run"]
end
subgraph Security["Security Layer"]
ENV["Credential Isolation<br/>dotenv + gitignore"]
REDACT["Log Redaction Filter<br/>Regex-Based Scrubbing"]
AUDIT["Audit Trail<br/>Append-Only JSONL"]
AUTH["JWT + Salted SHA-256<br/>Dashboard Auth"]
end
subgraph Interface["Interface"]
API["FastAPI Backend<br/>Uvicorn + PM2"]
REACT["React 18 + Vite 5<br/>Tailwind + Recharts"]
end
subgraph Offline["Offline - Isolated"]
BT["Backtesting Engine<br/>Walk-Forward Validation"]
SHAP["SHAP Interpretability"]
end
BINANCE -->|"HTTPS"| WS
WS --> DP
DP --> FE
FE --> MODEL
MODEL --> META
META --> SIG
SIG --> RISK
RISK --> ORD
ORD -->|"HTTPS"| BINANCE
ENV -.->|"Runtime only"| WS
ORD -.->|"Every event"| AUDIT
SIG -.->|"Every signal"| AUDIT
REDACT -.->|"All streams"| AUDIT
API --> SIG
API --> AUDIT
REACT --> API
AUTH -.-> API
FE --> BT
MODEL --> BT
BT --> SHAP
Trust boundaries. Untrusted data enters the system at exactly one point: the Binance REST/WebSocket client in live/binance_client.py, which communicates exclusively over HTTPS. All market data passes through deduplication and integrity checks in live/data_pipeline.py — timestamp ordering validation, duplicate candle rejection, and a 500-candle warmup buffer to prevent cold-start indicator corruption — before reaching the feature engineering layer. API credentials never exist in source control: they are loaded from a gitignored .env file via python-dotenv at runtime. A custom _KeyRedactFilter in the logging pipeline uses regex pattern matching to scrub API keys, secrets, and passwords from all log streams before they reach console or disk, preventing credential exfiltration through the logging channel. The execution layer (order_manager.py) is the second critical trust boundary: it validates every field of every signal — side, entry price, confidence, quantity against exchange filters — before any parameter can become a real order. The risk manager enforces hard stops (daily loss circuit breaker, 90% margin utilization cap) that cannot be overridden by the signal engine.
- Credential isolation — API keys and secrets loaded exclusively from a gitignored
.envfile viapython-dotenv. No secrets in source code, no secrets in config files that touch version control..env.exampleships with placeholder values only. - Automated credential redaction in logs — A custom
_KeyRedactFilter(Pythonlogging.Filter) scrubs API keys, secrets, and passwords from all log records using case-insensitive regex before they reach console or disk. Setsrecord.args = ()after redaction to prevent Python's logger from re-interpolating unredacted arguments through format strings. - HTTPS on all outbound connections — All communication with Binance (REST and WebSocket) routes through HTTPS/TLS. Testnet endpoint explicitly set to
https://demo-fapi.binance.com/fapi. - Input validation on all trading parameters — Every signal is validated before order execution: required fields (
side,entry_price,confidence), side must beBUYorSELL, ATR must be positive, prices validated against Binance exchange filters (PRICE_FILTER,LOT_SIZE,MIN_NOTIONAL), quantities rounded withDecimal(ROUND_DOWN)to prevent over-ordering. - Risk controls as hard stops, not suggestions — Daily loss circuit breaker halts all trading when cumulative PnL breaches
MAX_DAILY_LOSS(5% of balance). Position sizing capped atRISK_PER_TRADE(2%). 90% margin utilization cap prevents over-leveraging. Consecutive loss tracking. Cooldown periods between losing trades. Isolated margin mode enforced on Binance (no cross-margin contamination). - Append-only audit trail — Every trade event (signal generation, order placement, fill, close, PnL) written to a JSONL file opened in append mode. UTC timestamps (both Unix and human-readable). File I/O errors are caught and contained — logging failures never interrupt order management.
- Atomic state persistence — Bot state written via
tempfile.mkstemp+os.replaceto prevent corruption during power loss or process crashes. Corrupted state files handled gracefully with fallback to clean startup. - Dry run mode as default —
DRY_RUN=Trueis the default configuration. Full signal generation, validation, risk checks, and audit logging execute — only the Binance API call is skipped. Production trading requires explicit opt-in. - Rate limiting — Client-side rate limiter tracks request timestamps and enforces Binance's 1,200 requests/minute limit with exponential backoff. Binance error code
-2021("Order would immediately trigger") intercepted and raised immediately without retry to prevent rapid-fire order loops. - Safe model deserialization — Primary models loaded from LightGBM native text format (
lgb.Booster(model_file=...)) rather than Python pickle, avoiding arbitrary code execution risks associated with pickle deserialization. Only meta-model feature name metadata usesjoblib.load(). - Signal conflict resolution — If both BUY and SELL models fire simultaneously, the signal engine discards both rather than executing either, preventing contradictory order execution.
- Session-based trading windows — Algorithmic execution restricted to London (07:00–11:00 UTC) and New York (13:00–17:00 UTC) kill zones, limiting exposure during low-liquidity periods.
- JWT authentication on dashboard — Dashboard API requires HS256-signed JWT tokens with 24-hour expiry. User passwords hashed with salted SHA-256. WebSocket connections authenticated via JWT query parameter. No anonymous access to trade history, balance, or configuration endpoints.
- Backtesting runs in an isolated environment — Models are validated offline with walk-forward cross-validation and configurable slippage/fee models before any live execution path is enabled.
ICT/SMC as domain feature engineering. Rather than feeding raw OHLCV candles into a model, the system extracts 70+ features based on Smart Money Concepts — a price action framework that models institutional order flow. These features encode structural market information: order blocks (zones where institutional orders cluster), fair value gaps (3-candle price inefficiencies where one candle's range doesn't overlap with the candle two bars prior), liquidity zones (clusters of equal highs/lows detected via density clustering that act as stop-loss magnets), break of structure and market structure shifts (trend continuation vs. reversal signals), displacement moves (impulsive candles exceeding 2x ATR), and change in state of delivery (transitions between trending and consolidation regimes). The thesis is that these features capture the footprint of large-scale order flow more effectively than standard technical indicators like RSI or moving average crossovers.
Model selection. The primary model is LightGBM (gradient-boosted decision trees), optimized with Optuna using Tree-structured Parzen Estimators across 30 trials. The pipeline also supports XGBoost as an alternative classifier. Both outperformed a 2-layer LSTM on this dataset — likely because the ICT/SMC features are already engineered to capture sequential structure, so tree models get the benefit of domain knowledge without needing to learn temporal patterns from scratch. Walk-forward cross-validation (TimeSeriesSplit, 5 folds) prevents lookahead bias. Class imbalance is handled via dynamic scale_pos_weight computation on each training split. An adaptive threshold search optimizes for F0.5 score (weighting precision 2x over recall) across the range 0.50–0.85, because in trading, false positives are more expensive than missed opportunities.
Multi-timeframe analysis and meta-labeling. The primary model operates on 15-minute candles, but the feature engineering pipeline resamples data to 1-hour and 4-hour timeframes and merges higher-timeframe structure signals (trend direction, BOS events, FVG activity, volatility) back into the 15-minute feature set. An htf_alignment feature scores directional confluence: +1 when all three timeframes agree bullish, -1 when all agree bearish, 0 when they conflict. A meta-labeling layer (following Marcos López de Prado) trains a secondary LightGBM classifier to predict whether the primary model's signal will actually hit its profit target within 16 bars (4 hours), using a 15-feature space that combines primary model confidence with volatility, SMC context, and order flow metrics. Composite trade confidence is primary_certainty × meta_probability, and trades are only executed above a configurable threshold. The backtesting engine measures Sharpe ratio, maximum drawdown, win rate, profit factor, Calmar ratio, expectancy, and average trade duration, all computed with configurable commission (default 0.04% per side) and slippage (0.01%) models.
| Metric | Value | Notes |
|---|---|---|
| Win Rate | 66% | Percentage of profitable trades |
| Total Return | 128.2% | Backtested cumulative return |
| Profit Factor | 7.62 | Gross profit / gross loss |
| Max Drawdown | 6.3% | Peak-to-trough |
| Backtest Period | 2022–2025 | BTC/USDT 15m data |
| Sell Setups | 136 | Total sell signals in backtest |
| Live PnL | +$901 | Live trading (47 closed trades) |
| Avg Win | +$82 | Average profit per winning trade |
Backend: Python 3.10+, FastAPI, WebSocket, uvicorn, PM2
ML: LightGBM, XGBoost, Scikit-learn, Optuna, SHAP, pandas, NumPy, joblib
Data: Binance API (spot + futures), multi-timeframe OHLCV (15m, 1H, 4H)
Frontend: React 18, Vite 5, Tailwind CSS, Recharts, Framer Motion, Axios
Security: python-dotenv, custom log redaction filter, salted SHA-256 + JWT (HS256), isolated margin enforcement
Infra: Environment-based config, PM2 process management, deploy script with uvicorn workers
# Clone the repository
git clone https://github.com/UmarMurtazam/tradIA.git
cd tradIA
# Create and activate virtual environment
python -m venv venv
# Windows
.\venv\Scripts\activate
# Linux/macOS
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Configure environment
cp .env.example .env
# Edit .env with your Binance API credentials and trading parameters
# DRY_RUN=True is the default — no real trades will execute
# Run the training pipeline (buy signals)
python run_buy_pipeline.py
# Run the training pipeline (sell signals)
python run_sell_pipeline.py
# Start the live trading bot (dry run mode by default)
python run_live_bot.py
# Start the dashboard
cd dashboard/frontend && npm install && npm run build && cd ../..
uvicorn dashboard.app:app --host 0.0.0.0 --port 5000The hardest part of this project was not the ML. Getting a gradient-boosted tree to classify buy/sell signals with reasonable accuracy is a well-understood problem. The hard part was designing a system where an API key with trading permissions cannot leak through logs, cannot be replayed if the process is compromised, and cannot be exfiltrated through the audit trail. Every design decision in the security layer came from asking one question repeatedly: "If this component is owned, what is the blast radius?" That question led to the regex-based _KeyRedactFilter scrubbing credentials from all log streams, to isolated margin mode preventing cross-account contamination, to atomic state writes preventing corruption-based state manipulation, to the daily loss circuit breaker as a hard stop that the signal engine cannot override, and to loading models from LightGBM's native text format instead of pickle to eliminate deserialization-based code execution.
Building this system changed how I think about threat modeling for AI systems generally. When a model's output triggers a real-world action — in this case, placing a leveraged futures order on Binance — the security requirements are not about the model. They are about the action pipeline. The model can be wrong; that is a performance problem. But if the action pipeline does not validate inputs, does not enforce risk limits, does not maintain an audit trail, and does not isolate credentials from the execution path, then the system is unsafe regardless of model accuracy. This is the same principle behind SR 11-7 model risk management: the controls around the model matter more than the model itself.
What I would do differently: I would add AES-256-GCM encryption at rest for the .env credentials instead of relying on filesystem permissions alone. I would separate the key management into its own process with a Unix domain socket interface instead of loading keys into the trading process memory. And I would add mutual TLS on the dashboard API from day one instead of relying on JWT alone.
- Add AES-256-GCM encryption at rest for API credentials, replacing plaintext
.envwith an encrypted keystore and PBKDF2 key derivation - Move to hardware security module (HSM) or cloud KMS for key storage in production deployments
- Add anomaly detection on the audit log to flag suspicious trade patterns (unusual position sizes, rapid-fire orders, off-hours activity)
- Integrate a formal model risk management framework (SR 11-7 style) with model inventory, validation schedules, and performance degradation alerts
- Deploy to a hardened container with a distroless base image and read-only filesystem
This is a research and portfolio project. It has not been audited for production use. The security controls described above reflect design intent and implementation effort, but they have not been validated by an independent security review. Do not use this system to trade real capital without conducting your own security assessment and understanding the financial risks involved.
Umar Murtaza — BS Cybersecurity, FAST-NUCES Islamabad. Working at the intersection of security and AI. Open to remote AppSec and AI security roles.
- GitHub: github.com/UmarMurtazam
- LinkedIn: linkedin.com/in/umar-murtazam
- Email: umarmurtaza605050@gmail.com
MIT