הרצנו שוב בדיקה היסטורית שמורה וקיבלנו עקומת הון אחרת. אותו קוד אסטרטגיה, אותו טווח תאריכים, אותו סימול. יתרת הסיום סטתה ב־1.8%, ושלוש עסקאות זזו בנר אחד.
זה מספיק כדי להקשות עלינו לתת אמון בתוצאת מחקר. אם עמית לצוות, גרסה עתידית שלכם או שירות למסחר מדומה לא יכולים לשחזר את ההרצה, אי אפשר לדעת אם שינוי שיפר את האסטרטגיה או רק שינה את הניסוי. כך פעלנו כדי למצוא את מקור הסטייה.
יום 1: הגדרנו מה פירוש „אותה הרצה”
הטעות הראשונה שלנו הייתה להתייחס לקובץ האסטרטגיה כאל הניסוי כולו. הוא לא היה כזה. ההרצה הייתה תלויה גם בנתוני הקלט, בגרסת המנוע, בלוח השנה, בפרטי המכשיר ובהגדרות הביצוע. הקוד תיאר רק חלק אחד מהחישוב.
לפני ששינינו משהו, הכנו רשומת פרטי הרצה. היא כללה את ה־commit של האסטרטגיה, מזהי תמונות מצב של הנתונים, טווח התאריכים, זירת המסחר, לוח העמלות, מקור נתוני המימון, מודל מילוי הפקודות וגרסאות התוכנה. שמרנו גם את הפקודות והמילויים שהתקבלו, כי עקומת הון לבדה לא מראה היכן שתי הרצות התחילו לסטות.
| פריט | מה לתעד | למה זה חשוב |
|---|---|---|
| נתוני שוק | מזהה תמונת מצב, גרסת סכימה, התאמות | ספקים מתקנים נתוני עבר ומעדכנים פעולות תאגידיות |
| ביצוע פקודות | דרגת עמלה, סדרת מימון, הגדרות מילוי והשפעת מחיר | ערכי ברירת מחדל והנחות לגבי החשבון משנים את התוצאות |
| סביבת הריצה | commit של הקוד, גרסאות המנוע והתלויות | ספריות עשויות לשנות את סדר הפעולות, העיגול או האינדיקטורים |
| תוצאה | פקודות, מילויים, פוזיציות ומדדים | מראה היכן ההרצות מתחילות להיות שונות |
יום 2: השווינו בין העסקאות, לא בין ערכי Sharpe
מדדי הסיכום הסיחו את דעתנו. ערכי Sharpe של שתי ההרצות היו כמעט זהים, אבל יומני המילויים חשפו את אי־ההתאמה הראשונה בעת סליקת מימון. בהרצה אחת התעריף חויב על הפוזיציה שהייתה פתוחה בזמן חותמת הסליקה; באחרת, על הפוזיציה לאחר האיזון מחדש של אותו מועד.
קוד האסטרטגיה לא השתנה. סדר האירועים במנוע השתנה. עדכון גרסה קטן הפך את הסדר למפורש, במקום שייקבע לפי האופן שבו שני אירועים הסתדרו במקרה.
עדכנו את מפרט ההרצה וקבענו בו את הסדר: קודם מחילים את המימון על הפוזיציה שהייתה קיימת לקראת הסליקה, ואז מעבדים את החלטות האסטרטגיה לאותו מועד. הנוהל המדויק עשוי להשתנות בין זירות ומנועים. הבאג הוא להשאיר אותו לא מוגדר.
יום 3: התברר שקובץ נתונים „זהה” היה שונה
אחרי שקיבענו את סדר האירועים, אי־ההתאמות שנותרו התרכזו בכמה עסקאות במניות. הספק תיקן התאמה היסטורית לפיצול מניות. לקובץ שלנו היו אותו שם ואותו מספר שורות כמו קודם, ולכן נדמה היה שלא השתנה.
עכשיו אנחנו יוצרים טביעת אצבע לכל תמונת מצב בלתי משתנה של נתונים, ושומרים לצדה את מדיניות ההתאמות. גיבוב מראה לנו אם הבתים השתנו, אבל לא מסביר למה. לכן רשומת פרטי ההרצה כוללת גם מקור, מועד אחזור וגרסת טרנספורמציה. כשנתונים מתעדכנים, הפרטים האלה הם חלק מהתוצאה.
בדיקה היסטורית שאפשר לשחזר מחייבת תשובה לשאלה: „איזו גרסה של העבר היא ראתה?”
יום 4: מצאנו ברירת מחדל חבויה
ההבדל האחרון היה עמלת maker שהוגדרה כ־0 כי הגדרת האסטרטגיה לא כללה את השדה. מנוע חדש יותר החיל את עמלת ברירת המחדל של החשבון. ברירת המחדל היחידה הזאת שינתה מספיק עסקאות גבוליות כדי להסביר את רוב הפער ביתרת הסיום.
הגדרנו במפורש הגדרות בעלות משמעות כלכלית, והגדרנו למנוע להדפיס את התצורה שנקבעה בפועל לרשומת ההרצה. ברירות מחדל נוחות כשמתנסים. הן ראיה חלשה כשמשווים תוצאות לאורך זמן.
על מה היינו מדלגים בפעם הבאה
בילינו חצי יום בהשוואת מדדים מצטברים לפני שבדקנו את המילוי הראשון שהיה שונה. אל תתחילו שם. מיינו את שני יומני האירועים לפי חותמת זמן והשוו את הסטייה הראשונה; ההבדלים שבאים אחריה נובעים לעיתים קרובות מאותו גורם.
היינו גם מוותרים על הרעיון שתמונת קונטיינר לבדה הופכת הרצה לניתנת לשחזור. היא מקבעת חלק גדול מסביבת התוכנה, אבל לא קובץ נתונים חיצוני, לוח עמלות שנשלף בזמן הריצה או היסטוריה שספק עדכן.
כשתוצאות בדיקה היסטורית משתנות, שמרו את שתי רשומות פרטי ההרצה ואת היומנים, ואז תקנו מקור סטייה אחד בכל פעם. התוצאה החשובה היא לא רק עקומה שאפשר להריץ שוב, אלא רשומה שמסבירה אילו נתונים והנחות הפיקו אותה ולמה ההרצה הבאה עשויה להיות שונה.
← כל הפוסטים


