Professional Trading Bridge System - PineTunnel EA Service for MT5
Project description
Relay TradingView webhook signals to MetaTrader 4/5 for automated order execution.
Quick Start · Architecture · API · Docs · Website
One Command. Two Minutes. Fully Functional.
pip install pinetunnel
pinetunnel
That's it. The setup wizard handles everything: dependencies, Redis, security keys, database, Telegram bot, OS service (24/7 auto-start), and EA installation. No Docker, no manual config, no DevOps experience needed.
What you get after setup:
- FastAPI server running 24/7 on your VPS (survives reboots via systemd/launchd)
- Telegram bot for real-time trade notifications and monitoring
- HTTPS webhook URL for TradingView alerts
- RC4 signal encryption (key generated automatically)
- OS service auto-start on boot
What you need:
- A VPS (any OS: Linux, macOS, Windows)
- A Telegram bot token from @BotFather (the setup walks you through it)
TradingView is the most popular charting platform for retail traders. MetaTrader 4/5 dominates retail forex/CFD broker terminals. But TradingView can't place trades on MetaTrader directly - there is no native integration.
PineTunnel closes this gap:
- Sub-second relay from TradingView alert to MT4/MT5 order via WebSocket
- 40+ trading commands: market orders, pending orders, partial close, breakeven, trailing stops, ATR-based stops
- Idempotent execution: duplicate webhook retries are deduplicated - no duplicate trades
- RC4 signal encryption: alerts encrypted in PineScript, decrypted on server (plaintext also supported)
- Telegram admin bot: mandatory - real-time trade notifications, monitoring, signal tracking
- 24/7 auto-start: OS service (systemd/launchd/sc.exe) installed automatically, survives reboots
- Dual MT4/MT5 support: separate MQL4 and MQL5 codebases with identical functionality
- C++ DLL integration: MetaTrader has no native WebSocket support - a custom C++ DLL implements it
Example webhook payload (CSV format):
YOUR_KEY,buy,EURUSD,lots=0.10,sl=1.0850,tp=1.0950,comment=mytrade,secret=YOUR_SECRET
Architecture
graph TD
A[TradingView Alert] --> B[FastAPI Webhook<br/>HMAC-signed payload verification]
B --> C[Signal Queue<br/>DB-backed, idempotent]
C --> D[WebSocket Bridge<br/>Redis pub/sub fan-out]
D <--> E[MT4/MT5 Expert Advisor<br/>C++ DLL WebSocket client]
E --> F[Trade Execution<br/>HMAC auth + server-side risk checks]
F --> G[Telegram Bot<br/>admin monitoring + trade notifications]
A TradingView alert hits the FastAPI webhook endpoint. The signal is parsed, validated, and queued in a persistent database-backed queue. The WebSocket bridge relays it to the MT4/MT5 Expert Advisor running inside MetaTrader, which executes the trade via a custom C++ DLL. The Telegram bot notifies admins of results, failures, and connection status changes.
Table of Contents
- Quick Start
- Architecture
- API
- Tech Stack
- Configuration
- Engineering Challenges
- Observability
- Security
- Project Structure
- CI/CD
- Documentation
- For AI Coding Assistants
- Contributing
- License
Quick Start
The only command you need
pip install pinetunnel
pinetunnel
The auto-setup runs 5 steps automatically:
- Server - installs deps, starts Redis, generates .env (chmod 600), runs migrations, starts daemon, verifies health
- Webhook URL - Public IP (HTTP, zero cost) or Cloudflare domain (HTTPS, persistent)
- Telegram bot - paste bot token + admin user ID from @BotFather (required, not optional)
- OS service - installs systemd / launchd / sc.exe automatically (24/7 auto-start on boot)
- EA install - auto-detects MetaTrader on Windows and copies .ex5/.ex4 + DLL
Alternative install (from source)
git clone https://github.com/TheFractalyst/PineTunnel.git
cd PineTunnel && pip install -e ".[all]"
pinetunnel
CLI commands
pinetunnel First run: auto-setup (5 steps, 2 minutes)
pinetunnel start Start server (foreground)
pinetunnel start --daemon Start as background daemon
pinetunnel stop Stop the daemon
pinetunnel status Check if running + health probe
pinetunnel check Health checks (deps, Redis, .env, DB, server)
pinetunnel test Send a test webhook signal
pinetunnel migrate Run database migrations
pinetunnel guide Print the full setup guide
pinetunnel version Show version + platform info
pinetunnel install-service Install as OS service (auto-start on boot)
pinetunnel uninstall-service Remove OS service
pinetunnel install-ea Auto-detect MetaTrader and install EA + DLL
pinetunnel setup-cloudflare Set up Cloudflare DNS or tunnel for HTTPS
pinetunnel stop-cloudflare Stop Cloudflare tunnel
pinetunnel setup-proxy Set up nginx reverse proxy and Let's Encrypt SSL
Send a test alert
curl -X POST http://127.0.0.1:8000/ \
-H "Content-Type: text/plain" \
-d 'YOUR_KEY,buy,EURUSD,lots=0.10,sl=1.0850,tp=1.0950,comment=test,secret=YOUR_SECRET'
Install the EA on MetaTrader
- Copy
PineTunnel_EA.ex5(MT5) orPineTunnel_EA_MT4.ex4(MT4) to yourMQL5/ExpertsorMQL4/Expertsfolder - Copy
PTWebSocket.dlltoMQL5/LibrariesorMQL4/Libraries - Restart MetaTrader, attach the EA to a chart
- Set
InpLicenseIDandInpServerURLin the EA inputs - Enable DLL imports: Tools -> Options -> Expert Advisors -> Allow DLL imports
See EA Setup Guide for detailed steps.
API
Key endpoints:
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/webhook |
POST | Webhook secret | Receive TradingView alerts |
/ws/{license_key} |
WS | License key | Real-time signal push to EA |
/health |
GET | None | Server health (DB, Redis) |
/api/signals/{license_key} |
GET | License key | EA polling: fetch pending signals |
/api/ea/download/{platform} |
GET | License key | Download EA compiled file |
/api/trades/report |
POST | License + secret | Trade execution report from EA |
/metrics |
GET | None | Prometheus metrics |
/api/diagnostics |
GET | Admin key | Full system diagnostics (8 probes) |
/api/admin/replay |
POST | Admin key | Inject test signal into live pipeline |
/.well-known/security.txt |
GET | None | Security contact info |
Full API surface (34 endpoints)
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/webhook |
POST | Webhook secret | Receive TradingView alerts (JSON body) |
/ |
POST | Webhook secret | Root webhook (plain text or JSON) |
/ws/{license_key} |
WS | License key | Real-time signal push to EA |
/health |
GET | None | Server health (DB, Redis) |
/health/live |
GET | None | Liveness probe |
/health/ready |
GET | None | Readiness probe |
/api/signals/{license_key} |
GET | License key | EA polling: fetch pending signals |
/api/signals-longpoll/{license_key} |
GET | License key | Long-poll signal fetch |
/api/signals/{license_key}/{signal_id} |
DELETE | License key | Acknowledge processed signal |
/api/signals-batch-ack/{license_key} |
POST | License key | Batch acknowledge signals |
/api/ea/check/{platform} |
GET | License key | Lightweight EA version check |
/api/ea/download/{platform} |
GET | License key | Download EA compiled file (base64) |
/api/ea/download/{user_id}/{platform}/{sig} |
GET | Signed token | Download EA + DLL (zip) via persistent link |
/api/ea/audit/{license_key} |
POST | License key | EA audit logging |
/api/ea/dll/{platform} |
GET | Admin key | DLL version info |
/api/ea/dll/download/{platform} |
GET | License key | Download DLL file |
/api/trades/report |
POST | License + secret | Receive trade execution report from EA |
/api/trades/close |
POST | License + secret | Receive trade close notification |
/api/trades/stats |
POST | License + secret | Receive account stats from EA |
/api/admin/rate-limits |
GET | Admin key | Rate limit statistics per IP |
/api/admin/rate-limits/{ip} |
DELETE | Admin key | Reset rate limit for IP |
/api/admin/rate-limits/{ip}/reset |
POST | Admin key | Reset rate limit for IP (POST) |
/api/logs/errors |
GET | Session | Error logs |
/api/audit/actions |
GET | Session | Admin audit logs |
/api/database/stats |
GET | Session | Database table sizes and row counts |
/api/auth/sessions |
GET | Session | Active auth sessions |
/api/auth/logs |
GET | Session | Auth access logs |
/api/connections |
GET | Session | WebSocket connections overview |
/api/ea/ws-telemetry/open-positions/{license_key} |
GET | Admin key | Open positions for a connection |
/api/ea/ws-telemetry/trade-history/{license_key} |
GET | Admin key | Trade history for a connection |
/api/ea/ws-telemetry/signal-log/{license_key} |
GET | Admin key | Signal delivery log for a connection |
/api/ea/ws-telemetry/health/{license_key} |
GET | Admin key | WebSocket connection health |
/api/admin/replay/batch |
POST | Admin key | Batch inject test signals (max 50) |
/api/admin/replay/results |
GET | Admin key | Recent replay delivery results |
/.well-known/security.txt |
GET | None | Security contact info |
Tech Stack
| Technology | Role |
|---|---|
| Python 3.13 | Backend language, async throughout |
| FastAPI | Async web framework with middleware pipeline |
| Redis | WebSocket connection state, pub/sub messaging, rate limiting |
| SQLAlchemy 2.0 | Raw SQL via text() with dual DB support (SQLite + PostgreSQL) |
| Alembic | 7 database migrations |
| C++ (CMake) | WebSocket DLL for MT5 (x64) and MT4 (x86) |
| MQL5/MQL4 | Expert Advisor: order execution, partial close, trailing stops, ATR |
| python-telegram-bot | Admin bot (mandatory - trade notifications + monitoring) |
| GitHub Actions | CI/CD: DLL compilation (Windows), EA compilation (MetaEditor) |
| Uvicorn | Production ASGI server |
Configuration
All values are auto-generated by pinetunnel setup. No manual editing needed.
| Variable | Required | Description |
|---|---|---|
WEBHOOK_SECRET |
Yes | Shared secret for TradingView webhook verification (32+ chars) |
JWT_SECRET |
Yes | JWT token signing key (32+ chars) |
ADMIN_API_KEY |
Yes | Admin API endpoint authentication (32+ chars) |
SERVER_BASE_URL |
Yes | Public URL of your server (for EA download links) |
HOST |
Yes | Bind address (default: 127.0.0.1, use 0.0.0.0 for public IP) |
SIGNAL_ENCRYPTION_KEY |
Yes | RC4 encryption key for PineScript signals (64-char hex) |
TELEGRAM_BOT_TOKEN |
Yes | Telegram bot token from @BotFather |
TELEGRAM_ADMIN_IDS |
Yes | Comma-separated Telegram admin user IDs |
REDIS_URL |
No | Redis connection URL (default: localhost:6379) |
DATABASE_URL |
No | PostgreSQL URL (default: SQLite local file) |
Engineering Challenges
Idempotent signal execution
TradingView retries webhook delivery on timeout. Without deduplication, a single alert could place multiple trades. The signal queue computes a content hash (excluding the secret field) and stores it with status acknowledged before execution. A retry within the dedup window is detected and rejected; the EA never sees it.
WebSocket state recovery
When the server restarts or a Uvicorn worker recycles, EA connections drop. Redis stores connection state (license key, EA version, last seen) so any worker can recover the connection table. The EA reconnects automatically with exponential backoff.
Multi-worker WebSocket fan-out
The webhook hits worker A, but the EA's WebSocket is on worker B. Redis pub/sub broadcasts signals to all workers, ensuring delivery regardless of which worker receives the webhook.
Dual-database architecture
SQLite and PostgreSQL share a common async base class (DatabaseManager) with identical method signatures. Alembic migrations run on both. The DATABASE_URL env var selects the backend - no code changes needed between dev and production.
C++ DLL integration into MetaTrader
MetaTrader doesn't natively support WebSocket or HTTP client libraries. The C++ DLL (PTWebSocket.dll) implements a WebSocket client using WinHTTP, compiled for both x64 (MT5) and x86 (MT4) via CMake. The MQL5 EA loads the DLL and calls its functions via #import.
HMAC-signed EA protocol
Every message between server and EA is HMAC-SHA256 signed. The server verifies signatures with hmac.compare_digest() (constant-time) to prevent timing attacks. Messages include a timestamp with skew check to detect replay attacks.
RC4 signal encryption (PineScript to server)
Signals can be RC4-encrypted in PineScript before sending to TradingView. The server auto-detects encrypted messages (prefix RC4,) and decrypts before parsing. Plaintext signals are still accepted (backward compatible). RC4-drop256 is used to mitigate FMS early-bias attacks. The encryption key is generated by pinetunnel setup and displayed for the user to paste into PineScript's encKey input field.
Observability
Prometheus Metrics (/metrics)
The server exports Prometheus-compatible metrics at /metrics for scraping by Prometheus/Grafana/Zabbix. No external dependency - the text exposition format is implemented directly.
pinetunnel_http_requests_total{method,path,status} - Counter
pinetunnel_http_request_duration_seconds{method,path} - Histogram (10 buckets)
pinetunnel_webhook_signals_total{command,result} - Counter
pinetunnel_signal_queue_depth - Gauge
pinetunnel_websocket_connections - Gauge
pinetunnel_websocket_signals_delivered_total - Counter
pinetunnel_redis_operations_total{operation,result} - Counter
pinetunnel_db_queries_total - Counter
Diagnostics (/api/diagnostics)
Admin endpoint that probes all subsystems concurrently and reports status with per-component latency:
| Probe | Checks |
|---|---|
| database | SELECT 1 round-trip |
| redis | PING round-trip |
| websocket_hub | connection count |
| signal_queue | pending signal count |
| rate_limiter | initialized state |
| client_manager | registered client count |
| disk | data dir usage % |
| memory | process RSS |
Public version at /api/diagnostics/public returns only overall status (no auth required).
Signal Data Validation
All signals pass through a validation layer before queuing. Rejects impossible signals that would fail at the broker:
- Symbol format (uppercase, 2-32 alphanumeric chars)
- Volume bounds (lots 0-1000, risk 0-100%)
- SL/TP direction (buy SL < entry < TP, sell SL > entry > TP)
- SL/TP distance sanity (rejects 50%+ stops to catch fat-finger errors)
- Pending order entry price required for pending commands
Rejected signals return 422 with the validation reason.
Signal Replay (/api/admin/replay)
Admin endpoint to inject test signals into the live pipeline without TradingView. Verifies end-to-end delivery: parser -> validator -> queue -> WebSocket -> EA response.
Security
- Server defaults to 127.0.0.1 (localhost only, not exposed to internet)
- .env file chmod 600 (owner-only read/write)
- SQLite database file chmod 600
- HMAC-SHA256 on all EA communication with constant-time comparison
- Webhook secret verification on all TradingView alert endpoints
- RC4 signal encryption (optional, auto-detected)
- Rate limiting (Redis sliding window) + failed attempt blocking (10 -> 1hr IP block)
- IP validation (Cloudflare + TradingView allowlists) on webhook endpoints
- Request size limits (1MB) and content-type enforcement
- Security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, CSP)
- CORS restricted to known origins (deny-by-default)
- Docs disabled in production mode
- Error responses sanitized in production (no stack traces)
See SECURITY.md for the full security policy.
Project Structure
Directory tree
pinetunnel/
+- apps/
| +- server/ FastAPI backend
| | +- routes/ Route modules (webhook, auth, admin, signals, ea_download, etc.)
| | +- services/ Business logic (MT4/MT5, risk, Telegram bot)
| | +- middleware/ Security, rate limiting, IP validation, request validation
| | +- ws/ WebSocket connection management (Redis-backed)
| | +- webhook/ Signal parser, queue, executor, validator pipeline
| | +- crypto/ RC4 signal encryption/decryption
| | +- db/ SQLite + PostgreSQL adapters (shared base class)
| | +- utils/ Security (HMAC, IP validation), logging helpers
| | +- config/ Settings (Pydantic), lifespan, health checks, startup validation
| | +- models/ Pydantic request/response schemas
| +- cli/ CLI package (setup wizard, daemon, Cloudflare, EA install)
| | +- main.py 17 commands, interactive setup, deps/Redis/firewall
| | +- service.py Daemon mode, OS service install (systemd/launchd/sc.exe)
| | +- cloudflare.py DNS API, quick tunnel, named tunnel via browser OAuth
| | +- proxy.py Nginx reverse proxy, Let's Encrypt SSL
| | +- ea_install.py MetaTrader auto-detect, EA+DLL copy, GitHub Releases download, source compilation
| +- ea/ MetaTrader Expert Advisor
| +- mt5/ MT5 EA (.mq5) + MQL includes + compiled .ex5
| +- mt4/ MT4 EA (.mq4) + MQL includes + compiled .ex4
| +- dll/ C++ WebSocket client (CMake build, x64 + Win32)
| +- pine/ PineScript v6 library (43 exports: buy, sell, close, encrypt, etc.)
+- tests/ PyTest test suite (83 tests: parser, validator, metrics, security, crypto)
+- alembic/ 7 database migrations
+- .github/workflows/ CI/CD (test, DLL build, EA build)
+- CONTRIBUTING.md Development setup and PR process
+- SECURITY.md Vulnerability reporting and security architecture
CI/CD
GitHub Actions pipelines build the C++ DLL and MQL5/MQL4 EA on Windows runners:
- test.yml - Runs pytest with coverage on every push/PR
- build-dll.yml - CMake build for x64 and Win32 (Release + Debug)
- build-ea.yml - MetaEditor compilation for MT5 and MT4
Documentation
| Doc | Description |
|---|---|
| Quick Start Guide | Complete setup from zero to first trade |
| Command Reference | All 40+ trading commands with parameters and examples |
| EA Setup Guide | Step-by-step MT4/MT5 Expert Advisor installation |
| EA Settings Guide | All EA input parameters with options and examples |
| TradingView Alert Setup | Configuring TradingView webhooks and PineScript library import |
| FAQ | Common questions, error codes, troubleshooting, latency, repainting |
For AI Coding Assistants
This section is for AI coding assistants (Claude Code, Cursor, Copilot, etc.) navigating this codebase.
What this project is
A FastAPI backend that bridges TradingView webhooks to MetaTrader 4/5 EAs via WebSocket. Deployed on any VPS via pip install pinetunnel. The EA runs inside MetaTrader and uses a C++ DLL for WebSocket connectivity.
Read these files first (in order)
apps/server/main.py- App entry pointapps/server/app_factory.py- App construction, middleware, route mountingapps/server/config/settings.py- All settings (Pydantic BaseSettings), env varsapps/server/webhook/pipeline.py- Signal processing pipelineapps/server/webhook/parser.py- CSV signal parserapps/server/ws/handler.py- WebSocket message handler (EA protocol)apps/server/config/lifespan.py- Startup/shutdown lifecycleapps/cli/main.py- CLI entry point (17 commands, auto-setup wizard)
Key patterns
- Async throughout: All I/O uses
async/await. No blocking calls in request paths. - Dual database:
db/base.pydefines abstractDatabaseManager.db/sqlite.pyanddb/postgres.pyimplement it. Selected byDATABASE_URL. - Redis for state: WebSocket connections, pub/sub fan-out, rate limiting. Required infrastructure.
- Signal queue:
webhook/signal_queue.py- DB-backed queue with content hash dedup. - HMAC protocol: All server-to-EA messages are HMAC-SHA256 signed with timestamp + skew check.
- RC4 encryption:
crypto/signal_crypto.py- auto-detectsRC4,prefix, decrypts, falls back to plaintext. - CLI-driven deployment: All setup happens via
pinetunnelCLI. No Docker, no manual config.
Gotchas
- The webhook accepts CSV format, not JSON. The parser splits on commas and assigns key=value pairs.
webhook/executor.pydoes server-side validation, but trade execution happens in the EA (MQL). The server validates and relays; the EA executes.- Server defaults to
127.0.0.1. Only binds to0.0.0.0when user chooses Public IP in setup. - The
alembic/directory shadows the installedalembicpackage. CLI usespython -cwith sys.path filtering to avoid this. - Telegram bot is mandatory - setup requires a bot token. The bot provides trade notifications and monitoring.
Debugging entry points
| Symptom | Start here |
|---|---|
| Webhook not parsing | apps/server/webhook/parser.py |
| Signal not reaching EA | apps/server/ws/handler.py + webhook/signal_queue.py |
| EA not connecting | apps/server/ws/connection_redis.py + apps/ea/dll/PTWebSocket/ptwebsocket.cpp |
| Trade not executing | apps/server/webhook/executor.py (server) + apps/ea/mt5/PineTunnel_EA.mq5 (EA) |
| Telegram bot issues | apps/server/services/telegram/bot.py + mixins/ |
| CLI setup fails | apps/cli/main.py (cmd_setup function) |
| RC4 decryption fails | apps/server/crypto/signal_crypto.py |
| Migrations fail | alembic/env.py + alembic/versions/ |
Contributing
See CONTRIBUTING.md for development setup, code style, and PR process.
License
MIT - see LICENSE
pinetunnel.com · fractalyst.dev · TradingView · contact@pinetunnel.com
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pinetunnel-7.2.1.tar.gz.
File metadata
- Download URL: pinetunnel-7.2.1.tar.gz
- Upload date:
- Size: 1.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c0372cf228e6d8b47775e589d11aaa6de409d9095a5bc6fe4fe110300ebb5cba
|
|
| MD5 |
6256f74aef837f922f06a67b3e17028b
|
|
| BLAKE2b-256 |
f99da63b9e1a45bb70b2c8e5820a53395cae34c2180b53b7c600757f93d27a2d
|
File details
Details for the file pinetunnel-7.2.1-py3-none-any.whl.
File metadata
- Download URL: pinetunnel-7.2.1-py3-none-any.whl
- Upload date:
- Size: 1.3 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
385053f4c2c9bfab8063b1327ba1557ce6b8a2ce1b283111b219c69c8902cb9c
|
|
| MD5 |
73b26910f79b5e3132f4b1f8768686e2
|
|
| BLAKE2b-256 |
ae0c5d75a6f52f2a103184e4e09f0b504d07f1428754fa6262080e6ae31e2424
|