Cython для трейдинга – скорость имеет значение

Python замечательный язык, де факто питон стал OpenSource альтернативой множеству дорогостоящих математических пакетов в научной среде, трейдинг здесь не является исплючением. Я считаю, что трейдинг  – это больше наука, чем искусство. Весь прошлый месяц я делал инструмент, который выполняет тонны расчетов, при чем он это делает на питоне. А как любой интерпретируемый язык – питон достаточно медленный, и прожорливый по части памяти, но в умелых руках его скорости могут достигать скоростей C/C++ языков! Поделюсь небольшим опытом оптимизации расчетов на питоне, а также поделюсь single pass алгоритмом для расчета СКО на Питоне. Сразу скажу, для этого конкретного кода мне удалось добиться увеличения производительности кода в 30 (тридцать) раз!

Вот тот самый код, который считает СКО, среднее и количество в online режиме, это позволяет получить большой выигрыш по производительности и по использованию памяти. Если кто не знает, обычный расчет СКО подразумевает 2 прохода по всем данным, сначала рассчитывается средняя, потом среднеквадратическое отклонение от нее. Более того большинство online алгоритмов расчета СКО не проходили проверку на стабильность, я тупо сравнивал с numpy.std(), и мой алгоритм имеет 100% сходимость с numpy. Хотя алгоритм конечно не мой, я откопал его где-то в интернетах и переписал на питон.

class Stats_py:
    k = 0.0
    Mk = 0.0
    Qk = 0.0
    def __init__(self):
        self.k = 0.0
        
    def add(self, x):
        self.k += 1
        if self.k == 1.0:    
            self.Mk = x
            self.Qk = 0.0
        else:
            d = x - self.Mk
            self.Qk += (self.k-1.0)*d*d/self.k
            self.Mk += d / self.k
    
    def std(self):
        return sqrt(self.Qk/self.k)
    def mean(self):
        return self.Mk
    def count(self):
        return self.k

Делаем простую проверку на скорость в IPython:

values = np.random.random(10000)
s = Stats_py()
for x in values:
    s.add(x)  
print np.std(values), s.std()

%timeit s.add(1)
------
0.289223020474 0.289223020474
100000 loops, best of 3: 2.93 µs per loop

Как вы видите цифры бьються до последнего знака, а выполнение одного вызова функции add() занимает 2.93 микросекунды = 2930 наносекунд. С одной стороны это очень мало, с другой стороны когда нужно совершить миллиарды итераций это время начинает уже сильно ощущаться.

Теперь давайте выжмем из этого кода все на что он способен! Для этого нам нужно взять в руки напильник Cython и научиться с ним работать. IPython Notebook предоставляет отличную возможность писать и отлаживать код Cython прямо в браузере! Для этого есть специальный %%cyton magiс, примеры работы с ним можно посмотреть в разделе Cython Magic Functions Extension.

Оптимизация cython

Читать далее…

Предохранитель торговых систем

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

Первое что приходит мне в голову – это управление лимитом стратегии на основе анализа ее же equity. Основная идея такого подхода заключается в том, что в момент просадки по системе у нас срабатывал бы предохранитель, который консервирует стратегию или снижает ее лимит.
По такому случаю я написал пару простеньких скриптов в Амиброкере. Эта штука помогает быстро прикинуть качество того или иного алгоритма, я добавил 3 простых Equity стратегии: Parabolic SAR, EMA, и на основе Median.

Как оно работает:

1. В после теста Амиброкер создает тикер с названием ~~Equity
2. Накидываете оба скрипта на график и наглядно видите улучшились или ухудшились результаты при применении Equity стратегии.

Вот как это будет выглядеть:

31

Теперь немного отойдем от технических вещей, и поговорим о том что ближе к деньгам :)

Читать далее…

Python+Pandas для исследований рынка

Решил запостить небольшую сказку из чата, где я показал простую задачу: скачать котировки 2х рядов, объединить их и подготовить данные для последующей работы.

[16:59] Тема: скачка и формирование данных с помощью python+pandas
[17:00] Задача: скачать с сайта ММВБ данные по индексам РЕПО + ММВБ, объединить их, и подготовить для анализа
[17:00] Все лезим и качаем URL http://www.micex.ru/issrpc/marketdata/stock/index/history/by_ticker/index_history_MICEXEQRRON.csv?secid=MICEXEQRRON&lang=ru
[17:01] pandas это тоже может сделать, более того, он автоматом распарсит эту csv, и распознает дату/время, и сделает ее индексом.
[17:02] индекс может быть любым, в нашем случае это дата время
[17:03] шаг 1: качаем данные по РЕПО, и конвертим их в объект DataFrame

Selection_628

[17:04] DataFrame – это как таблица в БД, в нашем случае это временные ряды OHLC которые отдала нам биржа
[17:05] дальше, нам нужно переименовать наш DF, чтобы не было конфликта колонок CLOSE когда мы будем объединять их с индексом ММВБ

Selection_627

[17:07] шаг 2: качаем данные индекса ММВБ
[17:08] на сайте биржи эти данные с 1997 года, а индексы репо с 2006. Поэтому целесообразно часть данных до 2006 года убрать.

Selection_622

[17:09] Хотя при объединении 2х ДФ, Pandas мягко обойдет грабли, пропущенные данные будут заменены NaN.
[17:09] Мы просто сделали это для удобства
[17:09] дальше нам надо смержить 2 набора данных, и синхронизировать их по времени
[17:10] Синхронизация в pandas делается на лету, по значению df.index, т.к. у нас это дата-время, то все будет корректно выровнено по времени
[17:11] Шаг 3: приводим все в комфортный для понимания вид, и удаляем ненужные поля из ДФ

Selection_629

[17:12] дальше можно что-то начать анализировать
[17:13] вот наш готовый ДФ

Selection_625

[17:19] вот пример как посчитать MA, и построить отношение

Selection_626

Более подробно исходный код и результаты его работы можно посмотреть по ссылке:
http://nbviewer.ipython.org/4153430/

Рисерч выполнен с помощью IPython Notebook

Python: формула оценки последовательности убыточных сделок

Риск системы, и ее просадка во многом зависит от последовательности нескольких убыточных сделок. Как правило MaxDD возникает как серия из нескольких убытков, а не как один большой лось.

Существует аналитическая формула, которая позволяет оценить ожидаемое количество убытков подряд, на основе количества сделок в системе и вероятности выигрыша.

LoosingStreak = LN(1 / nTradesCount)/LN(1 – WinProbability)

FYI: Логарифмы натуральные. WinProbability – проценты в долях единицы, 0.6 = 60%

Я решил эту формулу численно перепроверить и написал простенький скрипт на Python.

Скрипт показывает количество убытков подряд, и их зависимость от числа сделок, при жестко заданной вероятности.

Вот результаты моделирования при Win% = 60%

14

Названия колонок:
Trades – число сделок
Theo – количество убыточных сделок подряд, по теоретической формуле
Real – количество убыточных сделок подряд, которое показало моделирование
Error – Theo – Real

Как вы видите показатели Theo и Real практически сходятся, а Error остается постоянной. Это говорит нам о том, что теоретическая формула работает.

Код на Python:

# -*- coding: utf-8 -*-
"""
Created on Thu Jul 19 15:10:23 2012

@author: ubertrader
"""

# -*- coding: utf-8 -*-
"""
Created on Tue Jul 17 14:06:39 2012

@author: ubertrader
"""

import random 
from math import *



winperc = 0.6
MCIterations = 10000

mintr = 100
maxtr = 1000
steptr = 100

TheoStreak = []
RealStreak = []

for trcount in range(mintr, maxtr, steptr):    
    sumstreak = 0
    cnt = 0
    for mci in range(1,MCIterations):
        streak = 0
        maxstreak = 0
        
        for t in range(0, trcount):        
            if random.random() < winperc:             
                profit = 1
            else:
                profit = -1
                
            if profit > 0:            
                streak = 0
            else:            
                streak = streak + 1
                        
            maxstreak = max(streak, maxstreak)
            
        
        sumstreak = sumstreak + maxstreak
        cnt = cnt + 1
        
    #Calc streak error
    LoosingStreak = log(1/float(trcount))/log(float(1-winperc))
    avgstreak = float(sumstreak) / float(cnt)
    
    RealStreak.append(avgstreak)
    TheoStreak.append(LoosingStreak)
    

print 'Trades\tTheo\tRealAvg\tError'
idx = 0
for trcount in range(mintr, maxtr, steptr):        
    print '{0}\t{1:0.2f}\t{2:0.2f}\t{3:0.2f}'.format(trcount, TheoStreak[idx], RealStreak[idx], TheoStreak[idx] - RealStreak[idx])
    idx = idx + 1
    

    

Python: код для генерации баров из тиков

Еще один простенький скрипт на python для генерации баров из тиков.

Какие примеры дает этот код:
1. Чтение/запись файлов
2. Работа с CSV
3. Парсинг даты/времени
4. Good Practice создания ООП кода

P.S. Код достаточно медленный из-за парсинга Даты-Времени в этом месте self.Time = strptime(time[0:16], timeformat);
Читать далее…

Python: скрипт для risk management

Небольшое дополнение к посту от Aspirin и Avals. Простенькая число-дробилка, которая показывает ожидаемые параметры по системе, рассчитывает NetProfit, MaxDD, PF, %win, и число убыточных сделок подряд.

Ее можно использовать для оценки рисков по системе (risk management), т.к. как правило MaxDD одного теста нерепрезентативно, потому как может не учитывать все вероятности получения череды убыточных сделок.

Вот результат моделирования “системы” у которой avgwin = 50, avgloss = 50, win% = 50%. Теоретически мы знаем, что МО этой системы = 0. Но в зависимости от того, как падает наша монетка, мы можем получить как + так и – по этой системе.

Я прогнал 100 000 случайных генераций, по 100 трейдов с описанными выше параметрами. Что дало следующие результаты:

13

На картинке мы видим распределение случайных результатов по процентилям, 50% процентиль – это среднее значение, и оно нам подтверждает что система имеет МО = 0. Также результат показывает, что в среднем мы можем ожидать 6 убыточных сделок подряд, и есть 10% вероятность, что мы увидим 8 убытков к ряду.

Самая главная часть, состоит в том, что в 5% случаях есть вероятность увидеть просадку выше -1050, это число кстати может быть оптимистичнее, чем то что есть у вас на тестах. В силу того, что этот скрипт работает со средними значениями, его результаты несколько оптимистичны. У меня есть другой скрипт, который я опубликую для членов клуба позже.

Настройки:
1. Вероятность выигрыша в долях единицы 0.5 = 50%
winperc = 0.50
2. Средний выигрыш в % или деньгах
avgwin = 50
3. Средний проигрыш в % или деньгах
avgloss = 50
4. Минимальное число сделок в год
mintradesperyear = 100
5. Максимальное число сделок в год
maxtradesperyear = 100

FYI: Скрипт считает модель на основе годового периода, хотя это полнейшая условность.Мин и Макс число сделок в год вносит дополнительную “случайность”, можно ставить одинаковое значение.

Сам код:
Читать далее…

Python для трейдера

Python – кросс-платформенный язык, который обладает очень простым и читаемым синтаксисом. Спасибо druidkgm и Doctor Leo за то, что они обратили мое внимание на этот язык. Раньше я использовал C# для своих прикладных задач, это тоже отличный язык, но в некоторых случаях нужно написать в 3-5 раз больше кода чем на Python или Matlab, чтобы добиться решения одной и той же задачи.

Конечно же я полностью не отказался от C# и Matlab, каждый язык нужен для своих задач. Python я использую в тех случаях когда мне нужно, сделать небольшую утилитку “на коленке” и быстро получить результат.

Этот пост, будет вводным. Дам несколько ссылок на интересные ресурсы.

Python wikipedia – общее представление о языке

Python (x,y) – среда разработки Python,а по совместительству целая платформа по численному анализу, чем-то напоминает Матлаб.

Модуль для Visual Studio – вся мощь Python доступна теперь и в Visual Studio. Интеграция с Iron Pyhon а значит и c C#. Т.е. мы можем легко писать сборки на питоне и потом их легко использовать в любых C# приложениях. Обзор возможностей на русском

Также нужно отметить, что есть вариант языка с синтаксисом Python, но с возможностью доступа ко всем классам .net. IronPython Однако, нужно отметить, что эта реализация языка лишает возможности обращаться к стандартным библиотекам и модулям Python. Более того она реализована через механизм DLR (wiki) и программа “hello world!” компилируется в такую кашу кода, что хватаешься за голову. Понятно что IronPython работает медленнее, чем C# .net код, и даже медленнее чем оригинальный Python. Однако, в интернете, есть примеры приложений на C#, в которые можно встроить динамический скриптовый движок на IronPython.

Вообще Python язык сверхвысокого уровня, он может все, что может C++ или C#. На нем написаны множество приложений. У него есть даже специальные компиляторы, которые помогают ускорить работу там, где это необходимо.

Есть 2 модуля, которые нужны для работы с числами, хотя я близко с ними не сталкивался. NumPy – векторные вычисления как в Matlab. Matplotlib – библиотека вывода графиков. Оба пакета есть в Python (x,y),ссылку на этот софт я давал выше.

В следующих постах, я опубликую несколько Python скриптов, которые решают некоторые трейдерские задачи.

Yahoo dividend downloader

Небольшая утилитка для скачивания дивидендов и сплитов с finance.yahoo.com

Usage:
Code:


YahooDivLoader.exe Ticker BeginDate EndDate [-a]
     Ticker - ticker name according to Yahoo rules
     BeginDate - dd.mm.yyyy
     EndDate - dd.mm.yyyy
     -a - [Optional] Automatically adjust splits and div in Amibroker

Работает как консольное приложение из командной строки. Указываете тикер, дату начала и конца. В папку где лежит программа падает файлик вида [TickerName].csv

Например тикер ABV.csv
Формат файла
<Date>;Split(-1)/Div(1);Split/Div Value
Code:


21.03.2006 0:00:00;1;0.0592
01.06.2006 0:00:00;1;0.0616
22.03.2007 0:00:00;1;0.0784
26.09.2007 0:00:00;1;0.1792
15.04.2008 0:00:00;1;0.2242
22.07.2008 0:00:00;1;0.2126
30.09.2008 0:00:00;1;0.157
06.07.2009 0:00:00;1;0.1274
16.09.2009 0:00:00;1;0.1858
03.12.2009 0:00:00;1;0.2584
19.03.2010 0:00:00;1;0.1912
30.09.2010 0:00:00;1;0.3958
01.12.2010 0:00:00;1;0.402
28.12.2010 0:00:00;-1;0.2
10.03.2011 0:00:00;1;0.368
07.07.2011 0:00:00;1;0.272

Программа также имеет опциональный ключ [-a], для его использования необходимо иметь установленный Amibroker. Программа изначально преследует цель сделать Split/Div Adjust для интрадейных котировок от IQFeed.net. В отличие от котировок Yahoo, у которых точность AdjClose всего 2 знака, моя софтина ограничена точностью float типа. С ключем -a происходит автоматическое изменение данных БД (сделайте бекап!), в поле OI записывается поправочный коэффициент, а в поле Aux1 (у Ами доп поля для любого бара Aux1\2) значение выплаченных дивидендов. Таким образом база данных не только пополняется Adjusted котировками, но и историей дивидендов.Если вы используете Mixed Intraday/EOD БД Амиброкера то дневные данные игнорируются, т.к. у IQFeed они Split Adjusted. В архиве лежит AFL скрипт для самообновления БД Амиброкера через Scan.

Системные требования: .Net Framework 2.0
Программа компилировалась под “Any CPU” на x64 машине, просьба отписаться как она будет запускаться на х86. Исходники пока не буду выкладывать, кому надо воспользуется Reflector.

Программа предоставляется AS IS, без каких либо гарантий работоспособности.

Исходники программы на C#

Гистограмма месячной прибыли…

Кому интересен код рисующий подобный график:

В

Взят отсюда : www.amibroker.com/kb/2007/10/11/low-level-gfx-example-yearlymonthly-profit-chart/

Только оригинальный код в % считает, а я в деньгах. Можно закинуть его в папку Formulas\Report Charts, и в версиях выше 5.16 Вам в отчеты будет падать график с этой гистограммой.
Посему прикрепляю свой код:

SetBarsRequired(1000000,1000000);
eq = Foreign("~~~EQUITY", "C" );

yr = Year();
mo = Month();

YearChange = yr != Ref( yr, -1 );
MonChange = mo != Ref( mo, -1 );

FirstYr = 0;
LastYr = 0;

startbar = 0;

////////////////////////////
// SKIP non-trading bars
////////////////////////////
for( i = 0; i <; BarCount; i++ )
{
  if( eq[ i ] )
  {
    startbar = i;
    break;
  } 
}

////////////////////////////
// collect yearly / monthly changes in equity
// into dynamic variables
////////////////////////////

LastYrValue = eq[ startbar  ];
LastMoValue = eq[ startbar  ];

MaxYrProfit = MinYrProfit = 0;
MaxMoProfit = MinMoProfit = 0;

for( i = startbar + 1; i < BarCount; i++ )
{
  if( YearChange[ i ] || i == BarCount - 1 )
  {
    Chg = ( eq[ i ] - LastYrValue );
    VarSet("ChgYear"+ yr[ i - 1 ], Chg );

    MaxYrProfit = Max( MaxYrProfit, Chg );
    MinYrProfit = Min( MinYrProfit, Chg );

    if( FirstYr == 0 ) FirstYr = yr[ i - 1 ];
    LastYr = yr[ i ];

    LastYrValue = eq[ i ];
  }

  if( MonChange [ i ] || i == BarCount - 1 )
  {
    mon = mo[ i - 1 ];

    Chg = ( eq[ i ] - LastMoValue );

    VarSet("ChgMon" + yr[ i - 1 ] + "-" + mon, Chg );
    VarSet("SumChgMon"+ mon, Chg + Nz( VarGet("SumChgMon"+ mon ) ) );
    VarSet("SumMon" + mon, 1 + Nz( VarGet("SumMon"+ mon ) ) );
 
    MaxMoProfit = Max( MaxMoProfit, Chg );
    MinMoProfit = Min( MinMoProfit, Chg );

    LastMoValue = eq[ i ];
  }
}

/////////////////////////////////////////////////
// Drawing code & helper functions
////////////////////////////////////////////////

GfxSetOverlayMode( 2 );

CellHeight = (Status("pxheight")-1)/(LastYr - FirstYr + 3 ); 
CellWidth = (Status("pxwidth")-1)/14; 
//GfxSelectFont( "Tahoma", 8.5 ); 
GfxSelectFont( "Tahoma", 8.5 , 1, False, False, 0.3); 


GfxSetBkMode( 1 );

function PrintInCell( string, row, Col ) 
{
 Color =  ColorRGB( IIf( row == 0 || col == 0 || col == 13, 220, 255 ), 255, IIf( row % 2, 255, 220 ) );
 GfxSelectSolidBrush( Color   );
 GfxRectangle( Col * CellWidth, 
                    row * CellHeight, (Col + 1 ) * CellWidth + 1, 
                    (row + 1 ) * CellHeight  + 1); 
 GfxDrawText( string, Col * CellWidth + 1, 
                    row * CellHeight + 1, 
                    (Col + 1 ) * CellWidth, (row + 1 ) * CellHeight, 32+5 ); 
} 

YOffset = 25;
XOffset = 15;

function DrawBar( text, bar, numbars, y, Miny, Maxy )
{
 BarWidth = (Status("pxwidth") - 4 * XOffset )/( numbars + 1 ); 
 BarHeight = Status("pxheight") - 2 * YOffset;
 relpos = ( y - Miny ) / (Maxy - Miny );

 xp = XOffset + ( bar + 0.5 ) * BarWidth;
 yp = YOffset + BarHeight * ( 1 - relpos );
 xe = XOffset + ( bar + 1 ) * BarWidth;
 ye = YOffset + BarHeight * ( 1 - ( -miny )/( maxy - miny ) );
  
 if( y > 0 )
 {
 GfxGradientRect( xp, yp, 
                  xe , ye,
                  ColorHSB( 70, 255 * relpos, 255 ), ColorHSB( 70, 20, 255 ) ); 
 }
 else
 {
 GfxGradientRect( xp, ye, 
                  xe , yp,
                  ColorHSB( 0, 20, 255 ), ColorHSB( 0, 255 * ( 1 - relpos ), 255 ) ); 
 }
 GfxSelectFont( "Tahoma", 8.5 , 1, False, False, 0.3); 
 GfxTextOut( text, xp, ye );
// GfxTextOut( StrFormat("%.2f", y ), xp, yp );
// GfxSelectFont( "Tahoma", 8.5 ); 
}    

