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

تصور کنید ایدهای برای معاملهگری دارید: “وقتی میانگین متحرک 20 روزه از میانگین 50 روزه عبور کرد، بخر و وقتی شرایط برعکس شد، بفروش.” ساده به نظر میرسد، اما آیا واقعاً در گذشته جواب داده است؟ آیا با درنظرگرفتن کارمزدها هم سودده است؟ پاسخ این سوالات را نه با حدس و گمان، بلکه با بکتستگیری علمی (Backtesting) میتوان یافت. همان طور که در مقالات “بک تستینگ: بک تست چیست ؟ سنگ محک استراتژیهای معاملاتی شما” و “بک تست گیری در برنامه متاتریدر و ترید برد: یک مقایسه جامع” اشاره کردیم، بکتست گیری یکی از مهمترین ابزار برای هر معامله گر است. اما در بیشتر اوقات، بکتست گرفتن از یک استراتژی ساده در چند بازار یا نماد مختلف به قدری وقت گیر است که باعث میشود انجام آن برای همه امکان پذیر نباشد. در این مقاله، شما را گامبهگام از پیاده سازی یک ایده ساده به کم کد پایتون، به یک استراتژی تستشده خواهیم رساند. شما پس از مطالعه این مقاله قادر بود تقریبا هر نوع استراتژی اندیکاتوری را در قالب کُد پایتونی تعریف کنید و در عرض چند دقیقه و کمتر، آن را در هر بازار و نماد دلخواه بکتست گیری کنید. با یادگیری این مهارت، شما از این پس قادر خواهید بود تعداد بسیار زیادی استراتژی مختلف را در مدت کوتاهی بکتست گیری کنید. شما همچنین میتوانید استراتژیهای خودتان را برای نماد یا بازار خاصی بهینه سازی کنید. برای آشنایی با نحوه بهینه سازی یک استراتژی، به مقاله بعدی ما با عنوان “چگونه یک استراتژی معاملاتی را به کمک کد پایتونی بهینه سازی کنیم” رجوع کنید. به عنوان پیش نیاز این مهارت، شما میبایست آشنایی مقدماتی با زبان برنامه نویسی پایتون داشته باشید.
ما در این مسیر چهار مرحله اصلی را طی میکنیم:
در پایان، شما یک فایل پایتون عملیاتی خواهید داشت که میتواند استراتژی نهایی شما را روی هر سهمی تست کند و گزارش جامعی ارائه دهد.
قبل از هر چیز، باید ابزارهایمان را آماده کنیم. پایتون به دلیل کتابخانههای غنی و جامعه فعال، بهترین انتخاب برای این کار است.
پایتون به تنهایی کافی نیست. نیاز به کتابخانههایی داریم که عملیات سنگین مالی و تحلیلی را برای ما ساده کنند.
بیایید مطمئن شویم همه چیز درست نصب شده است. کد زیر را در یک فایل پایتون (مثلا test_install.py) ذخیره و اجرا کنید.
import backtesting
import pandas as pd
import numpy as np
import yfinance as yf
import matplotlib
print("✅ تمام کتابخانهها با موفقیت import شدند!")
print("ورژن backtesting.py:", backtesting.__version__)
print("ورژن pandas:", pd.__version__)
نکته مهم:گاهی اوقات استفاده از کتابخانه yfinance برای ایرانیان با محدودیت مواجه میشود و دانلود داده ها از این روش امکان پذیر نیست. شما میتوانید برای دریافت داده های تاریخی به کانالهای تلگرام و یا ایتای ما مراجعه کنید. نمونه هایی از داده های تاریخی در فرمت فایل های CSV برای استفاده شما در این کانالها قرار داده شده است. بنابراین، درصورتی که فایلها را از کانال دریافت کرده اید میتوانید از این بخش عبور کنید.
داده تاریخی، سوخت موتور بکتست ماست. بدون داده دقیق و کامل، هر نتیجهای بیمعنا خواهد بود.
کتابخانه yfinance دسترسی سادهای به دادههای دهها سال گذشته هزاران سهم و ETF فراهم میکند.
# فایل: download_data.py
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
# 1. تنظیم پارامترهای دانلود
SYMBOL = "BTC-USD" # جفت ارز بیتکوین به دلار (میتوانید به جفت ارزهای دیگر و نمادهای بازار سهام امریکا TSLA, GOOGL و... تغییر دهید)
START_DATE = "2020-01-01"
END_DATE = "2025-12-01"
INTERVAL = "1d" # داده روزانه. گزینههای دیگر: '1h', '1wk', '1mo'
print(f"در حال دانلود دادههای {SYMBOL} از {START_DATE} تا {END_DATE}...")
# 2. دانلود داده
df = yf.download(
tickers=SYMBOL,
start=START_DATE,
end=END_DATE,
interval=INTERVAL,
progress=False # نوار پیشرفت را نشان نده
)
# 3. بررسی و نمایش اطلاعات اولیه داده
print(f"\n✅ دانلود تکمیل شد. {len(df)} ردیف داده دریافت شد.")
print("📅 بازه زمانی:", df.index[0].strftime('%Y-%m-%d'), "تا", df.index[-1].strftime('%Y-%m-%d'))
print("\nستونهای موجود:")
for col in df.columns:
print(f" - {col}")
print("\nنمونهای از دادهها (5 ردیف اول):")
print(df.head())
# 4. ذخیره داده در فایل CSV برای استفادههای بعدی
csv_filename = f"{SYMBOL}_historical.csv"
df.to_csv(csv_filename)
print(f"\n💾 دادهها در فایل '{csv_filename}' ذخیره شدند.")
# 5. رسم نمودار سریع قیمت بستهشدن
plt.figure(figsize=(14, 6))
plt.plot(df.index, df['Close'], label='Close Price', color='navy', linewidth=1.5)
plt.title(f'Price history {SYMBOL}', fontsize=14, fontweight='bold')
plt.xlabel('Date')
plt.ylabel('Price (USD)')
plt.grid(True, alpha=0.3)
plt.legend()
plt.tight_layout()
plt.savefig(f'{SYMBOL}_price_chart.png', dpi=150)
print(f"📈 نمودار قیمت در فایل '{SYMBOL}_price_chart.png' ذخیره شد.")
plt.show()
دادههای دانلود شده یک DataFrame پانداس با ستونهای زیر است:
برای بکتست معمولاً از Adj Close استفاده میکنیم تا تحلیل واقعبینانهتری داشته باشیم.
حالا نوبت زنده کردن ایده معاملاتی ماست. ما از کتابخانه backtesting.py استفاده میکنیم که ساختار منظم و کارآمدی برای این کار ارائه میدهد.
کتابخانه های جایگزین دیگری نیز مانند backtrader با امکانات مشابه وجود دارند. شما می توانید به داکیومنت این کتابخانه ها مراجعه کنید تا بیشتر با جزییاتشان آشنا شوید.
هر استراتژی یک کلاس پایتون است که از کلاس پایه Strategy ارثبری میکند. دو تابع کلیدی دارد:
فرض کنید میخواهیم یک استراتژی ساده تعریف کنیم که طی آن: اگر میانگین متحرک با طول 20، میانگین متحرک با طول 50 را به سمت بالا قطع کرد، وارد یک معامله خرید بشویم و در صورتی که عکس این اتفاق افتاد، ایک معامله فروش انجام دهیم. کدهای مربوط به تعریف این استراتژی را در ادامه میبینید:
# فایل: simple_sma_crossover.py
import pandas as pd
import yfinance as yf
from backtesting import Backtest, Strategy
from backtesting.lib import crossover
class SimpleSMACrossover(Strategy):
"""
استراتژی قطع شدن میانگین متحرک ساده (SMA Crossover).
پارامترها:
n1: دوره میانگین متحرک کوتاهمدت (پیشفرض 20)
n2: دوره میانگین متحرک بلندمدت (پیشفرض 50)
"""
# پارامترهای قابل تنظیم استراتژی
n1 = 20
n2 = 50
def init(self):
# محاسبه اندیکاتورها در اینجا انجام میشود.
# self.I() تابعی است که اندیکاتور را به دادههای استراتژی اضافه میکند.
close_prices = self.data.Close
self.sma_short = self.I(lambda x: pd.Series(x).rolling(self.n1).mean(),
close_prices, name=f'SMA{self.n1}')
self.sma_long = self.I(lambda x: pd.Series(x).rolling(self.n2).mean(),
close_prices, name=f'SMA{self.n2}')
def next(self):
# این تابع برای هر روز (کندل) اجرا میشود.
# self.position نشاندهنده موقعیت فعلی ما است. اگر خالی باشد، یعنی موقعیتی نداریم.
# شرط خرید: اگر موقعیتی نداریم و SMA کوتاه از SMA بلند بالاتر رفته باشد (قطع به سمت بالا)
if not self.position and crossover(self.sma_short, self.sma_long):
# دستور خرید. size=1 یعنی تمام سرمایه موجود را وارد معامله کن.
self.buy()
# شرط فروش: اگر موقعیت خرید داریم و SMA بلند از SMA کوتاه بالاتر رفته باشد (قطع به سمت پایین)
elif self.position and crossover(self.sma_long, self.sma_short):
# بستن موقعیت خرید (فروش)
self.position.close()
if __name__ == "__main__":
# --- بخش اجرا و گزارشگیری ---
print("🚀 شروع بکتست استراتژی SMA Crossover ساده")
print("="*50)
# 1. دانلود داده (میتوانید از فایل CSV ذخیره شده هم بخوانید)
print("در حال دریافت دادههای BTC...")
data = yf.download("BTC-USD", start="2020-01-01", end="2025-12-01")
# اصلاح ستونها: حذف MultiIndex و تبدیل به ستونهای ساده (در صورتی که از روشی غیر از کتابخانه yfindnce داده ها را دریافت کرده اید، نیازی به انجام این اصلاح نخواهید داشت)
if isinstance(data.columns, pd.MultiIndex):
# اگر MultiIndex است، آن را ساده کنیم
data.columns = [col[0] for col in data.columns]
# اطمینان حاصل کنیم که ستونهای مورد نیاز وجود دارند
required_cols = ['Open', 'High', 'Low', 'Close', 'Volume']
for col in required_cols:
if col not in data.columns:
print(f"⚠️ ستون {col} در دادهها یافت نشد!")
print(f"\nدادههای دریافت شده: {len(data)} کندل")
print(f"ستونها: {list(data.columns)}")
# 2. ایجاد موتور بکتست
# cash: سرمایه اولیه به دلار
# commission: کارمزد معاملات (مثلاً 0.1%)
# exclusive_orders=True: فقط یک موقعیت باز در هر زمان داشته باش.
bt = Backtest(data, SimpleSMACrossover,
cash=1000000,
commission=.001,
exclusive_orders=True)
# 3. اجرای استراتژی روی دادهها
print("\nدر حال اجرای شبیهسازی معاملات...")
output = bt.run()
# 4. نمایش خلاصه نتایج کلیدی
print("\n" + "="*50)
print("📊 خلاصه نتایج بکتست")
print("="*50)
print(f"📈 بازده نهایی استراتژی: {output['Return [%]']:.2f}%")
print(f"🏦 سرمایه نهایی: ${output['Equity Final [$]']:.2f}")
print(f"😨 حداکثر افت سرمایه (Max Drawdown): {output['Max. Drawdown [%]']:.2f}%")
print(f"🔢 تعداد معاملات: {output['# Trades']}")
print(f"✅ درصد معاملات سودده (Win Rate): {output['Win Rate [%]']:.2f}%")
print(f"⚖️ نسبت سود به ضرر (Profit Factor): {output['Profit Factor']:.2f}")
# 5. رسم نمودار تعاملی کامل (قیمت، اندیکاتورها، معاملات، سرمایه)
print("\nدر حال تولید نمودارها... (پنجره نمودار باز میشود)")
bt.plot()
خروجی اجرای کد:
با اجرای کد بالا، خروجی زیر در ترمینال نمایش داده میشود:

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

