ACCOUNT = 'SPBFUT00000'; -- Идентификатор счета CLASS_CODE = 'SPBFUT'; -- Код класса SEC_CODE = 'SiM8'; -- Код бумаги INTERVAL = INTERVAL_M1; -- Таймфрейм графика Name = "*AO (Awesome Oscillator)"; SHORT_Period = 5; LONG_Period =34; Metod = "EMA"; --(SMA, MMA, EMA, WMA, SMMA, VMA) VType = "Median"; -- Open, High, Low, Close, Volume, Median, Typical, Weighted, Difference STOP_LOSS = 10; -- Размер СТОП-ЛОССА (в шагах цены) TAKE_PROFIT = 30; -- Размер ТЕЙК-ПРОФИТА (в шагах цены) --/*РАБОЧИЕ ПЕРЕМЕННЫЕ РОБОТА*/ SEC_PRICE_STEP = 0; -- ШАГ ЦЕНЫ ИНСТРУМЕНТА SEC_NO_SHORT = false; -- Флаг, что по данному инструменту запрещены операции шорт DS = nil; -- Источник данных графика (DataSource) ROBOT_STATE ='В ПОИСКЕ ТОЧКИ ВХОДА';-- СОСТОЯНИЕ робота ['В ПРОЦЕССЕ СДЕЛКИ', либо 'В ПОИСКЕ ТОЧКИ ВХОДА'] trans_id = os.time(); -- Задает начальный номер ID транзакций trans_Status = nil; -- Статус текущей транзакции из функции OnTransPeply trans_result_msg = ''; -- Сообщение по текущей транзакции из функции OnTransPeply CurrentDirect = 'BUY'; -- Текущее НАПРАВЛЕНИЕ ['BUY', или 'SELL'] LastOpenBarIndex = 0; -- Индекс свечи, на которой была открыта последняя позиция (нужен для того, чтобы после закрытия по стопу тут же не открыть еще одну позицию) Run = true; -- Флаг поддержания работы бесконечного цикла в main -- Функция первичной инициализации скрипта (ВЫЗЫВАЕТСЯ ТЕРМИНАЛОМ QUIK в самом начале) function OnInit() -- Получает доступ к свечам графика local Error = ''; DS,Error = CreateDataSource(CLASS_CODE, SEC_CODE, INTERVAL); -- Проверка if DS == nil then message('Простой AO-робот:ОШИБКА получения доступа к свечам! '..Error); -- Завершает выполнение скрипта Run = false; return; end; -- Получает ШАГ ЦЕНЫ ИНСТРУМЕНТА SEC_PRICE_STEP = getParamEx(CLASS_CODE, SEC_CODE, "SEC_PRICE_STEP").param_value; end; function main() -- Выводит сообщение message('Простой AO-робот: '..ROBOT_STATE); -- "Бесконечный" цикл while Run do --Если СОСТОЯНИЕ робота "В ПРОЦЕССЕ СДЕЛКИ" if ROBOT_STATE == 'В ПРОЦЕССЕ СДЕЛКИ' then -- Выводит сообщение message('Простой AO-робот: В ПРОЦЕССЕ СДЕЛКИ'); -- Делает 10 попыток открыть сделку local Price = false; -- Переменная для получения результата открытия позиции (цена, либо ошибка(false)) for i=1,10 do if not Run then return; end; -- Если скрипт останавливается, не затягивает процесс -- Если первый раз пытается открыть SELL, а операции шорт по данному инструменту запрещены if CurrentDirect == "SELL" and SEC_NO_SHORT then -- Прерывает цикл FOR break; end; -- Совершает СДЕЛКУ указанного типа ["BUY", или "SELL"] по рыночной(текущей) цене размером в 1 лот, --- возвращает цену открытой сделки, либо FALSE, если невозможно открыть сделку Price = Trade(CurrentDirect); -- Если сделка открылась if Price ~= false then -- Прерывает цикл FOR break; end; sleep(100); -- Пауза в 100 мс между попытками открыть сделку end; if not Run then return; end; -- Если скрипт останавливается, не затягивает процесс -- Если сделка открыта if Price ~= false then -- Запоминает индекс свечи, на которой была открыта последняя позиция (нужен для того, чтобы после закрытия по стопу тут же не открыть еще одну позицию) LastOpenBarIndex = DS:Size(); -- Выводит сообщение message('Простой AO-робот: Открыта сделка '..CurrentDirect..' по цене '..Price); -- Делает 10 попыток выставить СТОП-ЛОСС и ТЕЙК-ПРОФИТ, и дождаться закрытия сделки message('Простой AO-робот: Делает 10 попыток выставить СТОП-ЛОСС и ТЕЙК-ПРОФИТ, и дождаться закрытия сделки'); local Result = nil; -- Переменная для получения результата выставления и срабатывания СТОП-ЛОСС и ТЕЙК-ПРОФИТ for i=1,10 do if not Run then return; end; -- Если скрипт останавливается, не затягивает процесс -- Выставляет СТОП-ЛОСС и ТЕЙК-ПРОФИТ, ЖДЕТ пока он сработает, принимает ЦЕНУ и ТИП ["BUY", или "SELL"] открытой сделки, --- возвращает FALSE, если не удалось выставить СТОП-ЛОСС и ТЕЙК-ПРОФИТ, или TRUE, если сделка закрылась, --- либо NIL, если при ошибке за 10 попыток не удалось принудительно закрыть позицию Result = SL_TP(Price, CurrentDirect); -- Если сделка закрылась if Result == true then -- Прерывает цикл FOR break; end; end; -- Если за 10 попыток не удалось закрыть позицию if Result == nil or Result == false then -- Выводит сообщение message('Простой AO-робот: После 10-и попыток не удалось закрыть сделку!!! Завершение скрипта!!!'); -- Завершает выполнение скрипта Run = false; -- Прерывает основной цикл WHILE break; else -- УДАЛОСЬ ЗАКРЫТЬ СДЕЛКУ -- Выводит сообщение message('Простой AO-робот: Сделка закрыта по СТОП-ЛОССУ, либо ТЕЙК-ПРОФИТУ'); --Меняет СОСТОЯНИЕ робота на "В ПОИСКЕ ТОЧКИ ВХОДА" ROBOT_STATE = 'В ПОИСКЕ ТОЧКИ ВХОДА'; -- Выводит сообщение message('Простой AO-робот: В ПОИСКЕ ТОЧКИ ВХОДА'); end; else -- Сделку не удалось открыть -- Если первый раз пытался открыть SELL, а операции шорт по данному инструменту запрещены if CurrentDirect == "SELL" and SEC_NO_SHORT then -- Выводит сообщение message('Простой AO-робот: Была первая попытка совершить запрещенную операцию шорт! Больше этого не повторится:)'); --Меняет СОСТОЯНИЕ робота на "В ПОИСКЕ ТОЧКИ ВХОДА" ROBOT_STATE = 'В ПОИСКЕ ТОЧКИ ВХОДА'; -- Выводит сообщение message('Простой AO-робот: В ПОИСКЕ ТОЧКИ ВХОДА'); else -- Выводит сообщение message('Простой AO-робот: После 10-и попыток не удалось открыть сделку!!! Завершение скрипта!!!'); -- Завершает выполнение скрипта Run = false; end; end; else -- СОСТОЯНИЕ робота 'В ПОИСКЕ ТОЧКИ ВХОДА' -- Если на этой свече еще не было открыто позиций if DS:Size() > LastOpenBarIndex then -- Если значение индикатора больше 100 if AO() > 100 then -- Задает направление НА ПОКУПКУ CurrentDirect = 'BUY'; message('CurrentDirect = "BUY"'); -- Меняет СОСТОЯНИЕ робота на "В ПРОЦЕССЕ СДЕЛКИ" ROBOT_STATE = 'В ПРОЦЕССЕ СДЕЛКИ'; -- Если значение индикатора меньше -100 elseif AO() < -100 then -- Если по данному инструменту не запрещены операции шорт if not SEC_NO_SHORT then -- Задает направление НА ПРОДАЖУ CurrentDirect = 'SELL'; message('CurrentDirect = "SELL"'); -- Меняет СОСТОЯНИЕ робота на "В ПРОЦЕССЕ СДЕЛКИ" ROBOT_STATE = 'В ПРОЦЕССЕ СДЕЛКИ'; end; end; end; end; sleep(10);--Пауза 10 мс, для того, чтобы не перегружать процессор компьютера end; end; -- Функция вызывается терминалом QUIK при получении ответа на транзакцию пользователя function OnTransReply(trans_reply) -- Если поступила информация по текущей транзакции if trans_reply.trans_id == trans_id then -- Передает статус в глобальную переменную trans_Status = trans_reply.status; -- Передает сообщение в глобальную переменную trans_result_msg = trans_reply.result_msg; end; end; -- Функция ВЫЗЫВАЕТСЯ ТЕРМИНАЛОМ QUIK при остановке скрипта function OnStop() Run = false; end; ----------------------------- -- ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ -- ----------------------------- -- Совершает СДЕЛКУ указанного типа (Type) ["BUY", или "SELL"] по рыночной(текущей) цене размером в 1 лот, --- возвращает цену открытой сделки, либо FALSE, если невозможно открыть сделку function Trade(Type) --Получает ID транзакции trans_id = trans_id + 1; local Price = 0; local Operation = ''; --Устанавливает цену и операцию, в зависимости от типа сделки и от класса инструмента if Type == 'BUY' then if CLASS_CODE ~= 'QJSIM' and CLASS_CODE ~= 'TQBR' then Price = getParamEx(CLASS_CODE, SEC_CODE, 'offer').param_value + 10*SEC_PRICE_STEP;end; -- по цене, завышенной на 10 мин. шагов цены Operation = 'B'; else if CLASS_CODE ~= 'QJSIM' and CLASS_CODE ~= 'TQBR' then Price = getParamEx(CLASS_CODE, SEC_CODE, 'bid').param_value - 10*SEC_PRICE_STEP;end; -- по цене, заниженной на 10 мин. шагов цены Operation = 'S'; end; -- Заполняет структуру для отправки транзакции local Transaction={ ['TRANS_ID'] = tostring(trans_id), ['ACTION'] = 'NEW_ORDER', ['CLASSCODE'] = CLASS_CODE, ['SECCODE'] = SEC_CODE, ['OPERATION'] = Operation, -- операция ("B" - buy, или "S" - sell) ['TYPE'] = 'M', -- по рынку (MARKET) ['QUANTITY'] = '1', -- количество ['ACCOUNT'] = ACCOUNT, ['PRICE'] = tostring(Price), ['COMMENT'] = 'Простой AO-робот' } -- Отправляет транзакцию sendTransaction(Transaction); -- Ждет, пока получит статус текущей транзакции (переменные "trans_Status" и "trans_result_msg" заполняются в функции OnTransReply()) while Run and trans_Status == nil do sleep(1); end; -- Запоминает значение local Status = trans_Status; -- Очищает глобальную переменную trans_Status = nil; -- Если транзакция не выполнена по какой-то причине if Status ~= 3 then -- Если данный инструмент запрещен для операции шорт if Status == 6 then -- Выводит сообщение message('Простой AO-робот: Данный инструмент запрещен для операции шорт!'); SEC_NO_SHORT = true; else -- Выводит сообщение с ошибкой message('Простой AO-робот: Транзакция не прошла!\nОШИБКА: '..trans_result_msg); end; -- Возвращает FALSE return false; else --Транзакция отправлена local OrderNum = nil; --ЖДЕТ пока ЗАЯВКА на ОТКРЫТИЕ сделки будет ИСПОЛНЕНА полностью --Запоминает время начала в секундах local BeginTime = os.time(); while Run and OrderNum == nil do --Перебирает ТАБЛИЦУ ЗАЯВОК for i=0,getNumberOf('orders')-1 do local order = getItem('orders', i); --Если заявка по отправленной транзакции ИСПОЛНЕНА ПОЛНОСТЬЮ if order.trans_id == trans_id and order.balance == 0 then --Запоминает номер заявки OrderNum = order.order_num; --Прерывает цикл FOR break; end; end; --Если прошло 10 секунд, а заявка не исполнена, значит произошла ошибка if os.time() - BeginTime > 9 then -- Выводит сообщение с ошибкой message('Простой AO-робот: Прошло 10 секунд, а заявка не исполнена, значит произошла ошибка'); -- Возвращает FALSE return false; end; sleep(10); -- Пауза 10 мс, чтобы не перегружать процессор компьютера end; --ЖДЕТ пока СДЕЛКА ОТКРЫТИЯ позиции будет СОВЕРШЕНА --Запоминает время начала в секундах BeginTime = os.time(); while Run do --Перебирает ТАБЛИЦУ СДЕЛОК for i=0,getNumberOf('trades')-1 do local trade = getItem('trades', i); --Если сделка по текущей заявке if trade.order_num == OrderNum then --Возвращает фАКТИЧЕСКУЮ ЦЕНУ открытой сделки return trade.price; end; end; --Если прошло 10 секунд, а сделка не совершена, значит на демо-счете произошла ошибка if os.time() - BeginTime > 9 then -- Выводит сообщение с ошибкой message('Простой AO-робот: Прошло 10 секунд, а сделка не совершена, значит на демо-счете произошла ошибка'); -- Возвращает FALSE return false; end; sleep(10); -- Пауза 10 мс, чтобы не перегружать процессор компьютера end; end; end; -- ПРИНУДИТЕЛЬНО ЗАКРЫВАЕТ ОТКРЫТУЮ ПОЗИЦИЮ переданного типа (Type) ["BUY", или "SELL"] function KillPos(Type) -- Дается 10 попыток local Count = 0; -- Счетчик попыток if Type == 'BUY' then -- Пока скрипт не остановлен и позиция не закрыта while Run and not Trade('SELL') do -- Открывает SELL, тем самым закрывая BUY, если Trade('SELL') вернет TRUE, цикл прекратится Count = Count + 1; -- Увеличивает счетчик -- Если за 10 попыток не удалось закрыть позицию if Count == 10 then -- Возвращает NIL return nil; end; sleep(100); -- Пауза 100 мс, чтобы изменилась ситуация на сервере end; else -- Пока скрипт не остановлен и позиция не закрыта while Run and not Trade('BUY') do -- Открывает BUY, тем самым закрывая SELL, если Trade('BUY') вернет TRUE, цикл прекратится Count = Count + 1; -- Увеличивает счетчик -- Если за 10 попыток не удалось закрыть позицию if Count == 10 then -- Возвращает NIL return nil; end; sleep(100); -- Пауза 100 мс, чтобы изменилась ситуация на сервере end; end; -- Возвращает TRUE, если удалось принудительно закрыть позицию return true; end; -- Выставляет СТОП-ЛОСС и ТЕЙК-ПРОФИТ, ЖДЕТ пока он сработает, принимает ЦЕНУ (Price) и ТИП (Type) ["BUY", или "SELL"] открытой сделки, --- возвращает FALSE, если не удалось выставить СТОП-ЛОСС и ТЕЙК-ПРОФИТ, либо TRUE, если сделка закрылась, --- либо NIL, если при ошибке за 10 попыток не удалось принудительно закрыть позицию function SL_TP(Price, Type) -- ID транзакции trans_id = trans_id + 1; -- Находит направление для заявки local operation = ""; local price = "0"; -- Цена, по которой выставится заявка при срабатывании Стоп-Лосса (для рыночной заявки по акциям должна быть 0) local stopprice = ""; -- Цена Тейк-Профита local stopprice2 = ""; -- Цена Стоп-Лосса local market = "YES"; -- После срабатывания Тейка, или Стопа, заявка сработает по рыночной цене -- Если открыт BUY, то направление стоп-лосса и тейк-профита SELL, иначе направление стоп-лосса и тейк-профита BUY if Type == 'BUY' then operation = "S"; -- Тейк-профит и Стоп-лосс на продажу(чтобы закрыть BUY, нужно открыть SELL) -- Если не акции if CLASS_CODE ~= 'QJSIM' and CLASS_CODE ~= 'TQBR' then price = tostring(math.floor(getParamEx(CLASS_CODE, SEC_CODE, 'PRICEMIN').param_value)); -- Цена выставляемой заявки после страбатывания Стопа минимально возможная, чтобы не проскользнуло market = "NO"; -- После срабатывания Тейка, или Стопа, заявка сработает НЕ по рыночной цене end; stopprice = tostring(Price + TAKE_PROFIT*SEC_PRICE_STEP); -- Уровень цены, когда активируется Тейк-профит stopprice2 = tostring(Price - STOP_LOSS*SEC_PRICE_STEP); -- Уровень цены, когда активируется Стоп-лосс else -- открыт SELL operation = "B"; -- Тейк-профит и Стоп-лосс на покупку(чтобы закрыть SELL, нужно открыть BUY) -- Если не акции if CLASS_CODE ~= 'QJSIM' and CLASS_CODE ~= 'TQBR' then price = tostring(math.floor(getParamEx(CLASS_CODE, SEC_CODE, 'PRICEMAX').param_value)); -- Цена выставляемой заявки после страбатывания Стопа максимально возможная, чтобы не проскользнуло market = "NO"; -- После срабатывания Тейка, или Стопа, заявка сработает НЕ по рыночной цене end; stopprice = tostring(Price - TAKE_PROFIT*SEC_PRICE_STEP); -- Уровень цены, когда активируется Тейк-профит stopprice2 = tostring(Price + STOP_LOSS*SEC_PRICE_STEP); -- Уровень цены, когда активируется Стоп-лосс end; -- Заполняет структуру для отправки транзакции на Стоп-лосс и Тейк-профит local Transaction = { ["ACTION"] = "NEW_STOP_ORDER", -- Тип заявки ["TRANS_ID"] = tostring(trans_id), ["CLASSCODE"] = CLASS_CODE, ["SECCODE"] = SEC_CODE, ["ACCOUNT"] = ACCOUNT, ["OPERATION"] = operation, -- Операция ("B" - покупка(BUY), "S" - продажа(SELL)) ["QUANTITY"] = "1", -- Количество в лотах ["PRICE"] = price, -- Цена, по которой выставится заявка при срабатывании Стоп-Лосса (для рыночной заявки по акциям должна быть 0) ["STOPPRICE"] = stopprice, -- Цена Тейк-Профита ["STOP_ORDER_KIND"] = "TAKE_PROFIT_AND_STOP_LIMIT_ORDER", -- Тип стоп-заявки ["EXPIRY_DATE"] = "TODAY", -- Срок действия стоп-заявки ("GTC" – до отмены,"TODAY" - до окончания текущей торговой сессии, Дата в формате "ГГММДД") -- "OFFSET" - (ОТСТУП)Если цена достигла Тейк-профита и идет дальше в прибыль, -- то Тейк-профит сработает только когда цена вернется минимум на 2 шага цены назад, -- это может потенциально увеличить прибыль ["OFFSET"] = tostring(2*SEC_PRICE_STEP), ["OFFSET_UNITS"] = "PRICE_UNITS", -- Единицы измерения отступа ("PRICE_UNITS" - шаг цены, или "PERCENTS" - проценты) -- "SPREAD" - Когда сработает Тейк-профит, выставится заявка по цене хуже текущей на 100 шагов цены, -- которая АВТОМАТИЧЕСКИ УДОВЛЕТВОРИТСЯ ПО ТЕКУЩЕЙ ЛУЧШЕЙ ЦЕНЕ, -- но то, что цена значительно хуже, спасет от проскальзывания, -- иначе, сделка может просто не закрыться (заявка на закрытие будет выставлена, но цена к тому времени ее уже проскочит) ["SPREAD"] = tostring(100*SEC_PRICE_STEP), ["SPREAD_UNITS"] = "PRICE_UNITS", -- Единицы измерения защитного спрэда ("PRICE_UNITS" - шаг цены, или "PERCENTS" - проценты) -- "MARKET_TAKE_PROFIT" = ("YES", или "NO") должна ли выставится заявка по рыночной цене при срабатывании Тейк-Профита. -- Для рынка FORTS рыночные заявки, как правило, запрещены, -- для лимитированной заявки на FORTS нужно указывать заведомо худшую цену, чтобы она сработала сразу же, как рыночная ["MARKET_TAKE_PROFIT"] = market, ["STOPPRICE2"] = stopprice2, -- Цена Стоп-Лосса ["IS_ACTIVE_IN_TIME"] = "NO", -- "MARKET_TAKE_PROFIT" = ("YES", или "NO") должна ли выставится заявка по рыночной цене при срабатывании Стоп-Лосса. -- Для рынка FORTS рыночные заявки, как правило, запрещены, -- для лимитированной заявки на FORTS нужно указывать заведомо худшую цену, чтобы она сработала сразу же, как рыночная ["MARKET_STOP_LIMIT"] = market, ["COMMENT"] = "Простой AO-робот ТЕЙК-ПРОФИТ и СТОП-ЛОСС" } -- Отправляет транзакцию на установку ТЕЙК-ПРОФИТ и СТОП-ЛОСС sendTransaction(Transaction); -- Ждет, пока не получит статус текущей транзакции (переменные "trans_Status" и "trans_result_msg" заполняются в функции OnTransReply()) while Run and trans_Status == nil do sleep(10); end; -- Запоминает значение local Status = trans_Status; -- Очищает глобальную переменную trans_Status = nil; -- Если транзакция не выполнена по какой-то причине if Status ~= 3 then -- Выводит сообщение с ошибкой message('Простой AO-робот: Установка ТЕЙК-ПРОФИТ и СТОП-ЛОСС не удалась!\nОШИБКА: '..trans_result_msg); -- Возвращает FALSE return false; else -- Выводит сообщение message('Простой AO-робот: ВЫСТАВЛЕНА заявка ТЕЙК-ПРОФИТ и СТОП-ЛОСС: '..trans_id); local OrderNum_CLOSE = nil; -- ЖДЕТ пока СТОП-ЗАЯВКА на СТОП-ЛОСС и ТЕЙК-ПРОФИТ будет ИСПОЛНЕНА полностью while Run and OrderNum_CLOSE == nil do -- Перебирает ТАБЛИЦУ СТОП-ЗАЯВОК for i=0,getNumberOf("stop_orders")-1 do local stop_order = getItem("stop_orders", i); -- Если заявка по текущей транзакции СТОП-ЛОСС и ТЕЙК-ПРОФИТ if stop_order.trans_id == trans_id then -- Если заявка по отправленной СТОП-ЛОСС и ТЕЙК-ПРОФИТ транзакции ИСПОЛНЕНА ПОЛНОСТЬЮ if stop_order.balance == 0 then -- Если по наступлению стоп-цены выставлена заявка if stop_order.linkedorder > 0 then -- Запоминает номер заявки, которая была создана при срабатывании СТОП-ЛОСС, или ТЕЙК-ПРОФИТ OrderNum_CLOSE = stop_order.linkedorder; -- Прерывает цикл FOR break; -- Стоп-заявка сработала, но была отвергнута торговой системой elseif CheckBit(stop_order.flags, 10) == 1 then -- ПРИНУДИТЕЛЬНО ЗАКРЫВАЕТ ОТКРЫТУЮ ПОЗИЦИЮ и выходит из функции return KillPos(Type); end; end; end; end; sleep(10); -- Пауза 10 мс, чтобы не перегружать процессор компьютера end; --ЖДЕТ пока СДЕЛКА ЗАКРЫТИЯ позиции будет СОВЕРШЕНА --Запоминает время начала в секундах BeginTime = os.time(); while Run do --Перебирает ТАБЛИЦУ СДЕЛОК for i=0,getNumberOf("trades")-1 do local trade = getItem("trades", i); --Если сделка по текущей заявке на СТОП-ЛОСС и ТЕЙК-ПРОФИТ if trade.order_num == OrderNum_CLOSE then -- Возвращает TRUE return true; end; end; --Если прошло 10 секунд, а сделка не совершена, значит на демо-счете произошла ошибка сервера "Обработка кросс-заявок блокирована" if os.time() - BeginTime > 9 then -- ПРИНУДИТЕЛЬНО ЗАКРЫВАЕТ ОТКРЫТУЮ ПОЗИЦИЮ и выходит из функции return KillPos(Type); end; sleep(1); end; end; end; Settings={ Name = "*AO (Awesome Oscillator)", SHORT_Period = 5, LONG_Period =34, Metod = "EMA", --(SMA, MMA, EMA, WMA, SMMA, VMA) VType = "Median", -- Open, High, Low, Close, Volume, Median, Typical, Weighted, Difference(для построения скользящих) line = {{ Name = "Horizontal line", Type = TYPE_LINE, Color = RGB(140, 140, 140) }, { Width = 3, Name = "AO_Up", Type = TYPE_HISTOGRAM, Color = RGB(0, 206, 0) }, { Width = 3, Name = "AO_Down", Type = TYPE_HISTOGRAM, Color = RGB(221, 44, 44) } }, Round = "off", Multiply = 1, Horizontal_line="0" } function Init() func = AO(); return #Settings.line; end; function OnCalculate(Index) local Out = ConvertValue(Settings, func(Index, Settings)); local HL = tonumber(Settings.Horizontal_line); if Out then if Out > (HL or 0) then return HL,Out,nil; else return HL,nil,Out; end; else return HL,nil,nil; end; end; function AO() --Awesome Oscillator ("AO") local S_MA = MA(); local L_MA = MA(); local it = {p=0, l=0}; return function (I, Fsettings, ds) local Fsettings=(Fsettings or {}); local SP = (Fsettings.SHORT_Period or 5); local LP = (Fsettings.LONG_Period or 34); local M = (Fsettings.Metod or EMA); local VT = (Fsettings.VType or MEDIAN); local AOP = 100; if (SP>0) and (LP>0) then if I == 1 then if (M==VMA) then M=SMA end; it = {p=0, l=0} end; local S = S_MA(I, {Period=SP, Metod = M, VType=VT}, ds); local L = L_MA(I, {Period=LP, Metod = M, VType=VT}, ds); if CandleExist(I,ds) then if I~=it.p then it={p=I, l=it.l+1} end; if it.l >= LP and S and L then return S - L; end; end; end; return nil; end; end; function MA() --Moving Average ("MA") local T_MA = {[EMA]=F_EMA()} return function (I, Fsettings, ds) local Fsettings=(Fsettings or {}); local P = (Fsettings.Period or 14); if (P > 0) then return T_MA[string.upper(Fsettings.Metod or EMA)](I, P, (Fsettings.VType or CLOSE), ds); end; return nil; end; end; --[[Exponential Moving Average (EMA) EMAi = (EMAi-1*(n-1)+2*Pi) / (n+1)]] function F_EMA() local tmp = {pp=nil, p=nil} local it = {p=0, l=0} return function(I, P, VT, ds) if I == 1 then tmp = {pp=nil, p=nil} it = {p=0, l=0} end; if CandleExist(I,ds) then if I~=it.p then it = {p=I, l=it.l+1} tmp.pp = tmp.p end; if it.l == 1 then tmp.p = GetValueEX(it.p,VT,ds) else tmp.p = (tmp.pp*(P-1) + 2*GetValueEX(it.p,VT,ds)) / (P+1) end; if it.l >= P then return tmp.p; end; end; return nil; end; end; EMA = "EMA"; OPEN,HIGH,LOW,CLOSE,VOLUME,MEDIAN,TYPICAL,WEIGHTED,DIFFERENCE,ANY = "O","H","L","C","V","M","T","W","D","A"; function CandleExist(I,ds) return (type(C)=="function" and C(I)~=nil) or (type(ds)=="table" and (ds[I]~=nil or (type(ds.Size)=="function" and (I>0) and (I<=ds:Size())))); end; function Squeeze(I,P) return math.fmod(I-1,P+1); end; function ConvertValue(T,...) local function r(V, R) if R and string.upper(R)== "ON" then R=0 end; if V and tonumber(R) then if V >= 0 then return math.floor(V * 10^R + 0.5) / 10^R; else return math.ceil(V * 10^R - 0.5) / 10^R; end; else return V; end; end; if arg.n > 0 then for i = 1, arg.n do arg[i]=arg[i] and r(arg[i] * ((T and T.Multiply) or 1), (T and T.Round) or "off"); end; return unpack(arg); else return nil; end; end; function GetValueEX(I,VT,ds) VT=(VT and string.upper(string.sub(VT,1,1))) or ANY if VT == OPEN then --Open return (O and O(I)) or (ds and ds:O(I)); elseif VT == HIGH then --High return (H and H(I)) or (ds and ds:H(I)); elseif VT == LOW then --Low return (L and L(I)) or (ds and ds:L(I)); elseif VT == CLOSE then --Close return (C and C(I)) or (ds and ds:C(I)); elseif VT == VOLUME then --Volume return (V and V(I)) or (ds and ds:V(I)); elseif VT == MEDIAN then --Median return ((GetValueEX(I,HIGH,ds) + GetValueEX(I,LOW,ds)) / 2); elseif VT == TYPICAL then --Typical return ((GetValueEX(I,MEDIAN,ds) * 2 + GetValueEX(I,CLOSE,ds))/3); elseif VT == WEIGHTED then --Weighted return ((GetValueEX(I,TYPICAL,ds) * 3 + GetValueEX(I,OPEN,ds))/4); elseif VT == DIFFERENCE then --Difference return (GetValueEX(I,HIGH,ds) - GetValueEX(I,LOW,ds)); else --Any return (ds and ds[I]); end; return nil; end; -- Функция возвращает значение бита (число 0, или 1) под номером bit (начинаются с 0) в числе flags, если такого бита нет, возвращает nil function CheckBit(flags, bit) -- Проверяет, что переданные аргументы являются числами if type(flags) ~= "number" then error("Предупреждение!!! Checkbit: 1-й аргумент не число!"); end; if type(bit) ~= "number" then error("Предупреждение!!! Checkbit: 2-й аргумент не число!"); end; local RevBitsStr = ""; -- Перевернутое (задом наперед) строковое представление двоичного представления переданного десятичного числа (flags) local Fmod = 0; -- Остаток от деления local Go = true; -- Флаг работы цикла while Go do Fmod = math.fmod(flags, 2); -- Остаток от деления flags = math.floor(flags/2); -- Оставляет для следующей итерации цикла только целую часть от деления RevBitsStr = RevBitsStr ..tostring(Fmod); -- Добавляет справа остаток от деления if flags == 0 then Go = false; end; -- Если был последний бит, завершает цикл end; -- Возвращает значение бита local Result = RevBitsStr :sub(bit+1,bit+1); if Result == "0" then return 0; elseif Result == "1" then return 1; else return nil; end; end;