Розробка бота на базі скользящих середніх (MA/EMA)
Скользящі середні (Moving Average) — базовий і один з найбільш надійних інструментів технічного аналізу. Бот на MA/EMA генерує сигнали на основі пересічень та положення ціни відносно середньої. Простий у реалізації, зрозумілий у логіці, та при правильній параметризації дає стабільні результати на трендових ринках.
MA vs EMA: в чому різниця
SMA (Simple MA) — просте середнє за N періодів. Усі свічки мають однаковий вага.
EMA (Exponential MA) — зважене середнє, останні свічки мають більший вага. Швидше реагує на зміни ціни.
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()
Для трейдингу EMA переважніша: швидше сигналізує про розвороти тренду.
Класичні стратегії
Golden Cross / Death Cross
Пересічення EMA 50 та EMA 200:
- Golden Cross: EMA50 пересікає EMA200 знизу вгору → бичий сигнал
- Death Cross: EMA50 пересікає 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'
Triple EMA стратегія (9/21/55)
Три середні дають більше підтверджень:
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]
# Бичий сигнал: усі EMA вишикувані по порядку
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'
Реалізація бота
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"Закрили long, PnL: {pnl:.2f}%")
self.position = None
Параметризація та оптимізація
Оптимальні періоди EMA залежать від таймфрейму:
| Таймфрейм | Fast EMA | Slow EMA | Застосування |
|---|---|---|---|
| 1h | 9 | 21 | Інтрадей |
| 4h | 21 | 55 | Свинг |
| Daily | 50 | 200 | Golden/Death Cross |
| Weekly | 20 | 50 | Довгострок |
EMA-стратегії працюють погано на боковому ринку — дають багато хибних сигналів. Додайте фільтр ADX (Average Directional Index): торгуйте тільки коли ADX > 25 (ринок у тренді). Це зменшує кількість сделок, але покращує якість.







