پیاده سازی اندیکاتور مکدی MACD در پایتون — راهنمای گام به گام

۶۵۸ بازدید
آخرین به‌روزرسانی: ۰۷ شهریور ۱۴۰۲
زمان مطالعه: ۱۱ دقیقه
پیاده سازی اندیکاتور مکدی MACD در پایتون — راهنمای گام به گام

میانگین‌های متحرک (Moving Average یا MA) به‌تنهایی ابزارهایی ساده و بسیار کاربردی هستند که در اغلب ابزارهای «تحلیل تکنیکال» (Technical Analysis) ردپایی از آن‌ها دیده می‌شود. یکی از این ابزارها، اندیکاتور همگرایی-واگرایی میانگین متحرک (MACD یا Moving Average Convergence-Divergence) یا مکدی است. در این آموزش از «مجله فرادرس»، به پیاده سازی اندیکاتور مکدی MACD در پایتون می‌پردازیم.

آشنایی با اندیکاتور مکدی (MACD)

در این اندیکاتور ابتدا با تعیین $$L_1$$ و $$L_2$$ دو میانگین متحرک نمایی (Exponential Moving Average یا EMA) با طول پنجره متفاوت بر روی قیمت محاسبه می‌شود:

$$\begin{aligned}
&E M A 1_{t}=E M A_{t}\left(\text {Close }, L_{1}\right) \\
&E M A 2_{t}=E M A_{t}\left(\text {Close }, L_{2}\right)
\end{aligned}$$

توجه داشته باشید که $$L_1$$ همواره باید کوچک‌تر از $$L_2$$ باشد. سپس اختلاف بین این دو میانگین متحرک به شکل زیر محاسبه می‌شود و مقدار حاصل MACD نامیده می‌شود:

$$M A C D_{t}=E M A 1_{t}-E M A 2_{t} $$

به این ترتیب، مقدار MACD فاصله و موقعیت این دو میانگین متحرک نسبت به هم را نشان خواهد داد. تا به اینجا ابزار ایجادشده می‌تواند کاربردی باشد، اما یک بخش جدید نیز به آن اضافه می‌شود تا سیگنال‌های بهتری تولید کند.

در این مرحله با تعیین یک $$L_s$$ که طول میانگین متحرک سیگنال است، از خط MACD یک میانگین متحرک نمایی گرفته می‌شود:

$${Signal}_{t}=E M A_{t}\left(M A C D, L_{s}\right)$$

بنابراین، می‌توانیم حدس بزنیم که حرکت خط Signal کندتر از MACD خواهد بود. اختلاف این دو خط را نیز به عنوان معیار جدیدی به نام هیستوگرام (Histogram) نشان می‌دهیم:

$$ { Histogram }_{t}=M A C D_{t}- { Signal }_{t} $$

 به این ترتیب، در خروجی سه خط با نام‌های MACD و Signal و Histogram خواهیم داشت.

برای $$L_1$$ و $$L_2$$ و $$L_s$$ معمولاً، به‌ترتیب، از اعداد ۱۲ و ۲۶ و ۹ استفاده می‌شود که تنظیمات مشهوری بوده و نتایج خوبی را ایجاد می‌کند.

اندیکاتور MACD سیگنال‌های متنوعی می‌تواند ایجاد کند که هرکدام در شرایطی از اعتبار بالایی برخوردار هستند. برای آشنایی بیشتر با اندیکاتور MACD می‌توانید به مطلب «اندیکاتور MACD چیست؟ آموزش تصویری و به زبان ساده» مراجعه کنید.

دریافت و رسم مجموعه داده

حال وارد محیط برنامه‌نویسی می‌شویم و کتابخانه‌های مورد نیاز را فراخوانی می‌کنیم:

1import numpy as np
2import pandas as pd
3import yfinance as yf
4import matplotlib.pyplot as plt

این 4 کتابخانه به ترتیب برای موارد زیر کاربرد دارند:

  1. کار با آرایه (Array) و محاسبات برداری (Vectorized Computation)
  2. کار با دیتافریم‌ها (Data Frame)
  3. دریافت داده از طریق API مربوط به Yahoo Finance
  4. رسم نمودار قیمت و اندیکاتور

حال تنظیمات زیر را برای نمودارها اعمال می‌کنیم تا ظاهر نمودارها مناسب باشد:

1plt.style.use('ggplot')

حال می‌توانیم مجموعه داده مربوط به شاخص بورس نزدک یا NASDAQ را دریافت کنیم. به این منظور از تابع yfinance.download استفاده می‌کنیم:

1DF = yf.download('^IXIC', start='2018-01-01', end='2022-01-01')

نماد مربوط به شاخص نزدک در Yahoo Finance به شکل IXIC^ است که برای یافتن آن‌ها می‌توان به سایت Yahoo Finance مراجعه کرد. حال برای بررسی مجموعه داده دریافتی، می‌توانیم از دو متد head و tail استفاده کنیم:

1print(DF.head())
2
3print(DF.tail())

که در خروجی خواهیم داشت:

                   Open         High          Low        Close    Adj Close      Volume
Date
2018-01-02  6937.649902  7006.910156  6924.080078  7006.899902  7006.899902  1914930000
2018-01-03  7017.069824  7069.149902  7016.700195  7065.529785  7065.529785  2166780000
2018-01-04  7089.500000  7098.049805  7072.379883  7077.910156  7077.910156  2098890000
2018-01-05  7105.740234  7137.040039  7097.080078  7136.560059  7136.560059  2020900000
2018-01-08  7135.379883  7161.350098  7124.089844  7157.390137  7157.390137  2051430000
                    Open          High           Low         Close     Adj Close      Volume
Date
2021-12-27  15696.830078  15871.400391  15696.830078  15871.259766  15871.259766  3730120000
2021-12-28  15895.200195  15901.469727  15757.070312  15781.719727  15781.719727  3623600000
2021-12-29  15794.919922  15821.809570  15679.849609  15766.219727  15766.219727  3694500000
2021-12-30  15758.980469  15868.089844  15729.160156  15741.559570  15741.559570  3732730000
2021-12-31  15722.910156  15777.429688  15643.940430  15644.969727  15644.969727  3379850000

به این ترتیب، می‌توان تا حدود زیادی از درستی مجموعه داده اطمینان یافت. حال می‌توانیم نمودار مربوط به ستون Close را نیز رسم و از روند شاخص مطلع شویم. برای این منظور، می‌توان هر دو روش زیر را در پیش گرفت و به نمودار رسید:

  1. رسم ستون مربوط به Close با استفاده از تاریخ
  2. رسم ستون مربوط به Close پس از تبدیل به آرایه با استفاده از شماره داده

برای مورد اول، می‌توانیم به شکل زیر عمل کنیم:

1plt.semilogy(DF['Close'], ls='-', lw=0.9, c='k')
2plt.title('NASDAQ Composite')
3plt.xlabel('Date')
4plt.ylabel('Value')
5plt.show()

در این شرایط مجموعه داده را به آرایه Numpy تبدیل نمی‌کنیم و مقادیر محور افقی نیز تاریخ روزها است. در خروجی نمودار زیر حاصل می‌شود.

مکدی در پایتون

به این ترتیب، نمودار مورد نظر حاصل می‌شود. حال برای حالت دوم از رسم نمودار، ابتدا مقادیر ستون Close را به شکل آرایه Numpy دریافت می‌کنیم:

1C = DF['Close'].to_numpy()

حال یک آرایه دیگر به‌عنوان شماره روزها ایجاد می‌کنیم. بدین منظور، تابع numpy.arange مناسب است:

1T = np.arange(start=1, stop=C.size + 1, step=1)

حال می‌توانیم رسم نمودار را دوباره تکرار کنیم:

1plt.semilogy(T, C, ls='-', lw=0.9, c='k')
2plt.title('NASDAQ Composite')
3plt.xlabel('Time (Day)')
4plt.ylabel('Value')
5plt.show()

در این حالت نیز نمودار به‌شکل زیر حاصل خواهد شد.

پیاده سازی اندیکاتور MACD

در نهایت، مشاهده می‌کنیم که مقادیر محور عمودی ثابت هستند، اما در محور افقی، به‌جای تاریخ شاهد شماره روزها هستیم.

بنابراین، روند کلی نماد و صحت مقادیر آن قابل مشاهده است. حال می‌توانیم اندیکاتور MACD را پیاده‌سازی کنیم.

برای یادگیری برنامه‌نویسی با زبان پایتون، پیشنهاد می‌کنیم به مجموعه آموزش‌های مقدماتی تا پیشرفته پایتون فرادرس مراجعه کنید که لینک آن در ادامه آورده شده است.

  • برای مشاهده مجموعه آموزش‌های برنامه نویسی پایتون (Python) — مقدماتی تا پیشرفته + اینجا کلیک کنید.

پیاده‌سازی اندیکاتور MACD با استفاده از Numpy

با توجه به اینکه در روند محاسبه اندیکاتور، از اندیکاتور EMA نیز استفاده می‌کنیم، باید آن را نیز وارد کد کنیم. تابع مربوط به EMA را به‌صورت زیر تعریف می‌کنیم:

1def EMA(S:np.ndarray, L:int, r:float=1):
2    a = (1 + r) / (L + r)
3    nD0 = S.size
4    nD = nD0 - L + 1
5    M = np.zeros(nD)
6    M[0] = np.mean(S[:L])
7    for i in range(1, nD):
8        M[i] = a * S[i + L - 1] + (1 - a) * M[i - 1]
9    return M

برای آشنایی با میانگین متحرک نمایی و روش پیاده‌سازی آن می‌توانید به مطلب «پیاده سازی میانگین متحرک نمایی در پایتون – راهنمای گام به گام» مراجعه کنید.

حال می‌توانیم یک تابع برای MACD ایجاد کنیم که در ورودی آرایه مربوط به Closeها، و مقادیر $$L_1$$ و $$L_2$$ و $$L_s$$  را دریافت کند:

1def MACD(C:np.ndarray, L1:int, L2:int, Ls:int):

حال در اولین مرحله، میانگین‌های متحرک نمایی را محاسبه می‌کنیم:

1def MACD(C:np.ndarray, L1:int, L2:int, Ls:int):
2    ema1 = EMA(C, L1)
3    ema2 = EMA(C, L2)

با توجه به اینکه برای محاسبه خط MACD نیاز داریم تا اختلاف بین این دو مقدار را محاسبه کنیم، باید طول دو آرایه ema1 و ema2 برابر باشد که نیست. برای برقراری این شرط، به‌شکل زیر تعدادی از اعضای ابتدای ema1 را حذف می‌کنیم تا هر دو هم‌اندازه شوند:

1def MACD(C:np.ndarray, L1:int, L2:int, Ls:int):
2    ema1 = EMA(C, L1)
3    ema2 = EMA(C, L2)
4    ema1 = ema1[-ema2.size:]

حال می‌توانیم خط MACD را محاسبه کنیم:

1def MACD(C:np.ndarray, L1:int, L2:int, Ls:int):
2    ema1 = EMA(C, L1)
3    ema2 = EMA(C, L2)
4    ema1 = ema1[-ema2.size:]
5    macd = ema1 - ema2

در این مرحله، باید با اعمال EMA روی خط MACD، خط Signal را به‌دست آوریم:

1def MACD(C:np.ndarray, L1:int, L2:int, Ls:int):
2    ema1 = EMA(C, L1)
3    ema2 = EMA(C, L2)
4    ema1 = ema1[-ema2.size:]
5    macd = ema1 - ema2
6    signal = EMA(macd, Ls)

به این ترتیب، این دو خط حاصل می‌شوند. با توجه به اینکه طول دو آرایه macd و signal با یکدیگر برابر نیست، باید بار دیگر چند عضو ابتدای macd را حذف کنیم تا خط Histogram قابل محاسبه باشد:

1def MACD(C:np.ndarray, L1:int, L2:int, Ls:int):
2    ema1 = EMA(C, L1)
3    ema2 = EMA(C, L2)
4    ema1 = ema1[-ema2.size:]
5    macd = ema1 - ema2
6    signal = EMA(macd, Ls)
7    macd = macd[-signal.size:]

حال محاسبه آرایه histogram امکان‌پذیر خواهد بود:

1def MACD(C:np.ndarray, L1:int, L2:int, Ls:int):
2    ema1 = EMA(C, L1)
3    ema2 = EMA(C, L2)
4    ema1 = ema1[-ema2.size:]
5    macd = ema1 - ema2
6    signal = EMA(macd, Ls)
7    macd = macd[-signal.size:]
8    histogram = macd - signal

