פיתוח מנוע בקטסטינג בעל ביצועים גבוהים ב-Python

הרצת נתונים היסטוריים נמשכת שעות, ובדיקת השערות מעכבת את כל התחום. אנו מפתחים ליבת חישוב לבקטסטינג ב-Python כפתרון מלא: מחדשים את ארכיטקטורת החישוב ומסירים צווארי בקבוק. זמן ההרצה מתקצר פי כמה, והתוצאות משוחזרות על אותם נתונים.

שירותי פיתוח בלוקצ'יין

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

למה בקטסטינג בפייתון נתקע במהירות

בקטע בדיקה של שבוע מסחר, ההרצה נכנסת לדקות, על כמה שנות היסטוריה — כבר בשעות. בקטסטינג בפייתון נתקע בכך לא בנפח הנתונים כשלעצמו: כל קיבוץ מחדש, חלון מחליק וחתך יוצרים עותקים של DataFrame, ולולאה אלמנטרית על פני הברים מוסיפה תקורה על כל קריאה. צוואר הבקבוק של החישוב כמעט תמיד אינו במתמטיקה של האסטרטגיה, אלא בהעברת נתונים בין מבנים.

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

אופטימיזציה עיוורת היא חסרת משמעות: מדידת ביצועים מראה איזה קטע אוכל את הזמן העיקרי, ורק אחריה תיקונים נותנים אפקט כפול, לא שכתוב לשם שכתוב.

יכולת שחזור תוצאות — בעיה פחות מובנת מאליה. סדר פעולות בנקודה צפה, מיון לא יציב, מחוללי מספרים אקראיים לא מקובעים וסדר מעבר שונה מובילים לכך שהרצה חוזרת על אותם נתונים מפיקה מספרים אחרים. השוואה בין שתי גרסאות של אסטרטגיה מאבדת משמעות: לא ברור אם זה שיפור או רעש עיגולים.

הרצה איטית ופערים במספרים — לא תכונות של השפה, אלא תוצאה של מבנה צינור החישוב: היכן הנתונים חיים, מי מחזיק בעותקים, מה ובאיזה סדר מחושב. להלן נבחן כיצד כל אחד מהמקומות הללו נסגר — ממדידות ועד קיבוע סביבת ההרצה.

מה נותנת עיבוד מחדש של מנוע הבקטסטינג בפייתון

מהירות הרצה על נתונים היסטוריים

עיבוד מחדש של המנוע מתחיל בפרופיילינג: אנו מוצאים היכן החישוב נתקע במפרש, והיכן — בהעתקת נתונים מיותרת. אנו מווקטרים את חישוב האינדיקטורים, מסירים מעברים חוזרים על מחירים ומעבירים קטעים כבדים לקוד מהודר Python.

מהירות ההרצה על היסטוריה של שנים רבות עולה פי כמה: מה שנחשב לעשרות דקות נכנס לדקות, וכל רשת הפרמטרים נבדקת בהרצה אחת במקום לילה של המתנה.

יציבות החישוב

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

את הזמן שהתפנה הצוות מבזבז על השערות, ולא על המתנה וניתוח נפילות. הדוח על כל הרצה משוחזר, חזרה לתצורה קודמת אורכת הרצה אחת — בדיקת רעיונות מתנהלת ברציפות, ללא עקיפות ידניות ובנייה מחדש של הסביבה.

אפשרויות לארגון חישובים: השוואת מצבים

מצב החישוב קובע לא רק את המהירות, אלא גם אילו בדיקות בכלל ניתנות לשחזור. הרצה אירועית מנהלת את ההיסטוריה באותו סדר שבו פועלת הלוגיקה המסחרית, וקטורית מחשבת מערך שלם במעבר אחד, מבוזרת מפזרת תרחישים בלתי תלויים בין צמתים. את בחירת המצב מכתיבים מספר התרחישים והדרישות לדיוק מודל הביצוע — להלן החלופות ומה משלמים על כל אחת.

אפשרות מתי מתאימה מגבלות
הרצה אירועית הלוגיקה תלויה בזרימת הזמנות, ביצועים חלקיים והפעלת סטופים בתוך הבר מעבר מלא אחד על ההיסטוריה לכל תרחיש, יקר בעת סריקת פרמטרים
חישוב וקטורי בדיקה המונית של אותות ואינדיקטורים על היסטוריה רחבה אינה משחזרת תור הזמנות, החלקה ועדיפות פקודות
חישוב מבוזר על מספר צמתים מאות ערכות פרמטרים בלתי תלויות והרצות על כלים שונים דרושים מטמון משותף של נתוני שוק, סריאליזציה של מצב ובקרה שהתוצאה באשכול תואמת להרצה בודדת
חישוב מצטבר השתנה הקטע האחרון של הנתונים או בלוק חוקים אחד דורש מפת תלויות: חלק מהתוצאות יצטרכו להיחשב מחדש לחלוטין

