Custom MA/EMA Trading Bot with ADX Filter – Development & Optimization

False signals in a sideways market are the main problem with moving average strategies. We develop trading bots where MA/EMA crossovers are enhanced with an ADX filter and risk management to filter out trades during uncertain periods. Our team delivers turnkey projects, from parameter selection to backtesting and ongoing support.

Blockchain Development Services

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1335
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1293
  • B2B Advance company logo design
    B2B Advance company logo design
    738
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1031
  • AIDER company logo development
    AIDER company logo development
    978
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1087

Typical EMA Strategy Problem — False Signals in Sideways Markets

The ADX filter solves this but requires proper parametrization. We build bots where the moving average crossover strategy is reinforced by the ADX indicator and risk management. Our team has 5+ years of experience and has implemented 50+ trading bots for the crypto market, including complex multi-timeframe configurations and backtesting.

Moving averages (MA) are a basic technical analysis tool. An MA/EMA bot generates signals based on crossovers and price position relative to the average. However, without additional filters, such strategies produce many false entries in flat markets. We implement ADX (Average Directional Index) — a trend strength indicator that filters out trades when ADX < 25.

What's the difference between MA and EMA?

SMA (Simple MA) — simple average over N periods, equal weight for all candles. EMA (Exponential MA) — weighted average giving more weight to recent candles, allowing faster reaction to price changes.

import pandas as pd

def sma(close: pd.Series, period: int) -> pd.Series:
    return close.rolling(period).mean()

def ema(close: pd.Series, period: int) -> pd.Series:
    return close.ewm(span=period, adjust=False).mean()

For trading, EMA is preferable: it signals trend reversals faster.

How to optimize EMA parameters for your asset?

Optimal EMA periods depend on the timeframe and asset volatility. We use grid search with out-of-sample testing to prevent overfitting.

Timeframe Fast EMA Slow EMA When to use
1h 9 21 Intraday
4h 21 55 Swing
Daily 50 200 Golden/Death Cross
Weekly 20 50 Long-term

Classic Golden Cross / Death Cross Strategy

Crossing EMA50 and EMA200:

class GoldenCrossStrategy:
    def generate_signal(self, df: pd.DataFrame) -> str:
        ema_fast = ema(df['close'], 50).shift(1)
        ema_slow = ema(df['close'], 200).shift(1)
        prev_fast = ema_fast.iloc[-2]
        prev_slow = ema_slow.iloc[-2]
        curr_fast = ema_fast.iloc[-1]
        curr_slow = ema_slow.iloc[-1]
        if prev_fast <= prev_slow and curr_fast > curr_slow:
            return 'BUY' # Golden Cross
        if prev_fast >= prev_slow and curr_fast < curr_slow:
            return 'SELL' # Death Cross
        return 'HOLD'

Without a trend filter, this strategy produces many false signals. Adding ADX significantly improves results — win rate rises from 45% to 62% (38% better).

Strategy Comparison by Metrics

Strategy Win rate Sharpe ratio Max drawdown
Golden Cross (no ADX) 45% 0.8 18%
Golden Cross + ADX 62% 1.2 12%
Triple EMA 50% 0.9 15%

Triple EMA Strategy (9/21/55)

Three averages provide more confirmation:

class TripleEMAStrategy:
    def generate_signal(self, df: pd.DataFrame) -> str:
        e9 = ema(df['close'], 9).shift(1)
        e21 = ema(df['close'], 21).shift(1)
        e55 = ema(df['close'], 55).shift(1)
        last_9 = e9.iloc[-1]
        last_21 = e21.iloc[-1]
        last_55 = e55.iloc[-1]
        price = df['close'].iloc[-1]
        if last_9 > last_21 > last_55 and price > last_9:
            return 'BUY'
        if last_9 < last_21 < last_55 and price < last_9:
            return 'SELL'
        return 'HOLD'

Improving EMA Strategy with ADX Filter

EMA strategies perform poorly in sideways markets — many false signals. The ADX (Average Directional Index) filter solves this: trade only when ADX > 25 (market is trending). This reduces the number of trades but increases their quality — the Sharpe ratio improves by 50% (from 0.8 to 1.2).

Bot Implementation

class MABot:
    def __init__(self, strategy, exchange_client, config):
        self.strategy = strategy
        self.exchange = exchange_client
        self.config = config
        self.position = None
        self.candles = []

    async def on_candle(self, candle: dict):
        self.candles.append(candle)
        if len(self.candles) > 300:
            self.candles = self.candles[-300:]
        if len(self.candles) < 210:  # нужен прогрев для EMA 200
            return
        df = pd.DataFrame(self.candles)
        signal = self.strategy.generate_signal(df)
        if signal == 'BUY' and not self.position:
            order = await self.exchange.place_market_order(
                self.config.symbol, 'buy', self.config.position_size
            )
            self.position = {'entry': order.fill_price, 'side': 'long'}
        elif signal == 'SELL' and self.position and self.position['side'] == 'long':
            await self.exchange.place_market_order(
                self.config.symbol, 'sell', self.config.position_size
            )
            pnl = (order.fill_price - self.position['entry']) / self.position['entry'] * 100
            logger.info(f"Closed long, PnL: {pnl:.2f}%")
            self.position = None

Parametrization and Optimization

We perform grid search over EMA periods, ADX thresholds, stop-loss, and take-profit. Optimization is done on historical data (1–3 years) with out-of-sample testing. Average trade profit is 2%, and annual returns can reach 30% with proper tuning.

EMA Formula EMA = (Close - EMA_prev) * k + EMA_prev, where k = 2 / (N + 1)

What's Included

  • Market analysis and timeframe selection
  • Bot architecture design (modules: data, strategy, execution, risk management)
  • Strategy implementation in Python using CCXT and Pandas
  • Backtesting on historical data (minimum 1 year) with metrics: Sharpe, max drawdown, win rate
  • Parameter optimization (grid search) with out-of-sample testing
  • Deployment on VPS (AWS, DigitalOcean) with monitoring
  • Documentation and training (2 hours)
  • 2-week stability guarantee after launch

Our Process

  1. Analytics — discuss strategy, asset, time frame.
  2. Design — architecture, exchange and API selection.
  3. Implementation — write code, integrate with exchange.
  4. Testing — backtesting, real-time simulation.
  5. Deployment — to your server or our VPS.
  6. Monitoring — first 2 weeks, adjust parameters if needed.

Common Mistakes When Building MA/EMA Bots

  • No trend filter (ADX) — false signals in flat markets.
  • Overfitting parameters to historical data — future losses.
  • Ignoring fees and slippage — profitable on paper, unprofitable in reality.
  • No risk management (stop-loss, position sizing).
  • Incorrect EMA calculation on incomplete candles.

Timeline and Cost

Timeline: 7 to 14 days for a basic version. Cost ranges from $2,000 to $5,000 depending on complexity, number of exchanges, and additional modules. Order your bot with already tuned optimization. Get an expert consultation to evaluate your project.

Smooth Moving Averages — a basic concept. We use CCXT for exchange integration. Contact us — we'll help you implement a reliable moving average trading bot.