Indicators Reference

🤖 RussBot is a free, open-source binary options trading bot for PocketOption, built on BinaryOptionsToolsV2Get it on GitLab
💬 Need help? Join the Chipa Discord · ⚡ Go no-code with ChipaEditor

RussBot implements its three indicators from scratch in the TechnicalIndicators class — no TA library required. This page documents each implementation so you can verify, test, or modify them.

EMA — Exponential Moving Average

Used for: trend baseline (period 10) and as the building block for MACD.

The EMA seeds with an SMA over the first period values, then applies the standard recursive smoothing:

@staticmethod
def ema(data: List[float], period: int) -> List[float]:
    """Calculate Exponential Moving Average"""
    if len(data) < period:
        return [np.nan] * len(data)

    ema_values = []
    multiplier = 2 / (period + 1)

    # Start with SMA for first value
    sma = sum(data[:period]) / period
    ema_values.extend([np.nan] * (period - 1))
    ema_values.append(sma)

    # Calculate EMA for remaining values
    for i in range(period, len(data)):
        ema = (data[i] * multiplier) + (ema_values[-1] * (1 - multiplier))
        ema_values.append(ema)

    return ema_values

Key properties:

  • Returns NaN for the first period - 1 positions (not enough data)
  • Multiplier is 2 / (period + 1) — for EMA(10) that’s ≈ 0.1818, so each new close carries ~18% weight
  • In the strategy, the slope of the EMA (current vs. previous value) is as important as the price’s position relative to it

CCI — Commodity Channel Index

Used for: detecting momentum extremes at ±100 (period 7).

@staticmethod
def cci(high: List[float], low: List[float], close: List[float], period: int = 7) -> List[float]:
    """Calculate Commodity Channel Index"""
    cci_values = []

    for i in range(len(close)):
        if i < period - 1:
            cci_values.append(np.nan)
            continue

        # Typical price for each bar in the window
        typical_prices = [
            (high[j] + low[j] + close[j]) / 3
            for j in range(i - period + 1, i + 1)
        ]

        # SMA of typical price and mean absolute deviation
        sma_tp = sum(typical_prices) / period
        mad = sum(abs(tp - sma_tp) for tp in typical_prices) / period

        # CCI formula (0.015 is Lambert's scaling constant)
        current_tp = (high[i] + low[i] + close[i]) / 3
        cci = (current_tp - sma_tp) / (0.015 * mad) if mad != 0 else 0

        cci_values.append(cci)

    return cci_values

Key properties:

  • Typical price = (high + low + close) / 3
  • The 0.015 constant scales CCI so ~70–80% of values fall between −100 and +100 — which is exactly why the strategy treats ±100 as “momentum extreme reached”
  • Zero mean-deviation (a perfectly flat window) safely returns 0 instead of dividing by zero
  • The short period (7) makes it responsive enough for 15-second candles

MACD — Moving Average Convergence Divergence

Used for: momentum context in the analysis output, and as the minimum-data gate (its 26-period slow EMA means the bot won’t trade until 26 candles exist).

@staticmethod
def macd(data: List[float], fast: int = 12, slow: int = 26, signal: int = 9):
    """Calculate MACD (line, signal, histogram)"""
    ema_fast = TechnicalIndicators.ema(data, fast)
    ema_slow = TechnicalIndicators.ema(data, slow)

    # MACD line = fast EMA - slow EMA
    macd_line = [
        ema_fast[i] - ema_slow[i]
        if not (pd.isna(ema_fast[i]) or pd.isna(ema_slow[i])) else np.nan
        for i in range(len(data))
    ]

    # Signal line = EMA(9) of the MACD line, computed on valid values only
    # then mapped back to the full-length array
    ...

    # Histogram = MACD line - signal line
    histogram = [...]

    return macd_line, signal_line, histogram

Key properties:

  • The signal line is computed only on the non-NaN portion of the MACD line, then mapped back to full-length arrays — this avoids NaN contamination that naive implementations suffer from
  • Returns three aligned lists: MACD line, signal line, histogram
  • With defaults (12, 26, 9), the first valid signal value appears around candle 34

Data Requirements

IndicatorFirst valid value at candle #
EMA(10)10
CCI(7)7
MACD line (12, 26)26
MACD signal (9)~34

The bot’s calculate_indicators() requires at least 26 candles before returning anything, and the historical pre-load (1 hour via api.history()) means this is normally satisfied instantly at startup.

Verifying the Implementations

Run the bundled test script to check all three indicators against sample data — no PocketOption connection required:

python test_indicators.py

See Testing for details.

Customizing

All periods are configurable in config.json (ema_period, cci_period, macd_fast, macd_slow, macd_signal). If you swap in different indicators entirely, keep two things intact:

  1. The minimum-data gate — make sure calculate_indicators() still refuses to run before your longest lookback is satisfied
  2. The NaN discipline — return None from calculate_indicators() whenever any current value is NaN, so the bot never trades on partial data

💡 Want to test indicator variations without touching the math? ChipaEditor lets you compose and backtest indicator-based strategies visually — and the Chipa Discord is the place to compare notes on what’s working.