//--- enums
enum ENUM_INTERSECT_DIRECTION
{
INTERSECT_DIRECTION_NONE= 0, // 교차 없음
INTERSECT_DIRECTION_UP = 1, // 상방 교차
INTERSECT_DIRECTION_DOWN=-1, // 하방 교차
};
//--- 입력 매개변수
input uint InpPeriod = 10; // MA 기간
input int InpShift = 0; // MA 쉬프트
input ENUM_MA_METHOD InpMethod = MODE_SMA; // MA 메서드
input ENUM_APPLIED_PRICE InpPrice = PRICE_CLOSE; // MA 적용 가격
//--- 글로벌 변수
int ExtMaHandle;
int ExtMaPeriod;
double ExtData[2];
MqlRates ExtRates[2];
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- 입력 매개변수에 0을 지정하면 이동 평균을 계산하는 기간은 기본값(10)과 같습니다.r
ExtMaPeriod=int(InpPeriod<1 ? 10 : InpPeriod);
//--- 지정된 매개변수를 사용하여 이동 평균 지표에 대한 핸들을 생성합니다.
ExtMaHandle=iMA(Symbol(),PERIOD_CURRENT,ExtMaPeriod,InpShift,InpMethod,InpPrice);
ResetLastError();
if(ExtMaHandle==INVALID_HANDLE)
{
PrintFormat("Failed to create iMA() handle. Error code: %d",GetLastError());
return(INIT_FAILED);
}
//--- 마지막 가격의 업데이트 시간을 가져옵니다.
datetime tick_time=TickTime();
//--- 마지막 두 캔들에서 이동 평균 데이터와 가격 데이터를 가져옵니다.
if(GetData(ExtMaHandle,ExtData,ExtRates) && tick_time!=0)
{
//--- 가격이 MA보다 높은 경우
if(ExtRates[1].close>ExtData[1])
{
//--- 메시지 텍스트 작성 및 경고 표시
string message=StringFormat("Bar time: %s. The price is above the moving average",TimeToString(ExtRates[1].time));
Alert(message+" at "+TimeToString(tick_time,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
/*
Result:
Alert: Bar time: 2024.02.16 18:00. The price is above the moving average at 2024.02.16 18:47:43
*/
}
else
{
//--- 가격이 MA보다 낮은 경우
if(ExtRates[1].close<ExtData[1])
{
//--- 메시지 텍스트 작성 및 경고 표시
string message=StringFormat("Bar time: %s. The price is below the moving average.",TimeToString(ExtRates[1].time));
Alert(message+" at "+TimeToString(tick_time,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
/*
Result:
Alert: Bar time: 2024.02.16 19:00. The price is below the moving average at 2024.02.16 19:33:14
*/
}
else
{
//--- 메시지 텍스트 작성 및 경고 표시
string message=StringFormat("Bar time: %s. The price and moving average are equal.",TimeToString(ExtRates[1].time));
Alert(message+" at "+TimeToString(tick_time,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
/*
Result:
Alert: Bar time: 2024.02.16 20:00. The price and moving average are equal at 2024.02.16 20:12:22
*/
}
}
}
//--- 성공
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
ResetLastError();
//--- 마지막 두 캔들에서 이동 평균 데이터와 가격 데이터를 가져옵니다.
if(!GetData(ExtMaHandle,ExtData,ExtRates))
return;
//--- 현재 캔들의 이동평균을 교차하는 가격의 방향을 구합니다.
ENUM_INTERSECT_DIRECTION intersect=GetIntersectDirection(ExtData,ExtRates);
//--- 이전 메시지를 저장하기 위한 변수
static string message_prev="";
//--- 가격이 현재 캔들의 이동 평균을 상방 교차한 경우
if(intersect==INTERSECT_DIRECTION_UP)
{
//--- 교차가 발생한 틱 시간을 가져옵니다.
datetime tick_time=TickTime();
if(tick_time==0)
return;
//--- 메세지 텍스트를 생성
string message=StringFormat("Bar time: %s. The price crossed the MA from bottom to top",TimeToString(ExtRates[1].time));
//--- 이전 메시지가 현재 메시지와 같지 않으면 메시지 및 틱 시간과 함께 경고를 표시합니다.
if(message!=message_prev)
{
Alert(message+" at "+TimeToString(tick_time,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
message_prev=message;
/*
Result:\
Alert: Bar time: 2024.02.16 09:00. The price crossed the MA from bottom to top at 2024.02.16 09:20:35
*/
}
}
//--- 가격이 현재 바의 이동 평균을 하향 교차한 경우
if(intersect==INTERSECT_DIRECTION_DOWN)
{
//--- 교차가 발생한 틱 시간을 가져옵니다.
datetime tick_time=TickTime();
if(tick_time==0)
return;
//--- 메세지 텍스트를 생성
string message=StringFormat("Bar time: %s. The price crossed the MA from top to bottom",TimeToString(ExtRates[1].time));
//--- 이전 메시지가 현재 메시지와 같지 않으면 메시지 및 틱 시간과 함께 경고를 표시합니다.
if(message!=message_prev)
{
Alert(message+" at "+TimeToString(tick_time,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
message_prev=message;
/*
Result:\
Alert: Bar time: 2024.02.16 10:00. The price crossed the MA from top to bottom at 2024.02.16 10:42:15
*/
}
}
}
//+------------------------------------------------------------------+
//| 가격을 확인하고 평균 데이터를 배열로 이동 |
//+------------------------------------------------------------------+
bool GetData(int handle,double &ma_data[],MqlRates &price_data[])
{
ResetLastError();
//--- 마지막 두 바에서 이동 평균 데이터를 가져옵니다.
if(CopyBuffer(handle,0,0,2,ma_data)!=2)
{
PrintFormat("CopyBuffer() failed. Error code: %d",GetLastError());
return(false);
}
//--- 마지막 두 바의 가격 데이터를 가져옵니다.
if(CopyRates(Symbol(),PERIOD_CURRENT,0,2,price_data)!=2)
{
PrintFormat("CopyRates() failed. Error code: %d",GetLastError());
return(false);
}
return(true);
}
//+------------------------------------------------------------------+
//| 이동평균을 교차하는 가격의 방향을 반환합니다. |
//+------------------------------------------------------------------+
ENUM_INTERSECT_DIRECTION GetIntersectDirection(double &ma_data[],MqlRates &price_data[])
{
double ma0=ma_data[1];
double ma1=ma_data[0];
double close0=price_data[1].close;
double close1=price_data[0].close;
if(close1<=ma1 && close0>ma0)
return(INTERSECT_DIRECTION_UP);
else
{
if(close1>=ma1 && close0<ma0)
return(INTERSECT_DIRECTION_DOWN);
else
return(INTERSECT_DIRECTION_NONE);
}
}
//+------------------------------------------------------------------+
//| 틱 시간을 초 단위로 반환합니다.
//+------------------------------------------------------------------+
datetime TickTime()
{
MqlTick tick={};
ResetLastError();
if(!SymbolInfoTick(Symbol(),tick))
{
PrintFormat("SymbolInfoTick() failed. Error code: %d",GetLastError());
return(0);
}
return(tick.time);
}
|