Compare commits
2 Commits
e4138ee60a
...
bc1844b233
| Author | SHA1 | Date | |
|---|---|---|---|
| bc1844b233 | |||
| 242df24ca7 |
@@ -0,0 +1,164 @@
|
|||||||
|
# ForexBot2 — Usage Guide
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Set your Twelve Data API key (or it falls back to the key in `config.py`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export TWELVE_DATA_API_KEY=your_key_here # Linux/Mac
|
||||||
|
set TWELVE_DATA_API_KEY=your_key_here # Windows CMD
|
||||||
|
$env:TWELVE_DATA_API_KEY="your_key_here" # PowerShell
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Basic Syntax
|
||||||
|
|
||||||
|
```
|
||||||
|
python main.py [OPTIONS]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Option | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `--pairs PAIR [PAIR ...]` | all major pairs | Specific pairs to analyze |
|
||||||
|
| `--timeframe TF` | `H1` | Primary timeframe: `M5 M15 M30 H1 H4 D1` |
|
||||||
|
| `--all` | off | Add EUR/GBP, EUR/JPY, GBP/JPY and other cross pairs |
|
||||||
|
| `--detail` | off | Full indicator breakdown per pair |
|
||||||
|
| `--top N` | all | Show only the top N signals by strength |
|
||||||
|
| `--min-grade GRADE` | `F` (show all) | Filter by minimum quality grade: `A+ A B C D F` |
|
||||||
|
| `--json` | off | Output results as JSON instead of the terminal UI |
|
||||||
|
| `--watch` | off | Continuous monitoring — refreshes every 5 minutes |
|
||||||
|
| `--verbose` / `-v` | off | Enable debug logging |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Examples
|
||||||
|
|
||||||
|
### Analyze all major Forex pairs (default)
|
||||||
|
```bash
|
||||||
|
python main.py
|
||||||
|
```
|
||||||
|
Analyzes EUR/USD, GBP/USD, USD/JPY, USD/CHF, AUD/USD, USD/CAD, NZD/USD on H1.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Analyze Gold (XAU/USD)
|
||||||
|
```bash
|
||||||
|
python main.py --pairs XAUUSD
|
||||||
|
```
|
||||||
|
Pass the symbol without the slash. The engine converts it to Twelve Data format automatically.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gold on the 4-hour chart with full detail
|
||||||
|
```bash
|
||||||
|
python main.py --pairs XAUUSD --timeframe H4 --detail
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Multiple pairs at once
|
||||||
|
```bash
|
||||||
|
python main.py --pairs XAUUSD EURUSD GBPUSD
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Only show high-conviction signals (Grade B or above)
|
||||||
|
```bash
|
||||||
|
python main.py --min-grade B
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Top 3 signals across all pairs including crosses
|
||||||
|
```bash
|
||||||
|
python main.py --all --top 3
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Continuous monitoring — updates every 5 minutes
|
||||||
|
```bash
|
||||||
|
python main.py --watch
|
||||||
|
```
|
||||||
|
Add `--pairs XAUUSD` to watch a single instrument, or `--all` for everything.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python main.py --pairs XAUUSD --watch --detail
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### JSON output (for piping or logging)
|
||||||
|
```bash
|
||||||
|
python main.py --pairs XAUUSD --json
|
||||||
|
python main.py --all --json > signals.json
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Signal Grades
|
||||||
|
|
||||||
|
| Grade | Meaning |
|
||||||
|
|---|---|
|
||||||
|
| A+ | Strongest — all timeframes aligned, high confidence |
|
||||||
|
| A | Strong signal, minor divergence |
|
||||||
|
| B | Good setup, watch for confirmation |
|
||||||
|
| C | Weak — for reference only |
|
||||||
|
| D/F | Noise — best ignored |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Supported Symbols
|
||||||
|
|
||||||
|
Pass any symbol without slashes. Examples:
|
||||||
|
|
||||||
|
| You type | Pair |
|
||||||
|
|---|---|
|
||||||
|
| `XAUUSD` | Gold vs USD |
|
||||||
|
| `EURUSD` | Euro vs USD |
|
||||||
|
| `GBPUSD` | British Pound vs USD |
|
||||||
|
| `USDJPY` | USD vs Japanese Yen |
|
||||||
|
| `GBPJPY` | GBP vs Japanese Yen |
|
||||||
|
| `USDCAD` | USD vs Canadian Dollar |
|
||||||
|
| `AUDUSD` | Australian Dollar vs USD |
|
||||||
|
|
||||||
|
Any pair supported by Twelve Data can be used.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Output Explained
|
||||||
|
|
||||||
|
```
|
||||||
|
EUR/USD │ Price: 1.08423 │ Regime: trending_up │ Vol: normal
|
||||||
|
|
||||||
|
SIGNAL: STRONG BUY │ Score: +62.4 │ Grade: A │ Confidence: 74%
|
||||||
|
Action: Strong buy signal with multi-timeframe alignment...
|
||||||
|
|
||||||
|
Multi-Timeframe: ✓ ALIGNED
|
||||||
|
M15: BUY (+28.1)
|
||||||
|
H1: STRONG BUY (+62.4)
|
||||||
|
H4: BUY (+41.0)
|
||||||
|
|
||||||
|
Trade Setup:
|
||||||
|
Direction: BUY
|
||||||
|
Entry: 1.08423
|
||||||
|
Stop Loss: 1.08150 (27.3 pips)
|
||||||
|
Take Profit 1: 1.08970 (54.7 pips)
|
||||||
|
Take Profit 2: 1.09517
|
||||||
|
Risk:Reward: 1:2.0
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Score** — ranges from -100 (strong sell) to +100 (strong buy)
|
||||||
|
- **Grade** — summary quality of the signal setup
|
||||||
|
- **Regime** — `trending_up`, `trending_down`, or `ranging`
|
||||||
|
- **Trade Setup** — auto-calculated entry, SL, and TP based on ATR
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -76,7 +76,7 @@ class TechnicalConfig:
|
|||||||
rsi_neutral_upper: float = 60.0
|
rsi_neutral_upper: float = 60.0
|
||||||
rsi_neutral_lower: float = 40.0
|
rsi_neutral_lower: float = 40.0
|
||||||
|
|
||||||
# MACD (12, 26, 9 is standard but we validate)
|
# MACD (12, 26, 9 is standard)
|
||||||
macd_fast: int = 12
|
macd_fast: int = 12
|
||||||
macd_slow: int = 26
|
macd_slow: int = 26
|
||||||
macd_signal: int = 9
|
macd_signal: int = 9
|
||||||
@@ -110,6 +110,9 @@ class TechnicalConfig:
|
|||||||
# Pivot points lookback
|
# Pivot points lookback
|
||||||
pivot_lookback: int = 1
|
pivot_lookback: int = 1
|
||||||
|
|
||||||
|
# Divergence detection — how many bars back to scan for swing points
|
||||||
|
divergence_lookback: int = 14
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SignalConfig:
|
class SignalConfig:
|
||||||
@@ -130,10 +133,6 @@ class SignalConfig:
|
|||||||
# Multi-timeframe confirmation requirements
|
# Multi-timeframe confirmation requirements
|
||||||
mtf_agreement_threshold: int = 2 # out of 3 timeframes must agree
|
mtf_agreement_threshold: int = 2 # out of 3 timeframes must agree
|
||||||
|
|
||||||
# Divergence detection sensitivity
|
|
||||||
divergence_lookback: int = 14
|
|
||||||
divergence_min_swing: float = 0.001 # minimum price swing for valid divergence
|
|
||||||
|
|
||||||
# Signal cooldown (prevent over-signalling same direction)
|
# Signal cooldown (prevent over-signalling same direction)
|
||||||
cooldown_bars: int = 5
|
cooldown_bars: int = 5
|
||||||
|
|
||||||
@@ -185,10 +184,13 @@ class PlatformConfig:
|
|||||||
# Data source
|
# Data source
|
||||||
data_lookback_days: int = 60 # enough for 200 EMA on H1
|
data_lookback_days: int = 60 # enough for 200 EMA on H1
|
||||||
|
|
||||||
|
# Twelve Data API key (overridden by TWELVE_DATA_API_KEY env var)
|
||||||
|
twelve_data_api_key: str = "f563b22ffdfd4924955decbe1a8c8701"
|
||||||
|
|
||||||
# Dashboard refresh interval (seconds)
|
# Dashboard refresh interval (seconds)
|
||||||
refresh_interval: int = 300 # 5 minutes
|
refresh_interval: int = 300 # 5 minutes
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
log_level: str = "INFO"
|
log_level: str = "INFO"
|
||||||
log_signals_to_file: bool = True
|
log_signals_to_file: bool = True
|
||||||
signal_log_path: str = "signals.log"
|
signal_log_path: str = "signals.log"
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
numpy>=1.24
|
||||||
|
pandas>=2.0
|
||||||
|
requests>=2.28
|
||||||
+24
-23
@@ -16,16 +16,16 @@ Key principles:
|
|||||||
import logging
|
import logging
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Dict, List, Optional, Tuple
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
from config import (
|
from config import (
|
||||||
PlatformConfig, SignalStrength, Timeframe,
|
PlatformConfig, SignalStrength, Timeframe,
|
||||||
SYMBOL_DISPLAY, MAJOR_PAIRS, CROSS_PAIRS,
|
SYMBOL_DISPLAY,
|
||||||
)
|
)
|
||||||
from data_provider import ForexDataProvider
|
from data_provider import ForexDataProvider
|
||||||
from technical_engine import TechnicalEngine, TechnicalAnalysis
|
from engine import TechnicalEngine, TechnicalAnalysis
|
||||||
from news_engine import NewsEngine, FundamentalContext
|
from news_engine import NewsEngine, FundamentalContext
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -99,7 +99,7 @@ class SignalReport:
|
|||||||
class SignalAggregator:
|
class SignalAggregator:
|
||||||
"""
|
"""
|
||||||
Master signal aggregator.
|
Master signal aggregator.
|
||||||
|
|
||||||
Orchestrates data fetching, technical analysis across timeframes,
|
Orchestrates data fetching, technical analysis across timeframes,
|
||||||
fundamental analysis, and produces final graded signals.
|
fundamental analysis, and produces final graded signals.
|
||||||
"""
|
"""
|
||||||
@@ -118,6 +118,12 @@ class SignalAggregator:
|
|||||||
reports = []
|
reports = []
|
||||||
context = self.news_engine.get_fundamental_context()
|
context = self.news_engine.get_fundamental_context()
|
||||||
|
|
||||||
|
# Batch-prefetch all pairs × all needed timeframes (3 API calls instead of 21)
|
||||||
|
all_timeframes = (
|
||||||
|
[self.config.primary_timeframe] + self.config.confirmation_timeframes
|
||||||
|
)
|
||||||
|
self.data_provider.prefetch_batch(self.config.watch_pairs, all_timeframes)
|
||||||
|
|
||||||
for symbol in self.config.watch_pairs:
|
for symbol in self.config.watch_pairs:
|
||||||
try:
|
try:
|
||||||
report = self.analyze_pair(symbol, context)
|
report = self.analyze_pair(symbol, context)
|
||||||
@@ -135,9 +141,7 @@ class SignalAggregator:
|
|||||||
symbol: str,
|
symbol: str,
|
||||||
context: Optional[FundamentalContext] = None,
|
context: Optional[FundamentalContext] = None,
|
||||||
) -> SignalReport:
|
) -> SignalReport:
|
||||||
"""
|
"""Complete analysis of a single pair across all layers."""
|
||||||
Complete analysis of a single pair across all layers.
|
|
||||||
"""
|
|
||||||
if context is None:
|
if context is None:
|
||||||
context = self.news_engine.get_fundamental_context()
|
context = self.news_engine.get_fundamental_context()
|
||||||
|
|
||||||
@@ -227,7 +231,7 @@ class SignalAggregator:
|
|||||||
def _compute_final_signal(self, report: SignalReport):
|
def _compute_final_signal(self, report: SignalReport):
|
||||||
"""
|
"""
|
||||||
Compute the final signal by combining all layers.
|
Compute the final signal by combining all layers.
|
||||||
|
|
||||||
Priority:
|
Priority:
|
||||||
1. Event risk can downgrade or block signals
|
1. Event risk can downgrade or block signals
|
||||||
2. MTF agreement boosts confidence
|
2. MTF agreement boosts confidence
|
||||||
@@ -239,9 +243,8 @@ class SignalAggregator:
|
|||||||
|
|
||||||
# ── MTF adjustment ─────────────────────────────────────────
|
# ── MTF adjustment ─────────────────────────────────────────
|
||||||
if report.mtf_agreement:
|
if report.mtf_agreement:
|
||||||
# All timeframes agree — boost confidence
|
# All timeframes agree — boost confidence and blend scores
|
||||||
confidence = min(confidence + 15, 95)
|
confidence = min(confidence + 15, 95)
|
||||||
# Average the MTF scores for more stable signal
|
|
||||||
all_scores = list(report.mtf_scores.values())
|
all_scores = list(report.mtf_scores.values())
|
||||||
if all_scores:
|
if all_scores:
|
||||||
mtf_avg = sum(all_scores) / len(all_scores)
|
mtf_avg = sum(all_scores) / len(all_scores)
|
||||||
@@ -251,7 +254,6 @@ class SignalAggregator:
|
|||||||
confidence *= 0.7
|
confidence *= 0.7
|
||||||
if len(report.mtf_scores) >= 2:
|
if len(report.mtf_scores) >= 2:
|
||||||
scores = list(report.mtf_scores.values())
|
scores = list(report.mtf_scores.values())
|
||||||
# Check for conflicting directions
|
|
||||||
has_bull = any(s > 10 for s in scores)
|
has_bull = any(s > 10 for s in scores)
|
||||||
has_bear = any(s < -10 for s in scores)
|
has_bear = any(s < -10 for s in scores)
|
||||||
if has_bull and has_bear:
|
if has_bull and has_bear:
|
||||||
@@ -259,7 +261,6 @@ class SignalAggregator:
|
|||||||
confidence *= 0.6
|
confidence *= 0.6
|
||||||
|
|
||||||
# ── News sentiment adjustment ──────────────────────────────
|
# ── News sentiment adjustment ──────────────────────────────
|
||||||
sent_weight = self.config.signals.weights.get("sentiment", 0.1)
|
|
||||||
if abs(report.news_sentiment) > 0.2:
|
if abs(report.news_sentiment) > 0.2:
|
||||||
# Sentiment aligns with technical
|
# Sentiment aligns with technical
|
||||||
if (report.news_sentiment > 0 and score > 0) or \
|
if (report.news_sentiment > 0 and score > 0) or \
|
||||||
@@ -274,7 +275,7 @@ class SignalAggregator:
|
|||||||
# ── Event risk adjustment ──────────────────────────────────
|
# ── Event risk adjustment ──────────────────────────────────
|
||||||
if report.event_risk == "danger":
|
if report.event_risk == "danger":
|
||||||
report.warnings.append(f"EVENT RISK: {report.event_description}")
|
report.warnings.append(f"EVENT RISK: {report.event_description}")
|
||||||
confidence *= 0.3 # Heavily penalize
|
confidence *= 0.3 # Heavily penalize — event will disrupt price action
|
||||||
report.action_recommendation = f"WAIT — {report.event_description}"
|
report.action_recommendation = f"WAIT — {report.event_description}"
|
||||||
elif report.event_risk == "caution":
|
elif report.event_risk == "caution":
|
||||||
report.warnings.append(f"Upcoming event: {report.event_description}")
|
report.warnings.append(f"Upcoming event: {report.event_description}")
|
||||||
@@ -325,7 +326,7 @@ class SignalAggregator:
|
|||||||
elif report.signal_quality == "B":
|
elif report.signal_quality == "B":
|
||||||
report.action_recommendation = f"{sig_name} — Decent setup, wait for confirmation"
|
report.action_recommendation = f"{sig_name} — Decent setup, wait for confirmation"
|
||||||
elif report.signal_quality in ("C", "D"):
|
elif report.signal_quality in ("C", "D"):
|
||||||
report.action_recommendation = f"WATCH — Signal present but low confidence"
|
report.action_recommendation = "WATCH — Signal present but low confidence"
|
||||||
else:
|
else:
|
||||||
report.action_recommendation = "NO TRADE — No clear signal"
|
report.action_recommendation = "NO TRADE — No clear signal"
|
||||||
|
|
||||||
@@ -379,7 +380,14 @@ class SignalAggregator:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _collect_reasons(self, report: SignalReport):
|
def _collect_reasons(self, report: SignalReport):
|
||||||
"""Collect bullish/bearish reasons from all indicators."""
|
"""
|
||||||
|
Collect bullish/bearish reasons from all indicators.
|
||||||
|
|
||||||
|
Each indicator's description is captured once via the indicators
|
||||||
|
dict. Pattern signals (candlestick) are already included in the
|
||||||
|
'candlestick' indicator description, so we do NOT re-process
|
||||||
|
pattern_signals to avoid duplicate entries.
|
||||||
|
"""
|
||||||
if not report.primary_analysis:
|
if not report.primary_analysis:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -394,15 +402,8 @@ class SignalAggregator:
|
|||||||
prefix = "STRONG: " if ind.signal == SignalStrength.STRONG_SELL else ""
|
prefix = "STRONG: " if ind.signal == SignalStrength.STRONG_SELL else ""
|
||||||
report.bearish_reasons.append(f"{prefix}{ind.name}: {ind.description}")
|
report.bearish_reasons.append(f"{prefix}{ind.name}: {ind.description}")
|
||||||
|
|
||||||
# Add pattern signals
|
|
||||||
for pattern in report.primary_analysis.pattern_signals:
|
|
||||||
if any(kw in pattern.lower() for kw in ["bullish", "hammer", "buy"]):
|
|
||||||
report.bullish_reasons.append(f"Pattern: {pattern}")
|
|
||||||
elif any(kw in pattern.lower() for kw in ["bearish", "shooting", "sell"]):
|
|
||||||
report.bearish_reasons.append(f"Pattern: {pattern}")
|
|
||||||
|
|
||||||
# Add news context
|
# Add news context
|
||||||
if report.news_sentiment > 0.1:
|
if report.news_sentiment > 0.1:
|
||||||
report.bullish_reasons.append(f"News: {report.news_description}")
|
report.bullish_reasons.append(f"News: {report.news_description}")
|
||||||
elif report.news_sentiment < -0.1:
|
elif report.news_sentiment < -0.1:
|
||||||
report.bearish_reasons.append(f"News: {report.news_description}")
|
report.bearish_reasons.append(f"News: {report.news_description}")
|
||||||
|
|||||||
Reference in New Issue
Block a user