RUMUS-2, язык RuLang
и автоматическое тестирование торговых стратегий
Кияница А.С.,
Академия «Форекс Клуб», 03.07.2006
Периодически наши клиенты спрашивают нас, какими программами технического анализа мы пользуемся сами. И, когда вместо того, чтобы назвать MetaStock или TradeStation, мы говорим: «RUMUS».
Безусловно, всегда есть что совершенствовать, но RUMUS-2 уже в его текущем состоянии является очень интересным продуктом. Он абсолютно бесплатный. Его интерфейс действительно удобен – все видно и все понятно. Его язык программирования RuLang таков, что для его освоения не нужно быть профессионалом в программировании – достаточно оказаться просто думающим человеком. RUMUS-2 интересен удобством работы с графиками разных масштабов, системой работы с окнами и главное – теперь уже яркими перспективами. Разработка платформы слегка затянулась, однако сейчас RUMUS-2 наконец-то находится в начале реального периода своего расцвета. Сотрудники «Форекс Клуба» в его будущей популярности уверены !
К сожалению, пока что не хватает системы автоматического тестирования. Но довольно простой и легко осваиваемый язык программирования индикаторов RuLang может вам эффективно помочь и решить задачу быстрого тестирования строго формализованных торговых систем. Рассказываю, как строится кривая дохода в пунктах на языке RuLang (RUMUS Language) программы RUMUS-2.

