trAIder: an AI trading system, built like production software.
trAIder is our own AI-driven trading system for US equities. Every trading day it evaluates thousands of stocks, decides for itself what to buy and sell, and is designed to run without a human in the loop. We have been developing and testing it since 2025. trAIder is an application in development and testing that is currently not offered to third parties; it is not an investment offer, and this page says nothing about returns.

Why we show it: trAIder is our lab. Here we are client and developer at the same time, and every mistake hits us. Everything we tell clients about data pipelines, LLM infrastructure, model validation and operations we tried on ourselves first. And because we document every result, this page also says what did not work.
Status September 2026: trAIder runs in forward paper trading with several parallel books that are rolled forward automatically every night. The broker connection is built and tested. A first pilot with real money is prepared and tied to criteria set in advance.
Two approaches, one platform
trAIder has two generations. The first approach (2025 to mid-2026) tried to learn a trading strategy from news and prices with reinforcement learning: language models turn financial articles into structured features, and a deep Q-learning agent learns buy and sell decisions from them. The second approach (since 2026) is deliberately simpler: a supervised model ranks stocks on price data, and the effort goes into validation, execution and risk control instead of the model.
The first approach did not live up to the expectations placed in it. But it produced the platform the second one runs on, and most of the lessons that carry the second one. That is why we describe both.
Approach 1: news, language models and reinforcement learning
The idea was that an agent can recognise strong price moves if, besides the price, it also knows what is being reported about a company. That took three things: a news pipeline that turns articles into machine-readable features, a training environment for the agent, and a platform on which both scale.

The news pipeline. The starting point is GDELT, a project that indexes news worldwide in over 100 languages but, for copyright reasons, only supplies metadata and URLs. The news data loader, written in Python and run as Kubernetes jobs, queries the GDELT API and automatically narrows time windows because the API returns only 250 hits per request. It then downloads the articles itself: with a real Chrome browser via undetected-chromedriver, because many financial sites are JavaScript-heavy and lock out simple HTTP clients. Every download is classified before it is processed further: valid article, cookie wall, bot challenge, Cloudflare error, access denied. Cookie walls are clicked away automatically in four stages, from known consent selectors through a CMP-independent overlay search to iframes. Domains that block permanently lock themselves out after five failed attempts. Several downloader pods work in parallel and fetch their work from PostgreSQL with SELECT … FOR UPDATE SKIP LOCKED, without coordination overhead and without duplicate downloads.
The raw HTML pages land in MinIO, an S3-compatible object store, metadata in TimescaleDB, and the cleaned texts as Parquet in a data lake that can be queried with SQL via Apache Drill. Cleaning is done by a language model: Meta-Llama-3.1-8B-Instruct, AWQ-quantised to INT4, removes advertising, navigation and newsletter boxes and returns structured JSON with paragraphs, lists and tables in which numbers must remain unchanged. This is how we processed 2.65 million articles. The language models run as vLLM servers on our own GPU platform.
From articles to features. Reinforcement learning needs observations of fixed length; news has none. So articles are not used as text but as a feature vector per stock and day. Two models share the work: a small model (Qwen2.5-7B-Instruct, AWQ) cheaply checks whether an article reports on company results or regulatory events at all. Only then does a large financial model based on Llama 3.1 with 70 billion parameters analyse the text: surprise positive or negative, guidance raised or lowered, strength of the statement, plus tone, promotional character and rumour flags. All answers are strictly schema-bound JSON, categorical values are one-hot encoded, with upstream checks against the most common misinterpretations. The result is a fixed-length vector per day and stock with temporal decay, stored in the database. There are no LLM calls during training. Every feature group carries a version number and a computation timestamp, so that when a prompt or model changes only the affected part needs recomputing.
The agent. The training environment is built in PyTorch. The agent is a Double DQN with experience replay; as networks we compared multi-layer perceptrons and a Conv1D-LSTM variant over time windows. Episodes are time windows per stock in which the agent can buy, hold or sell. The real work was in what textbooks leave out: outlier candles that dominate training, rewards that have to be clipped, a portfolio value that needs lower and upper bounds, fixed random seeds for reproducibility, a universe filter against illiquid and structurally unstable stocks, and a training GUI that draws every step of the agent live onto the chart via WebSocket (see below).
Why we stopped. The agent learned the training data, but not the market. On known stocks and known periods it looked good; on new stocks it was close to zero or below. Eight pre-registered experiments with different configurations changed nothing. A targeted counter-test, a supervised model on the same observations, then showed that the problem was not the training but the input data: it did not contain the signal we were looking for in usable strength, with or without news features. We froze the RL path and shut down the news pipeline. Both are recorded in the documentation, with date and reasoning.