FractionalBacktest استفاده کنید. در مثال بعدی از این کلاس استفاده خواهیم کرد.نکته مهم:گاهی اوقات استفاده از کتابخانه yfinance برای ایرانیان با محدودیت مواجه میشود و دانلود داده ها از این روش امکان پذیر نیست. شما میتوانید برای دریافت داده های تاریخی به کانالهای تلگرام و یا ایتای ما مراجعه کنید. نمونه هایی از داده های تاریخی در فرمت فایل های CSV برای استفاده شما در این کانالها قرار داده شده است. بنابراین، درصورتی که فایلها را از کانال دریافت کرده اید میتوانید از این بخش عبور کنید. کدهای فراخوانی داده ها از یک فایل در بخش “سوالات متداول و عیبیابی” توضیح داده شده اند
یک روش ساده تر برای تست این کتابخانه بدون دانلود داده های تاریخی، استفاده از داده های تست موجود در همین کتابخانه است. برای این کار شما میتوانید از دستور زیر برای استخراج داده های بیتکوین و یا جفت ارز یورو به دلار استفاده کنید:
from backtesting.test import EURUSD, BTCUSD
شما در ادامه میبایست از دستور زیر برای بکتست گیری از این داده ها استفاده کنید (دیگر نیازی به مرتب سازی داده ها ندارید):
bt = Backtest(EURUSD, SimpleSMACrossover,
cash=1000,
commission=.001,
exclusive_orders=True)
استفاده از اندیکاتورهای آماده از کتابخانه pandas-ta: در کد استراتژی بالایی، ما اندیکاتورهای میانگین متحرک ساده را بصورت دستی تعریف کردیم. شما به همین نحو میتوانید تقریبا هر اندیکاتور تکنیکال شخصی خودتان را تعریف کنید. با این حال بسیاری از اندیکاتورهای تکنیکل را میتوان از کتابخانه pandas-ta استخراج کرد. برای این کار کافی است پس از لود کردن این کتابخانه بصورت زیر:
import pandas-ta as ta
خطوط زیر در کد تعریف استراتژی را:
self.sma_short = self.I(lambda x: pd.Series(x).rolling(self.n1).mean(),
close_prices, name=f'SMA{self.n1}')
self.sma_long = self.I(lambda x: pd.Series(x).rolling(self.n2).mean(),
close_prices, name=f'SMA{self.n2}')
با این کد جایگزین کنیم:
self.sma_short = self.I(ta.sma, close_prices, name=f'SMA{self.n1}')
self.sma_long = self.I(ta.sma, close_prices, name=f'SMA{self.n2}')
استراتژی ساده ما فقط با سیگنالهای SMA کار میکند. اما یک تریدر حرفهای همیشه مدیریت ریسک میکند. بیایید دو قانون کلیدی را اضافه کنیم:
# فایل: sma_crossover_with_sltp.py
import pandas as pd
from backtesting import Strategy
from backtesting.lib import crossover, FractionalBacktest
class SMACrossWithSLTP(Strategy):
"""
استراتژی SMA Crossover همراه با مدیریت ریسک (حد سود و حد ضرر).
"""
n1 = 20
n2 = 50
stop_loss_pct = 0.08 # حد ضرر 8% پایینتر از قیمت ورود
take_profit_pct = 3.5 # حد سود 350% بالاتر از قیمت ورود
def init(self):
close = self.data.Close
self.sma_short = self.I(lambda x: pd.Series(x).rolling(self.n1).mean(), close)
self.sma_long = self.I(lambda x: pd.Series(x).rolling(self.n2).mean(), close)
def next(self):
current_price = self.data.Close[-1]
if not self.position:
if crossover(self.sma_short, self.sma_long):
# محاسبه قیمتهای حد سود و ضرر نسبت به قیمت فعلی
stop_price = current_price * (1 - self.stop_loss_pct)
take_profit_price = current_price * (1 + self.take_profit_pct)
# خرید با تعیین SL و TP
self.buy(sl=stop_price, tp=take_profit_price)
# نکته: دیگر شرط فروش با crossover را اینجا نداریم، چون موقعیت یا با SL/TP بسته میشود، یا باید یک شرط فروش جداگانه اضافه کنیم.
# در این نسخه، فقط با SL/TP از معامله خارج میشویم.
# میتوانیم شرط فروش با crossover را هم حفظ کنیم:
elif self.position and crossover(self.sma_long, self.sma_short):
self.position.close()
# اجرا و مقایسه با استراتژی قبلی
if __name__ == "__main__":
import yfinance as yf
data = yf.download("BTC-USD", "2020-01-01", "2025-12-01")
# اصلاح ستونها: حذف MultiIndex و تبدیل به ستونهای ساده
if isinstance(data.columns, pd.MultiIndex):
# اگر MultiIndex است، آن را ساده کنیم
data.columns = [col[0] for col in data.columns]
# اطمینان حاصل کنیم که ستونهای مورد نیاز وجود دارند
required_cols = ['Open', 'High', 'Low', 'Close', 'Volume']
for col in required_cols:
if col not in data.columns:
print(f"⚠️ ستون {col} در دادهها یافت نشد!")
bt_simple = FractionalBacktest(data, SimpleSMACrossover, fractional_unit=1e-06, cash=1000, commission=.001)
bt_sltp = FractionalBacktest(data, SMACrossWithSLTP, fractional_unit=1e-06, cash=1000, commission=.001)
res_simple = bt_simple.run()
res_sltp = bt_sltp.run()
print("\n" + "="*60)
print("🆚 Performance comparison: Simple against strategy with SL/TP")
print("="*60)
print(f"{'reference':<25} {'simple':<15} {'with SL/TP':<15}")
print("-"*60)
print(f"{'Total return %':<25} {res_simple['Return [%]']:<15.2f} {res_sltp['Return [%]']:<15.2f}")
print(f"{'Max Drawdown %':<25} {res_simple['Max. Drawdown [%]']:<15.2f} {res_sltp['Max. Drawdown [%]']:<15.2f}")
print(f"{'Win Rate %':<25} {res_simple['Win Rate [%]']:<15.2f} {res_sltp['Win Rate [%]']:<15.2f}")
print(f"{'Number of trades':<25} {res_simple['# Trades']:<15} {res_sltp['# Trades']:<15}")
print(f"{'Avg. return/trade':<25} {res_simple['Avg. Trade [%]']:<15.2f} {res_sltp['Avg. Trade [%]']:<15.2f}")
پس از اجرای کد بالا، مقایسه جالبی خواهید دید که در تصویر زیر نمایش داده شده است. این مقایسه نشان میدهد:
هدف، پیدا کردن توازن بین سود و ریسک است.

