Избранное трейдера AntiKukl
Добрый день. В предыдущем посте были описаны базовые компоненты – классы обёртки над API брокера. Не хотелось нагружать их дополнительной логикой, поэтому оставим их как есть, и перейдём к чуть более сложному объекту. На сцене появляется IOrderManager, который отвечает за заявки и сделки по ним.
interface IOrderManager { List<Order> GetOrders(string symbol, int strategyID); void PlaceOrder(string symbol, int strategyID, OrderAction action, OrderType type, double price, double amount, double stopPrice); }
Всего два метода – выставить заявку и получить их список. Но, у реализации IOrderManager’а непростая задача – надо не просто выставлять заявки, но также хранить какая стратегия это сделала и какие прошли сделки. Получается, у OrderManager’а есть некое состояние – список заявок/сделок, поэтому этот объект относится больше к модели, чем к сервисному слою программы. Перед этим я описывал IPortfolioGate – класс-обёртка для работы с портфелем, вот у него нет состояния, он просто транслирует вызов методов внешней COM библиотеки, а вот OrderManager это некий дополнительный уровень над всем этим – у него появляются «знания» о предметной области, и именно он используется в классах стратегий.
Также, появляются две сущности – заявка (Order) и сделка (Trade). Класс Order имеет список сделок прошедших по данной заявке.
class Order { public string Symbol { get; set; } public OrderAction Action { get; set; } public double Price { get; set; } … public List<Trade> Trades { get; set; } }
Приветствую! В предыдущем посте была теория, теперь к делу. Кое-что буду упрощать, чтобы представить картинку в целом.
Итак, чтобы проект не зависел от API внешней com библиотеки (SmartCom или д.р.), чтобы в коде стратегий не использовались специфические типы, разработку я начала с обёрток над смарткомом. Я определил три базовых интерфейса: IConnectGate, IMarketDataGate и IPortfolioGate. Соответственно для подключения, для получения маркет-даты и для выставления заявок и работы с портфелем. Причём каждый из этих трёх интерфейсов мне надо было реализовать минимум дважды – для смарткома и для локального тестера.
В случае со смарткомом, это некий адаптер-обёртка, благодаря которому, я оперирую собственными типами и не завишу от com библиотеки. Т.е. у меня есть свои типы (например, направление заявки, тайм-фрейм), которые используются в коде, а адаптер-обёртка конвертирует их в специфические, понятные внешней библиотеке. Также, желательно, чтобы у каждого объекта, в программе, была только одна обязанность, поэтому никакой дополнительной логики эти обёртки не несут.
Добрый день. В предыдущем посте я вкратце описал предпосылки и суть системы. Сегодня будет немного теории, я думал не вставлять эту часть, но описанные здесь термины будут использоваться в последующих статьях.
Даже такие проекты, как торговая система, требуют определённого подхода к структуре – проект разрастается со временем, хочется большего, и на это надо закладываться в самом начале. Даже, если система пишется под себя, не стоит уклоняться от более формального подхода. Это, как ставить кубик на кубик — хорошая расстановка даст более прочную башню. Такие проекты справляются с увеличением сложности, новые изменения усиливают проект, в конце концов, такие проекты, способны перерасти во что-то более сложное (типа фазового перехода). А проекты с непродуманной архитектурой наоборот – со временем вносить изменения становится мучительнее и дороже, возрастают затраты на обслуживание, новые изменения ослабляют проект и он не способен перерасти во что-то более сложное. Но, сами понимаете, не всё так просто. Выбирая, каким будет проект, мы, опираясь на опыт, всё равно, угадываем направления развития.
Привет всем! Хотел опубликовать серию постов с описанием, как я на C# разработал систему для тестирования и торговли. Уклон будет больше в программирование, но в рамках алго.
Смысл в том, что я старался придерживаться правил ООП и сделать систему простой и конфигурируемой. В нескольких статьях я простыми словами расскажу про фишечки программирования, которые использовал. Расскажу про подходы к написанию объектно-ориентированного кода и про соответствующие библиотеки, которые использовал. Уделю внимание базам данных, как можно связываться с базами посредством объектно-реляционных преобразований и про сам SQL. Опишу, что такое внедрение зависимостей и IoC контейнер, и как благодаря этому, только от одной переменной зависит режим работы – тестовый или торговый. Приведу пример реализации стратегии в рамках системы.
Оговорюсь, что это не hft – здесь не будет специальной оптимизации, работы с драйверами, памятью и т.д. В разработке использовал SmartCom и открытые библиотеки на C#. Чтобы не получилось слишком объёмно – буду сокращать, и опишу только часть моментов, опустив остальное (многопоточнось, проверки, защиту от сбоев и т.д.) Знаю, что есть StockSharp и пр. но… но… у меня с этим не пошло… мне проще оказалось сделать самому, чем от кого-то зависеть.
Оговорюсь так же, что всё нижесказанное – это моё личное мнение, сформированное в рамках моего понимания, не претендующее ни на что. Я всё буду объяснять своими словами, и лишь хочу осветить тот материал, который здесь не обсуждался, либо обсуждался мало. В своё время, смарт-лаб очень много дал мне, что бы не говорили, это очень хороший ресурс, где много интересных людей! Я хочу внести и свою лепту в копилку, может кому-то, когда-нибудь пригодиться. Буду публиковать по одной – две статьи в неделю, всего будет 11 статей.
Для спецов в опционах сегодня я могу поделиться опытом использования обратного пропорционального спреда. За последнее время это наиболее популярная конструкция в моей торговле (направленная торговля опционами), и расскажу почему.
Обратный пропорциональный спред активно применяется мной по следующим причинам: по наблюдениям даёт хороший прирост цены (обычно выше, чем участвующие в нём опционы на его страйки по отдельности), а также хорош для управления позицией.
Не буду рассказывать теорию, а поделюсь практикой.
Вообще я читал в западных источниках, что такая конструкция подходит, когда вы ожидаете например резкий рост на БА, но его вероятность — невысокая. Тогда типа покупай такую конструкцию за практически 0 – в случае роста цены на БА вы получаете хорошую прибыль, а если его не происходит – то цена опциона не меняется да и вы ничем не рисковали, в смысле не было ваших расходов. Например, это подходит для биотеха, если ждете прорыва у какой-то компании, выдачи разрешения FDA и т.п.
require"QL" log = "sbrf.log" seccode = "SRM6" lots_in_trade = 80 accnt = "" better = -5 chart = "sberbankxxx" is_run = true prev_datetime = {} len = 100 basis = 9 k_bal = {0,1,2,3} sell = false buy = false id = 0 first = true function trade_signal(shift) number_of_candles = getNumCandles(chart) bars_temp,res,legend = getCandlesByIndex(chart,0,number_of_candles-2*len-shift,2*len) bars={} i=len j=2*len while i>=1 do if bars_temp[j-1].datetime.hour>=10 then sk=true if bars_temp[j-1].datetime.hour==18 and bars_temp[j-1].datetime.min==45 then sk=false end if sk then bars[i]=bars_temp[j-1] i=i-1 end end j=j-1 end t = len+1 do_sell = false do_buy = true value = 0 if do_sell then value = 1 end if do_buy then value = -1 end toLog(log,"value="..value.." on candle: "..bars[len].datetime.year.."-"..bars[len].datetime.month.."-"..bars[len].datetime.day.." "..bars[len].datetime.hour..":"..bars[len].datetime.min.." O="..bars[len].open.." H="..bars[len].high.." L="..bars[len].low.." C="..bars[len].close.." V="..bars[len].volume) return value end function mysplit(inputstr, sep) if sep == nil then sep = "%s" end local t={} ; i=1 for str in string.gmatch(inputstr, "([^"..sep.."]+)") do t[i] = str i = i + 1 end return t end function OnInit(path) log=getScriptPath()..'\\'..log toLog(log,"==========OnInit: START") toLog(log,"==========OnInit: FINISH") end function OnStop() is_run = false toLog(log,"==========OnStop: script finished manually") end 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; function killorders(ccode,scode) for i=0,getNumberOf("orders")-1,1 do local t=getItem("orders", i) if t ~= nil and type(t) == "table" then if( t.seccode == scode and CheckBit(t.flags, 0) == 1) then local transaction={ ["TRANS_ID"]=tostring(math.random(2000000000)), ["ACTION"]="KILL_ORDER", ["CLASSCODE"]=ccode, ["SECCODE"]=scode, ["ACCOUNT"] = accnt, ["ORDER_KEY"]=tostring(t.ordernum), } res=sendTransaction(transaction) end end end end function killstoporders(ccode,scode) for i=0,getNumberOf("stop_orders")-1,1 do local t=getItem("stop_orders", i) if t ~= nil and type(t) == "table" then if( t.seccode == scode and CheckBit(t.flags, 0) == 1) then local transaction={ ["TRANS_ID"]=tostring(math.random(2000000000)), ["ACTION"]="KILL_STOP_ORDER", ["CLASSCODE"]=ccode, ["SECCODE"]=scode, ["ACCOUNT"] = accnt, ["STOP_ORDER_KEY"]=tostring(t.ordernum), } res=sendTransaction(transaction) end end end end function main() toLog(log,"==========main: START") while is_run do if isConnected() == 1 then ss = getInfoParam("SERVERTIME") if string.len(ss) >= 5 then hh = mysplit(ss,":") str=hh[1]..hh[2] h = tonumber(str) if (h>=1000 and h<1400) or (h>=1405 and h<1845) or (h>=1905 and h<2350) then if first then for ti = 50,2,-1 do trade_signal(ti) end if buy and not sell then message(seccode.." Current state: green and buy",1) end if sell and not buy then message(seccode.." Current state: red and sell",1) end if buy and sell then message(seccode.." ERROR: green and red",1) end if not buy and not sell then message(seccode.." WARNING: nothing",1) end first = false end prev_candle = getPrevCandle(chart,0) if not isEqual(prev_candle.datetime,prev_datetime) then current_value = trade_signal(1) if current_value ~= 0 then optn = "B" if current_value==1 then optn = "S" end curvol=0 no=getNumberOf("FUTURES_CLIENT_HOLDING") if no>0 then for i=0,no-1,1 do im=getItem("FUTURES_CLIENT_HOLDING", i) if im.sec_code==seccode then curvol=im.totalnet end end end trvol = -current_value*lots_in_trade-curvol if trvol ~= 0 then killorders("SPBFUT",seccode) killstoporders("SPBFUT",seccode) f = io.open(getScriptPath().."\\sbrf2_pos.txt","r") sbrf2_pos=f:read("*n") f:close() f = io.open(getScriptPath().."\\sbrf3_pos.txt","r") sbrf3_pos=f:read("*n") f:close() pr,n,l = getCandlesByIndex ("futsber", 0, getNumCandles("futsber")-1, 1) local trans = { ["ACTION"] = "NEW_ORDER", ["CLASSCODE"] = "SPBFUT", ["SECCODE"] = seccode, ["ACCOUNT"] = accnt, ["OPERATION"] = optn, ["PRICE"] = toPrice(seccode,pr[0].close+current_value*better), ["QUANTITY"] = tostring(math.abs(curvol-sbrf2_pos-sbrf3_pos)), ["TRANS_ID"] = tostring(getTradeDate().month*100+getTradeDate().day+id) } id = id+1 --res = sendTransaction(trans) message(seccode.." Send : " .. res, 2) toLog(log,"Send: ".. res) for btr=0,200,5 do local trans = { ["ACTION"] = "NEW_STOP_ORDER", ["CLASSCODE"] = "SPBFUT", ["SECCODE"] = seccode, ["ACCOUNT"] = accnt, ["OPERATION"] = optn, ["PRICE"] = toPrice(seccode,pr[0].close-current_value*btr), ["STOPPRICE"] = toPrice(seccode,pr[0].close-current_value*(btr+better)), ["QUANTITY"] = tostring(6), ["TRANS_ID"] = tostring(getTradeDate().month*100+getTradeDate().day+id), ["EXPIRY_DATE"] = "GTC" } id = id+1 --res = sendTransaction(trans) message(seccode.." Send : " .. res, 2) toLog(log,"Send: ".. res) end if current_value == 1 then message(seccode..' RED: buy->sell',1) toLog(log,"RED signal") else message(seccode..' GREEN: sell->buy',1) toLog(log,"GREEN signal") end else if current_value == 1 then message(seccode..' RED: buy->sell',1) toLog(log,"RED signal, but nothing to do") else message(seccode..' GREEN: sell->buy',1) toLog(log,"GREEN signal, but nothing to do") end end else if buy and not sell then toLog(log,"Nothing to do. Current state: green and buy",1) end if sell and not buy then toLog(log,"Nothing to do. Current state: red and sell",1) end if buy and sell then toLog(log,"Nothing to do. ERROR: green and red",1) end if not buy and not sell then toLog(log,"Nothing to do. WARNING: nothing",1) end end prev_datetime = prev_candle.datetime end end end end sleep(5*1000) end toLog(log,"==========main: FINISH") end
Введение
Сегодня я расскажу, что необходимо для создания и применения высокочастотных стратегий на российском рынке. Постараюсь этот рассказ проиллюстрировать примерами из нашей практики.