Turbo Pascal
Turbo Pascal личный блог
04 апреля 2019, 16:19

Робот "Два Боллинджера" с исходниками

Хорош философствовать. Давайте писать более полезные посты.
Итак, робот на двух графиках Боллинджера.
Общий принцип:
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
52 Комментария
  • принц Оранский
    04 апреля 2019, 16:23
    Вопрос праздного туриста! Вот интересно, что в среде «продвинутых программеров-алготрейдеров» — особой популярностью пользуется си шарп. Чисто теоретически — возможно писать алгоритмы на том же турбопаскале -  подсоединяться на прямую к бирже? 
  • принц Оранский
    04 апреля 2019, 16:34
    Просто интересно — что такое особенное есть в си шарпе, чего нет в простом турбопаскале??? я имею ввиду только и исключительно задачи алго (не высокачастотную торговлю).
  • Friendly Deep Space
    04 апреля 2019, 16:42
    Бэктест бы еще)
  • smit
    04 апреля 2019, 16:43
    function WLOG(p_st) -- Универсальная функция записи в лог.
    	l_file=io.open(LogFileName, "a")
    	l_file:write(p_st.."\n")
    	l_file:close()
    end
    я бы не открывал файл каждый раз. Держал бы дескриптор фала и периодически flush

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн