Update
@@ -1,281 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSIScalping.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>
|
||||
|
||||
//--- Input parameters
|
||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H4; // Timeframe for Analysis
|
||||
input int RSI_Period = 14; // RSI Period
|
||||
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
||||
input double RSI_Overbought = 82; // RSI Overbought Level
|
||||
input double RSI_Oversold = 55; // RSI Oversold Level
|
||||
input double RSI_Target_Buy = 39; // RSI Target for Buy Exit
|
||||
input double RSI_Target_Sell = 35; // RSI Target for Sell Exit
|
||||
input int BarsToWait = 2; // Bars to wait when RSI goes against position
|
||||
input double LotSize = 50; // Lot Size
|
||||
input int MagicNumber = 12345; // Magic Number
|
||||
input int Slippage = 3; // Slippage in points
|
||||
|
||||
//--- Global variables
|
||||
CTrade trade;
|
||||
int rsi_handle;
|
||||
double rsi_buffer[];
|
||||
double rsi_prev, rsi_current, rsi_two_bars_ago;
|
||||
bool position_open = false;
|
||||
int position_ticket = 0;
|
||||
ENUM_POSITION_TYPE current_position_type = POSITION_TYPE_BUY;
|
||||
datetime last_bar_time = 0;
|
||||
bool rsi_against_position = false;
|
||||
int bars_against_count = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Initialize RSI indicator
|
||||
rsi_handle = iRSI(_Symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
|
||||
if(rsi_handle == INVALID_HANDLE)
|
||||
{
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
// Initialize trade object
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
trade.SetDeviationInPoints(Slippage);
|
||||
trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
|
||||
// Allocate arrays
|
||||
ArraySetAsSeries(rsi_buffer, true);
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(rsi_handle != INVALID_HANDLE)
|
||||
IndicatorRelease(rsi_handle);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Check if we have enough bars
|
||||
if(Bars(_Symbol, TimeFrame) < RSI_Period + 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a new bar
|
||||
datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
|
||||
if(current_bar_time == last_bar_time)
|
||||
{
|
||||
return; // Still the same bar, don't process
|
||||
}
|
||||
|
||||
last_bar_time = current_bar_time;
|
||||
|
||||
// Update RSI values
|
||||
if(!UpdateRSI())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for existing position
|
||||
CheckExistingPosition();
|
||||
|
||||
// Check for new entry signals
|
||||
if(!position_open)
|
||||
{
|
||||
CheckEntrySignals();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update RSI values |
|
||||
//+------------------------------------------------------------------+
|
||||
bool UpdateRSI()
|
||||
{
|
||||
if(CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer) < 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rsi_current = rsi_buffer[0]; // Current bar
|
||||
rsi_prev = rsi_buffer[1]; // Previous bar
|
||||
rsi_two_bars_ago = rsi_buffer[2]; // Two bars ago
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check existing position for exit conditions |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckExistingPosition()
|
||||
{
|
||||
if(!position_open)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if position still exists
|
||||
if(!PositionSelectByTicket(position_ticket))
|
||||
{
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Exit conditions based on RSI target
|
||||
if(current_position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
// Check if RSI is against the position (below oversold)
|
||||
if(rsi_current < RSI_Oversold)
|
||||
{
|
||||
if(!rsi_against_position)
|
||||
{
|
||||
rsi_against_position = true;
|
||||
bars_against_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bars_against_count++;
|
||||
}
|
||||
|
||||
// Close position if RSI has been against for Y bars
|
||||
if(bars_against_count >= BarsToWait)
|
||||
{
|
||||
ClosePosition();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RSI is no longer against the position, reset counter
|
||||
if(rsi_against_position)
|
||||
{
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
|
||||
// Exit long position when RSI reaches buy target
|
||||
if(rsi_current >= RSI_Target_Buy)
|
||||
{
|
||||
ClosePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(current_position_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
// Check if RSI is against the position (above overbought)
|
||||
if(rsi_current > RSI_Overbought)
|
||||
{
|
||||
if(!rsi_against_position)
|
||||
{
|
||||
rsi_against_position = true;
|
||||
bars_against_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bars_against_count++;
|
||||
}
|
||||
|
||||
// Close position if RSI has been against for Y bars
|
||||
if(bars_against_count >= BarsToWait)
|
||||
{
|
||||
ClosePosition();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RSI is no longer against the position, reset counter
|
||||
if(rsi_against_position)
|
||||
{
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
|
||||
// Exit short position when RSI reaches sell target
|
||||
if(rsi_current <= RSI_Target_Sell)
|
||||
{
|
||||
ClosePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for entry signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckEntrySignals()
|
||||
{
|
||||
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
|
||||
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold)
|
||||
{
|
||||
OpenBuyPosition();
|
||||
}
|
||||
|
||||
// Sell signal: RSI crosses from overbought to below overbought (checking the actual crossover)
|
||||
if(rsi_two_bars_ago >= RSI_Overbought && rsi_prev < RSI_Overbought)
|
||||
{
|
||||
OpenSellPosition();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open buy position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenBuyPosition()
|
||||
{
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
if(trade.Buy(LotSize, _Symbol, ask, 0, 0, "RSI Scalping Buy"))
|
||||
{
|
||||
position_ticket = trade.ResultOrder();
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_BUY;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open sell position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenSellPosition()
|
||||
{
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
|
||||
if(trade.Sell(LotSize, _Symbol, bid, 0, 0, "RSI Scalping Sell"))
|
||||
{
|
||||
position_ticket = trade.ResultOrder();
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_SELL;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close current position |
|
||||
//+------------------------------------------------------------------+
|
||||
void ClosePosition()
|
||||
{
|
||||
if(trade.PositionClose(position_ticket))
|
||||
{
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
}
|
||||
@@ -1,363 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSIScalping.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>
|
||||
|
||||
//--- Input parameters
|
||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_M30; // Timeframe for Analysis
|
||||
input int RSI_Period = 14; // RSI Period
|
||||
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
||||
input double RSI_Overbought = 77; // RSI Overbought Level
|
||||
input double RSI_Oversold = 10; // RSI Oversold Level
|
||||
input double RSI_Target_Buy = 27; // RSI Target for Buy Exit
|
||||
input double RSI_Target_Sell = 43; // RSI Target for Sell Exit
|
||||
input int BarsToWait = 14; // Bars to wait when RSI goes against position
|
||||
input double LotSize = 0.1; // Lot Size
|
||||
input int MagicNumber = 12345; // Magic Number
|
||||
input int Slippage = 3; // Slippage in points
|
||||
|
||||
//--- Global variables
|
||||
CTrade trade;
|
||||
int rsi_handle;
|
||||
double rsi_buffer[];
|
||||
double rsi_prev, rsi_current, rsi_two_bars_ago;
|
||||
bool position_open = false;
|
||||
int position_ticket = 0;
|
||||
ENUM_POSITION_TYPE current_position_type = POSITION_TYPE_BUY;
|
||||
datetime last_bar_time = 0;
|
||||
bool rsi_against_position = false;
|
||||
int bars_against_count = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Initialize RSI indicator
|
||||
rsi_handle = iRSI(_Symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
|
||||
if(rsi_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("Error creating RSI indicator");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
// Initialize trade object
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
trade.SetDeviationInPoints(Slippage);
|
||||
trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
|
||||
// Allocate arrays
|
||||
ArraySetAsSeries(rsi_buffer, true);
|
||||
|
||||
Print("RSI Scalping EA initialized successfully on timeframe: ", EnumToString(TimeFrame));
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(rsi_handle != INVALID_HANDLE)
|
||||
IndicatorRelease(rsi_handle);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Check if we have enough bars
|
||||
if(Bars(_Symbol, TimeFrame) < RSI_Period + 2)
|
||||
{
|
||||
Print("TRACE: Not enough bars. Bars=", Bars(_Symbol, TimeFrame), " RSI_Period+2=", RSI_Period+2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a new bar
|
||||
datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
|
||||
if(current_bar_time == last_bar_time)
|
||||
{
|
||||
Print("TRACE: Same bar, skipping. current_bar_time=", current_bar_time, " last_bar_time=", last_bar_time);
|
||||
return; // Still the same bar, don't process
|
||||
}
|
||||
|
||||
Print("TRACE: New bar detected. current_bar_time=", current_bar_time, " last_bar_time=", last_bar_time);
|
||||
last_bar_time = current_bar_time;
|
||||
|
||||
// Update RSI values
|
||||
if(!UpdateRSI())
|
||||
{
|
||||
Print("TRACE: Failed to update RSI values");
|
||||
return;
|
||||
}
|
||||
|
||||
Print("TRACE: RSI values - Current=", rsi_current, " Previous=", rsi_prev);
|
||||
|
||||
// Check for existing position
|
||||
CheckExistingPosition();
|
||||
|
||||
// Check for new entry signals
|
||||
if(!position_open)
|
||||
{
|
||||
Print("TRACE: No position open, checking entry signals");
|
||||
CheckEntrySignals();
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Position already open, skipping entry signals");
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update RSI values |
|
||||
//+------------------------------------------------------------------+
|
||||
bool UpdateRSI()
|
||||
{
|
||||
Print("TRACE: Updating RSI values...");
|
||||
|
||||
if(CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer) < 3)
|
||||
{
|
||||
Print("TRACE: Error copying RSI data. Copied=", CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer));
|
||||
return false;
|
||||
}
|
||||
|
||||
rsi_current = rsi_buffer[0]; // Current bar
|
||||
rsi_prev = rsi_buffer[1]; // Previous bar
|
||||
rsi_two_bars_ago = rsi_buffer[2]; // Two bars ago
|
||||
|
||||
Print("TRACE: RSI buffer values - [0]=", rsi_buffer[0], " [1]=", rsi_buffer[1], " [2]=", rsi_buffer[2]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check existing position for exit conditions |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckExistingPosition()
|
||||
{
|
||||
if(!position_open)
|
||||
{
|
||||
Print("TRACE: No position open, skipping position check");
|
||||
return;
|
||||
}
|
||||
|
||||
Print("TRACE: Checking existing position. Ticket=", position_ticket, " Type=", (current_position_type == POSITION_TYPE_BUY ? "BUY" : "SELL"));
|
||||
|
||||
// Check if position still exists
|
||||
if(!PositionSelectByTicket(position_ticket))
|
||||
{
|
||||
Print("TRACE: Position no longer exists, resetting state");
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Exit conditions based on RSI target
|
||||
if(current_position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
Print("TRACE: Checking BUY position exit - rsi_current=", rsi_current, " RSI_Target_Buy=", RSI_Target_Buy, " RSI_Oversold=", RSI_Oversold);
|
||||
|
||||
// Check if RSI is against the position (below oversold)
|
||||
if(rsi_current < RSI_Oversold)
|
||||
{
|
||||
if(!rsi_against_position)
|
||||
{
|
||||
Print("TRACE: RSI went against BUY position (below oversold), starting counter");
|
||||
rsi_against_position = true;
|
||||
bars_against_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bars_against_count++;
|
||||
Print("TRACE: RSI still against BUY position. Bars against: ", bars_against_count, "/", BarsToWait);
|
||||
}
|
||||
|
||||
// Close position if RSI has been against for Y bars
|
||||
if(bars_against_count >= BarsToWait)
|
||||
{
|
||||
Print("TRACE: RSI against BUY position for ", BarsToWait, " bars, closing position!");
|
||||
ClosePosition();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RSI is no longer against the position, reset counter
|
||||
if(rsi_against_position)
|
||||
{
|
||||
Print("TRACE: RSI no longer against BUY position, resetting counter");
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
|
||||
// Exit long position when RSI reaches buy target
|
||||
if(rsi_current >= RSI_Target_Buy)
|
||||
{
|
||||
Print("TRACE: BUY position target reached!");
|
||||
ClosePosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: BUY position exit condition not met");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(current_position_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
Print("TRACE: Checking SELL position exit - rsi_current=", rsi_current, " RSI_Target_Sell=", RSI_Target_Sell, " RSI_Overbought=", RSI_Overbought);
|
||||
|
||||
// Check if RSI is against the position (above overbought)
|
||||
if(rsi_current > RSI_Overbought)
|
||||
{
|
||||
if(!rsi_against_position)
|
||||
{
|
||||
Print("TRACE: RSI went against SELL position (above overbought), starting counter");
|
||||
rsi_against_position = true;
|
||||
bars_against_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bars_against_count++;
|
||||
Print("TRACE: RSI still against SELL position. Bars against: ", bars_against_count, "/", BarsToWait);
|
||||
}
|
||||
|
||||
// Close position if RSI has been against for Y bars
|
||||
if(bars_against_count >= BarsToWait)
|
||||
{
|
||||
Print("TRACE: RSI against SELL position for ", BarsToWait, " bars, closing position!");
|
||||
ClosePosition();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RSI is no longer against the position, reset counter
|
||||
if(rsi_against_position)
|
||||
{
|
||||
Print("TRACE: RSI no longer against SELL position, resetting counter");
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
|
||||
// Exit short position when RSI reaches sell target
|
||||
if(rsi_current <= RSI_Target_Sell)
|
||||
{
|
||||
Print("TRACE: SELL position target reached!");
|
||||
ClosePosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: SELL position exit condition not met");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for entry signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckEntrySignals()
|
||||
{
|
||||
Print("TRACE: Checking entry signals...");
|
||||
Print("TRACE: Buy condition - rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold");
|
||||
Print("TRACE: Buy condition values - rsi_two_bars_ago=", rsi_two_bars_ago, " <= ", RSI_Oversold, " && rsi_prev=", rsi_prev, " > ", RSI_Oversold);
|
||||
|
||||
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
|
||||
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold)
|
||||
{
|
||||
Print("TRACE: Buy signal detected!");
|
||||
OpenBuyPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Buy signal condition not met");
|
||||
}
|
||||
|
||||
Print("TRACE: Sell condition - rsi_two_bars_ago >= RSI_Overbought && rsi_prev < RSI_Overbought");
|
||||
Print("TRACE: Sell condition values - rsi_two_bars_ago=", rsi_two_bars_ago, " >= ", RSI_Overbought, " && rsi_prev=", rsi_prev, " < ", RSI_Overbought);
|
||||
|
||||
// Sell signal: RSI crosses from overbought to below overbought (checking the actual crossover)
|
||||
if(rsi_two_bars_ago >= RSI_Overbought && rsi_prev < RSI_Overbought)
|
||||
{
|
||||
Print("TRACE: Sell signal detected!");
|
||||
OpenSellPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Sell signal condition not met");
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open buy position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenBuyPosition()
|
||||
{
|
||||
Print("TRACE: Attempting to open buy position...");
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
Print("TRACE: Current ask price=", ask, " LotSize=", LotSize);
|
||||
|
||||
if(trade.Buy(LotSize, _Symbol, ask, 0, 0, "RSI Scalping Buy"))
|
||||
{
|
||||
position_ticket = trade.ResultOrder();
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_BUY;
|
||||
Print("TRACE: Buy position opened successfully! Ticket=", position_ticket, " Price=", ask);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Error opening buy position. Retcode=", trade.ResultRetcode(), " Description=", trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open sell position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenSellPosition()
|
||||
{
|
||||
Print("TRACE: Attempting to open sell position...");
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
Print("TRACE: Current bid price=", bid, " LotSize=", LotSize);
|
||||
|
||||
if(trade.Sell(LotSize, _Symbol, bid, 0, 0, "RSI Scalping Sell"))
|
||||
{
|
||||
position_ticket = trade.ResultOrder();
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_SELL;
|
||||
Print("TRACE: Sell position opened successfully! Ticket=", position_ticket, " Price=", bid);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Error opening sell position. Retcode=", trade.ResultRetcode(), " Description=", trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close current position |
|
||||
//+------------------------------------------------------------------+
|
||||
void ClosePosition()
|
||||
{
|
||||
Print("TRACE: Attempting to close position. Ticket=", position_ticket);
|
||||
|
||||
if(trade.PositionClose(position_ticket))
|
||||
{
|
||||
Print("TRACE: Position closed successfully! Ticket=", position_ticket);
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Error closing position. Retcode=", trade.ResultRetcode(), " Description=", trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 242 KiB After Width: | Height: | Size: 242 KiB |
|
Before Width: | Height: | Size: 241 KiB After Width: | Height: | Size: 241 KiB |
|
Before Width: | Height: | Size: 250 KiB After Width: | Height: | Size: 250 KiB |
|
Before Width: | Height: | Size: 238 KiB After Width: | Height: | Size: 238 KiB |
@@ -0,0 +1,632 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| tick-momentum-catcher.mq5 |
|
||||
//| Tick-Level Momentum Catcher for BTCUSD |
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Tick Momentum Catcher"
|
||||
#property link ""
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
CTrade trade;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Input Parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== General Settings ==="
|
||||
input string TradingSymbol = "BTCUSD"; // Trading Symbol
|
||||
input double LotSize = 0.01; // Lot Size
|
||||
input int MagicNumber = 88010; // Magic Number
|
||||
input int Slippage = 3; // Slippage
|
||||
|
||||
input group "=== Tick Momentum Settings ==="
|
||||
input int TickWindow = 20; // Tick Window for Momentum Calculation
|
||||
input double MinTickMomentum = 0.05; // Min Tick Momentum % (0.05 = 0.05%)
|
||||
input double VolumeSpikeMultiplier = 2.0; // Volume Spike Multiplier
|
||||
input int MinTicksForSignal = 3; // Min Consecutive Ticks for Signal
|
||||
input double OrderFlowImbalance = 1.5; // Order Flow Imbalance Ratio (1.5 = 50% more)
|
||||
|
||||
input group "=== Entry Settings ==="
|
||||
input double EntryMomentumThreshold = 0.1; // Entry Momentum Threshold %
|
||||
input int MaxBarsHold = 5; // Max Bars to Hold Position (1 minute bars)
|
||||
input double MaxLossPercent = 0.3; // Max Loss % to Force Close
|
||||
input double TakeProfitPercent = 0.15; // Take Profit % (0.15 = 0.15%)
|
||||
input double StopLossPercent = 0.1; // Stop Loss % (0.1 = 0.1%)
|
||||
|
||||
input group "=== Self-Optimization ==="
|
||||
input bool EnableAutoOptimization = true; // Enable Auto Optimization
|
||||
input int OptimizationIntervalBars = 100; // Bars Between Optimizations
|
||||
input int OptimizationPeriodMinutes = 30; // Backtesting Period (Minutes)
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Tick Data Structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct TickData
|
||||
{
|
||||
double price;
|
||||
ulong volume; // Match MqlTick.volume type (ulong)
|
||||
datetime time;
|
||||
bool is_buy; // true if price moved up, false if down
|
||||
double momentum; // Price change percentage
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables |
|
||||
//+------------------------------------------------------------------+
|
||||
TickData tick_buffer[];
|
||||
int tick_buffer_size = 1000;
|
||||
int last_optimization_bar = 0;
|
||||
int bars_since_optimization = 0;
|
||||
|
||||
// Current momentum tracking
|
||||
double current_momentum = 0.0;
|
||||
int consecutive_buy_ticks = 0;
|
||||
int consecutive_sell_ticks = 0;
|
||||
double buy_volume_sum = 0.0;
|
||||
double sell_volume_sum = 0.0;
|
||||
|
||||
// Position tracking
|
||||
datetime position_entry_time = 0;
|
||||
double position_entry_price = 0.0;
|
||||
ENUM_POSITION_TYPE position_type = WRONG_VALUE;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
ArrayResize(tick_buffer, tick_buffer_size);
|
||||
ArraySetAsSeries(tick_buffer, false); // New ticks at end
|
||||
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
trade.SetDeviationInPoints(Slippage);
|
||||
trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
|
||||
Print("=== Tick Momentum Catcher Initialized ===");
|
||||
Print("Symbol: ", TradingSymbol);
|
||||
Print("Tick Window: ", TickWindow);
|
||||
Print("Min Momentum: ", MinTickMomentum, "%");
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
ArrayFree(tick_buffer);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Get current tick
|
||||
MqlTick tick;
|
||||
if(!SymbolInfoTick(TradingSymbol, tick))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Process tick
|
||||
ProcessTick(tick);
|
||||
|
||||
// Check for optimization
|
||||
if(EnableAutoOptimization)
|
||||
{
|
||||
int current_bar = iBars(TradingSymbol, PERIOD_M1);
|
||||
if(current_bar > last_optimization_bar)
|
||||
{
|
||||
bars_since_optimization++;
|
||||
if(bars_since_optimization >= OptimizationIntervalBars)
|
||||
{
|
||||
OptimizeParameters();
|
||||
bars_since_optimization = 0;
|
||||
}
|
||||
}
|
||||
last_optimization_bar = current_bar;
|
||||
}
|
||||
|
||||
// Check existing position
|
||||
CheckPosition();
|
||||
|
||||
// Look for entry signals
|
||||
if(!PositionSelect(TradingSymbol))
|
||||
{
|
||||
CheckEntrySignals();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Process Tick Data |
|
||||
//+------------------------------------------------------------------+
|
||||
void ProcessTick(MqlTick &tick)
|
||||
{
|
||||
static double last_price = 0.0;
|
||||
static datetime last_time = 0;
|
||||
|
||||
if(last_price == 0.0)
|
||||
{
|
||||
last_price = tick.last;
|
||||
last_time = tick.time;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate momentum
|
||||
double price_change = tick.last - last_price;
|
||||
double momentum_pct = 0.0;
|
||||
if(last_price > 0)
|
||||
{
|
||||
momentum_pct = (price_change / last_price) * 100.0;
|
||||
}
|
||||
|
||||
// Determine if buy or sell tick
|
||||
bool is_buy = (price_change > 0);
|
||||
|
||||
// Add to buffer (circular buffer)
|
||||
static int tick_index = 0;
|
||||
tick_buffer[tick_index].price = tick.last;
|
||||
tick_buffer[tick_index].volume = tick.volume; // ulong type matches
|
||||
tick_buffer[tick_index].time = tick.time;
|
||||
tick_buffer[tick_index].is_buy = is_buy;
|
||||
tick_buffer[tick_index].momentum = momentum_pct;
|
||||
|
||||
tick_index++;
|
||||
if(tick_index >= tick_buffer_size) tick_index = 0;
|
||||
|
||||
// Update momentum tracking
|
||||
UpdateMomentumTracking(is_buy, momentum_pct, (double)(long)tick.volume); // Convert ulong to double via long
|
||||
|
||||
last_price = tick.last;
|
||||
last_time = tick.time;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update Momentum Tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdateMomentumTracking(bool is_buy, double momentum, double volume)
|
||||
{
|
||||
// Track consecutive ticks
|
||||
if(is_buy)
|
||||
{
|
||||
consecutive_buy_ticks++;
|
||||
consecutive_sell_ticks = 0;
|
||||
buy_volume_sum += volume;
|
||||
}
|
||||
else
|
||||
{
|
||||
consecutive_sell_ticks++;
|
||||
consecutive_buy_ticks = 0;
|
||||
sell_volume_sum += volume;
|
||||
}
|
||||
|
||||
// Calculate current momentum from recent ticks
|
||||
int recent_ticks = MathMin(TickWindow, tick_buffer_size);
|
||||
double momentum_sum = 0.0;
|
||||
int count = 0;
|
||||
|
||||
for(int i = tick_buffer_size - 1; i >= 0 && count < recent_ticks; i--)
|
||||
{
|
||||
if(tick_buffer[i].price > 0)
|
||||
{
|
||||
momentum_sum += MathAbs(tick_buffer[i].momentum);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if(count > 0)
|
||||
{
|
||||
current_momentum = momentum_sum / count;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check Entry Signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckEntrySignals()
|
||||
{
|
||||
// Get current price
|
||||
double ask = SymbolInfoDouble(TradingSymbol, SYMBOL_ASK);
|
||||
double bid = SymbolInfoDouble(TradingSymbol, SYMBOL_BID);
|
||||
|
||||
// Calculate order flow imbalance
|
||||
double order_flow_ratio = 0.0;
|
||||
if(sell_volume_sum > 0)
|
||||
{
|
||||
order_flow_ratio = buy_volume_sum / sell_volume_sum;
|
||||
}
|
||||
else if(buy_volume_sum > 0)
|
||||
{
|
||||
order_flow_ratio = 999.0; // All buy volume
|
||||
}
|
||||
|
||||
// Check for violent momentum (use optimized value if available)
|
||||
double momentum_threshold = (optimized_min_tick_momentum > 0.0) ? optimized_min_tick_momentum : MinTickMomentum;
|
||||
bool violent_momentum = (current_momentum >= momentum_threshold);
|
||||
|
||||
// Check for consecutive ticks in same direction
|
||||
bool strong_buy_signal = (consecutive_buy_ticks >= MinTicksForSignal);
|
||||
bool strong_sell_signal = (consecutive_sell_ticks >= MinTicksForSignal);
|
||||
|
||||
// Check order flow imbalance (use optimized value if available)
|
||||
double imbalance_threshold = (optimized_order_flow_imbalance > 0.0) ? optimized_order_flow_imbalance : OrderFlowImbalance;
|
||||
bool buy_imbalance = (order_flow_ratio >= imbalance_threshold);
|
||||
bool sell_imbalance = (order_flow_ratio <= (1.0 / imbalance_threshold));
|
||||
|
||||
// Entry conditions
|
||||
// BUY: Violent momentum + consecutive buy ticks + buy volume dominance
|
||||
if(violent_momentum && strong_buy_signal && buy_imbalance)
|
||||
{
|
||||
double entry_momentum = CalculateEntryMomentum(true);
|
||||
if(entry_momentum >= EntryMomentumThreshold)
|
||||
{
|
||||
OpenBuyPosition(ask);
|
||||
}
|
||||
}
|
||||
// SELL: Violent momentum + consecutive sell ticks + sell volume dominance
|
||||
else if(violent_momentum && strong_sell_signal && sell_imbalance)
|
||||
{
|
||||
double entry_momentum = CalculateEntryMomentum(false);
|
||||
if(entry_momentum >= EntryMomentumThreshold)
|
||||
{
|
||||
OpenSellPosition(bid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate Entry Momentum |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateEntryMomentum(bool is_buy)
|
||||
{
|
||||
double momentum_sum = 0.0;
|
||||
int count = 0;
|
||||
int lookback = MathMin(MinTicksForSignal, tick_buffer_size);
|
||||
|
||||
for(int i = tick_buffer_size - 1; i >= 0 && count < lookback; i--)
|
||||
{
|
||||
if(tick_buffer[i].price > 0)
|
||||
{
|
||||
if(is_buy && tick_buffer[i].is_buy)
|
||||
{
|
||||
momentum_sum += tick_buffer[i].momentum;
|
||||
count++;
|
||||
}
|
||||
else if(!is_buy && !tick_buffer[i].is_buy)
|
||||
{
|
||||
momentum_sum += MathAbs(tick_buffer[i].momentum);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(count > 0)
|
||||
{
|
||||
return MathAbs(momentum_sum / count);
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Normalize Stops According to Broker Requirements |
|
||||
//+------------------------------------------------------------------+
|
||||
void NormalizeStops(double price, double &sl, double &tp, bool is_buy)
|
||||
{
|
||||
double point = SymbolInfoDouble(TradingSymbol, SYMBOL_POINT);
|
||||
int digits = (int)SymbolInfoInteger(TradingSymbol, SYMBOL_DIGITS);
|
||||
int stops_level = (int)SymbolInfoInteger(TradingSymbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
|
||||
// Calculate minimum distance in points
|
||||
double min_stop_distance = stops_level * point;
|
||||
if(min_stop_distance == 0) min_stop_distance = point * 10; // Default to 10 points if not specified
|
||||
|
||||
// Normalize to required digits
|
||||
sl = NormalizeDouble(sl, digits);
|
||||
tp = NormalizeDouble(tp, digits);
|
||||
|
||||
// Ensure stops meet minimum distance requirement
|
||||
if(is_buy)
|
||||
{
|
||||
// For buy: SL below price, TP above price
|
||||
double sl_distance = price - sl;
|
||||
double tp_distance = tp - price;
|
||||
|
||||
if(sl_distance < min_stop_distance)
|
||||
{
|
||||
sl = NormalizeDouble(price - min_stop_distance, digits);
|
||||
}
|
||||
|
||||
if(tp_distance < min_stop_distance)
|
||||
{
|
||||
tp = NormalizeDouble(price + min_stop_distance, digits);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For sell: SL above price, TP below price
|
||||
double sl_distance = sl - price;
|
||||
double tp_distance = price - tp;
|
||||
|
||||
if(sl_distance < min_stop_distance)
|
||||
{
|
||||
sl = NormalizeDouble(price + min_stop_distance, digits);
|
||||
}
|
||||
|
||||
if(tp_distance < min_stop_distance)
|
||||
{
|
||||
tp = NormalizeDouble(price - min_stop_distance, digits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open Buy Position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenBuyPosition(double price)
|
||||
{
|
||||
double sl = price * (1.0 - StopLossPercent / 100.0);
|
||||
double tp = price * (1.0 + TakeProfitPercent / 100.0);
|
||||
|
||||
// Normalize stops according to broker requirements
|
||||
NormalizeStops(price, sl, tp, true);
|
||||
|
||||
if(trade.Buy(LotSize, TradingSymbol, price, sl, tp, "Tick Momentum Buy"))
|
||||
{
|
||||
position_entry_time = TimeCurrent();
|
||||
position_entry_price = price;
|
||||
position_type = POSITION_TYPE_BUY;
|
||||
|
||||
double order_flow_display = (sell_volume_sum > 0) ? (buy_volume_sum / sell_volume_sum) : 999.0;
|
||||
Print("BUY opened: Price=", price, " Momentum=", DoubleToString(current_momentum, 3),
|
||||
"% Consecutive=", consecutive_buy_ticks, " OrderFlow=", DoubleToString(order_flow_display, 2));
|
||||
|
||||
// Reset tracking
|
||||
consecutive_buy_ticks = 0;
|
||||
consecutive_sell_ticks = 0;
|
||||
buy_volume_sum = 0.0;
|
||||
sell_volume_sum = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open Sell Position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenSellPosition(double price)
|
||||
{
|
||||
double sl = price * (1.0 + StopLossPercent / 100.0);
|
||||
double tp = price * (1.0 - TakeProfitPercent / 100.0);
|
||||
|
||||
// Normalize stops according to broker requirements
|
||||
NormalizeStops(price, sl, tp, false);
|
||||
|
||||
if(trade.Sell(LotSize, TradingSymbol, price, sl, tp, "Tick Momentum Sell"))
|
||||
{
|
||||
position_entry_time = TimeCurrent();
|
||||
position_entry_price = price;
|
||||
position_type = POSITION_TYPE_SELL;
|
||||
|
||||
double order_flow_display = (buy_volume_sum > 0) ? (sell_volume_sum / buy_volume_sum) : 999.0;
|
||||
Print("SELL opened: Price=", price, " Momentum=", DoubleToString(current_momentum, 3),
|
||||
"% Consecutive=", consecutive_sell_ticks, " OrderFlow=", DoubleToString(order_flow_display, 2));
|
||||
|
||||
// Reset tracking
|
||||
consecutive_buy_ticks = 0;
|
||||
consecutive_sell_ticks = 0;
|
||||
buy_volume_sum = 0.0;
|
||||
sell_volume_sum = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check Position |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckPosition()
|
||||
{
|
||||
if(!PositionSelect(TradingSymbol)) return;
|
||||
|
||||
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) return;
|
||||
|
||||
ulong ticket = PositionGetInteger(POSITION_TICKET);
|
||||
double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
datetime open_time = (datetime)PositionGetInteger(POSITION_TIME);
|
||||
|
||||
// Get current price
|
||||
double current_price = (pos_type == POSITION_TYPE_BUY) ?
|
||||
SymbolInfoDouble(TradingSymbol, SYMBOL_BID) :
|
||||
SymbolInfoDouble(TradingSymbol, SYMBOL_ASK);
|
||||
|
||||
// Calculate profit percentage
|
||||
double profit_pct = 0.0;
|
||||
if(pos_type == POSITION_TYPE_BUY)
|
||||
profit_pct = ((current_price - open_price) / open_price) * 100.0;
|
||||
else
|
||||
profit_pct = ((open_price - current_price) / open_price) * 100.0;
|
||||
|
||||
// Check max loss
|
||||
if(profit_pct < -MaxLossPercent)
|
||||
{
|
||||
trade.PositionClose(ticket);
|
||||
Print("Position closed: Max loss reached ", DoubleToString(profit_pct, 2), "%");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check time-based exit (1 minute bars)
|
||||
int bars_held = (int)((TimeCurrent() - open_time) / 60); // Convert to minutes
|
||||
if(bars_held >= MaxBarsHold)
|
||||
{
|
||||
trade.PositionClose(ticket);
|
||||
Print("Position closed: Max bars held ", bars_held);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for momentum reversal (exit if momentum reverses)
|
||||
if(pos_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
// Exit if strong sell momentum develops
|
||||
if(consecutive_sell_ticks >= MinTicksForSignal && current_momentum >= MinTickMomentum)
|
||||
{
|
||||
double reversal_momentum = CalculateEntryMomentum(false);
|
||||
if(reversal_momentum >= EntryMomentumThreshold)
|
||||
{
|
||||
trade.PositionClose(ticket);
|
||||
Print("Position closed: Momentum reversal detected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // SELL
|
||||
{
|
||||
// Exit if strong buy momentum develops
|
||||
if(consecutive_buy_ticks >= MinTicksForSignal && current_momentum >= MinTickMomentum)
|
||||
{
|
||||
double reversal_momentum = CalculateEntryMomentum(true);
|
||||
if(reversal_momentum >= EntryMomentumThreshold)
|
||||
{
|
||||
trade.PositionClose(ticket);
|
||||
Print("Position closed: Momentum reversal detected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables for Optimization |
|
||||
//+------------------------------------------------------------------+
|
||||
double optimized_min_tick_momentum = 0.0;
|
||||
double optimized_order_flow_imbalance = 0.0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Optimize Parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
void OptimizeParameters()
|
||||
{
|
||||
Print("=== Optimizing Tick Momentum Parameters ===");
|
||||
|
||||
// Initialize optimized values if not set
|
||||
if(optimized_min_tick_momentum == 0.0)
|
||||
{
|
||||
optimized_min_tick_momentum = MinTickMomentum;
|
||||
}
|
||||
if(optimized_order_flow_imbalance == 0.0)
|
||||
{
|
||||
optimized_order_flow_imbalance = OrderFlowImbalance;
|
||||
}
|
||||
|
||||
// Simple optimization: test different thresholds
|
||||
double best_profit = CalculateCurrentProfitability();
|
||||
double best_momentum = optimized_min_tick_momentum;
|
||||
double best_imbalance = optimized_order_flow_imbalance;
|
||||
|
||||
// Test momentum thresholds
|
||||
for(double test_momentum = 0.03; test_momentum <= 0.15; test_momentum += 0.02)
|
||||
{
|
||||
optimized_min_tick_momentum = test_momentum;
|
||||
|
||||
double profit = BacktestParameters(OptimizationPeriodMinutes);
|
||||
|
||||
if(profit > best_profit)
|
||||
{
|
||||
best_profit = profit;
|
||||
best_momentum = test_momentum;
|
||||
}
|
||||
}
|
||||
|
||||
// Test imbalance thresholds
|
||||
for(double test_imbalance = 1.2; test_imbalance <= 2.5; test_imbalance += 0.2)
|
||||
{
|
||||
optimized_order_flow_imbalance = test_imbalance;
|
||||
|
||||
double profit = BacktestParameters(OptimizationPeriodMinutes);
|
||||
|
||||
if(profit > best_profit)
|
||||
{
|
||||
best_profit = profit;
|
||||
best_imbalance = test_imbalance;
|
||||
}
|
||||
}
|
||||
|
||||
// Update if better found
|
||||
if(best_profit > CalculateCurrentProfitability() * 1.1) // 10% improvement
|
||||
{
|
||||
optimized_min_tick_momentum = best_momentum;
|
||||
optimized_order_flow_imbalance = best_imbalance;
|
||||
Print("Parameters optimized: Momentum=", best_momentum, " Imbalance=", best_imbalance);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Keeping current parameters. Profit: ", DoubleToString(best_profit, 2), "%");
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate Current Profitability |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateCurrentProfitability()
|
||||
{
|
||||
double total_profit = 0.0;
|
||||
|
||||
// Check open positions
|
||||
if(PositionSelect(TradingSymbol))
|
||||
{
|
||||
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber)
|
||||
{
|
||||
double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double current_price = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ?
|
||||
SymbolInfoDouble(TradingSymbol, SYMBOL_BID) :
|
||||
SymbolInfoDouble(TradingSymbol, SYMBOL_ASK);
|
||||
|
||||
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
||||
total_profit = ((current_price - open_price) / open_price) * 100.0;
|
||||
else
|
||||
total_profit = ((open_price - current_price) / open_price) * 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check historical deals (last hour)
|
||||
datetime end_time = TimeCurrent();
|
||||
datetime start_time = end_time - 3600;
|
||||
|
||||
if(HistorySelect(start_time, end_time))
|
||||
{
|
||||
int total_deals = HistoryDealsTotal();
|
||||
for(int i = 0; i < total_deals; i++)
|
||||
{
|
||||
ulong ticket = HistoryDealGetTicket(i);
|
||||
if(ticket > 0)
|
||||
{
|
||||
if(HistoryDealGetString(ticket, DEAL_SYMBOL) == TradingSymbol &&
|
||||
HistoryDealGetInteger(ticket, DEAL_MAGIC) == MagicNumber)
|
||||
{
|
||||
double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
|
||||
double volume = HistoryDealGetDouble(ticket, DEAL_VOLUME);
|
||||
double price = HistoryDealGetDouble(ticket, DEAL_PRICE);
|
||||
|
||||
if(price > 0 && volume > 0)
|
||||
{
|
||||
total_profit += (profit / (price * volume)) * 100.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total_profit;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Backtest Parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
double BacktestParameters(int minutes)
|
||||
{
|
||||
// Simplified backtest - would need historical tick data
|
||||
// For now, return current profitability as approximation
|
||||
return CalculateCurrentProfitability();
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||