Latest commit

History

270 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

ArgusSentinel

⚠️ TELEGRAM INTEGRATION REMOVED (2026-06-12). The Telethon/po_broker_bot integration described below has been deleted from the codebase (telegram_feed/, v1 main.py, navigator-driven loop, all TELEGRAM_* settings). Setup now only requires PO_SSID. The payout-first signals loop is the only trade driver. Telegram-related sections in this document are historical.

Telegram-driven binary options bot. Reads trade signals from po_broker_bot via Telegram, confirms with internal technical analysis, then places independent CALL/PUT trades via the PocketOption WebSocket API.

⚠️Risk Disclaimer: Binary options carry extreme risk of capital loss. Both the unofficial PocketOption API and the Telethon user session violate the respective platforms' Terms of Service. This project is for educational and research purposes only. Always use DEMO mode. Never risk money you cannot afford to lose.


How It Works

po_broker_bot (Telegram)
│
│ Telethon user session reads DMs
▼
Navigator drives po_broker_bot menus
│ → top pair + win rate (prediction screen)
│ → CALL/PUT direction (direction screen)
▼
Pair quality gate
│ win rate ≥ PAIR_SELECT_MIN_WIN_RATE (default 0.82)
▼
Internal TA Confluence Engine (5 signals)
│ RSI · MACD · Bollinger · EMA Cross · Candle Patterns
│ ≥ 3 signals must agree on same direction
▼
Decision logic
│ bot direction must match our TA direction
│ combined probability = (bot win% + our confluence score) / 2
▼
Risk Manager gates
│ min balance · max trades/hr · daily loss limit · cooldown
▼
PocketOption API (binaryoptionstoolsv2)
│ buy/sell(pair, $1.50, expiry)
│ Never clicks the martingale bot's amount button
│ Up to 6 trades open concurrently
▼
Background resolver (non-blocking)
│ poll_trade_outcome() → polls closed_deals() after expiry
│ WIN / LOSS / DRAW (check_win fallback if polling exhausted)
▼
decisions.jsonl + WinRateTracker + RiskManager

Architecture

PocketOptionBot/
├── main_v2.py # v2 entrypoint (--cycles N)
├── main.py # legacy entrypoint (unwired, kept)
│
├── config/
│ └── settings.py # All config via .env (Pydantic)
│
├── telegram_feed/
│ ├── client.py # TelegramSignalFeed (legacy listener)
│ ├── navigator.py # Button-drives po_broker_bot menus
│ ├── prediction_parser.py # Parses pair/win-rate screen
│ ├── direction_parser.py # Parses CALL/PUT direction screen
│ ├── pair_norm.py # Normalises pair labels → API symbols
│ └── parser.py # Legacy signal parser (kept)
│
├── broker/
│ └── po_api.py # PocketOptionAPIClient (buy/sell/check_win)
│ └── [connector/scraper/executor.py] # Legacy CDP modules (unwired)
│
├── signals/
│ ├── base.py # BaseSignal abstract class
│ ├── rsi.py # RSI (oversold/overbought)
│ ├── macd.py # MACD crossover
│ ├── bollinger.py # Bollinger Bands mean reversion
│ ├── ema_cross.py # EMA golden/death cross
│ ├── candle_pattern.py # Candlestick patterns
│ └── confluence.py # ConfluenceEngine: weighted scoring + ≥3 gate
│
├── strategy/
│ ├── manager_v2.py # v2 orchestrator (navigate→TA→decide→trade)
│ ├── decision.py # Pure agree/disagree + combined probability
│ ├── expiry.py # Nearest-allowed expiry selection
│ ├── trade_logger.py # decisions.jsonl writer + outcome backfill
│ ├── signal_gate.py # Legacy 3-gate filter (kept)
│ ├── win_rate.py # Per-pair win rate tracker (data/win_rates.json)
│ ├── risk.py # RiskManager (balance/hr/daily/cooldown)
│ └── manager.py # Legacy event-driven manager (kept)
│
├── data/
│ ├── candles.py # API candle dicts → o/h/l/c/v DataFrame
│ └── feed.py # Legacy price feed (kept)
│
├── tools/
│ ├── v2_smoke.py # One dry-run cycle smoke test
│ ├── gen_telegram_session.py # One-time Telethon session auth
│ └── test_telegram_feed.py # Live feed capture / debugging
│
├── utils/
│ └── logger.py # Loguru: logs/ + data/trades.jsonl
│
├── tests/ # 100 offline unit tests
│
└── docs/
└── superpowers/plans/
└── 2026-06-04-telebot-evolution.md # Full implementation plan

Quick Start

1. Prerequisites

  • Python 3.12+
  • An existing Telethon session authenticated to your Telegram account
    (run python3 tools/gen_telegram_session.py once if not set up)
  • A PocketOption SSID (copy the 42["auth",{...}] string from your browser's DevTools Network tab while logged in to pocketoption.com)

2. Install

cd~/code/openclaw/projects/PocketOptionBot
pip3 install -r requirements.txt

3. Configure

cp .env.example .env
# Edit .env with your credentials

Minimum required settings:

# TelegramTELEGRAM_API_ID=123456TELEGRAM_API_HASH=your_api_hashTELEGRAM_SESSION=~/.telebot/telegram.session# PocketOptionPO_SSID=42["auth",{"session":"...","isDemo":1,...}]# Safety (leave these until you are confident)TRADE_MODE=DEMODRY_RUN=trueSTAKE_AMOUNT=1.50

4. Smoke test (no trade placed)

python3 tools/v2_smoke.py
# Runs one full cycle: navigate → TA → decide → log (DRY_RUN forced true)# Check data/decisions.jsonl for the result

5. Run the bot

python3 main_v2.py # run until Ctrl-C
python3 main_v2.py --cycles 5 # run exactly 5 cycles then exit

Web Dashboard

A live monitoring + settings UI (FastAPI + WebSocket backend, zero-build vanilla JS frontend). Landscape layout: Performance (equity/P&L curve + win/loss) · Active Trades (live countdowns) · Trade History, plus an editable Settings tab. It reads the bot's output files and updates live over a WebSocket — it does not need the trading stack, an SSID, or Telegram.

Run it locally (one command)

git clone https://github.com/force-push/PocketOptionBot.git
cd PocketOptionBot
./scripts/run_dashboard.sh # venv + install + seed demo data + serve

Then open http://127.0.0.1:8787. Use --no-seed to keep existing data.

Or step by step (works on Windows too)

python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements-dashboard.txt
python tools/dashboard_demo.py # optional: seed synthetic demo data
python -m dashboard.server # http://127.0.0.1:8787

requirements-dashboard.txt is a minimal 5-package subset (FastAPI, uvicorn, watchfiles, pydantic-settings, python-dotenv) — no playwright / telethon / Rust wheel needed just to view the dashboard.

Demo data vs. live trading data

  • Demo (default above):tools/dashboard_demo.py writes deterministic synthetic data/decisions.jsonl + data/live_state.json so the UI is fully populated with no bot running. Re-run it any time to refresh.

  • Live: run the bot with the bridge enabled so it streams real state to the dashboard as it trades:

    DASHBOARD_ENABLED=true python3 main_v2.py

    Start the dashboard server in a second terminal; it picks up changes live.

Settings tab (writes to .env)

The Settings tab can edit configuration and save it back to .env. Safety rails are enforced server-side: secrets are masked and only written when changed, and flipping TRADE_MODE to LIVE is fail-closed — it requires explicit confirmation and an SSID that decodes as a live session. Most changes need a bot restart to take effect (the UI flags which).

Dashboard settings (.env)

VariableDefaultPurpose
DASHBOARD_ENABLEDfalsebot streams live state when true
DASHBOARD_HOST127.0.0.1bind address (keep localhost)
DASHBOARD_PORT8787server port
DASHBOARD_TOKEN(unset)when set, required to save settings (sent as a Bearer token)

🔒 Bind to 127.0.0.1 only. The Settings tab can change trading config, so do not expose the dashboard publicly without setting DASHBOARD_TOKEN.


Configuration Reference

All settings live in .env. See .env.example for the full list.

Critical safety settings

VariableDefaultDescription
TRADE_MODEDEMODEMO or LIVE. Hard-reset to DEMO if unset.
DRY_RUNtrueLog trades but never call buy/sell on the API.
STAKE_AMOUNT1.50Fixed stake per trade (USD).

v2 gate settings

VariableDefaultDescription
PAIR_SELECT_MIN_WIN_RATE0.0Minimum bot-stated win rate to consider a pair. Set to 0.82 for real runs; 0.0 disables the gate during testing.
DEFAULT_EXPIRY_SECONDS30Trade expiry. Snapped to nearest allowed value.
CLICK_TRADE_ANYWAYtrueAuto-click the "low-balance" nag page when it appears.
DECISIONS_LOG_PATHdata/decisions.jsonlPath for the structured decision log.
MIN_CONFLUENCE_SCORE0.35Minimum TA confluence score to agree with a signal (reduced from 0.75 to allow more trades during testing; actual threshold is adaptive based on signal agreement).
MIN_SIGNAL_AGREEMENT3Minimum number of signals that must agree on same direction (increased from 2 to 3 for stricter confluence).
BLOCKED_PAIRS["EURUSD_otc", "ETHUSD_otc"]List of pair API symbols (e.g., "EURUSD_otc") to skip during pair selection. When the bot presents a list of candidate pairs, the navigator cycles through and selects the first non-blocked pair, avoiding wasted analysis time. If all pairs are blocked, the cycle is skipped.
SHADOW_RECORD_MODEfalseResearch/data-collection mode (DEMO only). When true and TRADE_MODE=DEMO, the bot stops blocking at the TA-agreement, EV, and risk gates — it places the bot-direction trade anyway, tags the row shadow=true with would_skip_reason, and records the outcome. This builds an uncensored dataset (normally we only see outcomes for trades that passed every gate). Hard-guarded: ignored in LIVE; low_payout is still enforced; shadow outcomes are written to decisions.jsonl but do not feed the production win-rate tracker or risk stats. See Research & Calibration.

Risk settings

VariableDefaultDescription
MAX_TRADES_PER_HOUR10Rolling 1-hour cap.
MAX_DAILY_LOSS_USD20.0Daily loss stops trading for the day.
COOLDOWN_AFTER_LOSS_SECONDS120Pause after each loss.
MIN_BALANCE_MULTIPLIER5.0Balance must be ≥ this × stake to trade.

Telegram settings

VariableDefaultDescription
TELEGRAM_API_IDFrom my.telegram.org
TELEGRAM_API_HASHFrom my.telegram.org
TELEGRAM_SESSIONpo_sessionPath to .session file or session name.
SIGNAL_BOT_USERNAMEpo_broker_botThe Telegram bot to read from.

Technical Analysis Signals

All signals consume an o/h/l/c/v time-indexed DataFrame from data/candles.py.

SignalWeightCALL triggerPUT trigger
RSI0.20RSI < 30 (oversold)RSI > 70 (overbought)
MACD0.20MACD crosses above signalMACD crosses below signal
Bollinger0.20Price at lower band + revertingPrice at upper band + reverting
EMA Cross0.15Fast EMA crosses above slow EMAFast EMA crosses below slow EMA
Candle Patterns0.25Bullish engulfing / hammerBearish engulfing / shooting star

Confluence rule (updated 2026-06-09): the trade decision is now gated on MACD + EMA only (decision_signals) — both must agree on the same direction. RSI, Bollinger, and CandlePattern are still evaluated and recorded in the decision log for research, but they no longer affect whether a trade is taken. Data over ~410 trades showed only MACD/EMA carry a positive edge and that 3-signal agreement won less than 2-signal. See docs/signal-strategy-research.md for the full analysis and the candidate signals (ADX, ATR, Supertrend, …) queued for testing.


Decision Logic

Each cycle produces a DecisionRow appended to data/decisions.jsonl:

TRADE → bot direction matches our TA direction + both gates pass
SKIP → one of: no_direction · ta_disagree · ta_low_score · risk_blocked

Confidence (logged, not gated) — stored as combined_probability, shown in the dashboard as confidence (it is a heuristic score, not a calibrated probability):

confidence = (bot_win_rate + our_confluence_score) / 2

A learned, calibrated P(win) is also recorded per trade as calibrated_probability when a model exists — see Research & Calibration. It is display/diagnostic only and never influences trade decisions.


Decision Log (data/decisions.jsonl)

One JSON line per evaluated signal. Key fields:

{
"cycle_id": "20260605T042310-0001",
"pair_raw": "GBP/USD",
"pair_api": "GBPUSD",
"bot_win_rate": 0.90,
"bot_direction": "CALL",
"our_direction": "CALL",
"our_confluence_score": 0.78,
"agreement": true,
"combined_probability": 0.84,
"calibrated_probability": 0.61,
"decision": "TRADE",
"skip_reason": null,
"shadow": false,
"would_skip_reason": null,
"stake": 1.5,
"trade_id": "trade_abc123",
"outcome": "WIN",
"pnl": 1.28,
"pnl_currency": "USD",
"balance_before": 1000.0,
"balance_after": 1001.28,
"ts": "2026-06-05T04:23:10.000000+00:00"
}

Use this log to calibrate signal quality, tune gates, and identify which pairs perform well over time.


Research & Calibration

Tooling for understanding why trades win or lose and for improving the signal stack.

Signal-attribution report

python scripts/analyze_signals.py # full report
python scripts/analyze_signals.py --min-n 30 # raise the min sample for flags

Read-only analysis over data/decisions.jsonl. For resolved trades it reports, per signal, the win rate when the signal agrees / is neutral / opposes the traded direction (with Wilson CIs and lift), confluence-score and agreement-count buckets, per-pair win rates, the break-even edge vs observed payouts, and a censoring summary. It flags signals that hurt when they agree, look inverted (opposing beats base), or never fire. When shadow data exists it also breaks outcomes down by would_skip_reason.

Probability calibrator

A learned win-probability model (L2 logistic regression) that records a real calibrated_probability alongside the heuristic confidence.

python -m strategy.train_calibrator # train from decisions.jsonl → data/models/
  • strategy/probability_calibrator.py — model + graceful fallback to the heuristic mean when no model/sklearn is present (never raises).
  • Decision-inert: the calibrated value is display/diagnostic only; decide(), the EV gate, and risk sizing are unaffected.
  • Model artifacts in data/models/ are gitignored (regenerable). Retrain as data grows.

The model is only as good as the data. On the first ~300 trades it sits near AUC 0.53 (no better than the average) — keep it dormant until the dataset is larger.

Building an uncensored dataset (shadow mode)

The decision log is censored: outcomes only exist for trades that passed every gate, so the disagreement cases needed to calibrate signals are never observed. Enable SHADOW_RECORD_MODE=true (DEMO only) to trade and record the would-be-skipped cases:

# .env
TRADE_MODE=DEMO
SHADOW_RECORD_MODE=true

The bot then places bot-direction trades it would normally skip (no_direction, ta_disagree, negative_ev, risk_blocked), tags them shadow=true with the would_skip_reason, and records outcomes — without feeding the production win-rate tracker or risk stats. Let it run, then re-run analyze_signals.py to see which gates actually earn their keep.


Safety Invariants

These must never be weakened without explicit intent:

  1. DEMO by default.TRADE_MODE=DEMO is the hard default. Even an empty TRADE_MODE= env var resets to DEMO.
  2. Demo guard. Before any real buy, broker/po_api.py decodes isDemo from the SSID using the API-native is_demo() method (with SSID-string fallback). If SSID is live but TRADE_MODE=DEMO, the trade is aborted.
  3. DRY_RUN. When DRY_RUN=true, buy/sell logs the would-be trade and returns without calling the API.
  4. Navigator never clicks amount buttons. The only safe action is clicking pair names, "Start Autotrade", "Main Menu", and "Trade Anyway" nag buttons. Clicking an amount/stake button places a martingale bot trade with the bot's own tokens — our trades go through the PocketOption API exclusively.
  5. Single session writer. Only one process may use the Telethon session at a time. Ensure telebot/scripts/pocket_robot_trader.py is stopped before running main_v2.py.

Data Files

PathDescription
data/decisions.jsonlStructured log: one row per evaluated signal
data/win_rates.jsonPer-pair win rate tracker (persisted)
data/trades.jsonlLegacy trade log (kept for compatibility)
logs/bot.logHuman-readable rotating log (1 day, 7-day retention)

Testing

All 100 tests run fully offline — no network, no SSID, no Telegram credentials.

pytest # all tests
pytest tests/ -v # verbose
pytest tests/test_signals.py # one module

For a live smoke test against the real bot (no trade placed):

python3 tools/v2_smoke.py
python3 tools/v2_smoke.py --pair GBPUSD_otc # skip navigation, test TA only

Development Notes

  • All live-path code is async/await. Errors are caught per-iteration and logged; the bot never crashes on a single bad cycle.
  • Frozen dataclasses are used for immutable results (TelegramSignal, SignalResult, ConfluenceResult, DirectionScreen, PredictionScreen). Mutable state lives in RiskManager, WinRateTracker, and the API client.
  • Direction is always the string "CALL", "PUT", or None — never booleans or enums.
  • Imports are absolute from project root.
  • pandas-ta is not used (incompatible with newer Python). All indicators are pure pandas/numpy.

Progression Path

PhaseConfigPurpose
Smoke testDRY_RUN=trueVerify navigation + TA works end-to-end
Capture modePAIR_SELECT_MIN_WIN_RATE=0.0, DRY_RUN=trueObserve all signals, log everything
Gated testingPAIR_SELECT_MIN_WIN_RATE=0.82, DRY_RUN=trueVerify gate logic against real signals
Demo liveTRADE_MODE=DEMO, DRY_RUN=falseReal trades on demo account
LiveTRADE_MODE=LIVE, DRY_RUN=falseOnly after sustained demo profitability

Built with: Python 3.12+ · Telethon · binaryoptionstoolsv2 · Pydantic · Loguru · pandas/numpy

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

270 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

ArgusSentinel

⚠️ TELEGRAM INTEGRATION REMOVED (2026-06-12). The Telethon/po_broker_bot integration described below has been deleted from the codebase (telegram_feed/, v1 main.py, navigator-driven loop, all TELEGRAM_* settings). Setup now only requires PO_SSID. The payout-first signals loop is the only trade driver. Telegram-related sections in this document are historical.

Telegram-driven binary options bot. Reads trade signals from po_broker_bot via Telegram, confirms with internal technical analysis, then places independent CALL/PUT trades via the PocketOption WebSocket API.

⚠️Risk Disclaimer: Binary options carry extreme risk of capital loss. Both the unofficial PocketOption API and the Telethon user session violate the respective platforms' Terms of Service. This project is for educational and research purposes only. Always use DEMO mode. Never risk money you cannot afford to lose.


How It Works

po_broker_bot (Telegram)
│
│ Telethon user session reads DMs
▼
Navigator drives po_broker_bot menus
│ → top pair + win rate (prediction screen)
│ → CALL/PUT direction (direction screen)
▼
Pair quality gate
│ win rate ≥ PAIR_SELECT_MIN_WIN_RATE (default 0.82)
▼
Internal TA Confluence Engine (5 signals)
│ RSI · MACD · Bollinger · EMA Cross · Candle Patterns
│ ≥ 3 signals must agree on same direction
▼
Decision logic
│ bot direction must match our TA direction
│ combined probability = (bot win% + our confluence score) / 2
▼
Risk Manager gates
│ min balance · max trades/hr · daily loss limit · cooldown
▼
PocketOption API (binaryoptionstoolsv2)
│ buy/sell(pair, $1.50, expiry)
│ Never clicks the martingale bot's amount button
│ Up to 6 trades open concurrently
▼
Background resolver (non-blocking)
│ poll_trade_outcome() → polls closed_deals() after expiry
│ WIN / LOSS / DRAW (check_win fallback if polling exhausted)
▼
decisions.jsonl + WinRateTracker + RiskManager

Architecture

PocketOptionBot/
├── main_v2.py # v2 entrypoint (--cycles N)
├── main.py # legacy entrypoint (unwired, kept)
│
├── config/
│ └── settings.py # All config via .env (Pydantic)
│
├── telegram_feed/
│ ├── client.py # TelegramSignalFeed (legacy listener)
│ ├── navigator.py # Button-drives po_broker_bot menus
│ ├── prediction_parser.py # Parses pair/win-rate screen
│ ├── direction_parser.py # Parses CALL/PUT direction screen
│ ├── pair_norm.py # Normalises pair labels → API symbols
│ └── parser.py # Legacy signal parser (kept)
│
├── broker/
│ └── po_api.py # PocketOptionAPIClient (buy/sell/check_win)
│ └── [connector/scraper/executor.py] # Legacy CDP modules (unwired)
│
├── signals/
│ ├── base.py # BaseSignal abstract class
│ ├── rsi.py # RSI (oversold/overbought)
│ ├── macd.py # MACD crossover
│ ├── bollinger.py # Bollinger Bands mean reversion
│ ├── ema_cross.py # EMA golden/death cross
│ ├── candle_pattern.py # Candlestick patterns
│ └── confluence.py # ConfluenceEngine: weighted scoring + ≥3 gate
│
├── strategy/
│ ├── manager_v2.py # v2 orchestrator (navigate→TA→decide→trade)
│ ├── decision.py # Pure agree/disagree + combined probability
│ ├── expiry.py # Nearest-allowed expiry selection
│ ├── trade_logger.py # decisions.jsonl writer + outcome backfill
│ ├── signal_gate.py # Legacy 3-gate filter (kept)
│ ├── win_rate.py # Per-pair win rate tracker (data/win_rates.json)
│ ├── risk.py # RiskManager (balance/hr/daily/cooldown)
│ └── manager.py # Legacy event-driven manager (kept)
│
├── data/
│ ├── candles.py # API candle dicts → o/h/l/c/v DataFrame
│ └── feed.py # Legacy price feed (kept)
│
├── tools/
│ ├── v2_smoke.py # One dry-run cycle smoke test
│ ├── gen_telegram_session.py # One-time Telethon session auth
│ └── test_telegram_feed.py # Live feed capture / debugging
│
├── utils/
│ └── logger.py # Loguru: logs/ + data/trades.jsonl
│
├── tests/ # 100 offline unit tests
│
└── docs/
└── superpowers/plans/
└── 2026-06-04-telebot-evolution.md # Full implementation plan

Quick Start

1. Prerequisites

  • Python 3.12+
  • An existing Telethon session authenticated to your Telegram account
    (run python3 tools/gen_telegram_session.py once if not set up)
  • A PocketOption SSID (copy the 42["auth",{...}] string from your browser's DevTools Network tab while logged in to pocketoption.com)

2. Install

cd~/code/openclaw/projects/PocketOptionBot
pip3 install -r requirements.txt

3. Configure

cp .env.example .env
# Edit .env with your credentials

Minimum required settings:

# TelegramTELEGRAM_API_ID=123456TELEGRAM_API_HASH=your_api_hashTELEGRAM_SESSION=~/.telebot/telegram.session# PocketOptionPO_SSID=42["auth",{"session":"...","isDemo":1,...}]# Safety (leave these until you are confident)TRADE_MODE=DEMODRY_RUN=trueSTAKE_AMOUNT=1.50

4. Smoke test (no trade placed)

python3 tools/v2_smoke.py
# Runs one full cycle: navigate → TA → decide → log (DRY_RUN forced true)# Check data/decisions.jsonl for the result

5. Run the bot

python3 main_v2.py # run until Ctrl-C
python3 main_v2.py --cycles 5 # run exactly 5 cycles then exit

Web Dashboard

A live monitoring + settings UI (FastAPI + WebSocket backend, zero-build vanilla JS frontend). Landscape layout: Performance (equity/P&L curve + win/loss) · Active Trades (live countdowns) · Trade History, plus an editable Settings tab. It reads the bot's output files and updates live over a WebSocket — it does not need the trading stack, an SSID, or Telegram.

Run it locally (one command)

git clone https://github.com/force-push/PocketOptionBot.git
cd PocketOptionBot
./scripts/run_dashboard.sh # venv + install + seed demo data + serve

Then open http://127.0.0.1:8787. Use --no-seed to keep existing data.

Or step by step (works on Windows too)

python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements-dashboard.txt
python tools/dashboard_demo.py # optional: seed synthetic demo data
python -m dashboard.server # http://127.0.0.1:8787

requirements-dashboard.txt is a minimal 5-package subset (FastAPI, uvicorn, watchfiles, pydantic-settings, python-dotenv) — no playwright / telethon / Rust wheel needed just to view the dashboard.

Demo data vs. live trading data

  • Demo (default above):tools/dashboard_demo.py writes deterministic synthetic data/decisions.jsonl + data/live_state.json so the UI is fully populated with no bot running. Re-run it any time to refresh.

  • Live: run the bot with the bridge enabled so it streams real state to the dashboard as it trades:

    DASHBOARD_ENABLED=true python3 main_v2.py

    Start the dashboard server in a second terminal; it picks up changes live.

Settings tab (writes to .env)

The Settings tab can edit configuration and save it back to .env. Safety rails are enforced server-side: secrets are masked and only written when changed, and flipping TRADE_MODE to LIVE is fail-closed — it requires explicit confirmation and an SSID that decodes as a live session. Most changes need a bot restart to take effect (the UI flags which).

Dashboard settings (.env)

VariableDefaultPurpose
DASHBOARD_ENABLEDfalsebot streams live state when true
DASHBOARD_HOST127.0.0.1bind address (keep localhost)
DASHBOARD_PORT8787server port
DASHBOARD_TOKEN(unset)when set, required to save settings (sent as a Bearer token)

🔒 Bind to 127.0.0.1 only. The Settings tab can change trading config, so do not expose the dashboard publicly without setting DASHBOARD_TOKEN.


Configuration Reference

All settings live in .env. See .env.example for the full list.

Critical safety settings

VariableDefaultDescription
TRADE_MODEDEMODEMO or LIVE. Hard-reset to DEMO if unset.
DRY_RUNtrueLog trades but never call buy/sell on the API.
STAKE_AMOUNT1.50Fixed stake per trade (USD).

v2 gate settings

VariableDefaultDescription
PAIR_SELECT_MIN_WIN_RATE0.0Minimum bot-stated win rate to consider a pair. Set to 0.82 for real runs; 0.0 disables the gate during testing.
DEFAULT_EXPIRY_SECONDS30Trade expiry. Snapped to nearest allowed value.
CLICK_TRADE_ANYWAYtrueAuto-click the "low-balance" nag page when it appears.
DECISIONS_LOG_PATHdata/decisions.jsonlPath for the structured decision log.
MIN_CONFLUENCE_SCORE0.35Minimum TA confluence score to agree with a signal (reduced from 0.75 to allow more trades during testing; actual threshold is adaptive based on signal agreement).
MIN_SIGNAL_AGREEMENT3Minimum number of signals that must agree on same direction (increased from 2 to 3 for stricter confluence).
BLOCKED_PAIRS["EURUSD_otc", "ETHUSD_otc"]List of pair API symbols (e.g., "EURUSD_otc") to skip during pair selection. When the bot presents a list of candidate pairs, the navigator cycles through and selects the first non-blocked pair, avoiding wasted analysis time. If all pairs are blocked, the cycle is skipped.
SHADOW_RECORD_MODEfalseResearch/data-collection mode (DEMO only). When true and TRADE_MODE=DEMO, the bot stops blocking at the TA-agreement, EV, and risk gates — it places the bot-direction trade anyway, tags the row shadow=true with would_skip_reason, and records the outcome. This builds an uncensored dataset (normally we only see outcomes for trades that passed every gate). Hard-guarded: ignored in LIVE; low_payout is still enforced; shadow outcomes are written to decisions.jsonl but do not feed the production win-rate tracker or risk stats. See Research & Calibration.

Risk settings

VariableDefaultDescription
MAX_TRADES_PER_HOUR10Rolling 1-hour cap.
MAX_DAILY_LOSS_USD20.0Daily loss stops trading for the day.
COOLDOWN_AFTER_LOSS_SECONDS120Pause after each loss.
MIN_BALANCE_MULTIPLIER5.0Balance must be ≥ this × stake to trade.

Telegram settings

VariableDefaultDescription
TELEGRAM_API_IDFrom my.telegram.org
TELEGRAM_API_HASHFrom my.telegram.org
TELEGRAM_SESSIONpo_sessionPath to .session file or session name.
SIGNAL_BOT_USERNAMEpo_broker_botThe Telegram bot to read from.

Technical Analysis Signals

All signals consume an o/h/l/c/v time-indexed DataFrame from data/candles.py.

SignalWeightCALL triggerPUT trigger
RSI0.20RSI < 30 (oversold)RSI > 70 (overbought)
MACD0.20MACD crosses above signalMACD crosses below signal
Bollinger0.20Price at lower band + revertingPrice at upper band + reverting
EMA Cross0.15Fast EMA crosses above slow EMAFast EMA crosses below slow EMA
Candle Patterns0.25Bullish engulfing / hammerBearish engulfing / shooting star

Confluence rule (updated 2026-06-09): the trade decision is now gated on MACD + EMA only (decision_signals) — both must agree on the same direction. RSI, Bollinger, and CandlePattern are still evaluated and recorded in the decision log for research, but they no longer affect whether a trade is taken. Data over ~410 trades showed only MACD/EMA carry a positive edge and that 3-signal agreement won less than 2-signal. See docs/signal-strategy-research.md for the full analysis and the candidate signals (ADX, ATR, Supertrend, …) queued for testing.


Decision Logic

Each cycle produces a DecisionRow appended to data/decisions.jsonl:

TRADE → bot direction matches our TA direction + both gates pass
SKIP → one of: no_direction · ta_disagree · ta_low_score · risk_blocked

Confidence (logged, not gated) — stored as combined_probability, shown in the dashboard as confidence (it is a heuristic score, not a calibrated probability):

confidence = (bot_win_rate + our_confluence_score) / 2

A learned, calibrated P(win) is also recorded per trade as calibrated_probability when a model exists — see Research & Calibration. It is display/diagnostic only and never influences trade decisions.


Decision Log (data/decisions.jsonl)

One JSON line per evaluated signal. Key fields:

{
"cycle_id": "20260605T042310-0001",
"pair_raw": "GBP/USD",
"pair_api": "GBPUSD",
"bot_win_rate": 0.90,
"bot_direction": "CALL",
"our_direction": "CALL",
"our_confluence_score": 0.78,
"agreement": true,
"combined_probability": 0.84,
"calibrated_probability": 0.61,
"decision": "TRADE",
"skip_reason": null,
"shadow": false,
"would_skip_reason": null,
"stake": 1.5,
"trade_id": "trade_abc123",
"outcome": "WIN",
"pnl": 1.28,
"pnl_currency": "USD",
"balance_before": 1000.0,
"balance_after": 1001.28,
"ts": "2026-06-05T04:23:10.000000+00:00"
}

Use this log to calibrate signal quality, tune gates, and identify which pairs perform well over time.


Research & Calibration

Tooling for understanding why trades win or lose and for improving the signal stack.

Signal-attribution report

python scripts/analyze_signals.py # full report
python scripts/analyze_signals.py --min-n 30 # raise the min sample for flags

Read-only analysis over data/decisions.jsonl. For resolved trades it reports, per signal, the win rate when the signal agrees / is neutral / opposes the traded direction (with Wilson CIs and lift), confluence-score and agreement-count buckets, per-pair win rates, the break-even edge vs observed payouts, and a censoring summary. It flags signals that hurt when they agree, look inverted (opposing beats base), or never fire. When shadow data exists it also breaks outcomes down by would_skip_reason.

Probability calibrator

A learned win-probability model (L2 logistic regression) that records a real calibrated_probability alongside the heuristic confidence.

python -m strategy.train_calibrator # train from decisions.jsonl → data/models/
  • strategy/probability_calibrator.py — model + graceful fallback to the heuristic mean when no model/sklearn is present (never raises).
  • Decision-inert: the calibrated value is display/diagnostic only; decide(), the EV gate, and risk sizing are unaffected.
  • Model artifacts in data/models/ are gitignored (regenerable). Retrain as data grows.

The model is only as good as the data. On the first ~300 trades it sits near AUC 0.53 (no better than the average) — keep it dormant until the dataset is larger.

Building an uncensored dataset (shadow mode)

The decision log is censored: outcomes only exist for trades that passed every gate, so the disagreement cases needed to calibrate signals are never observed. Enable SHADOW_RECORD_MODE=true (DEMO only) to trade and record the would-be-skipped cases:

# .env
TRADE_MODE=DEMO
SHADOW_RECORD_MODE=true

The bot then places bot-direction trades it would normally skip (no_direction, ta_disagree, negative_ev, risk_blocked), tags them shadow=true with the would_skip_reason, and records outcomes — without feeding the production win-rate tracker or risk stats. Let it run, then re-run analyze_signals.py to see which gates actually earn their keep.


Safety Invariants

These must never be weakened without explicit intent:

  1. DEMO by default.TRADE_MODE=DEMO is the hard default. Even an empty TRADE_MODE= env var resets to DEMO.
  2. Demo guard. Before any real buy, broker/po_api.py decodes isDemo from the SSID using the API-native is_demo() method (with SSID-string fallback). If SSID is live but TRADE_MODE=DEMO, the trade is aborted.
  3. DRY_RUN. When DRY_RUN=true, buy/sell logs the would-be trade and returns without calling the API.
  4. Navigator never clicks amount buttons. The only safe action is clicking pair names, "Start Autotrade", "Main Menu", and "Trade Anyway" nag buttons. Clicking an amount/stake button places a martingale bot trade with the bot's own tokens — our trades go through the PocketOption API exclusively.
  5. Single session writer. Only one process may use the Telethon session at a time. Ensure telebot/scripts/pocket_robot_trader.py is stopped before running main_v2.py.

Data Files

PathDescription
data/decisions.jsonlStructured log: one row per evaluated signal
data/win_rates.jsonPer-pair win rate tracker (persisted)
data/trades.jsonlLegacy trade log (kept for compatibility)
logs/bot.logHuman-readable rotating log (1 day, 7-day retention)

Testing

All 100 tests run fully offline — no network, no SSID, no Telegram credentials.

pytest # all tests
pytest tests/ -v # verbose
pytest tests/test_signals.py # one module

For a live smoke test against the real bot (no trade placed):

python3 tools/v2_smoke.py
python3 tools/v2_smoke.py --pair GBPUSD_otc # skip navigation, test TA only

Development Notes

  • All live-path code is async/await. Errors are caught per-iteration and logged; the bot never crashes on a single bad cycle.
  • Frozen dataclasses are used for immutable results (TelegramSignal, SignalResult, ConfluenceResult, DirectionScreen, PredictionScreen). Mutable state lives in RiskManager, WinRateTracker, and the API client.
  • Direction is always the string "CALL", "PUT", or None — never booleans or enums.
  • Imports are absolute from project root.
  • pandas-ta is not used (incompatible with newer Python). All indicators are pure pandas/numpy.

Progression Path

PhaseConfigPurpose
Smoke testDRY_RUN=trueVerify navigation + TA works end-to-end
Capture modePAIR_SELECT_MIN_WIN_RATE=0.0, DRY_RUN=trueObserve all signals, log everything
Gated testingPAIR_SELECT_MIN_WIN_RATE=0.82, DRY_RUN=trueVerify gate logic against real signals
Demo liveTRADE_MODE=DEMO, DRY_RUN=falseReal trades on demo account
LiveTRADE_MODE=LIVE, DRY_RUN=falseOnly after sustained demo profitability

Built with: Python 3.12+ · Telethon · binaryoptionstoolsv2 · Pydantic · Loguru · pandas/numpy

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

270 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

ArgusSentinel

⚠️ TELEGRAM INTEGRATION REMOVED (2026-06-12). The Telethon/po_broker_bot integration described below has been deleted from the codebase (telegram_feed/, v1 main.py, navigator-driven loop, all TELEGRAM_* settings). Setup now only requires PO_SSID. The payout-first signals loop is the only trade driver. Telegram-related sections in this document are historical.

Telegram-driven binary options bot. Reads trade signals from po_broker_bot via Telegram, confirms with internal technical analysis, then places independent CALL/PUT trades via the PocketOption WebSocket API.

⚠️Risk Disclaimer: Binary options carry extreme risk of capital loss. Both the unofficial PocketOption API and the Telethon user session violate the respective platforms' Terms of Service. This project is for educational and research purposes only. Always use DEMO mode. Never risk money you cannot afford to lose.


How It Works

po_broker_bot (Telegram)
│
│ Telethon user session reads DMs
▼
Navigator drives po_broker_bot menus
│ → top pair + win rate (prediction screen)
│ → CALL/PUT direction (direction screen)
▼
Pair quality gate
│ win rate ≥ PAIR_SELECT_MIN_WIN_RATE (default 0.82)
▼
Internal TA Confluence Engine (5 signals)
│ RSI · MACD · Bollinger · EMA Cross · Candle Patterns
│ ≥ 3 signals must agree on same direction
▼
Decision logic
│ bot direction must match our TA direction
│ combined probability = (bot win% + our confluence score) / 2
▼
Risk Manager gates
│ min balance · max trades/hr · daily loss limit · cooldown
▼
PocketOption API (binaryoptionstoolsv2)
│ buy/sell(pair, $1.50, expiry)
│ Never clicks the martingale bot's amount button
│ Up to 6 trades open concurrently
▼
Background resolver (non-blocking)
│ poll_trade_outcome() → polls closed_deals() after expiry
│ WIN / LOSS / DRAW (check_win fallback if polling exhausted)
▼
decisions.jsonl + WinRateTracker + RiskManager

Architecture

PocketOptionBot/
├── main_v2.py # v2 entrypoint (--cycles N)
├── main.py # legacy entrypoint (unwired, kept)
│
├── config/
│ └── settings.py # All config via .env (Pydantic)
│
├── telegram_feed/
│ ├── client.py # TelegramSignalFeed (legacy listener)
│ ├── navigator.py # Button-drives po_broker_bot menus
│ ├── prediction_parser.py # Parses pair/win-rate screen
│ ├── direction_parser.py # Parses CALL/PUT direction screen
│ ├── pair_norm.py # Normalises pair labels → API symbols
│ └── parser.py # Legacy signal parser (kept)
│
├── broker/
│ └── po_api.py # PocketOptionAPIClient (buy/sell/check_win)
│ └── [connector/scraper/executor.py] # Legacy CDP modules (unwired)
│
├── signals/
│ ├── base.py # BaseSignal abstract class
│ ├── rsi.py # RSI (oversold/overbought)
│ ├── macd.py # MACD crossover
│ ├── bollinger.py # Bollinger Bands mean reversion
│ ├── ema_cross.py # EMA golden/death cross
│ ├── candle_pattern.py # Candlestick patterns
│ └── confluence.py # ConfluenceEngine: weighted scoring + ≥3 gate
│
├── strategy/
│ ├── manager_v2.py # v2 orchestrator (navigate→TA→decide→trade)
│ ├── decision.py # Pure agree/disagree + combined probability
│ ├── expiry.py # Nearest-allowed expiry selection
│ ├── trade_logger.py # decisions.jsonl writer + outcome backfill
│ ├── signal_gate.py # Legacy 3-gate filter (kept)
│ ├── win_rate.py # Per-pair win rate tracker (data/win_rates.json)
│ ├── risk.py # RiskManager (balance/hr/daily/cooldown)
│ └── manager.py # Legacy event-driven manager (kept)
│
├── data/
│ ├── candles.py # API candle dicts → o/h/l/c/v DataFrame
│ └── feed.py # Legacy price feed (kept)
│
├── tools/
│ ├── v2_smoke.py # One dry-run cycle smoke test
│ ├── gen_telegram_session.py # One-time Telethon session auth
│ └── test_telegram_feed.py # Live feed capture / debugging
│
├── utils/
│ └── logger.py # Loguru: logs/ + data/trades.jsonl
│
├── tests/ # 100 offline unit tests
│
└── docs/
└── superpowers/plans/
└── 2026-06-04-telebot-evolution.md # Full implementation plan

Quick Start

1. Prerequisites

  • Python 3.12+
  • An existing Telethon session authenticated to your Telegram account
    (run python3 tools/gen_telegram_session.py once if not set up)
  • A PocketOption SSID (copy the 42["auth",{...}] string from your browser's DevTools Network tab while logged in to pocketoption.com)

2. Install

cd~/code/openclaw/projects/PocketOptionBot
pip3 install -r requirements.txt

3. Configure

cp .env.example .env
# Edit .env with your credentials

Minimum required settings:

# TelegramTELEGRAM_API_ID=123456TELEGRAM_API_HASH=your_api_hashTELEGRAM_SESSION=~/.telebot/telegram.session# PocketOptionPO_SSID=42["auth",{"session":"...","isDemo":1,...}]# Safety (leave these until you are confident)TRADE_MODE=DEMODRY_RUN=trueSTAKE_AMOUNT=1.50

4. Smoke test (no trade placed)

python3 tools/v2_smoke.py
# Runs one full cycle: navigate → TA → decide → log (DRY_RUN forced true)# Check data/decisions.jsonl for the result

5. Run the bot

python3 main_v2.py # run until Ctrl-C
python3 main_v2.py --cycles 5 # run exactly 5 cycles then exit

Web Dashboard

A live monitoring + settings UI (FastAPI + WebSocket backend, zero-build vanilla JS frontend). Landscape layout: Performance (equity/P&L curve + win/loss) · Active Trades (live countdowns) · Trade History, plus an editable Settings tab. It reads the bot's output files and updates live over a WebSocket — it does not need the trading stack, an SSID, or Telegram.

Run it locally (one command)

git clone https://github.com/force-push/PocketOptionBot.git
cd PocketOptionBot
./scripts/run_dashboard.sh # venv + install + seed demo data + serve

Then open http://127.0.0.1:8787. Use --no-seed to keep existing data.

Or step by step (works on Windows too)

python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements-dashboard.txt
python tools/dashboard_demo.py # optional: seed synthetic demo data
python -m dashboard.server # http://127.0.0.1:8787

requirements-dashboard.txt is a minimal 5-package subset (FastAPI, uvicorn, watchfiles, pydantic-settings, python-dotenv) — no playwright / telethon / Rust wheel needed just to view the dashboard.

Demo data vs. live trading data

  • Demo (default above):tools/dashboard_demo.py writes deterministic synthetic data/decisions.jsonl + data/live_state.json so the UI is fully populated with no bot running. Re-run it any time to refresh.

  • Live: run the bot with the bridge enabled so it streams real state to the dashboard as it trades:

    DASHBOARD_ENABLED=true python3 main_v2.py

    Start the dashboard server in a second terminal; it picks up changes live.

Settings tab (writes to .env)

The Settings tab can edit configuration and save it back to .env. Safety rails are enforced server-side: secrets are masked and only written when changed, and flipping TRADE_MODE to LIVE is fail-closed — it requires explicit confirmation and an SSID that decodes as a live session. Most changes need a bot restart to take effect (the UI flags which).

Dashboard settings (.env)

VariableDefaultPurpose
DASHBOARD_ENABLEDfalsebot streams live state when true
DASHBOARD_HOST127.0.0.1bind address (keep localhost)
DASHBOARD_PORT8787server port
DASHBOARD_TOKEN(unset)when set, required to save settings (sent as a Bearer token)

🔒 Bind to 127.0.0.1 only. The Settings tab can change trading config, so do not expose the dashboard publicly without setting DASHBOARD_TOKEN.


Configuration Reference

All settings live in .env. See .env.example for the full list.

Critical safety settings

VariableDefaultDescription
TRADE_MODEDEMODEMO or LIVE. Hard-reset to DEMO if unset.
DRY_RUNtrueLog trades but never call buy/sell on the API.
STAKE_AMOUNT1.50Fixed stake per trade (USD).

v2 gate settings

VariableDefaultDescription
PAIR_SELECT_MIN_WIN_RATE0.0Minimum bot-stated win rate to consider a pair. Set to 0.82 for real runs; 0.0 disables the gate during testing.
DEFAULT_EXPIRY_SECONDS30Trade expiry. Snapped to nearest allowed value.
CLICK_TRADE_ANYWAYtrueAuto-click the "low-balance" nag page when it appears.
DECISIONS_LOG_PATHdata/decisions.jsonlPath for the structured decision log.
MIN_CONFLUENCE_SCORE0.35Minimum TA confluence score to agree with a signal (reduced from 0.75 to allow more trades during testing; actual threshold is adaptive based on signal agreement).
MIN_SIGNAL_AGREEMENT3Minimum number of signals that must agree on same direction (increased from 2 to 3 for stricter confluence).
BLOCKED_PAIRS["EURUSD_otc", "ETHUSD_otc"]List of pair API symbols (e.g., "EURUSD_otc") to skip during pair selection. When the bot presents a list of candidate pairs, the navigator cycles through and selects the first non-blocked pair, avoiding wasted analysis time. If all pairs are blocked, the cycle is skipped.
SHADOW_RECORD_MODEfalseResearch/data-collection mode (DEMO only). When true and TRADE_MODE=DEMO, the bot stops blocking at the TA-agreement, EV, and risk gates — it places the bot-direction trade anyway, tags the row shadow=true with would_skip_reason, and records the outcome. This builds an uncensored dataset (normally we only see outcomes for trades that passed every gate). Hard-guarded: ignored in LIVE; low_payout is still enforced; shadow outcomes are written to decisions.jsonl but do not feed the production win-rate tracker or risk stats. See Research & Calibration.

Risk settings

VariableDefaultDescription
MAX_TRADES_PER_HOUR10Rolling 1-hour cap.
MAX_DAILY_LOSS_USD20.0Daily loss stops trading for the day.
COOLDOWN_AFTER_LOSS_SECONDS120Pause after each loss.
MIN_BALANCE_MULTIPLIER5.0Balance must be ≥ this × stake to trade.

Telegram settings

VariableDefaultDescription
TELEGRAM_API_IDFrom my.telegram.org
TELEGRAM_API_HASHFrom my.telegram.org
TELEGRAM_SESSIONpo_sessionPath to .session file or session name.
SIGNAL_BOT_USERNAMEpo_broker_botThe Telegram bot to read from.

Technical Analysis Signals

All signals consume an o/h/l/c/v time-indexed DataFrame from data/candles.py.

SignalWeightCALL triggerPUT trigger
RSI0.20RSI < 30 (oversold)RSI > 70 (overbought)
MACD0.20MACD crosses above signalMACD crosses below signal
Bollinger0.20Price at lower band + revertingPrice at upper band + reverting
EMA Cross0.15Fast EMA crosses above slow EMAFast EMA crosses below slow EMA
Candle Patterns0.25Bullish engulfing / hammerBearish engulfing / shooting star

Confluence rule (updated 2026-06-09): the trade decision is now gated on MACD + EMA only (decision_signals) — both must agree on the same direction. RSI, Bollinger, and CandlePattern are still evaluated and recorded in the decision log for research, but they no longer affect whether a trade is taken. Data over ~410 trades showed only MACD/EMA carry a positive edge and that 3-signal agreement won less than 2-signal. See docs/signal-strategy-research.md for the full analysis and the candidate signals (ADX, ATR, Supertrend, …) queued for testing.


Decision Logic

Each cycle produces a DecisionRow appended to data/decisions.jsonl:

TRADE → bot direction matches our TA direction + both gates pass
SKIP → one of: no_direction · ta_disagree · ta_low_score · risk_blocked

Confidence (logged, not gated) — stored as combined_probability, shown in the dashboard as confidence (it is a heuristic score, not a calibrated probability):

confidence = (bot_win_rate + our_confluence_score) / 2

A learned, calibrated P(win) is also recorded per trade as calibrated_probability when a model exists — see Research & Calibration. It is display/diagnostic only and never influences trade decisions.


Decision Log (data/decisions.jsonl)

One JSON line per evaluated signal. Key fields:

{
"cycle_id": "20260605T042310-0001",
"pair_raw": "GBP/USD",
"pair_api": "GBPUSD",
"bot_win_rate": 0.90,
"bot_direction": "CALL",
"our_direction": "CALL",
"our_confluence_score": 0.78,
"agreement": true,
"combined_probability": 0.84,
"calibrated_probability": 0.61,
"decision": "TRADE",
"skip_reason": null,
"shadow": false,
"would_skip_reason": null,
"stake": 1.5,
"trade_id": "trade_abc123",
"outcome": "WIN",
"pnl": 1.28,
"pnl_currency": "USD",
"balance_before": 1000.0,
"balance_after": 1001.28,
"ts": "2026-06-05T04:23:10.000000+00:00"
}

Use this log to calibrate signal quality, tune gates, and identify which pairs perform well over time.


Research & Calibration

Tooling for understanding why trades win or lose and for improving the signal stack.

Signal-attribution report

python scripts/analyze_signals.py # full report
python scripts/analyze_signals.py --min-n 30 # raise the min sample for flags

Read-only analysis over data/decisions.jsonl. For resolved trades it reports, per signal, the win rate when the signal agrees / is neutral / opposes the traded direction (with Wilson CIs and lift), confluence-score and agreement-count buckets, per-pair win rates, the break-even edge vs observed payouts, and a censoring summary. It flags signals that hurt when they agree, look inverted (opposing beats base), or never fire. When shadow data exists it also breaks outcomes down by would_skip_reason.

Probability calibrator

A learned win-probability model (L2 logistic regression) that records a real calibrated_probability alongside the heuristic confidence.

python -m strategy.train_calibrator # train from decisions.jsonl → data/models/
  • strategy/probability_calibrator.py — model + graceful fallback to the heuristic mean when no model/sklearn is present (never raises).
  • Decision-inert: the calibrated value is display/diagnostic only; decide(), the EV gate, and risk sizing are unaffected.
  • Model artifacts in data/models/ are gitignored (regenerable). Retrain as data grows.

The model is only as good as the data. On the first ~300 trades it sits near AUC 0.53 (no better than the average) — keep it dormant until the dataset is larger.

Building an uncensored dataset (shadow mode)

The decision log is censored: outcomes only exist for trades that passed every gate, so the disagreement cases needed to calibrate signals are never observed. Enable SHADOW_RECORD_MODE=true (DEMO only) to trade and record the would-be-skipped cases:

# .env
TRADE_MODE=DEMO
SHADOW_RECORD_MODE=true

The bot then places bot-direction trades it would normally skip (no_direction, ta_disagree, negative_ev, risk_blocked), tags them shadow=true with the would_skip_reason, and records outcomes — without feeding the production win-rate tracker or risk stats. Let it run, then re-run analyze_signals.py to see which gates actually earn their keep.


Safety Invariants

These must never be weakened without explicit intent:

  1. DEMO by default.TRADE_MODE=DEMO is the hard default. Even an empty TRADE_MODE= env var resets to DEMO.
  2. Demo guard. Before any real buy, broker/po_api.py decodes isDemo from the SSID using the API-native is_demo() method (with SSID-string fallback). If SSID is live but TRADE_MODE=DEMO, the trade is aborted.
  3. DRY_RUN. When DRY_RUN=true, buy/sell logs the would-be trade and returns without calling the API.
  4. Navigator never clicks amount buttons. The only safe action is clicking pair names, "Start Autotrade", "Main Menu", and "Trade Anyway" nag buttons. Clicking an amount/stake button places a martingale bot trade with the bot's own tokens — our trades go through the PocketOption API exclusively.
  5. Single session writer. Only one process may use the Telethon session at a time. Ensure telebot/scripts/pocket_robot_trader.py is stopped before running main_v2.py.

Data Files

PathDescription
data/decisions.jsonlStructured log: one row per evaluated signal
data/win_rates.jsonPer-pair win rate tracker (persisted)
data/trades.jsonlLegacy trade log (kept for compatibility)
logs/bot.logHuman-readable rotating log (1 day, 7-day retention)

Testing

All 100 tests run fully offline — no network, no SSID, no Telegram credentials.

pytest # all tests
pytest tests/ -v # verbose
pytest tests/test_signals.py # one module

For a live smoke test against the real bot (no trade placed):

python3 tools/v2_smoke.py
python3 tools/v2_smoke.py --pair GBPUSD_otc # skip navigation, test TA only

Development Notes

  • All live-path code is async/await. Errors are caught per-iteration and logged; the bot never crashes on a single bad cycle.
  • Frozen dataclasses are used for immutable results (TelegramSignal, SignalResult, ConfluenceResult, DirectionScreen, PredictionScreen). Mutable state lives in RiskManager, WinRateTracker, and the API client.
  • Direction is always the string "CALL", "PUT", or None — never booleans or enums.
  • Imports are absolute from project root.
  • pandas-ta is not used (incompatible with newer Python). All indicators are pure pandas/numpy.

Progression Path

PhaseConfigPurpose
Smoke testDRY_RUN=trueVerify navigation + TA works end-to-end
Capture modePAIR_SELECT_MIN_WIN_RATE=0.0, DRY_RUN=trueObserve all signals, log everything
Gated testingPAIR_SELECT_MIN_WIN_RATE=0.82, DRY_RUN=trueVerify gate logic against real signals
Demo liveTRADE_MODE=DEMO, DRY_RUN=falseReal trades on demo account
LiveTRADE_MODE=LIVE, DRY_RUN=falseOnly after sustained demo profitability

Built with: Python 3.12+ · Telethon · binaryoptionstoolsv2 · Pydantic · Loguru · pandas/numpy

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

270 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

ArgusSentinel

⚠️ TELEGRAM INTEGRATION REMOVED (2026-06-12). The Telethon/po_broker_bot integration described below has been deleted from the codebase (telegram_feed/, v1 main.py, navigator-driven loop, all TELEGRAM_* settings). Setup now only requires PO_SSID. The payout-first signals loop is the only trade driver. Telegram-related sections in this document are historical.

Telegram-driven binary options bot. Reads trade signals from po_broker_bot via Telegram, confirms with internal technical analysis, then places independent CALL/PUT trades via the PocketOption WebSocket API.

⚠️Risk Disclaimer: Binary options carry extreme risk of capital loss. Both the unofficial PocketOption API and the Telethon user session violate the respective platforms' Terms of Service. This project is for educational and research purposes only. Always use DEMO mode. Never risk money you cannot afford to lose.


How It Works

po_broker_bot (Telegram)
│
│ Telethon user session reads DMs
▼
Navigator drives po_broker_bot menus
│ → top pair + win rate (prediction screen)
│ → CALL/PUT direction (direction screen)
▼
Pair quality gate
│ win rate ≥ PAIR_SELECT_MIN_WIN_RATE (default 0.82)
▼
Internal TA Confluence Engine (5 signals)
│ RSI · MACD · Bollinger · EMA Cross · Candle Patterns
│ ≥ 3 signals must agree on same direction
▼
Decision logic
│ bot direction must match our TA direction
│ combined probability = (bot win% + our confluence score) / 2
▼
Risk Manager gates
│ min balance · max trades/hr · daily loss limit · cooldown
▼
PocketOption API (binaryoptionstoolsv2)
│ buy/sell(pair, $1.50, expiry)
│ Never clicks the martingale bot's amount button
│ Up to 6 trades open concurrently
▼
Background resolver (non-blocking)
│ poll_trade_outcome() → polls closed_deals() after expiry
│ WIN / LOSS / DRAW (check_win fallback if polling exhausted)
▼
decisions.jsonl + WinRateTracker + RiskManager

Architecture

PocketOptionBot/
├── main_v2.py # v2 entrypoint (--cycles N)
├── main.py # legacy entrypoint (unwired, kept)
│
├── config/
│ └── settings.py # All config via .env (Pydantic)
│
├── telegram_feed/
│ ├── client.py # TelegramSignalFeed (legacy listener)
│ ├── navigator.py # Button-drives po_broker_bot menus
│ ├── prediction_parser.py # Parses pair/win-rate screen
│ ├── direction_parser.py # Parses CALL/PUT direction screen
│ ├── pair_norm.py # Normalises pair labels → API symbols
│ └── parser.py # Legacy signal parser (kept)
│
├── broker/
│ └── po_api.py # PocketOptionAPIClient (buy/sell/check_win)
│ └── [connector/scraper/executor.py] # Legacy CDP modules (unwired)
│
├── signals/
│ ├── base.py # BaseSignal abstract class
│ ├── rsi.py # RSI (oversold/overbought)
│ ├── macd.py # MACD crossover
│ ├── bollinger.py # Bollinger Bands mean reversion
│ ├── ema_cross.py # EMA golden/death cross
│ ├── candle_pattern.py # Candlestick patterns
│ └── confluence.py # ConfluenceEngine: weighted scoring + ≥3 gate
│
├── strategy/
│ ├── manager_v2.py # v2 orchestrator (navigate→TA→decide→trade)
│ ├── decision.py # Pure agree/disagree + combined probability
│ ├── expiry.py # Nearest-allowed expiry selection
│ ├── trade_logger.py # decisions.jsonl writer + outcome backfill
│ ├── signal_gate.py # Legacy 3-gate filter (kept)
│ ├── win_rate.py # Per-pair win rate tracker (data/win_rates.json)
│ ├── risk.py # RiskManager (balance/hr/daily/cooldown)
│ └── manager.py # Legacy event-driven manager (kept)
│
├── data/
│ ├── candles.py # API candle dicts → o/h/l/c/v DataFrame
│ └── feed.py # Legacy price feed (kept)
│
├── tools/
│ ├── v2_smoke.py # One dry-run cycle smoke test
│ ├── gen_telegram_session.py # One-time Telethon session auth
│ └── test_telegram_feed.py # Live feed capture / debugging
│
├── utils/
│ └── logger.py # Loguru: logs/ + data/trades.jsonl
│
├── tests/ # 100 offline unit tests
│
└── docs/
└── superpowers/plans/
└── 2026-06-04-telebot-evolution.md # Full implementation plan

Quick Start

1. Prerequisites

  • Python 3.12+
  • An existing Telethon session authenticated to your Telegram account
    (run python3 tools/gen_telegram_session.py once if not set up)
  • A PocketOption SSID (copy the 42["auth",{...}] string from your browser's DevTools Network tab while logged in to pocketoption.com)

2. Install

cd~/code/openclaw/projects/PocketOptionBot
pip3 install -r requirements.txt

3. Configure

cp .env.example .env
# Edit .env with your credentials

Minimum required settings:

# TelegramTELEGRAM_API_ID=123456TELEGRAM_API_HASH=your_api_hashTELEGRAM_SESSION=~/.telebot/telegram.session# PocketOptionPO_SSID=42["auth",{"session":"...","isDemo":1,...}]# Safety (leave these until you are confident)TRADE_MODE=DEMODRY_RUN=trueSTAKE_AMOUNT=1.50

4. Smoke test (no trade placed)

python3 tools/v2_smoke.py
# Runs one full cycle: navigate → TA → decide → log (DRY_RUN forced true)# Check data/decisions.jsonl for the result

5. Run the bot

python3 main_v2.py # run until Ctrl-C
python3 main_v2.py --cycles 5 # run exactly 5 cycles then exit

Web Dashboard

A live monitoring + settings UI (FastAPI + WebSocket backend, zero-build vanilla JS frontend). Landscape layout: Performance (equity/P&L curve + win/loss) · Active Trades (live countdowns) · Trade History, plus an editable Settings tab. It reads the bot's output files and updates live over a WebSocket — it does not need the trading stack, an SSID, or Telegram.

Run it locally (one command)

git clone https://github.com/force-push/PocketOptionBot.git
cd PocketOptionBot
./scripts/run_dashboard.sh # venv + install + seed demo data + serve

Then open http://127.0.0.1:8787. Use --no-seed to keep existing data.

Or step by step (works on Windows too)

python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements-dashboard.txt
python tools/dashboard_demo.py # optional: seed synthetic demo data
python -m dashboard.server # http://127.0.0.1:8787

requirements-dashboard.txt is a minimal 5-package subset (FastAPI, uvicorn, watchfiles, pydantic-settings, python-dotenv) — no playwright / telethon / Rust wheel needed just to view the dashboard.

Demo data vs. live trading data

  • Demo (default above):tools/dashboard_demo.py writes deterministic synthetic data/decisions.jsonl + data/live_state.json so the UI is fully populated with no bot running. Re-run it any time to refresh.

  • Live: run the bot with the bridge enabled so it streams real state to the dashboard as it trades:

    DASHBOARD_ENABLED=true python3 main_v2.py

    Start the dashboard server in a second terminal; it picks up changes live.

Settings tab (writes to .env)

The Settings tab can edit configuration and save it back to .env. Safety rails are enforced server-side: secrets are masked and only written when changed, and flipping TRADE_MODE to LIVE is fail-closed — it requires explicit confirmation and an SSID that decodes as a live session. Most changes need a bot restart to take effect (the UI flags which).

Dashboard settings (.env)

VariableDefaultPurpose
DASHBOARD_ENABLEDfalsebot streams live state when true
DASHBOARD_HOST127.0.0.1bind address (keep localhost)
DASHBOARD_PORT8787server port
DASHBOARD_TOKEN(unset)when set, required to save settings (sent as a Bearer token)

🔒 Bind to 127.0.0.1 only. The Settings tab can change trading config, so do not expose the dashboard publicly without setting DASHBOARD_TOKEN.


Configuration Reference

All settings live in .env. See .env.example for the full list.

Critical safety settings

VariableDefaultDescription
TRADE_MODEDEMODEMO or LIVE. Hard-reset to DEMO if unset.
DRY_RUNtrueLog trades but never call buy/sell on the API.
STAKE_AMOUNT1.50Fixed stake per trade (USD).

v2 gate settings

VariableDefaultDescription
PAIR_SELECT_MIN_WIN_RATE0.0Minimum bot-stated win rate to consider a pair. Set to 0.82 for real runs; 0.0 disables the gate during testing.
DEFAULT_EXPIRY_SECONDS30Trade expiry. Snapped to nearest allowed value.
CLICK_TRADE_ANYWAYtrueAuto-click the "low-balance" nag page when it appears.
DECISIONS_LOG_PATHdata/decisions.jsonlPath for the structured decision log.
MIN_CONFLUENCE_SCORE0.35Minimum TA confluence score to agree with a signal (reduced from 0.75 to allow more trades during testing; actual threshold is adaptive based on signal agreement).
MIN_SIGNAL_AGREEMENT3Minimum number of signals that must agree on same direction (increased from 2 to 3 for stricter confluence).
BLOCKED_PAIRS["EURUSD_otc", "ETHUSD_otc"]List of pair API symbols (e.g., "EURUSD_otc") to skip during pair selection. When the bot presents a list of candidate pairs, the navigator cycles through and selects the first non-blocked pair, avoiding wasted analysis time. If all pairs are blocked, the cycle is skipped.
SHADOW_RECORD_MODEfalseResearch/data-collection mode (DEMO only). When true and TRADE_MODE=DEMO, the bot stops blocking at the TA-agreement, EV, and risk gates — it places the bot-direction trade anyway, tags the row shadow=true with would_skip_reason, and records the outcome. This builds an uncensored dataset (normally we only see outcomes for trades that passed every gate). Hard-guarded: ignored in LIVE; low_payout is still enforced; shadow outcomes are written to decisions.jsonl but do not feed the production win-rate tracker or risk stats. See Research & Calibration.

Risk settings

VariableDefaultDescription
MAX_TRADES_PER_HOUR10Rolling 1-hour cap.
MAX_DAILY_LOSS_USD20.0Daily loss stops trading for the day.
COOLDOWN_AFTER_LOSS_SECONDS120Pause after each loss.
MIN_BALANCE_MULTIPLIER5.0Balance must be ≥ this × stake to trade.

Telegram settings

VariableDefaultDescription
TELEGRAM_API_IDFrom my.telegram.org
TELEGRAM_API_HASHFrom my.telegram.org
TELEGRAM_SESSIONpo_sessionPath to .session file or session name.
SIGNAL_BOT_USERNAMEpo_broker_botThe Telegram bot to read from.

Technical Analysis Signals

All signals consume an o/h/l/c/v time-indexed DataFrame from data/candles.py.

SignalWeightCALL triggerPUT trigger
RSI0.20RSI < 30 (oversold)RSI > 70 (overbought)
MACD0.20MACD crosses above signalMACD crosses below signal
Bollinger0.20Price at lower band + revertingPrice at upper band + reverting
EMA Cross0.15Fast EMA crosses above slow EMAFast EMA crosses below slow EMA
Candle Patterns0.25Bullish engulfing / hammerBearish engulfing / shooting star

Confluence rule (updated 2026-06-09): the trade decision is now gated on MACD + EMA only (decision_signals) — both must agree on the same direction. RSI, Bollinger, and CandlePattern are still evaluated and recorded in the decision log for research, but they no longer affect whether a trade is taken. Data over ~410 trades showed only MACD/EMA carry a positive edge and that 3-signal agreement won less than 2-signal. See docs/signal-strategy-research.md for the full analysis and the candidate signals (ADX, ATR, Supertrend, …) queued for testing.


Decision Logic

Each cycle produces a DecisionRow appended to data/decisions.jsonl:

TRADE → bot direction matches our TA direction + both gates pass
SKIP → one of: no_direction · ta_disagree · ta_low_score · risk_blocked

Confidence (logged, not gated) — stored as combined_probability, shown in the dashboard as confidence (it is a heuristic score, not a calibrated probability):

confidence = (bot_win_rate + our_confluence_score) / 2

A learned, calibrated P(win) is also recorded per trade as calibrated_probability when a model exists — see Research & Calibration. It is display/diagnostic only and never influences trade decisions.


Decision Log (data/decisions.jsonl)

One JSON line per evaluated signal. Key fields:

{
"cycle_id": "20260605T042310-0001",
"pair_raw": "GBP/USD",
"pair_api": "GBPUSD",
"bot_win_rate": 0.90,
"bot_direction": "CALL",
"our_direction": "CALL",
"our_confluence_score": 0.78,
"agreement": true,
"combined_probability": 0.84,
"calibrated_probability": 0.61,
"decision": "TRADE",
"skip_reason": null,
"shadow": false,
"would_skip_reason": null,
"stake": 1.5,
"trade_id": "trade_abc123",
"outcome": "WIN",
"pnl": 1.28,
"pnl_currency": "USD",
"balance_before": 1000.0,
"balance_after": 1001.28,
"ts": "2026-06-05T04:23:10.000000+00:00"
}

Use this log to calibrate signal quality, tune gates, and identify which pairs perform well over time.


Research & Calibration

Tooling for understanding why trades win or lose and for improving the signal stack.

Signal-attribution report

python scripts/analyze_signals.py # full report
python scripts/analyze_signals.py --min-n 30 # raise the min sample for flags

Read-only analysis over data/decisions.jsonl. For resolved trades it reports, per signal, the win rate when the signal agrees / is neutral / opposes the traded direction (with Wilson CIs and lift), confluence-score and agreement-count buckets, per-pair win rates, the break-even edge vs observed payouts, and a censoring summary. It flags signals that hurt when they agree, look inverted (opposing beats base), or never fire. When shadow data exists it also breaks outcomes down by would_skip_reason.

Probability calibrator

A learned win-probability model (L2 logistic regression) that records a real calibrated_probability alongside the heuristic confidence.

python -m strategy.train_calibrator # train from decisions.jsonl → data/models/
  • strategy/probability_calibrator.py — model + graceful fallback to the heuristic mean when no model/sklearn is present (never raises).
  • Decision-inert: the calibrated value is display/diagnostic only; decide(), the EV gate, and risk sizing are unaffected.
  • Model artifacts in data/models/ are gitignored (regenerable). Retrain as data grows.

The model is only as good as the data. On the first ~300 trades it sits near AUC 0.53 (no better than the average) — keep it dormant until the dataset is larger.

Building an uncensored dataset (shadow mode)

The decision log is censored: outcomes only exist for trades that passed every gate, so the disagreement cases needed to calibrate signals are never observed. Enable SHADOW_RECORD_MODE=true (DEMO only) to trade and record the would-be-skipped cases:

# .env
TRADE_MODE=DEMO
SHADOW_RECORD_MODE=true

The bot then places bot-direction trades it would normally skip (no_direction, ta_disagree, negative_ev, risk_blocked), tags them shadow=true with the would_skip_reason, and records outcomes — without feeding the production win-rate tracker or risk stats. Let it run, then re-run analyze_signals.py to see which gates actually earn their keep.


Safety Invariants

These must never be weakened without explicit intent:

  1. DEMO by default.TRADE_MODE=DEMO is the hard default. Even an empty TRADE_MODE= env var resets to DEMO.
  2. Demo guard. Before any real buy, broker/po_api.py decodes isDemo from the SSID using the API-native is_demo() method (with SSID-string fallback). If SSID is live but TRADE_MODE=DEMO, the trade is aborted.
  3. DRY_RUN. When DRY_RUN=true, buy/sell logs the would-be trade and returns without calling the API.
  4. Navigator never clicks amount buttons. The only safe action is clicking pair names, "Start Autotrade", "Main Menu", and "Trade Anyway" nag buttons. Clicking an amount/stake button places a martingale bot trade with the bot's own tokens — our trades go through the PocketOption API exclusively.
  5. Single session writer. Only one process may use the Telethon session at a time. Ensure telebot/scripts/pocket_robot_trader.py is stopped before running main_v2.py.

Data Files

PathDescription
data/decisions.jsonlStructured log: one row per evaluated signal
data/win_rates.jsonPer-pair win rate tracker (persisted)
data/trades.jsonlLegacy trade log (kept for compatibility)
logs/bot.logHuman-readable rotating log (1 day, 7-day retention)

Testing

All 100 tests run fully offline — no network, no SSID, no Telegram credentials.

pytest # all tests
pytest tests/ -v # verbose
pytest tests/test_signals.py # one module

For a live smoke test against the real bot (no trade placed):

python3 tools/v2_smoke.py
python3 tools/v2_smoke.py --pair GBPUSD_otc # skip navigation, test TA only

Development Notes

  • All live-path code is async/await. Errors are caught per-iteration and logged; the bot never crashes on a single bad cycle.
  • Frozen dataclasses are used for immutable results (TelegramSignal, SignalResult, ConfluenceResult, DirectionScreen, PredictionScreen). Mutable state lives in RiskManager, WinRateTracker, and the API client.
  • Direction is always the string "CALL", "PUT", or None — never booleans or enums.
  • Imports are absolute from project root.
  • pandas-ta is not used (incompatible with newer Python). All indicators are pure pandas/numpy.

Progression Path

PhaseConfigPurpose
Smoke testDRY_RUN=trueVerify navigation + TA works end-to-end
Capture modePAIR_SELECT_MIN_WIN_RATE=0.0, DRY_RUN=trueObserve all signals, log everything
Gated testingPAIR_SELECT_MIN_WIN_RATE=0.82, DRY_RUN=trueVerify gate logic against real signals
Demo liveTRADE_MODE=DEMO, DRY_RUN=falseReal trades on demo account
LiveTRADE_MODE=LIVE, DRY_RUN=falseOnly after sustained demo profitability

Built with: Python 3.12+ · Telethon · binaryoptionstoolsv2 · Pydantic · Loguru · pandas/numpy

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

270 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

ArgusSentinel

⚠️ TELEGRAM INTEGRATION REMOVED (2026-06-12). The Telethon/po_broker_bot integration described below has been deleted from the codebase (telegram_feed/, v1 main.py, navigator-driven loop, all TELEGRAM_* settings). Setup now only requires PO_SSID. The payout-first signals loop is the only trade driver. Telegram-related sections in this document are historical.

Telegram-driven binary options bot. Reads trade signals from po_broker_bot via Telegram, confirms with internal technical analysis, then places independent CALL/PUT trades via the PocketOption WebSocket API.

⚠️Risk Disclaimer: Binary options carry extreme risk of capital loss. Both the unofficial PocketOption API and the Telethon user session violate the respective platforms' Terms of Service. This project is for educational and research purposes only. Always use DEMO mode. Never risk money you cannot afford to lose.


How It Works

po_broker_bot (Telegram)
│
│ Telethon user session reads DMs
▼
Navigator drives po_broker_bot menus
│ → top pair + win rate (prediction screen)
│ → CALL/PUT direction (direction screen)
▼
Pair quality gate
│ win rate ≥ PAIR_SELECT_MIN_WIN_RATE (default 0.82)
▼
Internal TA Confluence Engine (5 signals)
│ RSI · MACD · Bollinger · EMA Cross · Candle Patterns
│ ≥ 3 signals must agree on same direction
▼
Decision logic
│ bot direction must match our TA direction
│ combined probability = (bot win% + our confluence score) / 2
▼
Risk Manager gates
│ min balance · max trades/hr · daily loss limit · cooldown
▼
PocketOption API (binaryoptionstoolsv2)
│ buy/sell(pair, $1.50, expiry)
│ Never clicks the martingale bot's amount button
│ Up to 6 trades open concurrently
▼
Background resolver (non-blocking)
│ poll_trade_outcome() → polls closed_deals() after expiry
│ WIN / LOSS / DRAW (check_win fallback if polling exhausted)
▼
decisions.jsonl + WinRateTracker + RiskManager

Architecture

PocketOptionBot/
├── main_v2.py # v2 entrypoint (--cycles N)
├── main.py # legacy entrypoint (unwired, kept)
│
├── config/
│ └── settings.py # All config via .env (Pydantic)
│
├── telegram_feed/
│ ├── client.py # TelegramSignalFeed (legacy listener)
│ ├── navigator.py # Button-drives po_broker_bot menus
│ ├── prediction_parser.py # Parses pair/win-rate screen
│ ├── direction_parser.py # Parses CALL/PUT direction screen
│ ├── pair_norm.py # Normalises pair labels → API symbols
│ └── parser.py # Legacy signal parser (kept)
│
├── broker/
│ └── po_api.py # PocketOptionAPIClient (buy/sell/check_win)
│ └── [connector/scraper/executor.py] # Legacy CDP modules (unwired)
│
├── signals/
│ ├── base.py # BaseSignal abstract class
│ ├── rsi.py # RSI (oversold/overbought)
│ ├── macd.py # MACD crossover
│ ├── bollinger.py # Bollinger Bands mean reversion
│ ├── ema_cross.py # EMA golden/death cross
│ ├── candle_pattern.py # Candlestick patterns
│ └── confluence.py # ConfluenceEngine: weighted scoring + ≥3 gate
│
├── strategy/
│ ├── manager_v2.py # v2 orchestrator (navigate→TA→decide→trade)
│ ├── decision.py # Pure agree/disagree + combined probability
│ ├── expiry.py # Nearest-allowed expiry selection
│ ├── trade_logger.py # decisions.jsonl writer + outcome backfill
│ ├── signal_gate.py # Legacy 3-gate filter (kept)
│ ├── win_rate.py # Per-pair win rate tracker (data/win_rates.json)
│ ├── risk.py # RiskManager (balance/hr/daily/cooldown)
│ └── manager.py # Legacy event-driven manager (kept)
│
├── data/
│ ├── candles.py # API candle dicts → o/h/l/c/v DataFrame
│ └── feed.py # Legacy price feed (kept)
│
├── tools/
│ ├── v2_smoke.py # One dry-run cycle smoke test
│ ├── gen_telegram_session.py # One-time Telethon session auth
│ └── test_telegram_feed.py # Live feed capture / debugging
│
├── utils/
│ └── logger.py # Loguru: logs/ + data/trades.jsonl
│
├── tests/ # 100 offline unit tests
│
└── docs/
└── superpowers/plans/
└── 2026-06-04-telebot-evolution.md # Full implementation plan

Quick Start

1. Prerequisites

  • Python 3.12+
  • An existing Telethon session authenticated to your Telegram account
    (run python3 tools/gen_telegram_session.py once if not set up)
  • A PocketOption SSID (copy the 42["auth",{...}] string from your browser's DevTools Network tab while logged in to pocketoption.com)

2. Install

cd~/code/openclaw/projects/PocketOptionBot
pip3 install -r requirements.txt

3. Configure

cp .env.example .env
# Edit .env with your credentials

Minimum required settings:

# TelegramTELEGRAM_API_ID=123456TELEGRAM_API_HASH=your_api_hashTELEGRAM_SESSION=~/.telebot/telegram.session# PocketOptionPO_SSID=42["auth",{"session":"...","isDemo":1,...}]# Safety (leave these until you are confident)TRADE_MODE=DEMODRY_RUN=trueSTAKE_AMOUNT=1.50

4. Smoke test (no trade placed)

python3 tools/v2_smoke.py
# Runs one full cycle: navigate → TA → decide → log (DRY_RUN forced true)# Check data/decisions.jsonl for the result

5. Run the bot

python3 main_v2.py # run until Ctrl-C
python3 main_v2.py --cycles 5 # run exactly 5 cycles then exit

Web Dashboard

A live monitoring + settings UI (FastAPI + WebSocket backend, zero-build vanilla JS frontend). Landscape layout: Performance (equity/P&L curve + win/loss) · Active Trades (live countdowns) · Trade History, plus an editable Settings tab. It reads the bot's output files and updates live over a WebSocket — it does not need the trading stack, an SSID, or Telegram.

Run it locally (one command)

git clone https://github.com/force-push/PocketOptionBot.git
cd PocketOptionBot
./scripts/run_dashboard.sh # venv + install + seed demo data + serve

Then open http://127.0.0.1:8787. Use --no-seed to keep existing data.

Or step by step (works on Windows too)

python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements-dashboard.txt
python tools/dashboard_demo.py # optional: seed synthetic demo data
python -m dashboard.server # http://127.0.0.1:8787

requirements-dashboard.txt is a minimal 5-package subset (FastAPI, uvicorn, watchfiles, pydantic-settings, python-dotenv) — no playwright / telethon / Rust wheel needed just to view the dashboard.

Demo data vs. live trading data

  • Demo (default above):tools/dashboard_demo.py writes deterministic synthetic data/decisions.jsonl + data/live_state.json so the UI is fully populated with no bot running. Re-run it any time to refresh.

  • Live: run the bot with the bridge enabled so it streams real state to the dashboard as it trades:

    DASHBOARD_ENABLED=true python3 main_v2.py

    Start the dashboard server in a second terminal; it picks up changes live.

Settings tab (writes to .env)

The Settings tab can edit configuration and save it back to .env. Safety rails are enforced server-side: secrets are masked and only written when changed, and flipping TRADE_MODE to LIVE is fail-closed — it requires explicit confirmation and an SSID that decodes as a live session. Most changes need a bot restart to take effect (the UI flags which).

Dashboard settings (.env)

VariableDefaultPurpose
DASHBOARD_ENABLEDfalsebot streams live state when true
DASHBOARD_HOST127.0.0.1bind address (keep localhost)
DASHBOARD_PORT8787server port
DASHBOARD_TOKEN(unset)when set, required to save settings (sent as a Bearer token)

🔒 Bind to 127.0.0.1 only. The Settings tab can change trading config, so do not expose the dashboard publicly without setting DASHBOARD_TOKEN.


Configuration Reference

All settings live in .env. See .env.example for the full list.

Critical safety settings

VariableDefaultDescription
TRADE_MODEDEMODEMO or LIVE. Hard-reset to DEMO if unset.
DRY_RUNtrueLog trades but never call buy/sell on the API.
STAKE_AMOUNT1.50Fixed stake per trade (USD).

v2 gate settings

VariableDefaultDescription
PAIR_SELECT_MIN_WIN_RATE0.0Minimum bot-stated win rate to consider a pair. Set to 0.82 for real runs; 0.0 disables the gate during testing.
DEFAULT_EXPIRY_SECONDS30Trade expiry. Snapped to nearest allowed value.
CLICK_TRADE_ANYWAYtrueAuto-click the "low-balance" nag page when it appears.
DECISIONS_LOG_PATHdata/decisions.jsonlPath for the structured decision log.
MIN_CONFLUENCE_SCORE0.35Minimum TA confluence score to agree with a signal (reduced from 0.75 to allow more trades during testing; actual threshold is adaptive based on signal agreement).
MIN_SIGNAL_AGREEMENT3Minimum number of signals that must agree on same direction (increased from 2 to 3 for stricter confluence).
BLOCKED_PAIRS["EURUSD_otc", "ETHUSD_otc"]List of pair API symbols (e.g., "EURUSD_otc") to skip during pair selection. When the bot presents a list of candidate pairs, the navigator cycles through and selects the first non-blocked pair, avoiding wasted analysis time. If all pairs are blocked, the cycle is skipped.
SHADOW_RECORD_MODEfalseResearch/data-collection mode (DEMO only). When true and TRADE_MODE=DEMO, the bot stops blocking at the TA-agreement, EV, and risk gates — it places the bot-direction trade anyway, tags the row shadow=true with would_skip_reason, and records the outcome. This builds an uncensored dataset (normally we only see outcomes for trades that passed every gate). Hard-guarded: ignored in LIVE; low_payout is still enforced; shadow outcomes are written to decisions.jsonl but do not feed the production win-rate tracker or risk stats. See Research & Calibration.

Risk settings

VariableDefaultDescription
MAX_TRADES_PER_HOUR10Rolling 1-hour cap.
MAX_DAILY_LOSS_USD20.0Daily loss stops trading for the day.
COOLDOWN_AFTER_LOSS_SECONDS120Pause after each loss.
MIN_BALANCE_MULTIPLIER5.0Balance must be ≥ this × stake to trade.

Telegram settings

VariableDefaultDescription
TELEGRAM_API_IDFrom my.telegram.org
TELEGRAM_API_HASHFrom my.telegram.org
TELEGRAM_SESSIONpo_sessionPath to .session file or session name.
SIGNAL_BOT_USERNAMEpo_broker_botThe Telegram bot to read from.

Technical Analysis Signals

All signals consume an o/h/l/c/v time-indexed DataFrame from data/candles.py.

SignalWeightCALL triggerPUT trigger
RSI0.20RSI < 30 (oversold)RSI > 70 (overbought)
MACD0.20MACD crosses above signalMACD crosses below signal
Bollinger0.20Price at lower band + revertingPrice at upper band + reverting
EMA Cross0.15Fast EMA crosses above slow EMAFast EMA crosses below slow EMA
Candle Patterns0.25Bullish engulfing / hammerBearish engulfing / shooting star

Confluence rule (updated 2026-06-09): the trade decision is now gated on MACD + EMA only (decision_signals) — both must agree on the same direction. RSI, Bollinger, and CandlePattern are still evaluated and recorded in the decision log for research, but they no longer affect whether a trade is taken. Data over ~410 trades showed only MACD/EMA carry a positive edge and that 3-signal agreement won less than 2-signal. See docs/signal-strategy-research.md for the full analysis and the candidate signals (ADX, ATR, Supertrend, …) queued for testing.


Decision Logic

Each cycle produces a DecisionRow appended to data/decisions.jsonl:

TRADE → bot direction matches our TA direction + both gates pass
SKIP → one of: no_direction · ta_disagree · ta_low_score · risk_blocked

Confidence (logged, not gated) — stored as combined_probability, shown in the dashboard as confidence (it is a heuristic score, not a calibrated probability):

confidence = (bot_win_rate + our_confluence_score) / 2

A learned, calibrated P(win) is also recorded per trade as calibrated_probability when a model exists — see Research & Calibration. It is display/diagnostic only and never influences trade decisions.


Decision Log (data/decisions.jsonl)

One JSON line per evaluated signal. Key fields:

{
"cycle_id": "20260605T042310-0001",
"pair_raw": "GBP/USD",
"pair_api": "GBPUSD",
"bot_win_rate": 0.90,
"bot_direction": "CALL",
"our_direction": "CALL",
"our_confluence_score": 0.78,
"agreement": true,
"combined_probability": 0.84,
"calibrated_probability": 0.61,
"decision": "TRADE",
"skip_reason": null,
"shadow": false,
"would_skip_reason": null,
"stake": 1.5,
"trade_id": "trade_abc123",
"outcome": "WIN",
"pnl": 1.28,
"pnl_currency": "USD",
"balance_before": 1000.0,
"balance_after": 1001.28,
"ts": "2026-06-05T04:23:10.000000+00:00"
}

Use this log to calibrate signal quality, tune gates, and identify which pairs perform well over time.


Research & Calibration

Tooling for understanding why trades win or lose and for improving the signal stack.

Signal-attribution report

python scripts/analyze_signals.py # full report
python scripts/analyze_signals.py --min-n 30 # raise the min sample for flags

Read-only analysis over data/decisions.jsonl. For resolved trades it reports, per signal, the win rate when the signal agrees / is neutral / opposes the traded direction (with Wilson CIs and lift), confluence-score and agreement-count buckets, per-pair win rates, the break-even edge vs observed payouts, and a censoring summary. It flags signals that hurt when they agree, look inverted (opposing beats base), or never fire. When shadow data exists it also breaks outcomes down by would_skip_reason.

Probability calibrator

A learned win-probability model (L2 logistic regression) that records a real calibrated_probability alongside the heuristic confidence.

python -m strategy.train_calibrator # train from decisions.jsonl → data/models/
  • strategy/probability_calibrator.py — model + graceful fallback to the heuristic mean when no model/sklearn is present (never raises).
  • Decision-inert: the calibrated value is display/diagnostic only; decide(), the EV gate, and risk sizing are unaffected.
  • Model artifacts in data/models/ are gitignored (regenerable). Retrain as data grows.

The model is only as good as the data. On the first ~300 trades it sits near AUC 0.53 (no better than the average) — keep it dormant until the dataset is larger.

Building an uncensored dataset (shadow mode)

The decision log is censored: outcomes only exist for trades that passed every gate, so the disagreement cases needed to calibrate signals are never observed. Enable SHADOW_RECORD_MODE=true (DEMO only) to trade and record the would-be-skipped cases:

# .env
TRADE_MODE=DEMO
SHADOW_RECORD_MODE=true

The bot then places bot-direction trades it would normally skip (no_direction, ta_disagree, negative_ev, risk_blocked), tags them shadow=true with the would_skip_reason, and records outcomes — without feeding the production win-rate tracker or risk stats. Let it run, then re-run analyze_signals.py to see which gates actually earn their keep.


Safety Invariants

These must never be weakened without explicit intent:

  1. DEMO by default.TRADE_MODE=DEMO is the hard default. Even an empty TRADE_MODE= env var resets to DEMO.
  2. Demo guard. Before any real buy, broker/po_api.py decodes isDemo from the SSID using the API-native is_demo() method (with SSID-string fallback). If SSID is live but TRADE_MODE=DEMO, the trade is aborted.
  3. DRY_RUN. When DRY_RUN=true, buy/sell logs the would-be trade and returns without calling the API.
  4. Navigator never clicks amount buttons. The only safe action is clicking pair names, "Start Autotrade", "Main Menu", and "Trade Anyway" nag buttons. Clicking an amount/stake button places a martingale bot trade with the bot's own tokens — our trades go through the PocketOption API exclusively.
  5. Single session writer. Only one process may use the Telethon session at a time. Ensure telebot/scripts/pocket_robot_trader.py is stopped before running main_v2.py.

Data Files

PathDescription
data/decisions.jsonlStructured log: one row per evaluated signal
data/win_rates.jsonPer-pair win rate tracker (persisted)
data/trades.jsonlLegacy trade log (kept for compatibility)
logs/bot.logHuman-readable rotating log (1 day, 7-day retention)

Testing

All 100 tests run fully offline — no network, no SSID, no Telegram credentials.

pytest # all tests
pytest tests/ -v # verbose
pytest tests/test_signals.py # one module

For a live smoke test against the real bot (no trade placed):

python3 tools/v2_smoke.py
python3 tools/v2_smoke.py --pair GBPUSD_otc # skip navigation, test TA only

Development Notes

  • All live-path code is async/await. Errors are caught per-iteration and logged; the bot never crashes on a single bad cycle.
  • Frozen dataclasses are used for immutable results (TelegramSignal, SignalResult, ConfluenceResult, DirectionScreen, PredictionScreen). Mutable state lives in RiskManager, WinRateTracker, and the API client.
  • Direction is always the string "CALL", "PUT", or None — never booleans or enums.
  • Imports are absolute from project root.
  • pandas-ta is not used (incompatible with newer Python). All indicators are pure pandas/numpy.

Progression Path

PhaseConfigPurpose
Smoke testDRY_RUN=trueVerify navigation + TA works end-to-end
Capture modePAIR_SELECT_MIN_WIN_RATE=0.0, DRY_RUN=trueObserve all signals, log everything
Gated testingPAIR_SELECT_MIN_WIN_RATE=0.82, DRY_RUN=trueVerify gate logic against real signals
Demo liveTRADE_MODE=DEMO, DRY_RUN=falseReal trades on demo account
LiveTRADE_MODE=LIVE, DRY_RUN=falseOnly after sustained demo profitability

Built with: Python 3.12+ · Telethon · binaryoptionstoolsv2 · Pydantic · Loguru · pandas/numpy

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

270 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

ArgusSentinel

⚠️ TELEGRAM INTEGRATION REMOVED (2026-06-12). The Telethon/po_broker_bot integration described below has been deleted from the codebase (telegram_feed/, v1 main.py, navigator-driven loop, all TELEGRAM_* settings). Setup now only requires PO_SSID. The payout-first signals loop is the only trade driver. Telegram-related sections in this document are historical.

Telegram-driven binary options bot. Reads trade signals from po_broker_bot via Telegram, confirms with internal technical analysis, then places independent CALL/PUT trades via the PocketOption WebSocket API.

⚠️Risk Disclaimer: Binary options carry extreme risk of capital loss. Both the unofficial PocketOption API and the Telethon user session violate the respective platforms' Terms of Service. This project is for educational and research purposes only. Always use DEMO mode. Never risk money you cannot afford to lose.


How It Works

po_broker_bot (Telegram)
│
│ Telethon user session reads DMs
▼
Navigator drives po_broker_bot menus
│ → top pair + win rate (prediction screen)
│ → CALL/PUT direction (direction screen)
▼
Pair quality gate
│ win rate ≥ PAIR_SELECT_MIN_WIN_RATE (default 0.82)
▼
Internal TA Confluence Engine (5 signals)
│ RSI · MACD · Bollinger · EMA Cross · Candle Patterns
│ ≥ 3 signals must agree on same direction
▼
Decision logic
│ bot direction must match our TA direction
│ combined probability = (bot win% + our confluence score) / 2
▼
Risk Manager gates
│ min balance · max trades/hr · daily loss limit · cooldown
▼
PocketOption API (binaryoptionstoolsv2)
│ buy/sell(pair, $1.50, expiry)
│ Never clicks the martingale bot's amount button
│ Up to 6 trades open concurrently
▼
Background resolver (non-blocking)
│ poll_trade_outcome() → polls closed_deals() after expiry
│ WIN / LOSS / DRAW (check_win fallback if polling exhausted)
▼
decisions.jsonl + WinRateTracker + RiskManager

Architecture

PocketOptionBot/
├── main_v2.py # v2 entrypoint (--cycles N)
├── main.py # legacy entrypoint (unwired, kept)
│
├── config/
│ └── settings.py # All config via .env (Pydantic)
│
├── telegram_feed/
│ ├── client.py # TelegramSignalFeed (legacy listener)
│ ├── navigator.py # Button-drives po_broker_bot menus
│ ├── prediction_parser.py # Parses pair/win-rate screen
│ ├── direction_parser.py # Parses CALL/PUT direction screen
│ ├── pair_norm.py # Normalises pair labels → API symbols
│ └── parser.py # Legacy signal parser (kept)
│
├── broker/
│ └── po_api.py # PocketOptionAPIClient (buy/sell/check_win)
│ └── [connector/scraper/executor.py] # Legacy CDP modules (unwired)
│
├── signals/
│ ├── base.py # BaseSignal abstract class
│ ├── rsi.py # RSI (oversold/overbought)
│ ├── macd.py # MACD crossover
│ ├── bollinger.py # Bollinger Bands mean reversion
│ ├── ema_cross.py # EMA golden/death cross
│ ├── candle_pattern.py # Candlestick patterns
│ └── confluence.py # ConfluenceEngine: weighted scoring + ≥3 gate
│
├── strategy/
│ ├── manager_v2.py # v2 orchestrator (navigate→TA→decide→trade)
│ ├── decision.py # Pure agree/disagree + combined probability
│ ├── expiry.py # Nearest-allowed expiry selection
│ ├── trade_logger.py # decisions.jsonl writer + outcome backfill
│ ├── signal_gate.py # Legacy 3-gate filter (kept)
│ ├── win_rate.py # Per-pair win rate tracker (data/win_rates.json)
│ ├── risk.py # RiskManager (balance/hr/daily/cooldown)
│ └── manager.py # Legacy event-driven manager (kept)
│
├── data/
│ ├── candles.py # API candle dicts → o/h/l/c/v DataFrame
│ └── feed.py # Legacy price feed (kept)
│
├── tools/
│ ├── v2_smoke.py # One dry-run cycle smoke test
│ ├── gen_telegram_session.py # One-time Telethon session auth
│ └── test_telegram_feed.py # Live feed capture / debugging
│
├── utils/
│ └── logger.py # Loguru: logs/ + data/trades.jsonl
│
├── tests/ # 100 offline unit tests
│
└── docs/
└── superpowers/plans/
└── 2026-06-04-telebot-evolution.md # Full implementation plan

Quick Start

1. Prerequisites

  • Python 3.12+
  • An existing Telethon session authenticated to your Telegram account
    (run python3 tools/gen_telegram_session.py once if not set up)
  • A PocketOption SSID (copy the 42["auth",{...}] string from your browser's DevTools Network tab while logged in to pocketoption.com)

2. Install

cd~/code/openclaw/projects/PocketOptionBot
pip3 install -r requirements.txt

3. Configure

cp .env.example .env
# Edit .env with your credentials

Minimum required settings:

# TelegramTELEGRAM_API_ID=123456TELEGRAM_API_HASH=your_api_hashTELEGRAM_SESSION=~/.telebot/telegram.session# PocketOptionPO_SSID=42["auth",{"session":"...","isDemo":1,...}]# Safety (leave these until you are confident)TRADE_MODE=DEMODRY_RUN=trueSTAKE_AMOUNT=1.50

4. Smoke test (no trade placed)

python3 tools/v2_smoke.py
# Runs one full cycle: navigate → TA → decide → log (DRY_RUN forced true)# Check data/decisions.jsonl for the result

5. Run the bot

python3 main_v2.py # run until Ctrl-C
python3 main_v2.py --cycles 5 # run exactly 5 cycles then exit

Web Dashboard

A live monitoring + settings UI (FastAPI + WebSocket backend, zero-build vanilla JS frontend). Landscape layout: Performance (equity/P&L curve + win/loss) · Active Trades (live countdowns) · Trade History, plus an editable Settings tab. It reads the bot's output files and updates live over a WebSocket — it does not need the trading stack, an SSID, or Telegram.

Run it locally (one command)

git clone https://github.com/force-push/PocketOptionBot.git
cd PocketOptionBot
./scripts/run_dashboard.sh # venv + install + seed demo data + serve

Then open http://127.0.0.1:8787. Use --no-seed to keep existing data.

Or step by step (works on Windows too)

python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements-dashboard.txt
python tools/dashboard_demo.py # optional: seed synthetic demo data
python -m dashboard.server # http://127.0.0.1:8787

requirements-dashboard.txt is a minimal 5-package subset (FastAPI, uvicorn, watchfiles, pydantic-settings, python-dotenv) — no playwright / telethon / Rust wheel needed just to view the dashboard.

Demo data vs. live trading data

  • Demo (default above):tools/dashboard_demo.py writes deterministic synthetic data/decisions.jsonl + data/live_state.json so the UI is fully populated with no bot running. Re-run it any time to refresh.

  • Live: run the bot with the bridge enabled so it streams real state to the dashboard as it trades:

    DASHBOARD_ENABLED=true python3 main_v2.py

    Start the dashboard server in a second terminal; it picks up changes live.

Settings tab (writes to .env)

The Settings tab can edit configuration and save it back to .env. Safety rails are enforced server-side: secrets are masked and only written when changed, and flipping TRADE_MODE to LIVE is fail-closed — it requires explicit confirmation and an SSID that decodes as a live session. Most changes need a bot restart to take effect (the UI flags which).

Dashboard settings (.env)

VariableDefaultPurpose
DASHBOARD_ENABLEDfalsebot streams live state when true
DASHBOARD_HOST127.0.0.1bind address (keep localhost)
DASHBOARD_PORT8787server port
DASHBOARD_TOKEN(unset)when set, required to save settings (sent as a Bearer token)

🔒 Bind to 127.0.0.1 only. The Settings tab can change trading config, so do not expose the dashboard publicly without setting DASHBOARD_TOKEN.


Configuration Reference

All settings live in .env. See .env.example for the full list.

Critical safety settings

VariableDefaultDescription
TRADE_MODEDEMODEMO or LIVE. Hard-reset to DEMO if unset.
DRY_RUNtrueLog trades but never call buy/sell on the API.
STAKE_AMOUNT1.50Fixed stake per trade (USD).

v2 gate settings

VariableDefaultDescription
PAIR_SELECT_MIN_WIN_RATE0.0Minimum bot-stated win rate to consider a pair. Set to 0.82 for real runs; 0.0 disables the gate during testing.
DEFAULT_EXPIRY_SECONDS30Trade expiry. Snapped to nearest allowed value.
CLICK_TRADE_ANYWAYtrueAuto-click the "low-balance" nag page when it appears.
DECISIONS_LOG_PATHdata/decisions.jsonlPath for the structured decision log.
MIN_CONFLUENCE_SCORE0.35Minimum TA confluence score to agree with a signal (reduced from 0.75 to allow more trades during testing; actual threshold is adaptive based on signal agreement).
MIN_SIGNAL_AGREEMENT3Minimum number of signals that must agree on same direction (increased from 2 to 3 for stricter confluence).
BLOCKED_PAIRS["EURUSD_otc", "ETHUSD_otc"]List of pair API symbols (e.g., "EURUSD_otc") to skip during pair selection. When the bot presents a list of candidate pairs, the navigator cycles through and selects the first non-blocked pair, avoiding wasted analysis time. If all pairs are blocked, the cycle is skipped.
SHADOW_RECORD_MODEfalseResearch/data-collection mode (DEMO only). When true and TRADE_MODE=DEMO, the bot stops blocking at the TA-agreement, EV, and risk gates — it places the bot-direction trade anyway, tags the row shadow=true with would_skip_reason, and records the outcome. This builds an uncensored dataset (normally we only see outcomes for trades that passed every gate). Hard-guarded: ignored in LIVE; low_payout is still enforced; shadow outcomes are written to decisions.jsonl but do not feed the production win-rate tracker or risk stats. See Research & Calibration.

Risk settings

VariableDefaultDescription
MAX_TRADES_PER_HOUR10Rolling 1-hour cap.
MAX_DAILY_LOSS_USD20.0Daily loss stops trading for the day.
COOLDOWN_AFTER_LOSS_SECONDS120Pause after each loss.
MIN_BALANCE_MULTIPLIER5.0Balance must be ≥ this × stake to trade.

Telegram settings

VariableDefaultDescription
TELEGRAM_API_IDFrom my.telegram.org
TELEGRAM_API_HASHFrom my.telegram.org
TELEGRAM_SESSIONpo_sessionPath to .session file or session name.
SIGNAL_BOT_USERNAMEpo_broker_botThe Telegram bot to read from.

Technical Analysis Signals

All signals consume an o/h/l/c/v time-indexed DataFrame from data/candles.py.

SignalWeightCALL triggerPUT trigger
RSI0.20RSI < 30 (oversold)RSI > 70 (overbought)
MACD0.20MACD crosses above signalMACD crosses below signal
Bollinger0.20Price at lower band + revertingPrice at upper band + reverting
EMA Cross0.15Fast EMA crosses above slow EMAFast EMA crosses below slow EMA
Candle Patterns0.25Bullish engulfing / hammerBearish engulfing / shooting star

Confluence rule (updated 2026-06-09): the trade decision is now gated on MACD + EMA only (decision_signals) — both must agree on the same direction. RSI, Bollinger, and CandlePattern are still evaluated and recorded in the decision log for research, but they no longer affect whether a trade is taken. Data over ~410 trades showed only MACD/EMA carry a positive edge and that 3-signal agreement won less than 2-signal. See docs/signal-strategy-research.md for the full analysis and the candidate signals (ADX, ATR, Supertrend, …) queued for testing.


Decision Logic

Each cycle produces a DecisionRow appended to data/decisions.jsonl:

TRADE → bot direction matches our TA direction + both gates pass
SKIP → one of: no_direction · ta_disagree · ta_low_score · risk_blocked

Confidence (logged, not gated) — stored as combined_probability, shown in the dashboard as confidence (it is a heuristic score, not a calibrated probability):

confidence = (bot_win_rate + our_confluence_score) / 2

A learned, calibrated P(win) is also recorded per trade as calibrated_probability when a model exists — see Research & Calibration. It is display/diagnostic only and never influences trade decisions.


Decision Log (data/decisions.jsonl)

One JSON line per evaluated signal. Key fields:

{
"cycle_id": "20260605T042310-0001",
"pair_raw": "GBP/USD",
"pair_api": "GBPUSD",
"bot_win_rate": 0.90,
"bot_direction": "CALL",
"our_direction": "CALL",
"our_confluence_score": 0.78,
"agreement": true,
"combined_probability": 0.84,
"calibrated_probability": 0.61,
"decision": "TRADE",
"skip_reason": null,
"shadow": false,
"would_skip_reason": null,
"stake": 1.5,
"trade_id": "trade_abc123",
"outcome": "WIN",
"pnl": 1.28,
"pnl_currency": "USD",
"balance_before": 1000.0,
"balance_after": 1001.28,
"ts": "2026-06-05T04:23:10.000000+00:00"
}

Use this log to calibrate signal quality, tune gates, and identify which pairs perform well over time.


Research & Calibration

Tooling for understanding why trades win or lose and for improving the signal stack.

Signal-attribution report

python scripts/analyze_signals.py # full report
python scripts/analyze_signals.py --min-n 30 # raise the min sample for flags

Read-only analysis over data/decisions.jsonl. For resolved trades it reports, per signal, the win rate when the signal agrees / is neutral / opposes the traded direction (with Wilson CIs and lift), confluence-score and agreement-count buckets, per-pair win rates, the break-even edge vs observed payouts, and a censoring summary. It flags signals that hurt when they agree, look inverted (opposing beats base), or never fire. When shadow data exists it also breaks outcomes down by would_skip_reason.

Probability calibrator

A learned win-probability model (L2 logistic regression) that records a real calibrated_probability alongside the heuristic confidence.

python -m strategy.train_calibrator # train from decisions.jsonl → data/models/
  • strategy/probability_calibrator.py — model + graceful fallback to the heuristic mean when no model/sklearn is present (never raises).
  • Decision-inert: the calibrated value is display/diagnostic only; decide(), the EV gate, and risk sizing are unaffected.
  • Model artifacts in data/models/ are gitignored (regenerable). Retrain as data grows.

The model is only as good as the data. On the first ~300 trades it sits near AUC 0.53 (no better than the average) — keep it dormant until the dataset is larger.

Building an uncensored dataset (shadow mode)

The decision log is censored: outcomes only exist for trades that passed every gate, so the disagreement cases needed to calibrate signals are never observed. Enable SHADOW_RECORD_MODE=true (DEMO only) to trade and record the would-be-skipped cases:

# .env
TRADE_MODE=DEMO
SHADOW_RECORD_MODE=true

The bot then places bot-direction trades it would normally skip (no_direction, ta_disagree, negative_ev, risk_blocked), tags them shadow=true with the would_skip_reason, and records outcomes — without feeding the production win-rate tracker or risk stats. Let it run, then re-run analyze_signals.py to see which gates actually earn their keep.


Safety Invariants

These must never be weakened without explicit intent:

  1. DEMO by default.TRADE_MODE=DEMO is the hard default. Even an empty TRADE_MODE= env var resets to DEMO.
  2. Demo guard. Before any real buy, broker/po_api.py decodes isDemo from the SSID using the API-native is_demo() method (with SSID-string fallback). If SSID is live but TRADE_MODE=DEMO, the trade is aborted.
  3. DRY_RUN. When DRY_RUN=true, buy/sell logs the would-be trade and returns without calling the API.
  4. Navigator never clicks amount buttons. The only safe action is clicking pair names, "Start Autotrade", "Main Menu", and "Trade Anyway" nag buttons. Clicking an amount/stake button places a martingale bot trade with the bot's own tokens — our trades go through the PocketOption API exclusively.
  5. Single session writer. Only one process may use the Telethon session at a time. Ensure telebot/scripts/pocket_robot_trader.py is stopped before running main_v2.py.

Data Files

PathDescription
data/decisions.jsonlStructured log: one row per evaluated signal
data/win_rates.jsonPer-pair win rate tracker (persisted)
data/trades.jsonlLegacy trade log (kept for compatibility)
logs/bot.logHuman-readable rotating log (1 day, 7-day retention)

Testing

All 100 tests run fully offline — no network, no SSID, no Telegram credentials.

pytest # all tests
pytest tests/ -v # verbose
pytest tests/test_signals.py # one module

For a live smoke test against the real bot (no trade placed):

python3 tools/v2_smoke.py
python3 tools/v2_smoke.py --pair GBPUSD_otc # skip navigation, test TA only

Development Notes

  • All live-path code is async/await. Errors are caught per-iteration and logged; the bot never crashes on a single bad cycle.
  • Frozen dataclasses are used for immutable results (TelegramSignal, SignalResult, ConfluenceResult, DirectionScreen, PredictionScreen). Mutable state lives in RiskManager, WinRateTracker, and the API client.
  • Direction is always the string "CALL", "PUT", or None — never booleans or enums.
  • Imports are absolute from project root.
  • pandas-ta is not used (incompatible with newer Python). All indicators are pure pandas/numpy.

Progression Path

PhaseConfigPurpose
Smoke testDRY_RUN=trueVerify navigation + TA works end-to-end
Capture modePAIR_SELECT_MIN_WIN_RATE=0.0, DRY_RUN=trueObserve all signals, log everything
Gated testingPAIR_SELECT_MIN_WIN_RATE=0.82, DRY_RUN=trueVerify gate logic against real signals
Demo liveTRADE_MODE=DEMO, DRY_RUN=falseReal trades on demo account
LiveTRADE_MODE=LIVE, DRY_RUN=falseOnly after sustained demo profitability

Built with: Python 3.12+ · Telethon · binaryoptionstoolsv2 · Pydantic · Loguru · pandas/numpy

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

270 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

ArgusSentinel

⚠️ TELEGRAM INTEGRATION REMOVED (2026-06-12). The Telethon/po_broker_bot integration described below has been deleted from the codebase (telegram_feed/, v1 main.py, navigator-driven loop, all TELEGRAM_* settings). Setup now only requires PO_SSID. The payout-first signals loop is the only trade driver. Telegram-related sections in this document are historical.

Telegram-driven binary options bot. Reads trade signals from po_broker_bot via Telegram, confirms with internal technical analysis, then places independent CALL/PUT trades via the PocketOption WebSocket API.

⚠️Risk Disclaimer: Binary options carry extreme risk of capital loss. Both the unofficial PocketOption API and the Telethon user session violate the respective platforms' Terms of Service. This project is for educational and research purposes only. Always use DEMO mode. Never risk money you cannot afford to lose.


How It Works

po_broker_bot (Telegram)
│
│ Telethon user session reads DMs
▼
Navigator drives po_broker_bot menus
│ → top pair + win rate (prediction screen)
│ → CALL/PUT direction (direction screen)
▼
Pair quality gate
│ win rate ≥ PAIR_SELECT_MIN_WIN_RATE (default 0.82)
▼
Internal TA Confluence Engine (5 signals)
│ RSI · MACD · Bollinger · EMA Cross · Candle Patterns
│ ≥ 3 signals must agree on same direction
▼
Decision logic
│ bot direction must match our TA direction
│ combined probability = (bot win% + our confluence score) / 2
▼
Risk Manager gates
│ min balance · max trades/hr · daily loss limit · cooldown
▼
PocketOption API (binaryoptionstoolsv2)
│ buy/sell(pair, $1.50, expiry)
│ Never clicks the martingale bot's amount button
│ Up to 6 trades open concurrently
▼
Background resolver (non-blocking)
│ poll_trade_outcome() → polls closed_deals() after expiry
│ WIN / LOSS / DRAW (check_win fallback if polling exhausted)
▼
decisions.jsonl + WinRateTracker + RiskManager

Architecture

PocketOptionBot/
├── main_v2.py # v2 entrypoint (--cycles N)
├── main.py # legacy entrypoint (unwired, kept)
│
├── config/
│ └── settings.py # All config via .env (Pydantic)
│
├── telegram_feed/
│ ├── client.py # TelegramSignalFeed (legacy listener)
│ ├── navigator.py # Button-drives po_broker_bot menus
│ ├── prediction_parser.py # Parses pair/win-rate screen
│ ├── direction_parser.py # Parses CALL/PUT direction screen
│ ├── pair_norm.py # Normalises pair labels → API symbols
│ └── parser.py # Legacy signal parser (kept)
│
├── broker/
│ └── po_api.py # PocketOptionAPIClient (buy/sell/check_win)
│ └── [connector/scraper/executor.py] # Legacy CDP modules (unwired)
│
├── signals/
│ ├── base.py # BaseSignal abstract class
│ ├── rsi.py # RSI (oversold/overbought)
│ ├── macd.py # MACD crossover
│ ├── bollinger.py # Bollinger Bands mean reversion
│ ├── ema_cross.py # EMA golden/death cross
│ ├── candle_pattern.py # Candlestick patterns
│ └── confluence.py # ConfluenceEngine: weighted scoring + ≥3 gate
│
├── strategy/
│ ├── manager_v2.py # v2 orchestrator (navigate→TA→decide→trade)
│ ├── decision.py # Pure agree/disagree + combined probability
│ ├── expiry.py # Nearest-allowed expiry selection
│ ├── trade_logger.py # decisions.jsonl writer + outcome backfill
│ ├── signal_gate.py # Legacy 3-gate filter (kept)
│ ├── win_rate.py # Per-pair win rate tracker (data/win_rates.json)
│ ├── risk.py # RiskManager (balance/hr/daily/cooldown)
│ └── manager.py # Legacy event-driven manager (kept)
│
├── data/
│ ├── candles.py # API candle dicts → o/h/l/c/v DataFrame
│ └── feed.py # Legacy price feed (kept)
│
├── tools/
│ ├── v2_smoke.py # One dry-run cycle smoke test
│ ├── gen_telegram_session.py # One-time Telethon session auth
│ └── test_telegram_feed.py # Live feed capture / debugging
│
├── utils/
│ └── logger.py # Loguru: logs/ + data/trades.jsonl
│
├── tests/ # 100 offline unit tests
│
└── docs/
└── superpowers/plans/
└── 2026-06-04-telebot-evolution.md # Full implementation plan

Quick Start

1. Prerequisites

  • Python 3.12+
  • An existing Telethon session authenticated to your Telegram account
    (run python3 tools/gen_telegram_session.py once if not set up)
  • A PocketOption SSID (copy the 42["auth",{...}] string from your browser's DevTools Network tab while logged in to pocketoption.com)

2. Install

cd~/code/openclaw/projects/PocketOptionBot
pip3 install -r requirements.txt

3. Configure

cp .env.example .env
# Edit .env with your credentials

Minimum required settings:

# TelegramTELEGRAM_API_ID=123456TELEGRAM_API_HASH=your_api_hashTELEGRAM_SESSION=~/.telebot/telegram.session# PocketOptionPO_SSID=42["auth",{"session":"...","isDemo":1,...}]# Safety (leave these until you are confident)TRADE_MODE=DEMODRY_RUN=trueSTAKE_AMOUNT=1.50

4. Smoke test (no trade placed)

python3 tools/v2_smoke.py
# Runs one full cycle: navigate → TA → decide → log (DRY_RUN forced true)# Check data/decisions.jsonl for the result

5. Run the bot

python3 main_v2.py # run until Ctrl-C
python3 main_v2.py --cycles 5 # run exactly 5 cycles then exit

Web Dashboard

A live monitoring + settings UI (FastAPI + WebSocket backend, zero-build vanilla JS frontend). Landscape layout: Performance (equity/P&L curve + win/loss) · Active Trades (live countdowns) · Trade History, plus an editable Settings tab. It reads the bot's output files and updates live over a WebSocket — it does not need the trading stack, an SSID, or Telegram.

Run it locally (one command)

git clone https://github.com/force-push/PocketOptionBot.git
cd PocketOptionBot
./scripts/run_dashboard.sh # venv + install + seed demo data + serve

Then open http://127.0.0.1:8787. Use --no-seed to keep existing data.

Or step by step (works on Windows too)

python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements-dashboard.txt
python tools/dashboard_demo.py # optional: seed synthetic demo data
python -m dashboard.server # http://127.0.0.1:8787

requirements-dashboard.txt is a minimal 5-package subset (FastAPI, uvicorn, watchfiles, pydantic-settings, python-dotenv) — no playwright / telethon / Rust wheel needed just to view the dashboard.

Demo data vs. live trading data

  • Demo (default above):tools/dashboard_demo.py writes deterministic synthetic data/decisions.jsonl + data/live_state.json so the UI is fully populated with no bot running. Re-run it any time to refresh.

  • Live: run the bot with the bridge enabled so it streams real state to the dashboard as it trades:

    DASHBOARD_ENABLED=true python3 main_v2.py

    Start the dashboard server in a second terminal; it picks up changes live.

Settings tab (writes to .env)

The Settings tab can edit configuration and save it back to .env. Safety rails are enforced server-side: secrets are masked and only written when changed, and flipping TRADE_MODE to LIVE is fail-closed — it requires explicit confirmation and an SSID that decodes as a live session. Most changes need a bot restart to take effect (the UI flags which).

Dashboard settings (.env)

VariableDefaultPurpose
DASHBOARD_ENABLEDfalsebot streams live state when true
DASHBOARD_HOST127.0.0.1bind address (keep localhost)
DASHBOARD_PORT8787server port
DASHBOARD_TOKEN(unset)when set, required to save settings (sent as a Bearer token)

🔒 Bind to 127.0.0.1 only. The Settings tab can change trading config, so do not expose the dashboard publicly without setting DASHBOARD_TOKEN.


Configuration Reference

All settings live in .env. See .env.example for the full list.

Critical safety settings

VariableDefaultDescription
TRADE_MODEDEMODEMO or LIVE. Hard-reset to DEMO if unset.
DRY_RUNtrueLog trades but never call buy/sell on the API.
STAKE_AMOUNT1.50Fixed stake per trade (USD).

v2 gate settings

VariableDefaultDescription
PAIR_SELECT_MIN_WIN_RATE0.0Minimum bot-stated win rate to consider a pair. Set to 0.82 for real runs; 0.0 disables the gate during testing.
DEFAULT_EXPIRY_SECONDS30Trade expiry. Snapped to nearest allowed value.
CLICK_TRADE_ANYWAYtrueAuto-click the "low-balance" nag page when it appears.
DECISIONS_LOG_PATHdata/decisions.jsonlPath for the structured decision log.
MIN_CONFLUENCE_SCORE0.35Minimum TA confluence score to agree with a signal (reduced from 0.75 to allow more trades during testing; actual threshold is adaptive based on signal agreement).
MIN_SIGNAL_AGREEMENT3Minimum number of signals that must agree on same direction (increased from 2 to 3 for stricter confluence).
BLOCKED_PAIRS["EURUSD_otc", "ETHUSD_otc"]List of pair API symbols (e.g., "EURUSD_otc") to skip during pair selection. When the bot presents a list of candidate pairs, the navigator cycles through and selects the first non-blocked pair, avoiding wasted analysis time. If all pairs are blocked, the cycle is skipped.
SHADOW_RECORD_MODEfalseResearch/data-collection mode (DEMO only). When true and TRADE_MODE=DEMO, the bot stops blocking at the TA-agreement, EV, and risk gates — it places the bot-direction trade anyway, tags the row shadow=true with would_skip_reason, and records the outcome. This builds an uncensored dataset (normally we only see outcomes for trades that passed every gate). Hard-guarded: ignored in LIVE; low_payout is still enforced; shadow outcomes are written to decisions.jsonl but do not feed the production win-rate tracker or risk stats. See Research & Calibration.

Risk settings

VariableDefaultDescription
MAX_TRADES_PER_HOUR10Rolling 1-hour cap.
MAX_DAILY_LOSS_USD20.0Daily loss stops trading for the day.
COOLDOWN_AFTER_LOSS_SECONDS120Pause after each loss.
MIN_BALANCE_MULTIPLIER5.0Balance must be ≥ this × stake to trade.

Telegram settings

VariableDefaultDescription
TELEGRAM_API_IDFrom my.telegram.org
TELEGRAM_API_HASHFrom my.telegram.org
TELEGRAM_SESSIONpo_sessionPath to .session file or session name.
SIGNAL_BOT_USERNAMEpo_broker_botThe Telegram bot to read from.

Technical Analysis Signals

All signals consume an o/h/l/c/v time-indexed DataFrame from data/candles.py.

SignalWeightCALL triggerPUT trigger
RSI0.20RSI < 30 (oversold)RSI > 70 (overbought)
MACD0.20MACD crosses above signalMACD crosses below signal
Bollinger0.20Price at lower band + revertingPrice at upper band + reverting
EMA Cross0.15Fast EMA crosses above slow EMAFast EMA crosses below slow EMA
Candle Patterns0.25Bullish engulfing / hammerBearish engulfing / shooting star

Confluence rule (updated 2026-06-09): the trade decision is now gated on MACD + EMA only (decision_signals) — both must agree on the same direction. RSI, Bollinger, and CandlePattern are still evaluated and recorded in the decision log for research, but they no longer affect whether a trade is taken. Data over ~410 trades showed only MACD/EMA carry a positive edge and that 3-signal agreement won less than 2-signal. See docs/signal-strategy-research.md for the full analysis and the candidate signals (ADX, ATR, Supertrend, …) queued for testing.


Decision Logic

Each cycle produces a DecisionRow appended to data/decisions.jsonl:

TRADE → bot direction matches our TA direction + both gates pass
SKIP → one of: no_direction · ta_disagree · ta_low_score · risk_blocked

Confidence (logged, not gated) — stored as combined_probability, shown in the dashboard as confidence (it is a heuristic score, not a calibrated probability):

confidence = (bot_win_rate + our_confluence_score) / 2

A learned, calibrated P(win) is also recorded per trade as calibrated_probability when a model exists — see Research & Calibration. It is display/diagnostic only and never influences trade decisions.


Decision Log (data/decisions.jsonl)

One JSON line per evaluated signal. Key fields:

{
"cycle_id": "20260605T042310-0001",
"pair_raw": "GBP/USD",
"pair_api": "GBPUSD",
"bot_win_rate": 0.90,
"bot_direction": "CALL",
"our_direction": "CALL",
"our_confluence_score": 0.78,
"agreement": true,
"combined_probability": 0.84,
"calibrated_probability": 0.61,
"decision": "TRADE",
"skip_reason": null,
"shadow": false,
"would_skip_reason": null,
"stake": 1.5,
"trade_id": "trade_abc123",
"outcome": "WIN",
"pnl": 1.28,
"pnl_currency": "USD",
"balance_before": 1000.0,
"balance_after": 1001.28,
"ts": "2026-06-05T04:23:10.000000+00:00"
}

Use this log to calibrate signal quality, tune gates, and identify which pairs perform well over time.


Research & Calibration

Tooling for understanding why trades win or lose and for improving the signal stack.

Signal-attribution report

python scripts/analyze_signals.py # full report
python scripts/analyze_signals.py --min-n 30 # raise the min sample for flags

Read-only analysis over data/decisions.jsonl. For resolved trades it reports, per signal, the win rate when the signal agrees / is neutral / opposes the traded direction (with Wilson CIs and lift), confluence-score and agreement-count buckets, per-pair win rates, the break-even edge vs observed payouts, and a censoring summary. It flags signals that hurt when they agree, look inverted (opposing beats base), or never fire. When shadow data exists it also breaks outcomes down by would_skip_reason.

Probability calibrator

A learned win-probability model (L2 logistic regression) that records a real calibrated_probability alongside the heuristic confidence.

python -m strategy.train_calibrator # train from decisions.jsonl → data/models/
  • strategy/probability_calibrator.py — model + graceful fallback to the heuristic mean when no model/sklearn is present (never raises).
  • Decision-inert: the calibrated value is display/diagnostic only; decide(), the EV gate, and risk sizing are unaffected.
  • Model artifacts in data/models/ are gitignored (regenerable). Retrain as data grows.

The model is only as good as the data. On the first ~300 trades it sits near AUC 0.53 (no better than the average) — keep it dormant until the dataset is larger.

Building an uncensored dataset (shadow mode)

The decision log is censored: outcomes only exist for trades that passed every gate, so the disagreement cases needed to calibrate signals are never observed. Enable SHADOW_RECORD_MODE=true (DEMO only) to trade and record the would-be-skipped cases:

# .env
TRADE_MODE=DEMO
SHADOW_RECORD_MODE=true

The bot then places bot-direction trades it would normally skip (no_direction, ta_disagree, negative_ev, risk_blocked), tags them shadow=true with the would_skip_reason, and records outcomes — without feeding the production win-rate tracker or risk stats. Let it run, then re-run analyze_signals.py to see which gates actually earn their keep.


Safety Invariants

These must never be weakened without explicit intent:

  1. DEMO by default.TRADE_MODE=DEMO is the hard default. Even an empty TRADE_MODE= env var resets to DEMO.
  2. Demo guard. Before any real buy, broker/po_api.py decodes isDemo from the SSID using the API-native is_demo() method (with SSID-string fallback). If SSID is live but TRADE_MODE=DEMO, the trade is aborted.
  3. DRY_RUN. When DRY_RUN=true, buy/sell logs the would-be trade and returns without calling the API.
  4. Navigator never clicks amount buttons. The only safe action is clicking pair names, "Start Autotrade", "Main Menu", and "Trade Anyway" nag buttons. Clicking an amount/stake button places a martingale bot trade with the bot's own tokens — our trades go through the PocketOption API exclusively.
  5. Single session writer. Only one process may use the Telethon session at a time. Ensure telebot/scripts/pocket_robot_trader.py is stopped before running main_v2.py.

Data Files

PathDescription
data/decisions.jsonlStructured log: one row per evaluated signal
data/win_rates.jsonPer-pair win rate tracker (persisted)
data/trades.jsonlLegacy trade log (kept for compatibility)
logs/bot.logHuman-readable rotating log (1 day, 7-day retention)

Testing

All 100 tests run fully offline — no network, no SSID, no Telegram credentials.

pytest # all tests
pytest tests/ -v # verbose
pytest tests/test_signals.py # one module

For a live smoke test against the real bot (no trade placed):

python3 tools/v2_smoke.py
python3 tools/v2_smoke.py --pair GBPUSD_otc # skip navigation, test TA only

Development Notes

  • All live-path code is async/await. Errors are caught per-iteration and logged; the bot never crashes on a single bad cycle.
  • Frozen dataclasses are used for immutable results (TelegramSignal, SignalResult, ConfluenceResult, DirectionScreen, PredictionScreen). Mutable state lives in RiskManager, WinRateTracker, and the API client.
  • Direction is always the string "CALL", "PUT", or None — never booleans or enums.
  • Imports are absolute from project root.
  • pandas-ta is not used (incompatible with newer Python). All indicators are pure pandas/numpy.

Progression Path

PhaseConfigPurpose
Smoke testDRY_RUN=trueVerify navigation + TA works end-to-end
Capture modePAIR_SELECT_MIN_WIN_RATE=0.0, DRY_RUN=trueObserve all signals, log everything
Gated testingPAIR_SELECT_MIN_WIN_RATE=0.82, DRY_RUN=trueVerify gate logic against real signals
Demo liveTRADE_MODE=DEMO, DRY_RUN=falseReal trades on demo account
LiveTRADE_MODE=LIVE, DRY_RUN=falseOnly after sustained demo profitability

Built with: Python 3.12+ · Telethon · binaryoptionstoolsv2 · Pydantic · Loguru · pandas/numpy

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

270 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

ArgusSentinel

⚠️ TELEGRAM INTEGRATION REMOVED (2026-06-12). The Telethon/po_broker_bot integration described below has been deleted from the codebase (telegram_feed/, v1 main.py, navigator-driven loop, all TELEGRAM_* settings). Setup now only requires PO_SSID. The payout-first signals loop is the only trade driver. Telegram-related sections in this document are historical.

Telegram-driven binary options bot. Reads trade signals from po_broker_bot via Telegram, confirms with internal technical analysis, then places independent CALL/PUT trades via the PocketOption WebSocket API.

⚠️Risk Disclaimer: Binary options carry extreme risk of capital loss. Both the unofficial PocketOption API and the Telethon user session violate the respective platforms' Terms of Service. This project is for educational and research purposes only. Always use DEMO mode. Never risk money you cannot afford to lose.


How It Works

po_broker_bot (Telegram)
│
│ Telethon user session reads DMs
▼
Navigator drives po_broker_bot menus
│ → top pair + win rate (prediction screen)
│ → CALL/PUT direction (direction screen)
▼
Pair quality gate
│ win rate ≥ PAIR_SELECT_MIN_WIN_RATE (default 0.82)
▼
Internal TA Confluence Engine (5 signals)
│ RSI · MACD · Bollinger · EMA Cross · Candle Patterns
│ ≥ 3 signals must agree on same direction
▼
Decision logic
│ bot direction must match our TA direction
│ combined probability = (bot win% + our confluence score) / 2
▼
Risk Manager gates
│ min balance · max trades/hr · daily loss limit · cooldown
▼
PocketOption API (binaryoptionstoolsv2)
│ buy/sell(pair, $1.50, expiry)
│ Never clicks the martingale bot's amount button
│ Up to 6 trades open concurrently
▼
Background resolver (non-blocking)
│ poll_trade_outcome() → polls closed_deals() after expiry
│ WIN / LOSS / DRAW (check_win fallback if polling exhausted)
▼
decisions.jsonl + WinRateTracker + RiskManager

Architecture

PocketOptionBot/
├── main_v2.py # v2 entrypoint (--cycles N)
├── main.py # legacy entrypoint (unwired, kept)
│
├── config/
│ └── settings.py # All config via .env (Pydantic)
│
├── telegram_feed/
│ ├── client.py # TelegramSignalFeed (legacy listener)
│ ├── navigator.py # Button-drives po_broker_bot menus
│ ├── prediction_parser.py # Parses pair/win-rate screen
│ ├── direction_parser.py # Parses CALL/PUT direction screen
│ ├── pair_norm.py # Normalises pair labels → API symbols
│ └── parser.py # Legacy signal parser (kept)
│
├── broker/
│ └── po_api.py # PocketOptionAPIClient (buy/sell/check_win)
│ └── [connector/scraper/executor.py] # Legacy CDP modules (unwired)
│
├── signals/
│ ├── base.py # BaseSignal abstract class
│ ├── rsi.py # RSI (oversold/overbought)
│ ├── macd.py # MACD crossover
│ ├── bollinger.py # Bollinger Bands mean reversion
│ ├── ema_cross.py # EMA golden/death cross
│ ├── candle_pattern.py # Candlestick patterns
│ └── confluence.py # ConfluenceEngine: weighted scoring + ≥3 gate
│
├── strategy/
│ ├── manager_v2.py # v2 orchestrator (navigate→TA→decide→trade)
│ ├── decision.py # Pure agree/disagree + combined probability
│ ├── expiry.py # Nearest-allowed expiry selection
│ ├── trade_logger.py # decisions.jsonl writer + outcome backfill
│ ├── signal_gate.py # Legacy 3-gate filter (kept)
│ ├── win_rate.py # Per-pair win rate tracker (data/win_rates.json)
│ ├── risk.py # RiskManager (balance/hr/daily/cooldown)
│ └── manager.py # Legacy event-driven manager (kept)
│
├── data/
│ ├── candles.py # API candle dicts → o/h/l/c/v DataFrame
│ └── feed.py # Legacy price feed (kept)
│
├── tools/
│ ├── v2_smoke.py # One dry-run cycle smoke test
│ ├── gen_telegram_session.py # One-time Telethon session auth
│ └── test_telegram_feed.py # Live feed capture / debugging
│
├── utils/
│ └── logger.py # Loguru: logs/ + data/trades.jsonl
│
├── tests/ # 100 offline unit tests
│
└── docs/
└── superpowers/plans/
└── 2026-06-04-telebot-evolution.md # Full implementation plan

Quick Start

1. Prerequisites

  • Python 3.12+
  • An existing Telethon session authenticated to your Telegram account
    (run python3 tools/gen_telegram_session.py once if not set up)
  • A PocketOption SSID (copy the 42["auth",{...}] string from your browser's DevTools Network tab while logged in to pocketoption.com)

2. Install

cd~/code/openclaw/projects/PocketOptionBot
pip3 install -r requirements.txt

3. Configure

cp .env.example .env
# Edit .env with your credentials

Minimum required settings:

# TelegramTELEGRAM_API_ID=123456TELEGRAM_API_HASH=your_api_hashTELEGRAM_SESSION=~/.telebot/telegram.session# PocketOptionPO_SSID=42["auth",{"session":"...","isDemo":1,...}]# Safety (leave these until you are confident)TRADE_MODE=DEMODRY_RUN=trueSTAKE_AMOUNT=1.50

4. Smoke test (no trade placed)

python3 tools/v2_smoke.py
# Runs one full cycle: navigate → TA → decide → log (DRY_RUN forced true)# Check data/decisions.jsonl for the result

5. Run the bot

python3 main_v2.py # run until Ctrl-C
python3 main_v2.py --cycles 5 # run exactly 5 cycles then exit

Web Dashboard

A live monitoring + settings UI (FastAPI + WebSocket backend, zero-build vanilla JS frontend). Landscape layout: Performance (equity/P&L curve + win/loss) · Active Trades (live countdowns) · Trade History, plus an editable Settings tab. It reads the bot's output files and updates live over a WebSocket — it does not need the trading stack, an SSID, or Telegram.

Run it locally (one command)

git clone https://github.com/force-push/PocketOptionBot.git
cd PocketOptionBot
./scripts/run_dashboard.sh # venv + install + seed demo data + serve

Then open http://127.0.0.1:8787. Use --no-seed to keep existing data.

Or step by step (works on Windows too)

python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements-dashboard.txt
python tools/dashboard_demo.py # optional: seed synthetic demo data
python -m dashboard.server # http://127.0.0.1:8787

requirements-dashboard.txt is a minimal 5-package subset (FastAPI, uvicorn, watchfiles, pydantic-settings, python-dotenv) — no playwright / telethon / Rust wheel needed just to view the dashboard.

Demo data vs. live trading data

  • Demo (default above):tools/dashboard_demo.py writes deterministic synthetic data/decisions.jsonl + data/live_state.json so the UI is fully populated with no bot running. Re-run it any time to refresh.

  • Live: run the bot with the bridge enabled so it streams real state to the dashboard as it trades:

    DASHBOARD_ENABLED=true python3 main_v2.py

    Start the dashboard server in a second terminal; it picks up changes live.

Settings tab (writes to .env)

The Settings tab can edit configuration and save it back to .env. Safety rails are enforced server-side: secrets are masked and only written when changed, and flipping TRADE_MODE to LIVE is fail-closed — it requires explicit confirmation and an SSID that decodes as a live session. Most changes need a bot restart to take effect (the UI flags which).

Dashboard settings (.env)

VariableDefaultPurpose
DASHBOARD_ENABLEDfalsebot streams live state when true
DASHBOARD_HOST127.0.0.1bind address (keep localhost)
DASHBOARD_PORT8787server port
DASHBOARD_TOKEN(unset)when set, required to save settings (sent as a Bearer token)

🔒 Bind to 127.0.0.1 only. The Settings tab can change trading config, so do not expose the dashboard publicly without setting DASHBOARD_TOKEN.


Configuration Reference

All settings live in .env. See .env.example for the full list.

Critical safety settings

VariableDefaultDescription
TRADE_MODEDEMODEMO or LIVE. Hard-reset to DEMO if unset.
DRY_RUNtrueLog trades but never call buy/sell on the API.
STAKE_AMOUNT1.50Fixed stake per trade (USD).

v2 gate settings

VariableDefaultDescription
PAIR_SELECT_MIN_WIN_RATE0.0Minimum bot-stated win rate to consider a pair. Set to 0.82 for real runs; 0.0 disables the gate during testing.
DEFAULT_EXPIRY_SECONDS30Trade expiry. Snapped to nearest allowed value.
CLICK_TRADE_ANYWAYtrueAuto-click the "low-balance" nag page when it appears.
DECISIONS_LOG_PATHdata/decisions.jsonlPath for the structured decision log.
MIN_CONFLUENCE_SCORE0.35Minimum TA confluence score to agree with a signal (reduced from 0.75 to allow more trades during testing; actual threshold is adaptive based on signal agreement).
MIN_SIGNAL_AGREEMENT3Minimum number of signals that must agree on same direction (increased from 2 to 3 for stricter confluence).
BLOCKED_PAIRS["EURUSD_otc", "ETHUSD_otc"]List of pair API symbols (e.g., "EURUSD_otc") to skip during pair selection. When the bot presents a list of candidate pairs, the navigator cycles through and selects the first non-blocked pair, avoiding wasted analysis time. If all pairs are blocked, the cycle is skipped.
SHADOW_RECORD_MODEfalseResearch/data-collection mode (DEMO only). When true and TRADE_MODE=DEMO, the bot stops blocking at the TA-agreement, EV, and risk gates — it places the bot-direction trade anyway, tags the row shadow=true with would_skip_reason, and records the outcome. This builds an uncensored dataset (normally we only see outcomes for trades that passed every gate). Hard-guarded: ignored in LIVE; low_payout is still enforced; shadow outcomes are written to decisions.jsonl but do not feed the production win-rate tracker or risk stats. See Research & Calibration.

Risk settings

VariableDefaultDescription
MAX_TRADES_PER_HOUR10Rolling 1-hour cap.
MAX_DAILY_LOSS_USD20.0Daily loss stops trading for the day.
COOLDOWN_AFTER_LOSS_SECONDS120Pause after each loss.
MIN_BALANCE_MULTIPLIER5.0Balance must be ≥ this × stake to trade.

Telegram settings

VariableDefaultDescription
TELEGRAM_API_IDFrom my.telegram.org
TELEGRAM_API_HASHFrom my.telegram.org
TELEGRAM_SESSIONpo_sessionPath to .session file or session name.
SIGNAL_BOT_USERNAMEpo_broker_botThe Telegram bot to read from.

Technical Analysis Signals

All signals consume an o/h/l/c/v time-indexed DataFrame from data/candles.py.

SignalWeightCALL triggerPUT trigger
RSI0.20RSI < 30 (oversold)RSI > 70 (overbought)
MACD0.20MACD crosses above signalMACD crosses below signal
Bollinger0.20Price at lower band + revertingPrice at upper band + reverting
EMA Cross0.15Fast EMA crosses above slow EMAFast EMA crosses below slow EMA
Candle Patterns0.25Bullish engulfing / hammerBearish engulfing / shooting star

Confluence rule (updated 2026-06-09): the trade decision is now gated on MACD + EMA only (decision_signals) — both must agree on the same direction. RSI, Bollinger, and CandlePattern are still evaluated and recorded in the decision log for research, but they no longer affect whether a trade is taken. Data over ~410 trades showed only MACD/EMA carry a positive edge and that 3-signal agreement won less than 2-signal. See docs/signal-strategy-research.md for the full analysis and the candidate signals (ADX, ATR, Supertrend, …) queued for testing.


Decision Logic

Each cycle produces a DecisionRow appended to data/decisions.jsonl:

TRADE → bot direction matches our TA direction + both gates pass
SKIP → one of: no_direction · ta_disagree · ta_low_score · risk_blocked

Confidence (logged, not gated) — stored as combined_probability, shown in the dashboard as confidence (it is a heuristic score, not a calibrated probability):

confidence = (bot_win_rate + our_confluence_score) / 2

A learned, calibrated P(win) is also recorded per trade as calibrated_probability when a model exists — see Research & Calibration. It is display/diagnostic only and never influences trade decisions.


Decision Log (data/decisions.jsonl)

One JSON line per evaluated signal. Key fields:

{
"cycle_id": "20260605T042310-0001",
"pair_raw": "GBP/USD",
"pair_api": "GBPUSD",
"bot_win_rate": 0.90,
"bot_direction": "CALL",
"our_direction": "CALL",
"our_confluence_score": 0.78,
"agreement": true,
"combined_probability": 0.84,
"calibrated_probability": 0.61,
"decision": "TRADE",
"skip_reason": null,
"shadow": false,
"would_skip_reason": null,
"stake": 1.5,
"trade_id": "trade_abc123",
"outcome": "WIN",
"pnl": 1.28,
"pnl_currency": "USD",
"balance_before": 1000.0,
"balance_after": 1001.28,
"ts": "2026-06-05T04:23:10.000000+00:00"
}

Use this log to calibrate signal quality, tune gates, and identify which pairs perform well over time.


Research & Calibration

Tooling for understanding why trades win or lose and for improving the signal stack.

Signal-attribution report

python scripts/analyze_signals.py # full report
python scripts/analyze_signals.py --min-n 30 # raise the min sample for flags

Read-only analysis over data/decisions.jsonl. For resolved trades it reports, per signal, the win rate when the signal agrees / is neutral / opposes the traded direction (with Wilson CIs and lift), confluence-score and agreement-count buckets, per-pair win rates, the break-even edge vs observed payouts, and a censoring summary. It flags signals that hurt when they agree, look inverted (opposing beats base), or never fire. When shadow data exists it also breaks outcomes down by would_skip_reason.

Probability calibrator

A learned win-probability model (L2 logistic regression) that records a real calibrated_probability alongside the heuristic confidence.

python -m strategy.train_calibrator # train from decisions.jsonl → data/models/
  • strategy/probability_calibrator.py — model + graceful fallback to the heuristic mean when no model/sklearn is present (never raises).
  • Decision-inert: the calibrated value is display/diagnostic only; decide(), the EV gate, and risk sizing are unaffected.
  • Model artifacts in data/models/ are gitignored (regenerable). Retrain as data grows.

The model is only as good as the data. On the first ~300 trades it sits near AUC 0.53 (no better than the average) — keep it dormant until the dataset is larger.

Building an uncensored dataset (shadow mode)

The decision log is censored: outcomes only exist for trades that passed every gate, so the disagreement cases needed to calibrate signals are never observed. Enable SHADOW_RECORD_MODE=true (DEMO only) to trade and record the would-be-skipped cases:

# .env
TRADE_MODE=DEMO
SHADOW_RECORD_MODE=true

The bot then places bot-direction trades it would normally skip (no_direction, ta_disagree, negative_ev, risk_blocked), tags them shadow=true with the would_skip_reason, and records outcomes — without feeding the production win-rate tracker or risk stats. Let it run, then re-run analyze_signals.py to see which gates actually earn their keep.


Safety Invariants

These must never be weakened without explicit intent:

  1. DEMO by default.TRADE_MODE=DEMO is the hard default. Even an empty TRADE_MODE= env var resets to DEMO.
  2. Demo guard. Before any real buy, broker/po_api.py decodes isDemo from the SSID using the API-native is_demo() method (with SSID-string fallback). If SSID is live but TRADE_MODE=DEMO, the trade is aborted.
  3. DRY_RUN. When DRY_RUN=true, buy/sell logs the would-be trade and returns without calling the API.
  4. Navigator never clicks amount buttons. The only safe action is clicking pair names, "Start Autotrade", "Main Menu", and "Trade Anyway" nag buttons. Clicking an amount/stake button places a martingale bot trade with the bot's own tokens — our trades go through the PocketOption API exclusively.
  5. Single session writer. Only one process may use the Telethon session at a time. Ensure telebot/scripts/pocket_robot_trader.py is stopped before running main_v2.py.

Data Files

PathDescription
data/decisions.jsonlStructured log: one row per evaluated signal
data/win_rates.jsonPer-pair win rate tracker (persisted)
data/trades.jsonlLegacy trade log (kept for compatibility)
logs/bot.logHuman-readable rotating log (1 day, 7-day retention)

Testing

All 100 tests run fully offline — no network, no SSID, no Telegram credentials.

pytest # all tests
pytest tests/ -v # verbose
pytest tests/test_signals.py # one module

For a live smoke test against the real bot (no trade placed):

python3 tools/v2_smoke.py
python3 tools/v2_smoke.py --pair GBPUSD_otc # skip navigation, test TA only

Development Notes

  • All live-path code is async/await. Errors are caught per-iteration and logged; the bot never crashes on a single bad cycle.
  • Frozen dataclasses are used for immutable results (TelegramSignal, SignalResult, ConfluenceResult, DirectionScreen, PredictionScreen). Mutable state lives in RiskManager, WinRateTracker, and the API client.
  • Direction is always the string "CALL", "PUT", or None — never booleans or enums.
  • Imports are absolute from project root.
  • pandas-ta is not used (incompatible with newer Python). All indicators are pure pandas/numpy.

Progression Path

PhaseConfigPurpose
Smoke testDRY_RUN=trueVerify navigation + TA works end-to-end
Capture modePAIR_SELECT_MIN_WIN_RATE=0.0, DRY_RUN=trueObserve all signals, log everything
Gated testingPAIR_SELECT_MIN_WIN_RATE=0.82, DRY_RUN=trueVerify gate logic against real signals
Demo liveTRADE_MODE=DEMO, DRY_RUN=falseReal trades on demo account
LiveTRADE_MODE=LIVE, DRY_RUN=falseOnly after sustained demo profitability

Built with: Python 3.12+ · Telethon · binaryoptionstoolsv2 · Pydantic · Loguru · pandas/numpy

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages