Am repornit o strategie paper la jumătatea unei tranzacții și am obținut o secvență de ordine diferită de cea produsă înainte de repornire. Aceleași date de piață, aceeași versiune a strategiei, același sold al contului. Calculul semnalului a fost identic. Memoria strategiei despre poziție și ordinele în așteptare, nu.
Am urmărit cauza diferenței pe parcursul unei zile de replay. Lecția utilă a fost simplă: pentru software-ul tău, repornirea este un eveniment de piață. Dacă nu testezi cum își reconstruiește strategia starea, un backtest curat poate ascunde un sistem paper care uită ce deține.
09:10 — Am ales o poziție banală
Strategia de test tranzacționa un contract perpetual lichid: intra long când media mobilă pe termen scurt trecea peste cea pe termen lung, apoi ieșea la încrucișarea inversă. Am început replay-ul cu o poziție mică deja deschisă și un ordin limit reduce-only activ, pentru a o reduce. Astfel, procesul de recuperare trebuia să reconstruiască 2 lucruri: ce dețineam și ce îi ceruserăm deja bursei să facă.
La punctul de control, contul deținea 0.04 contracte. Ordinul activ era pentru 0.01. Procesul strategiei păstrase ambele valori în cache, însă la pornire prelua doar poziția. A presupus că ordinul din cache dispăruse.
09:25 — A apărut primul ordin duplicat
După repornire, strategia a detectat poziția long existentă, și-a executat logica de semnal și a trimis încă un ordin de reducere de 0.01. Acum, platforma paper avea 2 ordine active. Luat separat, niciunul nu era un ordin greșit. Împreună, puteau vinde de 2 ori cantitatea intenționată dacă ambele erau executate.
La început am dat vina pe bucla de semnal. Nu bucla era problema, ci instantaneul incomplet. Strategia a întrebat: „Ce poziție am?”, dar nu și: „Ce ordine sunt încă active?”
| Starea după repornire | Ce credea procesul | Ce deținea contul |
|---|---|---|
| Poziție | Long 0.04 | Long 0.04 |
| Ordine de reducere active | Niciunul | 2 ordine de câte 0.01 |
| Expunerea intenționată după executarea unui ordin | Long 0.03 | Ar putea ajunge la long 0.02 |
10:00 — Am reparat recuperarea, apoi am descoperit o problemă de sincronizare
Am modificat procesul de pornire ca să reconstruiască starea din pozițiile contului și ordinele active înainte de a permite noi decizii. Asta a eliminat duplicatul. Apoi am făcut deconectarea mai puțin ordonată: un ordin a fost executat cât timp strategia era offline, iar notificarea execuției a sosit după reconectare.
Instantaneul contului reflecta deja execuția. Notificarea întârziată a redus apoi poziția locală încă o dată. Timp de câteva secunde, strategia a crezut că deține 0.02 contracte, deși contul deținea 0.03. Următoarea reechilibrare s-a bazat pe un deficit imaginar.
Am adăugat reguli de reconciliere: folosim instantaneul contului ca punct de pornire, ignorăm prin identificatorii evenimentelor execuțiile deja reflectate acolo și nu trimitem ordine până la încheierea sincronizării inițiale. O notificare poate sosi târziu sau de 2 ori. Procesul de recuperare trebuie să le gestioneze pe ambele situații.
13:40 — Replay-ul a scos la iveală o diferență greu de observat
Am rulat același parcurs al prețului atât pentru execuția inițială, cât și pentru cea de după repornire. Compararea P&L-ului final nu ar fi scos problema la iveală: cele 2 versiuni au încheiat cu aceeași poziție după inversarea pieței. Diferența s-a văzut când am comparat evenimentele asociate ordinelor.
Am înregistrat fiecare decizie împreună cu starea folosită: poziția, ordinele active, identificatorul ultimei execuții procesate, valoarea semnalului și versiunea strategiei. Astfel, am putut explica primul eveniment divergent. Într-o execuție exista un ordin activ; în cealaltă, lista era goală. Mai târziu, într-una dintre ele, o execuție a fost aplicată de 2 ori.
Un sold final identic nu dovedește că și comportamentul a fost identic. Compară secvența deciziilor și a ordinelor, mai ales în jurul etapelor de recuperare.
16:20 — Ce am face diferit data viitoare
Am petrecut prea mult timp reluând datele de preț înainte să verificăm tranzițiile de stare ale contului. Data viitoare, am injecta mai întâi cazurile de eroare și am menține parcursul pieței aproape plat. Astfel, eroarea software-ului iese ușor în evidență, fără ca o mișcare volatilă să încurce diagnosticul.
- Repornește cu o poziție și un ordin executat parțial.
- Deconectează-te după trimiterea ordinului, apoi reconectează-te înainte să sosească notificarea execuției.
- Livrează de 2 ori același eveniment de execuție și verifică dacă schimbă starea o singură dată.
- Blochează ordinele noi până când sunt reconciliate atât pozițiile, cât și ordinele active.
- Compară jurnalele deciziilor și ordinelor din execuțiile continue cu cele din execuțiile repornite.
Am învățat și să salvăm un instantaneu de recuperare alături de versiunea strategiei și jurnalul de evenimente. Astfel, am putut reproduce eroarea în câteva minute, fără să depindem de memoria cuiva despre secvența exactă de reconectare.
O strategie paper care se comportă corect doar cât timp procesul ei rămâne activ nu a trecut printr-o repetiție completă. Repornește-o cu o poziție deschisă, fă execuțiile să sosească târziu și inspectează fiecare ordin trimis ulterior. Scopul nu este să demonstrezi că nu va eșua niciodată. Scopul este să-i faci procesul de recuperare vizibil înainte ca un cont paper să te învețe aceeași lecție pe neașteptate.
← Toate articolele


