Блог им. Mrak

Алготрейдинг в опционной торговле на Qlua. (МНОГО КОДА!)

    • 24 октября 2018, 17:35
    • |
    • Mrak
  • Еще
Добрый день, уважаемые алготрейдеры!
Написал на днях некий алгоритм самостоятельного расчета греков опционов на Qlua срочном рынке ММВБ-РТС, которые 
показываются в виде таблицы значений в Quik.

Подскажите, каким образом добавить в этот алгоритм выставление заявок после сравнения расчетных величин с теми, что транслирует биржа?
RiskFree=5.6/100 
BaseClassCode = "SPBFUT"  
ClassCode = "SPBOPT"  
BaseSecList = "SiZ8"  
SecList = getClassSecurities(ClassCode) 
INTERVAL = 1000  
doLogging=true log_file=getScriptPath() .. "\\last.csv"  
 
tbl = { 
["caption"]="Greek",
 [1]="Название",
 [2]="Код опциона",
 [3]="Тип опциона",
 [4]="Баз. актив",
 [5]="Расчетная цена",
 [6]="Страйк",
 [7]="Волатильность",
 [8]="До исполнения",
 [9]="Дельта",
 [10]="Гамма(%)",
 [11]="Тэта",
 [12]="Вега",
 [13]="Ро",
 [14]="Цена",
 [15]="last",
 [16]="quik",
 ["t_id"]=0
 }
 abTable = {}
 BaseCol = {}
 Sec2row = {}
 file = nil
 Sep = ";"
 YearLen=365.0
 WORK = true
 CALC = false
 G_ROW = -1
 if (BaseSecList == "") or (BaseSecList == nil) then
 BaseSecList = getClassSecurities(BaseClassCode) 
 end 
 if (SecList == "") or (SecList == nil) then
 SecList = getClassSecurities(ClassCode)
 end 
 function Logging(str)
 if file~=nil and doLogging then
 file:write(str .. "\n")
 file:flush()        
end 

end
 function N(x)
 if (x > 10) then
 return 1    
 elseif (x < -10) then
 return 0    
 else
 local t = 1 / (1 + 0.2316419 * math.abs(x))       
 local p = 0.3989423 * math.exp(-0.5 * x * x) * t * ((((1.330274 * t - 1.821256) * t + 1.781478) * t - 0.3565638) * t + 0.3193815)       
 if x > 0 then
 p=1-p
 end        
 return p
 end 
 
end 
function pN(x)     
return math.exp(-0.5 * x * x) / math.sqrt(2 * math.pi)  
end 
 
