
Вы упускаете торговые возможности:
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Регистрация
Вход
Вы принимаете политику сайта и условия использования
Если у вас нет учетной записи, зарегистрируйтесь
Не уверен, что это сильно ускорит обработку. Насколько я знаю, стандартные индикаторы - уже выполняют эту оптимизацию. Впрочем, попробовать можно.
Говорю ж... Профилировщик очень бы помог...
Конечно, ускорит. iHighest и iLowest - это не индикаторы. Хотя, оптимизация прослеживается:
Конечно, ускорит. iHighest и iLowest - это не индикаторы. Хотя, оптимизация прослеживается:
Не, все что вы сказали - верно. Действительно, наибольшее время вычисляются именно переменные iHigest и iLowest.
Просто мне казалось, что там уже эта оптимизация есть. Ну, если нету - да, ее можно применить.
Хотя, оптимизация прослеживается:
Даже не оптимизация, а использование кэша.
При первом доступе к тайм-серии тратится в 2 раза больше времени.
В коде без использования классов я так и делал, как предложил Andrey Khatimlianskii
Но в моем советнике используется 18 стратегий, где period пересчитывается и является динамическим, т.е. зависящим от рынка. Поэтому перешел на классы.
Все советники используют этот класс.
Так вот вопрос правильно перед class Channel объявить эти массивы ?
На мой взгляд не правильно. Т.к. на каждом тике будут браться значения последнего расчета для h и l . А как тогда правильно это сделать ?
Чтоб было понятней: как запомнить значения в классе рассчитанные на предыдущем тике и при этом не потерять в производительности? Насколько я понимаю, можно все скидывать в файл, но думаю выигрыша это особого не даст.
Dimeon, как я понял, Андрей имел ввиду совсем другое.
В исходном коде - каждый раз берется максимальное и минимальное значение ряда баров.
А можно сделать иначе.
При каждом тике - глядим, новый ли это бар, или нет.
Если это бар старый - то только контролируем, не выше ли он максимума (не ниже минимума), и корректируем максимум или минимум.
Если это бар новый - то смотрим еще и на тот бар, что сейчас стал неактуален, и запрашиваем пересчет максимума или минимума только если он равен максимуму (минимуму).
В этом случае - полный пересчет максимума или минимума будет производиться гораздо реже, чем на каждом тике.
Этот алгоритм можно использовать и без классов, и с классом.
Скидывать в файл - крайне неразумно, файловая операция гораздо медленнее, чем даже цикл из тысячи обращений к памяти.
Dimeon, как я понял, Андрей имел ввиду совсем другое.
В исходном коде - каждый раз берется максимальное и минимальное значение ряда баров.
А можно сделать иначе.
При каждом тике - глядим, новый ли это бар, или нет.
Если это бар старый - то только контролируем, не выше ли он максимума (не ниже минимума), и корректируем максимум или минимум.
Если это бар новый - то смотрим еще и на тот бар, что сейчас стал неактуален, и запрашиваем пересчет максимума или минимума только если он равен максимуму (минимуму).
В этом случае - полный пересчет максимума или минимума будет производиться гораздо реже, чем на каждом тике.
Этот алгоритм можно использовать и без классов, и с классом.
Скидывать в файл - крайне неразумно, файловая операция гораздо медленнее, чем даже цикл из тысячи обращений к памяти.
Чтоб было понятней: как запомнить значения в классе рассчитанные на предыдущем тике и при этом не потерять в производительности? Насколько я понимаю, можно все скидывать в файл, но думаю выигрыша это особого не даст.
Массивы должны быть в классе, как и все рабочие переменные.
Экземпляр класса нужно создать на глобальном уровне, тогда он будет жить, пока жива программа, и все внутренние данные будут храниться.
Я же написал, что использовал этот алгоритм. А вот как передать данные в класс по считанные на предыдущем тике ?
Начните с простого алгоритма без ООП, будет понятнее и проще.
Dimeon, я бы оптимизацию в рамках твоего класса сделал бы следующим образом.
1. Вынес бы из описания класса все функции. Лучше вобще в отдельный файл (я вынес их в том же файле).
2. Перенес бы все переменные в защищенную секцию, а для доступа к ним использовал бы функции типа GetHighBound() (не делал, это затронет много другого кода, хотя и нарушает принципы ООП);
3. Вынес бы конструктор из защищенной секции в паблик-секцию. Вобще, все интерфейсные функции - должны быть паблик. Защищенными должны быть только функции, которые не должны использоваться внешними пользователями (не делал, это больше относится к принципам ООП).
4. Ввел две переменных - номер стартового бара и длина канала (они используются для оптимизации)
5. Ввел функцию SetChannelOptimized() - такую же, как и SetChannel(), но с контролем необходимости вызовов iHigest и iLowest.
6. Также ввел защищенные функции установки канала для появления нового бара и для непоявления, а также функцию определения нового бара. Все эти функции начинаю с символа подчеркивания, так я обозначаю фунции, которые используют неявные соглашения, и должны использоваться пользователями класса аккуратно.
7. Функцию IsNewBar() я сделал так, как ты мне предложил, то есть по сравнению с величиной Bars. Однако, на мой взгляд - это опасная практика. Если число баров на чарте ограничено, то при появлении нового бара - может исчезнуть стары, и тогда эта функция вернет результат, что нового бара нет, а на самом деле - он есть. Лично я предпочитаю эту функцию строить через тиковый объем.
8. На нотацию я бы обращал бы больше внимания и комментов побольше бы вставлял. Оно никаких проблем, когда класс такой небольшой, но когда в эксперте используется хотя бы десяток классов - уже правильная нотация - очень помогает избегать ошибок, да и понимание облегчает.
// Канал для Димеона
class Channel
{
public:
double h; // Верхняя граница канала
double l; // Нижняя граница канала
int hbar; // Индекс верхнего значения канала
int lbar; // Индекс нижнего значения канала
protected:
int m_iStartBar;
int m_iNumOfBars;
int m_iTotalBars;
// Функция пересчитывает канал для случая, когда новых баров нет
void _SetShannelInOldBar();
// Функция пересчитывает канал для случая, когда новый бар есть.
void _SetShannelAfterNewBar();
// Функция возвращает true, если появился новый бар.
// Кроме того, запоминает новое значение количества баров
bool _IsNewBar();
// Конструктор по умолчанию
// Устанавливает канал по диапазону текущего бара
Channel();
// Конструктор с параметрами
// Устанавливает канал, начиная с бара iStartBar по iNumOfBars ,барам.
Channel(int iStartBar,int iNumOfBars);
// Функция устанавливает канал по диапазону текущего бара.
void Channel_0();
// Устанавливает канал, начиная с бара iStartBar по iNumOfBars ,барам.
void SetChannel(int iStartBar,int iNumOfBars);
// Устанавливает канал, начиная с бара iStartBar по iNumOfBars ,барам c буфферированием.
void SetChannelOptimized(int iStartBar,int iNumOfBars);
}; // Конец объявления класса
// Функции-члены класса
Channel::Channel()
{
Channel_0();
m_iTotalBars = Bars;
};
Channel::Channel(int _StartBar,int _CountBar)
{
SetChannel(_StartBar,_CountBar);
m_iTotalBars = Bars;
};
void Channel::Channel_0()
{
h=iHigh(_Symbol,0,0);
l=iLow(_Symbol,0,0);
hbar = 0;
lbar = 0;
m_iStartBar = 0;
m_iNumOfBars = 1;
};
void Channel::SetChannel(int _StartBar,int _CountBar)
{
// Если предложенный канал выходит за границу данных баров -
// устанавливаем канал по нулевому бару
if(_StartBar+_CountBar>Bars)
{
Channel_0();
return;
};
// Если длина канала слишком маленькая -
// устанавливаем канал по нулевому бару
if(_CountBar<=0)
{
Channel_0();
return;
};
// Устанавливаем индекс верхнего бара
hbar=iHighest(NULL,0,MODE_HIGH,_CountBar,_StartBar);
// Если бар найден, запоминаем его верхнее значение, иначе - устанавливаем канал по нулевому бару.
if(hbar>=0)
h=iHigh(NULL,0,hbar);
else
{
Channel_0();
return;
};
// Устанавливаем индекс нижнего бара
lbar=iLowest(NULL,0,MODE_LOW,_CountBar,_StartBar);
// Если бар найден, запоминаем его нижнее значение, иначе - устанавливаем канал по нулевому бару.
if(lbar>=0)
l=iLow(NULL,0,lbar);
else
{
Channel_0();
return;
};
m_iStartBar = _StartBar;
m_iNumOfBars = _CountBar;
};
void Channel::SetChannelOptimized(int iStartBar,int iNumOfBars)
{
// Если длина канала или его стартовые индекс отличается - пересчитываем в любом случае.
if(iNumOfBars != m_iNumOfBars || iStartBar != m_iStartBar)
{
SetChannel(iStartBar,iNumOfBars);
return;
};
// Пересчитаем канал, в зависимости от того, пришел ли новый бар
if(_IsNewBar())
_SetShannelAfterNewBar();
else
_SetShannelInOldBar();
};
void Channel::_SetShannelInOldBar()
{
// Пересчитаем канал в старом баре
double dCurrentHigh = iHigh(NULL,0,hbar);
double dCurrentLow = iLow(NULL,0,lbar);
if(dCurrentHigh > h)
{
h = dCurrentHigh;
hbar = 0;
};
if(dCurrentLow < l)
{
l = dCurrentLow;
lbar = 0;
};
};
void Channel::_SetShannelAfterNewBar()
{
// Пересчитаем канал в новом баре
// Проверим, возможно, устаревшее значение - было экстремумом,
// в этом случае - надо пересчитать канал полностью
if(hbar+1 == m_iNumOfBars || lbar+1 == m_iNumOfBars)
{
SetChannel(m_iStartBar,m_iNumOfBars);
return;
};
// Устарвешее значение экстремумом не было.
// Следовательно, надо проверить только последний тик.
_SetShannelInOldBar();
};
bool Channel::_IsNewBar()
{
if(m_iTotalBars == Bars)
return(false);
m_iTotalBars = Bars;
return(true);
};