2025-09-20 02:27:35 -04:00
|
|
|
#include "..\IStrategy.mqh"
|
|
|
|
|
#include "..\KnowledgeBase.mqh"
|
|
|
|
|
#include "..\TradeManager.mqh"
|
|
|
|
|
|
|
|
|
|
class CAroonStrategy : public IStrategy
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
int m_period;
|
|
|
|
|
int m_upThreshold;
|
|
|
|
|
int m_downThreshold;
|
|
|
|
|
|
|
|
|
|
// Cached values
|
|
|
|
|
double m_aroon_up, m_aroon_down;
|
|
|
|
|
double m_close0, m_close1;
|
|
|
|
|
|
|
|
|
|
public:
|
2026-02-05 23:31:20 -05:00
|
|
|
CAroonStrategy(const string symbol, const ENUM_TIMEFRAMES tf) : IStrategy("AroonStrategy", symbol, tf)
|
2025-09-20 02:27:35 -04:00
|
|
|
{
|
|
|
|
|
m_period = 14; m_upThreshold = 70; m_downThreshold = 30;
|
|
|
|
|
m_aroon_up = m_aroon_down = 0;
|
|
|
|
|
m_close0 = m_close1 = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual string Name() { return "AroonStrategy"; }
|
|
|
|
|
|
|
|
|
|
virtual void Refresh()
|
|
|
|
|
{
|
2026-02-05 23:31:20 -05:00
|
|
|
m_close0 = iClose(m_symbol, m_timeframe, 0);
|
|
|
|
|
m_close1 = iClose(m_symbol, m_timeframe, 1);
|
2025-09-20 02:27:35 -04:00
|
|
|
|
|
|
|
|
// Manual Aroon computation: Up = (period - barsSinceHigh)/period * 100
|
|
|
|
|
// Down = (period - barsSinceLow)/period * 100
|
2026-02-05 23:31:20 -05:00
|
|
|
int bars_since_high = iHighest(m_symbol, m_timeframe, MODE_HIGH, m_period, 0);
|
|
|
|
|
int bars_since_low = iLowest(m_symbol, m_timeframe, MODE_LOW, m_period, 0);
|
2025-09-20 02:27:35 -04:00
|
|
|
if(bars_since_high>=0 && bars_since_low>=0)
|
|
|
|
|
{
|
|
|
|
|
m_aroon_up = 100.0 * (m_period - bars_since_high) / m_period;
|
|
|
|
|
m_aroon_down = 100.0 * (m_period - bars_since_low) / m_period;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual TradeOrder CheckSignal()
|
|
|
|
|
{
|
|
|
|
|
TradeOrder ord; ord.strategy_name = Name();
|
|
|
|
|
|
|
|
|
|
// Aroon crossover strategy
|
|
|
|
|
if(m_aroon_up > m_upThreshold && m_aroon_down < m_downThreshold)
|
|
|
|
|
{
|
|
|
|
|
// Strong uptrend
|
|
|
|
|
ord.action = ACTION_BUY;
|
|
|
|
|
ord.order_type = ORDER_TYPE_BUY;
|
|
|
|
|
double atr = GetATR(14, 0);
|
|
|
|
|
double spread = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
|
double min_stop = MathMax(atr * 1.5, MathMax(spread*3, SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point));
|
|
|
|
|
ord.stop_loss = m_close0 - min_stop;
|
|
|
|
|
ord.take_profit = m_close0 + min_stop * 3.0;
|
|
|
|
|
if(spread > min_stop*0.5) return ord; // skip if spread is too high
|
|
|
|
|
return ord;
|
|
|
|
|
}
|
|
|
|
|
else if(m_aroon_down > m_upThreshold && m_aroon_up < m_downThreshold)
|
|
|
|
|
{
|
|
|
|
|
// Strong downtrend
|
|
|
|
|
ord.action = ACTION_SELL;
|
|
|
|
|
ord.order_type = ORDER_TYPE_SELL;
|
|
|
|
|
double atr = GetATR(14, 0);
|
|
|
|
|
double spread = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
|
double min_stop = MathMax(atr * 1.5, MathMax(spread*3, SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point));
|
|
|
|
|
ord.stop_loss = m_close0 + min_stop;
|
|
|
|
|
ord.take_profit = m_close0 - min_stop * 3.0;
|
|
|
|
|
if(spread > min_stop*0.5) return ord; // skip if spread is too high
|
|
|
|
|
return ord;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ord;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void ExportFeatures(CFeaturesKB* kb, const datetime ts)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(kb)==POINTER_INVALID) return;
|
|
|
|
|
(*kb).WriteKV(ts, m_symbol, Name(), "aroon_up", m_aroon_up);
|
|
|
|
|
(*kb).WriteKV(ts, m_symbol, Name(), "aroon_down", m_aroon_down);
|
|
|
|
|
}
|
|
|
|
|
};
|