function PriceOpt(tmpGreek,tmpParam)
     local b = tmpParam.volatility / 100
     local S = tmpParam.settleprice
     local Tt = tmpParam.DAYS_TO_MAT_DATE / YearLen
     local K =  tmpParam.strike
     local r = RiskFree
     local delta = tmpGreek.Delta
	 local rho = tmpGreek.Rho
	 local d1 = (math.log(S / K) + (r + b * b * 0.5) * Tt) / (b * math.sqrt(Tt))
	 local d2 = d1-(b * math.sqrt(Tt))
	 local e = math.exp(-1 * r * Tt)
	 local Price = 0
	 if tmpParam.Optiontype == "Call" then
	 Price = S*e * N(d1) - K * e * N(d2)
	 else      
	 Price = K * e * N(-1 * d2)-S*e * N(-1*d1) 
	 end

	 if Price<1 then 
	 Price = 1
	 end 
	 return {["Price"] = Price} 
	 end 
	 function Greek(tmpParam)    
	 local b = tmpParam.volatility / 100     
	 local S = tmpParam.settleprice    
	 local Tt = tmpParam.DAYS_TO_MAT_DATE / YearLen    
	 local K =  tmpParam.strike     
	 local r = RiskFree     
	 local d1 = (math.log(S / K) + (r + b * b * 0.5) * Tt) / (b * math.sqrt(Tt))    
	 local d2 = d1-(b * math.sqrt(Tt)) 
	 local Delta = 0 
	 local Gamma = 0 
	 local Theta = 0 
	 local Vega = 0 
	 local Rho = 0  
	 local e = math.exp(-1 * r * Tt) 

 
   Gamma = pN(d1) / (S * b * math.sqrt(Tt))
   Vega = S * e * pN(d1) * math.sqrt(Tt)
   Theta = (-1 * S * b * e * pN(d1)) / (2 * math.sqrt(Tt))
   if tmpParam.Optiontype == "Call" then
   Delta = e * N(d1)
   Theta = Theta - (r * K * e * N(d2)) + r * S * e * N(d1)
   
   Rho = K * Tt * e * N(d2)
   else       
   Delta = -1 * e * N(-1*d1)       
   Theta = Theta + (r * K * e * N(-1 * d2)) - r * S * e * N(-1 * d1)       
      Rho = -1 * K * Tt * e * N(-1 * d2)
 end 
 return {    
 ["Delta"] = Delta,
 ["Gamma"] = 100 * Gamma,
 ["Theta"] = Theta / YearLen,
 ["Vega"] = Vega / 100,
 ["Rho"] = Rho / 100    }
 end 
 function GetRow(ID,row)
 local rows, col = GetTableSize(ID)
 local result = "" 
 if rows~=nil and row<=rows then
 for i=1,col do
 result=result..GetCell(ID,row,i).image .. Sep
 end 
 end    
 return result
 end

 function CSV(T)
 function FTEXT(V)
 V=tostring(V)
 if (string.len(V)==1) or (string.len(V)==5) then
 V="0".. V      
 end
 return V
 end
 local temp = os.date("*t")
 local Fname =getScriptPath() .. "\\" .. FTEXT(temp.year) .. FTEXT(temp.month)
 .. FTEXT(temp.day) .. ".csv"
 CSVFile = io.open(Fname, "w+")
 if CSVFile~=nil then
 local rows, col = GetTableSize(T.t_id)
 for i=1,col do
 CSVFile:write(T[i] .. Sep)
 end       
 CSVFile:write("\n")
 for i=1,rows do
 CSVFile:write(GetRow(T.t_id,i).."\n")
 end             
 CSVFile:flush()
 CSVFile:close()
 message("Файл успешно сохранен:\n"..Fname, 1)
 else        
 message("Ошибка при сохранении файла:\n"..Fname, 3)
 
   end
   end
   
   function round(num, idp)
   local mult = 10^(idp or 0)
   return math.floor(num * mult + 0.5) / mult
   end
   function comma_value(n)
   local left,num,right = string.match(n,'^([^%d]*%d)(%d*)(.-)$')
   return left..(num:reverse():gsub('(%d%d%d)','%1 '):reverse())..right
   end
   function CreateDataSourceEX(Class,Sec,Par)
   local ds,err = CreateDataSource(Class, Sec, INTERVAL_TICK, Par)
   if ds==nil then
   message("Ошибка при получении параметра "..Par..":\n"..err, 3)
   return false
   else
   ds:SetEmptyCallback()
   while ds:Size()==nil   do
   sleep(100)
   end
   return true
   end
   end
   
   function Stop()    
   if doLogging then
   file:close()
   end
   WORK = false
   end
   
   function Calculate(row,do_calc)
   if (row~=nil) and (row>=0) and (do_calc) then 
 
         local T=BaseCol[row] 
 
         local tmpParam ={
		 ["Optiontype"] = T.Optiontype,
		 ["settleprice"] = getParamEx(BaseClassCode,T.Optionbase,"settleprice").param_value+0,
		 ["strike"] = getParamEx(ClassCode,T.SecCode,"strike").param_value+0,
		 ["volatility"] = getParamEx(ClassCode,T.SecCode,"volatility").param_value+0,
		 ["DAYS_TO_MAT_DATE"] = T.DAYS_TO_MAT_DATE,
		 ["last"] = getParamEx(ClassCode,T.SecCode,"last").param_value+0,
		 ["THEORPRICE"] = getParamEx(ClassCode,T.SecCode,"THEORPRICE").param_value+0
		 }
		 local tmpGreek = Greek(tmpParam)
		 local tmpPrice = PriceOpt(tmpGreek,tmpParam)
		 SetCell(tbl.t_id, row, 5, comma_value(tmpParam.settleprice), tmpParam.settleprice) -- "Расчетная цена",
		 SetCell(tbl.t_id, row, 6, comma_value(tmpParam.strike), tmpParam.strike) --"Страйк",
		 SetCell(tbl.t_id, row, 7, tostring(tmpParam.volatility), tmpParam.volatility) -- "Волатильность",
          SetCell(tbl.t_id, row, 8, tostring(tmpParam.DAYS_TO_MAT_DATE), tmpParam.DAYS_TO_MAT_DATE) --"До исполнения",
          SetCell(tbl.t_id, row, 9, tostring(round(tmpGreek.Delta,2)), tmpGreek.Delta) --"Дельта",
          SetCell(tbl.t_id, row, 10, tostring(round(tmpGreek.Gamma,4)), tmpGreek.Gamma) -- "Гамма(%)",
          SetCell(tbl.t_id, row, 11, tostring(round(tmpGreek.Theta,2)), tmpGreek.Theta) -- "Тэта",
          SetCell(tbl.t_id, row, 12, tostring(round(tmpGreek.Vega,2)), tmpGreek.Vega) -- "Вега",
          SetCell(tbl.t_id, row, 13, tostring(round(tmpGreek.Rho,2)), tmpGreek.Rho) -- "Ро",
		  SetCell(tbl.t_id, row, 14, comma_value(round(tmpPrice.Price,2)), tmpPrice.Price)
		  SetCell(tbl.t_id, row, 15, tostring(tmpParam.last), tmpParam.last)
		  SetCell(tbl.t_id, row, 16, tostring(tmpParam.THEORPRICE), tmpParam.THEORPRICE)
		  Logging(os.date().. Sep .. GetRow(tbl.t_id,row))
		  end 
		  return false
		  end
		  
		  function f_cb(t_id,msg,par1,par2)
		  if (msg==QTABLE_CHAR)		  and (par2==19) then
		  CSV(tbl)
		  end
		  if (msg==QTABLE_CLOSE) then
		  Stop()
		  end
		  end
		  function OnStop()
		  Stop()
		  DestroyTable(tbl.t_id) 
		  end
		  function OnInit()
		  local STR = ""
		  if doLogging then
		  file = io.open(log_file, "w+")
		  end
		  tbl.t_id = AllocTable()
		  for i=1,table.maxn(tbl) do
		  if i<=4 then
          AddColumn(tbl.t_id, i, tbl[i], true, QTABLE_CACHED_STRING_TYPE, string.len(tbl[i])*2)
		  else
          AddColumn(tbl.t_id, i, tbl[i], true, QTABLE_DOUBLE_TYPE, 10)
		  end
		  if doLogging then
          STR=STR..tbl[i]..Sep
		  end
		  end
		  Logging("Дата Время".. Sep .. STR)
		  CreateWindow(tbl.t_id)
		  SetWindowCaption(tbl.t_id,tbl.caption)    SetTableNotificationCallback(tbl.t_id, f_cb)
		  end
		  function OnParam(class, sec) 
 
if (class==ClassCode) and (WORK) and (string.find(SecList,sec)~=nil) then
    G_ROW = Sec2row[sec]
    if (G_ROW~=nil) and (G_ROW>=0) then
	Highlight(tbl.t_id, G_ROW, QTABLE_NO_INDEX, RGB(255,0,0), QTABLE_DEFAULT_COLOR, 1000)
	CALC=true
    CALC=Calculate(G_ROW,CALC)
    end
	end
	end
	
	function main()
	WORK = false
	CALC=true
    for SecCode in string.gmatch(SecList, "([^,]+)") do --перебираем опционы по очереди.
	local Optionbase=getParamEx(ClassCode,SecCode,"optionbase").param_image
	local Optiontype=getParamEx(ClassCode,SecCode,"optiontype").param_image
	if (string.find(BaseSecList,Optionbase)~=nil) and (Optiontype=="Call") then
	local row = InsertRow(tbl.t_id,-1)
	local T={
	["Name"] = getSecurityInfo(ClassCode,SecCode).name,
	["SecCode"] = SecCode,
	["Optiontype"] = Optiontype,
	["Optionbase"] = Optionbase,
	["DAYS_TO_MAT_DATE"] = getParamEx(ClassCode,SecCode,"DAYS_TO_MAT_DATE").param_value+0
	}
	BaseCol[row]=T
	Sec2row[SecCode]=row
	SetCell(tbl.t_id, row, 1, BaseCol[row].Name) -- "Название опциона",
	SetCell(tbl.t_id, row, 2, BaseCol[row].SecCode) --"Код опциона",
	SetCell(tbl.t_id, row, 3, BaseCol[row].Optiontype) -- "Тип опциона",
	SetCell(tbl.t_id, row, 4, BaseCol[row].Optionbase) --"Баз. актив",
	CreateDataSourceEX(BaseClassCode,T.Optionbase,"settleprice")
	CreateDataSourceEX(ClassCode,T.SecCode,"strike")
	CreateDataSourceEX(ClassCode,T.SecCode,"volatility")
	CreateDataSourceEX(ClassCode,T.SecCode,"THEORPRICE")
	CALC=Calculate(row,true)
	end
    end
	
	for SecCode in string.gmatch(SecList, "([^,]+)") do
	local Optionbase=getParamEx(ClassCode,SecCode,"optionbase").param_image
	local Optiontype=getParamEx(ClassCode,SecCode,"optiontype").param_image
	if (string.find(BaseSecList,Optionbase)~=nil) and (Optiontype=="Put") then
	local row = InsertRow(tbl.t_id,-1)
	local T={
	["Name"] = getSecurityInfo(ClassCode,SecCode).name,
	["SecCode"] = SecCode,
	["Optiontype"] = Optiontype,
	["Optionbase"] = Optionbase,
	["DAYS_TO_MAT_DATE"] = getParamEx(ClassCode,SecCode,"DAYS_TO_MAT_DATE").param_value+0
	}
	BaseCol[row]=T
	Sec2row[SecCode]=row
	SetCell(tbl.t_id, row, 1, BaseCol[row].Name) -- "Название опциона",
	SetCell(tbl.t_id, row, 2, BaseCol[row].SecCode) --"Код опциона",
	SetCell(tbl.t_id, row, 3, BaseCol[row].Optiontype) -- "Тип опциона",
	SetCell(tbl.t_id, row, 4, BaseCol[row].Optionbase) --"Баз. актив",
	CreateDataSourceEX(BaseClassCode,T.Optionbase,"settleprice")
	CreateDataSourceEX(ClassCode,T.SecCode,"strike")
	CreateDataSourceEX(ClassCode,T.SecCode,"volatility")
	CreateDataSourceEX(ClassCode,T.SecCode,"last")
	CreateDataSourceEX(ClassCode,T.SecCode,"THEORPRICE")
	CALC=Calculate(row,true)
	end
    end
	
	WORK = true
	while WORK do
    sleep(INTERVAL)
	end
	end
Алготрейдинг в опционной торговле на Qlua. (МНОГО КОДА!)



★10
Как вариант:
smart-lab.ru/blog/195508.php
Только начиная с версии 7.6 Квика этот фреймворк не совсем корректно работает, надо hacktrade.lua допилить немного. Или старый Квик использовать.


avatar

Vkt

Vkt, мудреная штука… технологичная… слишком технологичная… через день-другой на ней лимитки начинают отваливаться, я так и не понял почему… хотя дизайнархитектура конечно вызывает искреннее уважение к автору сего конструкта.

А автору сего поста я бы посоветовал глянуть в сторону QL — она дубовая, здоровая, но зато надежная как железный лом.

но если по правде, то ан самом деле все эти расчеты херня представляют собой сугубо теоретическую ценность до тех пор пока вы не научитесь влёт считать цену закрытия в 18.38.
Остался Е. без новой рубашки
avatar

Lev

«Подскажите, каким образом добавить в этот алгоритм выставление заявок после сравнения расчетных величин с теми, что транслирует биржа?» — про греки написал, а как заявки выставлять не разобрался?
avatar

f0xtr0t

греки, греки...
греки — херня! 
торговать то в профит как!?

....все тэги
UPDONW