حالا بیایید استراتژی را هوشمندتر کنیم. اندیکاتور RSI (شاخص قدرت نسبی) میتواند به ما بگوید بازار در شرایط “اشباع خرید” (Overbought) یا “اشباع فروش” (Oversold) قرار دارد. میتوانیم از این اطلاعات برای فیلتر کردن سیگنالهای SMA استفاده کنیم.
منطق جدید: فقط زمانی بخریم که دو شرط با هم برقرار باشند: ۱) سیگنال خرید SMA ۲) RSI زیر سطح ۳۰ (اشباع فروش) باشد. این یعنی بازار در حال اصلاح است و احتمال بازگشت روند بیشتر است.
# فایل: advanced_strategy.py
import pandas as pd
import numpy as np
from backtesting import Backtest, Strategy
from backtesting.lib import crossover
def calculate_rsi(series: pd.Series, period: int = 14) -> pd.Series:
"""
تابع محاسبه RSI.
فرمول RSI: 100 - (100 / (1 + (میانگین سود / میانگین ضرر)))
"""
delta = series.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi
class AdvancedSMARSIStrategy(Strategy):
"""
استراتژی ترکیبی پیشرفته: فیلتر کردن سیگنالهای SMA با اندیکاتور RSI.
"""
# پارامترهای SMA
sma_short = 20
sma_long = 50
# پارامترهای RSI
rsi_period = 14
rsi_oversold = 30 # زیر این عدد، اشباع فروش محسوب میشود
rsi_overbought = 70 # بالای این عدد، اشباع خرید محسوب میشود
# پارامترهای مدیریت ریسک
stop_loss = 0.06
take_profit = 0.12
def init(self):
close = self.data.Close
# محاسبه اندیکاتور SMA
self.sma_fast = self.I(lambda x: pd.Series(x).rolling(self.sma_short).mean(), close)
self.sma_slow = self.I(lambda x: pd.Series(x).rolling(self.sma_long).mean(), close)
# محاسبه اندیکاتور RSI با استفاده از تابع کمکی
# توجه: باید محاسبات را در قالب یک تابع lambda به self.I() بسپاریم.
self.rsi = self.I(lambda x: calculate_rsi(pd.Series(x), self.rsi_period), close)
def next(self):
current_price = self.data.Close[-1]
current_rsi = self.rsi[-1] if not np.isnan(self.rsi[-1]) else 50
# --- شرایط ورود به معامله خرید (Long) ---
# 1. سیگنال خرید از SMA (قطع به سمت بالا)
# 2. RSI در ناحیه اشباع فروش باشد (تاییدیه از مومنتوم)
if not self.position:
if (crossover(self.sma_fast, self.sma_slow) and current_rsi < self.rsi_oversold):
sl_price = current_price * (1 - self.stop_loss)
tp_price = current_price * (1 + self.take_profit)
self.buy(sl=sl_price, tp=tp_price)
# --- شرایط خروج از معامله خرید ---
# 1. سیگنال فروش از SMA (قطع به سمت پایین) - خروج اصلی
# 2. RSI به ناحیه اشباع خرید برسد (خروج زودتر با سود) - خروج ثانویه
elif self.position:
if crossover(self.sma_slow, self.sma_fast):
self.position.close()
elif current_rsi > self.rsi_overbought:
self.position.close()
حالا همه قطعات پازل را داریم. بیایید استراتژی نهایی (ترکیبی پیشرفته) را روی دادههای یک سهم یا ارز اجرا کنیم و یک گزارش حرفهای تولید کنیم.
# فایل: final_backtest_project.py
import pandas as pd
import numpy as np
import yfinance as yf
from datetime import datetime
from backtesting import Strategy, Backtest # برای بکتست گیری در بازارهایی غیر از ارز دیجیتال
from backtesting.lib import crossover, FractionalBacktest # برای بکتست گیری در بازار ارز دیجیتال
# ---- تعریف توابع کمکی ----
def calculate_rsi(series, period=14):
delta = series.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
rs = gain / loss
return 100 - (100 / (1 + rs))
# ---- استراتژی نهایی ترکیبی ----
class FinalCombinedStrategy(Strategy):
sma_short = 20
sma_long = 60
rsi_period = 14
rsi_oversold = 35
rsi_overbought = 75
stop_loss = 0.05
take_profit = 0.15
def init(self):
close = self.data.Close
self.sma_fast = self.I(lambda x: pd.Series(x).rolling(self.sma_short).mean(), close)
self.sma_slow = self.I(lambda x: pd.Series(x).rolling(self.sma_long).mean(), close)
self.rsi = self.I(lambda x: calculate_rsi(pd.Series(x), self.rsi_period), close)
def next(self):
price = self.data.Close[-1]
rsi_val = self.rsi[-1]
if not self.position:
if (crossover(self.sma_fast, self.sma_slow) and rsi_val < self.rsi_oversold):
self.buy(sl=price*(1-self.stop_loss), tp=price*(1+self.take_profit))
elif self.position:
if crossover(self.sma_slow, self.sma_fast) or rsi_val > self.rsi_overbought:
self.position.close()
# ---- اجرای اصلی پروژه ----
if __name__ == "__main__":
print("🎯 پروژه عملی: بکتست استراتژی ترکیبی پیشرفته (SMA + RSI + SL/TP)")
print("="*70)
# 1. انتخاب نماد و دانلود داده
TICKER = "BTC-USD" # بیتکوین
YEARS = 5
end_date = datetime.now().strftime("%Y-%m-%d")
start_date = (datetime.now() - pd.DateOffset(years=YEARS)).strftime("%Y-%m-%d")
print(f"نماد: {TICKER}")
print(f"بازه زمانی: {start_date} تا {end_date} ({YEARS} سال)")
print("در حال دانلود داده...")
data = yf.download(TICKER, start=start_date, end=end_date, progress=False)
# 2. اجرای بکتست با استراتژی نهایی
bt = FractionalBacktest(data, FinalCombinedStrategy, fractional_unit=1e-06و
cash=1000, # سرمایه اولیه 1000 دلار
commission=.002, # کارمزد 0.2%
margin=1, # اهرم 1:1 (بدون اهرم)
exclusive_orders=True)
print("در حال اجرای بکتست (این ممکن است چند ثانیه طول بکشد)...")
results = bt.run()
# 3. نمایش گزارش جامع
print("\n" + "#"*70)
print("گزارش نهایی بکتست")
print("#"*70)
# بخش 1: عملکرد کلی
print("\n1. 📊 عملکرد کلی")
print("-"*40)
print(f" بازده کل: {results['Return [%]']:+.2f}%")
print(f" سرمایه اولیه: ${results['Start Equity']:.2f}")
print(f" سرمایه نهایی: ${results['Equity Final [$]']:.2f}")
annual_return = ((1 + results['Return [%]']/100) ** (1/YEARS) - 1) * 100
print(f" بازده سالانه: {annual_return:.2f}%")
# بخش 2: ریسک
print("\n2. 🛡️ معیارهای ریسک")
print("-"*40)
print(f" حداکثر افت سرمایه: {results['Max. Drawdown [%]']:.2f}%")
print(f" نسبت شارپ: {results.get('Sharpe Ratio', 'N/A'):.2f}")
print(f" نسبت سورتینو: {results.get('Sortino Ratio', 'N/A'):.2f}")
# بخش 3: آمار معاملات
print("\n3. 🔄 آمار معاملات")
print("-"*40)
print(f" تعداد کل معاملات: {results['# Trades']}")
if results['# Trades'] > 0:
print(f" درصد معاملات سودده: {results['Win Rate [%]']:.1f}%")
print(f" میانگین سود به ازای هر معامله: {results['Avg. Trade [%]']:.2f}%")
print(f" بیشترین سود یک معامله: {results['Best Trade [%]']:.2f}%")
print(f" بیشترین ضرر یک معامله: {results['Worst Trade [%]']:.2f}%")
print(f" نسبت سود به ضرر (Profit Factor): {results['Profit Factor']:.2f}")
print(f" میانگین مدت نگهداری معامله: {results['Avg. Holding Time']}")
# بخش 4: نتیجهگیری
print("\n4. 📝 نتیجهگیری و وضعیت استراتژی")
print("-"*40)
if results['Return [%]'] > 0 and results['Max. Drawdown [%]'] < 20 and results.get('Sharpe Ratio', 0) > 1:
status = "✅ قوی"
recommendation = "استراتژی از نظر سودآوری و کنترل ریسک عملکرد قابل قبولی دارد. میتوان با سرمایه کم آن را در بازار واقعی تست کرد."
elif results['Return [%]'] > 0:
status = "⚠️ متوسط"
recommendation = "استراتژی سودده بوده اما قبل از استفاده واقعی، روی بازههای زمانی دیگر تست شود."
else:
status = "❌ ضعیف"
recommendation = "استراتژی در این بازه زمانی سودده نبوده است. نیاز به بازنگری در پارامترها یا منطق پایه دارد."
print(f" وضعیت: {status}")
print(f" توصیه: {recommendation}")
# 5. رسم نمودارهای تعاملی
print("\nدر حال تولید نمودارهای تعاملی...")
bt.plot()
print("\n✨ پروژه عملی به پایان رسید!")
با اجرای کد بالا شما میبایست خروجی زیر را در ترمینال (یا محیط Jupyter notebook، Google Colab و … مشاهده کنید:)

