SimpleCandles/Strategies/SimpleCandlesStrategy.mqh

317 lines
24 KiB
MQL5
Raw Permalink Normal View History

2025-04-11 13:29:20 +03:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| SimpleCandlesStrategy.mqh |
//| Copyright 2025, Yuriy Bykov |
//| https://www.mql5.com/ru/users/antekov |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Yuriy Bykov"
#property link "https://www.mql5.com/ru/articles/17277"
2025-04-11 15:19:04 +03:00
#property version "1.02"
2025-04-11 13:29:20 +03:00
2025-04-24 22:18:33 +03:00
#include "../Include/Adwizard/Virtual/VirtualStrategy.mqh"
2025-04-11 13:29:20 +03:00
/** ?8A0=85 AB@0B5388
E>4=K5 ?0@0<5B@K:
- !8<2>;
- "09<D@59< 4;O ?>4AGQB0 >4=>=0?@02;5==KE A25G59
- >;8G5AB2> >4=>=0?@02;5==KE A25G59 (signalSeqLen)
- 5@8>4 ATR (periodATR)
- Stop Loss (2 ?C=:B0E 8;8 % ATR) (stopLevel)
- Take Profit (2 ?C=:B0E 8;8 % ATR) (takeLevel)
- 0:A8<0;L=>5 :>;8G5AB2> >4=>2@5<5==> >B@KBKE ?>78F89 (maxCountOfOrders)
- 0:A8<0;L=K9 @07<5@ A?@540 (maxSpread)
- 07<5@ ?>78F89
@8 =0ABC?;5=88 =>2>3> 10@0 ?@>25@O5< =0?@02;5=8O ?>A;54=8E 70:@KBKE signalSeqLen A25G59.
A;8 =0?@02;5=8O >48=0:>2K5 8 :>;8G5AB2> >B:@KBKE ?>78F89 <5=LH5 maxCountOfOrders
8 B5:CI89 A?@54 <5=LH5 maxSpread, B>:
- KG8A;O5< StopLoss 8 TakeProfit. A;8 periodATR = 0, B> ?@>AB> >BABC?05< >B B5:CI59
F5=K =0 :>;8G5AB2> ?C=:B>2, 27OBKE 87 ?0@0<5B@>2 stopLevel 8 takeLevel.
A;8 periodATR > 0, B> @0AAG8BK205< 25;8G8=C ATR, 8A?>;L7CO ?0@0<5B@ periodATR
4;O 4=52=>3> B09<D@59<0.
B B5:CI59 F5=K >BABC?05< =0 25;8G8=K ATR * stopLevel 8 ATR * takeLevel.
- B:@K205< ?>78F8N SELL, 5A;8 =0?@02;5=8O A25G59 1K;8 225@E 8
?>78F8N BUY, 5A;8 =0?@02;5=8O A25G59 1K;8 2=87.
@8 >B:@KB88 CAB0=02;8205< @0AAG8B0==K5 @0=55 C@>2=8 StopLoss 8 TakeProfit.
*/
//+------------------------------------------------------------------+
//| ">@3>20O AB@0B538O c 8A?>;L7>20=85< >4=>=0?@02;5==KE A25G59 |
//+------------------------------------------------------------------+
class CSimpleCandlesStrategy : public CVirtualStrategy {
protected:
string m_symbol; // !8<2>; (B>@3>2K9 8=AB@C<5=B)
ENUM_TIMEFRAMES m_timeframe; // 5@8>4 3@0D8:0 (B09<D@59<)
//--- 0@0<5B@K A83=0;0 : >B:@KB8N
int m_signalSeqLen; // >;8G5AB2> >4=>=0?@02;5==KE A25G59
int m_periodATR; // 5@8>4 ATR
//--- 0@0<5B@K ?>78F89
double m_stopLevel; // Stop Loss (2 ?C=:B0E 8;8 % ATR)
double m_takeLevel; // Take Profit (2 ?C=:B0E 8;8 % ATR)
//--- 0@0<5B@K C?@02;5=85 :0?8B0;><
int m_maxCountOfOrders; // 0:A. :>;8G5AB2> >4=>2@5<5==> >B@KBKE ?>78F89
int m_maxSpread; // 0:A. 4>?CAB8<K9 A?@54 (2 ?C=:B0E)
CSymbolInfo *m_symbolInfo; // 1J5:B 4;O ?>;CG5=8O 8=D>@<0F88 > A2>9AB20E A8<2>;0
double m_tp; // Stop Loss 2 ?C=:B0E
double m_sl; // Take Profit 2 ?C=:B0E
//--- 5B>4K
int SignalForOpen(); // !83=0; 4;O >B:@KB8O ?>78F88
void OpenBuy(); // B:@KB85 ?>78F88 BUY
void OpenSell(); // B:@KB85 ?>78F88 SELL
double ChannelWidth(ENUM_TIMEFRAMES p_tf = PERIOD_D1); // 0AGQB 25;8G8=K ATR
void UpdateLevels(); // 1=>2;5=85 C@>2=59 SL 8 TP
// 0:@KBK9 :>=AB@C:B>@
CSimpleCandlesStrategy(string p_params);
public:
// !B0B8G5A:89 :>=AB@C:B>@
STATIC_CONSTRUCTOR(CSimpleCandlesStrategy);
virtual string operator~() override; // @5>1@07>20=85 >1J5:B0 2 AB@>:C
virtual void Tick() override; // 1@01>BG8: A>1KB8O OnTick
};
// 538AB@0F8O :;0AA0-=0A;54=8:0 CFactorable
REGISTER_FACTORABLE_CLASS(CSimpleCandlesStrategy);
//+------------------------------------------------------------------+
//| >=AB@C:B>@ |
//+------------------------------------------------------------------+
CSimpleCandlesStrategy::CSimpleCandlesStrategy(string p_params) {
// '8B05< ?0@0<5B@K 87 AB@>:8 8=8F80;870F88
m_params = p_params;
m_symbol = ReadString(p_params);
m_timeframe = (ENUM_TIMEFRAMES) ReadLong(p_params);
m_signalSeqLen = (int) ReadLong(p_params);
m_periodATR = (int) ReadLong(p_params);
m_stopLevel = ReadDouble(p_params);
m_takeLevel = ReadDouble(p_params);
m_maxCountOfOrders = (int) ReadLong(p_params);
m_maxSpread = (int) ReadLong(p_params);
if(IsValid()) {
// 0?@0H8205< =C6=>5 :>;8G5AB2> >1J5:B>2 4;O 28@BC0;L=KE ?>78F89
CVirtualReceiver::Get(&this, m_orders, m_maxCountOfOrders);
// >102;O5< >BA;56820=85 =>2>3> 10@0 =0 =C6=>< B09<D@59<5
IsNewBar(m_symbol, m_timeframe);
// !>740Q< 8=D>@<0F8>==K9 >1J5:B 4;O =C6=>3> A8<2>;0
m_symbolInfo = CSymbolsMonitor::Instance()[m_symbol];
}
}
//+------------------------------------------------------------------+
//| @5>1@07>20=85 >1J5:B0 2 AB@>:C |
//+------------------------------------------------------------------+
string CSimpleCandlesStrategy::operator~() {
return StringFormat("%s(%s)", typename(this), m_params);
}
//+------------------------------------------------------------------+
//| "Tick" event handler function |
//+------------------------------------------------------------------+
void CSimpleCandlesStrategy::Tick() override {
// A;8 =0ABC?8; =>2K9 10@ ?> 7040==><C A8<2>;C 8 B09<D@59<C
if(IsNewBar(m_symbol, m_timeframe)) {
// A;8 :>;8G5AB2> >B:@KBKE ?>78F89 <5=LH5 4>?CAB8<>3>
if(m_ordersTotal < m_maxCountOfOrders) {
// >;CG05< A83=0; =0 >B:@KB85
int signal = SignalForOpen();
if(signal == 1) { // A;8 A83=0; =0 ?>:C?:C, B>
OpenBuy(); // >B:@K205< ?>78F8N BUY
} else if(signal == -1) { // A;8 A83=0; =0 ?@>406C, B>
OpenSell(); // >B:@K205< ?>78F8N SELL_STOP
}
}
}
}
//+------------------------------------------------------------------+
//| !83=0; 4;O >B:@KB8O >B;>65==KE >@45@>2 |
//+------------------------------------------------------------------+
int CSimpleCandlesStrategy::SignalForOpen() {
// >-C<>;G0=8N A83=0;0 =0 >B:@KB85 =5B
int signal = 0;
MqlRates rates[];
// >?8@C5< 7=0G5=8O :>B8@>2>: (A25G59) 2 <0AA82-?@8Q<=8:.
// ;O ?@>25@:8 A83=0;0 =0< =C6=> m_signalSeqLen 70:@KBKE A25G59 8 B5:CI0O A25G0,
// ?>MB><C 2A53> m_signalSeqLen + 1
int res = CopyRates(m_symbol, m_timeframe, 0, m_signalSeqLen + 1, rates);
// A;8 A:>?8@>20;>AL =C6=>5 :>;8G5AB2> A25G59
if(res == m_signalSeqLen + 1) {
signal = 1; // A83=0; =0 ?>:C?:C
// 5@518@05< 2A5 70:@KBK5 A25G8
for(int i = 1; i <= m_signalSeqLen; i++) {
// A;8 2AB@5G05BAO E>BL >4=0 A25G0 225@E, B> >B<5=O5< A83=0;
if(rates[i].open < rates[i].close ) {
signal = 0;
break;
}
}
if(signal == 0) {
signal = -1; // 8=0G5 - A83=0; =0 ?@>406C
// 5@518@05< 2A5 70:@KBK5 A25G8
for(int i = 1; i <= m_signalSeqLen; i++) {
// A;8 2AB@5G05BAO E>BL >4=0 A25G0 2=87, B> >B<5=O5< A83=0;
if(rates[i].open > rates[i].close ) {
signal = 0;
break;
}
}
}
}
// A;8 A83=0; 5ABL, B>
if(signal != 0) {
// A;8 B5:CI89 A?@54 1>;LH5 <0:A8<0;L=> @07@5HQ==>3>, B>
if(rates[0].spread > m_maxSpread) {
PrintFormat(__FUNCTION__" | IGNORE %s Signal, spread is too big (%d > %d)",
(signal > 0 ? "BUY" : "SELL"),
rates[0].spread, m_maxSpread);
signal = 0; // B<5=O5< A83=0;
}
}
return signal;
}
//+------------------------------------------------------------------+
//| B:@KB85 >@45@0 BUY |
//+------------------------------------------------------------------+
void CSimpleCandlesStrategy::OpenBuy() {
// 5@5< =5>1E>48<CN =0< 8=D>@<0F8N > A8<2>;5 8 F5=0E
double point = m_symbolInfo.Point();
int digits = m_symbolInfo.Digits();
// &5=0 >B:@KB8O
double price = m_symbolInfo.Ask();
// 1=>28< C@>2=8 SL 8 TP, @0AAG8B02 ATR
UpdateLevels();
// #@>2=8 StopLoss 8 TakeProfit
double sl = NormalizeDouble(price - m_sl * point, digits);
double tp = NormalizeDouble(price + m_tp * point, digits);
bool res = false;
for(int i = 0; i < m_maxCountOfOrders; i++) { // 5@518@05< 2A5 28@BC0;L=K5 ?>78F88
if(!m_orders[i].IsOpen()) { // A;8 =0H;8 =5 >B:@KBCN, B> >B:@K205<
// B:@KB85 28@BC0;L=>9 ?>78F88 SELL
res = m_orders[i].Open(m_symbol, ORDER_TYPE_BUY, m_fixedLot,
0,
NormalizeDouble(sl, digits),
NormalizeDouble(tp, digits));
break; // 8 2KE>48<
}
}
if(!res) {
PrintFormat(__FUNCTION__" | ERROR opening BUY virtual order", 0);
}
}
//+------------------------------------------------------------------+
//| B:@KB85 >@45@0 SELL |
//+------------------------------------------------------------------+
void CSimpleCandlesStrategy::OpenSell() {
// 5@5< =5>1E>48<CN =0< 8=D>@<0F8N > A8<2>;5 8 F5=0E
double point = m_symbolInfo.Point();
int digits = m_symbolInfo.Digits();
// &5=0 >B:@KB8O
double price = m_symbolInfo.Bid();
// 1=>28< C@>2=8 SL 8 TP, @0AAG8B02 ATR
UpdateLevels();
// #@>2=8 StopLoss 8 TakeProfit
double sl = NormalizeDouble(price + m_sl * point, digits);
double tp = NormalizeDouble(price - m_tp * point, digits);
bool res = false;
for(int i = 0; i < m_maxCountOfOrders; i++) { // 5@518@05< 2A5 28@BC0;L=K5 ?>78F88
if(!m_orders[i].IsOpen()) { // A;8 =0H;8 =5 >B:@KBCN, B> >B:@K205<
// B:@KB85 28@BC0;L=>9 ?>78F88 SELL
res = m_orders[i].Open(m_symbol, ORDER_TYPE_SELL, m_fixedLot,
0,
NormalizeDouble(sl, digits),
NormalizeDouble(tp, digits));
break; // 8 2KE>48<
}
}
if(!res) {
PrintFormat(__FUNCTION__" | ERROR opening SELL virtual order", 0);
}
}
//+------------------------------------------------------------------+
//| 1=>2;5=85 C@>2=59 SL 8 TP ?> @0AAG8B0==><C ATR |
//+------------------------------------------------------------------+
void CSimpleCandlesStrategy::UpdateLevels() {
// 0AAG8BK205< ATR
double channelWidth = (m_periodATR > 0 ? ChannelWidth() : 1);
// 1=>2;O5< C@>2=8 SL 8 TP
m_sl = m_stopLevel * channelWidth;
m_tp = m_takeLevel * channelWidth;
}
//+------------------------------------------------------------------+
//| 0AGQB 25;8G8=K ATR (=5AB0=40@B=0O @50;870F8O) |
//+------------------------------------------------------------------+
double CSimpleCandlesStrategy::ChannelWidth(ENUM_TIMEFRAMES p_tf = PERIOD_D1) {
int n = m_periodATR; // >;8G5AB2> 10@>2 4;O @0AGQB0
MqlRates rates[]; // 0AA82 4;O :>B8@>2>:
// >?8@C5< :>B8@>2:8 4=52=>3> (?> C<>;G0=8N) B09<D@59<0
int res = CopyRates(m_symbol, p_tf, 1, n, rates);
// A;8 A:>?8@>20;>AL =C6=>5 :>;8G5AB2>
if(res == n) {
double tr[]; // 0AA82 4;O 480?07>=>2 F5=K
ArrayResize(tr, n); // 7<5=O5< 53> @07<5@
double s = 0; // !C<<0 4;O ?>4AGQB0 A@54=53>
FOREACH(rates) {
tr[i] = rates[i].high - rates[i].low; // 0?><8=05< @07<5@ 10@0
}
ArraySort(tr); // !>@B8@C5< @07<5@K
// !C<<8@C5< 2=CB@5==85 425 G5B25@B8 @07<5@>2 10@>2
for(int i = n / 4; i < n * 3 / 4; i++) {
s += tr[i];
}
// >72@0I05< A@54=89 @07<5@ 2 ?C=:B0E
return 2 * s / n / m_symbolInfo.Point();
}
return 0.0;
}
//+------------------------------------------------------------------+