Допустим, стоит задача открываться по пересечению короткой и длинной средней. Закрываться будем либо по достижении ордера TP, либо по срабатыванию ордера SL. Пока какой-то из ордеров не сработает, позицию держим. Сразу комментарий: такая совсем простая система не есть самая эффективная. Рынок давно научился сдерживать захватнические порывы «механических трейдеров»… J Но все равно попробуем написать тестер.
Итак, покупка совершается, когда короткая средняя пересекает длинную среднюю снизу вверх, продажа – когда пересечение сверху вниз. Сделки для простоты совершаем по цене close сигнальной свечи.
Что требуется: найти момент открытия позиции, затем вплоть до момента закрытия отслеживать положение цены относительно ордеров TP и SL. Если какой-то ордер достигнут – закрыть позицию. Если средние дали сигнал на разворот – также закрыть позицию и открыть противоположную.
Начнем.
1.
Пишем запрос на ввод параметров средних с клавиатуры. Допустим, мы хотим работать как с обычными средними, так и со сдвинутыми. Значит, потребуется всего четыре параметра: p1 и p2 – периоды короткой и длинной средней, s1 и s2 – периоды сдвига средней. Вот как выглядит запрос на языке RuLang:
(1) – так я буду указывать части кода, чтобы на них ссылаться.
p1=inparam("Period 1",0,10000,5);
p2=inparam("Period 2",0,10000,10);
s1=inparam("Sdvig 1",0,10000,0);
s2=inparam("Sdvig 2",0,10000,0);
В данном случае сдвиги я оставил нулевые. Захотим – сдвинем. Рассчитаем наши средние с учетом сдвига, чтобы потом было короче их записывать. Их текущие значения:
(2)
m1=ref(mov(c,p1,s),-s1);
m2=ref(mov(c,p2,s),-s2);
Их значения 1 шаг назад (будут нужны для отслеживания факта пересечения средних):
ms1=ref(m1,-1);
ms2=ref(m2,-1);
Теперь пишем блоки открытия позиций. Интересующее событие – пересечение средних. Действие при возникновении события, которое мы должны совершить, – определение уровня открытия позиции и уровней ордеров на закрытие позиции. Запрос на размеры Take Profit-а и Stop Loss-а, а также спрэда (спрэд же разный может быть, и нам надо заранее его указать) записывается так:
(3)
tp=inparam("Take Profit",0,1,0.10);
st=inparam("Stop Loss",0,1,0.01);
sp=inparam("Spread",0,1,0.0005);
Если пересечение вверх, то производим покупку BUY и фиксируем цену открытия (popb = price open buy), уровень тэйк-профита (ptpb) и стоп-лосса (pstb). Спрэд учтем в окончательном расчете результата – будем уменьшать на его размер прибыль и увеличивать убыток. А при определении цен и фактов открытий и закрытий просто будем ориентироваться на график цен Bid, поступающих в RUMUS. Итак, вот как определяются интересующие нас цены:
(4) – пункт позже будет еще чуть-чуть откорректирован
If m1>m2 and ms1<ms2 then
begin
popb=c; ptpb=c+tp; pstb=c-st;
end;
Операторы begin и end нужны для того, чтобы при выполнении (или при невыполнении) условия, следующего после if, одновременно были выполнены (! – или не выполнены) несколько команд, заключенных внутрь «begin – end». То же самое – и для продажи, только ищем пересечение средних вниз, а ордера отсчитываются в другую сторону:
If m1<m2 and ms1>ms2 then
begin
pops=c; ptps=c-tp; psts=c+st;
end;
Следующие шаги до банальности простые. Нужно делать шаг за шагом вправо по графику и проверять, сработали ли какие-то ордера, а также нет ли сигнала на разворот. Но чтобы это делать, нужно будет в самом начале текста нашего индикатора, который по сути будет кривой капитала, «объявить переменные». Такими переменными будут цены открытия и уровни ордеров, а также два новых параметра posb (position buy) и poss (position sell), которые будут равны 1 и -1 при открытых позициях, а нулю – при закрытых. Я их называю показателями открытых позиций, т.е. если posb=1, значит у меня открыта длинная позиция. Объявлять переменные нужно для того, чтобы потом можно было даже через десять-двадцать-сто шагов узнать, какие цена открытия, какие цены ордеров, какая позиция открыта – на покупку или на продажу.
(5) – встанет перед 1!
variable: popb($data);
variable: ptpb($data);
variable: pstb($data);
variable: pops($data);
variable: ptps($data);
variable: psts($data);
variable: posb($data);
variable: poss($data);
В принципе, можно было бы обойтись и без разделения цен на цены buy и цены sell, но так проще понять, поэтому оставлю. Итак, вышеприведенную строку запишем в конце концов в самую шапку текста индикатора. Объявление этих переменных должно производиться то того, как они первый раз встречаются в тексте. Еще одно замечание. Зафиксировав, что есть сигнал на открытие позиции вверх или вниз, мы в строчках (4) должны будем сразу указать, открыта ли нами позиция вверх или вниз. Поэтому формулы (4) станут такими:
(4) – откорректированы
posb=0; poss=0;
If m1>m2 and ms1<ms2 then
begin
popb=c; ptpb=c+tp; pstb=c-st;
posb=1;
end;
If m1<m2 and ms1>ms2 then
begin
pops=c; ptps=c-tp; psts=c+st;
poss=-1;
end;
Наконец-то теперь, делая шаг за шагом вправо, мы: - «вспоминаем», какие были цены открытия и ордеров и каковы были показатели открытой позиции «вчера»; - проверяем положение цены закрытия относительно ордеров; - проверяем, нет ли сигнала на разворот (или на открытие, если позиций не было); - открываем или закрываем позицию; - рассчитываем доход. Чтобы вспомнить цены прошлого шага, мы должны проверить, была ли «вчера» открыта позиция, а затем текущим ценам присвоить вчерашние значения. Само собой, если «вчера» показатель позиции posb или poss был равен нулю, то соответствующей позиции не было. Попутно замечу, что в момент закрытия позиции мы в будущем обязательно должны будем показателю соответствующей позиции присваивать значение 0. Итак, для покупки и для продажи процесс «вспоминания» и переприсваивания значений цен и показателя позиции будут выглядеть так:
(6)
if ref(posb,-1)=1 then begin
popb=ref(popb,-1);
ptpb=ref(ptpb,-1);
pstb=ref(pstb,-1);
posb=ref(posb,-1);
end;
if ref(poss,-1)=-1 then begin
pops=ref(pops,-1);
ptps=ref(ptps,-1);
psts=ref(psts,-1);
poss=ref(poss,-1);
end;
Приступаем к проверке ордеров Take Profit для длинной и короткой позиции. Суть проверки вот в чем: если открыта длинная позиция и есть пересечение цены High с уровнем ордера Take Profit, фиксируем результат сделки resb (result buy). Для короткой позиции поступаем аналогично. При расчете результата от прибыли отнимаем спрэд.
(7)
if posb=1 and ref(posb,-1)=1 and h>ptpb then
begin
resb=ptpb-popb-sp;
posb=0;
end;
if poss=-1 and ref(poss,-1)=-1 and l<ptps then
begin
ress=pops-ptps-sp;
poss=0;
end;
Обратите внимание, что для правильного расчета дохода прибыльные сделки должны быть плюсовыми по результату, поэтому для короткой позиции я не от ptps отнял pops, а наоборот – от pops отнял ptps. Цена открытия короткой позиции выше цены закрытия. По факту закрытия позиции показателю позиции posb или poss присвоил нулевое значение (позиция закрыта, позиции нет). Теперь пишем закрытие по ордеру Stop Loss. Результаты будут отрицательными числами. Отнятый также как в случае с прибылью спрэд будет фактически прибавлен к убытку, увеличив его абсолютное значение.
(8)
if posb=1 and ref(posb,-1)=1 and l<pstb then
begin
resb=pstb-popb-sp;
posb=0;
end;
if poss=-1 and ref(poss,-1)=-1 and h>psts then
begin
ress=pops-psts-sp;
poss=0;
end;
Теперь следует рассчитать результаты в том случае, когда ордера достигнуты не были, но подан сигнал на переворот. Условие, которое нужно искать для закрытия длинной позиции, такое: показатель длинной позиции был «вчера» и есть «сегодня» +1, а показатель отрицательной изменился с 0 на -1, то есть позиция вниз только-только открывается. Соответственно, при открытии позиции вниз мы фиксируем результаты длинной позиции и показатель длинной позиции в итоге приравниваем к нулю. Для смены направления с нисходящего на восходящее – зеркальная ситуация.
(9)
if posb=1 and ref(posb,-1)=1 and ref(poss,-1)=0 and poss=-1 then
begin
resb=c-popb-sp;
posb=0;
end;
if poss=-1 and ref(poss,-1)=-1 and ref(posb,-1)=0 and posb=1 then
begin
ress=pops-c-sp;
poss=0;
end;
Теперь у нас на руках все результаты по всем сделкам – как прибыльные, так и убыточные. Какова же общая прибыль отдельно по длинным, а отдельно – по коротким позициям? И какая – совокупная? Ниже в пункте (10) – расчеты этих параметров. Есть такое понятие – «кумулятивная сумма». Кумулятивная сумма всех длинных позиций покажет общую прибыль resultBuy, а кумулятивная сумма всех коротких позиций – прибыль resultSell. Сложив эти две величины, мы на данный конкретный момент получим итоговую зафиксированную прибыль result, которую мы заработали, совершая сделки с применением средних как в длинную, так и в короткую сторону и закрываясь по ордерам и путем переворота. Две последние строчки пункта (10) позволяют нам рассчитывать «текущую незафиксированную прибыль или убыток» - «curve» (кривая капитала в пунктах).
(10)
resultBuy=cum(resb);
resultSell=cum(ress);
result=resultBuy+resultSell;
if posb=1 then begin buy=result+c-popb; curve=buy; end; else curve=result;
if poss=-1 then begin sell=result+pops-c; curve=sell; end;
(11)
Теперь выведем результаты на экран, а также укажем линию нулевой прибыли. В параметрах индикатора линию curve надо сделать синей (вид обычный – линия), а линии buy и sell сделать зеленой и красной, отразив в виде гистограммы. В результате будет видно, где позиций не было открыто вообще, где работала длинная, а где – короткая. На том баре, где длинная позиция была открыта, впервые появится зеленая гистограмма, где длинная позиция закрыта, зеленая гистограмма buy будет равна нулю. На баре, где короткая открыта, появится красная гистограмма, а там, где короткая закрыта, красная станет равна нулю.
curve;
buy; sell;
0;
Вот и родилась наша кривая дохода в пунктах. Как видите, мы можем все – и даже нарисовать кривую капитала с использованием языка RuLang. Если есть четкое понимание, как бы вы хотели управлять объемами контрактов, то и это вполне можно было бы запрограммировать. Надеюсь, разберетесь уже сами. Итоговый текст индикатора, строящего кривую капитала (просто скопируйте его):
// !_AK_Cap_2Mov, 30.06.2006, Alexey Kiyanitsa, Forex Club
// Capital curve for 2 Moving Average Trading System
// Open when cross and close when order made or when cross
//
// (5)
variable: popb($data);
variable: ptpb($data);
variable: pstb($data);
variable: pops($data);
variable: ptps($data);
variable: psts($data);
variable: posb($data);
variable: poss($data);
// (1)
p1=inparam("Period 1",0,10000,5);
p2=inparam("Period 2",0,10000,10);
s1=inparam("Sdvig 1",0,10000,0);
s2=inparam("Sdvig 2",0,10000,0);
// (2)
m1=ref(mov(c,p1,s),-s1);
m2=ref(mov(c,p2,s),-s2);
ms1=ref(m1,-1);
ms2=ref(m2,-1);
// (3)
tp=inparam("Take Profit",0,1,0.10);
st=inparam("Stop Loss",0,1,0.01);
sp=inparam("Spread",0,1,0.0005);
// (4)
posb=0; poss=0;
If m1>m2 and ms1<ms2 then
begin
popb=c; ptpb=c+tp; pstb=c-st;
//if ref(poss,-1)=0 then posb=1; else posb=0;
posb=1;
end;
If m1<m2 and ms1>ms2 then
begin
pops=c; ptps=c-tp; psts=c+st;
//if ref(posb,-1)=0 then poss=-1; else poss=0;
poss=-1;
end;
// (6)
if ref(posb,-1)=1 then begin
popb=ref(popb,-1);
ptpb=ref(ptpb,-1);
pstb=ref(pstb,-1);
posb=ref(posb,-1);
end;
if ref(poss,-1)=-1 then begin
pops=ref(pops,-1);
ptps=ref(ptps,-1);
psts=ref(psts,-1);
poss=ref(poss,-1);
end;
// (7)
if posb=1 and ref(posb,-1)=1 and h>ptpb then
begin
resb=ptpb-popb-sp;
posb=0;
end;
if poss=-1 and ref(poss,-1)=-1 and l<ptps then
begin
ress=pops-ptps-sp;
poss=0;
end;
//(8)
if posb=1 and ref(posb,-1)=1 and l<pstb then
begin
resb=pstb-popb-sp;
posb=0;
end;
if poss=-1 and ref(poss,-1)=-1 and h>psts then
begin
ress=pops-psts-sp;
poss=0;
end;
//(9)
if posb=1 and ref(posb,-1)=1 and ref(poss,-1)=0 and poss=-1 then
begin
resb=c-popb-sp;
posb=0;
end;
if poss=-1 and ref(poss,-1)=-1 and ref(posb,-1)=0 and posb=1 then
begin
ress=pops-c-sp;
poss=0;
end;
//(10)
resultBuy=cum(resb);
resultSell=cum(ress);
result=resultBuy+resultSell;
if posb=1 then begin buy=result+c-popb; curve=buy; end; else curve=result;
if poss=-1 then begin sell=result+pops-c; curve=sell; end;
//(11)
curve;
buy; sell;
0;
О графике, изображенном на рисунке выше. Этот график дневных свечей отражает доход в пунктах для британского фунта GBP за период с 01.01.2005 по 30.06.2006 при работе со средними, имеющими периоды 5 и 10. Тэйк-профит – 0.1000, стоп-лосс – 0.0100. Закрытие по ордерам или по цене закрытия сигнальной свечи при подаче средними приказа о совершении сделки в обратном направлении. Явный минус этой простейшей системы – ее чувствительность к параметрам. Чуть изменишь – дохода нет. На других валютах тоже «не уха». Таким образом, систему мы не рекомендуем. Однако делаем полезный вывод: чтобы стать успешным трейдером, имеет смысл учиться, потому что все гениальное просто, но, как правило, оно не совсем уж примитивно. J
Успеха и удачи!
|