שילוב מצבים במנוע אחד

מצבים נדירים נבחרים אחת ולתמיד. בדרך כלל מעבר וקטורי מסנן קומבינציות חלשות מראש על כל ההיסטוריה, אירועי בודק את הנבחרים עם מודל ביצוע מדויק, וחישוב מצטבר שומר על התוצאה מעודכנת עם הגעת נתונים חדשים.

מבוזרות מתחברת בשכבה גבוהה יותר — ברמת הצבת המשימות, ללא שינוי בלוגיקת האסטרטגיה: אותה מערכת חוקים נותנת דוח זהה גם במכונה מקומית וגם באשכול.

שלבי עיבוד מחדש: מביקורת החישוב ועד מסירה

עיבוד מחדש של ליבת החישוב מתבצע באיטרציות: קודם מודדים, אחר כך משנים, אחר כך משווים בהרצות בקרה. לאחר כל שלב נשאר חפץ, שלפיו רואים אם אפשר להתקדם הלאה.

  1. ביקורת המנוע ומדידת צווארי בקבוק. אנו עורכים פרופיילינג לחישוב על ההיסטוריה שלכם: היכן הזמן הולך להמתנה, היכן נתונים מחושבים מחדש, היכן התוצאה תלויה בסדר המעבר.
  2. סכימת חישוב. אנו מקבעים את זרימת הנתונים: מקורות מחירים, כללי ניקוי, רגע ההחלטה, התחשבנות בעמלות ובהחלקה. הסכימה הופכת לחוזה לקבלה.
  3. עיבוד מחדש של הליבה. מסירים חישובים חוזרים ועותקים מיותרים, מפרידים בין הכנת הנתונים להרצה, מעבירים קטעים כבדים לוקטוריזציה. Python נשארת שפת תיאור האסטרטגיות — הפנימיות של המנוע משתנה.
  4. בדיקה על היסטוריה. מריצים את המנוע החדש על אותם נתונים כמו הישן, ומשווים מטריקות עסקה אחר עסקה. פערים מנתחים עד הסיבה, ולא עד "בערך דומה".
  5. קבלת תוצאות. אתם מריצים את התרחישים שלכם, אנחנו מראים התאמה וניתוח פערים לפי סכימת החישוב, ולא לפי תחושות.
  6. מסירה והדרכה. מוסרים קוד, תצורות ותקנון הרצה, ומנתחים עם הצוות שלכם כיצד להוסיף אסטרטגיות ונתונים בלעדינו.

עבודה במקביל עם התשתית שלכם

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

ערכת אספקה: ליבת חישוב, סכימות, תקנונים

הערכה נמסרת לא כארכיון "כמו שיצא", אלא כמערכת של חפצים, שלפיהם הצוות שלכם מריץ את המנוע, משחזר את החישוב ומפתח אותו בלעדינו. להלן — מה בדיוק עובר ללקוח.

  • קוד המקור של המנוע — מודולים לחישוב פוזיציות ומטריקות, בדיקות, דוגמאות הרצות על נתונים היסטוריים.
  • סכימות חישוב — גרף קריאות, סדר עיבוד נרות וטיקים, נקודות התחשבנות בעמלות, החלקה ועיכובי ביצוע.
  • סכימות זרימת נתונים — מסלול ממקור המחירים ועד לחלון התוצאות: פורמטי קבצים, חלוקה למנות, לאן נכתבות המטריקות.
  • תצורות סביבות — פרמטרי הרצה, מערכי נתונים, פרופילים להרצה בודדת ומבוזרת.
  • מדיניות גישה ותפקידים — מי מריץ חישוב, מי משנה פרמטרים של אסטרטגיות, מי קורא תוצאות; סודות נשמרים מחוץ למאגר.
  • תקנון הפעלה ושחזור — התחלת הרצה, המשך מנקודת ביקורת, חזרה לגרסה קודמת של הליבה.
  • תיעוד והוראות לצוות — תיאור ממשקים, מדריך פרמטרים, סדר הוספת אסטרטגיה חדשה.

כיצד מתבצעת הקבלה

הקבלה מתבצעת לפי הרצות בקרה, ולא לפי רשימת קבצים. בצד שלכם אנו מריצים מערכת מוסכמת של אסטרטגיות, משווים מטריקות לתוצאת ייחוס ובודקים שחזור לאחר עצירה כפויה. בנפרד בודקים שמהנדס חדש לפי התיעוד מרים את הסביבה ומבצע חישוב ללא רמזים.

מקרה בוחן: כיצד קיצרנו את זמן ההרצה על נתונים היסטוריים

לקוח הגיע עם מנוע בקטסטינג ב-Python, שעבד כל עוד ההיסטוריה נכנסה לשנת מסחר אחת. הרצה מלאה על חלון של חמש שנים ארכה כארבע עשרה שעות ולעיתים קרובות לא שרדה עד הסוף: החישוב נפל על סדרות ארוכות, ושתי הרצות של אותה אסטרטגיה נתנו תוצאה שונה בספרה השלישית. היה צורך להשוות השערות ידנית, ואף אחד לא סמך על החלטות לפי מספרים כאלה.

מדידות לפי קטעי קוד הראו: את רוב הזמן אכלו לולאות שורה-שורה על פני ברים וחישוב מחדש של אינדיקטורים בכל צעד, ואת חוסר היציבות נתנו סדרי חיבור משתנים ומצב דולף בין הרצות.

מה בדיוק עיבדנו מחדש

לוגיקת החישוב הועברה לפעולות וקטוריות על מערכים: אינדיקטורים מחושבים פעם אחת על כל ההיסטוריה, ולא מחדש בכל בר. קטעים כבדים הועברו לתהליכים נפרדים, תוצאות ביניים נשמרות, לכן הרצה חוזרת לא מחשבת מחדש את המוכן. קיבענו את סדר האגרגציה ובודדנו את המצב: כל הרצה מתחילה מסביבה נקייה, והתוצאה משוחזרת ביט בביט.

לאחר עיבוד מחדש, הרצה מלאה על אותו חלון נכנסת לארבעים דקות, הפיזור בין הרצות — אפס, ועל היסטוריה ארוכה במשך רבעון תצפיות לא הייתה אף נפילה מתוך כמה מאות הרצות.

את המגבלות אנו מציינים מיד: על היסטוריית טיקים של כמה שנים החישוב עדיין אורך שעות, משימות כאלה אנו מציבים בחלון לילה, ומספר ההרצות המקבילות מוגבל על ידי זיכרון — סריקת פרמטרים מתבצעת בתורים, ולא על כל השדה בבת אחת.

במה שונה חישוב וקטורי מחישוב אירועי?

חישוב וקטורי מיישם חוק ישירות על כל סדרת המחירים: אינדיקטור ואות מחושבים כמערכים, ללא לולאה לפי זמן. היסטוריה של כמה שנים הרצה כזו עוברת בשניות, לכן עליה סורקים עשרות אלפי קומבינציות פרמטרים ומסננים חלשות מראש. המגבלה אחת, אבל קשוחה: הלוגיקה חייבת להיות ללא תנאי, ללא הסתמכות על מצב הפוזיציה.

הרצה אירועית מתנהלת נר אחר נר, ובכל נר משחזרת את המצב: פוזיציות פתוחות, יתרת כספים, הזמנות שהוגשו. רק כך מחושבים סטופים וטיים בתוך הנר, ביצוע חלקי, העברת פוזיציה דרך הלילה, עמלה והחלקה התלויה בנפח. בוקטורית זה לא מתבטא: התוצאה תלויה במה שהיה קודם, וסדר האירועים משנה את התוצאה.

ההבדל בדיוק החישוב מתבטא היכן שההחלטה תלויה בחשבון הנוכחי. וקטור יראה כניסה ויציאה לפי מחיר הסגירה, ואילו הרצה אירועית — שחלק מההזמנות לא בוצעו, והסטופ הופעל במחיר גרוע יותר. התשובה הראשונה מתאימה כדי לסנן רעיונות; השנייה — כדי להחליט אם אפשר לסמוך על האסטרטגיה.

מכאן השילוב של גישות במנוע אחד: חישוב וקטורי כמסנן על רשת פרמטרים רחבה, הרצה אירועית — לפיינליסטים ולבדיקת עמידות לעלויות. שני המצבים צריכים לקרוא אותם נתונים ולהחיל אותה לוגיקה, אחרת התוצאות יתפצלו ולא יהיה ברור למי להאמין.

