Добрый день. Вопрос для программистов на lua. Я начал разбираться с написанием робота. Сам робот на простой ma10. Вход при пробитии закрытия свечи ma(закрытием снизу вверх — лонг, сверху в них — шорт). Выход по стопам, при этом после стопа входить в противоположную сторону по открытию следующего часа. И выход по пробитию закрытия свечи и ma.
1)Не могу понять в какую часть когда записать следующее условие: нужно что бы робот выходил при пробитии закрытия свечи ma и делал переворот.
2)Как прописать условие переворота, по открытию следующего часа, переворота при выбивании стопа.
3)Как прописать перенос сделок через ноч.
Account = ""; -- счет клиента
ClientCode = ""; -- код клиента
FirmCode = ""; -- код фирмы
SecCode = ""; -- код инструмента
ClassCode = ""; -- класс инструмента
Tag = SecCode .. "_PRICE"; -- индикатор графика цены
EMA1 = SecCode .. "_EMA1"
EMA2 = SecCode .. "_EMA2"
Run = true; -- отвечает за работу скрипта
LastOpenBarIndex = 0;
FlagOrder = false; -- флаг наличия активной заявки
FlagStopOrder = false;
OrderNumber = 0; -- номер заявки
trans_id = os.time(); -- уникальный id транзакции
trans_number = 0; -- номер транзакции
trans_status = 0; -- статус транзакции
ClosePosition = true;
Count = 0;
Cycles = 60;
Rest = 1; -- остаток в заявке
Start = 65955; -- начало работы скрипта
Stop = 233000; -- конец работы скрипта
StopLossl = 350; -- размер стоп-лосса
StopLosss = 405; -- размер стоп-лосса
TakeProfit = 3.8/100
Step = 1; -- шаг цены
Slip = 0; -- проскальзывание цены
Unit = 10; -- количество лотов в заявке
-- Главная функция скрипта --
function main()
-- Основной цикл скрипта --
while Run do
if IsWindowClosed(t_id) then -- при закрытие окна торгового робота скрипт останавливается!!!
Run = false;
message("Stop trading robot!!!");
end;
if not_trade() then
file = io.open(getScriptPath() .. "/LastOpenBarIndex.txt", "r");
if file ~= nil then
LastOpenBarIndex = tonumber(file:read("*n"));
file:close();
end;
if LastOpenBarIndex == nil then LastOpenBarIndex = 0 end;
if FlagOrder then
done_order()
elseif FlagStopOrder then
stop_order()
elseif LastOpenBarIndex < getNumCandles(Tag) then
Predict = predict() -- получаем прогноз
Position = position() -- узнаем позицию по инструменту
if Predict > 0 and Position <= 0 then
if Position < 0 then
QtyBuy = math.abs(Position) + Unit;
elseif Position == 0 then
QtyBuy = Unit;
end;
buy(QtyBuy) -- покупаем
elseif Predict < 0 and Position >= 0 then
if Position > 0 then
QtySell = Position + Unit;
elseif Position == 0 then
QtySell = Unit;
end;
sell(QtySell) -- продаем
end;
LastOpenBarIndex = getNumCandles(Tag);
file = io.open(getScriptPath() .. "/LastOpenBarIndex.txt", "w");
if file ~= nil then
file:write(tostring(LastOpenBarIndex));
file:close();
end;
end;
-- вывод параметров скрипта в таблицу --
SetCell(t_id, 1, 1, tostring(getInfoParam("SERVERTIME")));
SetCell(t_id, 1, 2, SecCode);
SetCell(t_id, 1, 3, tostring(position()));
SetCell(t_id, 1, 4, tostring(round(tonumber(predict()), 10)));
SetCell(t_id, 1, 5, tostring(round(tonumber(getPortfolioInfo(FirmCode, ClientCode).total_limit_open_pos), 2)));
SetCell(t_id, 1, 6, tostring(round(tonumber(getPortfolioInfo(FirmCode, ClientCode).varmargin), 2)));
sleep(1000);
else
sleep(1000);
end;
end;
end;
function not_trade()
local Error = true;
if isConnected() == 1 then
local Time = 0;
SERVER_TIME = getInfoParam("SERVERTIME");
if string.len(SERVER_TIME) == 8 then
Time = tonumber(SERVER_TIME:sub (1,2) .. SERVER_TIME:sub (4,5) .. SERVER_TIME:sub (7,8));
else
Time = tonumber(SERVER_TIME:sub (1,1) .. SERVER_TIME:sub (3,4) .. SERVER_TIME:sub (6,7));
end;
if Time == nil then Time = 0 end;
if Time < Start then
Error = false;
SetCell(t_id, 1, 1, "ErrorTime");
for i = 2,6 do
SetCell(t_id, 1, i, "0");
end;
elseif Time > 135900 and Time < 140600 then
Error = false;
SetCell(t_id, 1, 1, "Clearing");
for i = 2,6 do
SetCell(t_id, 1, i, "0");
end;
elseif Time > 184400 and Time < 190600 then
Error = false;
SetCell(t_id, 1, 1, "Clearing");
for i = 2,6 do
SetCell(t_id, 1, i, "0");
end;
elseif Time > Stop then
Error = false;
SetCell(t_id, 1, 1, "ErrorTime");
for i = 2,6 do
SetCell(t_id, 1, i, "0");
end;
-- закрытие позиции и снятие стоп-заявки в конце торговой сессии --
--[[local Position = position();
if Position ~= 0 then
if ClosePosition then
if Position > 0 then
Slip = 0;
sell(Position);
elseif Position < 0 then
Slip = 0;
buy(math.abs(Position));
end;
FlagOrder = false;
ClosePosition = false;
end;
elseif Position == 0 then
stop_order()
ClosePosition = true;
end;]]
end;
else
Error = false;
SetCell(t_id, 1, 1, "ErrorConnection");
for i = 2,6 do
SetCell(t_id, 1, i, "0");
end;
end;
return Error;
end;
-- Функция покупки --
function buy(QtyBuy)
local PriceBuy = 0; -- цена по которой покупаем
while PriceBuy <= 0 do
PriceBuy = tonumber(getParamEx(ClassCode, SecCode, "offer").param_value) - Step * Slip -- получаем лучшую цену предложения
end;
message("Order to buy at the price: " .. PriceBuy .. ", qty: " .. QtyBuy);
trans_id = trans_id + 1;
local trans_params = {
['TRANS_ID'] = tostring(trans_id),
['ACTION'] = "NEW_ORDER",
['CLASSCODE'] = ClassCode,
['SECCODE'] = SecCode,
['OPERATION'] = "B",
['TYPE'] = "M",
['QUANTITY'] = tostring (math.floor(QtyBuy)),
['ACCOUNT'] = Account,
['PRICE'] = tostring(math.floor(PriceBuy)),
['CLIENT_CODE'] = ClientCode
}
trans_status = 0;
trans_number = 0;
sendTransaction(trans_params)
while trans_status ~= 3 do
sleep(20);
end;
OrderNumber = trans_number;
FlagOrder = true;
end;
-- Функция продажи --
function sell(QtySell)
local PriceSell = 0; -- цена по которой продаем
while PriceSell <= 0 do
PriceSell = tonumber(getParamEx(ClassCode, SecCode, "bid").param_value) - Step * Slip -- получаем лучшую цену спроса
end;
message("Order to sell at the price: " .. PriceSell .. ", in the quantity: " .. QtySell);
trans_id = trans_id + 1;
local trans_params = {
['TRANS_ID'] = tostring(trans_id),
['ACTION'] = "NEW_ORDER",
['CLASSCODE'] = ClassCode,
['SECCODE'] = SecCode,
['OPERATION'] = "S",
['TYPE'] = "M",
['QUANTITY'] = tostring (math.floor(QtySell)),
['ACCOUNT'] = Account,
['PRICE'] = tostring(math.floor(PriceSell)),
['CLIENT_CODE'] = ClientCode
}
trans_status = 0;
trans_number = 0;
sendTransaction(trans_params)
while trans_status ~= 3 do
sleep(20);
end;
OrderNumber = trans_number;
FlagOrder = true;
end;
-- Функция контроля исполнения активной заявки --
function done_order()
local QtyOrder = 0;
local PriceOrder = 0;
for i = 0, getNumberOf("orders") - 1 do
if getItem("orders", i).order_num == OrderNumber then
Rest = getItem("orders", i).balance;
while PriceOrder <= 0 do
PriceOrder = getItem("orders", i).price;
QtyOrder = getItem("orders", i).qty;
sleep(20);
end;
end;
end;
-- если заявка полностью исполнилась --
if Rest == 0 then
message(getInfoParam("SERVERTIME") .. " Order №" .. OrderNumber .. " fully executed, at the price without slipping: " .. PriceOrder .. " in quantity: " .. QtyOrder, 1)
FlagOrder = false;
FlagStopOrder = true;
Count = 0;
-- если заявка не исполнилась, снимаем отстаток --
elseif Count > Cycles then
kill_order(OrderNumber);
message(getInfoParam("SERVERTIME") .. " Order №" .. OrderNumber .. " canceled, rest: " .. Rest, 1)
Count = 0;
FlagOrder = false;
if position() ~= 0 then
FlagStopOrder = true;
end;
file = io.open(getScriptPath() .. "/LastOpenBarIndex.txt", "w");
file:write("");
file:close();
-- если заявка еще не исполнилась, но время на исполнение еще осталось --
elseif Rest > 0 then
Count = Count + 1;
end;
sleep(1000);
end;
-- Функция выставления stop_loss --
function stop_order()
local PriceStopOrder = 0;
local Position = position();
-- снимаем старую стоп-заявку --
for i = 0, getNumberOf("stop_orders") - 1 do
if bit.test(getItem("stop_orders", i).flags, 0) == true then
kill_stop_order(getItem("stop_orders", i).order_num);
end;
end;
-- определяем цену по которой заключена сделка --
if Position ~= 0 then
for i = 0, getNumberOf("trades") - 1 do
if getItem("trades", i).order_num == OrderNumber then
while PriceStopOrder <= 0 do
PriceStopOrder = getItem("trades", i).price;
sleep(20);
end;
end;
end;
end;
if PriceStopOrder > 0 and Position ~= 0 then
if Position > 0 then
StopPrice = PriceStopOrder - StopLossl;
PriceX = PriceStopOrder - Step * 200;
QtyStopOrder = Position;
Operation = "S";
elseif Position < 0 then
StopPrice = PriceStopOrder + Step * StopLosss;
PriceX = PriceStopOrder + Step * 200;
QtyStopOrder = math.abs(Position);
Operation = "B";
end;
trans_id = trans_id + 1;
local trans_params = {
['TRANS_ID'] = tostring(trans_id),
['ACTION'] = "NEW_STOP_ORDER",
['STOP_ORDER_KIND'] = "SIMPLE_STOP_ORDER",
['EXPIRY_DATE'] = "TODAY",
['STOPPRICE'] = tostring(math.floor(StopPrice)),
['CLASSCODE'] = ClassCode,
['SECCODE'] = SecCode,
['PRICE'] = tostring(math.floor(PriceX)),
['OPERATION'] = Operation,
['TYPE'] = "L",
['QUANTITY'] = tostring(math.floor(QtyStopOrder)),
['ACCOUNT'] = Account,
['CLIENT_CODE'] = ClientCode
}
trans_status = 0; -- сбрасываем прошлый статус транзакции
sendTransaction(trans_params); -- отправляем транзакцию
while trans_status ~= 3 do
sleep(20);
end;
if Position > 0 then
StopPrice = PriceStopOrder - StopLossl;
PriceX = PriceStopOrder - Step * 200;
QtyStopOrder = Position;
Operation = "S";
elseif Position < 0 then
StopPrice = PriceStopOrder + Step * StopLosss;
PriceX = PriceStopOrder + Step * 200;
QtyStopOrder = math.abs(Position);
Operation = "B";
end;
trans_id = trans_id + 1;
local trans_params = {
['TRANS_ID'] = tostring(trans_id),
['ACTION'] = "NEW_STOP_ORDER",
['STOP_ORDER_KIND'] = "SIMPLE_STOP_ORDER",
['EXPIRY_DATE'] = "TODAY",
['STOPPRICE'] = tostring(math.floor(StopPrice)),
['CLASSCODE'] = ClassCode,
['SECCODE'] = SecCode,
['PRICE'] = tostring(math.floor(PriceX)),
['OPERATION'] = Operation,
['TYPE'] = "L",
['QUANTITY'] = tostring(math.floor(QtyStopOrder)),
['ACCOUNT'] = Account,
['CLIENT_CODE'] = ClientCode
}
trans_status = 0; -- сбрасываем прошлый статус транзакции
sendTransaction(trans_params); -- отправляем транзакцию
while trans_status ~= 3 do
sleep(20);
end;
--[[]]
if Position > 0 then
StopPrice = PriceStopOrder *(TakeProfit+1) ;
PriceX = PriceStopOrder - Step * 200;
QtyStopOrder = Position;
Operation = "S";
elseif Position < 0 then
StopPrice = PriceStopOrder *(1-TakeProfit);
PriceX = PriceStopOrder + Step * 200;
QtyStopOrder = math.abs(Position);
Operation = "B";
end;
trans_id = trans_id + 1;
local trans_params = {
['TRANS_ID'] = tostring(trans_id),
['ACTION'] = "NEW_STOP_ORDER",
['STOP_ORDER_KIND'] = "SIMPLE_STOP_ORDER",
['EXPIRY_DATE'] = "TODAY",
['STOPPRICE'] = tostring(math.floor(StopPrice)),
['CLASSCODE'] = ClassCode,
['SECCODE'] = SecCode,
['PRICE'] = tostring(math.floor(PriceX)),
['OPERATION'] = Operation,
['TYPE'] = "L",
['QUANTITY'] = tostring(math.floor(QtyStopOrder)),
['ACCOUNT'] = Account,
['CLIENT_CODE'] = ClientCode
}
trans_status = 0; -- сбрасываем прошлый статус транзакции
sendTransaction(trans_params); -- отправляем транзакцию
while trans_status ~= 3 do
sleep(20);
end;
FlagStopOrder = false;
end;
end;
-- Функция снятия заявки --
function kill_order(Number)
trans_id = trans_id + 1;
local trans_params = {
['TRANS_ID'] = tostring(trans_id),
['ACTION'] = "KILL_ORDER",
['CLASSCODE'] = ClassCode,
['SECCODE'] = SecCode,
['ACCOUNT'] = Account,
['ORDER_KEY'] = tostring(Number)
}
sendTransaction(trans_params);
end;
-- Функция снятия стоп-заявки --
function kill_stop_order(Number)
trans_id = trans_id + 1;
local trans_params = {
['TRANS_ID'] = tostring(trans_id),
['ACTION'] = "KILL_STOP_ORDER",
['CLASSCODE'] = ClassCode,
['SECCODE'] = SecCode,
['ACCOUNT'] = Account,
['STOP_ORDER_KEY'] = tostring(Number)
}
sendTransaction(trans_params);
end;
-- Функция прогнозирования движения цены инструмента --
function predict()
local t, n, l = nil,nil,nil
while t == nil do
x1 = getNumCandles('Tag')
t, n, l = getCandlesByIndex('Tag', 0, 0, x1)
ma_1 = t[0].close
for i=1, x1-1 do
local C = t[i].close
local C1 = t[i-1].close
local C2 = t[i-2].close
local C3 = t[i-3].close
local C4 = t[i-4].close
local C5 = t[i-5].close
local C6 = t[i-6].close
local C7 = t[i-7].close
local C8 = t[i-8].close
local C9 = t[i-9].close
ma_2 = (C1+C2+C3+C4+C5+C6+C7+C8+C9+C)/10
ma_1 = ma_2
end
--
sleep(10)
end;
local ema2 = ma_1;
local ema1 = t[x1].close
local result
if ema1 > ema2 then
result = 1;
elseif ema1 < ema2 then
result = -1
end;
return result;
end;
-- Функция определния позиции по инструменту --
function position()
local res = 0;
for i = 0, getNumberOf("futures_client_holding") - 1 do
if getItem("futures_client_holding", i).sec_code == SecCode then
res = tonumber(getItem("futures_client_holding", i).totalnet);
end;
end;
return res;
end;
-- Функция обратного вызова запускается первой и создает таблицу торгового робота --
function OnInit()
message("Start trading robot!");
t_id = AllocTable(); -- создаем таблицу
AddColumn (t_id,1,"Time", true, QTABLE_TIME_TYPE, 15);
AddColumn (t_id,2,"Sec_Code", true, QTABLE_STRING_TYPE, 10);
AddColumn (t_id,3,"Position", true, QTABLE_STRING_TYPE, 10);
AddColumn (t_id,4,"Predict", true, QTABLE_STRING_TYPE, 15);
AddColumn (t_id,5,"Balance", true, QTABLE_STRING_TYPE, 10);
AddColumn (t_id,6,"Margin", true, QTABLE_STRING_TYPE, 10);
CreateWindow(t_id); -- создаем окно таблицы
SetWindowCaption(t_id, "Trading robot");
InsertRow(t_id, 1);
end;
-- Функция обратного вызова для получения результата отправки транзакции --
function OnTransReply(trans_reply)
if trans_reply.trans_id == trans_id then
trans_status = trans_reply.status;
trans_number = trans_reply.order_num;
end;
end;
-- Функция обратного вызова остановки скрипта --
function OnStop()
Run = false;
DestroyTable(t_id);
end;
-- Функция округления чисел --
function round(x, n)
n = math.pow(10, n or 0)
x = x * n
if x >= 0 then x = math.floor(x + 0.5) else x = math.ceil(x - 0.5) end;
return x / n
end;
Буду благодарен за помощь.
1-2)В QtyStopOrder для «SIMPLE_STOP_ORDER» указывай количество контрактов в два раза больше, тогда у тебя будет закрытие открытых позиций и открытие новых. Например, было открыто 3 лота, стоп ставишь 6. По достижении стопа 3 лота закроется и -3 лота откроется.
3) По поводу переноса через ночь: 'EXPIRY_DATE' = 'GTC' (до отмены). Но тут зависит от брокера: в ВТБ прокатывает, а СБЕР у меня требовал конкретную дату действия приказа. Там просто добавлял дней 10, например
1)нужно где-то прописать отслеживание самого индикаторы и выход из позы, если закрытие свечи пробивает ma
2)Это сработало бы при мгновенном перевороте, а мне нужно что бы выбился стоп. А вход(переворот) в другую сторону проходил по открытию следующего часа
3)в таком случае мне нужно руками каждый день прописывать статус робота. Я слышал, что есть базы банных(или внутренние библиотеки ), в которые можно записывать все сделки, что бы на утро все роботы торговали «свои контракты».
Ну и имейте ввиду, что main и все коллбэки — это 2 разных потока. Вот это изменение из main может быть видно в обратном порядке, например:
По уму робот это тысячи строк — с логированием и обработкой ошибок, которых емае сколько, везде есть нюансы
Я не возьмусь, т.к. фигню на 100 строчек неустойчивую совесть не даст продать за 10тыс ( а именно такие и продают гуры в инете), а нормальная устойчивая прога стоит больше, вам будет дорого моя цена
1)Не могу понять в какую часть когда записать следующее условие: нужно что бы робот выходил при пробитии закрытия свечи ma и делал переворот.
— суть вопроса остается неясной. Для полной версии нужно отслеживать состояние сделок (в позе/без позы) и в зависимости от этого уже плясать. Логировать/считывать можно с помощью io.open/write через .csv файл
2)Как прописать условие переворота, по открытию следующего часа, переворота при выбивании стопа.
— переворот по открытию следующего часа решается запросом времени сервера и дальнейшей работы с ним (например " getInfoParam(«SERVERTIME») ")
— переворот при выбивании стопа не нужен, т.к. стоп выбило и за ним следует просто вход в сделку по условию. В остальных случаях колбэки/чеки по number/id
3)Как прописать перенос сделок через ноч.
— самостоятельно в заявке указывать дату/GTC удержания заявки в строке EXPIRY_DATE