//+------------------------------------------------------------------+
//| EA가 추가 작업을 요청하는 MessageBox 창을 표시합니다. |
//| 일련의 수익성 없는 거래가 지정된 수에 도달하면 |
//| 지정된 캔들 수만큼 기다렸다가 MessageBox를 표시합니다. |
//| 작업을 계속해 달라는 요청과 함께. |
//| 확인하려면 여러 포지션을 수동으로 열고 닫아야 합니다. |
//| EA가 통제하지 않기 때문에 손실이 발생합니다. |
//| 단순화를 위해 매직 넘버로 자체 "위치"를 지정합니다. |
//+------------------------------------------------------------------+
//--- 입력 매개변수
input uint InpMaxLossDeals = 3; // 최대 손실 거래
input uint InpInactivityNumBars = 5; // EA 비활성 막대 수
//--- 글로벌 변수
bool ExtFirstStart=true; // 첫 번째 실행 플래그
bool ExtFlag=true; // EA 작동을 허용하는 플래그
uint ExtNumLoss; // 연속적으로 수익성 없는 거래 횟수
datetime ExtTimeLastLoss; // 손실 포지션을 청산하기 위한 마지막 거래 시간
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- 연속으로 손실이 발생한 거래 수와 포지션을 청산하기 위한 마지막 거래 시간을 가져옵니다.
ExtNumLoss=GetNumLosingTradesInRow(ExtTimeLastLoss);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment("");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- 시리즈의 마지막 청산 손실 포지션 이후 얼마나 많은 바가 지나갔는지 확인합니다.
int bars_remaining=iBarShift(Symbol(),PERIOD_CURRENT,ExtTimeLastLoss);
//--- 만약 처음 실행인 경우
if(ExtFirstStart)
{
//--- 일련의 수익성 없는 포지션 이후에 지정된 수의 바가 이미 지나간 경우 EA 작업 플래그를 설정합니다.
if(bars_remaining>(int)InpInactivityNumBars)
ExtFlag=true;
ExtFirstStart=false;
}
//--- EA 작업 플래그가 비활성화된 경우
if(!ExtFlag)
{
Comment(StringFormat("The advisor is stopped for %d bars. Num Loss positions: %u, Time last loss: %s",
(InpInactivityNumBars-bars_remaining),ExtNumLoss,TimeToString(ExtTimeLastLoss,TIME_DATE|TIME_MINUTES|TIME_SECONDS)));
//--- 일련의 수익성 없는 포지션 이후에 지정된 수의 바가 지나간 경우
if(bars_remaining>(int)InpInactivityNumBars)
{
//--- 지정된 텍스트와 창 제목을 사용하여 MessageBox 창을 표시합니다.
//--- 요청 창에는 두 개의 예/아니요 버튼과 물음표가 있는 아이콘이 있습니다.
//--- 기본적으로 예 버튼이 선택되어 있습니다.
string mb_text="The specified number of bars of EA inactivity have passed.\n Continue its work?";
string mb_caption="Please note";
int mb_id=MessageBox(mb_text,mb_caption,MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON1);
//--- MessageBox의 반환 코드가 Yes 버튼을 누른 경우 EA 작업 플래그를 설정합니다.
if(mb_id==IDYES)
{
ExtFlag=true;
return;
}
}
//--- EA 작업 플래그가 비활성화되었습니다. OnTick()을 종료합니다.
return;
}
//--- EA 작업 플래그가 설정되었습니다. EA는 아래 코드에서 제공하는 대로 작동합니다.
Comment(StringFormat("The advisor is working. Num Loss positions: %u, Time last loss: %s, Elapsed Bars: %d",
ExtNumLoss,TimeToString(ExtTimeLastLoss,TIME_DATE|TIME_MINUTES|TIME_SECONDS),bars_remaining));
}
//+------------------------------------------------------------------+
//| TradeTransaction function |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
const MqlTradeRequest& request,
const MqlTradeResult& result)
{
//--- 거래 유형이 내역에 거래를 추가하는 경우
if(trans.type==TRADE_TRANSACTION_DEAL_ADD)
{
//--- 딜 티켓을 받고 티켓별로 목록에서 딜을 선택하세요
ulong deal_ticket=trans.deal;
if(HistoryDealSelect(deal_ticket))
{
//--- 시장 청산 거래인 경우 연속 손실 거래 수와 마지막 거래 시간을 가져옵니다.
ENUM_DEAL_ENTRY entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal_ticket,DEAL_ENTRY);
if(entry==DEAL_ENTRY_OUT || entry==DEAL_ENTRY_INOUT || entry==DEAL_ENTRY_OUT_BY)
ExtNumLoss=GetNumLosingTradesInRow(ExtTimeLastLoss);
}
}
//--- 연속된 손실 거래 수가 지정된 값보다 크고 EA 작업 플래그가 활성화된 경우
if(ExtNumLoss>=InpMaxLossDeals && ExtFlag)
{
//--- 지정된 텍스트와 창 제목이 있는 MessageBox 창 표시
//--- 요청 창에는 두 개의 예/아니요 버튼과 느낌표가 있는 아이콘이 있습니다.
//--- 기본적으로 아니요 버튼이 선택되어 있습니다.
string mb_text="The number of losing trades has reached the specified maximum. The advisor is stopped.\n Continue its work?";
string mb_caption="Attention!";
int mb_id=MessageBox(mb_text,mb_caption,MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2);
//--- MessageBox의 반환 코드가 No 버튼을 누른 경우 EA 작업 플래그를 비활성화합니다.
if(mb_id==IDNO)
ExtFlag=false;
}
}
//+------------------------------------------------------------------+
//| 연속적으로 수익성이 없는 거래 횟수를 반환합니다. |
//| 손실 포지션을 청산하기 위한 마지막 거래 시간 |
//+------------------------------------------------------------------+
uint GetNumLosingTradesInRow(datetime &time_last_deal)
{
//--- 전체 기록을 선택합니다.
if(!HistorySelect(0,TimeCurrent()))
return(0);
//--- 루프의 과거 거래 목록을 통해 다음 거래 티켓을 가져옵니다.
uint res=0;
uint total=HistoryDealsTotal();
for(int i=(int)total-1; i>=0; i--)
{
ulong deal_ticket=HistoryDealGetTicket(i);
if(deal_ticket>0)
{
//--- 거래가 포지션 종료를 위한 것이 아닌 경우 다음 거래로 이동
ENUM_DEAL_ENTRY entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal_ticket,DEAL_ENTRY);
if(entry!=DEAL_ENTRY_OUT && entry!=DEAL_ENTRY_OUT_BY && entry!=DEAL_ENTRY_INOUT)
continue;
//--- 포지션 청산 결과에 이익이 있으면 루프를 중단합니다.
if(!IsClosePositionWithLoss(deal_ticket))
break;
//--- 연속 손실 거래 카운터 증가
res++;
//--- 최대 거래 시간을 변수에 씁니다(마지막 항목 찾기)
datetime deal_time=(datetime)HistoryDealGetInteger(deal_ticket,DEAL_TIME);
if(deal_time>time_last_deal)
time_last_deal=deal_time;
}
}
//--- 연속 손실 횟수를 반환합니다.
return(res);
}
//+------------------------------------------------------------------+
//| 손실이 있는 포지션 청산 플래그 반환 |
//+------------------------------------------------------------------+
bool IsClosePositionWithLoss(const ulong deal_ticket)
{
//--- 거래 이익에 영향을 미치는 속성 값을 가져옵니다.
double profit=HistoryDealGetDouble(deal_ticket,DEAL_PROFIT);
double comission=HistoryDealGetDouble(deal_ticket,DEAL_COMMISSION);
double swap=HistoryDealGetDouble(deal_ticket,DEAL_SWAP);
double fee=HistoryDealGetDouble(deal_ticket,DEAL_FEE);
//--- 수신된 속성의 총 값이 음수임을 나타내는 플래그를 반환합니다.
return(profit+comission+swap+fee<0);
}
|