Все записи автора ram555

Таблица "Портфель" в QUIKе

Автор записи: ram555

Представляю таблицу для портфельных инвестиций.  Цвет строки меняется если Прибыль%<>5%. Обновление каждые 5 сек. Показаны только бумаги с лимитом Т0, для недопущения дублирования. Если хотите, чтоб были бумаги только с лимитом Т2, замените в двух местах «limitKind<1» на «limitKind>1»

Для её создания необходимо:
1. Создать файл «tablePortfolio.txt» в папке «C:\QUIK\Scripts». Если папки нет, создать её.
2. Скопировать туда код скрипта
3. Сохранить, выбрав кодировку «ANSI», иначе вместо русских букв могут быть кракозябры.
4. Сменить расширение файла с ".txt" на ".lua"
5. Запустить скрипт командой Сервисы-> Lua  скрипты-> Добавить (выбрать файл tablePortfolio.lua) -> Запустить

Код скрипта
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
IsRun = true
class_code="TQBR"
 
function main()
   -- Получает доступный id для создания
   t_id = AllocTable()
 
   -- добавить столбцы
   AddColumn(t_id, 1, "Бумага",       true, QTABLE_STRING_TYPE, 20)
   AddColumn(t_id, 2, "Кол-во",       true, QTABLE_INT_TYPE,     7)
   AddColumn(t_id, 3, "Цена покупки", true, QTABLE_DOUBLE_TYPE, 14)
   AddColumn(t_id, 4, "Цена текущая", true, QTABLE_DOUBLE_TYPE,   14)
   AddColumn(t_id, 5, "Прибыль, р",   true, QTABLE_DOUBLE_TYPE,   14)
   AddColumn(t_id, 6, "Прибыль, %",   true, QTABLE_DOUBLE_TYPE, 14)
   t = CreateWindow(t_id)
 
   for iRow=1, getNumberOf("depo_limits")-1, 1 do
      rowInPortfolioTable = getItem("depo_limits", iRow) -- получить текущую строку из таблицы "Лимиты по бумагам"
      qtyBoughtLots  = tonumber(rowInPortfolioTable.currentbal)
      limitKind = rowInPortfolioTable.limit_kind
      if qtyBoughtLots>0 and limitKind<1 then
         InsertRow(t_id, iRow)-- добавить новую строку вниз таблицы
      end
   end
   local rows, columns = GetTableSize (t_id)
   InsertRow(t_id, rows+1) -- добавить новую строку вниз таблицы для "Итого"
 
   SetWindowCaption(t_id, "Портфель: прибыли и убытки    © ramirzaev@mail.ru")
 
   -- исполнять цикл, пока пользователь не остановит скрипт или не закроет окно таблицы
   while IsRun do
      if IsWindowClosed(t_id)==true then
         IsRun=false
      end
 
      local currentPrice=0
      local qtyBoughtLots=0
      local profitAbs = 0
      local profitPerc = 0
      local currentSecCode= ""
      local fullNameOfInstrument = ""
      local limitKind = 0
      local rowInPortfolioTable = {}    -- строка из таблицы "Лимиты по бумагам"
      local tableInstrument = {}    -- данные "Таблицы текущих торгов"
      local iRowInOutTable = 1
	  local totalInvest = 0
	  local totalPortfolio = 0
	  local totalProfit = 0
	  local totalPercent = 0
 
      for iRow=0, getNumberOf("depo_limits")-1, 1 do
         rowInPortfolioTable = getItem("depo_limits", iRow) -- получить текущую строку из таблицы "Лимиты по бумагам"
 
         qtyBoughtLots  = tonumber(rowInPortfolioTable.currentbal)
 
         limitKind = rowInPortfolioTable.limit_kind
 
         if qtyBoughtLots>0 and limitKind<1    then      -- если кол-во лотов >0 и тип лимита T0
            currentSecCode = rowInPortfolioTable.sec_code
            fullNameOfInstrument =  tostring(getParamEx(class_code, currentSecCode, "SHORTNAME").param_image or "0") --"LONGNAME"
            avgPrice       = tonumber(rowInPortfolioTable.awg_position_price)
            currentPrice = GetAskPrice(currentSecCode)
            profitAbs = (currentPrice-avgPrice)*qtyBoughtLots
            profitPerc    = 100*currentPrice/avgPrice   - 100
 
			totalInvest = totalInvest + avgPrice*qtyBoughtLots
			totalPortfolio = totalPortfolio + currentPrice*qtyBoughtLots
 
            SetCell(t_id, iRowInOutTable, 1, fullNameOfInstrument) -- "Бумага"
            SetCell(t_id, iRowInOutTable, 2, tostring(qtyBoughtLots)) -- "Кол-во"RemoveZero(tostring(qtyBoughtLots)))
            SetCell(t_id, iRowInOutTable, 3, tostring( math_round(avgPrice, 3) ))  -- tostring(avgPrice))   -- "Цена покупки"
            SetCell(t_id, iRowInOutTable, 4, RemoveZero(tostring(currentPrice)))   -- "Цена текущая"
            SetCell(t_id, iRowInOutTable, 5, tostring( math_round( profitAbs, 0)) ) -- "Прибыль, р"
            SetCell(t_id, iRowInOutTable, 6, tostring(math_round(profitPerc, 1)) .."%") -- "Прибыль, %"
 
            if profitPerc >5 then       -- окрашиваем
               ColourRowInGreen(iRowInOutTable)
            elseif profitPerc<-5 then
               ColourRowInRed(iRowInOutTable)
            else
               ColourRowInYellow(iRowInOutTable)
            end
            iRowInOutTable = iRowInOutTable+1
         end
      end
      totalProfit = totalPortfolio - totalInvest
      totalPercent   = 100*totalProfit/totalInvest
	  SetCell(t_id, iRowInOutTable, 1, "Итого")
      SetCell(t_id, iRowInOutTable, 3, tostring( math_round(totalInvest, 0) ))
      SetCell(t_id, iRowInOutTable, 4, tostring( math_round(totalPortfolio, 0)))
      SetCell(t_id, iRowInOutTable, 5, tostring( math_round( totalProfit, 0)) )
      SetCell(t_id, iRowInOutTable, 6, tostring(math_round(totalPercent, 1)) .."%")
 
	  if profitPerc >5 then       -- окрашиваем
               ColourRowInGreen(iRowInOutTable)
            elseif profitPerc<-5 then
               ColourRowInRed(iRowInOutTable)
            else
               ColourRowInYellow(iRowInOutTable)
            end
            iRowInOutTable = iRowInOutTable+1
      sleep(5000) -- пауза 5 сек.
      end
   --message("script table portfolio finished")
end
 
 
function ColourRowInRed(num_row)
   SetColor(t_id, num_row, QTABLE_NO_INDEX, RGB(255,150,150), RGB(0,0,0), RGB(255,150,150), RGB(0,0,0))
end
function ColourRowInYellow(num_row)
   SetColor(t_id, num_row, QTABLE_NO_INDEX, RGB(255,255,200), RGB(0,0,0), RGB(255,255,200), RGB(0,0,0))
end
function ColourRowInGreen(num_row)
   SetColor(t_id, num_row, QTABLE_NO_INDEX, RGB(150,255,150), RGB(0,0,0), RGB(150,255,150), RGB(0,0,0))
end
function GetAskPrice(inp_Sec_Code )
   local ask = tostring(getParamEx(class_code, inp_Sec_Code, "OFFER").param_value or 0)
   return ask
end
-- Округляет число до указанной точности
function math_round (num, idp)
   local mult = 10^(idp or 0)
   return math.floor(num * mult + 0.5) / mult
end
-- удаление точки и нулей после нее
function RemoveZero(str)
   while (string.sub(str,-1) == "0" and str ~= "0") do
      str = string.sub(str,1,-2)
   end
   if (string.sub(str,-1) == ".") then
      str = string.sub(str,1,-2)
   end
   return str
end
function OnStop()
   DestroyTable(t_id)
   IsRun = false
end

ПОСТАНОВКА И СНЯТИЕ STOP-ОРДЕРА В QLUA(LUA)

Автор записи: ram555

Когда передо мной встала задача удаления поставленного стоп-ордера, наткнулся в интернете на скудность информации по данной тематике.

Самая распространенная ошибка начинающего программиста отправка в SendTransaction в STOP_ORDER_KEY  trans_id стоп-ордера

Робот выставляет стоп-заявку на покупку по определенной цене, затем через 2 секунды снимает её.

Также в коде имеются следующие фишки:

  • Запись удобочитаемого лог-файла.  Записи с интервалом <=1 сек. группируются в пул. Между пулами - пустая строка. После остановки скрипта в файл добавляется двойная линия.
  • Функция преобразования числа в строку с удалением точки и нулей правее нее для отправки этой строки в SendTransaction
  • Функция, возвращающая Entry или Exit в зависимости от trans_id принадлежности транзакций к входу или выходу
Код скрипта
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
--/*НАСТРАИВАЕМЫЕ ПАРАМЕТРЫ*/
NAME_OF_STRATEGY = 'Str1_' -- НАЗВАНИЕ СТРАТЕГИИ (не более 9 символов!)
CLASS_CODE = "QJSIM" -- Код класса SPBFUT
SEC_CODE = "SBER" -- Код бумаги SiZ6 SiH7, SiM7, SiU7
ACCOUNT = "NL0011100043" -- Идентификатор счета SPBFUT00355
CLIENT_CODE = NAME_OF_STRATEGY..SEC_CODE -- "Код клиента"
QTY_LOTS = "1" -- Кол-во торгуемых лотов
FILE_LOG_NAME = "C:\\TRADING\\QUIK Junior\\Scripts\\Log.txt" -- ИМЯ ЛОГ-ФАЙЛА
 
--/*РАБОЧИЕ ПЕРЕМЕННЫЕ РОБОТА (менять не нужно)*/
g_price_step = 0 -- ШАГ ЦЕНЫ ИНСТРУМЕНТА
g_trans_id_entry = 110001 -- Задает начальный номер ID транзакций на вход
g_trans_id_exit = 220001 -- Задает начальный номер ID транзакций на выход
g_arrTransId_entry = {} -- массив ID транзакций на вход
g_arrTransId_exit = {} -- массив ID транзакций на выход
g_transId_del_order = "1234" -- ID ордера на удаление заявки (не меняется)
g_transId_del_stopOrder = "6789" -- ID ордера на удаление стоп заявки (не меняется)
g_currentPosition = 0 -- в позиции? сколько лотов и какое направление
g_IsTrallingStop = false -- выставлен ли трейлинг стоп на сервере
g_stopOrderEntry_num= "" -- номер стоп-заявки на вход в системе, по которому её можно снять
g_stopOrderExit_num = "" -- номер стоп-заявки на выход в системе, по которому её можно снять
g_order_num = "" -- номер заявки в системе, по которому её можно снять
g_oldTrade_num = "" -- номер предыдущей обработанной сделки
g_previous_time = os.time() -- помещение в переменную времени сервера в формате HHMMSS
isRun = true -- Флаг поддержания работы бесконечного цикла в main
 
function OnInit()
   -- Получает ШАГ ЦЕНЫ ИНСТРУМЕНТА
    g_price_step = getParamEx(CLASS_CODE, SEC_CODE, "SEC_PRICE_STEP").param_value
 
    f = io.open(FILE_LOG_NAME, "a+") -- открывает файл
    myLog("Initialization finished")
