Избранное трейдера OnlyHuman
Всех приветствую!
Первый год публичной алго торговли закончился с результатом +100%.
Первый пост о моем пути к алготрейдингу тут
В этом посте подробно разберу результаты за прошлый год, а также попытаюсь ответить на вопрос – одурачен ли я случайностью?
На рисунке изменение депозита и фьючерса долл./руб.

Все системы торговали на фьючерсе долл./руб. Примерно 75% систем работают на волатильности, остальные пытаются поймать тренд. В начале года затишье, которое к концу марта привело к просадке в 30%. Ну а дальше роботы оседлали взрыв рынка. 8 августа вывел 10% от первоначального депо, в этот же период был удержан НДФЛ на всю сумму накопившегося дохода.
Красным цветом выделил зоны, где алгоритмы не смогли заработать на волатильности. То есть движения были, но они были «плохими». В эти периоды дневные свечи имели большие тени как с верху, так и снизу. Поэтому, не смотря на хорошую волатильность их возило по стопам. Зеленые зоны – экстремально низкая волатильность и сильные просадки.

Settings={
Name="STATDIV3",
period=50,
line=
{
{
Name="curve",
Color=RGB(0,0,255),
Type=TYPE_LINE,
Width=1
},
{
Name="line",
Color=RGB(255,0,0),
Type=TYPE_LINE,
Width=1
},
{
Name="MA",
Color=RGB(0,0,255),
Type=TYPE_LINE,
Width=1
},
{
Name="MA2",
Color=RGB(0,128,128),
Type=TYPE_LINE,
Width=1
},
{
Name="line2",
Color=RGB(0,0,255),
Type=TYPE_LINE,
Width=1
},
{
Name="line3",
Color=RGB(0,128,128),
Type=TYPE_LINE,
Width=1
}
}
}
function Init()
cache_ind={}
cache_ind2={}
cache_ind3={}
return 2
end
function OnCalculate(index)
if index < Settings.period then
return nil
else
local sum1=0
local sum2=0
local sum0=0
local sum02=0
local sum03=0
for i=index-Settings.period+1, index do
do
if C(i) > O(i) then
sum1 = sum1 + C(i) - O(i)
sum2 = sum2 + C(i) - O(i)
else
sum2 = sum2 + O(i) - C(i)
end
end
cache_ind[index] = sum1/sum2
if index > Settings.period+12 then
--[[
sum0 = 1*cache_ind[index]+
(1)*cache_ind[index-1]+
(1)*cache_ind[index-2]+
(1)*cache_ind[index-3]+
(1)*cache_ind[index-4]+
(1)*cache_ind[index-5]+
(1)*cache_ind[index-6]+
(1)*cache_ind[index-7]+
(1)*cache_ind[index-8]+
(1/2)*cache_ind[index-9]+
(1/3)*cache_ind[index-10]+
(1/4)*cache_ind[index-11]+
(1/5)*cache_ind[index-12]
--]]
sum0 = 1*cache_ind[index]+
(1/2)*cache_ind[index-1]+
(1/3)*cache_ind[index-2]+
(1/4)*cache_ind[index-3]+
(1/5)*cache_ind[index-4]+
(1/6)*cache_ind[index-5]+
(1/7)*cache_ind[index-6]+
(1/8)*cache_ind[index-7]+
(1/9)*cache_ind[index-8]+
(1/10)*cache_ind[index-9]+
(1/11)*cache_ind[index-10]+
(1/12)*cache_ind[index-11]+
(1/13)*cache_ind[index-12]
end
--[[
sum0 = sum0/(1+1+1+1+1+1+1+1+1+1/2+1/3+1/4+1/5)
--]]
sum0 = sum0/(1+1/2+1/3+1/4+1/5+1/6+1/7+1/8+1/9+1/10+1/11+1/12+1/13)
cache_ind2[index] = sum0
if index > Settings.period+50 then
sum02 = 1*cache_ind2[index]+
(1)*cache_ind2[index-1]+
(1)*cache_ind2[index-2]+
(1)*cache_ind2[index-3]+
(1)*cache_ind2[index-4]+
(1)*cache_ind2[index-5]+
(1)*cache_ind2[index-6]+
(1)*cache_ind2[index-7]+
(1/2)*cache_ind2[index-8]+
(1/3)*cache_ind2[index-9]+
(1/4)*cache_ind2[index-10]+
(1/5)*cache_ind2[index-11]+
(1/6)*cache_ind2[index-12]
--[[
sum02 = 1*cache_ind2[index]+
(1/2)*cache_ind2[index-1]+
(1/3)*cache_ind2[index-2]+
(1/4)*cache_ind2[index-3]+
(1/5)*cache_ind2[index-4]+
(1/6)*cache_ind2[index-5]+
(1/7)*cache_ind2[index-6]+
(1/8)*cache_ind2[index-7]+
(1/9)*cache_ind2[index-8]+
(1/10)*cache_ind2[index-9]+
(1/11)*cache_ind2[index-10]+
(1/12)*cache_ind2[index-11]+
(1/13)*cache_ind2[index-12]
--]]
end
sum02 = sum02/(1+1+1+1+1+1+1+1+1/2+1/3+1/4+1/5+1/6)
--[[
sum02 = sum02/(1+1/2+1/3+1/4+1/5+1/6+1/7+1/8+1/9+1/10+1/11+1/12+1/13)
--]]
cache_ind3[index] = sum0 - sum02
if index > Settings.period+50 then
sum03 = 1*cache_ind3[index]+
(1/2)*cache_ind3[index-1]+
(1/3)*cache_ind3[index-2]+
(1/4)*cache_ind3[index-3]+
(1/5)*cache_ind3[index-4]+
(1/6)*cache_ind3[index-5]+
(1/7)*cache_ind3[index-6]+
(1/8)*cache_ind3[index-7]+
(1/9)*cache_ind3[index-8]+
(1/10)*cache_ind3[index-9]+
(1/11)*cache_ind3[index-10]+
(1/12)*cache_ind3[index-11]+
(1/13)*cache_ind3[index-12]
end
sum03 = sum03/(1+1/2+1/3+1/4+1/5+1/6+1/7+1/8+1/9+1/10+1/11+1/12+1/13)
end
if sum1/sum2 > 0.5 and sum03 > 0 then
sum1 = sum03
else
if sum1/sum2 < 0.5 and sum03 < 0 then
sum1 = sum03
else
sum1 = 0
end
end
return sum1, 0
end
end
У хорошего мастера должны быть хорошие инструменты – у кузнеца – молот, у столяра – рубанок, у алготрейдера… — оптимизатор.
Выкатил новую версию.
Настоящий мужской оптимизатор конечно же должен быть консольным. Этот красавчик быстр – 10 лет 5-минуток на простой стратегии вместе с вычислением всяких там PF, RF 0,3 секунды. И это на одном потоке! (с многопоточностью, к слову, пока не смог подружить, но заложил такую возможность).
Бэктестер берет задания из csv файла и пашет. Т.е. на данный момент задания на оптимизацию задаются в момент создания файла с заданиями, решил поменять план оптимизации – меняю файл – меняется дальнейшая оптимизация. Т.е. по факту сейчас план на оптимизацию предустановленный, но легко прикрутить в дальнейшем оптимизацию с обратной связью на результаты предыдущих бэктестов. Меня всегда смущали стандартные оптимизаторы в этой части – где перебирается один параметр, или несколько строго итерационно, но я не мог задать явно другие алгоритмы перебора или в общем случае даже не перебора, а «изменения» значений. А здесь могу: т.е., могу за раз закинуть задания сразу на нужное количество гипотез, хочу посмотреть, как стратегия себя ведет между тикерам – не трогаю ничего, меняю только тикер, хочу проверить как ведет себя между тайм-фреймами – меняю только тайм-фреймы и т.д., т.е. минут за 5-10 во всемогущем экселе можно создать файл с заданиями для нужного набора гипотез. Потом когда бэктестер отработает – берешь эксельку и дата-майнишь данные.
Все-таки когда ты точно знаешь, чего хочешь, свой бэктестер это кайф! Нужна скорость, но не нужна какая-то функциональность – супер, не делаешь тормозящую функциональность, получая преимущество в скорости, интересует какая-то конкретная парадигма в оптимизации – отлично, пилишь архитектуру именно под парадигму, без оглядки на стандартные «лучшие практики», «модные мнения» и прочие «а вот я так делаю, а ты какую-то херню».
Более менее ООПшная прога получилась, так что есть надежда, что можно мяса при необходимости накрутить с контролируемым уровнем сложности и поддерживаемости.
Если вдруг кто-то собирался завидовать – не надо, вам бы не понравился мой оптимизатор. Думаю, он будет нравиться только мне! :)
Если у вас есть проблемы с определением потенциального разворота рынка, тогда этот урок для вас. Ценовой паттерн Pivot Point Reversal — это один из тех сигналов, которые используют профессиональные трейдеры для нахождения точек разворота тренда. Никакие индикаторы в данном методе не используются. В данном уроке упоминается внутренний бар, о котором я уже писал руководство - https://smart-lab.ru/blog/518242.php.
Что такое Pivot Point Reversal?Pivot Point Reversal — это разворотный ценовой паттерн, который состоит из трёх и более свечей, и сделка открывается в направлении пробоя поддержки или сопротивления главной свечи: для медвежьего сетапа ожидаем пробоя поддержки, для бычьего сетапа ожидаем пробоя сопротивления.
--Массив с Тикерами, добавьте нужные тикеры
aTickerList = {"MSNG", "GAZP", "LKOH",
"SIBN", "GMKN","ROSN",
"SBER", "TATN", "NVTK",
"IRAO", "RSTI", "SBERP",
"PHOR", "SNGS", "TRNFP",
"VTBR", "FEES", "MVID",
"RASP", "MFON", "AFLT",
"MAGN", "ALRS", "MTSS", "MOEX",
"RTKM", "MGNT", "NLMK", "SNGSP",
"CHMF", "MTLR", "HYDR", "MFON",
"RSTI", "PLZL", "BANEP", "POLY"
};
--Функция поиска цены
function fGetPrice(sTickerName, sNum)
--Подключаемся к источнику данных
local ds=CreateDataSource("TQBR", sTickerName, INTERVAL_D1);
while (Error=="" or Error == nil) and ds:Size() ==0 do sleep(10) end;
if Error ~="" and Error ~=nil then message("Error: "..Error, 1) end;
local sSize=ds:Size();
local sCurrentPrice=ds:O(sSize);
local sLastWeekPrice7=0;
local sLastWeekPrice14=0;
--Берем цену закрытия свечи неделю назад
sLastWeekPrice7=ds:C(sSize-4);
--Берем цену закрытия свечи 2 недели назад
sLastWeekPrice14=ds:C(sSize-8);
--Вычисляем проценты
local sPrc7=math.floor((100-((sLastWeekPrice7*100)/sCurrentPrice))*100)/100;
local sPrc14=math.floor((100-((sLastWeekPrice14*100)/sCurrentPrice))*100)/100;
--Заполняем таблицу значениями
SetCell(t_id, sNum, 0, tostring(sTickerName));
SetCell(t_id, sNum, 1, tostring(sCurrentPrice),sCurrentPrice);
SetCell(t_id, sNum, 2, tostring(sLastWeekPrice7),sLastWeekPrice7);
SetCell(t_id, sNum, 3, tostring(sLastWeekPrice14),sLastWeekPrice14);
SetCell(t_id, sNum, 4, tostring(sPrc7),sPrc7);
SetCell(t_id, sNum, 5, tostring(sPrc14),sPrc14);
--Текущая цена больше цены прошлой недели - раскрашиваем зеленым
if sCurrentPrice>sLastWeekPrice7 then
fGreen(sNum);
end;
--Текущая цена меньше цены прошлой недели - раскрашиваем красным
if sCurrentPrice<sLastWeekPrice7 then
fRed(sNum);
end;
--Текущая цена больше цены прошлой недели и цена прошлой недели больше цены позапрошлой недели
--раскрашиваем желтым
if sCurrentPrice>sLastWeekPrice7 and sLastWeekPrice7>sLastWeekPrice14 then
fYellow(sNum);
end;
end;
--- Функция создает таблицу
function CreateTable()
-- Получает доступный id для создания
t_id = AllocTable();
-- Добавляет 6 колонок
AddColumn(t_id, 0, "Тикер", true, QTABLE_INT_TYPE, 15);
AddColumn(t_id, 1, "Сегодня", true, QTABLE_INT_TYPE, 15);
AddColumn(t_id, 2, "Неделя", true, QTABLE_INT_TYPE, 15);
AddColumn(t_id, 3, "2 Недели", true, QTABLE_INT_TYPE, 15);
AddColumn(t_id, 4, "Неделя (%)", true, QTABLE_INT_TYPE, 15);
AddColumn(t_id, 5, "2 Недели (%)", true, QTABLE_INT_TYPE, 15);
-- Создаем
t = CreateWindow(t_id);
-- Даем заголовок
SetWindowCaption(t_id, "7 Days");
-- Добавляем строки
for k,v in pairs(aTickerList) do
InsertRow(t_id, k);
end;
end;
--- Функции раскрашивают ячейки таблицы
function fRed(col)
SetColor(t_id, col, -1, RGB(255,168,164), RGB(0,0,0), RGB(255,168,164), RGB(0,0,0));
end;
function fGreen(col)
SetColor(t_id, col, -1, RGB(157,241,163), RGB(0,0,0), RGB(157,241,163), RGB(0,0,0));
end;
function fYellow(col)
SetColor(t_id, col, -1, RGB(249,247,172), RGB(0,0,0), RGB(249,247,172), RGB(0,0,0));
end;
--Основная функция
function main()
-- Создаем таблицу
CreateTable();
--Пробегаемся по массиву тикеров
for k,v in pairs(aTickerList) do
fGetPrice(v, k);
end;
end;как выглядит в квике:Добрый день.
Пригласили выступить спикером на Московской опционной конференции.
mok.derex.ru/program/
Тему доклада взял участия в конкурсе ЛЧИ.
Расскажу про стратегию, которую применял «Железный кондор», а также свои наработки, которые применяю повседневно в своей практике.
smart-lab.ru/blog/511421.php
Приходите, познакомимся. Живое общение будет куда лучше, виртуального.