Signed-off-by: kaedwen <kaedwen@heinrich.blue>
AI Trading Application
AI-assisted trading application with Interactive Brokers integration, news aggregation, and configurable trading strategies.
Features
✅ Phase 1: Foundation
- Go module structure
- SQLite database with migrations
- Configuration via environment variables
- Structured JSON logging
- Graceful shutdown handling
✅ Phase 2: IB Gateway Integration
- Interactive Brokers API client
- Connection with retry logic and exponential backoff
- Account balance tracking (every 5 minutes)
- Order placement and cancellation
- Market data subscription support
✅ Phase 3: News Aggregation
- RSS feed integration (CNBC, MarketWatch, Reuters)
- Keyword-based sentiment analysis
- Article deduplication by URL
- Symbol extraction from news content
- Automatic news polling (configurable interval)
✅ Phase 4: Trading Strategies
- Three configurable strategies: defensive, normal, aggressive
- Risk parameters per strategy (max trades, position size, stop-loss)
- Sentiment-based trade signal generation
- Comprehensive test coverage
✅ Phase 5: Trade Execution Engine
- Pending trade workflow with configurable timeout
- Rate limiting (hourly and parallel trade limits)
- Trade repository with full lifecycle tracking
- Stop-loss order management
- Trade executor with order placement
- User approval/rejection of pending trades
- Force immediate execution option
✅ Phase 6: Web Dashboard
- HTML/JavaScript frontend with real-time updates
- Server-Sent Events (SSE) for live trade notifications
- OpenID Connect authentication via Authelia (optional)
- Trade management UI (approve/reject/force)
- Whitelist Management UI - Add/edit/disable trading symbols with WKN/ISIN
- Real-time balance and statistics display
- Tabbed interface (Overview/Trades/Whitelist)
- Responsive design with modal forms
- Health check endpoint
✅ Phase 8: LLM-Based Sentiment Analysis (Optional)
- Ollama integration for contextual sentiment analysis
- Ensemble mode: weighted average of LLM + keyword scoring
- Graceful fallback to keyword analyzer on LLM timeout
- Support for Mistral, Llama2, and other Ollama models
- Configurable temperature and timeout
- Enhanced accuracy for complex financial language
Status
Completed: Phases 1-6, 8 ✅
Production Ready: Backend complete with optional LLM sentiment enhancement
Optional: LLM-Based Sentiment Analysis
The application can optionally use a local LLM (via Ollama) for more accurate sentiment analysis:
Benefits:
- Context-aware: understands "beats expectations despite loss" as positive
- Handles negations, sarcasm, and hedging language
- Adaptive to new financial terminology
- Provides confidence scores for position sizing
Setup:
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
# Pull model (4GB)
ollama pull mistral
# Start Ollama server
ollama serve # Runs on http://localhost:11434
Enable in config:
llm_scorer:
enabled: true
endpoint: http://localhost:11434
model_name: mistral
timeout: 30
temperature: 0.3
ensemble_weight: 0.7 # 70% LLM + 30% keyword
Resource Requirements:
- CPU-only: 2-4s per article (async processing)
- GPU (NVIDIA 4GB+): 0.5-1s per article
- Memory: 4-8GB RAM
Fallback: If LLM times out or Ollama is unavailable, automatically falls back to keyword-based sentiment analysis.
Docker Deployment
Quick Start with Docker Compose
# Prepare data directory for distroless nonroot user
mkdir -p data && chown 65532:65532 data
# Build and run
docker-compose up -d
# View logs
docker-compose logs -f aitrade
# Stop
docker-compose down
Image Details
Base Image: Google Distroless (gcr.io/distroless/static-debian12:nonroot)
- Size: ~23 MB (with healthcheck binary)
- No CGO: Pure Go with
modernc.org/sqlite - Embedded Migrations: No external files needed
- Health Check: Built-in lightweight binary (no curl/wget)
- Security: Non-root user (UID 65532), minimal attack surface
- Includes: CA certificates, timezone data
Using Pre-built Image from Gitea Registry
# Pull from registry
docker pull gitea.yourdomain.com/yourusername/aitrade:latest
# Run with environment variables
docker run -d \
-p 8080:8080 \
-v $(pwd)/data:/app/data \
-e TRADING_STRATEGY=normal \
-e DRY_RUN=true \
-e LLM_SCORER_ENABLED=false \
--name aitrade \
gitea.yourdomain.com/yourusername/aitrade:latest
Docker with Ollama (LLM Sentiment)
# Start both services
docker-compose --profile llm up -d
# Pull Mistral model
docker exec ollama ollama pull mistral
# Enable LLM in aitrade
docker-compose exec aitrade sh -c 'export LLM_SCORER_ENABLED=true'
docker-compose restart aitrade
Build Locally
# Build image
docker build -t aitrade:local .
# Run
docker run -d -p 8080:8080 -v $(pwd)/data:/app/data aitrade:local
CI/CD Pipeline
The repository includes a Gitea Actions workflow (.gitea/workflows/docker.yaml) that automatically:
- Builds Docker image on push to main/master/develop
- Tags images with branch name, commit SHA, and semantic version
- Pushes to Gitea Container Registry
- Creates
latesttag for default branch
Triggered by:
- Push to main/master/develop branches
- Git tags matching
v*(e.g.,v1.0.0) - Pull requests (build only, no push)
Required Secret: GITEA_TOKEN with registry write permissions
Quick Start
Option 1: Native Binary
# Build
go build -o aitrade ./cmd/aitrade
# Run
./aitrade
Option 2: Docker (Recommended)
# Using Docker Compose
docker-compose up -d
# Or pull from registry
docker pull gitea.yourdomain.com/username/aitrade:latest
docker run -d -p 8080:8080 -v $(pwd)/data:/app/data gitea.yourdomain.com/username/aitrade:latest
Access
Open browser: http://localhost:8080
Note: IB Gateway must be running for broker integration. See full documentation in docs/DOCKER.md.
Configuration
The application supports two configuration methods:
Option 1: YAML Configuration (Recommended)
Create a config.yaml file in one of these locations:
./config.yaml(current directory)~/.config/aitrade/config.yaml/etc/aitrade/config.yaml- Custom path via
CONFIG_FILE=/path/to/config.yaml
Example config.yaml:
trading:
strategy: normal
dry_run: true
dry_run_balance: 100000.0
max_trade_value: 2000.0
watch_symbols:
- AAPL
- MSFT
- GOOGL
database:
path: ./data/aitrade.db
web:
port: "8080"
See config.example.yaml for a complete configuration file.
Option 2: Environment Variables
If no YAML file is found, the application uses environment variables:
# Interactive Brokers
IB_GATEWAY_HOST=127.0.0.1
IB_GATEWAY_PORT=4001
IB_CLIENT_ID=1
# Trading Strategy
TRADING_STRATEGY=normal # defensive, normal, aggressive
STOP_LOSS_ENABLED=true
STOP_LOSS_PERCENT=3.0
# Auto-Trading
TRADING_ENABLED=true # Enable automatic trade generation
TRADING_INTERVAL_SECONDS=60 # How often to analyze market (60s default)
WATCH_SYMBOLS=AAPL,MSFT,GOOGL,TSLA,AMZN # Symbols to monitor
# SELL Triggers
TAKE_PROFIT_PERCENT=5.0 # Sell when profit reaches 5%
HOLD_TIME_MINUTES=30 # Minimum hold time before selling
SELL_ON_NEGATIVE_SENTIMENT=true # Sell on negative news sentiment
# Rate Limiting
MAX_TRADES_PER_HOUR=6
MAX_PARALLEL_TRADES=5
PENDING_TIME_SECONDS=300 # 5 minutes
# Trade Limits
MAX_TRADE_VALUE=2000.0 # Absolute max $ per trade (0 = unlimited)
# Dry Run Mode
DRY_RUN=true # Enable paper trading (no real money)
DRY_RUN_BALANCE=100000.0 # Starting virtual balance
# Database
DB_PATH=./data/aitrade.db
# Web
WEB_PORT=8080
# News
NEWS_POLL_INTERVAL=300 # seconds
# LLM Sentiment Scorer (Optional - requires Ollama)
LLM_SCORER_ENABLED=false # Set to true to enable
LLM_SCORER_ENDPOINT=http://localhost:11434
LLM_SCORER_MODEL=mistral
LLM_SCORER_TIMEOUT_SECONDS=30
LLM_SCORER_TEMPERATURE=0.3
LLM_SCORER_ENSEMBLE_WEIGHT=0.7 # 0.0-1.0 (1.0 = LLM only, 0.7 = 70% LLM + 30% keyword)
# OpenID Connect (Authelia) - Optional
OIDC_ENABLED=false # Set to true to enable
OIDC_ISSUER=https://auth.example.com
OIDC_CLIENT_ID=aitrade
OIDC_CLIENT_SECRET=<secret>
OIDC_REDIRECT_URL=http://localhost:8080/callback
OIDC_SCOPES=openid,profile,email
API Endpoints
Public
GET /health- Health checkGET /callback- OIDC callback (when auth enabled)
Protected (requires auth if OIDC enabled)
GET /- Main dashboardGET /trades- Get all trades (JSON)GET /events- SSE stream for real-time updatesGET /api/balance- Get current balanceGET /api/news- Get recent newsPOST /api/trades/{id}/approve?force=bool- Approve tradePOST /api/trades/{id}/reject- Reject trade (JSON body:{"reason": "..."})
Web Dashboard Features
- 📊 Three Tabs: Overview / All Trades / Whitelist Management
- 💹 Real-time statistics (balance, active trades, pending trades)
- 📈 Complete trade history with reasoning and P&L
- ⏱️ Live countdown timers for pending trades
- ✅ One-click approve/reject/force actions
- 🛡️ Whitelist Management: Add/edit/disable symbols with WKN/ISIN identifiers
- ⚡ Only whitelisted & enabled symbols can execute trades
- 🔄 Server-Sent Events for instant updates
- 🔒 Optional OpenID Connect authentication via Authelia
- 📱 Responsive design with modal forms
Trading Logic
Position Sizing (Capital Allocation)
The system uses intelligent position sizing that considers:
-
Strategy Base Percentage
- Defensive: 1.5% of capital per trade
- Normal: 4.0% of capital per trade
- Aggressive: 7.5% of capital per trade
-
Confidence-Based Scaling
- High confidence (0.9) → Larger position
- Low confidence (0.5) → Smaller position
- Multiplier ranges:
- Defensive: 0.3x - 0.8x
- Normal: 0.5x - 1.0x
- Aggressive: 0.7x - 1.2x
-
Parallel Trade Allocation
- Capital is divided by
MAX_PARALLEL_TRADES - Each trade slot gets:
TotalCapital / MaxParallel - Example: $100k with 5 parallel → $20k per slot
- Prevents first trade from consuming all capital
- Capital is divided by
-
Absolute Maximum per Trade
MAX_TRADE_VALUEsets hard limit (default: 0 = unlimited)- If calculated trade exceeds limit → quantity reduced to fit
- If price too high for even 1 share → trade rejected
- Example: MAX_TRADE_VALUE=$2000, price $3000 → rejected
Formula:
capitalPerSlot = totalCapital / maxParallelTrades
adjustedPercent = basePercent * (0.5 + confidence * 0.5)
positionValue = capitalPerSlot * (adjustedPercent / 100)
quantity = floor(positionValue / currentPrice)
Example (Normal Strategy):
- Total: $100,000
- Max Parallel: 5
- Per Slot: $20,000
- Confidence: 0.72
- Base: 4.0%
- Multiplier: 0.86x
- Adjusted: 3.44%
- Position: $20,000 × 3.44% = $688
- Price: $180
- Quantity: 3 shares
SELL Triggers
Positions are automatically sold when:
- Take Profit: Profit ≥
TAKE_PROFIT_PERCENT(default: 5%) - Negative Sentiment: Strong negative news (if
SELL_ON_NEGATIVE_SENTIMENT=true) - Stop Loss: Loss ≥
STOP_LOSS_PERCENT(default: 3%)
All SELL trades require minimum hold time (HOLD_TIME_MINUTES) before execution.
go build -o aitrade ./cmd/aitrade
Running
./aitrade
Note: IB Gateway or TWS must be running and configured to accept API connections on the specified port.
Testing
go test ./...
Architecture
/projects/Private/aitrade/
├── cmd/aitrade/ # Application entry point
├── pkg/
│ ├── app/
│ │ ├── client/ # IB Gateway client
│ │ ├── news/ # News aggregation
│ │ ├── strategy/ # Trading strategies
│ │ └── app.go # Main orchestrator
│ ├── config/ # Configuration
│ ├── db/ # Database layer
│ └── model/ # Data models
└── migrations/ # SQL migrations
Strategy Comparison
| Strategy | Max Parallel | Max/Hour | Position Size | Stop-Loss | Sentiment Threshold |
|---|---|---|---|---|---|
| Defensive | 2 | 3 | 1.5% | 2% | >0.5 (3+ pos news) |
| Normal | 5 | 6 | 4.0% | 3% | >0.3 (2+ pos news) |
| Aggressive | 10 | 12 | 7.5% | 5% | >0.0 (1+ pos news) |
Database Schema
trades- Trade lifecycle tracking (pending → submitted → filled → completed)balances- Account balance snapshotsnews_articles- Aggregated news with sentiment scoresschema_migrations- Migration version tracking
License
Private project