end
function main()
   g_trans_id_exit = g_trans_id_exit + 1
   g_arrTransId_exit[#g_arrTransId_exit+1] = g_trans_id_exit
 
   SendStopOrder("131.9", QTY_LOTS, "B", g_trans_id_exit) -- Отправить стоп ордер
   sleep(2000)                                            -- через 2 секунды
   DeleteStopOrder(g_stopOrderExit_num)                   -- удалить стоп-ордер
 
   while isRun do
      sleep(5000) -- обрабатываем цикл с задержкой 5сек.
   end
end
 
-- функция вызывается терминалом ТЕРМИНАЛОМ QUIK при остановке скрипта
function OnStop()
   myLog("Script Stoped")
   f:close() -- Закрывает файл
   isRun = false
end
 
function SendStopOrder(stopPrice, quantity, operation, trans_id)
   local offset=50 -- отступ для гарантированного исполнения ордера по рынку (в кол-ве шагов цены)
   local price
   local direction
 
   if operation=="B" then
      price = stopPrice + g_price_step*offset
      direction = "5" -- Направленность стоп-цены. «5» - больше или равно
   else
      price = stopPrice - g_price_step*offset
      direction = "4" -- Направленность стоп-цены. «4» - меньше или равно
   end
   --message("stopPrice"..stopPrice)
   --Пошлем стоп заявку
   local Transaction = {
                       ['ACTION'] = "NEW_STOP_ORDER",
                       ['PRICE'] = tostring(price),
                       ['EXPIRY_DATE'] = "TODAY",--"GTC", -- на учебном серве только стоп-заявки с истечением сегодня, потом поменять на GTC
                       ['STOPPRICE'] = tostring(stopPrice),
                       ['STOP_ORDER_KIND'] = "SIMPLE_STOP_ORDER",
                       ['TRANS_ID'] = removeZero(tostring(trans_id)),
                       ['CLASSCODE'] = CLASS_CODE,
                       ['SECCODE'] = SEC_CODE,
                       ['ACCOUNT'] = ACCOUNT,
                       ['CLIENT_CODE'] = CLIENT_CODE, -- Комментарий к транзакции, который будет виден в транзакциях, заявках и сделках
                       ['TYPE'] = "L",
                       ['OPERATION'] = tostring(operation),
                       ['CONDITION'] = direction, -- Направленность стоп-цены. Возможные значения: «4» - меньше или равно, «5» – больше или равно
                       ['QUANTITY'] = tostring(math.abs(quantity))
                       }
   local res = sendTransaction(Transaction)
   if string.len(res) ~= 0 then
      message('Error: '..res, 3)
      myLog("SendStopOrder(): Error "..res)
   else
      myLog("SendStopOrder(): "..EntryOrExit(trans_id).."; trans_id="..trans_id)
   end
end
 
function DeleteStopOrder(stopOrder_num)
   local Transaction = {
                       ['ACTION'] = "KILL_STOP_ORDER",
                       ['CLASSCODE'] = CLASS_CODE,
                       ['SECCODE'] = SEC_CODE,
                       ['ACCOUNT'] = ACCOUNT,
                       ['CLIENT_CODE'] = CLIENT_CODE,
                       ['TYPE'] = "L",
                       ['STOP_ORDER_KIND'] = "SIMPLE_STOP_ORDER",
                       ['TRANS_ID'] = g_transId_del_order, -- ID УДАЛЯЮЩЕЙ транзакции
                       ['STOP_ORDER_KEY'] = tostring(stopOrder_num)
                       }
 
   local res = sendTransaction(Transaction)
   if string.len(res) ~= 0 then
      message('Error: '..res, 3)
      myLog("DeleteStopOrder(): fail "..res)
   else
      myLog("DeleteStopOrder(): "..stopOrder_num.." success ")
   end
end
-- создан/изменен/сработал стоп-ордер
function OnStopOrder(stopOrder)
   -- Если не относится к роботу, выходит из функции
   if stopOrder.brokerref:find(CLIENT_CODE) == nil then return end
 
   local string state="_" -- состояние заявки
   --бит 0 (0x1) Заявка активна, иначе не активна
   if bit.band(stopOrder.flags,0x1)==0x1 then
      state="стоп-заявка создана"
      if EntryOrExit(stopOrder.trans_id) == "Entry" then
         g_stopOrderEntry_num = stopOrder.order_num
      end
      if EntryOrExit(stopOrder.trans_id) == "Exit" then
         g_stopOrderExit_num = stopOrder.order_num
      end
   end
   if bit.band(stopOrder.flags,0x2)==0x1 or stopOrder.flags==26 then
      state="стоп-заявка снята"
   end
   if bit.band(stopOrder.flags,0x2)==0x0 and bit.band(stopOrder.flags,0x1)==0x0 then
      state="стоп-ордер исполнен"
   end
   if bit.band(stopOrder.flags,0x400)==0x1 then
      state="стоп-заявка сработала, но была отвергнута торговой системой"
   end
   if bit.band(stopOrder.flags,0x800)==0x1 then
      state="стоп-заявка сработала, но не прошла контроль лимитов"
   end
   if state=="_" then
      state="Набор битовых флагов="..tostring(stopOrder.flags)
   end
   myLog("OnStopOrder(): sec_code="..stopOrder.sec_code.."; "..EntryOrExit(stopOrder.trans_id)..";\t"..state..
         "; condition_price="..stopOrder.condition_price.."; transID="..stopOrder.trans_id.."; order_num="..stopOrder.order_num )
end
------------------------- Сервисные функции--------------------
-- функция записывает в лог строчку с временем и датой
function myLog(str)
   if f==nil then return end
 
   local current_time=os.time()--tonumber(timeformat(getInfoParam("SERVERTIME"))) -- помещене в переменную времени сервера в формате HHMMSS
   if (current_time-g_previous_time)>1 then -- если текущая запись произошла позже 1 секунды, чем предыдущая
      f:write("\n") -- добавляем пустую строку для удобства чтения
   end
   g_previous_time = current_time
 
   f:write(os.date().."; ".. str .. ";\n")
 
   if str:find("Script Stoped") ~= nil then
      f:write("======================================================================================================================\n\n")
      f:write("======================================================================================================================\n")
   end
   f:flush() -- Сохраняет изменения в файле
end
-- удаление точки и нулей после нее
function removeZero(str)
   while (string.sub(str,-1) == "0" and str ~= "0") do
      str = string.sub(str,1,-2)
   end
   if (string.sub(str,-1) == ".") then
      str = string.sub(str,1,-2)
   end
   return str
end
-- Возвращает Entry или Exit в зависимости от trans_id
function EntryOrExit(trans_id)
   if isArrayContain(g_arrTransId_entry, trans_id) then
      return "Entry"
   elseif isArrayContain(g_arrTransId_exit, trans_id) then
      return "Exit"
   elseif trans_id==g_transId_del_order then
      return "DeleteOrder"
   elseif trans_id==g_transId_del_stopOrder then
      return "DeleteStopOrder"
   else
      return "NoId"
   end
end
 
function isArrayContain(array, value)
   if #array < 1 then return end
      for i=1, #array do
         if array[i]==value then
         return true
      end
   end
   return false
end

В итоге получается текстовый  лог-файл, удобный для чтения человеком, с которого можно считать данные программой (например на  C#) и подбить статистику

Log.txt

01/17/17 19:34:56; Initialization finished;
01/17/17 19:34:56; SendStopOrder(): Exit; trans_id=220002;
01/17/17 19:34:56; OnStopOrder(): sec_code=SBER; Exit; стоп-заявка создана; condition_price=131.9; transID=220002; order_num=7902164;

01/17/17 19:34:58; DeleteStopOrder(): 7902164 success ;
01/17/17 19:34:58; OnStopOrder(): sec_code=SBER; Exit; стоп-заявка снята; condition_price=131.9; transID=220002; order_num=7902164;

01/17/17 19:35:01; Script Stoped;
======================================================================================================================

======================================================================================================================

Если у Вас появились какие-то вопросы, задайте их в комментариях под статьей !!!