1011 行
92 KiB
MQL5
1011 行
92 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| ExpGRI_AMA_TEMA.mq5 |
|
|
//| Copyright 2025, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
#property version "1.00"
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Включаемые файлы |
|
|
//+------------------------------------------------------------------+
|
|
#include <Trade\Trade.mqh>
|
|
#include <Arrays\ArrayLong.mqh>
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Перечисления |
|
|
//+------------------------------------------------------------------+
|
|
//--- Состояния GRI
|
|
enum ENUM_GRI_STATE
|
|
{
|
|
GRI_STATE_UNKNOWN, // Нет данных
|
|
GRI_STATE_FLAT, // Низкая волатильность
|
|
GRI_STATE_VOLATILITY_HIGH, // Высокая волатильность
|
|
GRI_STATE_GROWING, // Волатильность растёт
|
|
GRI_STATE_FALLING, // Волатильность падает
|
|
GRI_STATE_TURN_TO_GROW, // Переход к росту волатильности
|
|
GRI_STATE_TURN_TO_FALL // Переход к снижению волатильности
|
|
};
|
|
//--- Типы сигналов скользящих средних
|
|
enum ENUM_SIGNAL_TYPE
|
|
{
|
|
SIGNAL_TYPE_NONE, // Нет сигнала
|
|
SIGNAL_TYPE_LONG, // Сигнал на покупку
|
|
SIGNAL_TYPE_SHORT, // Сигнал на продажу
|
|
SIGNAL_TYPE_CLOSE, // Сигнал на закрытие позиций
|
|
};
|
|
|
|
//--- Структура позиций
|
|
struct SData
|
|
{
|
|
CArrayLong list_tickets; // Список тикетов открытых позиций
|
|
double total_volume; // Общий объём открытых позиций
|
|
};
|
|
|
|
//--- Структура данных позиций по типам
|
|
struct SDataPositions
|
|
{
|
|
SData Buy; // Данные позиций Buy
|
|
SData Sell; // Данные позиций Sell
|
|
}
|
|
Data;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Макроподстановки |
|
|
//+------------------------------------------------------------------+
|
|
#define DATA_COUNT 3 // Количество получаемых данных от индикаторов (3 и более)
|
|
#define ENV_ATTEMPTS 3 // Количество попыток ожидания получения окружения
|
|
#define ENV_WAIT_ATTEMPT 1000 // Количество миллисекунд ожидания обновления окружения
|
|
#define SPREAD_MLTP 3 // Множитель спреда для дистанции стоп-приказов
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Входные параметры |
|
|
//+------------------------------------------------------------------+
|
|
//--- GRI
|
|
input int InpPeriodGRI = 9; /* GRI calculation period */ // Период расчёта GRI
|
|
input bool InpUseGRI = true; /* Use GRI filtering */ // Использовать фильтрацию по GRI
|
|
input double InpThresholdGRI = 50.0; /* GRI Volatility threshold */ // Порог волатильности GRI
|
|
|
|
//--- TEMA - указывает направление торговли
|
|
input int InpPeriodTEMA = 14; /* TEMA calculation period */ // Период расчёта TEMA
|
|
input ENUM_APPLIED_PRICE InpPriceTEMA = PRICE_CLOSE; /* TEMA applied price */ // Цена расчёта TEMA
|
|
input int InpShiftTEMA = 0; /* TEMA shift */ // Сдвиг линии TEMA
|
|
|
|
//--- AMA - фильтрует сигналы по положению цены выше/ниже
|
|
input int InpPeriodAMA = 9; /* AMA calculation period */ // Период расчёта AMA
|
|
input int InpFastEmaAMA = 2; /* AMA fast EMA period */ // Период быстрого EMA AMA
|
|
input int InpSlowEmaAMA = 30; /* AMA slow EMA period */ // Период медленного EMA AMA
|
|
input ENUM_APPLIED_PRICE InpPriceAMA = PRICE_CLOSE; /* AMA applied price */ // Цена расчёта AMA
|
|
input int InpShiftAMA = 0; /* AMA shift */ // Сдвиг линии AMA
|
|
|
|
//--- BB - установка стопов по значениям
|
|
input int InpPeriodBB = 58; /* BB calculation period */ // Период расчёта BB
|
|
input double InpDeviationBB = 2.0; /* BB deviations */ // Отклонения BB
|
|
input int InpShiftBB = 0; /* BB shift */ // Сдвиг BB
|
|
input ENUM_APPLIED_PRICE InpPriceBB = PRICE_CLOSE; /* BB applied price */ // Цена расчёта BB
|
|
|
|
//--- ATR - расчёт величины тейк-профит по значению
|
|
input int InpPeriodATR = 64; /* ATR calculation period */ // Период расчёта ATR
|
|
|
|
//--- Торговля
|
|
input double InpVolume = 0.1; /* Position volume */ // Объем позиции
|
|
sinput ulong InpDeviation = 10; /* Slippage (in points) */ // Проскальзывание (в пунктах)
|
|
sinput ulong InpMagic = 123456; /* Magic number */ // Магик
|
|
input int InpStopLoss = -1; /* Stop loss (in points), 0 - none, -1 - half of BB */ // Stop loss (в пунктах), 0 - отсутствует, -1 - половина BB
|
|
input int InpTakeProfit = -1; /* Take profit (in points), 0 - none, -1 - ATR value */ // Take profit (в пунктах), 0 - отсутствует, -1 - значение ATR
|
|
input double InpSLMltp = 2.6; /* Stop loss size multiplier, if SL==-1 */ // Множитель размера Stop loss, если Stop loss==-1
|
|
input double InpTPMltp = 1.3; /* Take profit size multiplier, if TP==-1 */ // Множитель размера Take profit, если Take profit==-1
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Глобальные переменные |
|
|
//+------------------------------------------------------------------+
|
|
CTrade trade; // Объект торгового класса
|
|
int handle_gri; // Хэндл индикатора GRI
|
|
int handle_tema; // Хэндл индикатора TEMA
|
|
int handle_ama; // Хэндл индикатора AMA
|
|
int handle_bb; // Хэндл индикатора BB
|
|
int handle_atr; // Хэндл индикатора ATR
|
|
|
|
double gri[DATA_COUNT]={}; // Массив значений GRI
|
|
double tema[DATA_COUNT]={}; // Массив значений TEMA
|
|
double ama[DATA_COUNT]={}; // Массив значений AMA
|
|
double bb0[DATA_COUNT]={}; // Массив значений BB, буфер 0 (Upper)
|
|
double bb1[DATA_COUNT]={}; // Массив значений BB, буфер 1 (Lower)
|
|
double bb2[DATA_COUNT]={}; // Массив значений BB, буфер 2 (Middle)
|
|
double atr[DATA_COUNT]={}; // Массив значений ATR
|
|
MqlRates prc[DATA_COUNT]={}; // Массив цен и времени
|
|
|
|
//--- GRI
|
|
int period_gri; // Период расчёта GRI
|
|
//--- TEMA
|
|
int period_tema; // Период расчёта TEMA
|
|
//--- AMA
|
|
int period_ama; // Период расчёта AMA
|
|
int fast_ema_ama; // Период быстрого EMA AMA
|
|
int slow_ema_ama; // Период медленного EMA AMA
|
|
//--- BB
|
|
int period_bb; // Период расчёта BB
|
|
double deviation_bb; // Отклонения BB
|
|
int shift_bb; // Сдвиг BB
|
|
//--- ATR
|
|
int period_atr; // Период расчёта ATR
|
|
|
|
//---
|
|
double lot; // Объём позиции
|
|
int prev_total; // Количество позиций на прошлой проверке
|
|
string program_name; // Имя программы
|
|
bool netto; // Признак нетто-счёта
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
//--- Если счёт не с типом хеджинг - ставим флаг и сообщаем о некорректной работе советника
|
|
netto=false;
|
|
if(AccountInfoInteger(ACCOUNT_MARGIN_MODE)!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
|
|
{
|
|
Print("The advisor is designed for use on a hedging account. Correct operation on a netting account is not guaranteed.");
|
|
netto=true;
|
|
}
|
|
|
|
//--- Устанавливаем и корректируем входные переменные индикаторов
|
|
//--- GRI
|
|
period_gri=(InpPeriodGRI<1 ? 5 : InpPeriodGRI);
|
|
//--- TEMA
|
|
period_tema=(InpPeriodTEMA<1 ? 14 : InpPeriodTEMA);
|
|
//--- AMA
|
|
period_ama=(InpPeriodAMA<1 ? 9 : InpPeriodAMA);
|
|
fast_ema_ama=(InpFastEmaAMA<1 ? 2 : InpFastEmaAMA);
|
|
slow_ema_ama=(InpSlowEmaAMA<1 ? 30 : InpSlowEmaAMA);
|
|
//--- BB
|
|
period_bb=(InpPeriodBB<2 ? 20 : InpPeriodBB);
|
|
deviation_bb=InpDeviationBB;
|
|
shift_bb=InpShiftBB;
|
|
//--- ATR
|
|
period_atr=(InpPeriodATR<1 ? 14 : InpPeriodATR);
|
|
|
|
//--- Инициализируем массивы значений индикаторов
|
|
ArrayInitialize(gri,EMPTY_VALUE);
|
|
ArrayInitialize(tema,EMPTY_VALUE);
|
|
ArrayInitialize(bb0,EMPTY_VALUE);
|
|
ArrayInitialize(bb1,EMPTY_VALUE);
|
|
ArrayInitialize(bb2,EMPTY_VALUE);
|
|
ArrayInitialize(atr,EMPTY_VALUE);
|
|
ZeroMemory(prc);
|
|
|
|
//--- Создаём хэндлы индикаторов
|
|
//--- GRI (должен находиться в папке репозитория)
|
|
handle_gri=iCustom(Symbol(),PERIOD_CURRENT,"Shared Projects\\Article-20795-MQL5-GRI-Indicator-Volatility-Math\\GRI",period_gri);
|
|
if(handle_gri==INVALID_HANDLE)
|
|
{
|
|
PrintFormat("%s: Failed to create handle for custom GRI(%d) indicator",__FUNCTION__,period_gri);
|
|
return INIT_FAILED;
|
|
}
|
|
//--- TEMA
|
|
handle_tema=iTEMA(Symbol(),PERIOD_CURRENT,period_tema,InpShiftTEMA,InpPriceTEMA);
|
|
if(handle_tema==INVALID_HANDLE)
|
|
{
|
|
PrintFormat("%s: Failed to create iTEMA(%d) handle",__FUNCTION__,period_tema);
|
|
return INIT_FAILED;
|
|
}
|
|
//--- AMA
|
|
handle_ama=iAMA(Symbol(),PERIOD_CURRENT,period_ama,fast_ema_ama,slow_ema_ama,InpShiftAMA,InpPriceAMA);
|
|
if(handle_ama==INVALID_HANDLE)
|
|
{
|
|
PrintFormat("%s: Failed to create iAMA(%d,%d,%d) handle",__FUNCTION__,period_ama,fast_ema_ama,slow_ema_ama);
|
|
return INIT_FAILED;
|
|
}
|
|
//--- BB
|
|
handle_bb=iBands(Symbol(),PERIOD_CURRENT,period_bb,InpShiftBB,InpDeviationBB,InpPriceBB);
|
|
if(handle_bb==INVALID_HANDLE)
|
|
{
|
|
PrintFormat("%s: Failed to create iBands(%d,%d,%.3f,%s) handle",__FUNCTION__,period_bb,shift_bb,deviation_bb,EnumToString(InpPriceBB));
|
|
return INIT_FAILED;
|
|
}
|
|
//--- ATR
|
|
handle_atr=iATR(Symbol(),PERIOD_CURRENT,period_atr);
|
|
if(handle_atr==INVALID_HANDLE)
|
|
{
|
|
PrintFormat("%s: Failed to create iATR(%d) handle",__FUNCTION__,period_atr);
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
//--- Имя программы и количество позиций на прошлой проверке
|
|
program_name=MQLInfoString(MQL_PROGRAM_NAME);
|
|
prev_total=0;
|
|
|
|
//--- Автоматическая установка типа заполнения
|
|
trade.SetTypeFilling(GetTypeFilling());
|
|
//--- Установка магика
|
|
trade.SetExpertMagicNumber(InpMagic);
|
|
//--- Установка проскальзывания
|
|
trade.SetDeviationInPoints(InpDeviation);
|
|
//--- Установка лота с корректировкой введённого значения
|
|
lot=CorrectLots(InpVolume);
|
|
|
|
//--- Всё успешно
|
|
PrintFormat("%s::%s: Initialization was successful",program_name,__FUNCTION__);
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
//--- Получаем в массивы данные трёх баров индикаторов и цен
|
|
if(!CopyIndicatorsData() || !CopyPricesData())
|
|
return;
|
|
|
|
//--- Заполняем списки тикетов позиций
|
|
int positions_total=PositionsTotal();
|
|
if(prev_total!=positions_total)
|
|
{
|
|
if(!FillingListTickets(Symbol(),InpMagic))
|
|
return;
|
|
prev_total=positions_total;
|
|
}
|
|
|
|
//--- Получаем сигналы от индикаторов
|
|
ENUM_SIGNAL_TYPE signal_tema=SignalTEMA(); // Направление вверх/вниз (SIGNAL_TYPE_LONG/SIGNAL_TYPE_SHORT)
|
|
ENUM_SIGNAL_TYPE signal_ama =SignalAMA(); // Цена выше/ниже (SIGNAL_TYPE_LONG/SIGNAL_TYPE_SHORT)
|
|
|
|
//--- Сигналы должны совпадать у TEMA и AMA (TEMA - направление, AMA - положение цены (выше/ниже))
|
|
ENUM_SIGNAL_TYPE signal=(signal_tema==signal_ama ? signal_tema : SIGNAL_TYPE_NONE);
|
|
|
|
//--- Если включена фильтрация по GRI
|
|
if(InpUseGRI)
|
|
{
|
|
//--- Корректируем сигнал по состоянию GRI
|
|
signal=SignalCorrectionByGRI(signal);
|
|
//--- Корректируем сигнал по порогу волатильности GRI
|
|
if(!IsHighVolatility(InpThresholdGRI) && signal!=SIGNAL_TYPE_CLOSE)
|
|
signal=SIGNAL_TYPE_NONE;
|
|
}
|
|
|
|
//--- Торгуем по сигналам
|
|
TradeProcess(signal);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Получает значения OHLCTV для трёх баров |
|
|
//+------------------------------------------------------------------+
|
|
bool CopyPricesData(void)
|
|
{
|
|
ResetLastError();
|
|
if(CopyRates(Symbol(),PERIOD_CURRENT,0,DATA_COUNT,prc)!=DATA_COUNT)
|
|
{
|
|
PrintFormat("%s: Failed to get price Open data. Error %d",__FUNCTION__,GetLastError());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Получает значения GRI для трёх баров |
|
|
//+------------------------------------------------------------------+
|
|
bool CopyGRIData(void)
|
|
{
|
|
ResetLastError();
|
|
if(CopyBuffer(handle_gri,0,0,DATA_COUNT,gri)!=DATA_COUNT)
|
|
{
|
|
PrintFormat("%s: Failed to get GRI data. Error %d",__FUNCTION__,GetLastError());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Получает значения TEMA для трёх баров |
|
|
//+------------------------------------------------------------------+
|
|
bool CopyTEMAData(void)
|
|
{
|
|
ResetLastError();
|
|
if(CopyBuffer(handle_tema,0,0,DATA_COUNT,tema)!=DATA_COUNT)
|
|
{
|
|
PrintFormat("%s: Failed to get TEMA data. Error %d", __FUNCTION__, GetLastError());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Получает значения TEMA для трёх баров |
|
|
//+------------------------------------------------------------------+
|
|
bool CopyAMAData(void)
|
|
{
|
|
ResetLastError();
|
|
if(CopyBuffer(handle_ama,0,0,DATA_COUNT,ama)!=DATA_COUNT)
|
|
{
|
|
PrintFormat("%s: Failed to get AMA data. Error %d", __FUNCTION__, GetLastError());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Получает значения BB для трёх баров |
|
|
//+------------------------------------------------------------------+
|
|
bool CopyBBData(void)
|
|
{
|
|
ResetLastError();
|
|
if(CopyBuffer(handle_bb,UPPER_BAND,0,DATA_COUNT,bb0)!=DATA_COUNT)
|
|
{
|
|
PrintFormat("%s: Failed to get BB Upper Line data. Error %d",__FUNCTION__,GetLastError());
|
|
return false;
|
|
}
|
|
if(CopyBuffer(handle_bb,LOWER_BAND,0,DATA_COUNT,bb1)!=DATA_COUNT)
|
|
{
|
|
PrintFormat("%s: Failed to get BB Lower Line data. Error %d",__FUNCTION__,GetLastError());
|
|
return false;
|
|
}
|
|
if(CopyBuffer(handle_bb,BASE_LINE,0,DATA_COUNT,bb2)!=DATA_COUNT)
|
|
{
|
|
PrintFormat("%s: Failed to get BB Base Line data. Error %d",__FUNCTION__,GetLastError());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Получает значения ATR для трёх баров |
|
|
//+------------------------------------------------------------------+
|
|
bool CopyATRData(void)
|
|
{
|
|
ResetLastError();
|
|
if(CopyBuffer(handle_atr,0,0,DATA_COUNT,atr)!=DATA_COUNT)
|
|
{
|
|
PrintFormat("%s: Failed to get ATR data. Error %d",__FUNCTION__,GetLastError());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Получает значения индикаторов для трёх баров |
|
|
//+------------------------------------------------------------------+
|
|
bool CopyIndicatorsData(void)
|
|
{
|
|
bool res=CopyTEMAData(); // Результат получения данных TEMA
|
|
res &=CopyAMAData(); // Результат получения данных AMA
|
|
res &=CopyGRIData(); // Результат получения данных GRI
|
|
res &=CopyBBData(); // Результат получения данных BB
|
|
res &=CopyATRData(); // Результат получения данных ATR
|
|
return res;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает из массива цену Open по индексу таймсерии (0 - 2) |
|
|
//+------------------------------------------------------------------+
|
|
double PriceOpen(const int index)
|
|
{
|
|
return(index<0 || index>DATA_COUNT-1 ? 0 : prc[DATA_COUNT-index-1].open);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//|Возвращает из массива цену High по индексу таймсерии (0 - 2)|
|
|
//+------------------------------------------------------------------+
|
|
double PriceHigh(const int index)
|
|
{
|
|
return(index<0 || index>DATA_COUNT-1 ? 0 : prc[DATA_COUNT-index-1].high);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает из массива цену Low по индексу таймсерии (0 - 2) |
|
|
//+------------------------------------------------------------------+
|
|
double PriceLow(const int index)
|
|
{
|
|
return(index<0 || index>DATA_COUNT-1 ? 0 : prc[DATA_COUNT-index-1].low);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает из массива цену Close по индексу таймсерии (0 - 2) |
|
|
//+------------------------------------------------------------------+
|
|
double PriceClose(const int index)
|
|
{
|
|
return(index<0 || index>DATA_COUNT-1 ? 0 : prc[DATA_COUNT-index-1].close);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает из массива время бара по индексу таймсерии (0 - 2) |
|
|
//+------------------------------------------------------------------+
|
|
datetime Time(const int index)
|
|
{
|
|
return(index<0 || index>DATA_COUNT-1 ? 0 : prc[DATA_COUNT-index-1].time);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает из массива данные GRI по индексу таймсерии (0 - 2) |
|
|
//+------------------------------------------------------------------+
|
|
double GRI(const int index)
|
|
{
|
|
return(index<0 || index>DATA_COUNT-1 ? EMPTY_VALUE : gri[DATA_COUNT-index-1]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает из массива данные TEMA по индексу таймсерии (0 - 2) |
|
|
//+------------------------------------------------------------------+
|
|
double TEMA(const int index)
|
|
{
|
|
return(index<0 || index>DATA_COUNT-1 ? EMPTY_VALUE : tema[DATA_COUNT-index-1]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает из массива данные AMA по индексу таймсерии (0 - 2) |
|
|
//+------------------------------------------------------------------+
|
|
double AMA(const int index)
|
|
{
|
|
return(index<0 || index>DATA_COUNT-1 ? EMPTY_VALUE : ama[DATA_COUNT-index-1]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//|Возвращает из массива данные BB Upper по индексу таймсерии (0 - 2)|
|
|
//+------------------------------------------------------------------+
|
|
double BBUpper(const int index)
|
|
{
|
|
return(index<0 || index>DATA_COUNT-1 ? EMPTY_VALUE : bb0[DATA_COUNT-index-1]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//|Возвращает из массива данные BB Lower по индексу таймсерии (0 - 2)|
|
|
//+------------------------------------------------------------------+
|
|
double BBLower(const int index)
|
|
{
|
|
return(index<0 || index>DATA_COUNT-1 ? EMPTY_VALUE : bb1[DATA_COUNT-index-1]);
|
|
}
|
|
//+-------------------------------------------------------------------+
|
|
//|Возвращает из массива данные BB Middle по индексу таймсерии (0 - 2)|
|
|
//+-------------------------------------------------------------------+
|
|
double BBMiddle(const int index)
|
|
{
|
|
return(index<0 || index>DATA_COUNT-1 ? EMPTY_VALUE : bb2[DATA_COUNT-index-1]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает половину ширины BB в пунктах |
|
|
//+------------------------------------------------------------------+
|
|
int HalfSizeBB(const int index)
|
|
{
|
|
double up=BBUpper(index);
|
|
double dn=BBLower(index);
|
|
if(up==EMPTY_VALUE || dn==EMPTY_VALUE)
|
|
return 0;
|
|
return (int)round(((up-dn)*0.5)/Point());
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает из массива данные ATR по индексу таймсерии (0 - 2) |
|
|
//+------------------------------------------------------------------+
|
|
double ATR(const int index)
|
|
{
|
|
return(index<0 || index>DATA_COUNT-1 ? EMPTY_VALUE : atr[DATA_COUNT-index-1]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Состояние GRI |
|
|
//+------------------------------------------------------------------+
|
|
ENUM_GRI_STATE GRIState(void)
|
|
{
|
|
//--- Получаем данные GRI
|
|
double gri0=GRI(0);
|
|
double gri1=GRI(1);
|
|
double gri2=GRI(2);
|
|
//--- Ошибка получения данных - нет сигнала
|
|
if(gri0==EMPTY_VALUE || gri1==EMPTY_VALUE || gri2==EMPTY_VALUE)
|
|
return GRI_STATE_UNKNOWN;
|
|
|
|
//--- Волатильность растёт
|
|
if(gri0>gri1 && gri1>gri2)
|
|
return GRI_STATE_GROWING;
|
|
//--- Волатильность падает
|
|
if(gri0<gri1 && gri1<gri2)
|
|
return GRI_STATE_FALLING;
|
|
//--- Переход к росту (был спад/флэт, теперь рост)
|
|
if(gri0>gri1 && gri1<=gri2)
|
|
return GRI_STATE_TURN_TO_GROW;
|
|
//--- Переход к падению (был рост/флэт, теперь спад)
|
|
if(gri0<gri1 && gri1>=gri2)
|
|
return GRI_STATE_TURN_TO_FALL;
|
|
//--- Флэт
|
|
return GRI_STATE_FLAT;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Корректирует сигнал по состоянию GRI |
|
|
//+------------------------------------------------------------------+
|
|
ENUM_SIGNAL_TYPE SignalCorrectionByGRI(const ENUM_SIGNAL_TYPE signal_type)
|
|
{
|
|
//--- Текущий сигнал
|
|
ENUM_SIGNAL_TYPE signal=signal_type;
|
|
//--- В зависимости от состояния GRI корректируем текущий сигнал
|
|
ENUM_GRI_STATE state=GRIState();
|
|
switch(state)
|
|
{
|
|
//--- Переход к росту волатильности
|
|
case GRI_STATE_TURN_TO_GROW :
|
|
// Здесь сигнал без изменений,
|
|
// но можно как-то ещё обрабатывать начало роста
|
|
break;
|
|
|
|
//--- Волатильность растёт
|
|
case GRI_STATE_GROWING :
|
|
// Здесь сигнал без изменений,
|
|
// но можно как-то ещё обрабатывать постоянный рост
|
|
break;
|
|
|
|
//--- Высокая волатильность
|
|
case GRI_STATE_VOLATILITY_HIGH :
|
|
// Здесь сигнал без изменений,
|
|
// но можно как-то ещё обрабатывать высокую волатильность
|
|
break;
|
|
|
|
//--- Переход к снижению волатильности
|
|
case GRI_STATE_TURN_TO_FALL :
|
|
signal=SIGNAL_TYPE_NONE; // Нет сигнала
|
|
// Здесь просто нет сигнала,
|
|
// но можно как-то иначе обрабатывать это состояние,
|
|
// например, здесь можно закрыть часть позиции или выставить стоп в безубыток,
|
|
// либо сделать и то, и то
|
|
break;
|
|
|
|
//--- Волатильность падает
|
|
case GRI_STATE_FALLING :
|
|
signal=SIGNAL_TYPE_NONE; // Нет сигнала
|
|
// Здесь просто нет сигнала,
|
|
// но можно как-то иначе обрабатывать это состояние,
|
|
// например, включить трейлинг-стоп или закрыть позиции:
|
|
//signal=SIGNAL_TYPE_CLOSE; // Сигнал на закрытие всех позиций (три бара подряд снижается волатильность)
|
|
break;
|
|
|
|
//--- Низкая волатильность
|
|
case GRI_STATE_FLAT :
|
|
signal=SIGNAL_TYPE_NONE; // Нет сигнала
|
|
// Здесь просто нет сигнала,
|
|
// но можно как-то иначе обрабатывать это состояние,
|
|
// например, здесь можно выставлять отложенные ордера
|
|
break;
|
|
|
|
//--- GRI_STATE_UNKNOWN Ошибка получения данных данных
|
|
default:
|
|
signal=SIGNAL_TYPE_NONE; // Нет сигнала
|
|
break;
|
|
}
|
|
return signal;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Превышение порога волатильности GRI |
|
|
//+------------------------------------------------------------------+
|
|
bool IsHighVolatility(double threshold)
|
|
{
|
|
//--- Получаем данные GRI
|
|
double gri0=GRI(0);
|
|
//--- Ошибка получения данных - false
|
|
if(gri0==EMPTY_VALUE)
|
|
return false;
|
|
return(gri0>=threshold);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Сигнал TEMA |
|
|
//+------------------------------------------------------------------+
|
|
ENUM_SIGNAL_TYPE SignalTEMA(void)
|
|
{
|
|
double tema0=TEMA(0);
|
|
double tema1=TEMA(1);
|
|
//--- Ошибка - нет сигнала
|
|
if(tema0==EMPTY_VALUE || tema1==EMPTY_VALUE)
|
|
return SIGNAL_TYPE_NONE;
|
|
|
|
//--- Сигнал на покупку: TEMA растёт
|
|
if(tema0>tema1)
|
|
return SIGNAL_TYPE_LONG;
|
|
|
|
//--- Сигнал на продажу: TEMA падает
|
|
if(tema0<tema1)
|
|
return SIGNAL_TYPE_SHORT;
|
|
|
|
//--- Нет сигнала
|
|
return SIGNAL_TYPE_NONE;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Сигнал AMA |
|
|
//+------------------------------------------------------------------+
|
|
ENUM_SIGNAL_TYPE SignalAMA(void)
|
|
{
|
|
double ama0=AMA(0);
|
|
double ama1=AMA(1);
|
|
double price=PriceClose(0);
|
|
//--- Ошибка - нет сигнала
|
|
if(ama0==EMPTY_VALUE || ama1==EMPTY_VALUE || price==0)
|
|
return SIGNAL_TYPE_NONE;
|
|
|
|
//--- Сигнал на покупку: цена выше AMA, AMA растёт
|
|
if(price>ama0 && ama0>ama1)
|
|
return SIGNAL_TYPE_LONG;
|
|
|
|
//--- Сигнал на продажу: цена ниже AMA, AMA падает
|
|
if(price<ama0 && ama0<ama1)
|
|
return SIGNAL_TYPE_SHORT;
|
|
|
|
//--- Нет сигнала
|
|
return SIGNAL_TYPE_NONE;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает тип исполнения ордера, равный type, |
|
|
//| если он доступен на символе, иначе - корректный вариант |
|
|
//| https://www.mql5.com/ru/forum/170952/page4#comment_4128864 |
|
|
//+------------------------------------------------------------------+
|
|
ENUM_ORDER_TYPE_FILLING GetTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_RETURN)
|
|
{
|
|
const ENUM_SYMBOL_TRADE_EXECUTION exe_mode=(ENUM_SYMBOL_TRADE_EXECUTION)::SymbolInfoInteger(Symbol(),SYMBOL_TRADE_EXEMODE);
|
|
const int filling_mode=(int)::SymbolInfoInteger(Symbol(),SYMBOL_FILLING_MODE);
|
|
|
|
return((filling_mode==0 || (type>=ORDER_FILLING_RETURN) || ((filling_mode &(type+1))!=type+1)) ?
|
|
(((exe_mode==SYMBOL_TRADE_EXECUTION_EXCHANGE) || (exe_mode==SYMBOL_TRADE_EXECUTION_INSTANT)) ?
|
|
ORDER_FILLING_RETURN :((filling_mode==SYMBOL_FILLING_IOC) ? ORDER_FILLING_IOC : ORDER_FILLING_FOK)) : type);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает корректный лот |
|
|
//+------------------------------------------------------------------+
|
|
double CorrectLots(const double lots,const bool to_min_correct=true)
|
|
{
|
|
double min=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
|
|
double max=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
|
|
double step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP);
|
|
return(to_min_correct ? VolumeRoundToSmaller(lots,min,max,step) : VolumeRoundToCorrect(lots,min,max,step));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает ближайший корректный лот |
|
|
//+------------------------------------------------------------------+
|
|
double VolumeRoundToCorrect(const double volume,const double min,const double max,const double step)
|
|
{
|
|
return(step==0 ? min : fmin(fmax(round(volume/step)*step,min),max));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает ближайший в меньшую сторону корректный лот |
|
|
//+------------------------------------------------------------------+
|
|
double VolumeRoundToSmaller(const double volume,const double min,const double max,const double step)
|
|
{
|
|
return(step==0 ? min : fmin(fmax(floor(volume/step)*step,min),max));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает флаг не превышения общего объёма на счёте |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckLotForLimitAccount(const ENUM_POSITION_TYPE position_type,const double volume)
|
|
{
|
|
double lots_limit=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT);
|
|
if(lots_limit==0)
|
|
return true;
|
|
double total_volume=(position_type==POSITION_TYPE_BUY ? Data.Buy.total_volume : Data.Sell.total_volume);
|
|
return(total_volume+volume<=lots_limit);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает корректный StopLoss относительно StopLevel |
|
|
//+------------------------------------------------------------------+
|
|
double CorrectStopLoss(const ENUM_POSITION_TYPE position_type,const int stop_loss)
|
|
{
|
|
if(stop_loss==0)
|
|
return 0;
|
|
double pt=Point();
|
|
double price=(position_type==POSITION_TYPE_BUY ? SymbolInfoDouble(Symbol(),SYMBOL_ASK) : SymbolInfoDouble(Symbol(),SYMBOL_BID));
|
|
int lv=StopLevel(), dg=Digits();
|
|
return(position_type==POSITION_TYPE_BUY ? NormalizeDouble(fmin(price-lv*pt,price-stop_loss*pt),dg) :
|
|
NormalizeDouble(fmax(price+lv*pt,price+stop_loss*pt),dg));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает корректный TakeProfit относительно StopLevel |
|
|
//+------------------------------------------------------------------+
|
|
double CorrectTakeProfit(const ENUM_POSITION_TYPE position_type,const int take_profit)
|
|
{
|
|
if(take_profit==0)
|
|
return 0;
|
|
double pt=Point();
|
|
double price=(position_type==POSITION_TYPE_BUY ? SymbolInfoDouble(Symbol(),SYMBOL_ASK) : SymbolInfoDouble(Symbol(),SYMBOL_BID));
|
|
int lv=StopLevel(), dg=Digits();
|
|
return(position_type==POSITION_TYPE_BUY ? NormalizeDouble(fmax(price+lv*pt,price+take_profit*pt),dg) :
|
|
NormalizeDouble(fmin(price-lv*pt,price-take_profit*pt),dg));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает рассчитанный StopLevel |
|
|
//+------------------------------------------------------------------+
|
|
int StopLevel(void)
|
|
{
|
|
int sp=(int)SymbolInfoInteger(Symbol(),SYMBOL_SPREAD);
|
|
int lv=(int)SymbolInfoInteger(Symbol(),SYMBOL_TRADE_STOPS_LEVEL);
|
|
return(lv==0 ? sp*SPREAD_MLTP : lv);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает "неопределённое" состояние торгового окружения |
|
|
//+------------------------------------------------------------------+
|
|
bool IsUncertainStateEnv(const string symbol_name,const ulong magic_number)
|
|
{
|
|
//--- В тестере сосотояние окружения всегда корректно
|
|
if(MQLInfoInteger(MQL_TESTER))
|
|
return false;
|
|
//--- В цикле по количеству ордеров
|
|
int total=OrdersTotal();
|
|
for(int i=total-1; i>=0; i--)
|
|
{
|
|
//--- выбираем ордер для получения его свойств
|
|
if(OrderGetTicket(i)==0)
|
|
continue;
|
|
//--- если магик ордера не соответствует искомому - пропускаем
|
|
if(OrderGetInteger(ORDER_MAGIC)!=magic_number)
|
|
continue;
|
|
//--- если тип ордера не Buy и не Sell - пропускаем
|
|
ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
|
|
if(type!=ORDER_TYPE_BUY && type!=ORDER_TYPE_SELL)
|
|
continue;
|
|
//--- если символ ордера соответствует искомому, но в ордере нет записи об идентификаторе позиции,
|
|
//--- значит данные об открывающейся позиции ещё не добавлены в историю ордеров.
|
|
//--- Это неопределённое состояние окружения - возвращаем true
|
|
if(!OrderGetInteger(ORDER_POSITION_ID) && OrderGetString(ORDER_SYMBOL)==symbol_name)
|
|
return true;
|
|
}
|
|
//--- Окружение в порядке
|
|
return false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Проверка состояния окружения |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckUncertainStateEnv(const string symbol_name,const ulong magic_number,const int attempts,const int wait)
|
|
{
|
|
//--- Если окружение в порядке - возвращаем true
|
|
if(IsUncertainStateEnv(symbol_name,magic_number))
|
|
return true;
|
|
//--- Делаем цикле ENV_ATTEMPTS попыток получения корректного окружения с ожиданием ENV_WAIT_ATTEMPT между попытками
|
|
int n=0;
|
|
while(!IsStopped() && n<attempts && IsUncertainStateEnv(symbol_name,magic_number))
|
|
{
|
|
n++;
|
|
Sleep(wait);
|
|
}
|
|
//--- Если по завершении ожидания окружение всё ещё неопределённое - сообщаем об этом и возвращаем false
|
|
if(n>=attempts && IsUncertainStateEnv(symbol_name,magic_number))
|
|
{
|
|
PrintFormat("%s: Uncertain state of the environment. Please try again.",__FUNCTION__);
|
|
return false;
|
|
}
|
|
//--- Окружение корректно
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Заполняет массивы тикетов позиций |
|
|
//+------------------------------------------------------------------+
|
|
bool FillingListTickets(const string symbol_name,const ulong magic_number)
|
|
{
|
|
//--- Если торговое окружение не корректно - возвращаем false
|
|
if(!CheckUncertainStateEnv(symbol_name,magic_number,ENV_ATTEMPTS,ENV_WAIT_ATTEMPT))
|
|
return false;
|
|
|
|
//--- Очищаем списки и инициализируем переменные
|
|
Data.Buy.list_tickets.Clear();
|
|
Data.Sell.list_tickets.Clear();
|
|
Data.Buy.total_volume=0;
|
|
Data.Sell.total_volume=0;
|
|
|
|
//--- В цикле по открытым позициям
|
|
int total=PositionsTotal();
|
|
for(int i=total-1; i>WRONG_VALUE; i--)
|
|
{
|
|
//--- выбираем позицию для получения свойств
|
|
ulong ticket=PositionGetTicket(i);
|
|
if(ticket==0)
|
|
continue;
|
|
//--- Если магик или символ не соответствуют переданным в функцию - идём дальше
|
|
if(PositionGetInteger(POSITION_MAGIC)!=InpMagic || PositionGetString(POSITION_SYMBOL)!=symbol_name)
|
|
continue;
|
|
//--- Получаем тип и объём позиции
|
|
ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
double volume=PositionGetDouble(POSITION_VOLUME);
|
|
//--- В зависимости от типа позиции добавляем тикет и объём в соответствующие списки
|
|
if(type==POSITION_TYPE_BUY)
|
|
{
|
|
Data.Buy.list_tickets.Add(ticket);
|
|
Data.Buy.total_volume+=volume;
|
|
}
|
|
//--- POSITION_TYPE_SELL
|
|
else
|
|
{
|
|
Data.Sell.list_tickets.Add(ticket);
|
|
Data.Sell.total_volume+=volume;
|
|
}
|
|
}
|
|
//--- Всё успешно
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает количество позиций Buy |
|
|
//+------------------------------------------------------------------+
|
|
int TotalBuy(void)
|
|
{
|
|
return Data.Buy.list_tickets.Total();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает количество позиций Sell |
|
|
//+------------------------------------------------------------------+
|
|
int TotalSell(void)
|
|
{
|
|
return Data.Sell.list_tickets.Total();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает последний добавленный тикет позиции по типу |
|
|
//+------------------------------------------------------------------+
|
|
ulong LastAddedTicket(const ENUM_POSITION_TYPE type)
|
|
{
|
|
return(type==POSITION_TYPE_BUY ? (TotalBuy()>0 ? Data.Buy.list_tickets.At(0) : 0) : (TotalSell()>0 ? Data.Sell.list_tickets.At(0) : 0));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает номер бара, на котором была открыта позиция |
|
|
//+------------------------------------------------------------------+
|
|
int PositionBar(const ulong ticket)
|
|
{
|
|
//--- Выбираем позицию по тикету
|
|
ResetLastError();
|
|
if(!PositionSelectByTicket(ticket))
|
|
{
|
|
PrintFormat("%s: Failed to select position by ticket #%I64u. Error %d",__FUNCTION__,ticket,GetLastError());
|
|
return -1;
|
|
}
|
|
//--- Получаем время открытия и символ позиции
|
|
datetime time=(datetime)PositionGetInteger(POSITION_TIME);
|
|
string symbol=PositionGetString(POSITION_SYMBOL);
|
|
|
|
//--- Возвращаем номер бара по времени открытия позиции
|
|
return iBarShift(symbol,PERIOD_CURRENT,time);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает наличие указанной позиции, открытой на текущем баре |
|
|
//+------------------------------------------------------------------+
|
|
bool IsPresentPosOnCurrentBar(const ENUM_POSITION_TYPE type)
|
|
{
|
|
ulong ticket=LastAddedTicket(type);
|
|
return(ticket>0 ? PositionBar(ticket)==0 : false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Возвращает цену открытия позиции по тикету |
|
|
//+------------------------------------------------------------------+
|
|
double PositionPriceOpen(const ulong ticket)
|
|
{
|
|
//--- Проверяем тикет
|
|
if(ticket==0)
|
|
return 0;
|
|
//--- Выбираем позицию по тикету
|
|
ResetLastError();
|
|
if(!PositionSelectByTicket(ticket))
|
|
{
|
|
PrintFormat("%s: Failed to select position by ticket #%I64u. Error %d",__FUNCTION__,ticket,GetLastError());
|
|
return 0;
|
|
}
|
|
//--- Возвращаем цену открытия позиции
|
|
return PositionGetDouble(POSITION_PRICE_OPEN);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Закрывает позиции Buy |
|
|
//+------------------------------------------------------------------+
|
|
bool CloseBuy(void)
|
|
{
|
|
int total=TotalBuy();
|
|
bool res=true;
|
|
for(int i=total-1; i>=0; i--)
|
|
{
|
|
ulong ticket=Data.Buy.list_tickets.At(i);
|
|
if(ticket==NULL)
|
|
continue;
|
|
if(!trade.PositionClose(ticket,InpDeviation))
|
|
res=false;
|
|
}
|
|
return res;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Закрывает позиции Sell |
|
|
//+------------------------------------------------------------------+
|
|
bool CloseSell(void)
|
|
{
|
|
int total=TotalSell();
|
|
bool res=true;
|
|
for(int i=total-1; i>=0; i--)
|
|
{
|
|
ulong ticket=Data.Sell.list_tickets.At(i);
|
|
if(ticket==NULL)
|
|
continue;
|
|
if(!trade.PositionClose(ticket,InpDeviation))
|
|
res=false;
|
|
}
|
|
return res;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Открытие позиции |
|
|
//+------------------------------------------------------------------+
|
|
bool OpenPosition(const string symbol_name,const ENUM_POSITION_TYPE type,const double volume,const string comment)
|
|
{
|
|
//--- Рассчитываем значения для стоп-приказов
|
|
int bb=int(HalfSizeBB(0)*InpSLMltp);
|
|
double atrd=ATR(0);
|
|
int atrp=(atrd!=EMPTY_VALUE ? int(round(atrd*InpTPMltp/Point())) : 0);
|
|
double sl=(InpStopLoss==0 ? 0 : (InpStopLoss<0 ? (bb!=0 ? CorrectStopLoss(type,bb) : 0) : CorrectStopLoss(type,InpStopLoss)));
|
|
double tp=(InpTakeProfit==0 ? 0 : (InpStopLoss<0 ? (atrp!=0 ? CorrectTakeProfit(type,atrp) : 0) : CorrectTakeProfit(type,InpTakeProfit)));
|
|
|
|
//--- На неттинговом счёте убираем стоп-приказы
|
|
if(netto)
|
|
sl=tp=0;
|
|
|
|
//--- Получаем цены
|
|
MqlTick tick={};
|
|
if(!SymbolInfoTick(symbol_name,tick))
|
|
{
|
|
PrintFormat("%s: Unable to get prices");
|
|
return false;
|
|
}
|
|
//--- Проверяем и получаем нормализованный лот открываемой позиции
|
|
double ll=trade.CheckVolume(symbol_name,volume,(type==POSITION_TYPE_BUY ? tick.ask : tick.bid),(ENUM_ORDER_TYPE)type);
|
|
if(ll==0)
|
|
{
|
|
PrintFormat("%s: Error. CheckVolume() returned a zero lot",__FUNCTION__);
|
|
return false;
|
|
}
|
|
|
|
//--- Проверяем ограничение на максимальный объём открытых позиций на счёте
|
|
if(!CheckLotForLimitAccount(type,ll))
|
|
{
|
|
PrintFormat("%s: CheckLotForLimitAccount() returned an error",__FUNCTION__);
|
|
return false;
|
|
}
|
|
|
|
//--- Может быть ситуация, когда торговый приказ уже был отправлен, но он ещё не полностью обработан,
|
|
//--- что может привести к задвоению открываемой позиции.
|
|
//--- Если торговое окружение не корректно - возвращаем false
|
|
if(!CheckUncertainStateEnv(symbol_name,InpMagic,ENV_ATTEMPTS,ENV_WAIT_ATTEMPT))
|
|
return false;
|
|
|
|
//--- Ожидание получения корректного торгового окружения может занять некоторое время
|
|
//--- Ещё раз получим цены
|
|
if(!SymbolInfoTick(symbol_name,tick))
|
|
{
|
|
PrintFormat("%s: Unable to get prices");
|
|
return false;
|
|
}
|
|
|
|
//--- Возвращаем результат отправки торгового запроса на сервер
|
|
return(type==POSITION_TYPE_BUY ? trade.Buy(ll,symbol_name,tick.ask,sl,tp,comment) : trade.Sell(ll,symbol_name,tick.bid,sl,tp,comment));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Процесс торговли |
|
|
//+------------------------------------------------------------------+
|
|
void TradeProcess(const ENUM_SIGNAL_TYPE signal)
|
|
{
|
|
//--- Нет сигнала - уходим
|
|
if(signal==SIGNAL_TYPE_NONE)
|
|
return;
|
|
|
|
//--- Сигнал на закрытие
|
|
if(signal==SIGNAL_TYPE_CLOSE)
|
|
{
|
|
CloseBuy();
|
|
CloseSell();
|
|
}
|
|
|
|
//--- Сигнал на покупку
|
|
if(signal==SIGNAL_TYPE_LONG)
|
|
{
|
|
//--- Если нет открытой позиции Buy на этом баре
|
|
if(!IsPresentPosOnCurrentBar(POSITION_TYPE_BUY))
|
|
{
|
|
//--- Получаем цену последней открытой позиции Buy
|
|
double price_last=PositionPriceOpen(LastAddedTicket(POSITION_TYPE_BUY));
|
|
//--- Если это самая первая позиция Buy, либо цена открытия лучше цены открытия прошлой позиции -
|
|
//--- отсылаем запрос на открытие позиции Buy
|
|
if(price_last==0 || price_last>SymbolInfoDouble(Symbol(),SYMBOL_ASK))
|
|
{
|
|
//--- Если позиция открыта - обновляем списки тикетов открытых позиций
|
|
if(OpenPosition(Symbol(),POSITION_TYPE_BUY,lot,""))
|
|
FillingListTickets(Symbol(),InpMagic);
|
|
}
|
|
}
|
|
}
|
|
|
|
//--- Сигнал на продажу
|
|
if(signal==SIGNAL_TYPE_SHORT)
|
|
{
|
|
//--- Если нет открытой позиции Sell на этом баре
|
|
if(!IsPresentPosOnCurrentBar(POSITION_TYPE_SELL))
|
|
{
|
|
//--- Получаем цену последней открытой позиции Sell
|
|
double price_last=PositionPriceOpen(LastAddedTicket(POSITION_TYPE_SELL));
|
|
//--- Если это самая первая позиция Sell, либо цена открытия лучше цены открытия прошлой позиции -
|
|
//--- отсылаем запрос на открытие позиции Sell
|
|
if(price_last<SymbolInfoDouble(Symbol(),SYMBOL_ASK))
|
|
{
|
|
//--- Если позиция открыта - обновляем списки тикетов открытых позиций
|
|
if(OpenPosition(Symbol(),POSITION_TYPE_SELL,lot,""))
|
|
FillingListTickets(Symbol(),InpMagic);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|