~Russian Bear’Z Blog~



Тестирование опционных стратегий в Omega TradeStation

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

Иной раз приходилось использовать абсурдные вещи: делать сигнал на открытие позиции с параметром k контрактов, где k принимает +1/-1, т.е. на “как и было”/ “поменять покупку на продажу и наоборот”. Оптимизация это та чёрная работа, которую компьютер может сделать лучше человека. Это к тому что и опционов много и параметров тоже.

Итак, что бы провести тестирование стратегии на опционах Omega – наилучший инструмент для этого (после Excel конечно). Для этого не обязательно иметь данные по всем страйкам опционов, со всем возможными датами погашения. А достаточно иметь дневные свечи по активу (spot) и волатильности (implied volatility). Возьмем, к примеру на Yahoo Finance, QQQQ(Trust Shares NASDAQ 100) и ^QQV(QQQ Implied Volatility Index), и загрузим их в Omega как показано на рисунке ниже:


Рис. 1. Тестирование опционной стратегии.

Для этого сначала загружается QQQQ, после клавишей F5 добавляется Data2.

Продолжение

Рассмотрим теперь стратегию, по которой проведём тестирование. Излюбленная стратегия всех опционных трейдеров на российском и американском рынке – это продажа стрэнгла (short put, short call). Для большего интереса, добавим к ней дельта-хеджирование базовым активом. Понятно что при падении рынка придётся для поддержки нейтральности продавать спот, а при росте покупать. Поэтому заодно и постараемся ответить на вопрос, выгодно ли такое дельта-хеджирование?

Итак формализуем алгоритм. Опционы на QQQQ имеют экспирацию каждый месяц, и страйки расположены через 1 пункт. За back_days дней до экспирации продаём стрэнгл со всевозможными страйками, зависящими от параметра type:

  1. 0: strike_put = floor(price)+1, strike_call = floor(price)
  2. 1: strike_put = floor(price), strike_call = floor(price)
  3. 2: strike_put = floor(price)-1, strike_call = floor(price)
  4. 3: strike_put = floor(price)+1, strike_call = floor(price)+1
  5. 4: strike_put = floor(price), strike_call = floor(price)+1
  6. 5: strike_put = floor(price)-1, strike_call = floor(price)+1
  7. 6: strike_put = floor(price)+1, strike_call = floor(price)+2
  8. 7: strike_put = floor(price), strike_call = floor(price)+2
  9. 8: strike_put = floor(price)-1, strike_call = floor(price)+2

У полученного стрэнгла оцениваем дельту (delta), в соответсвии с этим проводим хеджирование активом с коэффициентом k_delta (положительное значение - хеджируем частью базового актива, 0 – не проводим хеджирование, отрицательное значение - наращиваем позицию против рынка). Процедура хеджирования осуществляется каждый день по цене закрытия вплоть до экпирации.

Ниже приведён код стратегии (реализованной как индикатор):


{|||||||||||||||||||||||||||||||||||||||||||||||||||||
~ Option Back-testing Indicator
~ EasyLanguage code for Omega TradeStation
~ whiteline Group Copyright (c) 2004
|||||||||||||||||||||||||||||||||||||||||||||||||||||}
input:
    type(0), {0-8}
    k_delta(10), {0-100}
    days_back(25),{3-28}
    print_status(false);