function DrawLevels( Miny, Maxy )
{
  range = Maxy - Miny;

  grid = grid = round(range / 20 / 100)* 100; 
/*
  if( range < 10 ) grid = 1;
  else 
  if( range < 20 ) grid = 2;
  else 
  if( range < 50 ) grid = 5;
  else 
  if( range < 100 ) grid = 10;
  else 
  if( range < 200 ) grid = 20;
  else 
  if( range < 500 ) grid = 50;
  else 
  if( range < 5000 ) grid = 500;
  else 
  if( range < 10000 ) grid = 1000;
*/


//  _TRACE("grid = "+grid +" range "+range );
  
  width = Status("pxwidth") - 4 * XOffset;
  height = Status("pxheight") - 2 * YOffset;

  GfxSelectPen( colorBlack, 1, 2 );
  for( y = grid * ceil( Miny / grid ); y <= grid * floor( Maxy / grid ); y += grid )
  {
    yp =  YOffset + Height * ( 1 -  ( y - Miny ) / (Maxy - Miny ) );

    GfxMoveTo( XOffset, yp );
    GfxLineTo( XOffset + width , yp );
    GfxTextOut( ""+ y, XOffset + 2 + width, yp );
  }

  GfxSelectPen( colorBlack, 1, 0 );
  GfxMoveTo( XOffset, YOffset );
  GfxLineTo( XOffset + width, YOffset );
  GfxLineTo( XOffset + width, YOffset + Height );
  GfxLineTo( XOffset , YOffset + Height );
  GfxLineTo( XOffset , YOffset );
}

function DrawYear(  text, bar, numbars)
{
 BarWidth = (Status("pxwidth") - 4 * XOffset )/( numbars + 1 ); 
 BarHeight = Status("pxheight") - 2 * YOffset;

 height = Status("pxheight") - 2 * YOffset;

 xp = XOffset + ( bar + 0.5 ) * BarWidth;
  
 GfxSelectFont( "Tahoma", 12 , 700); 
 GfxTextOut( text, xp, BarHeight );
 GfxSelectPen( colorGrey40, 1, 2 );

  GfxMoveTo( xp - 0.25  * BarWidth, YOffset );
  GfxLineTo( xp - 0.25  * BarWidth, Status("pxheight") );
 GfxSelectFont( "Tahoma", 8.5 ); 

}


MonthNames = "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec";

function DisplayProfitTable( )
{
 Header = "Year,"+MonthNames+",Yr Profit%";
 for( Col = 0; (Colname = StrExtract( Header, Col ) ) != ""; Col++ )
 {
  PrintInCell( ColName, 0, Col );
 }

 Row = 1;
 for( y = FirstYr; y <= LastYr; y++ )
 {
  PrintInCell( StrFormat("%g", y ), Row, 0 ); 
  PrintInCell( StrFormat("%.1f%", VarGet("ChgYear" + y ) ), Row, 13 ); 
  for( m = 1; m <= 12; m++ )
  { 
   Chg = VarGet("ChgMon" + y + "-" + m);
   if( Chg ) 
     PrintInCell( StrFormat("%.1f%", Chg ), Row, m );
   else
     PrintInCell( "N/A", Row, m );
  }
  Row++;
 } 

 PrintInCell("Mon. Avg", Row, 0 );
 for( m = 1; m <= 12; m++ )
 { 
   PrintInCell( StrFormat("%.1f%",  Nz( VarGet("SumChgMon" + m)/VarGet("SumMon" + m ) ) ), Row, m );
 }

}

function DisplayYearlyProfits()
{
 Bar = 0;
 for( y = FirstYr; y <= LastYr; y++ )
 {
   Chg = VarGet("ChgYear" + y );
   DrawBar( ""+y, Bar++, ( LastYr - FirstYr + 1 ), Chg, MinYrProfit, MaxYrProfit );
 }
 GfxTextOut("Yearly % Profit chart", 10, 10 );
 DrawLevels( MinYrProfit, MaxYrProfit ); 
}

