Расчёты по инструментам рынка Forex в EasyLanguage.

  1. Постановка вопроса.
  2. Расчёты по валютам, котируемым к доллару США
  3. Расчеты по кросс-курсам
  4. Расчёты уровней Take Profit & Stop Loss и сложные случаи.

 

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):

  • Fixed Unit - фиксированное количество
  • Dollars per Transaction - в долларах на сделку(см. рисунок).

Казалось бы, что нам более подходит второй вариант - 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. Для кросс-курсов нужно будет создать отдельный сигнал, потому что нам потребуется для расчётов дополнительные потоки данных (котировки валют из числителя и знаменателя кросс курса к доллару США).

Для начала создадим сигнал для валют, котируемых к доллару США. Следует отметить, что они в свою очередь подразделяется на две подгруппы

  1. Инструменты с обратной котировкой - AUD/USD, EUR/USD, GBP/USD, NZD/USD . Размер лота рассчитывается делением суммы капитала на курс валюты; прибыль выражена в долларах США.
  2. Инструменты с прямой котировкой - USD/CAD, USD/CHF, USD/JPY. Размер лота равен сумме капитала, прибыль выражена в валюте знаменателя, для пересчёта в доллары США нужно разделить прибыль на курс валюты.

Данные в 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 begin

temp=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; {запомним количество завершенных сделок}

{-------------------------------------------------------------------------------------------------------------------------------}

 

3. Расчёты по кросс-курсам.

По кросс-курсам дела обстоят еще запутаннее - а именно, сигналы торговой стратегии исполняются по котировкам кросс-курса, лоты рассчитываются в валюте числителя кросс-курса к доллару США, а результаты торгов в 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; {запомним количество завершенных сделок}

{-------------------------------------------------------------------------------------------------------------------------------}

 

4.Расчёты уровней Take Profit & Stop Loss и сложные случаи.

В указанных выше сигналах торговой стратегии вход в рынок и переворот позиции происходит по цене закрытия текущего бара. В данном случае цена для всех валют, входящих в состав кросс-курса привязана к одному моменту времени и всё просто. А что будет, если мы будем производить входы/выходы не по цене в конкретный момент времени (т.е. открытие или закрытие бара), а по цене, которая возникает по времени внутри бара (типичный пример - максимальная или минимальная цена)?

Размер лота в указанном выше примере рассчитывается от среднего курса (медианы цены) и с ним проблем не возникает. Зато при выходе из позиции посередине бара кросс-курса мы не можем быть уверены в том, какая цена в данный момент была у валюты, находящейся в знаменателе кросс-курса (ведь прибыль будет выражена в ней). Мы ведь не знаем, за счет какой из составляющих кросс-курса произошло движение самого кросс курса, не так ли?

В данной ситуации наиболее простым вариантом является деление прибыли от сделки на среднюю (медиану) цену валюты из знаменателя кросс-курса, т.е. часть кода на 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);