در انتهای تابع نیز موارد مورد نیاز را برمی‌گردانیم:

1def MACD(C:np.ndarray, L1:int, L2:int, Ls:int):
2    ema1 = EMA(C, L1)
3    ema2 = EMA(C, L2)
4    ema1 = ema1[-ema2.size:]
5    macd = ema1 - ema2
6    signal = EMA(macd, Ls)
7    macd = macd[-signal.size:]
8    histogram = macd - signal
9    return ema1, ema2, macd, signal, histogram

در نتیجه، این تابع کامل خواهد بود. برای استفاده از تابع، به‌شکل زیر آن را فراخوانی می‌کنیم:

1ema1, ema2, macd, signal, histogram = MACD(C, 12, 26, 9)

حال می‌توانیم با استفاده از matplotlib.pyplot.subplot دو نمودار مربوط به مقدار شاخص و اندیکاتور را در زیر هم رسم کنیم:

1plt.subplot(3, 1, (1, 2))
2plt.semilogy(T, C, ls='-', lw=0.8, c='k', label='Close')
3plt.semilogy(T[-ema1.size:], ema1, ls='-', lw=0.9, c='teal', label='EMA 1')
4plt.semilogy(T[-ema2.size:], ema2, ls='-', lw=1.0, c='crimson', label='EMA 2')
5plt.title('NASDAQ Composite')
6plt.ylabel('Value')
7plt.xlim(left=0, right=C.size + 1)
8plt.tight_layout()
9plt.legend()
10
11plt.subplot(3, 1, 3)
12plt.plot(T[-macd.size:], macd, ls='-', lw=0.8, c='k', label='MACD Line')
13plt.plot(T[-signal.size:], signal, ls='-', lw=0.8, c='crimson', label='Signal Line')
14plt.bar(T[-signal.size:], histogram, color='grey', label='Histogram')
15plt.title('Moving Average Convergence Divergence (MACD)')
16plt.xlabel('Time (Day)')
17plt.ylabel('Value')
18plt.xlim(left=0, right=C.size + 1)
19plt.tight_layout()
20plt.legend()
21
22plt.show()

به این ترتیب، صفحه به سه سطر و یک ستون تقسیم خواهد شد و دو بخش بالایی برای رسم مقدار شاخص و میانگین‌های متحرک اختصاص خواهد یافت. یک بخش پایینی نیز برای رسم سه خط اندیکاتور استفاده خواهد شد. پس از اجرای این بخش از کد، نمودار زیر را خواهیم داشت.

اندیکاتور مکدی در python

بنابراین، مشاهده می‌کنیم که تمامی موارد مورد نیاز رسم می‌شود. با توجه به اینکه نمودار هیستوگرام به شکل یک رنگ رسم شده است، ممکن است مناسب نباشد. از طرفی بین ستون‌ها نیز فاصله افتاده است. برای رفع این مشکل، کد را به‌شکل زیر تغییر می‌دهیم:

1plt.subplot(3, 1, (1, 2))
2plt.semilogy(T, C, ls='-', lw=0.9, c='k', label='Close')
3plt.semilogy(T[-ema1.size:], ema1, ls='-', lw=0.9, c='teal', label='EMA 1')
4plt.semilogy(T[-ema2.size:], ema2, ls='-', lw=1.0, c='crimson', label='EMA 2')
5plt.title('NASDAQ Composite')
6plt.ylabel('Value')
7plt.xlim(left=0, right=C.size + 1)
8plt.tight_layout()
9plt.legend()
10
11plt.subplot(3, 1, 3)
12plt.plot(T[-macd.size:], macd, ls='-', lw=0.8, c='k', label='MACD Line')
13plt.plot(T[-signal.size:], signal, ls='-', lw=0.8, c='crimson', label='Signal Line')
14plt.bar(T[-signal.size:][histogram >= 0], histogram[histogram >= 0], color='b', width=1, label='Positive Histogram')
15plt.bar(T[-signal.size:][histogram < 0], histogram[histogram < 0], color='r', width=1, label='Negative Histogram')
16plt.title('Moving Average Convergence Divergence (MACD)')
17plt.xlabel('Time (Day)')
18plt.ylabel('Value')
19plt.xlim(left=0, right=C.size + 1)
20plt.tight_layout()
21plt.legend()
22
23plt.show()

برای جدا کردن مقادیر مثبت و منفی هیستوگرام، از maskهای موجود در کتابخانه Numpy استفاده می‌کنیم. مشکل جدایی ستون‌ها از هم نیز با تعیین width=1 قابل رفع است. حال اگر رسم نمودار را تکرار کنیم، شکل زیر را خواهیم داشت.

اندیکاتور مکدی در پایتون

به این ترتیب، مصورسازی نمودار بهبود می‌یابد و اطلاعات به نحو بهتری منتقل می‌شود.

نکته مهم دیگری که در رابطه با این اندیکاتور وجود دارد، شیوه نگرش به اختلاف دو میانگین متحرک است. به دلیل اینکه خط MACD از اختلاف دو میانگین متحرک حاصل می‌شود، دارای روند خواهد بود و شدت نوسان آن وابسته به مقیاس قیمت خواهد بود. این اتفاق می‌تواند باعث ضعیف یا قوی شدن برخی سیگنال‌ها شود که مناسب نیست. برای رفع این مشکل، می‌توان محاسبه MACD را به‌شکل زیر انجام داد:

$$ M A C D_{t} =\frac{E M A 1_{t}-E M A 2_{t}}{E M A 2_{t}}=\frac{E M A 1_{t}}{E M A 2_{t}}-1 $$

بنابراین، مقیاس قیمت در مقدار MACD بی‌تأثیر خواهد بود. می‌توان از لگاریتم نسبت دو میانگین متحرک نیز استفاده کرد که رفتار بهتری دارد:

$$ M A C D_{t} =\log \left(\frac{E M A 1_{t}}{E M A 2_{t}}\right)
$$

حال حالت دوم را می‌توانیم در اندیکاتور اعمال کنیم که خواهیم داشت:

1def MACD(C:np.ndarray, L1:int, L2:int, Ls:int):
2    ema1 = EMA(C, L1)
3    ema2 = EMA(C, L2)
4    ema1 = ema1[-ema2.size:]
5    macd = np.log(ema1 / ema2)
6    signal = EMA(macd, Ls)
7    macd = macd[-signal.size:]
8    histogram = macd - signal
9    return ema1, ema2, macd, signal, histogram

در این شرایط، اگر نمودار را تکرار کنیم، شکل زیر را خواهیم داشت.

اندیکاتور مکدی

به این ترتیب، مشاهده می‌کنیم که تغییرات اندکی در ظاهر اندیکاتور مشاهده می‌شود. توجه داشته باشید که نتایج حاصل از این حالت، قابل تعمیم به سایر نمادهای نیز هست. از طرفی حذف روند از اندیکاتورها، در اغلب شرایط به نفع ما است.

پیاده‌سازی اندیکاتور MACD با استفاده از Pandas

حال می‌توانیم به پیاده‌سازی اندیکاتور MACD با استفاده از امکانات کتابخانه Pandas بپردازیم. در این حالت نیز یک تابع ایجاد می‌کنیم و در ورودی دیتافریم را به همراه سه عدد $$L_1$$ و $$L_2$$ و $$L_s$$ دریافت می‌کنیم:

1def MACD(DF:pd.core.frame.DataFrame, L1:int, L2:int, Ls:int):

حال باید میانگین‌های متحرک نمایی را محاسبه کنیم. به این منظور، می‌توانیم از متد ewm استفاده کنیم و روی آن عمل میانگین‌‌گیری را انجام دهیم:

1def MACD(DF:pd.core.frame.DataFrame, L1:int, L2:int, Ls:int):
2    DF['EMA1'] = DF['Close'].ewm(span=L1).mean()
3    DF['EMA2'] = DF['Close'].ewm(span=L2).mean()

به این ترتیب، دو ستون جدید ایجاد شده و مقادیر میانگین‌های متحرک را در خود ذخیره خواهند کرد. در گام بعدی، اختلاف این دو ستون را محاسبه و به‌عنوان ستون MACD اضافه می‌کنیم:

1def MACD(DF:pd.core.frame.DataFrame, L1:int, L2:int, Ls:int):
2    DF['EMA1'] = DF['Close'].ewm(span=L1).mean()
3    DF['EMA2'] = DF['Close'].ewm(span=L2).mean()
4    DF['MACD'] = DF['EMA1'] - DF['EMA2']

حال ستون Signal نیز با استفاده از ewm قابل محاسبه خواهد بود:

1def MACD(DF:pd.core.frame.DataFrame, L1:int, L2:int, Ls:int):
2    DF['EMA1'] = DF['Close'].ewm(span=L1).mean()
3    DF['EMA2'] = DF['Close'].ewm(span=L2).mean()
4    DF['MACD'] = DF['EMA1'] - DF['EMA2']
5    DF['Signal'] = DF['MACD'].ewm(span=Ls).mean()

برای محاسبه خط Histogram نیز تفاضل دو خط قبلی را محاسبه می‌کنیم:

1def MACD(DF:pd.core.frame.DataFrame, L1:int, L2:int, Ls:int):
2    DF['EMA1'] = DF['Close'].ewm(span=L1).mean()
3    DF['EMA2'] = DF['Close'].ewm(span=L2).mean()
4    DF['MACD'] = DF['EMA1'] - DF['EMA2']
5    DF['Signal'] = DF['MACD'].ewm(span=Ls).mean()
6    DF['Histogram'] = DF['MACD'] - DF['Signal']

به این ترتیب، تمامی اجزا محاسبه می‌شود. نکته مهمی که در رابطه با این تابع وجود دارد، احتمال محاسبه چندین باره اندیکاتور است. برای مثال، ممکن است با چندین تنظیمات مختلف اندیکاتور محاسبه شود. در این شرایط، نتایج هر بار اجرا روی نتایج اجرای قبلی نوشته و مشکلاتی ایجاد خواهد شد. برای رفع این مشکل نام ستون‌ها را بر اساس پارامترهای ورودی تعیین می‌کنیم:

1def MACD(DF:pd.core.frame.DataFrame, L1:int, L2:int, Ls:int):
2    DF[f'EMA({L1})'] = DF['Close'].ewm(span=L1).mean()
3    DF[f'EMA({L2})'] = DF['Close'].ewm(span=L2).mean()
4    DF[f'MACD({L1}, {L2})'] = DF[f'EMA({L1})'] - DF[f'EMA({L2})']
5    DF[f'Signal({L1}, {L2}, {Ls})'] = DF[f'MACD({L1}, {L2})'].ewm(span=Ls).mean()
6    DF[f'Histogram({L1}, {L2}, {Ls})'] = DF[f'MACD({L1}, {L2})'] - DF[f'Signal({L1}, {L2}, {Ls})']

در نتیجه، نام هر ستون برگرفته از پارامترهای مورد استفاده در محاسبه آن خواهد بود. توجه داشته باشید که خط MACD تنها از مقادیر $$L_1$$ و $$L_2$$ تأثیر می‌پذیرد و ذکر مقدار $$L_s$$ در نام آن بیهوده خواهد بود. حال تابع را فراخوانی می‌کنیم:

1MACD(DF, 12, 26, 9)

پس از اجرای کد فوق، می‌توانیم ستون‌های موجود در دیتافریم را با کد زیر بررسی کنیم:

1print(DF.columns.to_list())

که خواهیم داشت:

['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume', 'EMA(12)', 'EMA(26)', 'MACD(12, 26)', 'Signal(12, 26, 9)', 'Histogram(12, 26, 9)']

به این ترتیب، مشاهده می‌کنیم که تمامی موارد مورد نیاز به درستی اضافه شده‌اند. حال برای رسم نمودار از کد زیر استفاده می‌کنیم:

1plt.subplot(3, 1, (1, 2))
2plt.semilogy(DF['Close'], ls='-', lw=0.9, c='k', label='Close')
3plt.semilogy(DF['EMA(12)'], ls='-', lw=0.9, c='teal', label='EMA(12)')
4plt.semilogy(DF['EMA(26)'], ls='-', lw=1.0, c='crimson', label='EMA(26)')
5plt.title('NASDAQ Composite')
6plt.ylabel('Value')
7plt.tight_layout()
8plt.legend()
9
10plt.subplot(3, 1, 3)
11plt.plot(DF['MACD(12, 26)'], ls='-', lw=0.8, c='k', label='MACD(12, 26)')
12plt.plot(DF['Signal(12, 26, 9)'], ls='-', lw=0.8, c='crimson', label='Signal(12, 26, 9)')
13plt.bar(DF.index[DF['Histogram(12, 26, 9)'] >= 0], DF['Histogram(12, 26, 9)'][DF['Histogram(12, 26, 9)'] >= 0], color='b', width=1, label='+Histogram(12, 26, 9)')
14plt.bar(DF.index[DF['Histogram(12, 26, 9)'] < 0], DF['Histogram(12, 26, 9)'][DF['Histogram(12, 26, 9)'] < 0], color='r', width=1, label='-Histogram(12, 26, 9)')
15plt.title('Moving Average Convergence Divergence (MACD)')
16plt.xlabel('Time (Day)')
17plt.ylabel('Value')
18plt.tight_layout()
19plt.legend()
20
21plt.show()

توجه داشته باشید که به‌دلیل استفاده از ستون‌های دیتافریم برای رسم نمودار، نیازی به تعریف آرایه زمان نیست و به‌صورت خودکار از Index دیتافریم استفاده می‌شود. نکته مهم دیگری که وجود دارد، روش تشخیص روزهای با هیستوگرام مثبت و منفی است. می‌توان با استفاده از Mask این کد را به‌شکل زیر ساده‌تر کرد:

1plt.subplot(3, 1, (1, 2))
2plt.semilogy(DF['Close'], ls='-', lw=0.9, c='k', label='Close')
3plt.semilogy(DF['EMA(12)'], ls='-', lw=0.9, c='teal', label='EMA(12)')
4plt.semilogy(DF['EMA(26)'], ls='-', lw=1.0, c='crimson', label='EMA(26)')
5plt.title('NASDAQ Composite')
6plt.ylabel('Value')
7plt.tight_layout()
8plt.legend()
9
10plt.subplot(3, 1, 3)
11m = DF['Histogram(12, 26, 9)'] >= 0
12plt.plot(DF['MACD(12, 26)'], ls='-', lw=0.8, c='k', label='MACD(12, 26)')
13plt.plot(DF['Signal(12, 26, 9)'], ls='-', lw=0.8, c='crimson', label='Signal(12, 26, 9)')
14plt.bar(DF.index[m], DF['Histogram(12, 26, 9)'][m], color='b', width=1, label='+Histogram(12, 26, 9)')
15plt.bar(DF.index[~m], DF['Histogram(12, 26, 9)'][~m], color='r', width=1, label='-Histogram(12, 26, 9)')
16plt.title('Moving Average Convergence Divergence (MACD)')
17plt.xlabel('Time (Day)')
18plt.ylabel('Value')
19plt.tight_layout()
20plt.legend()
21
22plt.show()

به این ترتیب، کد هم بهینه شده و هم ساده‌تر می‌شود. پس از اجرای کد، تصویر زیر را خواهیم داشت.

نمودار مکدی

به این ترتیب، نمودار مورد نظر حاصل می‌شود. توجه داشته باشید که برای روزهای تعطیل، مقداری وجود ندارد و نمودار خالی خواهد بود.

برای حذف روند، می‌توان تابع را به‌صورت زیر تغییر داد:

1def MACD(DF:pd.core.frame.DataFrame, L1:int, L2:int, Ls:int):
2    DF[f'EMA({L1})'] = DF['Close'].ewm(span=L1).mean()
3    DF[f'EMA({L2})'] = DF['Close'].ewm(span=L2).mean()
4    DF[f'MACD({L1}, {L2})'] = np.log(DF[f'EMA({L1})'] / DF[f'EMA({L2})'])
5    DF[f'Signal({L1}, {L2}, {Ls})'] = DF[f'MACD({L1}, {L2})'].ewm(span=Ls).mean()
6    DF[f'Histogram({L1}, {L2}, {Ls})'] = DF[f'MACD({L1}, {L2})'] - DF[f'Signal({L1}, {L2}, {Ls})']

در نتیجه، از نسبت لگاریتم دو میانگین متحرک استفاده خواهد شد.

محاسبه چندین MACD و رسم آن‌ها

با توجه به اینکه ممکن است برای تحلیل وضعیت نمادها یا انجام معاملات الگوریتمی نیاز به چندین MACD با تنظیمات مختلف داشته باشیم، می‌توانیم به‌صورت زیر چندین MACD را محاسبه کنیم:

