Add LOGIC.md — engine decision flow, API call count, indicator weights
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
# 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 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 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, 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)
|
||||
```
|
||||
Reference in New Issue
Block a user