בדיקה מעשית — להריץ אסטרטגיה אחת בשתי דרכים: פער בגבולות העלויות מאשר את נכונות מודל הביצוע, פער ניכר מצביע על שגיאה בו.

שאלות טכניות נפוצות על ליבת החישוב והנתונים

לפני ההרצה הראשונה עולים אותם ספקות: האם לשכתב הכל מחדש, האם החישובים יתאימו לשוק, האם התוצאות יחזרו על עצמן. להלן — חמש שאלות מפגישה טכנית וכיצד אנו סוגרים אותן.

האם יהיה צורך לשכתב את הקוד הקיים?

לא. אנו מפרידים את ליבת החישוב מטעינת הנתונים ותיאור האסטרטגיות באמצעות ממשק יציב: את החוקים הנוכחיים מעבירים, ומחליפים רק את צווארי הבקבוק — חישוב מחדש של אינדיקטורים ומעבר על ההיסטוריה. האסטרטגיות עצמן נשארות בצורתן הרגילה.

עד כמה החישוב יתאים לתנאים אמיתיים?

במודל העלויות אנו מכניסים עמלה, החלקה לפי ספר פקודות, עיכוב ביצוע ומילוי חלקי של הזמנות. בלי זה בקטסטינג בפייתון מראה תשואה שלא הייתה בשוק.

כיצד להבטיח שחזוריות של הרצות?

ההרצה מקובעת כולה: גרסת מערך הנתונים, פרמטרי האסטרטגיה, מצב התחלתי של מחולל המספרים האקראיים, הגדרות עלויות. חזרה כעבור חודש נותנת את אותה תוצאה — זה בסיס להשוואת השערות.

מה לעשות עם היסטוריה חסרה?

פערים וחוסר סנכרון של טווחי זמן אנו תופסים לפני החישוב: השוואה עם מקור שני, שחזור ברים, סימון מפורש של תקופות עם כיסוי נמוך. קטעים שנויים במחלוקת אנו מסמנים, ולא זורקים בשקט.

האם הליבה תעמוד בגידול במספר הכלים?

החישוב מפוצל למשימות בלתי תלויות ומתבצע במקביל על פני ליבות ומכונות, אינדיקטורים משותפים נשמרים במטמון. הוספת מאה כלים מגדילה את זמן ההרצה באופן ליניארי, ולא מפילה אותו.

מה נדרש כדי להתחיל: נדון במשימה ונחזור עם תוכנית

הערכה מדויקת של מנוע בקטסטינג נשענת לא על ניחושים, אלא על נתוני פתיחה. כל עוד לא רואים אילו תרחישים מורצים ועל איזה נפח היסטוריה, כל מועד יהיה משוער — לכן ההתחלה תמיד אחת: דיון קצר במשימה, שלאחריו אנו חוזרים עם תוכנית הטמעה ופירוט לפי שלבים.

כדי לחשב את עומס העבודה, שלחו:

  • תיאור תרחישי ההרצה — אילו אסטרטגיות נבדקות, באיזו תדירות משתנים פרמטרים, האם נדרשת סריקת קומבינציות;
  • נפח ופורמט ההיסטוריה — מספר הכלים, עומק הסדרות, מקור הנתונים ואיכותם: פערים, קרעים, אירועים תאגידיים;
  • סכימת החישוב הנוכחית — כיצד אתם מחשבים כעת: סקריפטים עצמאיים, ספרייה מוכנה או טבלאות, ומה בדיוק הפסיק להתאים;
  • דרישות לזמן הרצה מלאה — איזו משך מעבר נחשב לעבודה ובאיזו תצורה אתם מצפים לו;
  • מגבלות תשתית — היכן יתבצע החישוב: מכונות מקומיות, שרתים משלכם או ענן, האם יש דרישות לבידוד נתונים;
  • פורמט התוצאות — דוחות, מטריקות, ייצוא, האם נדרשת ויזואליזציה וממשק למערכות משיקות.

לפי נתוני פתיחה אלה אנו מרכיבים הערכת עבודה, מציעים ארכיטקטורת חישוב ומציינים מועדים. תוכנית ההטמעה מחולקת לשלבים כך שאת התוצאה של כל שלב ניתן לבדוק על הנתונים שלכם, ולא על דוגמה להדגמה.