Хорош философствовать. Давайте писать более полезные посты.
Итак, робот на двух графиках Боллинджера.
Общий принцип:
1) На цену накладываются два графика Боллинджера: с периодами 20 и 120 (назовем их local и global).
2) В зависимости от параметра внутри робота, входим либо когда цена входит внутрь local-Боллинджера (ContrTrendFlag=1), либо выходит из него (ContrTrendFlag=0).
3) Дополнительный фильтр: Лонг только когда когда мы в верхней половине global-Боллинджера, шорт — если в нижней.
Данные робот берет из графиков, так что график должен быть открыт, и прописаны идентификаторы.
График с двумя Боллинджерами выглядит примерно так:
Настройки на цене и индикаторах не забудьте:
Прям вот так поставить и тут же выкачивать милионы — не обещаю, но для примера — вэлкам, пользуйтесь, код ниже :)
А говнокодерам типа какбыгобота — пример, как нужно снабжать код комментариями.
p.s. В ДУ не беру, семинаров не веду, на заказ скрипты не пишу, каналов нет.
Просто для хоть какой-то пользы. А то превратился ресурс х.з. во что.
Ну и, исходный текст:
-- Два таймфрейма: Global (например H1) и Local (M5).
-- Вход Лонг: цена выще MA20, в момент пересечения противоположного Боллинджера (M5).
-- Шорт - наоборот.
-- Заточено под фьючи. Можно перестроить на акции, но не пробовал.
-- Сокращения ниже:
-- ВВ = боллинджер;
-- ЦПС = Цена Последней Сделки.
-- Настройка:
-- Открываем два графика торгуемого инструмента.
-- По умолчанию - H1 и М5.
-- Настраиваем идентификаторы на цену и индикаторы.
-- PREFIX+"_Price" - на цену на младшем таймфрейме.
-- PREFIX+"_BB_Global" - на Боллинджера на СТАРШЕМ таймфрейме.
-- PREFIX+"_BB_Local" - на Боллинджера на МЛАДШЕМ таймфрейме.
-- Кстати, можно использовать один график М5, и наложить на него двух Боллинджеров.
-- Один будет с периодом 20, второй 120. Это примерно будет соответствовать двум графикам.
-- Включаем робота.
-- Ждем профита. Вряд ли дождемся, но всё таки.
CLASS_CODE = "SPBFUT" -- или TQBR для акций
SEC_CODE = "SRM9" -- инструмент
PREFIX = "SBER" -- префикс для получения данных с графиков. PREFIX+"_Price" - цена на младшем таймфрейме,
-- PREFIX+"_BB_Global" и PREFIX+"_BB_Local" - Старший и младший Боллинджер.
-- Префикс прописыватся на вкладке "Дополнительно" в графиках.
ACCOUNT_CODE = "сюды пиши своё"
CLIENT_CODE = "и сюды тоже"
LogFileName = "C:\\1_log.txt"
SL = "50" -- Стоп-лосс.
TP = "55" -- Включение трейлинга.
TRAIL = "50" -- кол-во пунктов отступа при трейлинге прибыли
-- TP=55, TRAIL=5, то есть коснется +55, значит уже как минимум 50 взято, скорее всего сразу закроет.
-- TP=55, TRAIL=50, то есть коснется +55, значит +5 наше, запас хода 50 пунктов, еще покачается.
LotN = "1" -- количство лотов.
SleepDuration = 10 -- СЕКУНД на паузу. По умолчанию ставлю 10 сек.
-- Робот не ждет закрытия свечи, а открывает сделку в момент пересечения. Точнее, не в момент,
-- а раз в SleepDuration/1000 секунд.
ContrTrendFlag = 0 -- если = 0, то сделка открывается в момент вылета за Боллинджера
-- младшего таймфрейма.
-- А если = 1, то когда происходит вход внутрь канала Боллинджера на младшем таймфрейме.
-- Получается, что сделка открывается когда мы в тренде по
-- по старшему таймфрейму, и в контртренде по младшему.
is_run=true
function DoFire(p_price, p_dir) -- "B" or "S" -- СДЕЛКА ПО РЫНКУ!!!
WLOG("DoFire. Start. p_dir="..p_dir..". p_price="..p_price)
-- Здесь - три вспомогательных флага направления. Чтобы не писать отдельно для Лонг и Шорт.
if p_dir == "B" then AAA = 1 else AAA = -1 end
if p_dir == "B" then BBB = "S" else BBB = "B" end
if p_dir == "B" then CCC = "4" else CCC = "5" end
-- Готовим транзакцию для сделки.
t = {
["CLASSCODE"]=CLASS_CODE,
["SECCODE"]=SEC_CODE,
["ACTION"]="NEW_ORDER",
["ACCOUNT"]=ACCOUNT_CODE,
["CLIENT_CODE"]=CLIENT_CODE,
["TYPE"]="M", -- или "L" если отложка.
["OPERATION"]=p_dir,
["QUANTITY"]=tostring(LotN),
["PRICE"]=tostring(p_price+(20*AAA)),
["TRANS_ID"]="1"
}
res1 = sendTransaction(t) -- передаем сделку по рынку.
WLOG("Результат сделки по рынку (должно быть пусто) = '"..res1.."'")
if (res1=="") then -- если нормально открылись, то ставим стоп+трейл
-- Готовим транзакцию для Стопа.
t_stop =
{
['ACTION'] = "NEW_STOP_ORDER",
['PRICE'] = tostring(p_price-(100*AAA)), -- меньше, проскальзывание
['EXPIRY_DATE'] = "GTC",
['STOPPRICE'] = tostring(p_price+(TP*AAA)), -- тейк
['STOPPRICE2'] = tostring(p_price-(SL*AAA)), -- больше, срабатывание стопа
['STOP_ORDER_KIND'] = "TAKE_PROFIT_AND_STOP_LIMIT_ORDER",
['OFFSET'] = tostring(TRAIL),
["OFFSET_UNITS"] = "PRICE_UNITS",
["MARKET_TAKE_PROFIT"] = "YES",
['TRANS_ID'] = "2",
['CLASSCODE'] = CLASS_CODE,
['SECCODE'] = SEC_CODE,
['ACCOUNT'] = ACCOUNT_CODE,
['CLIENT_CODE'] = CLIENT_CODE,
['TYPE'] = "L", -- лимитка
['OPERATION'] = BBB, -- направление стопа (обратное к сделке).
['CONDITION'] = tostring(CCC), -- 4 или 5 ("меньше или равно" или "больше или равно") - направление стоп-цены.
['QUANTITY'] = tostring(LotN) -- кол-во контрактов
}
res2 = sendTransaction(t_stop)
WLOG("Результат выставления стопа (должно быть пусто) = '"..res2.."'")
end
WLOG("DoFire. End.") -- Пишем в лог, что эту контрольную точку прошли.
end
function main()
while is_run do
-- Выяснить, существует ли сейчас выставленная заявка или открытая сделка.
-- Если Да, то ВЫХОД
if HaveOpenPosition() then
-- Если есть открытая позиция, то ничего не делаем, курим.
-- И пишем об этом в лог.
WLOG(os.date().." Have Open Position. Do nothing.")
else
-- Получить значение МА (BB-средней) час и 5 минут, открытие свечи и текущую цену.
local NbbG=getNumCandles(PREFIX.."_BB_Global")
tbbG, nbbG, lbbG = getCandlesByIndex (PREFIX.."_BB_Global", 0, NbbG-1, 1) -- last свеча
iBB_Global_Middle = tbbG[0].close -- тек значение средней BB_Global
-- теперь собираем данные по младшему таймфрейму.
local NbbL=getNumCandles(PREFIX.."_BB_Local")
tbbL, nbbL, lbbL = getCandlesByIndex (PREFIX.."_BB_Local", 0, NbbL-1, 1) -- last свеча, средняя линия Боллинджера
iBB_Local_Middle = tbbL[0].close -- тек значение средней BB Local
tbbL, nbbL, lbbL = getCandlesByIndex (PREFIX.."_BB_Local", 1, NbbL-1, 1) -- last свеча, верхняя линия Боллинджера
iBB_Local_High = tbbL[0].close -- тек значение верхней BB Local
tbbL, nbbL, lbbL = getCandlesByIndex (PREFIX.."_BB_Local", 2, NbbL-1, 1) -- last свеча, нижняя линия Боллинджера
iBB_Local_Low = tbbL[0].close -- тек значение нижней BB Local
local NL=getNumCandles(PREFIX.."_Price")
tL, nL, lL = getCandlesByIndex (PREFIX.."_Price", 0, NL-1, 1) -- last свеча
iLastPrice = tL[0].close -- получили текущую цену (ЦПС)
iStartPrice = tL[0].open -- получили стартовую цену текущей свечи мледщего таймфрейма
--iHighPrice = tL[0].high -- получили хай свечи
--iLowPrice = tL[0].low -- получили лоу свечи
WLOG(os.date().." BB_Global="..iBB_Global_Middle.." | BB_Local="..iBB_Local_Middle.." | Open_Local="..iStartPrice.." | LastPrice="..iLastPrice)
-- Если (ЦПС > MA20H1) и пересекли Боллинджера внутрь снизу, то ЛОНГ
if (iLastPrice > iBB_Global_Middle) then
if (ContrTrendFlag==1) then -- вход внутрь Боллинджера младшего таймфрейма.
-- пересечение произошло между началом свечи и текущей ценой.
if (iStartPrice < iBB_Local_Low) and (iLastPrice > iBB_Local_Low) then
DoFire(tostring(iLastPrice), "B");
end
end
if (ContrTrendFlag==0) then -- вылет из локального Боллинджера.
-- Пересечение произошло между лоу свечки и текущей ценой.
if (iStartPrice < iBB_Local_High) and (iLastPrice > iBB_Local_High) then
DoFire(tostring(iLastPrice), "B");
end
end
end
-- Если (наоборот) то ШОРТ
if (iLastPrice < iBB_Global_Middle) then
if (ContrTrendFlag==1) then -- вход внутрь Боллинджера младшего таймфрейма.
-- пересечение произошло между началом свечи и текущей ценой.
if (iStartPrice > iBB_Local_High) and (iLastPrice < iBB_Local_High) then
DoFire(tostring(iLastPrice), "S");
end
end
if (ContrTrendFlag==0) then -- пересечение лоу Боллинджера младшего таймфрейма.
-- Пересечение произошло между ХАЙ свечки и текущей ценой.
-- То есть свечка пробила нижнюю линию локального Боллинджера вниз.
if (iStartPrice > iBB_Local_Low) and (iLastPrice < iBB_Local_Low) then
DoFire(tostring(iLastPrice), "S");
end
end
end
end
sleep(SleepDuration*1000) -- Отдыхаем SleepDuration секунд.
end
end
function OnStop(stop_flag)
-- Стандартная реакция на останов скрипта.
-- В будущем прописать сюда закрытие БД и чистку "хвостов".
is_run=false
end
function HaveOpenPosition() -- Возвращает TRUE, если есть открытая позиция по инструменту.
for i = 0,getNumberOf("FUTURES_CLIENT_HOLDING") - 1 do
if getItem("FUTURES_CLIENT_HOLDING",i).sec_code == SEC_CODE then
if getItem("FUTURES_CLIENT_HOLDING",i).totalnet ~= 0 then
return true
else
return false
end
end
end
end
function WLOG(p_st) -- Универсальная функция записи в лог.
l_file=io.open(LogFileName, "a")
l_file:write(p_st.."\n")
l_file:close()
end
Если код снаружи — на чем угодно. У меня и на Турбо Паскале есть :) (обмен с квиком — через текстовые файлы). Но это больше по приколу. Месье, так сказать, любит извращения.
C# популярен и хорош. Рекомендую к изучению.
Паскаль или Сишарп таких вольностей с типизацией, например, не допустит.
Зато ошибки ловить потом проще.
но Python хорош для алготрейдинга тем, что в нем больше библиотек на эту тематику
А вообще-то прелесть, когда подробно, с комментариями, смыслом и инструкциями
Мат библиотеки и для шарпа есть интересные. А если делать статистические исследования — тогда R.
Готовые тестеры стратегий вызывают недоверие и настороженность. =)
ch5oh,
1. в анализе данных Пайтон с R конкурирует. Питон всё таки по универсальней- хочешь сайт пиши, хочешь -сетевым оборудованием управляй, хочешь- пандас и во все тяжкие
2. а тестеры стратегий- почему недоверие? чем плохо? пилите вместо этого свою? если да- как вытаскивайте данные для тестирования
Gregori, в R тоже можно много чего наваять. Кластер для распределнных вычислений сделать. Вопросы сетевого взаимодействия и взаимодействия с базами, выкачка данных из разных источников и т.д. и т.п.
В целом, я не вижу в R заметных ограничений программистских возможностей по сравнению с обычными языками высокого уровня.
Turbo Pascal, строгая статическая типизация. тут вопрос не популярности среди молодёжи, а скорость разработки. набросать что то за пол дня легко. куча библиотек в различных сферах. Паскаль в виде дельфи увы сейчас уже во второй дивизион перешёл- ентерпрайз на java/c# чаще пишут. Если хочется сильно правильного и навороченного паскалеподобного для высоконадёжного ПО- есть ещё Ada. встроенные системы для боинга/аэрбаса/локхид матин + кучу софта для американской военки на нём пишут.
Там уже незарабатывающий скрипт, поэтому я его забросил давно.
Есть современный аналог Турбопаскаля — FreePascal, который хоронят уже много лет (даже десятилетий), а он всё никак не помрет, развивается и процветает :)
Но да, можно и так.
Спасибо.
Turbo Pascal, спасибо что хоть как то поддерживаешь тематику ресурса ;).
Или опять же погуглить можно. Тема совершенно несекретная и много где освещается. =)
С уважением.
Turbo Pascal, Спасибо. Сохранил .lua
Прописал коды (демо счет), префиксы, запускаю, и выдает сообщение:
116: attempt to index field '?' (a nil value)
Строка 116:
iBB_Global_Middle = tbbG[0].close — тек значение средней BB_Global
Если в скрипте PREFIX = «SBER», то идентификатор глобального Боллинджера должен быть «SBER_BB_Global».
Проверил только что.
=) а эквити этой чудо-страты не завалялось случайно?
При подгонке параметров дает плюс. Но год от года параметры разные.
Да и я не утверждаю, что тут грааль выложен.
Кроме ВВ есть другие коридоры.Например коридор регрессии и коридор Ошибки(антипод ВВ).
Андрей К, =) всегда приятно посмотреть на зеленые округлые холмы эквити.
Погонять страту в тестере на истории завсегда полезно. Тем более, когда её публикует уважаемый Turbo Pascal