Approach 2: a simpler model, harder testing
The second approach is an end-of-day system in four stages, each with its own versioned table in PostgreSQL: first, the investment universe is determined with the knowledge available on exactly that day. Then a supervised model computes a probability for every stock, which serves as a ranking. From that, buy and sell decisions for the next trading day are derived. Finally, these decisions are translated into orders.
Data. The foundation is more than eleven million daily prices for more than 6,000 US stocks since 2016, including those that have since disappeared from the exchange. Whoever tests only with stocks that still exist today tests with survivors. Every definition that enters a computation is a registry row with parameters and Git commit. Every backtest stores a fingerprint of all data it read and can freeze its inputs as a snapshot in MinIO, so that it can be repeated bit for bit months later. When our data vendor changed price histories retroactively, exactly this mechanism made it visible; we marked the affected older runs as unverifiable instead of rescuing them after the fact.
Validation. The most important rule in our documentation: a backtest earns paper trading, never live deployment. Whatever looks good in the backtest gets a paper book and has to prove itself there with real prices but without real money before real money is even discussed. Every hypothesis is written down beforehand, with the criteria on which it is allowed to fail, and tested exactly once. Evaluation is walk-forward: trained on one year, traded on the following one, over nine annual windows from 2018 to 2026, tested on stocks the model never saw in training. Controls run against every result: a random ranking with sixteen seeds, a variant with no signal at all to find biases in the test machinery, and a test with temporally shuffled prices that proves a result depends on the order of the days. Because the original data contained no bear year, we reloaded the history back to 2016. We estimate the range of possible losses with a block bootstrap over 30,000 simulated paths, and every strategy has to hold up at three times the trading costs.
No look into the future. Labels are used only for evaluation, never as model input. A model may never be tested on its own training year; the test machinery refuses. Decisions are made at the close and executed at the next open, because otherwise news after the close would feed into the same day’s decision, a mistake we found ourselves in approach 1. Prices are adjusted consistently for splits and dividends. A regression test checks that the model score for a day is byte-identical whether that day is the last one in the data or lies in the middle of the history.
From backtest to broker. The nightly roll-forward of the paper books and the backtest use the same function for a trading day; an acceptance test requires the live run to reproduce the stored backtest figures exactly. The broker connection speaks the broker’s web API via OAuth 1.0a, as our own implementation with 41 unit tests in which the other side is recomputed as well. Orders go into the opening auction as market-on-open, because its price is exactly the price the model calculates with. The transport layer never retries on its own: a timeout is precisely the moment at which the order may already have been accepted. As soon as real money is involved, the broker counts as the truth, not the internal book. The path there is a ladder with four rungs and criteria set in advance; a profitable pilot with three unexplained deviations counts as failed.
Risk. Before every order stand three independent controls that are brought together in exactly one place: the account mode, halts and risk limits. All are fail-closed. The default mode is off. Live mode needs two independent approvals, one of which is unreachable from the interface, and is blocked by a database trigger as long as no complete set of limits exists. The emergency stop is a Kubernetes CronJob with an impossible schedule that is triggered with a single command from any machine with cluster access; a counterpart to lift it deliberately does not exist. Automatic halts stop purchases, never sales. The limits are chosen so that none of them triggers in normal operation: if one fires regularly, that is a bug report, not a market signal.
The interface: trAIder GUI
An autonomous system does not need a control interface, but it needs an observation interface. The trAIder GUI is exactly that, and expressly not a place where a human overrides the system’s decisions. For every stock and every day it shows the price, above it the actual events as bands, the model score as a second band, buy and sell markers, holding phases and the cumulative profit and loss. Hits, false alarms, missed events and delay can be read directly from comparing the two bands. Added to that are one page per paper book with key figures, equity curve and order book, annual views for all backtests from 2018 to 2026 with automatic assignment of the correct out-of-sample model, and a walk-forward view that sets the return curves of all annual windows on a logarithmic axis against the band of the random control.