variable:
    start_account(1000000),
    period(20),
    account(0),
    vola(0),
    pl(0),
    ha(0),
    dd(0),
    exp_days(0),
    base_size(1),
    oc_price(0),
    op_price(0),
    oc_strike(0),
    op_strike(0),
    spot_size(0),
    o_delta(0),
    o_date(0),
    b_open(false),
    str(”");
if
    currentbar = 1
then
begin
    account = start_account;
    pl = account;
    FileDelete(”C:\Omega Export\pl.csv”);
    str =
        NumToStr(date, 0) + “, ” +
        NumToStr(account, 2) +
        NewLine;
    FileAppend(”C:\Omega Export\pl.csv”, str);
end;
if
    currentbar > period and
    BarStatus(1) = 2
then
begin {global}
    vola = close of data2;
    if
        b_open = false and
        days_back > Next3rdFriday(1) and
        Next3rdFriday(1) > 1
    then
    begin {base conract quantity}
        base_size = ceiling(MaxList(1, start_account/(c*100/5)));
        o_date = DateToJulian(date) + Next3rdFriday(1);
        exp_days = o_date - DateToJulian(date);
        op_strike = (Floor(c[1])+1-mod(type,3));
        oc_strike = (Floor(c[1])+floor(type/3));
        op_price = floor(20*blackscholes(exp_days, op_strike, close, 3.0, vola, put))/20;
        oc_price = floor(20*blackscholes(exp_days, oc_strike, close, 3.0, vola, call))/20;
        {commission 1$ per option contract}
        account = account +100*base_size*(op_price+oc_price-0.02); b_open = true;
    end;
    exp_days = o_date - DateToJulian(date);
    if
        exp_days > 0
    then
    begin {delta-hedging}
        o_delta =
        floor(0.01*k_delta*(spot_size - base_size*
        (f_delta(exp_days, op_strike, close, 3.0, vola, put) +
        f_delta(exp_days, oc_strike, close, 3.0, vola, call))));
        spot_size = spot_size - o_delta;
        {2$ is commision and slippage per 100 shares}
        account = account + 100*o_delta*close - AbsValue(2*o_delta);
        pl = account + 100*spot_size*close - AbsValue(2*spot_size) -
            100*base_size*blackscholes(exp_days, op_strike, close, 3.0, vola, put) -
            100*base_size*blackscholes(exp_days, oc_strike, close, 3.0, vola, call);
    end
    else if b_open
    then
    begin
        b_open = false;
        if spot_size <> 0 then
            account = account + 100*spot_size*close - AbsValue(2*spot_size);
        if close < op_strike then account = account - 100*base_size*(op_strike - close);
        if close > oc_strike then account = account - 100*base_size*(close - oc_strike);
        pl = account;
        op_strike = 0;
        oc_strike = 0;
        spot_size = 0;
    end; {Statistic code}
    if pl > ha then ha = pl;
    if pl < ha[1] then dd = MinList(dd, pl-ha);
    if
        print_status
    then
    begin
        str =
            NumToStr(Date, 0) + “,” +
            NumToStr(pl, 2) +
            NewLine;
        FileAppend(”C:\Omega Export\pl.csv”, str);
    end;
    if
        LastBarOnChart and
        print_status
    then
    begin
        str =
            ”LAST DAY REPORT” + newline +
            NumToStr(date, 0) + “, ” +
            NumToStr(pl/start_account, 2) + “,” +
            NumToStr(dd/start_account, 2);
        if print_status then print(str);
    end;
end; {global}
{ Indicator Code }
plot1(1);

Индикатор сохраняет в файл динамику P/L, и печатает отчёт о результатах прибыли и максимальной просадки в окне EasyLanguage.

Таким образом получили результаты по любому из наборов параметров, но что б ответить на вопрос о дельте хеджировании нужно перебрать все варианты решений. Это может занять, в некоторых случаях, очень много времени, но можно воспользоваться библиотекой Генетического Алгоритма для Omega TradeStation GA4TS.

Для этого в первую очередь, для работы с библиотекой, необходимо модифицировать код, предварительно сменив тип индикатора на сигнал, как показано ниже:


{|||||||||||||||||||||||||||||||||||||||||||||||||||||
~ Option Back-testing GA-strategy
~ EasyLanguage code for Omega TradeStation
~ whiteline Group Copyright (c) 2004
|||||||||||||||||||||||||||||||||||||||||||||||||||||}
input:
    count(0), {optimize 0-2000}
variable:
{Optimization}
    type(0), {0-8}
    k_delta(10), {0-100}
    days_back(25),{3-28} start_account(1000000),
    period(20),
    account(0),
    vola(0),
    pl(0),
    ha(0),
    dd(0),
    exp_days(0),
    base_size(1),
    oc_price(0),
    op_price(0),
    oc_strike(0),
    op_strike(0),
    spot_size(0),
    o_delta(0),
    o_date(0),
    b_open(false),
    str(”");
DefineDLLFunc: “C:\GA\GA4TS.dll”, void, “ga_create”, int, int, int, LPSTR;
DefineDLLFunc: “C:\GA\GA4TS.dll”, void, “ga_setadvancedoptions”, int, int, int, int, double;
DefineDLLFunc: “C:\GA\GA4TS.dll”, void, “ga_setcodelength”, int, int;
DefineDLLFunc: “C:\GA\GA4TS.dll”, int, “ga_generate”,int;
DefineDLLFunc: “C:\GA\GA4TS.dll”, int, “ga_loadbasepopulation”,LPSTR, int;
DefineDLLFunc: “C:\GA\GA4TS.dll”, int, “ga_getinitialized”;
DefineDLLFunc: “C:\GA\GA4TS.dll”, double, “ga_getcurrentgene”, int;
DefineDLLFunc: “C:\GA\GA4TS.dll”, int, “ga_getcurrentgeneint”, int;
DefineDLLFunc: “C:\GA\GA4TS.dll”, void, “ga_setcurrentvalue”, int, double;
DefineDLLFunc: “C:\GA\GA4TS.dll”, int, “ga_calculated”,int;
DefineDLLFunc: “C:\GA\GA4TS.dll”, int, “ga_writechain”,int, LPSTR;
DefineDLLFunc: “C:\GA\GA4TS.dll”, int, “ga_addelitechain”, LPSTR, LPSTR, LPSTR, LPSTR;
if
    currentbar = 1
then
begin
    account = start_account;
    pl = account;
    FileDelete(”C:\Omega Export\pl.csv”);
    str =
        NumToStr(date, 0) + “, ” +
        NumToStr(account, 2) +
        NewLine;
    FileAppend(”C:\Omega Export\pl.csv”, str);
    value1 = ga_getinitialized();
    if
        value1 <> 0
    then
    begin
        ga_create(2, 3, 2000, “C:\\GA\\log.txt”);
        ga_setadvancedoptions(1,2,1,0,0.002);
        ga_setcodelength(0, 4);
        ga_setcodelength(1, 7);
        ga_setcodelength(2, 5);
        ga_generate(200);
        value1 = 0;
    end;
    type = MinList(ga_getcurrentgeneint(0), 8 );
    k_delta = MinList(ga_getcurrentgeneint(1), 100);
    days_back = MinList(ga_getcurrentgeneint(2), 23) + 3;
end;
if
    currentbar > period and
    BarStatus(1) = 2
then
begin {global}
    vola = close of data2;
    if
        b_open = false and
        days_back > Next3rdFriday(1) and
        Next3rdFriday(1) > 1
    then
    begin{base conract quantity}
        base_size = ceiling(MaxList(1, start_account/(c*100/5)));
        o_date = DateToJulian(date) + Next3rdFriday(1);
        exp_days = o_date - DateToJulian(date);
        op_strike = (Floor(c[1])+1-mod(type,3));
        oc_strike = (Floor(c[1])+floor(type/3));
        op_price = floor(20*blackscholes(exp_days, op_strike, close, 3.0, vola, put))/20;
        oc_price = floor(20*blackscholes(exp_days, oc_strike, close, 3.0, vola, call))/20;
        {commission 1$ per option contract}
        account = account +100*base_size*(op_price+oc_price-0.02);
        b_open = true;
    end;
    exp_days = o_date - DateToJulian(date);
    if
        exp_days > 0
    then
    begin{delta-hedging}
        o_delta =
            floor(0.01*k_delta*(spot_size - base_size*
            (f_delta(exp_days, op_strike, close, 3.0, vola, put) +
            f_delta(exp_days, oc_strike, close, 3.0, vola, call))));
        spot_size = spot_size - o_delta;
        {2$ is commision and slippage per 100 shares}
        account = account + 100*o_delta*close - AbsValue(2*o_delta);
        pl = account + 100*spot_size*close - AbsValue(2*spot_size) -
            100*base_size*blackscholes(exp_days, op_strike, close, 3.0, vola, put) -
                100*base_size*blackscholes(exp_days, oc_strike, close, 3.0, vola, call);
    end
    else if b_open
    then
    begin
        b_open = false;
        if spot_size <> 0 then
            account = account + 100*spot_size*close - AbsValue(2*spot_size);
        if close < op_strike then account = account - 100*base_size*(op_strike - close);
        if close > oc_strike then account = account - 100*base_size*(close - oc_strike);
        pl = account;
        op_strike = 0;
        oc_strike = 0;
        spot_size = 0;
    end;
    {Statistic code}
    if pl > ha then ha = pl;
    if pl < ha[1] then dd = MinList(dd, pl-ha);
    if
        LastBarOnChart and
        print_status
    then
    begin
        ga_setcurrentvalue(0, pl/start_account);
        ga_setcurrentvalue(1, dd/start_account);
        str =
            NumToStr(count, 0) + “, ” +
            NumToStr(type, 0) + “,” +
            NumToStr(k_delta, 0) + “,” +
            NumToStr(days_back, 0) + “,” +
            NumToStr(pl/start_account, 2) + “, ” +
            NumToStr(dd/start_account, 2);
        print(str);
        value1 = ga_calculated(count);
        if
            value1 = 0
        then
        begin
            ga_writechain(200, “C:\\GA\\” + GetSymbolName + “_” +
            NumToStr(BarInterval, 0) + “.csv”);
            print(”written”);
        end;
    end;
end; {global}

Затем сигнал надо добавить в новую стратегию (меню Go->TradeStation StrategyBuilder), состоящую только из этого сигнала. После чего создаётся workspace, состоящий из двух окон, в одном из которых (левом) запущена стратегия с параметром count 0, а в другом (правом) только данные (без стратегии).


Рис. 2. Установка окон при запуске стратегии

После этого, но отключая стратегию в левом окне, запускаете оптимизацию по параметру count в правом, как показано на рисунке ниже.

Рис. 3. Параметры оптимизации

Эту процедуру необходимо проделать для того что бы TradeStation не выгружал из памяти динамическую библиотеку после каждого этапа оптимизации, а позволял использовать все данные для последующих этапов.

Результат работы ГА (рис 4), был получен менее чем за 3 минуты на 2 Гц процессоре, на полной истории QQQQ (1150 дневных баров или 4.5 года). Как видно из кода выше оптимизация проводилась по двум критериям, и для работы Генетического Алгоритма была выбрана оптимизация Парето.


Рис. 4. Последовательность поиска оптимального решения
Генетическим Алгоритмом


Рис. 5. Множество оптимальное по Парето

Рассмотрим подробней оба решения выделенных выноской. Первое из них (PL = 2.74, DD = -0.24) соответствует параметрам (type, k_delta, back_days) (8,9,23). Таким образом такой результат был получен на широко расставленном стрэнгле (8 тип), проданном за 23 дня, и «выравниванием» по дельте ежедневно на 9% от нулевого отклонения.

Динамика результатов стратегии с этими параметрами, полученная уже с помощью Konkop Xpress Analizator, приведена ниже:


Рис. 6. Помесячная доходность первой стратегии


Рис. 7. Кривая доходности первой стратегии

Второе решение (PL = 3,72, DD = -0.59) соответствует параметрам (7,0,24). Это означает стационарная продажа стрэнгла (7 тип) за 24 дня. Таким образом отсутствие хеджирования повышает риски, но и увеличивает доходность.


Рис. 8. Помесячная доходность второй стратегии


Рис. 9. Кривая доходности второй стратегии

Таким образом, можно сказать, относительно дельта-хеджирования подтверждается известный факт: чем меньше риск, тем меньше и ожидаемая прибыль стратегии. Но в множество Парето не вошло не одно решение ежедневный процент хеджирования которого составил более 20%, не говоря уже о дельта-нейтральной стратегии, для которой по этому алгоритму надо было бы проводить 100% хеджирование. Из дельта-нейтральных самой прибыльной (PL = 1.51, DD = -0.28) оказалась стратегия (6,100,26)

Стати по теме:
GA4TS.DLL – Генетический алгоритм для TradeStation
Поиск оптимальных по Парето стратегий


Tags: стратегия по опционам, тестирование опционных стратегий, результаты стратегии по опционам, применение генетического алгоритма, продажа стрэнгла, продажа стрэдла, продажа веги, продажа волатильности

Subscribe to comments with RSS or TrackBack to 'Тестирование опционных стратегий в Omega TradeStation'.

2 Responses to 'Тестирование опционных стратегий в Omega TradeStation'

  1. hi-tech said,

    on August 28th, 2008 at 5:35 pm

    Thanks for article

  2. Gioper said,

    on August 31st, 2008 at 6:45 pm

    В принципе согласен, но есть некоторые ньюансы, которые требуют более детального обсуждения.

Leave a Reply