Расчёты по инструментам рынка Forex в EasyLanguage.
1.Постановка вопроса.
Пакет программ Omega Research ProSuite (Omega TradeStation) используется многими и по праву считается одним из лучших среди программ технического анализа. Возможности программы по тестированию торговых стратегий на исторических данных впечатляют, но не лишены некоторых недостатков (или особенностей - кому как больше нравится). Об этих особенностях для инструментов рынка Forex и пойдёт речь ниже.
Представим следующую ситуацию - имеется некая механическая торговая система, которую мы хотели бы протестировать на портфеле из 10 инструментов
рынка Forex и подобрать оптимальные параметры её работы. При этом мы делим начальный капитал в 10000 USD на 10 равных частей - лотов, и торгуем по каждому инструменту только одним лотом. Мы тестируем следующие инструменты: AUD/USD, USD/CAD, USD/CHF, EUR/USD, EUR/JPY, EUR/GBP, GBP/USD, CHF/JPY, USD/JPY, NZD/USD - на дневных данных за период с 01/01/2000 по 01/01/2003 года. В расчётах примем маржинальное кредитование в размере 1:50.Торговая система основана на Стохастике (сигналы Stochastic Bull Crss и Stochastic Bear Crss объединены в один сигнал):
Inputs
: Length(14),OverBought(70),OverSold(30);Vars
: Kline(0),Dline(0);KLine = SlowKCustom(High, Low, Close, Length);
DLine = SlowDCustom(High, Low, Close, Length);
If
KLine Crosses Below DLine AND KLine > OverBought AND DLine > OverBought Then Sell ("StchSell") This Bar on Close;If
KLine Crosses Above DLine AND KLine < OverSold AND DLine < OverSold Then Buy ("StchBuy") This Bar on Close;Открытие позиции и переворот происходит по цене закрытия дня.
|
Создадим на основе нашего сигнала торговую стратегию StochBas с указанными выше параметрами по умолчанию. Теперь нужно приложить данную стратегию к графикам указанных выше инструментов. Так как мы торгуем лотом в 10% от капитала на каждый инструмент, то нам где-то нужно указать это условие. Откроем окно Format Strategy на закладке Costs. Omega TS предлагает два варианта размера лота (Default Trade Amount): Казалось бы, что нам более подходит второй вариант - Dollars per Transaction, при этом Omega TS автоматически рассчитает размер лота делением суммы в USD на курс покупки/продажи |
Однако это не поможет в нашем случае, потому что размер лота всегда выражается в единицах валюты, которая находится в числителе котировки. Рассмотрим торгуемые инструмента по отдельности:
AUD/USD - мы торгуем валютой AUD, размер лота в AUD, поэтому данный вариант нам подходит;
USD/CAD - мы торгуем USD, размер лота выражен в USD, поэтому нам не нужно делить сумму капитала на текущий курс покупки/продажи - следовательно более подходит вариант Fixed Unit;
EUR/JPY - мы торгуем EUR, размер лота выражен в EUR - нам не подходит ни один из имеющихся вариантов, так как для расчёта размера лота нужно делить сумму капитала на курс EUR/USD.
Размер лота - это не единственная особенность при расчётах в Omega TS. Другая, не менее важная особенность - это способ расчёта прибыли (а также всех показателей в Strategy Perfomance report). Omega TS рассчитывает прибыль следующим образом (длинные позиции):
ПРИБЫЛЬ/УБЫТОК = (ЦЕНА_ВЫХОДА - ЦЕНА_ВХОДА) * РАЗМЕР_ЛОТА * ЦЕНА_ПУНКТА.
Для коротких позиций нужно поменять местами в формуле параметры ЦЕНА_ВХОДА и ЦЕНА_ВЫХОДА. Данный метод расчёта применим к акциям и индексам, но не всегда применим к инструментам рынка Forex - по ним Omega TS рассчитывает прибыль в валюте, находящейся в знаменателе котировки. То есть, по EUR/USD прибыль будет рассчитываться в USD, по USD/JPY - в JPY, а по EUR/GBP - в GBP.
Получается, что стандартными средствами OmegaTS правильно рассчитать результаты работы по инструментам рынка FOREX невозможно, для этого необходимо использовать элементы управления капиталом и расчета прибыли в сигнале торговой стратегии на EasyLanguage.
На форумах нам указывали на способ расчёта прибыли/убытка в пунктах, при этом нужно в параметре Value указать цену пункта в долларах - в простых случаях это может быть очень удобно для оценки работоспособности системы. Однако в рамках поставленной задачи о тестировании портфеля, состоящего из нескольких инструментов, способ расчёта в пунктах даст нам искажённую информацию. Цена актива (и как следствие - стоимость 1 пункта) может изменяться в значительных пределах за период тестирования (иногда в 1.5-2 раза) и результаты тестирования будут недостоверными.
2.Расчёты по валютам, котируемым к доллару США.
Для того, чтобы правильно рассчитать размеры лотов и прибыль/убыток, целесообразно разделить инструменты на группы - кросс-курсы и котировки к USD. Для кросс-курсов нужно будет создать отдельный сигнал, потому что нам потребуется для расчётов дополнительные потоки данных (котировки валют из числителя и знаменателя кросс курса к доллару США).
Для начала создадим сигнал для валют, котируемых к доллару США. Следует отметить, что они в свою очередь подразделяется на две подгруппы
Данные в Strategy Perfomance Report для первой подгруппы будут правильными, а для второй подгруппы - нет, поэтому имеет смысл рассчитывать их в сигнале торговой стратегии, и потом выводить в файл для обработки в электронной таблице (например - Excel).
Указанный ниже сигнал работает следующим образом:
Пример сигнала торговой стратегии StochBas для инструментов, котируемых к доллару США:
{-------------------------------------------------------------------------------------------------------------------------------} {Сигнал работает со следующими валютами: AUD,CAD,CHF,EUR ,GBP,JPY,NZD } |
|||
Inputs : |
Equity(10),Lots(10), {параметры управления капиталом - сумма капитала в тысячах долларов, количество лотов} |
||
|
LogIt(1); {запись результатов в файл: 1 - записываем, другое значение - не записываем}Length(14),OverBought(70),OverSold(30); {параметры стохастика } |
||
Vars : |
Lot_Num(1000), {размер лота} Symbol(""), {наименование валюты} divmul(False), {тип котировки False - прямая, True - обратная}pmul(0), {курс валюты для делителя}Trades(0), {количество закрытых позиций} PLperiod(0), {прибыль за период} lastM(0), {номер месяца}filename(""), {имя файла для вывода результатов}, temp(0) {переменная с результатом в долларах США},Kline(0),Dline(0); {значения стохастика} |
||
{-------------------------------------------------------------------------------------------------------------------------------} {установка начальных значений : выбор типа валюты, назначение имени файла для вывода результатов} |
|||
if barnumber=1 then BEGIN |
|
||
|
if Symbol="" then Symbol=GetSymbolName;if filename="" then filename="c:\temp\"+Symbol+".csv";FileDelete (filename);If Symbol="AUD A0-FX" or Symbol="EUR A0-FX" or Symbol="GBP A0-FX" or Symbol="NZD A0-FX" then divmul=true;if Symbol="CAD A0-FX" or Symbol="CHF A0-FX" or Symbol="JPY A0-FX" then begin |
||
|
|
divmul= false;Lot_Num= IntPortion(Equity*1000/lots); {размер лота рассчитываем один раз, т.к. он в USD и не зависит от курса} |
|
|
end ; |
|
|
END ;{-------------------------------------------------------------------------------------------------------------------------------} {расчёт размера лота в случае, когда он зависит от курса валюты} pmul= MedianPrice;if divmul=true then Lot_Num=IntPortion(Equity*1000/(lots*pmul));{-------------------------------------------------------------------------------------------------------------------------------} {непосредственно сам торговый сигнал} KLine = SlowKCustom(High, Low, Close, Length);DLine = SlowDCustom(High, Low, Close, Length);If KLine Crosses Below DLine AND KLine > OverBought AND DLine > OverBought Then Sell ("StchSell") This Bar on Close;If KLine Crosses Above DLine AND KLine < OverSold AND DLine < OverSold Then Buy ("StchBuy") This Bar on Close;{-------------------------------------------------------------------------------------------------------------------------------} {запишем результаты работы в текстовой файл} if LogIt=1 then begin {записываем результаты в файл ежемесячно} |
|||
|
if Trades<>TotalTrades then begintemp= iff(ExitPrice(1)<>0,ExitPrice(1),1); |
||
|
|
if divmul=false then temp=PositionProfit(1)/temp else temp=PositionProfit(1); {рассчитаем прибыль в USD}PLperiod=PLperiod+temp; {прибыль за месяц накопительным итогом} |
|
|
end ;if Month (date)<>LastM or LastBarOnChart then begin |
||
|
|
FileAppend (filename,NumToStr(Month(Date),0)+"/"+NumToStr(Year(Date)+1900,0)+","+NumToStr(PlPeriod,2)+","+NewLine);PLperiod=0; {обнуляем итог за месяц} |
|
|
end ;lastM= Month(Date); |
||
end ;{-------------------------------------------------------------------------------------------------------------------------------} Trades= TotalTrades; {запомним количество завершенных сделок}{-------------------------------------------------------------------------------------------------------------------------------} |
По кросс-курсам дела обстоят еще запутаннее - а именно, сигналы торговой стратегии
исполняются по котировкам кросс-курса, лоты рассчитываются в валюте числителя кросс-курса к доллару США, а результаты торгов в Omega TradeStation представлены в валюте знаменателя кросс-курса к доллару США.Следовательно, для ведения правильных расчётов в долларах США нам необходимо использовать одновременно три потока данных с котировками.В свою очередь кросс-курсы могут быть четырёх видов в зависимости от сочетания типов котировок в числителе и знаменателе кросс-курса. Возможные варианты приведены в таблице ниже.
№ |
Тип котировки в числителе |
Тип котировки в знаменателе |
Для расчёта размера лота мы должны |
Для расчёта прибыли в USD мы должны |
Примеры кросс-курсов |
1 |
Обратная |
Обратная |
Делить |
Умножать |
EUR/AUD, EUG/GBP |
2 |
Обратная |
Прямая |
Делить |
Делить |
AUD/JPY, EUR/JPY,GBP/JPY, EUR/CHF, GBP/CHF (распространённый вариант) |
3 |
Прямая |
Обратная |
Умножать |
Умножать |
CAD/EUR (встречается редко) |
4 |
Прямая |
Прямая |
Умножать |
Делить |
CHF/JPY |
Для каждого из четырёх вариантов у нас будет свой способ расчёта. Для того чтобы унифицировать расчёты, мы введём в наш
сигнал две логических переменных:В нашем случае не будет кросс-курсов третьего типа, поэтому логику расчётов для него мы не приводим. Пример сигнала торговой стратегии StochCross на основании стохастика для кросс-курсов приведён ниже. Алгоритм работы данного сигнала полностью аналогичен алгоритму работы сигнала StochBas для валют, котируемых к доллару США.
Для того, чтобы всё рассчитывалось правильно, нужно в рабочей области в качестве первого потока данных установить котировки кросс-курса, в качестве второго потока данных - котировки валюты из числителя кросс курса, и третьим потоком данных будут котировки из знаменателя кросс-курса.
{-------------------------------------------------------------------------------------------------------------------------------} {Сигнал работает со следующими валютами: EURJPY, EURGBP, EURCHF, AUDJPY, GBPJPY, GBPCHF, CHFJPY } |
|||
Inputs : |
Equity(10),Lots(10), {параметры управления капиталом - сумма капитала в тысячах долларов, количество лотов} |
||
|
LogIt(1); {запись результатов в файл: 1 - записываем, другое значение - не записываем}Length(14),OverBought(70),OverSold(30); {параметры стохастика } |
||
Vars : |
Lot_Num(1000), {размер лота} Symbol(""), {наименование валюты} Trades(0), {количество закрытых позиций} PLperiod(0), {прибыль за период} lastM(0), {номер месяца} temp(0) {переменная с результатом в долларах США},lotmul( False),profmul(False),LotRate(0), PLRate(0),{переменные для выбора типа кросс-курса}filename(""), {имя файла для вывода результатов},Kline(0),Dline(0); {значения стохастика} |
||
{-------------------------------------------------------------------------------------------------------------------------------} {установка начальных значений : выбор типа валюты, назначение имени файла для вывода результатов} |
|||
if barnumber=1 then BEGIN |
|
||
|
if Symbol="" then Symbol=GetSymbolName;if filename="" then filename="c:\temp\"+Symbol+".csv";FileDelete (filename);if Symbol="EURJPY A0-FX" or Symbol="GBPJPY A0-FX" or Symbol="GBPCHF A0 FX" or Symbol="EURCHF A0-FX"or Symbol="AUDJPY A0-FX"then begin lotmul=false; profmul=false; end;if Symbol="EURGBP A0-FX" or Symbol="EURAUD A0-FX"then begin lotmul=false; profmul=true; end;if Symbol="CHFJPY A0-FX"then begin lotmul=true; profmul=false; end; |
||
END ;{-------------------------------------------------------------------------------------------------------------------------------} {расчёт размера лота } LotRatel= MedianPrice;if lotmul=false then Lot_Num=IntPortion(Equity/(lots*LotRate)); {EURJPY, GBPJPY, GBPCHF, EURCHF,}if lotmul=true then Lot_Num=IntPortion(Equity*LotRate/lots); {CHFJPY}{-------------------------------------------------------------------------------------------------------------------------------} {непосредственно сам торговый сигнал} KLine = SlowKCustom(High, Low, Close, Length);DLine = SlowDCustom(High, Low, Close, Length);If KLine Crosses Below DLine AND KLine > OverBought AND DLine > OverBought Then Sell ("StchSell") This Bar on Close;If KLine Crosses Above DLine AND KLine < OverSold AND DLine < OverSold Then Buy ("StchBuy") This Bar on Close;{-------------------------------------------------------------------------------------------------------------------------------} {запишем результаты работы в текстовой файл} if LogIt=1 then begin {записываем результаты в файл ежемесячно} |
|||
|
if Trades<>TotalTrades then begin |
||
|
|
PLRate= Close[1] of data3; {выходы только на закрытии}if profmul=false then temp=PositionProfit(1)*100/(PLRate*Equity); {рассчитаем прибыль в USD}if profmul=true then temp=PositionProfit(1)*100*PLRate/Equity;PLperiod=PLperiod+temp; {прибыль за месяц накопительным итогом} |
|
|
End ;if Month (date)<>LastM or LastBarOnChart then begin |
||
|
|
FileAppend (filename,NumToStr(Month(Date),0)+"/"+NumToStr(Year(Date)+1900,0)+","+NumToStr(PlPeriod,2)+","+NewLine);PLperiod=0; {обнуляем итог за месяц} |
|
|
End ;lastM= Month(Date); |
||
end ;{-------------------------------------------------------------------------------------------------------------------------------} Trades= TotalTrades; {запомним количество завершенных сделок}{-------------------------------------------------------------------------------------------------------------------------------} |
В указанных выше сигналах торговой стратегии вход в рынок и переворот позиции происходит по цене закрытия текущего бара. В данном случае цена для всех валют, входящих в состав кросс-курса привязана к одному моменту времени и всё просто. А что будет, если мы будем производить входы/выходы не по цене в конкретный момент времени (т.е. открытие или закрытие бара), а по цене, которая возникает по времени внутри бара (типичный пример - максимальная или минимальная цена)?
Размер лота в указанном выше примере рассчитывается от среднего курса (медианы цены) и с ним проблем не возникает. Зато при выходе из позиции посередине бара кросс-курса мы не можем быть уверены в том, какая цена в данный момент была у валюты, находящейся в знаменателе кросс-курса (ведь прибыль будет выражена в ней). Мы ведь не знаем, за счет какой из составляющих кросс-курса произошло движение самого кросс курса, не так ли?
В данной ситуации наиболее простым вариантом является деление прибыли от сделки на среднюю (медиану) цену валюты из знаменателя кросс-курса, т.е. часть кода на EasyLanguage, отвечающего за расчёт прибыли в долларах может выглядеть следующим образом
:
if Trades<>TotalTrades then begin |
||
|
If ExitPrice [1]=Close[1] then PlRate=Close[1] of data3 {выход был на закрытии}Else PlRate=MedianPrice[1] of data3; {выход был внутри бара, берём среднюю цену}if profmul=false then temp=PositionProfit(1)*100/(PLRate*Equity); {рассчитаем прибыль в USD} if profmul=true then temp=PositionProfit(1)*100*PLRate/Equity;PLperiod=PLperiod+temp; {прибыль за месяц накопительным итогом} |
|
End ; |
Мы также не рассматривали варианты выходов по ограничению убытков
Stop Loss (SL) и фиксированию прибыли Take Profit (TP). Далее мы будем вести речь о SL&TP, устанавливаемых в процентах от торгового капитала.Встроенные в
EasyLanguage варианты SL&TP обычно используют сумму в долларах США как условие для срабатывания, поэтому в чистом виде они нам не подходят., так как нам нужно пересчитывать сумму прибыли/убытка в доллары при необходимости. С точки зрения различных вариантов, мы снова выделяем валюты с котировкой к доллару США и кросс-курсы.По валютам, котируемым к
USD у нас есть два варианта - прямая котировка (прибыль/убыток выражен в валюте курса и мы должны умножить сумму в SL/TP ордере на котировку) и обратная котировка (прибыль/убыток выражен в USD, умножать не нужно).Ниже приведен пример кода на
Easy Language, рассчитывающего значения SL & TP в зависимости от значения переменной divmul. SL и TP - это стоп лосс и тейк-профит в процентах от капитала - входные параметры системы; SV и TV - рассчитываемые размеры стоп-лосса и тейк-профита.
pmul= MedianPrice;if divmul=false then begin {прямая котировка} |
||
If SL<>0 then SV=Equity*SL*pmul*0.01;If TP<>0 then TV= Equity*TP*pmul*0.01; |
||
End; |
||
if divmul=true then begin {обратная котировка} |
||
If SL<>0 then SV=Equity*SL*0.01;If TP<>0 then TV= Equity*TP*0.01; |
||
End; SetStopPosition ;If SV<>0 then SetStopLoss(SV);If TV<>0 then SetProfitTarget(TV); |
С кросс-курсами ситуация сложнее, так как сумма прибыли/убытка выражена в валюте знаменателя кросс-курса, и для того, чтобы знать какой процент от капитла составляет прибыль или
убыток, нам нужно разделить на курс валюты-знаменателя в будущем, в тот момент, когда у нас появится эта самая прибыль или этот самый убыток.Так как мы не можем знать какой курс у нас будет в будущем, то в качестве оценки для установки
SL&TP можно использовать данные цены самого кросс-курса. На результаты тестирования и последующего трейдинга это никак не повлияет, если в обоих процессах (тестировании итрейдинге) придерживаться одинаковой методики (при этом нам не важен тип котировки кросс-курса и расчёты будут довольно простыми).Ниже приведен пример кода на
EasyLanguage, который рассчитывает значения SL&TP для кросс-курсов:
If SV<>0 then SV=Equity*SL*0.01*MedianPrice;If TP<>0 then TV= Equity*TP*0.01*MedianPrice;SetStopPosition ;If SV<>0 then SetStopLoss(SV);If TV<>0 then SetProfitTarget(TV); |