Technically the GUI is deliberately lean: one FastAPI process under Uvicorn on Python 3.12, server-rendered Jinja2 templates and hand-written ES modules without a build step, TradingView Lightweight Charts 5 with a custom canvas layer for the bands and a second chart pane for the model score, three WebSocket channels for live updates from running trainings, and psycopg 3 with a connection pool directly on PostgreSQL, without an ORM. Long lists are rendered in blocks of 500 rows and loaded via infinite scroll; an order book with around 3,700 rows per year loads its pages in about 16 instead of 590 milliseconds after a switch to a materialised CTE with lateral joins, measured on the development database.
We built the login ourselves, after weighing it against Keycloak, which is recorded in the documentation: Argon2id for passwords, server-side sessions instead of JWT so that a session can be revoked immediately, mandatory TOTP with ten one-time recovery codes, lockout after ten failed attempts and an audit log. The connection is secured with TLS using certificates from our own PKI, renewed automatically by cert-manager. The access rules live in a single middleware that denies everything by default; a test runs over the real route table and checks that every writing route requires administrator rights and every WebSocket authenticates before the connection is established. Exactly this test found three unprotected WebSockets. And one rule is written down as a test: the interface can only downgrade a broker account, from live to paper to off, never upgrade it.
The most unusual part is an MCP server that runs in the same process. Through it an AI assistant can list and clone experiments, activate a configuration, start a training run on the GPU cluster, query its progress and read the evaluated results, in the same export format the GUI’s download button delivers, so that the two never drift apart. The guard rails are deliberately hard: a run starts only with explicit confirmation, only for a previously activated configuration, and never while another run is active. The progress display computes its remaining time from the observed arrival rates and would rather report „stalled“ than a wrong estimate.

The platform
Everything runs on our own hardware. The core is a self-built GPU host with an Intel Core i9 (24 cores), 64 GB RAM, NVMe storage and a GeForce RTX 5080, set up entirely with Ansible, from CUDA and the NVIDIA Container Toolkit through kubeadm Kubernetes to Bind9 DNS, ArgoCD and WireGuard VPN. If the host fails, a playbook rebuilds it. For the large language models, two NVIDIA DGX Spark were added, which as vLLM servers serve, among others, the quantised 70-billion-parameter model; thanks to their large unified memory it fits on one Spark without being split. For the RTX 5080 we temporarily had to compile PyTorch for the new GPU architecture (sm_120) ourselves before official builds supported it.
On Kubernetes we deploy everything via GitOps: ArgoCD in the app-of-apps pattern, Helm charts per application, database schemas via Flyway migrations with separate bootstrap and migration apps for dev and prod, GPU access through the NVIDIA GPU Operator, MetalLB and external-dns for reachability in the network. Credentials live in OpenBao and reach the pods via the Vault Secrets Operator; there are none in the repository, in charts or in images. The data stack: PostgreSQL 17 for all state (we removed TimescaleDB again after repeated crashes of the parallel workers), MinIO for objects and snapshots, Parquet as the exchange format, Qdrant for vectors. Prometheus, Alertmanager, Grafana and Loki provide metrics, alerts, dashboards and logs; alerts go to the phone.
A rule from the operations handbook: an alert that has never fired is no proof of health. Every alert needs a positive control, triggered once deliberately and logged. We introduced it after our monitoring had reported „Healthy“ for months while it was no longer updating itself. Backups are restored nightly into an empty database and compared table by table with the original. Three bugs we found only that way.
What you get out of it
Nothing on this page is the strategy. Everything on this page is transferable: an LLM pipeline that processes millions of documents on our own hardware, data pipelines with verifiable provenance, validation with control groups instead of hope, guards against information from the future, systems that stop when in doubt, and operations that test their own alerts. That is exactly what our experts bring to your project, whether it is about financial data or something else entirely.

Technologies
| Area | In use |
|---|---|
| Languages and ML | Python, PyTorch, Double DQN with experience replay, MLP and Conv1D-LSTM, supervised ranking model, NumPy/pandas |
| Language models | Meta-Llama-3.1-8B-Instruct (AWQ INT4), Llama-3.1-based 70B financial model (Q4_K_M), Qwen2.5-7B-Instruct (AWQ), vLLM, schema-bound JSON |
| Data | PostgreSQL 17 (previously TimescaleDB), Flyway, MinIO (S3), Parquet, Apache Drill, Qdrant, GDELT, SEC EDGAR |
| Pipelines | Kubernetes Jobs and CronJobs, undetected-chromedriver, FOR UPDATE SKIP LOCKED, thread pools against vLLM continuous batching |
| Platform | Ansible, kubeadm, NVIDIA GPU Operator, CUDA, ArgoCD (app of apps), Helm, OpenBao + Vault Secrets Operator, MetalLB, external-dns, Bind9, WireGuard |
| Operations | Prometheus, Alertmanager, Grafana, Loki, ntfy, tested pg_dump restores |
| Execution | Broker web API via OAuth 1.0a (own implementation), market-on-open, fail-closed risk gate |
| Interface | FastAPI, Uvicorn, Jinja2, ES modules without a build step, TradingView Lightweight Charts 5, WebSockets, psycopg 3, Argon2id, TOTP, MCP server (FastMCP, Streamable HTTP) |
| Hardware | Intel Core i9-13900K, 64 GB DDR5, NVMe, GeForce RTX 5080, 2× NVIDIA DGX Spark |
trAIder is in development and testing by Deep Data Ocean GmbH and is currently not offered to third parties. Nothing on this page is investment advice, a solicitation to trade or a statement about returns.
