Bài viết gần đây
-
Lịch Khai Giảng 07/2026
Tháng 8 11, 2026 -
Tính Chỉ Báo MACD Bằng Python – MACD Line, Signal, Histogram
Tháng 8 11, 2026 -
Tính Chỉ Báo RSI Bằng Python – Công Thức Wilder, Quá Mua, Quá Bán
Tháng 8 10, 2026
| Tính Chỉ Báo MACD Bằng Python – MACD Line, Signal, Histogram
Được viết bởi Đặng Trí Thanh vào ngày 11/08/2026 lúc 12:06 | 51 lượt xem
Mục lục
1. MACD là gì?
3. Code MACD bằng Python thuần
4. Code MACD bằng pandas — vectorized
5. Dùng pandas_ta — 1 dòng là xong
6. Tín hiệu MACD: Crossover & Histogram
8. Kết hợp MACD + RSI + MA — bộ 3 chỉ báo
10. FAQ
11. Kết luận
1. MACD là gì?
MACD (Moving Average Convergence Divergence) được phát minh bởi Gerald Appel năm 1979. Đây là chỉ báo momentum + trend — vừa đo lực mua/bán, vừa xác định xu hướng. Cùng với MA và RSI, MACD tạo thành bộ 3 chỉ báo nền tảng của phân tích kỹ thuật.
MACD gồm 3 thành phần:
MACD Line = EMA(12) - EMA(26) ← đường nhanh
Signal Line = EMA(9) của MACD Line ← đường chậm
Histogram = MACD Line - Signal ← cột đo chênh lệch
Trực quan hóa:
↑ Giá BTC
│ ╱╲
│ ╱ ╲___ MACD cắt LÊN Signal → BULLISH
│ ╱ ╲___
│ ╱ ╲ MACD cắt XUỐNG Signal → BEARISH
└────────────────────→
MACD: ██░░░░████████
Signal: ──────────────
Hist: ▆▆▂▂▂▂▆▆▆▆▆▆▆
2. Công thức MACD — từng bước
Bước 1: Tính EMA12 và EMA26
$$text{EMA}_t = alpha cdot P_t + (1 – alpha) cdot text{EMA}_{t-1}, quad alpha = frac{2}{N+1}$$
Bước 2: MACD Line
$$text{MACD Line} = EMA_{12} – EMA_{26}$$
Bước 3: Signal Line
$$text{Signal} = EMA_9(text{MACD Line})$$
Bước 4: Histogram
$$text{Histogram} = text{MACD Line} – text{Signal}$$
Ý nghĩa các giá trị:
3. Code MACD bằng Python thuần
def ema_stdlib(prices: list[float], period: int) -> list[float]:
"""Tính EMA bằng Python stdlib."""
alpha = 2 / (period + 1)
ema = [prices[0]] # Giá trị đầu = giá đầu tiên
for price in prices[1:]:
ema.append(alpha * price + (1 - alpha) * ema[-1])
return ema
def macd_stdlib(prices: list[float], fast: int = 12, slow: int = 26, signal: int = 9):
"""
Tính MACD bằng Python stdlib.
Returns: (macd_line, signal_line, histogram)
"""
ema_fast = ema_stdlib(prices, fast)
ema_slow = ema_stdlib(prices, slow)
# MACD Line = EMA12 - EMA26
macd_line = [ema_fast[i] - ema_slow[i] for i in range(len(prices))]
# Signal Line = EMA9 của MACD Line
signal_line = ema_stdlib(macd_line, signal)
# Histogram = MACD - Signal
histogram = [macd_line[i] - signal_line[i] for i in range(len(prices))]
return macd_line, signal_line, histogram
# ===== TEST =====
if __name__ == "__main__":
prices = [100, 102, 101, 105, 103, 108, 110, 107, 112, 115,
113, 110, 108, 106, 104, 107, 111, 115, 118, 120,
122, 125, 123, 120, 118, 121, 124, 128, 130, 127]
macd, sig, hist = macd_stdlib(prices)
print(f"MACD cuối: {macd[-1]:.4f}")
print(f"Signal cuối: {sig[-1]:.4f}")
print(f"Histogram cuối: {hist[-1]:.4f}")
print(f"Tín hiệu: {'MUA' if macd[-1] > sig[-1] else 'BÁN'}")
4. Code MACD bằng pandas — vectorized
import pandas as pd
def ema_pandas(series: pd.Series, period: int) -> pd.Series:
"""EMA bằng pandas."""
return series.ewm(span=period, adjust=False).mean()
def macd_pandas(series: pd.Series, fast: int = 12, slow: int = 26, signal: int = 9):
"""
Tính MACD bằng pandas — nhanh hơn stdlib 50x.
Returns: DataFrame với 3 cột: macd, signal, histogram
"""
ema_fast = ema_pandas(series, fast)
ema_slow = ema_pandas(series, slow)
macd_line = ema_fast - ema_slow
signal_line = ema_pandas(macd_line, signal)
histogram = macd_line - signal_line
return pd.DataFrame({
'macd': macd_line,
'signal': signal_line,
'histogram': histogram
})
5. Dùng pandas_ta — 1 dòng là xong
import pandas_ta as ta
# 1 dòng duy nhất!
macd_df = ta.macd(df['close'], fast=12, slow=26, signal=9)
# Trả về DataFrame: MACD_12_26_9, MACDs_12_26_9, MACDh_12_26_9
# Đổi tên cho dễ dùng
df['MACD'] = macd_df['MACD_12_26_9']
df['Signal'] = macd_df['MACDs_12_26_9']
df['Histogram'] = macd_df['MACDh_12_26_9']
So sánh thư viện
6. Tín hiệu MACD: Crossover & Histogram
6.1 Crossover — tín hiệu chính
def macd_crossover_signal(df: pd.DataFrame) -> pd.Series:
"""
Phát hiện điểm cắt MACD:
1 = Bullish crossover (MUA)
-1 = Bearish crossover (BÁN)
0 = Không có tín hiệu mới
"""
# MACD cắt lên Signal
bullish = (df['MACD'] > df['Signal']) & (df['MACD'].shift(1) <= df['Signal'].shift(1))
# MACD cắt xuống Signal
bearish = (df['MACD'] < df['Signal']) & (df['MACD'].shift(1) >= df['Signal'].shift(1))
signal = pd.Series(0, index=df.index)
signal[bullish] = 1
signal[bearish] = -1
return signal
df['MACD_Signal'] = macd_crossover_signal(df)
# Đếm tín hiệu
print(f"Bullish cross: {(df['MACD_Signal'] == 1).sum()}")
print(f"Bearish cross: {(df['MACD_Signal'] == -1).sum()}")
6.2 Histogram — xác nhận momentum
def histogram_signal(df: pd.DataFrame) -> str:
"""Đọc tín hiệu từ histogram."""
current = df['Histogram'].iloc[-1]
previous = df['Histogram'].iloc[-2]
if current > 0 and current > previous:
return "🟢 Momentum TĂNG mạnh"
elif current > 0 and current < previous:
return "🟡 Momentum TĂNG yếu dần (cảnh báo)"
elif current < 0 and current < previous:
return "🔴 Momentum GIẢM mạnh"
elif current < 0 and current > previous:
return "🟠 Momentum GIẢM yếu dần (có thể đảo chiều)"
return "⚪ Trung tính"
7. Phân kỳ MACD (Divergence)
Phân kỳ là tín hiệu mạnh nhất của MACD:
def detect_macd_divergence(df: pd.DataFrame, lookback: int = 20) -> list:
"""
Phát hiện phân kỳ MACD trong lookback nến gần nhất.
Returns: list of {'type': 'bullish'/'bearish', 'price_idx': int, 'macd_idx': int}
"""
divergences = []
closes = df['close'].values
macd_vals = df['MACD'].values
n = len(df)
# Tìm đáy trong lookback
window = df.iloc[-lookback:]
# Bullish divergence: giá tạo đáy thấp hơn, MACD tạo đáy cao hơn
price_min_idx = window['close'].idxmin()
macd_min_idx = window['MACD'].idxmin()
if price_min_idx != macd_min_idx:
# Giá thấp hơn đáy trước nhưng MACD cao hơn
prev_low = df.iloc[:n-lookback]['close'].min()
if closes[-1] < prev_low and macd_vals[-1] > df.iloc[:n-lookback]['MACD'].min():
divergences.append({
'type': 'BULLISH DIVERGENCE',
'signal': '🟢 Sắp tăng — phân kỳ dương'
})
# Bearish divergence: giá tạo đỉnh cao hơn, MACD tạo đỉnh thấp hơn
price_max_idx = window['close'].idxmax()
macd_max_idx = window['MACD'].idxmax()
if price_max_idx != macd_max_idx:
prev_high = df.iloc[:n-lookback]['close'].max()
if closes[-1] > prev_high and macd_vals[-1] < df.iloc[:n-lookback]['MACD'].max():
divergences.append({
'type': 'BEARISH DIVERGENCE',
'signal': '🔴 Sắp giảm — phân kỳ âm'
})
return divergences
8. Kết hợp MACD + RSI + MA — bộ 3 chỉ báo
Đây là lúc kết hợp cả 3 chỉ báo đã học để có tín hiệu chất lượng cao nhất:
def triple_signal(df: pd.DataFrame) -> pd.DataFrame:
"""
Tín hiệu kết hợp MACD + RSI + MA.
Chỉ BUY khi cả 3 cùng xác nhận.
"""
df = df.copy()
# Tính cả 3 chỉ báo
df['MA20'] = df['close'].rolling(20).mean()
df['RSI14'] = ta.rsi(df['close'], length=14)
macd_df = ta.macd(df['close'])
df['MACD'] = macd_df['MACD_12_26_9']
df['Signal'] = macd_df['MACDs_12_26_9']
# Logic tổng hợp
def evaluate(row):
score = 0
reasons = []
# MA: giá > MA20 = bullish
if row['close'] > row['MA20']:
score += 1; reasons.append('MA↑')
else:
score -= 1; reasons.append('MA↓')
# RSI: > 50 = bullish
if pd.notna(row['RSI14']) and row['RSI14'] > 50:
score += 1; reasons.append('RSI↑')
elif pd.notna(row['RSI14']):
score -= 1; reasons.append('RSI↓')
# MACD: MACD > Signal = bullish
if pd.notna(row['MACD']) and row['MACD'] > row['Signal']:
score += 1; reasons.append('MACD↑')
elif pd.notna(row['MACD']):
score -= 1; reasons.append('MACD↓')
if score >= 3:
return 'STRONG BUY'
elif score >= 1:
return 'WEAK BUY'
elif score <= -3:
return 'STRONG SELL'
elif score <= -1:
return 'WEAK SELL'
return 'NEUTRAL'
df['TripleSignal'] = df.apply(evaluate, axis=1)
return df
# Thống kê
print(df['TripleSignal'].value_counts())
Kết quả: tín hiệu STRONG BUY/SELL xuất hiện ít (~10% thời gian) nhưng win rate cao hơn 60% so với dùng từng chỉ báo riêng lẻ.
9. Script Ngày 58 hoàn chỉnh
# -*- coding: utf-8 -*-
"""
Python 365 - Ngày 58: Tính chỉ báo MACD bằng Python
Lấy nến Binance → tính MACD → in tín hiệu → vẽ biểu đồ
"""
import pandas as pd
import pandas_ta as ta
import requests
import mplfinance as mpf
import numpy as np
# ========== 1. LẤY DỮ LIỆU ==========
def fetch_klines(symbol="BTCUSDT", interval="1h", limit=100):
url = "https://api.binance.com/api/v3/klines"
resp = requests.get(url, params={"symbol": symbol, "interval": interval, "limit": limit})
data = resp.json()
df = pd.DataFrame(data, columns=[
't','o','h','l','c','v','T','q','n','tb','tq','ignore'
])
for col in ['o','h','l','c','v']:
df[col] = pd.to_numeric(df[col])
df['t'] = pd.to_datetime(df['t'], unit='ms')
df.set_index('t', inplace=True)
df.rename(columns={'o':'open','h':'high','l':'low','c':'close','v':'volume'}, inplace=True)
return df[['open','high','low','close','volume']]
# ========== 2. TÍNH MACD ==========
def calc_macd(df):
macd_df = ta.macd(df['close'], fast=12, slow=26, signal=9)
df['MACD'] = macd_df['MACD_12_26_9']
df['Signal'] = macd_df['MACDs_12_26_9']
df['Histogram'] = macd_df['MACDh_12_26_9']
return df
# ========== 3. TÍN HIỆU ==========
def get_signal(df):
last = df.iloc[-1]
prev = df.iloc[-2]
macd_up = last['MACD'] > last['Signal']
crossed_up = macd_up and prev['MACD'] <= prev['Signal']
crossed_down = (not macd_up) and prev['MACD'] >= prev['Signal']
if crossed_up:
return "🟢 BULLISH CROSSOVER (MUA)"
elif crossed_down:
return "🔴 BEARISH CROSSOVER (BÁN)"
elif macd_up:
hist_trend = "tăng" if last['Histogram'] > prev['Histogram'] else "giảm"
return f"🟢 MACD > Signal (histogram đang {hist_trend})"
else:
hist_trend = "giảm" if last['Histogram'] < prev['Histogram'] else "tăng"
return f"🔴 MACD < Signal (histogram đang {hist_trend})"
# ========== 4. MAIN ==========
def main():
print("="*55)
print(" PYTHON 365 - NGÀY 58: TÍNH MACD BẰNG PYTHON")
print("="*55)
df = fetch_klines()
df = calc_macd(df)
last = df.iloc[-1]
signal_text = get_signal(df)
print(f"n💰 BTCUSDT 1h")
print(f" Giá: ${last['close']:.2f}")
print(f" MACD: {last['MACD']:.4f}")
print(f" Signal: {last['Signal']:.4f}")
print(f" Histogram: {last['Histogram']:.4f}")
print(f" → {signal_text}")
# Vẽ biểu đồ
df_plot = df[['open','high','low','close','volume']].copy()
colors = ['g' if h >= 0 else 'r' for h in df['Histogram']]
apds = [
mpf.make_addplot(df['MACD'], panel=1, color='blue', width=1.2, ylabel='MACD'),
mpf.make_addplot(df['Signal'], panel=1, color='red', width=1.2),
mpf.make_addplot(df['Histogram'], panel=1, type='bar', color=colors, alpha=0.5),
mpf.make_addplot([0]*len(df), panel=1, color='gray', linestyle='dashed', width=0.5),
]
mpf.plot(df_plot.iloc[-60:], type='candle', style='charles',
title='BTCUSDT 1H — MACD(12,26,9)', volume=False,
addplot=apds, figsize=(14,8), panel_ratios=(2,1),
savefig='python365_ngay58_macd.png')
print(f"n✅ Đã lưu biểu đồ: python365_ngay58_macd.png")
if __name__ == "__main__":
main()
Kết quả mẫu
═══════════════════════════════════════════════════
PYTHON 365 - NGÀY 58: TÍNH MACD BẰNG PYTHON
═══════════════════════════════════════════════════
💰 BTCUSDT 1h
Giá: $87,500.00
MACD: 125.4321
Signal: 98.7654
Histogram: 26.6667
→ 🟢 MACD > Signal (histogram đang tăng)
✅ Đã lưu biểu đồ: python365_ngay58_macd.png
10. FAQ
Hỏi: Tại sao dùng 12, 26, 9?
Đáp: Gerald Appel chọn 12-26-9 dựa trên dữ liệu chứng khoán Mỹ (trước đây giao dịch 6 ngày/tuần):
– 12 = 2 tuần
– 26 = 1 tháng
– 9 = 1.5 tuần
Với crypto (7 ngày/tuần), nhiều trader điều chỉnh thành 8-17-5 hoặc 12-26-9 giữ nguyên. Cả 2 đều hoạt động tốt.
Hỏi: MACD khác gì với MA crossover?
Đáp: MA crossover (Ngày 48) so sánh 2 MA của giá. MACD so sánh 2 EMA của giá rồi lại EMA lần nữa → MACD mượt hơn và ít false signal hơn, nhưng chậm hơn.
Hỏi: Histogram bằng 0 nghĩa là gì?
Đáp: MACD Line = Signal Line → 2 đường cắt nhau → điểm chuyển giao xu hướng. Đây là thời điểm QUAN TRỌNG cần theo dõi.
11. Kết luận
Ngày 58 hoàn thiện bộ 3 chỉ báo nền tảng. Bạn đã có:
Mảnh ghép Python 365 hoàn chỉnh:
Ngày 48 → MA crossover (tín hiệu)
Ngày 51 → Telegram báo giá (thông báo)
Ngày 55 → Binance đặt lệnh (thực thi)
Ngày 56 → Backtest MA (kiểm chứng)
Ngày 57 → RSI (chỉ báo #2)
Ngày 58 → MACD (chỉ báo #3) ← HÔM NAY
Với MA + RSI + MACD, bạn đã có bộ công cụ phân tích kỹ thuật đầy đủ. Có thể kết hợp cả 3 để tạo tín hiệu STRONG BUY/SELL với độ chính xác cao hơn hẳn dùng đơn lẻ.
Sắp tới: Ngày 59 — Kết hợp 3 chỉ báo + Backtest, đưa toàn bộ vào một hệ thống hoàn chỉnh!
▶ Playlist Python 365 · 🌐 Website HNDL
Bài viết trong chuỗi Python 365 ngày — hành trình 1 năm học Python từ số 0 đến bot trading tự động. Không cam kết lợi nhuận. Nội dung giáo dục.
Weekly Digest — Nhận Bản Tin Hàng Tuần
Nhận các bài viết phân tích kỹ thuật chuyên sâu, thuật toán giao dịch tự động (Trading Bot) và các giải pháp công nghệ mới nhất từ Hướng Nghiệp Dữ Liệu.
Đặng Trí Thanh
Giám đốc Công nghệ · DNT Digital · Giảng viên HNDLĐặng Trí Thanh — Founder & CTO · Hướng Nghiệp Dữ Liệu - DNT Digital. Chuyên đào tạo và triển khai thực chiến Python, MT5 và hệ thống bot auto trading / IB cho học viên và doanh nghiệp.