# 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`: 1. Drop rows where all of Open/High/Low/Close are NaN 2. Forward-fill small gaps (max 3 consecutive bars) 3. Drop rows still missing Close 4. Remove bars with future timestamps (> 5 min ahead of now) 5. Fix OHLC inconsistencies (e.g. High < Low) by clamping 6. Remove extreme outlier bars (> 10 standard deviations return) 7. 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 TF analyses. It collects all unique timeframes needed across scans, then makes one batch API call per timeframe (all pairs in a single request). Current SCAN_TIMEFRAMES: M15, H1 (only intraday trade signals) Confirmations: - M15 uses: H1, D1 - H1 uses: M15, D1 D1 is kept as a confirmation for higher-timeframe trend context but no D1 trade signals are produced. W1 has been dropped entirely. Unique timeframes: M15, H1, D1 = **3 batch calls** Each call: 7 pairs × 1 credit = **7 credits per call** Total per scan: **3 calls × 7 credits = 21 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 **Minimum scan time: ~2 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 aligned - `ranging`: 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]) │ ├── 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) │ └── 2 threads (M15 + H1), 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) ```