/*
CLSMCore - класс, осуществляющий метод наименьших квадратов. Ядро.
Для использования - необходимо пронаследовать свой класс от ядра и перегрузить чисто виртуальные функции.
*/#include <MyLib\DebugOrRelease\DebugSupport.mqh>
enum ELSMType
{
LSM_FLAT = 0,
LSM_TREND = 1,
LSM_PARABOLIC = 2,
LSM_CUBIC = 3
};
struct SLSMPowers
{
double m_dThirdPower;
double m_dSecondPower;
double m_dFirstPower;
double m_dNullPower;
};
enum EQntExtremums
{
QE_NO_EXTREMUMS = 0,
QE_MAXIMUM = 1,
QE_MINIMUM = 2,
QE_BOTH = 3
};
struct SLSMExtremums
{
EQntExtremums m_qeExtremums;
double m_dMinX;
double m_dMinY;
double m_dMaxX;
double m_dMaxY;
};
class CLSMCore
{
protected:
// Степени многочленовstaticconstuint LSM_FLAT_POWER;
staticconstuint LSM_TREND_POWER;
staticconstuint LSM_PARABOLIC_POWER;
staticconstuint LSM_CUBIC_POWER;
staticconstint LSM_DEFAULT_POINT_WEIGHT;
// Функции, которые обязательно следует перегрузить// Именно эти функции возвращают число точек и их координаты.virtualuint _N() = 0; // Число точекvirtualdouble _X(uint uiIdx) = 0; // Значение X точки с индиксом uiIdxvirtualdouble _Y(uint uiIdx) =0; // Значение Y точки с индиксом uiIdx// Функци, которые можно не перегружать, если необходимо значение по умолчанию.// Это функции для указания веса и полярной точки.virtualint _W(uint uiIdx) { return(LSM_DEFAULT_POINT_WEIGHT); }; // Значение веса точки с индексом uiIdxvirtualint _PolarIdx() { return(WRONG_VALUE); } // Индекс точки, через которую должна проходить прямая, при отрицательном значении - такой точки нет. // Функция, проверяющаяя, что МНК указанного типа возможен// (Количество точек ненулевого веса должно быть на один больше, чем степень многочлена)virtualbool _LSMIsAvailible(ELSMType ltType);
// Расчетные функции (во всех случаях учитываются веса точек и отнимается полярная точка, если ее индекс валиден, обозначается "с" - корректированное).double _SummXc();
double _SummXc2();
double _SummXc3();
double _SummXc4();
double _SummXc5();
double _SummXc6();
double _SummX2c2();
double _SummX3c2();
double _SummXcYc();
double _SummX2cXc();
double _SummX2cYc();
double _SummXcX2c();
double _SummX3cX2c();
double _SummX3cXc();
double _SummX3cYc();
double _SummYc();
int _SummW();
// Функциии расчета для аппроксимированных значений.// Функции возвращают коэффициенты определенных кривых. // Последняя функция возвращает сразу все коэффициенты в структуре, ее чаще всего и имеет смысл использовать.// Функции вынесены в protected-секцию, чтобы можно было организовать различные способы доступа к данным// Для расчетов используются перегруженные функции: _N(); _X(); _Y(); _W(); _PolarIdx().// Функция расчета горизонтальной прямой (по сути среднего арифметического)double _CountFlatLevel();
// Функция расчета тренда для случая, когда есть точка-полюс, через которую должна лежать прямая// Предполагается, что _PolarIdx() != WRONG_VALUE (проверка по ASSERT)double _CountPolarTrend(double & rdPolarLevel);
// Функция расчета обычной прямой c трендом// Предполагается, что _PolarIdx() == WRONG_VALUE (проверка по ASSERT)double _CountTrend(double & rdLevel);
// Функция расчета параболы для случая, когда есть точка полюс, через которую парабола должна проходить.// Заполняет соответствующие коэффициенты// Предполагается, что _PolarIdx() != WRONG_VALUE (проверка по ASSERT)void _CountPolarParabola(double & rdSecondPower,double & rdFirstPower,double & rdNullPower);
// Функция расчета параболы.// Заполняет соответствующие коэффициенты// Предполагается, что _PolarIdx() == WRONG_VALUE (проверка по ASSERT)void _CountParabola(double & rdSecondPower,double & rdFirstPower,double & rdNullPower);
// Функция расчета кубической параболы для случая, когда есть точка полюс, через которую парабола должна проходить.// Заполняет соответствующие коэффициенты// Предполагается, что _PolarIdx() != WRONG_VALUE (проверка по ASSERT)void _CountPolarCubic(double & rdThirdPower,double & rdSecondPower,double & rdFirstPower,double & rdNullPower);
// Функция расчета кубической параболы.// Заполняет соответствующие коэффициенты// Предполагается, что _PolarIdx() == WRONG_VALUE (проверка по ASSERT)void _CountCubic(double & rdThirdPower,double & rdSecondPower,double & rdFirstPower,double & rdNullPower);
// Функция расчета указанной кривой.// Заполняет соответствующие коэффициенты (несуществующие коэффициента заполняются нулями// В зависимости от величины, возвращаемой _PolarIdx(), вызываются полярные или обычные функции
SLSMPowers _CountLSM(ELSMType ltType);
// Статические функции для матричных вычислений// Функция вычисляет детерменант матрицы размера 3х3.// Значения передаются по строкам.staticdouble _Determinant_3(double dX11,double dX12,double dX13,double dX21,double dX22,double dX23,double dX31,double dX32,double dX33) { return(dX11*dX22*dX33 - dX11*dX23*dX32 - dX12*dX21*dX33 + dX12*dX23*dX31 + dX13*dX21*dX32 - dX13*dX22*dX31);};
// Функция вычисляет детерменант матрицы размера 4х4.// Значения передаются по строкам.staticdouble _Determinant_4(double dX11,double dX12,double dX13,double dX14,double dX21,double dX22,double dX23,double dX24,double dX31,double dX32,double dX33,double dX34,double dX41,double dX42,double dX43,double dX44);
public:
CLSMCore() {};
~CLSMCore() {};
// Заполнение данной структуры одинаковыми значениями степеней.staticvoid SetPowers(SLSMPowers & lpPowers,double dPower) { lpPowers.m_dNullPower = dPower; lpPowers.m_dFirstPower = dPower; lpPowers.m_dSecondPower = dPower; lpPowers.m_dThirdPower = dPower; };
// Расчет многочлена с данными степенями и аргументом. staticdouble CountLSMPolynom(double dX,SLSMPowers & lpPowers) { return(dX*(dX*(dX*lpPowers.m_dThirdPower + lpPowers.m_dSecondPower)+lpPowers.m_dFirstPower)+lpPowers.m_dNullPower); };
// Расчет числа экстремумов для указанных степеней и, если они есть - заполняет их значения.// Возвращает структуру количеством экстремумов и их данными.// В структуре данных заполняется:// Для флета - в обоих экстремумах устанавливается Y, абсцисса у обоих устанавливается на EMPTY_VALUE// Для тренда - все значения устанавливаются на EMPTY_VALUE// Для параболы - существующий экстремум устанавливается на реальный экстремум, несуществующий на EMPTY_VALUE// Для кубики - устанавливаются оба экстремума (если они есть), либо все устанавливается на EMPTY_VALUE (если экстремумов нет).static SLSMExtremums GetExtremums(SLSMPowers & lpPowers);
};
В данном классе - есть все функции для вычисления коэффициентов МНК, и для дальнейшего построения кривой степени от 0 до 3.
Вот именно организацию данных внутри класса и нужно знать для процесса отладки и написания кода, так как это постоянно меняется по ходу дела.
Это - пока ты отлаживаешь работу самого класса.
Как только класс отлажен - ты забываешь про его внутреннее содержание.
Я уже приводил свой класс торговой позиции:
// СTradePositionI переносимый интерфейс торговой позиции // Позиция состоит из компонент-наследников интерфейса CTradePosComponentI// // Реально для МТ4 имплементацией интерфейса является объект CMT4PositionInfo, для МТ5 - объект CMT5PositionInfo// CMT4PositionInfo представляет из себя массив объектов CMT4OrderInfo, наследников интерфейса CTradePosComponentI.// Фактически это массив МТ4-ордеров.// CMT5PositionInfo представляет из себя массив объектов CMT5PositionInfoCore, наследников интерфейса CTradePosComponentI.// Объект CMT5PositionInfoCore не имеет прямого аналога в МТ5-терминах, это два массива ордеров и сделок, а также структура данных,// имеющуая идентификатор позиции (m_lPosID или POSITION_IDENTIFIER), тикет магик, вид позиции, и так далее, по аналогии с МТ4-ордером,// фактически, массивы ордеров и сделок - это аналоги ордеров и сделок обычной неттинговой МТ5-позиции, а структура данных - относится// к хеджевой позиции, и имеет аналог МТ4-ордера. //// Реально при запросе у позиции компоненты CTradePosComponentI в МТ4 мы получаем указатель на ордер (класс CMT4OrderInfo),// а в МТ5 - на ядро позиции (класс CMT5PositionInfoCore) #include <MyLib\DebugOrRelease\DebugSupport.mqh>
#include <MyLib\Common\MyObject.mqh>
#include <MyLib\Common\CurSymEnum.mq5>
#include <MyLib\Common\TrendEnums.mqh>
#include <MyLib\Trade\TradePosComponentI.mqh>
class CTradePositionI: public CMyObject
{
public:
void CTradePositionI() { SetMyObjectType(MOT_TRADE_POSITION_I); };
virtualvoid ~CTradePositionI() {};
// Выбор существующей позиции. // Указывается магик и символ, по которому выбираются действующие ордера.// Если ulMagic = 0 - выбираются все позиции по всем магикам.// Если ECurrencySymbol = CS_UNKNOWN - выбираются все позиции по всем символам// Если ECurrencySymbol = CS_CURRENT - запрашивается функция Symbol(), и выбираются все позиции по этому символу// Возвращает число компонент позиции внутри позиции (может быть нулевым если позиции нет) или WRONG_VALUE в случае ошибок// Каждая фабрика (наследник CEAPartsFactoryT) имеет одну позицию, которая на каждом CEAPartsFactoryT::OnRefresh()// обновляется в соответствии с магиком и рабочим символом фабрики. virtualint Select(ulong ulMagic = 0,ECurrencySymbol csSymbol = CS_CURRENT) = 0;
virtualuint GetTotalComponents() const = 0; // Получение общего числа компонентvirtualuint GetNumOfComponentsOnDirection(ENUM_POSITION_TYPE etDirection) const = 0; // Получение числа компонент указанного направления (если tdDirection = TD_FLAT - то всех компонент) и интерфейс отдельной компонентыvirtual CTradePosComponentI* GetComponent(uint uiComponentIdx) const = 0;
// Расширенный интерфейс// Функция исследует позицию, и возвращает ее направление:// Если все компоненты - лонги, то вверх.// Если все компоненты - шорты, то вниз.// Если компонент нет - то флет. // Если компоненты обоих типов, то смотрим на флаг bFlatIfBoth. // Если этот флаг установлен - то возвращаем флет.// Если этот флаг сброшен - то смотрим на флаг bConsiderVolume.// Если этот флаг установлен - то сравниваем общие объемы лонгов и шортов. Если сброшен - сравниваем количество лонгов и шортов.// Каких позиций (или объемов) больше - то направление и возвращаем. // Если позиций (или объемов) одинаково - возвращаем флет.// NOTE !!! Функция не проверяет магик и символ компонент !virtual ETrendDirection GetDirection(bool bFlatIfBoth = true,bool bConsiderVolume = true) const = 0;
// Функция ищет внутри позиции компоненту с указанным тикетом. // В случае, если ее нет - возвращается false.// Если компонента найдена - возвращается true, и uiComponentIdx устанавливается на индекс компоненты внутри позиции.virtualbool FindComponentByTicket(long lTicket,uint &uiComponentIdx) const = 0;
};
Как видишь, в нем все функции виртуальны - ты, получая позицию - даже не знаешь, где ты - то ли в МТ4, то ли в МТ5 (а, может быть, и еще где-то).
Ты просто получаешь указатель на CTradePositionI, и с его помощью - можешь перебрать все компоненты позиции.
Вызываешь у него функцию GetTotalComponents() - узнаешь, сколько их.
Потом можешь вызывать фукнцию GetComponent() - чтобы получать отдельные компоненты позиции.
Вызывая эту функцию - ты опять же, получаешь не данные, а интерфейс:
Можешь получить все данные по любому ордеру МТ4 или хедж-позиции МТ5 (при этом тебе даже неинтересно, что это реально).
Классы с данными - они отлажены, и работают. Пользователь - никакого доступа к ним не имеет. Он может получить только вот эти интерфейсы, и работать с позициями.
Если надо совершить торговое действие - опять же, для этого существует интерфейс торгового процессора:
class CTradeProcessorI : public CMyObject
{
public:
void CTradeProcessorI() { SetMyObjectType(MOT_TRADE_PROCESSOR_I); };
virtualvoid ~CTradeProcessorI() {};
// Настроечный интерфейсvirtualvoid SetSlippage(uint uiSlippage) = 0;
// Торговый интерфейс// Все функции возвращают код возврата торгового сервераvirtualint Buy(long & lTicket,ECurrencySymbol csSymbol,double dVolume,double dTP=0,double dSL=0,ulong ulMagic = 0,string strComment = NULL) = 0; // Всегда по цене Ask, если успешно - возвращается lTicketvirtualint Sell(long & lTicket,ECurrencySymbol csSymbol,double dVolume,double dTP=0,double dSL=0,ulong ulMagic = 0,string strComment = NULL) = 0; // Всегда по цене Bid, если успешно - возвращается lTicket virtualint ModifyTPSL(CTradePosComponentI* ptpcComponent,double dTP=0,double dSL=0) = 0;
virtualint ModifyTPSL(long lTPCTicket,double dTP=0,double dSL=0) = 0;
virtualint CloseTradeComponent(CTradePosComponentI* ptpcComponent,double dCloseVolume=EMPTY_VALUE) = 0; // dCloseVolume - закрываемый объем. Если равен EMPTY_VALUE или равен или больше, чем объем торговой компоненты с указанным тикетом - закрывается вся торговая компонента. virtualint CloseTradeComponent(long lTPCTicket,double dCloseVolume=EMPTY_VALUE) = 0; // dCloseVolume - закрываемый объем. Если равен EMPTY_VALUE или равен или больше, чем объем торговой компоненты с указанным тикетом - закрывается вся торговая компонента. virtualint PendingSet(long & lTicket,ECurrencySymbol csSymbol,ENUM_ORDER_TYPE otOrderType,double dSetPrice,double dVolume,double dTP=0,double dSL=0,ulong ulMagic = 0,string strComment = NULL) = 0; // если успешно - возвращается lTicketvirtualint PendingDelete(long lTicket) = 0;
virtualint PendingDelete(COrderInfoCore* poiOrder) = 0;
virtualint PendingModify(long lTicket,double dSetPrice,double dTP=0,double dSL=0) = 0;
virtualint PendingModify(COrderInfoCore* poiOrder,double dSetPrice,double dTP=0,double dSL=0) = 0;
// Проверка кода возвратаvirtualbool TradeOperationWasSuccessful(int iTradeResCode) const = 0;
// Если функция возвращает true, то при возникновении пользовательских ошибок (iTradeResCode > ERR_USER_ERROR_FIRST) советник прекратит работу// В случае false - работа будет продолженаvirtualbool IsUserErrorsFalal() const = 0;
// TODO: Для МТ4 был зарегистрирован код ошибки 128 - сделать не фатальным..
};
Заметь, снова - все функции виртуальные, и пользователь никакого доступа к данным торгового процессора не имеет.
Хочешь совершить торговое действие - вызывай соответствующую виртуальную функцию, а лезть во внутренние структуры ты не можешь.
Инкапсуляция - очень полезная штука, позволяющая сосредоточить внимание на конкретном действии, и не думать о внешних объектах, ограничиваясь только лишь интерфейсом взаимодействия.
Но только, когда возвратишься к коду спустя некоторое время - любая система, написанная без инкапсуляции - требует куда больше ресурсов на модификацию. Фактически, приходится писать все заново.
Вот этот код с реализацией МНК - годится для любого места, где он нужен. А если бы данные были доступны извне (особенно для модификации) - я не поручусь, что где-то я случайно не изменю их, а в результате - начнут неверно работать и прочие места.
Наследование - это просто эффективный механизм реюза кода. Если код нам нужен только один раз - возиться с ООП-оформлением не имеет смысла. Но, если код нам может понадобиться во многих местах - ООП-оформление с лихвой себя окупает.
А если бы данные были доступны извне (особенно для модификации) - я не поручусь, что где-то я случайно не изменю их, а в результате - начнут неверно работать и прочие места.
Вот поэтому каждый человек должен для себя решить - насколько его забывчивость прогрессирует, так как если программист может сесть и случайно перепортить все свои коды, даже не обратив на это внимание, то скорее всего ему ООП очень даже подойдет, а в остальных случаях наверно эти страхи не сбудутся и смысла для него в ООП нет.
Как вариант лечения забывчивости и от случайного написания кода там где не надо - можно использовать документирование кода и создание резервных копий.
Также полезно не пить много крепких напитков во время программирования так как это сильно увеличивает забывчивость и количество случайно написанного или стертого кода. ))
Также полезно не пить много крепких напитков во время программирования так как это сильно увеличивает забывчивость и количество случайно написанного или стертого кода. ))
Это пожалуй самый ценный совет...
Вообще конечно всем спасибо за разъяснения. Но наверно мне проще в процедурном варианте писать. Тем более что сама логика не самая сложная.
Вот поэтому каждый человек должен для себя решить - насколько его забывчивость прогрессирует, так как если программист может сесть и случайно перепортить все свои коды, даже не обратив на это внимание, то скорее всего ему ООП очень даже подойдет, а в остальных случаях наверно эти страхи не сбудутся и смысла для него в ООП нет.
Как вариант лечения забывчивости и от случайного написания кода там где не надо - можно использовать документирование кода и создание резервных копий.
Также полезно не пить много крепких напитков во время программирования так как это сильно увеличивает забывчивость и количество случайно написанного или стертого кода. ))
Да-да, есть тут такой Peter Konov - гигант запоминания с резко ослабленными способностями к забыванию.
Но я не раз был самому себе благодарен за то, что в любом месте кода у меня доступны исключительно только те данные, которые в этом месте нужны, а к другим - доступ ограничен. Неоднократно я сталкивался с ситуациями, когда смотрю - да какого хрена здесь я не могу получить необходимые данные ? А начинаю разбираться - опа... Не учел (забыл) важные тонкости, эти данные надо получать иначе, через другие интерфейсы, а иначе - я могу их получить с ошибкой, и даже не сразу это обнаружить.
При этом - я вобще не пью, и вроде как память имею вполне надежную. Но, только программы сейчас имеют настолько сложную структуру, что упомнить все - нереально.
Поэтому я очень широко использую ООП.
Но, в простейших случаях, когда надо что-то "проверить на коленке" - я обхожусь почти без ООП.
"Почти" - потому, что все равно, даже когда ООП не надо - мне удобно использовать старые наработки, которые выполнены в ООП-стиле.
Скажем, вот код инициализации LSM-канала:
CDataProviderT dpDataProvider(true); // Объявление дата-провайдера вне функций
/// ------------------------------------- Функция Init()
CLSMChannel_IParams lpParams; // Объявляем параметры нужного индикатора, и далее - заполняем их
lpParams.SetCommonParameters(_Symbol2CurrencyEnum(Symbol()),_GetMainTimeframe(),uiICanalLength);
lpParams.SetSpecialParameters(lpcParams);
piLSMCanal = dpDataProvider.GetIndicator(GetPointer(lpParams),uiICanalLength); // Получаем индикатор
/// ------------------------------------ Функция OnCalculate()
dpDataProvider.Refresh(); // Рефрешим дата-провайдер
// Дорисовываем канал
for(int iCurBar = 0; iCurBar<iBarsToCount; ++iCurBar)
{
adMiddleBuffer[iCurBar] = piLSMCanal.GetValue(iCurBar);
adHiBuffer[iCurBar] = piLSMCanal.GetValue(iCurBar,1);
adLoBuffer[iCurBar] = piLSMCanal.GetValue(iCurBar,2);
adHiOCBuffer[iCurBar] = piLSMCanal.GetValue(iCurBar,3);
adLoOCBuffer[iCurBar] = piLSMCanal.GetValue(iCurBar,4);
};
Объект CDataProviderT - производит все действия по созданию индикатора, возвращает указатель на него, и далее, при своем рефреше - рефрешит и этот индикатор.
По полученному указателю - можно брать нужные данные и нарисовать красивый канал:
Символ: EURUSD. Период графика: H1. Брокер: Alpari International Limited. Торговая платформа: MetaTrader 4. Режим торговли: Real. Дата: 2018.05.01 09:28 UTC.
Для авторизации и пользования сайтом MQL5.com необходимо разрешить использование файлов Сookie.
Пожалуйста, включите в вашем браузере данную настройку, иначе вы не сможете авторизоваться.
Вы вошли в аккаунт. Двухфакторная аутентификация отключена. Настройте её заново и отсканируйте новый QR-код или продолжите с отключённой двухфакторной аутентификацией.
У как эти коэффициенты извлекаются из класса?
Соответствующими функциями.
Вот мой заголовочный файл класса МНК:
В данном классе - есть все функции для вычисления коэффициентов МНК, и для дальнейшего построения кривой степени от 0 до 3.
А сами данные - отсутствуют, они получаются с помощью виртуальных функций.
Для того, чтобы использовать данный класс - надо пронаследоваться от него, и определить эти функции получения исходных данных.
Вот именно организацию данных внутри класса и нужно знать для процесса отладки и написания кода, так как это постоянно меняется по ходу дела.
Это - пока ты отлаживаешь работу самого класса.
Как только класс отлажен - ты забываешь про его внутреннее содержание.
Я уже приводил свой класс торговой позиции:
Как видишь, в нем все функции виртуальны - ты, получая позицию - даже не знаешь, где ты - то ли в МТ4, то ли в МТ5 (а, может быть, и еще где-то).
Ты просто получаешь указатель на CTradePositionI, и с его помощью - можешь перебрать все компоненты позиции.
Вызываешь у него функцию GetTotalComponents() - узнаешь, сколько их.
Потом можешь вызывать фукнцию GetComponent() - чтобы получать отдельные компоненты позиции.
Вызывая эту функцию - ты опять же, получаешь не данные, а интерфейс:
Можешь получить все данные по любому ордеру МТ4 или хедж-позиции МТ5 (при этом тебе даже неинтересно, что это реально).
Классы с данными - они отлажены, и работают. Пользователь - никакого доступа к ним не имеет. Он может получить только вот эти интерфейсы, и работать с позициями.
Если надо совершить торговое действие - опять же, для этого существует интерфейс торгового процессора:
Заметь, снова - все функции виртуальные, и пользователь никакого доступа к данным торгового процессора не имеет.
Хочешь совершить торговое действие - вызывай соответствующую виртуальную функцию, а лезть во внутренние структуры ты не можешь.
Инкапсуляция - очень полезная штука, позволяющая сосредоточить внимание на конкретном действии, и не думать о внешних объектах, ограничиваясь только лишь интерфейсом взаимодействия.
А сами данные - отсутствуют, они получаются с помощью виртуальных функций.
Для того, чтобы использовать данный класс - надо пронаследоваться от него, и определить эти функции получения исходных данных.
А без наследования напрямую нельзя данные взять?
А без наследования напрямую нельзя данные взять?
Можно.
Можно даже вобще с классами не заморачиваться.
Но только, когда возвратишься к коду спустя некоторое время - любая система, написанная без инкапсуляции - требует куда больше ресурсов на модификацию. Фактически, приходится писать все заново.
Вот этот код с реализацией МНК - годится для любого места, где он нужен. А если бы данные были доступны извне (особенно для модификации) - я не поручусь, что где-то я случайно не изменю их, а в результате - начнут неверно работать и прочие места.
Наследование - это просто эффективный механизм реюза кода. Если код нам нужен только один раз - возиться с ООП-оформлением не имеет смысла. Но, если код нам может понадобиться во многих местах - ООП-оформление с лихвой себя окупает.
Georgiy Merts:
А если бы данные были доступны извне (особенно для модификации) - я не поручусь, что где-то я случайно не изменю их, а в результате - начнут неверно работать и прочие места.
Вот поэтому каждый человек должен для себя решить - насколько его забывчивость прогрессирует, так как если программист может сесть и случайно перепортить все свои коды, даже не обратив на это внимание, то скорее всего ему ООП очень даже подойдет, а в остальных случаях наверно эти страхи не сбудутся и смысла для него в ООП нет.
Как вариант лечения забывчивости и от случайного написания кода там где не надо - можно использовать документирование кода и создание резервных копий.
Также полезно не пить много крепких напитков во время программирования так как это сильно увеличивает забывчивость и количество случайно написанного или стертого кода. ))
Это пожалуй самый ценный совет...
Вообще конечно всем спасибо за разъяснения. Но наверно мне проще в процедурном варианте писать. Тем более что сама логика не самая сложная.
Вот поэтому каждый человек должен для себя решить - насколько его забывчивость прогрессирует, так как если программист может сесть и случайно перепортить все свои коды, даже не обратив на это внимание, то скорее всего ему ООП очень даже подойдет, а в остальных случаях наверно эти страхи не сбудутся и смысла для него в ООП нет.
Как вариант лечения забывчивости и от случайного написания кода там где не надо - можно использовать документирование кода и создание резервных копий.
Также полезно не пить много крепких напитков во время программирования так как это сильно увеличивает забывчивость и количество случайно написанного или стертого кода. ))
Да-да, есть тут такой Peter Konov - гигант запоминания с резко ослабленными способностями к забыванию.
Но я не раз был самому себе благодарен за то, что в любом месте кода у меня доступны исключительно только те данные, которые в этом месте нужны, а к другим - доступ ограничен. Неоднократно я сталкивался с ситуациями, когда смотрю - да какого хрена здесь я не могу получить необходимые данные ? А начинаю разбираться - опа... Не учел (забыл) важные тонкости, эти данные надо получать иначе, через другие интерфейсы, а иначе - я могу их получить с ошибкой, и даже не сразу это обнаружить.
При этом - я вобще не пью, и вроде как память имею вполне надежную. Но, только программы сейчас имеют настолько сложную структуру, что упомнить все - нереально.
Поэтому я очень широко использую ООП.
Но, в простейших случаях, когда надо что-то "проверить на коленке" - я обхожусь почти без ООП.
"Почти" - потому, что все равно, даже когда ООП не надо - мне удобно использовать старые наработки, которые выполнены в ООП-стиле.
Скажем, вот код инициализации LSM-канала:
Объект CDataProviderT - производит все действия по созданию индикатора, возвращает указатель на него, и далее, при своем рефреше - рефрешит и этот индикатор.
По полученному указателю - можно брать нужные данные и нарисовать красивый канал:
https://www.mql5.com/ru/charts/8629095/eurusd-h1-alpari-international-limited