Automated trading system for Saxo Bank's Knock-out warrants (Turbos), executing trades via webhook signals.
Warning
This is a personal learning project and not production-ready software.
Do not risk money which you are afraid to lose. USE THE SOFTWARE AT YOUR OWN RISK. THE AUTHORS AND ALL AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR TRADING RESULTS.
Always start by running WATA with small money amounts to test the system and its performance. Use a secondary Saxo account if possible. Understand the risks involved in trading before you start, and do not engage money before you understand how it works and what profit/loss you should expect.
Risk Warning: WATA CAN LOSE ALL YOUR MONEY due to:
- Insufficient code testing
- Limited security measures on your server
- Lack of fail-safe mechanisms
- No comprehensive monitoring included
- Absence of fail-over systems
- Limited user experience
- Change of Saxo Bank API
- Because trading with leverage is very risky
This software is provided "as is" without warranty. The authors accept no liability for any damages arising from its use.
WATA (Warrants Automated Trading Assistant) is an algorithmic trading system compagnon, designed for automated execution of Knock-out warrants (Turbos) on Saxo Bank. It serves as a reliable bridge between trading signals and actual market execution, offering several key benefits:
- Automated Execution: Eliminates emotional bias and human error by executing trades based on predefined rules and signals (from TradingView, for example)
- Risk Management: Implements systematic position monitoring with stop-loss and take-profit mechanisms
- Cached Instrument Selection: Maintains a background watchlist of candidate turbos for configured indices and directions
- Performance Tracking: Provides comprehensive analytics and reporting for trade analysis
- Real-time Monitoring: Uses Saxo WebSocket updates for open positions and delivers notifications via Telegram
- Scalability: Uses asynchronous services, PostgreSQL, and a microservice architecture
The system is particularly suited for traders who:
- Want to automate their trading strategies
- Need reliable execution of trading signals
- Require comprehensive trade tracking and analysis
- Value real-time monitoring and alerts
- Prefer systematic, rule-based trading over discretionary decisions
WATA runs as a set of Docker Compose services:
| Component (role) | Purpose |
|---|---|
| Traefik | Routes public webhook traffic over HTTPS, obtains TLS certificates, and applies the configured IP allowlist |
Web Server (web_server) |
Validates webhook signals and keeps the newest signal per indice in an in-memory store |
Trader (trader) |
Polls the Web Server for signals, applies trading rules, calls Saxo asynchronously, and persists trading data |
Watchlist Manager (watchlist_manager) |
Builds and refreshes cached best-turbo candidates for configured indices and directions |
Position Monitor (position_monitor) |
Monitors live Saxo position updates and handles stop-loss, take-profit, trailing-stop, and operational events |
Scheduler (scheduler) |
Sends scheduled operations to trading-ops and scheduled close signals to the Web Server |
| PostgreSQL | Primary runtime database for orders, positions, and trade performance |
| RabbitMQ | Carries trading-ops messages and Telegram notifications; the Trader no longer consumes trade signals from a RabbitMQ queue |
Telegram (telegram) |
Delivers trade and service notifications |
flowchart TD
classDef external fill:#f9f9f9,stroke:#aaa,stroke-width:1px,color:#000
classDef service fill:#e1f5fe,stroke:#03a9f4,stroke-width:1px,color:#000
classDef execution fill:#e8f5e9,stroke:#4caf50,stroke-width:1px,color:#000
classDef queue fill:#fff8e1,stroke:#ffc107,stroke-width:1px,color:#000
classDef database fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px,stroke-dasharray:5 5,color:#000
subgraph ExtSys["External Systems"]
direction LR
TV["TradingView"]
SaxoREST["Saxo Bank REST API"]
SaxoWS["Saxo Bank WebSocket"]
TG["Telegram"]
end
subgraph CoreSys["WATA Services"]
direction TB
Proxy["Traefik<br>HTTPS ingress"]:::service
WS["Web Server<br>latest-signal store"]:::service
TR["Async Trader<br>polls /latest-signals"]:::execution
WM["Watchlist Manager<br>best-turbo cache"]:::service
PM["Position Monitor"]:::execution
SC["Scheduler"]:::service
RMQ["RabbitMQ<br>trading-ops + telegram_channel"]:::queue
DB[("PostgreSQL 16")]:::database
end
TV -->|HTTPS webhook| Proxy --> WS
TR -->|GET /latest-signals every 500 ms| WS
WS -->|newest signal per indice| TR
TR -->|instrument search and orders| SaxoREST
TR -->|read and write trading data| DB
TR -->|cached candidate lookup| WM
WM -->|candidate search| SaxoREST
SaxoWS -->|live quote updates| WM
SaxoWS -->|position updates| PM
PM -->|position and performance data| DB
SC -->|scheduled operational messages| RMQ
RMQ -->|trading-ops| PM
SC -->|scheduled close signal| WS
TR -->|notifications| RMQ
PM -->|notifications| RMQ
RMQ -->|telegram_channel| TG
class TV,SaxoREST,SaxoWS,TG external
class Proxy,WS,WM,SC service
class TR,PM execution
-
Signal Reception
- Receive webhook traffic through Traefik HTTPS and validate the token and payload schema
- Keep the newest signal for each
indicein the Web Server's in-memory store - The Trader polls
/latest-signalsevery 500 ms by default; older signals for anindiceare ignored
-
Rule Validation
- Verify market hours and timestamp freshness
- Check allowed indices, duplicate positions, and configured risk limits
- Use the Watchlist Manager's cached candidates when available and validate instrument eligibility
-
Trade Execution
- Use asynchronous Saxo API calls for instrument search, order calculation, placement, and confirmation
- Persist orders, positions, and performance data in PostgreSQL
-
Real-Time Position Monitoring
- The Position Monitor consumes Saxo WebSocket updates instead of relying on the old 7-second REST polling loop
- Evaluate stop-loss, take-profit, and trailing-stop rules; receive scheduled operational events through
trading-ops
-
Reporting and Notifications
- Generate daily statistics and trade reports, including closed-trade and daily-profit history
- Send trade and service notifications through the Telegram service
PostgreSQL 16 is the primary runtime database. The application uses an asyncpg connection pool and creates its schema on startup.
- Order tracking: Orders and execution details
- Position management: Open and closed positions, including profit/loss data
- Performance analytics: Trade performance and daily statistics
- DuckDB migration:
src/database/migration.pyimports existing DuckDB data into PostgreSQL. DuckDB is no longer used by the live trading services.
WATA uses OAuth 2.0 for Saxo Bank API integration:
- When authenticating, the application send you a URL via Telegram (or logs of container)
- Open this URL in your browser and log in with your Saxo credentials, and do all the steps to authorize the application (2FA, risk warning, etc.)
- After authorization, you'll be redirected to a page with a URL containing a
codeparameter - Copy the code and run
watasaxoauthon your server (you will be prompted to enter the code securely) - The application completes authentication using this code
Command Reference:
watasaxoauth: Run this command and you'll be prompted to enter the authorization code securely (the code won't be visible when typing)- The authorization code is valid only for a short time (typically a few minutes)
Troubleshooting:
- If you receive an invalid/expired code error, repeat the process
- A "Timeout waiting for authorization code" error means the application waited for 5 minutes without receiving the code
Please refer to the dedicated docs pages for Saxo Authentication for more details.
WATA uses token-based authentication to secure webhook endpoints:
- Each webhook request must include a valid token in the URL (e.g.,
/webhook?token=YOUR_TOKEN) - This token is securely generated and stored with strong encryption
- Manage your webhook token with the
watawebtokencommand
Command Reference:
watawebtoken: View your current webhook tokenwatawebtoken --new: Generate a new webhook token- The command provides instructions on how to use the token in your webhook URL
Security Features:
- All tokens are encrypted before storage
- File permissions are restricted to the owner only
- The deployed Traefik route applies an IP allowlist for TradingView webhook traffic
In the Docker Compose deployment, Traefik redirects HTTP to HTTPS and obtains certificates through Let's Encrypt. Configure API_DOMAIN and ACME_EMAIL in deploy/.env, point the domain at the server, and allow inbound ports 80 and 443. HTTPS and the IP allowlist supplement the webhook token; they do not replace it.
For a comprehensive, step-by-step guide on setting up and using WATA, including:
- Detailed prerequisites and estimated costs
- Setting up Saxo Bank integration
- Configuring Telegram notifications
- Deploying and configuring WATA
- Setting up TradingView webhooks
- Starting and monitoring your trading
Please refer to our detailed HOW-TO Guide.
- Saxo Bank account with API access
- Dedicated Ubuntu server (on VPS or local machine)
- Docker and Docker Compose installed
- Python 3.12+
- Ansible (for automated deployment)
- Telegram bot for notifications
- (Optional) TradingView account for webhook signals
For detailed setup instructions, refer to our HOW-TO Guide.
-
Configure Inventory
- Copy the example Ansible inventory file:
cp deploy/tools/ansible/inventory/inventory_example.ini deploy/tools/ansible/inventory/inventory.ini
- Edit
inventory.iniwith your server details
- Copy the example Ansible inventory file:
-
Build Application
- Build the package:
./package.sh
- Build the package:
-
Deploy Application
- Run the deployment script:
cd deploy/tools ./deploy_app_to_your_server.sh - The script will use Ansible to deploy the application to your server
- Run the deployment script:
-
Configure Application
- On the server, set up your credentials in
etc/config.json(see below in Configuration section)
- On the server, set up your credentials in
-
Manage Application Use the following aliases on your server:
watastart: Start the applicationwatastop: Stop the applicationwatalogs: View application logswatastatus: Check application statuswatasaxoauth: Launch the secure authorization code submission processwatawebtoken: View your webhook authentication tokenwatawebtoken --new: Generate a new webhook token
The current stack is defined in deploy/docker-compose.yml; it does not use the old setup service or a separate Compose override. It includes Traefik, PostgreSQL, RabbitMQ, the Web Server, Trader, Scheduler, Telegram, Position Monitor, and Watchlist Manager.
The Compose file uses health checks and service dependencies for PostgreSQL, RabbitMQ, and Traefik. Traefik is the public HTTPS ingress; the other application services communicate over the internal Compose network.
After deployment, you need to set up your configuration:
-
Configure Environment Variables
# Navigate to the deploy directory cd /app/deploy # Copy the example .env file cp .env.example .env # Edit passwords and the public API hostname nano .env
Set
RABBITMQ_PASSWORD,ACME_EMAIL, andAPI_DOMAIN. Also set a strongPOSTGRES_PASSWORDin.env; the Compose file has a fallback if it is omitted, but it should not be used for a real deployment. PointAPI_DOMAINto the server so Traefik can issue the HTTPS certificate. -
Copy the Example Config
cp /app/etc/config_example.json /app/etc/config.json
-
Update Configuration Edit
/app/etc/config.jsonwith your specific settings:-
Saxo Bank Authentication
"authentication": { "saxo": { "app_config_object": { "AppName": "your_app_name", "AppKey": "your_app_key", "AppSecret": "your_app_secret" } } }
-
WebServer Security
"webserver": { "persistant": { "token_path": "/app/var/lib/web_server/persist_token.json" }, "app_secret": "YOUR_STRONG_SECRET_KEY" }
-
Telegram Notifications
"telegram": { "bot_token": "your_bot_token", "chat_id": "your_chat_id", "bot_name": "your_bot_name" }
-
Trading Rules
allowed_indices: Configure allowed trading indicesmarket_closed_dates: Set market holidaysday_trading: Define profit targets and limits
-
Other Settings
- Logging configuration
- RabbitMQ connection details
- PostgreSQL DSN and pool sizing (
postgresql) - Saxo streaming refresh and reconnect settings (
trade.config.general.streaming) - Cached candidate settings (
trade.config.watchlist_manager) - Signal polling endpoint and interval (
trade.config.signal_polling)
The
duckdbconfiguration is retained for the one-time migration source and legacy reporting export; it is not the live trading database. See the v0.6.x migration notes for the DuckDB-to-PostgreSQL migration steps.For a complete and detailed explanation of all configuration options, please refer to the CONFIGURATION Guide.
-
-
Restart Services
watastop watastart
For a deployed instance, send TradingView signals to the HTTPS hostname configured by API_DOMAIN. The Web Server stores only the newest signal per indice in memory; older timestamps for that index are ignored, and pending signals are not persisted across a Web Server restart. The Trader polls this store internally.
Send trading signals to:
POST /webhook?token=YOUR_SECRET_TOKEN
Payload:
{
"action": "long",
"indice": "us100",
"signal_timestamp": "2023-07-01T12:00:00Z",
"alert_timestamp": "2023-07-01T12:00:01Z"
}WATA generates trading reports and also includes a local visualization dashboard built on Observable Framework:
- Daily/cumulative profit tracking
- Performance analysis by action type
- Win-rate and position duration metrics
- Closed-trade and daily-profit history
- Current account balance and milestone projections in daily reports
- Interactive data exploration
(yeah, I'm still loosing money... but at least ... I know how much !)
Data-source note: PostgreSQL is the live runtime database, but the current Observable dashboard sync scripts still download the legacy DuckDB file from /app/wata/var/lib/duckdb/ and export it with the DuckDB CLI. They do not export records written to PostgreSQL after the migration. Treat this dashboard sync as a legacy DuckDB export until a PostgreSQL export path is added; application-generated daily reports use the runtime database.
To use the reporting system, you need:
- Node.js and npm installed
- DuckDB CLI for the existing legacy dashboard export
- Python 3.12+ (with required libraries
./reporting/requirements.txt) - Ansible configured with proper inventory (same as deployment stage)
-
Run the Setup Script
./reporting/setup_dashboard.sh
This script creates a new Observable Framework project in
reporting/trading-dashboard. -
Sync Trading Data
./reporting/sync_reporting_data.sh
This script synchronizes your trading data from the server to your local dashboard:
- Fetches the legacy DuckDB file from your production server
- Exports the database to Parquet format
- Generates the necessary JSON files for visualization
- Copies all data to the Observable Framework project
-
Start the Dashboard Server
./reporting/start_report_server.sh
This launches the development server on port 4321. Access the dashboard at: http://localhost:4321
Here are some of the most common questions about WATA:
- Is WATA suitable for beginners?
WATA requires understanding of trading concepts, Python, and server management. It's recommended for users with intermediate technical skills.
- What markets can I trade with WATA?
WATA supports trading Knock-out warrants (Turbos) available on Saxo Bank, primarily focusing on major indices.
- What are the minimum server requirements?
A basic VPS with 2GB RAM, 50GB SSD, 2 core CPU should be sufficient for getting started.
For a comprehensive list of questions and answers, please see our detailed FAQ documentation.
- @hootnot: Saxo OpenAPI library
- PostgreSQL, asyncpg, HTTPX, Observable Framework, DuckDB (migration and legacy reporting), FastAPI, RabbitMQ, Traefik, Ansible
- @ioiti: Project author and maintainer
- @hootnot: Saxo OpenAPI library
We welcome contributions to WATA! Here's how you can help:
- Report Issues: Submit bugs or suggest features via GitHub issues
- Submit Pull Requests: Code improvements, documentation fixes, or new features
- Share Feedback: Let us know how you're using WATA and what could be improved
MIT License
Copyright (c) 2025 IOITI
WATA have a Docusaurus-based documentation website. The website is hosted on GitHub Pages and can be accessed at https://ioiti.github.io/wata-docs/.
The documentation is organized into the following sections:
- Introduction: Overview of WATA
- How-To Guide: Step-by-step instructions for setting up and using WATA
- Configuration Guide: Details on how to configure WATA
- Saxo Authentication: Guide for authenticating with Saxo Bank's API
- FAQ: Frequently asked questions about WATA
We welcome contributions to the documentation! If you find any errors or have suggestions for improvements, please submit a pull request or open an issue on this GitHub repository.

