Ми перезапустили паперову стратегію посеред угоди й отримали іншу послідовність ордерів, ніж до перезапуску. Ті самі ринкові дані, та сама версія стратегії, той самий баланс рахунку. Розрахунок сигналу збігався. А от пам’ять стратегії про позицію та незавершені ордери — ні.
Ми простежили розбіжність протягом дня відтворень. Висновок простий: для вашого програмного забезпечення перезапуск — це ринкова подія. Якщо не перевірити, як стратегія відновлює свій стан, чистий бектест може приховати, що паперова система забуває, чим володіє.
09:10 — Ми обрали нічим не примітну позицію
Тестова стратегія торгувала ліквідним безстроковим контрактом: відкривала довгу позицію, коли коротша ковзна середня перетинала довшу знизу вгору, і закривала її при зворотному перетині. Ми почали відтворення з уже відкритою невеликою позицією та виставленим лімітним ордером reduce-only для її скорочення. Так відновленню треба було відтворити два факти: чим ми володіли і що вже попросили майданчик зробити.
На контрольному етапі на рахунку було 0.04 контракту. Ордер на 0.01 залишався відкритим. Процес стратегії кешував обидва значення в пам’яті, але під час запуску завантажував лише позицію. Він вважав, що кешованого ордера більше немає.
09:25 — З’явився перший дублікат ордера
Після перезапуску стратегія побачила наявну довгу позицію, виконала логіку сигналу й надіслала ще один ордер на скорочення на 0.01. Тепер на паперовому майданчику було два активні ордери. Кожен окремо був цілком коректним. Разом вони могли продати вдвічі більше, ніж планувалося, якби спрацювали обидва.
Спершу ми звинуватили цикл сигналів. Але проблема була не в циклі, а в неповному знімку стану. Стратегія запитала: «Яка в мене позиція?», але не запитала: «Які ордери досі активні?»
| Стан після перезапуску | Що вважав процес | Що було на рахунку |
|---|---|---|
| Позиція | Довга 0.04 | Довга 0.04 |
| Відкриті ордери на скорочення | Немає | Два по 0.01 кожен |
| Запланована експозиція після виконання одного ордера | Довга 0.03 | Могла стати довгою 0.02 |
10:00 — Ми виправили відновлення, а тоді знайшли проблему з часом подій
Ми змінили процес запуску: перш ніж дозволяти нові рішення, він мав відновити стан за позиціями рахунку та відкритими ордерами. Дублікат зник. Потім ми ускладнили сценарій відключення: один ордер виконався, поки стратегія була офлайн, а сповіщення про виконання надійшло вже після перепідключення.
Знімок рахунку вже враховував виконання. Коли ж надійшло затримане сповіщення, локальна позиція зменшилася вдруге. Кілька секунд стратегія вважала, що має 0.02 контракту, хоча на рахунку було 0.03. Наступне ребалансування ґрунтувалося на уявній нестачі позиції.
Ми додали правила звірки: брати знімок рахунку за початковий стан, за ідентифікаторами подій ігнорувати виконання, уже враховані в ньому, і не надсилати ордери, доки не завершиться початкова синхронізація. Сповіщення може надійти із запізненням або двічі. Відновлення має витримувати обидва випадки.
13:40 — Відтворення виявило непомітну розбіжність
Ми відтворили той самий рух ціни для початкового запуску й запуску після перезапуску. Порівняння кінцевого P&L не виявило б проблеми: після розвороту ринку обидві версії завершили з однаковою позицією. Розбіжність стала помітною лише під час порівняння подій ордерів.
Ми журналювали кожне рішення разом зі станом, на якому воно ґрунтувалося: позиція, відкриті ордери, ідентифікатор останнього обробленого виконання, значення сигналу та версія стратегії. Тоді ми змогли пояснити першу розбіжність. В одному запуску стратегія бачила активний ордер, а в іншому — порожній список. Пізніше один запуск двічі застосував одне й те саме виконання.
Однаковий кінцевий баланс не доводить, що поведінка збігалася. Порівнюйте послідовність рішень і ордерів, особливо під час відновлення.
16:20 — Що ми зробили б інакше наступного разу
Ми надто довго відтворювали дані цін, перш ніж перевірити, як змінювався стан рахунку. Наступного разу спершу відтворили б сценарії з помилками, залишивши ринкову траєкторію майже рівною. Так програмну помилку легше помітити, а волатильний рух не заплутуватиме діагностику.
- Перезапустити стратегію з відкритою позицією та частково виконаним ордером.
- Перервати з’єднання після надсилання ордера, а потім під’єднатися знову до надходження сповіщення про виконання.
- Двічі доставити ту саму подію виконання й перевірити, що стан зміниться лише один раз.
- Заблокувати нові ордери, доки не буде звірено позиції та відкриті ордери.
- Порівняти журнали рішень і ордерів для безперервного запуску та запуску після перезапуску.
Ми також зрозуміли, що варто зберігати знімок стану для відновлення разом із версією стратегії та журналом подій. Завдяки цьому помилку можна було відтворити за лічені хвилини, а не покладатися на те, що хтось точно згадає послідовність перепідключення.
Паперова стратегія, яка працює правильно лише поки її процес не зупиняється, ще не пройшла повну репетицію. Перезапустіть її посеред позиції, затримайте сповіщення про виконання й перевірте кожен ордер, який вона надішле після цього. Мета не в тому, щоб довести, ніби збоїв ніколи не буде. Треба побачити, як саме вона відновлюється, перш ніж паперовий рахунок зненацька викладе вам той самий урок.
← Усі статті


