tgoop.com/AspiringDataScience/2705
Last Update:
#trading #backtesting #bestpractices #tradinggems
Узнал сегодня что-то новое.
Пилю бэктест торговой стратегии, он показывает плюс на истории, я доволен. Вдруг думаю, дай-ка я последую собственному умному совету, который я даю, когда разбираю чужие решения:
do a system/backtesting framework sanity check. ensure:
1) random entry/exit signals produce very bad trading metrics (PnL, PF, Sharpe)
2) faked perfect entry/exit signals produce amazing trading metrics (PnL, PF, Sharpe)
3) your real trading policy on valid data is somewhere in the middle
Конечно, это касается не только одного отдельно взятого бэктеста, эти же условия должны быть справедливы и для оптимизатора.
Если ваш оптимизатор торговой политики (основанный на бэктестере) находит "прибыльные политики" на случайных сигналах либо случайных рыночных данных, шерше ля ошибку в его реализации.
К моему удивлению, я получил прибыли на случайных сигналах. И залип разбираться, что же я наговновайбкодил.
Выяснилось следуюющее:
Стратегия на барах и лимитных ордерах (для контроля проскальзывания), решение об изменении позиции принимается в конце текущего бара.
Я хотел открывать/закрывать позицию в течение следующего бара по цене не сильно хуже чем цена close последнего известного бара.
Если же такая не была доступна на рынке, я предпочитал позицию не менять, до следующего сигнала.
Покажу проблему на примере закрытия позиции. ИИ транслировал мои пожелания вот в такой невинный код
expected_fill_price=prev_close*(1+slippage_pct*np.sign(current_position)) # для реалистичности закладываем проскальзывание
close_ok= l<= expected_fill_price <=h
if close_ok:
#здесь закрываем позу
else:
# сохраняем позу
Получив плюсовой бэктест на случайности, я сразу подумал о какой-то утечке инфы из будущего, а т.к. эти проверки диапазонов были единственной инфой из будущего, с которой я работал, стал над ними думать серьёзнее.
Ну и действительно, такая небрежность в принятии решения, когда всё делается красивым универсальнынм однострочником close_ok= l<= expected_fill_price <=h, приводила к тому, что случайные позиции быстро закрывались с небольшими убытками, но оставались открытыми для "хороших баров", причём факт хорошести брался из будущего.
Вроде бы стремление быть оверконсервативным в оценках финрезультата похвальное, а приводило к такому провалу.
Я понял, что если консервативная цена не достижима, я ведь рад закрыть позицию по лучшей цене.
Вот какой код был правильным:
# close_ok= l<= expected_fill_price <=h leads to a fatal future leak, so that even random backtest becomes positive!
if pos_units[tk] > 0: # Closing long (sell limit)
close_ok = h >= expected_fill_price # Fill if market reaches limit or better (higher prices)
expected_fill_price = max(expected_fill_price, l) # Conservative: lowest price in range >= limit (worst PNL for seller)
else: # Closing short (buy limit)
close_ok = l <= expected_fill_price # Fill if market reaches limit or better (lower prices)
expected_fill_price = min(expected_fill_price, h) # Conservative: highest price in range <= limit (worst PNL for buyer)
С такими правками (симметрично для открытия позы) бэктесты на случайности пошли ожидаемо провальные.
Что интересно, ИИ (не платный) оказался не способен найти верное решение.
Будьте внимательны, калибруйтесь и делайте "защиту от дурака", в общем. От себя то есть ) Часто помогает.
BY Aspiring Data Science
Share with your friend now:
tgoop.com/AspiringDataScience/2705