Last Updated: 2026-10-26
Ниже — запускаемый офлайн-пример контрольного контура, а не готовый торговый бот. Он не отправляет заявки, не читает счет и не требует API-ключей. Проверяются незакрытые данные, повторные события и остановка, которая должна сохраняться после перезапуска. Нужна только стандартная библиотека Python.
Разделить обязанности до выбора площадки
Прокрутите таблицу по горизонтали, чтобы увидеть все столбцы. С клавиатуры: перейдите к области таблицы и используйте стрелки.
| Слой | Вход и выход | Что должна выяснить проверка |
|---|---|---|
| Данные | Наблюдения со временем | Закрыта ли свеча, свежа ли она и тот ли это рынок? |
| Сигнал | Версия правила и доступные данные | Был ли сигнал известен до предполагаемой цены исполнения? |
| Риск | Капитал, позиции и незавершенные заявки | Что запрещает новые действия и переживает ли запрет перезапуск? |
| Исполнение | Намерение, ID запроса и ответ | Заявка отклонена, исполнена, частична или ее состояние неизвестно? |
| Сверка | Заявки, исполнения и балансы | Совпадают ли записи с площадкой после прерывания? |
Пример охватывает только часть границы данных и риска. Сигналов и исполнения здесь нет. Пересечение средней не доказывает прибыльность; собственный код сам по себе не создает преимущество.
Запустить воспроизводимый пример
Сохраните код как paper_control.py и выполните python paper_control.py. Демонстрация создает отдельный временный файл с вымышленным состоянием. Ожидаемые статусы: recorded_only, halted, halted; orders_sent всегда равен нулю. После условного пика $1 000 значение $890 включает остановку, которую последующее значение $1 100 не снимает.
"""Offline, single-process teaching fixture. No exchange or order calls."""
import json
from decimal import Decimal, InvalidOperation
from pathlib import Path
def positive(value):
if isinstance(value, bool):
raise ValueError("boolean is not a numeric input")
try:
number = Decimal(str(value))
except (InvalidOperation, ValueError):
raise ValueError("invalid numeric input") from None
if not number.is_finite() or number <= 0:
raise ValueError("finite positive number required")
return number
def inspect(path, sample, now):
path = Path(path)
state = json.loads(path.read_text()) if path.exists() else {
"halted": False, "peak": "1000", "seen": []
}
# Corrupt/unknown saved state raises instead of silently resetting the halt.
if (type(state.get("halted")) is not bool
or not isinstance(state.get("seen"), list)
or not all(isinstance(x, str) for x in state["seen"])):
raise ValueError("invalid saved state")
peak = positive(state["peak"])
if state["halted"]:
return {"status": "halted", "orders_sent": 0}
stamp = sample.get("closed_at")
if (type(now) is not int or type(stamp) is not int
or not 0 <= now - stamp <= 10):
return {"status": "stale_or_future", "orders_sent": 0}
if sample.get("closed") is not True:
return {"status": "bar_not_closed", "orders_sent": 0}
event = sample.get("event_id")
if not isinstance(event, str) or not event:
return {"status": "missing_event_id", "orders_sent": 0}
if event in state["seen"]:
return {"status": "duplicate", "orders_sent": 0}
equity = positive(sample.get("equity"))
peak = max(peak, equity)
state["peak"] = str(peak)
# 10% is a toy boundary, not a recommended account-risk limit.
state["halted"] = equity <= peak * Decimal("0.9")
state["seen"].append(event)
temporary = path.with_name(path.name + ".pending")
temporary.write_text(json.dumps(state))
temporary.replace(path)
# A failed write raises before any accepted result is returned.
return {"status": "halted" if state["halted"] else "recorded_only",
"orders_sent": 0}
if __name__ == "__main__":
import tempfile
with tempfile.TemporaryDirectory() as folder:
saved = Path(folder) / "synthetic-state.json"
base = {"closed": True, "closed_at": 100}
print(inspect(saved, dict(base, event_id="a", equity="1000"), 101))
print(inspect(saved, dict(base, event_id="b", equity="890"), 102))
# A new call reloads the persisted halt even after equity recovers.
print(inspect(saved, dict(base, event_id="c", equity="1100"), 103))
Десять секунд свежести и граница 10% выбраны для учебного примера. Это не рекомендуемый режим или лимит риска. Следующий вызов заново читает сохраненную остановку. Демонстрационный запуск каждый раз создает новый временный каталог и не затрагивает существующие записи счета.
Проверять отказы, а не только успешный сценарий
Подайте незакрытую свечу, будущую и старую временные метки, затем один ID события дважды. Результат должен называть причину отказа. Нечисловой, бесконечный или неположительный капитал должен вызвать ошибку. Поврежденное сохраненное состояние нельзя молча превращать в новый счет. Ошибка записи файла возникает до сообщения об успешной фиксации решения.
Пример рассчитан на один процесс и одного писателя. Это не транзакционный журнал; рост файла не ограничен, устойчивость к потере питания и конкурирующим записям не установлена. Функции сброса нет. Для реальной системы снятие остановки требует отдельной сверки и записанного решения. Настройка службы Restart=always такого решения не заменяет.
Что еще требуется до интеграции исполнения
- Закрытые данные. Последняя OHLCV-свеча может быть незакрытой. Проверяйте семантику конкретного источника. Нельзя использовать конечное закрытие для предположения о более раннем исполнении по той же цене.
- Идентичность заявки. Сохраняйте намерение и клиентский ID до запроса. После тайм-аута сначала установите фактическое состояние. Тайм-аут не доказывает, что заявки нет.
- Количество и расходы. Точность, минимальное количество и минимальный номинал зависят от рынка. Шесть знаков после запятой не являются универсальным правилом. Комиссия 0,1% от $20 равна $0,02 до прочих расходов.
- Открытый риск. Завершение процесса не отменяет заявки и не закрывает позиции. Отмена тоже может иметь неопределенный результат. Частичные исполнения нужно учитывать отдельно.
- Полномочия. Ключ с торговлей без вывода все равно может причинить убыток через заявки. Разделяйте чтение и исполнение, не добавляйте секреты в статью, репозиторий или фикстуру.
- Оценка. Сохраняйте отложенные периоды, все испробованные варианты, комиссии, спред, финансирование и обработку пропусков. Тестовая сеть проверяет часть API-поведения, а не реальную ликвидность или будущую прибыль.
Фиксированное число бумажных дней не дает автоматического основания увеличивать капитал. Укажите, каких доказательств работы стратегии, интеграции и эксплуатации еще нет. Хостинг, данные, наблюдение и сопровождение могут требовать времени и денег даже при бесплатном коде. Срок выхода на прибыль здесь не обещается.
Расчеты заявок и потерь разобраны в базовом руководстве. Отдельный бумажный расчет выплат прогнозного рынка проверяет входные данные, не предсказывая исход события.
Учебный инженерный пример, не работающая торговая система, гарантия защиты счета или инвестиционная рекомендация. AITR может получать комиссии по другим коммерческим ссылкам сайта; это не подтверждает качество бота.