-- SuperScalp.lua, © hismatullin.h@gmail.com, xsharp.ru
-- с возможностями полного протоколирования действий программы.
local ver = '1.42' -- 25.12.2020, 11.11.2016
local lastPos = 0
local lastPrice = 0
local string_gmatch=string.gmatch
local string_find=string.find
local string_sub=string.sub
local string_len=string.len
local string_format=string.format
local math_modf=math.modf
local SeaGreen=12713921 -- RGB(193, 255, 193)
local RosyBrown=12698111 -- RGB(255, 193, 193)
local logFile
local trades = {}
local _trades = {}
local orders = {}
local scriptPath = getScriptPath()
local Terminal_Version=getInfoParam('VERSION')
local function versionLess(ver1,ver2)
local begin,ver_1=0
for ver_2 in string_gmatch(ver2,'%d+') do
_,begin,ver_1=string_find(ver1,'(%d+)',begin+1)
if ver_1~=ver_2 then return not ver_1 or ver_1+0<ver_2+0 end
end
return false
end
local table_insert = table.sinsert
local table_remove = table.sremove
local table_concat = table.sconcat
-----
local dc = QTABLE_DEFAULT_COLOR
local testQuik = true -- подписка на OnTransReply, OnTrade, OnOrder
--local testQuik = false -- без подписки
function firm_id()
for i = 0, getNumberOf("money_limits") - 1 do
local row = getItem("money_limits", i)
if row ~= nil and row.firmid ~= nil then
local ss = tostring(string_sub(row.firmid, 2, 2))
if ss == "C" or ss == "R" or ss == "B" then
return tostring(row.firmid)
end
end
end
return nil
end
local tostring__ = tostring
function tostring(x)
if math.tointeger(x) then
return string_format("%0i",x)
end
return tostring__(x)
end
local is_forts = true
--настройки
function getInitParameter()
if is_forts then
account = 'SPBFUT001ad'
classCode = 'SPBFUT'
secCode = 'SiH1'
OpenSlippage = 50
else
account="NL0011100043"
ClientCode = "1133"
classCode = 'QJSIM'
secCode = 'SBER'
OpenSlippage = 0.5
firm_id = firm_id()
message('firm_id = '..firm_id)
end
workSize = 1 -- рабочий размер
logFileName1 = 'logFile1.txt' -- файл для укороченного протоколирования коллбэков
logFileName2 = 'logFile2.txt' -- файл для печати всех полей коллбэков
end
--
function event_callback_tblH(t_id, msg, par1, par2)
if msg == QTABLE_LBUTTONDOWN then -- нажата левая кнопка мыши
lastPos = futures_position()
local status = tonumber(getParamEx(classCode, secCode,"status").param_value)
--local mes = secCode..', '..classCode..', нажато: купить-. status = '..status
if status ~= 1 or par1 == 1 then -- если бумага не торгуется, заявку не подаем
Highlight(t_id, par1, par2, RosyBrown, dc, 500)
if status ~= 1 then
mes = 'Ошибка статуса.'
message(mes); io_log(mes);
end
return
end
Highlight(t_id, par1, par2, SeaGreen, dc, 500) -- подсветка SeaGreen
if par1 == 2 and par2 == 1 then
if lastPos < 0 then
mes = secCode..'; нажато: купить, '..-lastPos
message(mes); io_log(mes);
Buy(classCode, secCode, -lastPos, 'CloseShort')
else
mes = secCode..'; нажато: купить, '..workSize
message(mes); io_log(mes);
Buy(classCode, secCode, workSize, 'OpenLong')
end
elseif par1 == 2 and par2 == 2 then -- продать, левая кнопка
if lastPos > 0 then
mes = secCode..'; нажато: продать, '..lastPos
message(mes); io_log(mes);
Sell(classCode, secCode, lastPos, 'CloseLong')
else
mes = secCode..'; нажато: продать, '..workSize
message(mes); io_log(mes);
Sell(classCode, secCode, workSize, 'OpenShort')
end
elseif par1 == 2 and par2 == 3 then -- закрыть позиции
if lastPos > 0 then
mes = secCode..'; нажато: закрыть лонги, '..lastPos
message(mes); io_log(mes);
Sell(classCode, secCode, lastPos, 'CloseAll')
elseif lastPos < 0 then
mes = secCode..'; нажато: закрыть шорты, '..-lastPos
message(mes); io_log(mes);
Buy(classCode, secCode, -lastPos, 'CloseAll')
else
mes = secCode..'; нажато: закрыть. Нет позиций для закрытия!'
message(mes); io_log(mes);
end
elseif par1 == 3 and par2 == 1 then
if lastPos < 0 then
mes = secCode..'; нажато: купить-, '..-lastPos
message(mes); io_log(mes);
BuyBid(classCode, secCode, -lastPos, 'CloseShort')
else
mes = secCode..'; нажато: купить-, '..workSize
message(mes); io_log(mes);
BuyBid(classCode, secCode, workSize, 'OpenLong')
end
elseif par1 == 3 and par2 == 2 then
if lastPos > 0 then
mes = secCode..'; нажато: продать+, '..lastPos
message(mes); io_log(mes);
SellOffer(classCode, secCode, lastPos, 'CloseLong')
else
mes = secCode..'; нажато: продать+, '..workSize
message(mes); io_log(mes);
SellOffer(classCode, secCode, workSize, 'OpenShort')
end
elseif par1 == 3 and par2 == 3 then
mes = secCode..'; нажато: Снять все заявки!'
message(mes); io_log(mes);
KillOrders()
end
elseif msg == QTABLE_CLOSE then
OnStop()
end
end
--
QTable ={}
QTable.__index = QTable
function QTable.new()
local t_id = AllocTable()
if t_id ~= nil then
q_table = {}
setmetatable(q_table, QTable)
q_table.t_id=t_id
q_table.caption = ""
q_table.created = false
q_table.curr_col=0
-- таблица с описанием параметров столбцов
q_table.columns={}
return q_table
else
return nil
end
end
tblH = QTable:new()
--
function get_trans_id()
local s = tostring(os.clock())
local x, g = string_find(s, "(%d+)")
s = string_sub(s, g + 2)
for i = 1, 3 - string_len(s) do
s = "0" .. s
end
return os.date("%H%M%S") .. s
end
--
function send_order(client, class, seccode, account, operation, quantity, price)
account = 'blablabla'
local trans_id = get_trans_id()
local mes = 'send_order, client = '..client..', class = '.. class..', seccode = '..seccode..', account = '..account
..', quantity = '..quantity.. ', operation = '..operation.. ', price = '..price.. ', trans_id = '..trans_id
message(mes); io_log(mes);
rus = false
--rus = true
if rus then
trans_params = {
CLASSCODE = class,
CLIENT_CODE = client,
SECCODE = seccode,
ACCOUNT = account,
TYPE = new_type,
TRANS_ID = trans_id,
OPERATION = operation,
QUANTITY = tostring(quantity),
PRICE = tostring(price),
ACTION = "NEW_ORDER"
}
else
if operation == "B" then
operation = 'Покупка'
else
operation = 'Продажа'
end
trans_params = {
["TRANS_ID"] = trans_id,
["Комментарий"] = client,
["ACTION"] = "Ввод заявки",
["CLASSCODE"] = class,
["Тип"] = "Лимитированная",
["Условие исполнения"] = "Поставить в очередь",
["Класс"] = class,
["Инструмент"] = seccode,
["Количество"] = tostring(quantity),
["Цена"] = tostring(price),
['К/П'] = operation,
["Торговый счет"] = account
}
end
return sendTransaction(trans_params)
end
--
function Buy(classCode, secCode, size, action)
local best_offer = getParamEx(classCode, secCode, "offer").param_value
local buyPrice = best_offer + (OpenSlippage or 0)
local res = send_order(action, classCode, secCode, account, "B", size, buyPrice)
if string_len(res) ~= 0 then
local mes = 'Ошибка: '..res..', '.. action..', '..secCode..', '.."B"..', '..size..', price='..buyPrice
message(mes,3); io_log(mes);
end
end
--
function Sell(classCode, secCode, size, action)
local best_bid = getParamEx(classCode, secCode, "bid").param_value
local sellPrice = best_bid - (OpenSlippage or 0)
local res = send_order(action, classCode, secCode, account, "S", size, sellPrice)
if string_len(res) ~= 0 then
local mes = 'Ошибка: '..res..', '.. action..', '..secCode..', '.."S"..', '..size..', price='..sellPrice
message(mes,3); io_log(mes);
end
end
--
function BuyBid(classCode, secCode, size, action)
local best_bid = getParamEx(classCode, secCode, "bid").param_value
local buyPrice = best_bid - (OpenSlippage or 0)
local res = send_order(action, classCode, secCode, account, "B", size, buyPrice)
if string_len(res) ~= 0 then
local mes = 'Ошибка: '..res..', '.. action..', '..secCode..', '.."B"..', '..size..', price='..buyPrice
message(mes,3); io_log(mes);
end
end
--
function SellOffer(classCode, secCode, size, action)
local best_offer = getParamEx(classCode, secCode, "offer").param_value
local sellPrice = best_offer + (OpenSlippage or 0)
local res = send_order(action, classCode, secCode, account, "S", size, sellPrice)
if string_len(res) ~= 0 then
local mes = 'Ошибка: '..res..', '.. action..', '..secCode..', '.."S"..', '..size..', price='..sellPrice
message(mes,3); io_log(mes);
end
end
--
function KillOrders()
local NumberOf = getNumberOf("orders")
for i = 0, NumberOf - 1 do
local ord = getItem("orders", i)
if ord.sec_code == secCode and ord.account == account then
local order_flag = get_order_status(ord.flags)
if order_flag.status == "active" then
trans_id = get_trans_id()
local trans_params = {
["CLASSCODE"] = classCode,
["TRANS_ID"] = trans_id,
["ACTION"] = "KILL_ORDER",
["ORDER_KEY"] = tostring(ord.order_num)
}
local res = sendTransaction(trans_params)
if 0 < string_len(res) then
local mes = 'Ошибка: '..res
message(mes,3); io_log(mes);
end
end
end
end
end
--
function HandleBS()
local t = tblH.t_id
AddColumn(t, 1, 'Бумага', true,QTABLE_CACHED_STRING_TYPE,12)
AddColumn(t, 2, 'ТЧП', true,QTABLE_STRING_TYPE,12)
AddColumn(t, 3, 'Цена послед.', true,QTABLE_STRING_TYPE,17)
CreateWindow(t)
SetWindowCaption(t, "SuperScalp "..ver)
SetWindowPos(t, 0, 100, 250, 120)
local li=InsertRow(t, -1)
SetCell(t, li, 1, secCode)
SetCell(t, li, 2, lastPos)
SetCell(t, li, 3, 'waiting')
local li=InsertRow(t, -1)
SetCell(t, li, 1, 'Купить')
SetCell(t, li, 2, 'Продать')
SetCell(t, li, 3, 'Закрыть')
local li=InsertRow(t, -1)
SetCell(t, li, 1, 'купить —')
SetCell(t, li, 2, 'продать +')
SetCell(t, li, 3, 'Снять все')
SetTableNotificationCallback(t,event_callback_tblH)
end
--
--прочитать ТТП и вытащить ТЧП.
function futures_position()
if is_forts then
local count=getNumberOf("futures_client_holding") --Позиции по клиентским счетам (фьючерсы)
for i=0,count-1, 1 do
local row=getItem("futures_client_holding",i)
if row.trdaccid~=nil then
local seccode=row.sec_code --Код фьючерсного контракта, "Инструмент"
local totn=row.totalnet --Текущие чистые позиции "ТЧП"
if seccode == secCode then
return totn
end
end
end
else
local t = getDepoEx(firm_id, ClientCode, secCode, account, 0)
if t then
local T = t.currentbal
SetCell(tblH.t_id, 1, 2, tostring(T))
positionColor(T)
return T
end
end
return 0
end
--
function positionColor(tot)
if tot>0 then
SetColor(tblH.t_id,1,2, SeaGreen, dc, dc, dc)
elseif tot<0 then
SetColor(tblH.t_id,1,2, RosyBrown, dc, dc, dc)
else
SetColor(tblH.t_id,1,2,dc, dc, dc, dc)
end
end
--
function get_order_status(flags)
local rt = {}
local band = bit.band
local tobit = bit.tobit
if band(tobit(flags), 1) ~= 0 and band(tobit(flags), 2) == 0 then
rt.status = "active"
elseif band(tobit(flags), 1) == 0 and band(tobit(flags), 2) ~= 0 then
rt.status = "cancelled"
elseif band(tobit(flags), 1) == 0 and band(tobit(flags), 2) == 0 then
rt.status = "filled"
else
rt.status = "unknown"
end
if band(tobit(flags), 4) ~= 0 then
rt.operation = "S"
else
rt.operation = "B"
end
return rt
end
--
--запись лога с текущим простым временем
function io_log(str)
local file, err = io.open(logFile, "a")
assert(file, "Ошибка записи "..logFile..", \n"..str)
local str0 = os.date("%X", os.time())
str0 = str0..'; '.. str
file:write(str0 .. "\n")
file:flush()
file:close()
return true
end
--
if testQuik then
-- 2.2.17 - Функция вызывается терминалом QUIK при получении ответа на транзакцию пользователя.
function OnTransReply(reply)
--if reply.account == account then
if running then
local mes = reply.sec_code..'; OnTrans, order_num = '..reply.order_num..', '..tostring(reply.price)..' x '..
tostring(reply.quantity)..', t_id = '..reply.trans_id..', account = '..reply.account
message(mes); io_log(mes);
end
--end
end
-- 2.2.3 Функция вызывается терминалом QUIK при получении сделки.
function OnTrade(trade)
--if trade.account == account then -- только если заявка из нашего счета - 01.11.2014
if running then
if not _trades[trade.trade_num] then
local mes = trade.sec_code..'; OnTrade, order_num = '..trade.order_num..', t_n = '..trade.trade_num..' ('..trade.price..'x'..trade.qty..'), account = '..trade.account
message(mes); io_log(mes);
_trades[trade.trade_num] = true
table_insert(trades,trade)
end
end
--end
end
--2.2.4 OnOrder Функция вызывается терминалом QUIK при получении новой заявки или при изменении параметров существующей заявки.
function OnOrder(order)
--if order.account == account then
local order_flag = get_order_status(order.flags)
local op = order_flag.operation
local mes = order.sec_code..'; OnOrder, '..op.. ', order_num = '..order.order_num..' ('..order.price..'x'..order.qty.."), t_id = "..order.trans_id..', flag = '..order.flags..", "..order.brokerref..", balance = "..order.balance..', '..order_flag.status..', account = '..order.account
message(mes); io_log(mes);
--end
end
end
--
function OnFuturesClientHolding(tab)
local sec_code = tab.sec_code
local totalnet = tab.totalnet
if running and sec_code == secCode then
local t= tonumber(totalnet)
if t ~= nil then
SetCell(tblH.t_id, 1, 2, tostring(totalnet), totalnet)
positionColor(t)
end
end
end
--
function OnAccountBalance(acc_bal)
if acc_bal.sec_code==secCode then
local t= acc_bal.currentpos
if t ~= nil then
SetCell(tblH.t_id, 1, 2, tostring(t))
positionColor(t)
end
end
end
function OnParam(class, seccode)
if seccode == secCode then -- выбираем нужную нам бумагу
local lp = tonumber(getParamEx(class, seccode, "last").param_value)
if lp > lastPrice then
Highlight(tblH.t_id, 1, 3, SeaGreen, dc, 1000) -- подсветка мягкий, зеленый
lastPrice = lp -- цена последней сделки
SetCell(tblH.t_id, 1, 3, tostring(lastPrice))
elseif lp < lastPrice then
Highlight(tblH.t_id, 1, 3, RosyBrown, dc, 1000) -- подсветка
lastPrice = lp
SetCell(tblH.t_id, 1, 3, tostring(lastPrice))
end
end
end
--
function OnStop()
local mes = 'Stop SuperScalp.'
message(mes); io_log(mes);
running = false
DestroyTable(tblH.t_id)
end
--
function OnInit()
getInitParameter()
logFile = scriptPath..'\\'..logFileName1
local mes = 'Start SuperScalp '..ver..', QUIK '..Terminal_Version
message(mes); io_log(mes);
running = true
HandleBS()
lastPos = futures_position()
SetCell(tblH.t_id, 1, 2, tostring(lastPos))
positionColor(lastPos)
end
--
function main()
while running do
sleep(1000)
if not is_forts then
local t = getDepoEx(firm_id, ClientCode, secCode, account, 0)
local T = t.currentbal
SetCell(tblH.t_id, 1, 2, tostring(T))
positionColor(T)
end
end
end
|