כל המאמרים
BacktestingPythonyfinanceמדריך
·נכתב על ידי עדן מרגוליס

Backtesting בפייתון — מדריך שלב אחר שלב

מדריך מעשי ל-Backtesting בפייתון: איך למשוך נתונים, לכתוב לולאת בדיקה בסיסית, לחשב מדדים כמו Sharpe Ratio ו-Drawdown, ואיך להימנע מהשגיאות הנפוצות.

Backtesting בפייתון מתבצע בשלושה שלבים: משיכת נתוני מחיר היסטוריים, הרצת לוגיקת האסטרטגיה על הנתונים תוך שמירה על סדר הזמן הנכון, וחישוב מדדי ביצוע כמו Sharpe Ratio ו-Max Drawdown. הכלים הבסיסיים — yfinance, pandas, numpy — חינמיים לחלוטין ומספיקים לבדיקה ראשונית. השגיאה הכי יקרה בתהליך היא Look-ahead Bias — שימוש בטעות במידע שלא היה זמין בזמן אמת.


שלב 1: משיכת נתונים היסטוריים

import yfinance as yf
import pandas as pd

df = yf.download("AAPL", start="2019-01-01", end="2024-01-01", interval="1d")
df = df.dropna()

עבור בדיקה ראשונית, מספיקה מניה בודדת או כמה מניות. חשוב לבדוק תקופה שכוללת גם שנים "קשות" (כמו 2020 או 2022) ולא רק שנים של עלייה רציפה — אחרת הבדיקה לא תחשוף איך האסטרטגיה מתמודדת עם ירידות שוק.

שלב 2: חישוב אינדיקטורים

df["ma20"] = df["Close"].rolling(20).mean()
df["ma50"] = df["Close"].rolling(50).mean()

delta = df["Close"].diff()
gain = delta.where(delta > 0, 0).rolling(14).mean()
loss = -delta.where(delta < 0, 0).rolling(14).mean()
df["rsi"] = 100 - (100 / (1 + gain / loss))

rolling() מחשב את הערך רק על סמך הנתונים עד לאותה נקודה, בלי להסתכל קדימה — זה נכון מבחינת מניעת Look-ahead Bias. הסכנה מתחילה בשלב הבא, כשמחליטים איך להשתמש באינדיקטורים האלה להחלטת מסחר.

שלב 3: לוגיקת האיתותים — בלי להסתכל קדימה

# האיתות נבדק על נתוני היום הקודם (shift(1)), הביצוע קורה במחיר הפתיחה של היום
df["signal"] = (df["rsi"].shift(1) < 35) & (df["Close"].shift(1) > df["ma20"].shift(1))
df["entry_price"] = df["Open"].where(df["signal"], None)

ה-.shift(1) הוא ההבדל בין בקטסט תקין לבקטסט עם Look-ahead Bias: הוא מבטיח שההחלטה מתקבלת על סמך מידע שכבר היה ידוע במלואו (סוף היום הקודם), והביצוע קורה במחיר הפתיחה של היום הנוכחי — בדיוק כמו שבוט אמיתי היה פועל.

שלב 4: הרצת סימולציית העסקאות

trades = []
in_position = False
entry_price = 0
stop_loss_pct = 0.03
take_profit_pct = 0.06

for i in range(len(df)):
    row = df.iloc[i]
    if not in_position and row["signal"]:
        entry_price = row["entry_price"]
        stop_price = entry_price * (1 - stop_loss_pct)
        target_price = entry_price * (1 + take_profit_pct)
        in_position = True
    elif in_position:
        if row["Low"] <= stop_price:
            trades.append({"entry": entry_price, "exit": stop_price, "result": "stop_loss"})
            in_position = False
        elif row["High"] >= target_price:
            trades.append({"entry": entry_price, "exit": target_price, "result": "take_profit"})
            in_position = False

trades_df = pd.DataFrame(trades)
trades_df["return_pct"] = (trades_df["exit"] - trades_df["entry"]) / trades_df["entry"]

זו לולאה בסיסית מתאימה להבנה ולבדיקה ראשונית. לאסטרטגיות מורכבות יותר (כמה פוזיציות בו-זמנית, עמלות, Slippage), ספריות ייעודיות כמו backtrader או vectorbt חוסכות הרבה עבודת תשתית וכוללות ניהול פוזיציות מובנה.

שלב 5: חישוב מדדי ביצוע

win_rate = (trades_df["return_pct"] > 0).mean()
avg_win = trades_df.loc[trades_df["return_pct"] > 0, "return_pct"].mean()
avg_loss = trades_df.loc[trades_df["return_pct"] < 0, "return_pct"].mean()

print(f"מספר עסקאות: {len(trades_df)}")
print(f"Win Rate: {win_rate:.1%}")
print(f"רווח ממוצע בעסקה מנצחת: {avg_win:.1%}")
print(f"הפסד ממוצע בעסקה מפסידה: {avg_loss:.1%}")

חישובי Sharpe Ratio ו-Max Drawdown המלאים מוסברים במדריכים הייעודיים שלהם — שני המדדים החשובים ביותר לצד Win Rate.

המדדים שחייבים לבדוק לפני שממשיכים הלאה

| מדד | ערך סביר | דגל אדום | |---|---|---| | מספר עסקאות | 50+ | פחות מ-30 — אין מובהקות סטטיסטית | | תקופת בדיקה | 3+ שנים, כולל שנה קשה | פחות משנה, או רק שנים "טובות" | | Sharpe Ratio | מעל 1.0 | מעל 3.0 — חשוד לOverfitting | | Max Drawdown | מתחת ל-20% | מעל 30% |

שאלות נפוצות

מה ההבדל בין הלולאה הידנית לספריות כמו backtrader?

הלולאה הידנית טובה להבנת המנגנון ולבדיקות ראשוניות פשוטות. backtrader ו-vectorbt מטפלים אוטומטית בעמלות, Slippage, ניהול כמה פוזיציות בו-זמנית, ומייצרים דוחות מפורטים — שווה לעבור אליהן ברגע שהאסטרטגיה מורכבת מ"קנה/מכור נכס בודד".

האם חובה לכלול עמלות ב-Backtesting?

מומלץ מאוד, במיוחד לאסטרטגיות עם מסחר תדיר. גם עמלה נמוכה של $0-1 לעסקה יכולה לשחוק משמעותית אסטרטגיה עם מרווחי רווח קטנים ומספר עסקאות גבוה.

כמה נתונים היסטוריים צריך לבדיקה אמינה?

מינימום 2-3 שנים, ועדיף יותר, כולל תקופה עם ירידת שוק משמעותית (כמו 2022). בדיקה רק על שוק עולה נותנת תמונה חלקית ומטעה על עמידות האסטרטגיה.

מה השלב הבא אחרי Backtesting מוצלח?

Paper Trading — הרצת האסטרטגיה בזמן אמת עם כסף וירטואלי, כדי לחשוף בעיות ש-Backtesting לא מדמה (Slippage, עיכובי API, תנאי שוק חיים). ראו את השלב המלא במדריך בניית בוט מסחר AI.

רוצה לבנות בוט כזה בעצמך?

שיחה קצרה של 20 דקות.

דברו איתנו חינם