feat: automated futures contract rollover #1
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/futures-rollover"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
days_beforeofLastTradeDate, switch to the next contract in the same series viaFuturesNavigator.ApplyRoll, update config FIGI, recreate trader, resume.ASDegraded+Notifier. Details indocs/rollover.md.Test plan
direnv exec . go test ./...direnv exec . golangci-lint runrollovernear expiry, confirm flat switch and 1:1 roll with seeded quotesРевью MR #1:
feat: automated futures contract rollover1. RequestID — случайный UUID, идемпотентность не работает через рестарты
orderExecutor.place()генерирует свежийuuid.NewString()какRequestIDпри каждом вызове. Если процесс упадёт междуPostOrderи обработкой ответа, после перезапуска rollover запуститplace()с новым UUID — и создаст дублирующий ордер на бирже.Фактический риск невысокий (окно узкое, трейдер suspended), но если хочется идеальной идемпотентности —
RequestIDдолжен выводиться из детерминированных входных данных (FIGI + direction + lots + kind). Например, хеш от этих полей.2.
contractSwitcher.Switch— контекст может быть отменёнВ цепочке
evaluate → roll → finalize → Switchиспользуется родительскийctx. Если во время rollover придётSIGHUP(hot-reload) илиSIGTERM, контекст отменится, иupdateTraderFIGI/recreateTraderмогут не успеть сохранить конфиг с новым FIGI. Рекомендую использоватьcontext.WithoutCancel(ctx)или отдельныйcontext.Background()для операций записи конфига.3.
futuresCatalogTTL = 24h— может пропустить свежий контракт в день проверкиЕсли каталог загружен, а Tinkoff добавил новый контракт в течение дня,
nextCheckDelayотложит проверку до завтра, а каталог обновится только через 24ч от первой загрузки. Предлагаю форсировать инвалидацию каталога при каждом успешном ежедневном чеке вevaluate(), а не полагаться только на TTL.4.
PairCandleQuotes— map поtime.Timeможет не сматчитьсяnextByTimeиспользуетtime.Timeкак ключ map. Ты нормализуешь через.UTC(), но уHistoricCandleиз Tinkoff время может быть с наносекундами или без. Явное округление до минут/интервала не помешало бы для надёжности.5.
TestFuturesCatalogTTLмутирует shared stateТест форсирует инвалидацию через прямое изменение
info.futuresCache.loadedAt. Хотя тесты по умолчанию не параллелят пакеты, модификация shared глобальногоsharedFuturesCatalogможет проявиться приgo test -count=1 ./...в соседних тестах. Рекомендую сохранять/восстанавливать состояние каталога (или обернуть в setup/teardown).6.
normalizeRolloverConfigs—days_before: 0становится дефолтомЕсли в конфиге явно указано
days_before: 0, нормализация превращает его в3. Стоит иметь в виду — intentional zero не поддерживается. Можно через указатель если когда-нибудь понадобится.В целом архитектура чистая, тесты отличные, документация есть. Ничего критического. Мержить можно, замечания опциональны.
По замечаниям из ревью:
#2 (ctx cancel на Switch) — починено:
context.WithoutCancelвContractSwitcher(wire) и вfinalize/switchAndResume, чтобы запись конфига, recreate и resume не обрывались на SIGTERM/SIGHUP mid-roll.#4 (PairCandleQuotes) — оказалось мёртвым кодом после упрощения (history больше не используется сервисом). Убрано вместе с
PairedQuoteHistory,PairedTopOfBookиWithDeferredReady.#1, #3, #5, #6 — оставляем как опциональные follow-up (idempotent RequestID, invalidate futures catalog, тест TTL,
days_before: 0).Пуш:
refactor(rollover): drop unused quote history and harden switch ctx.