Moving Average (MA/EMA) Trading Bot Development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Moving Average (MA/EMA) Trading Bot Development
Simple
~3-5 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Moving Average Bot Development (MA/EMA)

Moving Averages (MA) are a basic and one of the most reliable technical analysis tools. A bot based on MA/EMA generates signals from crossovers and price position relative to the average. Simple to implement, clear in logic, and with proper parameterization provides stable results on trending markets.

MA vs EMA: What's the Difference

SMA (Simple MA) — simple average over N periods. All candles have equal weight.

EMA (Exponential MA) — weighted average, recent candles have more weight. Reacts faster 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.

Classic Strategies

Golden Cross / Death Cross

Crossing of EMA 50 and EMA 200:

  • Golden Cross: EMA50 crosses EMA200 from below → bullish signal
  • Death Cross: EMA50 crosses EMA200 from above → bearish signal
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)

        # Crossover: different relationship on previous candle than current
        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'

Triple EMA Strategy (9/21/55)

Three averages provide more confirmations:

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]

        # Bullish signal: all EMAs aligned in order
        if last_9 > last_21 > last_55 and price > last_9:
            return 'BUY'

        # Bearish signal: reverse order
        if last_9 < last_21 < last_55 and price < last_9:
            return 'SELL'

        return 'HOLD'

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:]  # keep history

        if len(self.candles) < 210:  # need warmup for 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

Parameterization and Optimization

Optimal EMA periods depend on timeframe:

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

MA-strategies perform poorly on sideways markets — they give many false signals. Add ADX (Average Directional Index) filter: trade only when ADX > 25 (market is in trend). This reduces trade count but improves quality.