در این پروژه عملی، شما موفق شدید:
این چارچوب، پایهای قوی برای تست هر ایده معاملاتی دیگر است. کافی است کلاس استراتژی را تغییر دهید.
مطمئن شوید کتابخانه yfinance را با دستور pip install yfinance نصب کردهاید. اگر در محیط مجازی کار میکنید، فعال بودن آن را بررسی کنید.
اگر از سرور یا محیطی بدون رابط گرافیکی استفاده میکنید (مثل برخی IDEهای آنلاین)، دستور bt.plot() ممکن است کار نکند. به جای آن از bt.plot(resample=False, open_browser=False) استفاده کنید و نمودار را در فایل ذخیره کنید.
تاریخ پایان (end_date) را چک کنید. برای دریافت داده تا امروز، میتوانید از end_date = datetime.now().strftime("%Y-%m-%d") استفاده کنید.
پارامترهای استراتژی (مثل دورههای SMA) ممکن است برای بازار انتخابی شما مناسب نباشد. ابتدا سیگنالهای اندیکاتورها را روی نمودار قیمت به صورت دستی بررسی کنید. همچنین میتوانید در تابع next() با دستور print، مقادیر اندیکاتورها را چاپ کنید تا ببینید شرایط منطقی شما کی True میشود.
در صورتی که نتوانستید با کتابخانه yfinance کار کنید میتوانید با جایگزینهای دیگری مانند کتابخانه ccxt برای ارزهای دیجیتال کار کنید. برای دانلود داده های قیمت مربوط به یک سال اخیر بیتکوین به کمک این کتابخانه دستورات زیر را اجرا کنید:
ابتدا نصب کتابخانه با اجرای دستور: pip install ccxt
سپس دانلود داده ها:
import ccxt
# ---- مشخص کردن صرافی و دانلود داده ها ----
exchange = ccxt.kucoin()
timeframe = '4h'
start_date = datetime(year=2025, month=1, day=1)
since = int(start_date.timestamp()*1000) # تبدیل میکرو به میلی ثانیه
ohlcv = exchange.fetch_ohlcv(symbol='BTC/USDT', timeframe=timeframe, since=since)
data = pd.DataFrame(data = ohlcv, columns = ['timestamp', 'open', 'high', 'low', 'close', 'volume'])
data.rename(columns={
'open': 'Open',
'high': 'High',
'low': 'Low',
'close': 'Close',
'volume': 'Volume'
}, inplace=True)
پلتفرم تریدبرد با درک چالشهای پیش روی معامله گران علاقه مند به بکتست گیری اتوماتیک، و با هدف ساده و قابل دسترس کردن فرآیند بک تست و استراتژی سازی طراحی شده است. این پلتفرم یک راهکار تحت وب ارائه میدهد که بسیاری از موانع موجود در پلتفرم های سنتی را از بین میبرد. اگر شما یک معامله گر نا آشنا با کُدنویسی حرفه ای و معاملات الگوریتمی هستید، تریدبرد این امکان را برایتان فراهم کرده تا استراتژی اندیکاتوری خود را به کمک یک رابط کاربری ساده و زیبا طراحی کنید و سپس در بازارهای مختلف، ارزهای مختلف و تایم فریمهای مختلف از آن بکتست گیری کنید. این فرایند کوتاهتر از یک دقیقه خواهد بود و شما را از سختی کار با پلتفرم های دستی بکتست گیری رها خواهد کرد.
اما اگر شما با کُدنویسی پایتونی آشنا هستید، یا استراتژی شما پیچیده تر از آن است که با اندیکاتورهای متنوع تریدبرد تعریف شود، میتوانید استراتژی خود را بصورت کُدی در این پلتفرم تعریف کنید و سپس به سادگی در بازارها و ارزهای مختلف از آن بکتست گیری کنید. شما میتوانید با تغییر پارامترهای استراتژی خود و بکتست گیری مداوم، استراتژی پیچیده خود را در مدت زمان کوتاهی بهینه سازی کنید. کاری که شاید در بکتست گیری دستی هرگز امکان پذیر نباشد. همچنین شما میتوانید استراتژی های الگوریتمی خود را در تریدبرد ثبت کرده و برایشان روباتهای سیگنال ساز بسازید تا در صورت برآورده شدن شرایط ورود و خروج از معاملات، این روباتها سیگنالهای خرید و فروش را به شما در شبکه های اجتماعی اطلاع رسانی کنند.
پیشنهاد می کنیم برای آشنایی بیشتر با ربات های سیگنال دهی مقاله ی رباتهای سیگنالدهی چیستند و چگونه کار میکنند؟ از اسطوره تا واقعیت را مطالعه کنید.
کافیست مرورگر وب خود را باز کرده و به آدرس پلتفرم تریدبرد مراجعه کنید. شما برای استفاده از امکانات پلتفرم نیازی به حساب کارگزار ندارید و تنها یک ثبت نام ساده در خود پلتفرم کافیست.

صفحه اصلی و لاگین پلتفرم تریدبرد
تریدبرد دو راه قدرتمند و انعطاف پذیر برای تعریف استراتژی در اختیار شما میگذارد:

صفحه Strategy Builder در تریدبرد (هم حالت اندیکاتوری و هم محیط کدنویسی پایتون)
پس از ذخیره استراتژی، از منوی جانبی به بخش بک تست ها مراجعه کنید و روی دکمه “افزودن بکتست” (برای بک تست گیری از استراتژیهای اندیکاتوری) و یا “افزودن بکتست از استراتژی کدی” (برای بکتست گیری از استراتژیهای کدی) کلیک کنید.
استراتژی ای که قبلاً تعریف کرده اید (چه اندیکاتوری و چه کدپایتون) را از لیست انتخاب کنید.
بازار مورد نظر خود را (فارکس، ارز دیجیتال، بورس ایران، طلا و نقره) و سپس نماد خاص را انتخاب نمایید. داده های ارزهای دیجیتال مستقیماً از صرافی معتبر بایننس تامین میشود که از صحت و کیفیت آن اطمینان دارید.
تایم فریم مورد نظر برای تست را از ۱ دقیقه تا ۱ روز انتخاب کنید.
تاریخ شروع و پایان دقیق دوره بک تست را با استفاده از تقویم مشخص کنید.
با کلیک بر روی دکمه ساخت بکتست، یک کارت بکتست با تنظیمات مشخص شده توسط شما در صفحه ساخته میشود که در وضعیت “درانتظار اجرا” قرار دارد. با کلیک بر روی دکمه “اجرای بکتست” مربوط به این کارت، بک تست اجرا میشود و پس از خاتمه یافتن فرایند بکتست گیری (در کمتر از یک دقیقه)، وضعیت کارت بروز شده و به حالت “انجام شده” تغییر میکند.

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

