Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9.1 KiB
ForexBot2 — Engine Logic Reference
1. Data Provider (Twelve Data API)
What the API provides
Each API call returns an OHLCV time series for a currency pair:
| Field | Description |
|---|---|
| Datetime | UTC timestamp of the candle open |
| Open | Opening price |
| High | Highest price during the candle |
| Low | Lowest price during the candle |
| Close | Closing price |
| Volume | Tick volume (limited on Forex) |
Supported intervals: 1min, 5min, 15min, 30min, 1h, 4h, 1day, 1week.
Supported pairs (free tier — live data)
EURUSD, GBPUSD, USDJPY, USDCHF, AUDUSD, USDCAD, NZDUSD, EURGBP, EURJPY, GBPJPY, AUDJPY, EURCHF, CADJPY. All other symbols (XAU, XAG, indices) return no data on the free plan.
API credit limits (free tier)
- 8 credits per minute
- 800 credits per day
- 1 credit per symbol in a batch request (7 pairs in one request = 7 credits, not 7 requests)
Data validation and cleaning
After every fetch the data goes through _validate_and_clean:
- Drop rows where all of Open/High/Low/Close are NaN
- Forward-fill small gaps (max 3 consecutive bars)
- Drop rows still missing Close
- Remove bars with future timestamps (> 5 min ahead of now)
- Fix OHLC inconsistencies (e.g. High < Low) by clamping
- Remove extreme outlier bars (> 10 standard deviations return)
- Sort index and deduplicate (keep last on duplicates)
2. How Many API Calls Are Made
Per scan or /signal command
The bot does one shared prefetch before spawning the 4 TF analyses. It collects all unique timeframes needed across all scans, then makes one batch API call per timeframe (all pairs in a single request).
Current SCAN_TIMEFRAMES: M15, H1, D1, W1 Confirmations:
- M15 uses: H1, D1
- H1 uses: M15, D1
- D1 uses: H1, W1
- W1 uses: D1
Unique timeframes: M15, H1, D1, W1 = 4 batch calls
Each call: 7 pairs × 1 credit = 7 credits per call
Total per scan: 4 calls × 7 credits = 28 credits
With the 8 credits/minute limit:
- Call 1 (7 credits): fires immediately — 1 credit remaining
- Call 2 (7 credits): must wait ~60s for window to clear
- Call 3 (7 credits): waits another ~60s
- Call 4 (7 credits): waits another ~60s
Minimum scan time: ~3 minutes.
After the shared prefetch, the 4 concurrent TF analyses read from the in-memory cache and make zero additional API calls.
Cache behaviour
Results are cached per symbol+timeframe for refresh_interval seconds
(default in PlatformConfig). A second scan within that window skips all
API calls entirely and returns cached data instantly.
3. Technical Engine — How Signals Are Decided
Step 1 — Fetch OHLCV data
Primary timeframe data is fetched first. If it fails, the report is immediately graded F and returned — no further analysis is attempted. Confirmation timeframe data is fetched next; failures are logged as warnings but do not abort the analysis.
Step 2 — Compute 11 indicators
Each indicator returns: value, signal (STRONG_BUY / BUY / NEUTRAL / SELL / STRONG_SELL), confidence (0–100), and a weight.
| Indicator | Weight | What it measures |
|---|---|---|
| EMA Trend | 1.2 | Alignment of fast/medium/slow/trend EMAs |
| MACD | 1.1 | Momentum crossover and histogram direction |
| Ichimoku | 1.1 | Cloud position, TK cross, cloud colour |
| RSI | 1.0 | Overbought/oversold momentum |
| ADX | 1.0 | Trend strength and directional index |
| Bollinger Bands | 0.9 | Price position within volatility envelope |
| Stochastic | 0.8 | Oscillator crossover in OB/OS zones |
| Candlestick | 0.7 | Pattern detection (hammer, engulfing, doji…) |
| Divergence | 1.5 | RSI/MACD divergence from price (reversal) |
| Pivot Points | 0.6 | Classical pivot S/R relative to current price |
| ATR / Volume | 0.0 | Context only — not included in score |
Divergence has the highest weight (1.5) because divergences are strong reversal predictors. ATR and Volume have weight 0 — they inform the trade setup and volatility regime but do not vote on direction.
Step 3 — Classify market regime
Using ADX value and EMA direction:
trending_up/trending_down: ADX ≥ strong threshold + EMA alignedranging: ADX below threshold
The regime changes indicator weights for the composite score:
- Trending: EMA (×1.5), ADX (×1.3), MACD (×1.2) weighted up; RSI (×0.7) and Bollinger (×0.6) weighted down
- Ranging: RSI (×1.4), Bollinger (×1.4), Stochastic (×1.3) weighted up; EMA (×0.6) and ADX (×0.5) weighted down
Step 4 — Compute composite score (−100 to +100)
For each indicator:
weighted_score = signal_value × weight × regime_multiplier × (confidence / 100)
Signal values: STRONG_BUY = +2, BUY = +1, NEUTRAL = 0, SELL = −1, STRONG_SELL = −2
Final composite score = (sum of weighted scores / total weight) × 50
Composite signal thresholds:
- score ≥ +40 → STRONG BUY
- score ≥ +15 → BUY
- score ≤ −40 → STRONG SELL
- score ≤ −15 → SELL
- otherwise → NEUTRAL
Step 5 — Multi-timeframe (MTF) agreement check
All timeframe signals (primary + confirmations) are collected.
MTF agreement = True if at least mtf_agreement_threshold timeframes
all point bullish or all point bearish.
Effect on confidence:
- Agreement: confidence +15 (capped at 95); score blended with average of all TF scores (60% primary, 40% MTF average)
- Disagreement: confidence ×0.7
- Conflict (some bullish, some bearish): confidence ×0.7 × 0.6 = ×0.42
- warning added to report
Step 6 — Fundamental adjustments
Applied in order, each multiplying confidence:
| Condition | Confidence adjustment |
|---|---|
| News aligns with technical | +5 (capped 95) |
| News conflicts with technical | ×0.85 |
| Event risk: caution | ×0.70 |
| Event risk: danger | ×0.30 + block signal |
| Volatility: extreme | ×0.80 |
Step 7 — Final signal determination
Uses the adjusted score and confidence:
| Condition | Signal |
|---|---|
| score ≥ +40 and confidence ≥ 65 | STRONG BUY |
| score ≥ +15 and confidence ≥ 50 | BUY |
| score ≤ −40 and confidence ≥ 65 | STRONG SELL |
| score ≤ −15 and confidence ≥ 50 | SELL |
| otherwise | NEUTRAL |
Step 8 — Quality grade
Combines score, confidence, and MTF agreement into a letter grade:
| Grade | abs(score) | confidence | MTF agreement |
|---|---|---|---|
| A+ | ≥ 50 | ≥ 75 | required |
| A | ≥ 35 | ≥ 65 | required |
| B | ≥ 25 | ≥ 55 | not required |
| C | ≥ 15 | ≥ 45 | not required |
| D | ≥ 10 | any | not required |
| F | < 10 | any | — |
A+ and A require MTF agreement as a hard condition — no matter how strong the primary score, without timeframe alignment the grade caps at B.
Step 9 — Trade setup (grades A+, A, B only)
Entry = current close price Stop Loss = entry ± (ATR × 1.5) Take Profit 1 = entry ± (SL distance × R:R ratio) Take Profit 2 = entry ± (SL distance × R:R ratio × 1.5)
ATR is used as the volatility measure so SL/TP adapt to current market conditions rather than using fixed pip distances.
4. Signal Flow Summary
Telegram /scan or /signal
│
▼
_analyze_all_tfs(pairs)
│
├── prefetch_batch(pairs, [M15, H1, D1, W1])
│ ├── Batch call 1: all pairs @ M15 (7 credits, wait if needed)
│ ├── Batch call 2: all pairs @ H1 (7 credits, wait if needed)
│ ├── Batch call 3: all pairs @ D1 (7 credits, wait if needed)
│ └── Batch call 4: all pairs @ W1 (7 credits, wait if needed)
│
└── 4 threads (one per TF), all reading from warm cache
│
└── per thread: SignalAggregator.analyze_all_pairs()
│
└── per pair: analyze_pair()
├── fetch_ohlcv (cache hit)
├── TechnicalEngine.analyze()
│ ├── 11 indicators
│ ├── Regime classification
│ └── Composite score
├── MTF agreement check
├── News / event risk adjustments
├── Final signal + grade
└── Trade setup (A+/A/B only)