function DisplayMonthlyProfits()
{
 Bar = 0; 
 MaxBars = 0;
 MinAvgProf = 99999999999999999;
 MaxAvgProf = 0;

for( y = FirstYr; y <= LastYr; y++ )
 {
  for( m = 1; m <= 12; m++ )
  { 
   Chg = VarGet("ChgMon" + y + "-" + m);
	if(IsEmpty(Chg))
		continue;
	Maxbars++;
   MinAvgProf = Min( MinAvgProf, Chg );
   MaxAvgProf = Max( MaxAvgProf, Chg );
//	_TRACE("Maxavg: " + MaxAvgProf + "MinAvg " + MinAvgProf);
  }
}

 for( y = FirstYr; y <= LastYr; y++ )
 {
  for( m = 1; m <= 12; m++ )
  { 
   Chg = VarGet("ChgMon" + y + "-" + m);


//DrawBar("" + y + "\n" + StrExtract(MonthNames, m-1 ), Bar++, ( LastYr - FirstYr + 1 ) * 13, Chg, MinAvgProf , MaxAvgProf );   

   if( !IsEmpty(Chg) ) 
//	   DrawBar("" + y + "\n" + StrExtract(MonthNames, m-1 ), Bar++, MaxBars, Chg, MinAvgProf , MaxAvgProf );   
	   DrawBar("" + (m) , Bar++, MaxBars, Chg, MinAvgProf , MaxAvgProf );   
	
	if(m == 1)
	{
		DrawYear("" + y, Bar-1, MaxBars);
	}
  }

 } 
DrawLevels( MinAvgProf , MaxAvgProf ); 


/*

//--------
 Bar = 0; 
 MinAvgProf = MaxAvgProf = 0;
 for( y = 1; y <= 12; y++ )
 {
   Chg = VarGet("SumChgMon" + y ) / VarGet("SumMon" + y );
   MinAvgProf = Min( MinAvgProf, Chg );
   MaxAvgProf = Max( MaxAvgProf, Chg );
 }

 for( y = 1; y <= 12; y++ )
 {
   Chg = VarGet("SumChgMon" + y ) / VarGet("SumMon" + y );
   DrawBar( StrExtract(MonthNames, y-1 ), Bar++, 13, Chg, MinAvgProf , MaxAvgProf );
 }
 GfxTextOut("Avg. Monthly % Profit chart", 10, 10 );

 DrawLevels( MinAvgProf , MaxAvgProf ); 
*/
}

///////////////////////////
// This function checks if currently selected symbol
// is portfolio equity
//////////////////////////
function CheckSymbol()
{
 if( Name() != "~~~EQUITY" )
 {
  GfxSelectFont( "Tahoma", 20 ); 
  GfxSetBkMode( 2 );
  GfxTextOut("For accurate results switch to ~~~EQUITY symbol", 10, 10 );
 }
}

////////////////////////////
// Main program - chart type switch
////////////////////////////
type = ParamList("Chart Type", "Avg. Monthly Profits|Profit Table|Yearly Profits", 0 );

switch( type )
{
 case "Profit Table": 
         DisplayProfitTable();  
         break;
 case "Yearly Profits": 
         DisplayYearlyProfits();
         break;
 case "Avg. Monthly Profits": 
         DisplayMonthlyProfits();
         break;
}

CheckSymbol();

//
//	Exploration Monthly profits
//
if(Status("Action") == actionExplore)
{
Filter = 0;
SetOption("NoDefaultColumns", True );
fi = 0;

DT = 0;
mRet = 0;

for( y = FirstYr; y <= LastYr; y++ )
 {
  for( m = 1; m <= 12; m++ )
  {	
   Chg = VarGet("ChgMon" + y + "-" + m);
	if(IsEmpty(Chg))
		continue;
	Filter[fi] = 1;
	DT[fi] = 10000 * (y - 1900) + 100 * m + 1;
	mRet[fi] = Chg;	
	fi++;
  }
}

AddColumn(DateTimeConvert( 2, DT ), "Date", formatDateTime);
AddColumn(mRet, "MonthReturn", 1.1);
//AddColumn(StDev(mRet,12), "StDev(12) MoRetrn", 1.2);
//AddColumn(MA(mRet,12), "Avg(12) MoRetrn", 1.2);
//AddColumn(MA(mRet,12) / StDev(mRet,12), "ModSR MoRetrn", 1.2);

}

 

Исследуем сделки торговой системы

Накидал небольшой код, суть его проста: в режиме Exploration добавлять к сделкам сгенерированным из Buy/Sell сигналов, любые характеристики (будь то уровень RSI, соотношение объемов и т.п. до входа в сделку).

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

Главное, что этот подход позволяет отойти от модели “написал правило – оттестировал – оптимизировал”, так скажем новый взгляд – осознанный подход. Это позволяет трейдеру стать имхо более робастным, в поиске закономерностей в системе.
Области применения вижу следующие:
– Анализ существующих систем, для поиска оптимальных стопов/тейков
– Поиск фильтров для систем
– Анализ серий сделок для реализации ММ на основе последовательностей выигрышных и проигрышных сделок
– Определение фазы рынка в котором система может работать наиболее эффективно
– И многое другое

Полученные от Exploration’a данные можно вставить в эксель и обработать как душе угодно, как всегда дальше вы ограничены только своей фантазией.
Читать далее…