صفحه نتایج بک تست با لیست کامل معیارها در تریدبرد
نمودار اصلی قیمت به همراه مارکرهای واضحی که نشان دهنده نقطه ورود (با فلش سبز) و خروج (با فلش قرمز) هر معامله هستند. این نمودار درک بصری عالی از رفتار استراتژی به شما میدهد.
دو منحنی روی یک نمودار:
یک نمودار میله ای که سود یا زیان هر معامله را به ترتیب زمانی نشان میدهد. این نمودار به شما کمک میکند توالی معاملات برنده و بازنده را به راحتی شناسایی کنید.

مجموعه نمودارها (قیمت، دارایی، دارایی خرید و نگهداری، مارکرهای ورود و خروج از معاملات ) در تریدبرد
یکی از ویژگیهای منحصر به فرد تریدبرد، امکان ذخیره نتایج هر بک تست است. شما میتوانید نتایج تستهای مختلف خود را ذخیره کنید و در هر زمان دیگری به آنها مراجعه کرده، مجدداً تحلیل کنید یا با نتایج جدید مقایسه نمایید. این ویژگی برای ردیابی پیشرفت و توسعه استراتژی شما در طول زمان حیاتی است.
برای تحلیلهای دقیق تر، شما میتوانید گزارش کامل تمام معاملات را در قالب یک فایل CSV دانلود کرده و در نرم افزارهایی مانند Excel یا Google Sheets باز کرده و تحلیلهای آماری پیشرفته تری روی آنها انجام دهید. همچنین میتوانید نتایج بکتست های مربوط به بازه های زمانی متوالی مخصوصا مربوط با تایم فریم های کوچک (که محدودیت بازه زمانی انتخابی برای آنها صدق میکند) را در یک فایل CSV جمع آوری کرده و تحلیل جامع تری از استراتژی خود در این تایم فریم ها داشته باشید.

نمودار ستونی بازدهی معاملات (قابل ذخیره سازی به فرمتهای گوناگون از جمله png و csv)
تبریک! شما اکنون توانایی بکتستگیری یک استراتژی معاملاتی از صفر تا صد را دارید. شما نه تنها یک استراتژی ساده را تست کردید، بلکه آن را با افزودن قوانین مدیریت ریسک و فیلترهای دیگر، به یک سیستم معاملاتی قویتر ارتقا دادید.
نتایج بکتستگیری ضمانتی برای سودآوری آینده نیستند. بازارها پویا هستند و شرایط تغییر میکند. همیشه:
هدف بکتستگیری، افزایش احتمال موفقیت و کاهش ریسکهای غیرضروری است، نه تضمین سود قطعی.
برای آشنایی بیشتر با مفهوم و کارایی بک تستینگ مقاله ی بک تستینگ: بک تست چیست ؟ سنگ محک استراتژیهای معاملاتی شما را مطالعه بکنید .
امیدواریم این آموزش گامبهگام برای شما مفید بوده باشد. برای هر سوال یا ابهامی، میتوانید کدهای کامل ارائه شده را اجرا و پارامترها را تغییر دهید. موفق و پرسود باشید!