1MACD(DF, 12, 26, 9)
2MACD(DF, 18, 39, 14)

پس از اجرای کد فوق، ستون‌های زیر در دیتافریم موجود خواهد بود:

['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume', 'EMA(12)', 'EMA(26)', 'MACD(12, 26)', 'Signal(12, 26, 9)', 'Histogram(12, 26, 9)', 'EMA(18)', 'EMA(39)', 'MACD(18, 39)', 'Signal(18, 39, 14)', 'Histogram(18, 39, 14)']

بنابراین، مشاهده می‌کنیم که 10 ستون اخیر، حاصل فراخوانی توابع MACD با دو تنظیمات متفاوت هستند. حال می‌توانیم برای مقایسه، دو خط Histogram را در کنار هم رسم کنیم. به این منظور، می‌توان کد زیر را استفاده کرد:

1plt.subplot(3, 1, (1, 2))
2plt.semilogy(DF['Close'], ls='-', lw=0.9, c='k')
3plt.title('NASDAQ Composite')
4plt.ylabel('Value')
5plt.tight_layout()
6
7plt.subplot(3, 1, 3)
8plt.plot(DF['Histogram(12, 26, 9)'], ls='-', lw=0.8, c='crimson', label='Histogram(12, 26, 9)')
9plt.plot(DF['Histogram(18, 39, 14)'], ls='-', lw=0.8, c='teal', label='Histogram(18, 39, 14)')
10plt.title('Moving Average Convergence Divergence (MACD)')
11plt.xlabel('Time (Day)')
12plt.ylabel('Value')
13plt.tight_layout()
14plt.legend()
15
16plt.show()

پس از اجرای کد فوق، شکل زیر را خواهیم داشت.

پیاده سازی MACD در پایتون

در نهایت، مشاهده می‌کنیم که بین دو خط همبستگی بالایی وجود دارد، اما تفاوت‌هایی نشان داده می‌شود که می‌تواند برای مقاصد مختلف استفاده شود. به این ترتیب، می‌توانیم با طراحی درست تابع، از آن به‌دفعات مختلف استفاده کنیم.

جمع‌بندی

در این آموزش، پیاده‌سازی اندیکاتور MACD را انجام دادیم. برای مطالعه بیشتر، می‌توان موارد زیر را بررسی کرد:

  1. چرا تنظیمات 12 و 26 و 9 عملکرد خوبی دارد؟
  2. چرا در اندیکاتور MACD از SMA استفاده نمی‌شود؟
  3. بررسی کنید از این اندیکاتور چه سیگنال‌های می‌شود گرفت؟
  4. در MACD برخورد دو میانگین متحرک با یکدیگر چگونه نشان داده می‌شود؟
  5. با مراجعه به مطالب نوشته‌شده در رابطه با ربات‌های معامله‌گر، یک ربات براساس اندیکاتور MACD ایجاد کرده و آن را آموزش دهید.
  6. خط Signal، چه ارتباطی با سطح بین نمودار دو میانگین متحرک دارد؟
  7. در بخشی از کد، از Maskها استفاده شد. در مورد Maskها تحقیق کنید و کار عملگر Tild یا ~ را برای آن بیابید.
بر اساس رای ۹ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
مجله فرادرس
۳ دیدگاه برای «پیاده سازی اندیکاتور مکدی MACD در پایتون — راهنمای گام به گام»

سلام
مطالبتون حرف نداره عالیه
جسارتا برای این مقاله هایی که منتشر کردید منبعی دارید یا خودتون پیاده سازی کردید؟؟
میخوام برای پروژه دانشگاهی از مطالبتون استفاده کنم و برای قبول کردن کارم ازم منبع معتبر میخوان
با تشکر

با سلام؛

منابع تمامی مطالب مجله فرادرس، اگر ترجمه باشند در انتهای متن و پیش از نام نویسنده آورده شده‌اند. برای استفاده از مطالب مجله فرادرس می‌توانید به شرایط استفاده در انتهای صفحه یا این لینک مراجعه کنید.

با تشکر از همراهی شما با مجله فرادرس

بی نهایت ممنون / اموزش تخصصی و جالب بود

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *