2254 行
166 KiB
MQL5
2254 行
166 KiB
MQL5
//given this whole file, what mql5 programming skills does the writer have and what skills does he need to get to a consistently profitable expert advisor??
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| ValueLevelsEA |
|
|
//| Copyright 2023, MetaQuotes Software Corp. |
|
|
//| https://www.metaquotes.net |
|
|
//+------------------------------------------------------------------+
|
|
#property strict
|
|
#property version "1.4"
|
|
|
|
// Define session times (in GMT)
|
|
#define ASIAN_START_HOUR 1
|
|
#define ASIAN_END_HOUR 9
|
|
#define LONDON_START_HOUR 8
|
|
#define LONDON_END_HOUR 16
|
|
#define NY_START_HOUR 13
|
|
#define NY_END_HOUR 21
|
|
|
|
#include <Trade\Trade.mqh>
|
|
CTrade trade;
|
|
// Define the structure for the neural network
|
|
|
|
|
|
|
|
input int PositionsThreshold = 4;
|
|
input int Grid_Step = 1400;
|
|
int Level_Step = 5;
|
|
input double Lots = 1;
|
|
int lookbackDays = 16;
|
|
int LevelsToCheck = 1400;
|
|
input double RiskToReward = 2;
|
|
ENUM_TIMEFRAMES LevelsUpdatePeriod = PERIOD_H1;
|
|
ENUM_TIMEFRAMES FractalPeriod = PERIOD_H1;
|
|
input ENUM_TIMEFRAMES timeframe = PERIOD_M1;
|
|
input double SigThresh = 0.1;
|
|
input double learningRate = 0.1;
|
|
|
|
// Variables to store Value Area High and Low for each session
|
|
double bid, ask, meanprice;
|
|
int bars, bars1;
|
|
bool ASFlag, LDFlag, NYFlag;
|
|
datetime asianStart;
|
|
datetime asianEnd;
|
|
datetime londonStart;
|
|
datetime londonEnd;
|
|
datetime nyStart;
|
|
datetime nyEnd;
|
|
|
|
double HlineArray[], mom[], atr[], obv[], rsi[], opens[], highs[], lows[], closes[];
|
|
long tradeVolume[];
|
|
int momH, atrH, obvH, rsiH;
|
|
MqlRates Bar2[];
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
// Initialization of the Expert Advisor
|
|
atrH = iATR(_Symbol, PERIOD_CURRENT,11);
|
|
obvH = iOBV(_Symbol, PERIOD_M15,VOLUME_TICK);
|
|
rsiH = iRSI(_Symbol, PERIOD_CURRENT,14,PRICE_MEDIAN);
|
|
|
|
//momH = iMomentum(_Symbol,PERIOD_H2 ,50,PRICE_MEDIAN);
|
|
|
|
ArraySetAsSeries(atr,true);
|
|
ArraySetAsSeries(mom,true);
|
|
//ArraySetAsSeries(inputData,true);
|
|
ArraySetAsSeries(opens,true);
|
|
ArraySetAsSeries(highs,true);
|
|
ArraySetAsSeries(lows,true);
|
|
ArraySetAsSeries(closes,true);
|
|
ArraySetAsSeries(tradeVolume,true);
|
|
Print("Value Area Expert Advisor initialized.");
|
|
|
|
|
|
int totalBars = Bars(_Symbol,timeframe);
|
|
CopyOpen(_Symbol,timeframe,0,totalBars,opens);
|
|
CopyHigh(_Symbol, timeframe, 0, totalBars, highs);
|
|
CopyLow(_Symbol, timeframe, 0, totalBars, lows);
|
|
CopyClose(_Symbol, timeframe, 0, totalBars, closes);
|
|
CopyTickVolume(_Symbol, timeframe, 0, totalBars, tradeVolume);
|
|
|
|
// Initialize the neural network
|
|
NeuralNetwork nn;
|
|
InitializeNeuralNetwork(nn);
|
|
|
|
double expected_outputs[];
|
|
ArrayResize(expected_outputs,totalBars);
|
|
|
|
for(int i=1;i<totalBars;i++)
|
|
{
|
|
expected_outputs[i-1] = opens[i] - closes[i];
|
|
}
|
|
|
|
|
|
int size = totalBars;
|
|
|
|
// Create an array of InputData structures
|
|
double inputData[][5];
|
|
CalculateDifferences(opens, highs, lows, closes, tradeVolume, inputData, size);
|
|
|
|
// Initialize the neural network
|
|
InitializeNeuralNetwork(nn);
|
|
|
|
// Prepare input data for training
|
|
double inputs[][5];
|
|
ArrayResize(inputs,size);
|
|
for(int i = 0; i < size-1; i++)
|
|
{
|
|
inputs[i][0] = inputData[i][0];
|
|
inputs[i][1] = inputData[i][1];
|
|
inputs[i][2] = inputData[i][2];
|
|
inputs[i][3] = inputData[i][3];
|
|
inputs[i][4] = (double)inputData[i][4];
|
|
}
|
|
|
|
// Example expected outputs for training (this should be your target values)
|
|
|
|
// Train the neural network
|
|
int epochs = 10000;
|
|
Train(nn, inputs, expected_outputs, epochs, learningRate);
|
|
|
|
// Predict using the trained network
|
|
double hidden_layer[5];
|
|
double output;
|
|
double result = Predict(nn, inputs, output, hidden_layer);
|
|
|
|
Print("Prediction: ", result);
|
|
|
|
return(INIT_SUCCEEDED);
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
// Deinitialization of the Expert Advisor
|
|
Print("Value Area Expert Advisor deinitialized.");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
// Calculate value areas for each session
|
|
|
|
datetime currentTime = TimeCurrent();
|
|
asianStart = currentTime - (currentTime % 86400) + ASIAN_START_HOUR * 3600;
|
|
asianEnd = currentTime - (currentTime % 86400) + ASIAN_END_HOUR * 3600;
|
|
londonStart = currentTime - (currentTime % 86400) + LONDON_START_HOUR * 3600;
|
|
londonEnd = currentTime - (currentTime % 86400) + LONDON_END_HOUR * 3600;
|
|
nyStart = currentTime - (currentTime % 86400) + NY_START_HOUR * 3600;
|
|
nyEnd = currentTime - (currentTime % 86400) + NY_END_HOUR * 3600;
|
|
|
|
|
|
if(bars < Bars(_Symbol, timeframe))
|
|
{
|
|
int totalBars = Bars(_Symbol,timeframe);
|
|
CopyHigh(_Symbol, timeframe, 0, totalBars, highs);
|
|
CopyOpen(_Symbol, timeframe, 0, totalBars, opens);
|
|
CopyLow(_Symbol, timeframe, 0, totalBars, lows);
|
|
CopyClose(_Symbol, timeframe, 0, totalBars, closes);
|
|
CopyTickVolume(_Symbol, timeframe, 0, totalBars, tradeVolume);
|
|
//copy time and extract the day of the week, month, hour and minute and add them to their respective arrays just like open, high low and close are added above
|
|
|
|
// Create an array of InputData structures
|
|
double inputData[][5];
|
|
CalculateDifferences(opens, highs, lows, closes, tradeVolume, inputData, totalBars);
|
|
|
|
// Initialize the neural network
|
|
NeuralNetwork nn;
|
|
InitializeNeuralNetwork(nn);
|
|
// Prepare input data for training
|
|
double inputs[][5];
|
|
ArrayResize(inputs,totalBars);
|
|
for(int i = 0; i < totalBars-1; i++)
|
|
{
|
|
inputs[i][0] = inputData[i][0];
|
|
inputs[i][1] = inputData[i][1];
|
|
inputs[i][2] = inputData[i][2];
|
|
inputs[i][3] = inputData[i][3];
|
|
inputs[i][4] = (double)inputData[i][4];
|
|
}
|
|
|
|
double expected_outputs[];
|
|
ArrayResize(expected_outputs,totalBars);
|
|
|
|
for(int i=1;i<totalBars;i++)
|
|
{
|
|
expected_outputs[i-1] = opens[i] - closes[i];
|
|
}
|
|
|
|
//Example expected outputs for training (this should be your target values)
|
|
int epochs = 1000;
|
|
Train(nn, inputs, expected_outputs, epochs, learningRate);
|
|
|
|
|
|
// Train the neural network
|
|
// Predict using the trained network
|
|
double hidden_layer[5];
|
|
double output;
|
|
double result = Predict(nn, inputs, output, hidden_layer);
|
|
|
|
Print("Prediction: ", result);
|
|
|
|
|
|
ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
|
|
bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
|
|
meanprice = (ask+bid)/2;
|
|
|
|
if(IsMarketOpen())
|
|
{
|
|
CopyBuffer(atrH,0,0,10,atr);
|
|
|
|
|
|
// Define lookback period in hours and level step in points
|
|
double levelStep = _Point*Level_Step;
|
|
|
|
datetime timeStart = asianStart;
|
|
datetime timeEnd = nyEnd;
|
|
// Declare arrays to store volume and price levels
|
|
double volumeArray[];
|
|
double priceLevelArray[];
|
|
CalculateVolumeDistribution(lookbackDays,levelStep,timeStart,timeEnd,volumeArray,priceLevelArray);
|
|
|
|
for(int i = 0; i < ArraySize(volumeArray); i++)
|
|
{
|
|
//Print("Level: ", priceLevelArray[i], " Volume: ", volumeArray[i], " Index: ", i);
|
|
}
|
|
|
|
|
|
|
|
// Calculate the volume distribution
|
|
if(currentTime > timeStart && currentTime < timeEnd)
|
|
{
|
|
|
|
int peaks[];
|
|
int peak_count = ArrayPeaks(volumeArray, peaks, levelStep);
|
|
|
|
//Print("Number of modes found: ", peak_count);
|
|
|
|
ArrayResize(HlineArray, peak_count);
|
|
|
|
|
|
double lastHigh = -DBL_MAX;
|
|
double lastLow = DBL_MAX;
|
|
FindLastHighLow(5,lookbackDays, LevelsUpdatePeriod, lastHigh, lastLow);
|
|
DrawTwoHorizontalLines(lastHigh,lastLow,clrRed,clrRed,STYLE_DASHDOT,1, "lastHighLow");
|
|
|
|
double vaHigh = -DBL_MAX;
|
|
double vaLow = DBL_MAX;
|
|
CalculateValueArea(priceLevelArray, volumeArray,vaHigh,vaLow, 50.0);
|
|
DrawTwoHorizontalLines(vaHigh,vaLow,clrBlue, clrGreen, STYLE_DASHDOT,1, "VAHighLow");
|
|
|
|
double buyVolume = Lots * (1+BuyPositionsCount());
|
|
double sellVolume = Lots * (1+SellPositionsCount());
|
|
//double buyStop = lastHigh;
|
|
//double sellStop = lastLow;
|
|
double buySL = ask - atr[0]*RiskToReward;
|
|
double buyTP = ask + atr[0];
|
|
double sellSL = bid + atr[0]*RiskToReward;
|
|
double sellTP = bid - atr[0];
|
|
|
|
|
|
if(!OtherBuyPosition() && !OtherSellPosition()
|
|
&& PositionsTotal() < PositionsThreshold
|
|
&& CheckMoneyForTrade(_Symbol,buyVolume,ORDER_TYPE_BUY)
|
|
&& atr[0] > SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)
|
|
&& result > 1-SigThresh-SigThresh
|
|
&& result < 1-SigThresh
|
|
|
|
)
|
|
{
|
|
//trade.BuyLimit(Volume,GetLowestVAL(),_Symbol,GetLowestVAL() - atr,GetHighestVAH(),0,0,"VALAsian");
|
|
//trade.BuyStop(Volume,buyStop,_Symbol,buySL,buyTP,0,0,NULL);
|
|
trade.Buy(buyVolume,_Symbol,0,buySL,buyTP,NULL);
|
|
}
|
|
|
|
|
|
|
|
if(!OtherBuyPosition() && !OtherSellPosition()
|
|
&& PositionsTotal() < PositionsThreshold
|
|
&& CheckMoneyForTrade(_Symbol,sellVolume,ORDER_TYPE_SELL)
|
|
&& atr[0] > SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)
|
|
&& result > SigThresh
|
|
&& result < SigThresh+SigThresh
|
|
)
|
|
{
|
|
//trade.SellLimit(Volume,GetHighestVAH(),_Symbol,GetHighestVAH() + atr,GetLowestVAL(),0,0,"VAHNY");
|
|
//trade.SellStop(Volume,sellStop,_Symbol,sellSL,sellTP,0,0,NULL);
|
|
trade.Sell(sellVolume,_Symbol,0,sellSL,sellTP,NULL);
|
|
}
|
|
TrailingSL();
|
|
//CloseWinning();
|
|
}
|
|
bars = Bars(_Symbol,timeframe);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Trade transaction handler |
|
|
//+------------------------------------------------------------------+
|
|
void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result)
|
|
{
|
|
// Check if the transaction is a deal and if it's a pending order activation
|
|
if(trans.type == TRADE_TRANSACTION_DEAL_ADD &&(trans.deal_type == DEAL_TYPE_BUY || trans.deal_type == DEAL_TYPE_SELL))
|
|
{
|
|
// Get the order ticket of the executed order
|
|
ulong executedOrderTicket = trans.order;
|
|
|
|
// Cancel other pending orders
|
|
CancelOtherPendingOrders(executedOrderTicket);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
|
// define a function that takes an array and normalizes the values from 0 to 1
|
|
|
|
// To create a function in MQL5 that normalizes the values of an array from 0 to 1, you can follow these steps. The normalization process involves scaling the values so that the minimum value of the array becomes 0 and the maximum value becomes 1. Here's how you can implement this:
|
|
//
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Function to normalize an array's values to the range [0, 1] |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void NormalizeArrayTo01(double &array[][5])
|
|
{
|
|
|
|
int arraySize = ArrayRange(array,0);
|
|
if(arraySize <= 0)
|
|
return; // Check if the array size is valid
|
|
// Find the minimum and maximum values in the array
|
|
for(int j=0;j<5;j++)
|
|
{
|
|
|
|
double minValue = array[0][j];
|
|
double maxValue = array[0][j];
|
|
|
|
for(int i = 0; i < arraySize-1; i++)
|
|
{
|
|
if(array[i][j] < minValue)
|
|
minValue = array[i][j];
|
|
|
|
if(array[i][j] > maxValue)
|
|
maxValue = array[i][j];
|
|
}
|
|
|
|
// Calculate the range
|
|
double range = maxValue - minValue;
|
|
|
|
// Normalize the array values
|
|
for(int i = 0; i < arraySize; i++)
|
|
{
|
|
if(range != 0) // Avoid division by zero
|
|
array[i][j] = (array[i][j] - minValue) / range;
|
|
else
|
|
array[i][j] = 0; // If all values are the same, set them to zero after normalization
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// ### Explanation:
|
|
// 1. **Parameters:**
|
|
// - The `NormalizeArrayTo01` function takes a reference to a double array and its size. The reference allows the function to modify the array in place.
|
|
//
|
|
// 2. **Finding Min and Max:**
|
|
// - The function first finds the minimum and maximum values in the array, which are necessary for the normalization formula.
|
|
//
|
|
// 3. **Normalization:**
|
|
// - It computes the range (difference between the max and min values). Each element is then transformed using the formula: \((\text{value} - \text{minValue}) / \text{range}\).
|
|
//
|
|
// 4. **Edge Case Handling:**
|
|
// - If all elements in the array are the same, the range will be 0, which would cause division by zero. In this case, it sets all normalized values to 0.
|
|
//
|
|
// 5. **Example Usage:**
|
|
// - The `OnStart` function demonstrates how to use the normalization function with an example array.
|
|
//
|
|
// This code will print the original and normalized array to the output, showing the transformation of the data.
|
|
//
|
|
|
|
//// Define a structure for the input data
|
|
//struct InputData {
|
|
// double highDiff;
|
|
// double lowDiff;
|
|
// double closeDiff;
|
|
// long volumeDiff;
|
|
//};
|
|
|
|
struct NeuralNetwork
|
|
{
|
|
double input_to_hidden_weights[5][5]; // Weights from input layer to hidden layer
|
|
double hidden_biases[5]; // Biases for hidden layer
|
|
double hidden_to_output_weights[5]; // Weights from hidden layer to output layer
|
|
double output_bias; // Bias for output layer
|
|
};
|
|
|
|
|
|
// Function to calculate differences
|
|
void CalculateDifferences(double &open[], double &high[], double &low[], double &close[], long &volume[], double &inputData[][5], int size)
|
|
{
|
|
ArrayResize(inputData, size);
|
|
|
|
|
|
for(int i = 0; i < size-1; i++)
|
|
{
|
|
inputData[i][3] = open[i+1] - open[i];
|
|
inputData[i][0] = high[i+1] - high[i];
|
|
inputData[i][1] = low[i+1] - low[i];
|
|
inputData[i][2] = close[i+1] - close[i];
|
|
inputData[i][4] = (double) volume[i+1] - volume[i];
|
|
}
|
|
NormalizeArrayTo01(inputData);
|
|
}
|
|
|
|
|
|
// Function to initialize the neural network with random weights and biases
|
|
void InitializeNeuralNetwork(NeuralNetwork &nn)
|
|
{
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
for(int j = 0; j < 5; j++)
|
|
{
|
|
nn.input_to_hidden_weights[i][j] = MathRand() / 32767.0 * 2.0 - 1.0; // Random values between -1 and 1
|
|
}
|
|
nn.hidden_biases[i] = MathRand() / 32767.0 * 2.0 - 1.0; // Random values between -1 and 1
|
|
nn.hidden_to_output_weights[i] = MathRand() / 32767.0 * 2.0 - 1.0; // Random values between -1 and 1
|
|
}
|
|
nn.output_bias = MathRand() / 32767.0 * 2.0 - 1.0; // Random value between -1 and 1
|
|
}
|
|
|
|
// Activation function (e.g., sigmoid)
|
|
double Sigmoid(double x)
|
|
{
|
|
return 1.0 / (1.0 + MathExp(-x));
|
|
}
|
|
|
|
// Derivative of the sigmoid function
|
|
double SigmoidDerivative(double x)
|
|
{
|
|
return x * (1.0 - x);
|
|
}
|
|
|
|
// Forward pass for prediction
|
|
double Predict(NeuralNetwork &nn, double &inputs[][5], double &output, double &hidden_layer[])
|
|
{
|
|
// Calculate hidden layer activations
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
hidden_layer[i] = 0.0;
|
|
for(int j = 0; j < 5; j++)
|
|
{
|
|
hidden_layer[i] += inputs[i][j] * nn.input_to_hidden_weights[j][i];
|
|
}
|
|
hidden_layer[i] += nn.hidden_biases[i];
|
|
hidden_layer[i] = Sigmoid(hidden_layer[i]); // Apply activation function
|
|
}
|
|
|
|
// Calculate output layer activation
|
|
output = 0.0;
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
output += hidden_layer[i] * nn.hidden_to_output_weights[i];
|
|
}
|
|
output += nn.output_bias;
|
|
output = Sigmoid(output); // Apply activation function
|
|
|
|
// Return the final output
|
|
return output;
|
|
}
|
|
|
|
// Train the network using backpropagation
|
|
void Train(NeuralNetwork &nn, double &inputs[][5], double &expected_outputs[], int epochs, double learning_rate)
|
|
{
|
|
int num_samples = 0;
|
|
for(int epoch = 0; epoch < epochs; epoch++)
|
|
{
|
|
for(int sample = 0; sample < num_samples; sample++)
|
|
{
|
|
double hidden_layer[5];
|
|
double output = 0;
|
|
|
|
// Forward pass
|
|
double predicted = Predict(nn, inputs, output, hidden_layer);
|
|
|
|
// Calculate the error
|
|
double error = expected_outputs[sample] - predicted;
|
|
double d_output = error * SigmoidDerivative(output);
|
|
|
|
// Backpropagation
|
|
double d_hidden[5];
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
d_hidden[i] = d_output * nn.hidden_to_output_weights[i] * SigmoidDerivative(hidden_layer[i]);
|
|
}
|
|
|
|
// Update the weights and biases
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
nn.hidden_to_output_weights[i] += learning_rate * d_output * hidden_layer[i];
|
|
}
|
|
nn.output_bias += learning_rate * d_output;
|
|
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
for(int j = 0; j < 5; j++)
|
|
{
|
|
nn.input_to_hidden_weights[j][i] += learning_rate * d_hidden[i] * inputs[sample][j];
|
|
}
|
|
nn.hidden_biases[i] += learning_rate * d_hidden[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// use the InputData structure for training and prediction.
|
|
|
|
// The given code defines a simple neural network in MQL5 for processing financial data, specifically designed to work with differences in high, low, close prices, and volume. Here's how you can use the `InputData` structure for training and prediction:
|
|
//
|
|
// ### Step-by-step Guide:
|
|
//
|
|
// 1. **Calculate Differences:**
|
|
// - Use the `CalculateDifferences` function to preprocess your price and volume data into differences. This will populate an array of `InputData` structures.
|
|
//
|
|
// 2. **Initialize the Neural Network:**
|
|
// - Call `InitializeNeuralNetwork` to set up the neural network with random weights and biases.
|
|
//
|
|
// 3. **Prepare Training Data:**
|
|
// - Organize your `InputData` into an array that can be fed into the neural network. You'll need to extract the `highDiff`, `lowDiff`, `closeDiff`, and `volumeDiff` into a 2D array suitable for the `Train` function.
|
|
//
|
|
// 4. **Train the Neural Network:**
|
|
// - Use the `Train` function to train the neural network. You'll need to define the number of epochs and the learning rate. Also, set up the expected outputs for your samples.
|
|
//
|
|
// 5. **Make Predictions:**
|
|
// - Use the `Predict` function to obtain predictions from the neural network once it's trained.
|
|
//
|
|
// ### Example Implementation:
|
|
//
|
|
// Below is an example to illustrate how you might set up your data and use the neural network functions:
|
|
//
|
|
|
|
// Example usage
|
|
// ### Explanation:
|
|
// - **Data Preparation:** We calculate differences using `CalculateDifferences` and store them in `InputData`.
|
|
// - **Neural Network Initialization:** Random weights and biases are set up using `InitializeNeuralNetwork`.
|
|
// - **Training:** The `Train` function adjusts the weights using backpropagation.
|
|
// - **Prediction:** The `Predict` function provides an output based on the neural network's current state.
|
|
//
|
|
// Ensure that you have your expected outputs correctly defined based on the problem you're trying to solve. The neural network's performance will depend significantly on the quality and nature of your data and expected outputs.
|
|
//
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Function to cancel other pending orders |
|
|
//+------------------------------------------------------------------+
|
|
void CancelOtherPendingOrders(ulong executedOrderTicket)
|
|
{
|
|
// Loop through all orders
|
|
for(int i = OrdersTotal() - 1; i >= 0; i--)
|
|
{
|
|
// Select the order by index
|
|
if(OrderSelect(OrderGetTicket(i)))
|
|
{
|
|
// Check if the order is a pending order and not the executed one
|
|
if((OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_LIMIT || OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_LIMIT ||
|
|
OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP || OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP) &&
|
|
OrderGetInteger(ORDER_TICKET) != executedOrderTicket
|
|
)
|
|
{
|
|
// Prepare a request to delete the order
|
|
MqlTradeRequest request;
|
|
MqlTradeResult result;
|
|
ZeroMemory(request);
|
|
ZeroMemory(result);
|
|
|
|
request.action = TRADE_ACTION_REMOVE;
|
|
request.order = OrderGetInteger(ORDER_TICKET);
|
|
|
|
// Send the request
|
|
if(!OrderSend(request, result))
|
|
{
|
|
//Print("Failed to cancel order: ", result.comment);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Identifying fractal levels maxima/minima within n bars |
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
|
|
/// Global arrays to store fractal highs and lows
|
|
double fractalHighs[];
|
|
double fractalLows[];
|
|
double fractalHighsTime[];
|
|
double fractalLowsTime[];
|
|
int fractalHighsIndex[];
|
|
int fractalLowsIndex[];
|
|
|
|
MqlRates Bar3[];
|
|
|
|
//Function to identify fractal bars and store their levels
|
|
void IdentifyFractalLevels(int period, int n, ENUM_TIMEFRAMES TF)
|
|
{
|
|
// Ensure the period is valid
|
|
if(period < n)
|
|
{
|
|
//Print("Period must be at least ", n);
|
|
return;
|
|
}
|
|
|
|
// Resize arrays to the specified period
|
|
double highs[];
|
|
double lows[];
|
|
ArrayResize(highs, period);
|
|
ArrayResize(lows, period);
|
|
|
|
// Copy high and low prices for the given period
|
|
if(CopyHigh(_Symbol, TF, 0, period, highs) <= 0 || CopyLow(_Symbol, TF, 0, period, lows) <= 0) // || CopyTime(_Symbol, TF, 0, period, time) <= 0)
|
|
{
|
|
//Print("Failed to copy price data.");
|
|
return;
|
|
}
|
|
if(CopyRates(_Symbol, TF, 0, period, Bar3) <= 0)
|
|
{
|
|
//Print("Failed to copy rates");
|
|
return;
|
|
}
|
|
|
|
// Clear previous fractal data
|
|
ArrayResize(fractalHighs, 0);
|
|
ArrayResize(fractalLows, 0);
|
|
ArrayResize(fractalHighsTime, 0);
|
|
ArrayResize(fractalLowsTime, 0);
|
|
ArrayResize(fractalHighsIndex,0);
|
|
ArrayResize(fractalLowsIndex,0);
|
|
|
|
|
|
// Adjust the loop to find local maxima and minima for n bars around the highest/lowest bar
|
|
for(int i = n; i < period - n; i++)
|
|
{
|
|
bool isLocalMax = true;
|
|
bool isLocalMin = true;
|
|
|
|
// Check for local maximum
|
|
for(int j = 1; j <= n; j++)
|
|
{
|
|
if(highs[i] <= highs[i + j] || highs[i] <= highs[i - j])
|
|
{
|
|
isLocalMax = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(isLocalMax)
|
|
{
|
|
ArrayResize(fractalHighs, ArraySize(fractalHighs) + 1);
|
|
fractalHighs[ArraySize(fractalHighs) - 1] = highs[i];
|
|
ArrayResize(fractalHighsTime, ArraySize(fractalHighsTime) + 1);
|
|
fractalHighsTime[ArraySize(fractalHighsTime) - 1] = (double) Bar3[i].time;
|
|
ArrayResize(fractalHighsIndex, ArraySize(fractalHighsIndex) + 1);
|
|
fractalHighsIndex[ArraySize(fractalHighsIndex) - 1] = i;
|
|
|
|
DrawArrow(highs[i],i,"top", clrRed);
|
|
}
|
|
|
|
// Check for local minimum
|
|
for(int j = 1; j <= n; j++)
|
|
{
|
|
if(lows[i] >= lows[i + j] || lows[i] >= lows[i - j])
|
|
{
|
|
isLocalMin = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(isLocalMin)
|
|
{
|
|
ArrayResize(fractalLows, ArraySize(fractalLows) + 1);
|
|
fractalLows[ArraySize(fractalLows) - 1] = lows[i];
|
|
ArrayResize(fractalLowsTime, ArraySize(fractalLowsTime) + 1);
|
|
fractalLowsTime[ArraySize(fractalLowsTime) - 1] = (double) Bar3[i].time;
|
|
ArrayResize(fractalLowsIndex, ArraySize(fractalLowsIndex) + 1);
|
|
fractalLowsIndex[ArraySize(fractalLowsIndex) - 1] = i;
|
|
|
|
DrawArrow(lows[i],i,"bottom", clrRed);
|
|
|
|
}
|
|
}
|
|
//Print the fractal highs and lows for debugging
|
|
for(int i = 0; i < ArraySize(fractalHighs); i++)
|
|
{
|
|
//Print("Fractal High at index ", i, ": ", fractalHighs[i]);
|
|
}
|
|
for(int i = 0; i < ArraySize(fractalLows); i++)
|
|
{
|
|
//Print("Fractal Low at index ", i, ": ", fractalLows[i]);
|
|
}
|
|
|
|
}
|
|
|
|
//draw arrows either above or below bars given arguments: price level, index, arrow position(top, bottom), arrow code, arrow color
|
|
//
|
|
// To draw arrows on a chart in MQL5, you can use the `ObjectCreate` and `ObjectSetInteger` functions to create and customize the arrow objects. Below is a sample code snippet that demonstrates how to draw arrows either above or below bars on a chart based on the given arguments: price level, index, arrow position (top, bottom), arrow code, and arrow color.
|
|
//
|
|
|
|
// Function to draw an arrow on the chart
|
|
void DrawArrow(double priceLevel, int index, string arrowPosition, color arrowColor)
|
|
{
|
|
// Create a unique name for the arrow object
|
|
string arrowName = "Arrow_" + IntegerToString(index) + "_" + arrowPosition;
|
|
int arrowCode = 0;
|
|
// Determine the y-coordinate for the arrow based on the position (top or bottom)
|
|
double yCoordinate = priceLevel;
|
|
if(arrowPosition == "top")
|
|
{
|
|
// Place the arrow above the bar
|
|
yCoordinate += 10 * Point(); // Adjust the multiplier as needed
|
|
arrowCode = 233;
|
|
}
|
|
else
|
|
if(arrowPosition == "bottom")
|
|
{
|
|
// Place the arrow below the bar
|
|
yCoordinate -= 10 * Point(); // Adjust the multiplier as needed
|
|
arrowCode = 234;
|
|
}
|
|
|
|
// Create the arrow object
|
|
if(!ObjectCreate(0, arrowName, OBJ_ARROW, 0,index, yCoordinate))
|
|
{
|
|
//Print("Failed to create arrow object: ", arrowName);
|
|
return;
|
|
}
|
|
|
|
// Set the properties of the arrow
|
|
ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowCode);
|
|
ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor);
|
|
ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 1); // Set the width of the arrow
|
|
ObjectSetInteger(0, arrowName, OBJPROP_SELECTABLE, false);
|
|
ObjectSetInteger(0, arrowName, OBJPROP_HIDDEN, true);
|
|
ObjectSetInteger(0, arrowName, OBJPROP_ZORDER, 0);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Draw Arrows at level - time coordinates | |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void DrawArrowsAtLevels(double &levels[], int &barIndexes[], int arrowCode, color arrowColor)
|
|
{
|
|
// Ensure the levels and barIndexes arrays have the same size
|
|
int size = ArraySize(levels);
|
|
if(size != ArraySize(barIndexes))
|
|
{
|
|
//Print("Error: The levels and barIndexes arrays must have the same size.");
|
|
return;
|
|
}
|
|
|
|
// Loop through each level and draw an arrow
|
|
for(int i = 0; i < size; i++)
|
|
{
|
|
// Create a unique name for each arrow object
|
|
string arrowName = "Arrow_" + ColorToString(arrowColor) + IntegerToString(i);
|
|
|
|
// Check if the object already exists and delete it if it does
|
|
if(ObjectFind(0, arrowName) != -1)
|
|
{
|
|
ObjectDelete(0, arrowName);
|
|
}
|
|
|
|
// Create the arrow object
|
|
if(!ObjectCreate(0, arrowName, OBJ_ARROW, 0,iTime(_Symbol,PERIOD_CURRENT,barIndexes[i]),levels[i]))
|
|
{
|
|
//Print("Error creating arrow object: ", arrowName);
|
|
continue;
|
|
}
|
|
|
|
// Set the properties of the arrow
|
|
ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor);
|
|
ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowCode);
|
|
ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 1); // Set the width of the arrow
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------+
|
|
// Function to determine the trend based on fractal highs and lows |
|
|
//-----------------------------------------------------------------------+
|
|
string CheckFractalTrend(int n)
|
|
{
|
|
// Ensure there are enough data points to analyze
|
|
if(ArraySize(fractalHighs) < 2 || ArraySize(fractalLows) < 2)
|
|
{
|
|
//Print("Not enough fractal data to determine trend.");
|
|
return "Not enough fractal data to determine trend.";
|
|
}
|
|
|
|
// Initialize flags for trend detection
|
|
bool higherHighs = false;
|
|
bool lowerLows = false;
|
|
bool higherLows = false;
|
|
bool lowerHighs = false;
|
|
|
|
// modify to Check only the last n values from fractal highs and lows
|
|
|
|
// To modify the code to check only the last `n` values from fractal highs and lows, you need to iterate over the last `n` elements of the `fractalHighs` and `fractalLows` arrays. You can achieve this by using a loop that starts from the end of the array and goes back `n` elements. Here's how you can modify your code:
|
|
//
|
|
|
|
// Define the number of last values to check
|
|
|
|
// Check the last n values for fractal highs
|
|
for(int i = ArraySize(fractalHighs) - 1; i >= ArraySize(fractalHighs) - n; i--)
|
|
{
|
|
if(i <= 0)
|
|
break; // Ensure we don't go out of bounds
|
|
|
|
if(fractalHighs[i] < fractalHighs[i - 1])
|
|
{
|
|
higherHighs = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check the last n values for fractal lows
|
|
for(int i = ArraySize(fractalLows) - 1; i >= ArraySize(fractalLows) - n; i--)
|
|
{
|
|
if(i <= 0)
|
|
break; // Ensure we don't go out of bounds
|
|
|
|
if(fractalLows[i] > fractalLows[i - 1])
|
|
{
|
|
lowerLows = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Now higherHighs and lowerLows will reflect the result of checking the last n values
|
|
|
|
//
|
|
// ### Explanation:
|
|
// - **Loop Setup**: The loops iterate from the last element of the array to the element `n` positions back. This ensures that only the last `n` elements are checked.
|
|
// - **Boundary Check**: The `if(i <= 0) break;` statement ensures that the loop does not attempt to access an element before the start of the array, which would cause an out-of-bounds error.
|
|
// - **Condition Check**: The conditions inside the loops check if the current element is less than or greater than the previous one, updating the `higherHighs` and `lowerLows` flags accordingly.
|
|
// - **Flags**: The `higherHighs` and `lowerLows` flags are set to `true` initially and are only set to `false` if a condition is not met during the iteration. This means that if all conditions are met, the flags remain `true`.
|
|
//
|
|
|
|
|
|
// Determine and print the trend
|
|
if(higherHighs && lowerLows)
|
|
{
|
|
//Print("The fractals are making undefined moves (notrend).");
|
|
Comment("Notrend");
|
|
return "undefined";
|
|
}
|
|
else
|
|
if(higherHighs)
|
|
{
|
|
//Print("The fractals are making higher highs (uptrend).");
|
|
Comment("BULL");
|
|
return "Bullish";
|
|
}
|
|
else
|
|
if(lowerLows)
|
|
{
|
|
//Print("The fractals are making lower lows (downtrend).");
|
|
Comment("Bear");
|
|
return "Bearish";
|
|
}
|
|
else
|
|
{
|
|
//Print("The fractals do not show a clear trend.");
|
|
Comment("???");
|
|
return "The fractals do not show a clear trend.";
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Function to calculate the moving average of n elements |
|
|
//| from a time series array |
|
|
//+------------------------------------------------------------------+
|
|
double CalculateMovingAverage(double &modifiedArray[], const double &sourceArray[], int n)
|
|
{
|
|
int arraySize = ArraySize(sourceArray);
|
|
|
|
// Check if the period n is valid
|
|
if(n <= 0 || n > arraySize)
|
|
{
|
|
//Print("Invalid period for moving average calculation.");
|
|
return 0;
|
|
}
|
|
|
|
// Initialize the modifiedArray with the same size as sourceArray
|
|
ArrayResize(modifiedArray, arraySize);
|
|
|
|
// Calculate the moving average
|
|
for(int i = 0; i < arraySize; i++)
|
|
{
|
|
double sum = 0;
|
|
int count = 0;
|
|
|
|
// Sum up the n elements
|
|
for(int j = i; j > i - n && j >= 0; j--)
|
|
{
|
|
sum += sourceArray[j];
|
|
count++;
|
|
}
|
|
|
|
// Calculate the average
|
|
modifiedArray[i] = sum / count;
|
|
}
|
|
|
|
return 1; // Return 1 to indicate successful calculation
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Function to find the last highest high and lowest low |
|
|
//| on a specified timeframe. |
|
|
//| Parameters: |
|
|
//| n - Number of bars around the high/low to consider |
|
|
//| timeframe - The timeframe to use for the calculation |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void FindLastHighLow(int n, int kDays, ENUM_TIMEFRAMES timefram, double &lastHigh, double &lastLow)
|
|
{
|
|
// Ensure 'n' and 'kDays' are positive numbers
|
|
if(n <= 0 || kDays <= 0)
|
|
{
|
|
//Print("Error: 'n' and 'kDays' must be greater than 0.");
|
|
return;
|
|
}
|
|
|
|
// Initialize variables to store the last highest high and lowest low
|
|
lastHigh = -DBL_MAX;
|
|
lastLow = DBL_MAX;
|
|
|
|
// Get the total number of bars on the specified timeframe
|
|
int totalBars = Bars(_Symbol, timefram);
|
|
|
|
// Calculate the number of bars in one day for the given timeframe
|
|
int barsPerDay = 0;
|
|
switch(timeframe)
|
|
{
|
|
case PERIOD_M1:
|
|
barsPerDay = 1440;
|
|
break;
|
|
case PERIOD_M2:
|
|
barsPerDay = 720;
|
|
break;
|
|
case PERIOD_M3:
|
|
barsPerDay = 480;
|
|
break;
|
|
case PERIOD_M4:
|
|
barsPerDay = 360;
|
|
break;
|
|
case PERIOD_M5:
|
|
barsPerDay = 288;
|
|
break;
|
|
case PERIOD_M6:
|
|
barsPerDay = 240;
|
|
break;
|
|
case PERIOD_M10:
|
|
barsPerDay = 144;
|
|
break;
|
|
case PERIOD_M12:
|
|
barsPerDay = 120;
|
|
break;
|
|
case PERIOD_M15:
|
|
barsPerDay = 96;
|
|
break;
|
|
case PERIOD_M20:
|
|
barsPerDay = 72;
|
|
break;
|
|
case PERIOD_M30:
|
|
barsPerDay = 48;
|
|
break;
|
|
case PERIOD_H1:
|
|
barsPerDay = 24;
|
|
break;
|
|
case PERIOD_H2:
|
|
barsPerDay = 12;
|
|
break;
|
|
case PERIOD_H3:
|
|
barsPerDay = 8;
|
|
break;
|
|
case PERIOD_H4:
|
|
barsPerDay = 6;
|
|
break;
|
|
case PERIOD_H6:
|
|
barsPerDay = 4;
|
|
break;
|
|
case PERIOD_H8:
|
|
barsPerDay = 3;
|
|
break;
|
|
case PERIOD_H12:
|
|
barsPerDay = 2;
|
|
break;
|
|
case PERIOD_D1:
|
|
barsPerDay = 1;
|
|
break;
|
|
default:
|
|
//Print("Error: Unsupported timeframe.");
|
|
return;
|
|
}
|
|
|
|
// Calculate the number of bars to look back
|
|
int barsToLookBack = kDays * barsPerDay;
|
|
|
|
// Ensure 'barsToLookBack' does not exceed the total number of bars
|
|
if(barsToLookBack > totalBars)
|
|
{
|
|
//Print("Error: 'kDays' exceeds the total number of days available in bars.");
|
|
return;
|
|
}
|
|
|
|
// Loop through the last 'barsToLookBack' bars, starting from the most recent
|
|
for(int i = 0; i < barsToLookBack; i++)
|
|
{
|
|
bool isHighest = true;
|
|
bool isLowest = true;
|
|
|
|
// Check the 'n' bars around the current bar
|
|
for(int j = 1; j <= n; j++)
|
|
{
|
|
if(i - j >= 0 && i + j < totalBars)
|
|
{
|
|
if(iHigh(_Symbol, timefram, i) <= iHigh(_Symbol, timefram, i - j) ||
|
|
iHigh(_Symbol, timefram, i) <= iHigh(_Symbol, timefram, i + j))
|
|
{
|
|
isHighest = false;
|
|
}
|
|
|
|
if(iLow(_Symbol, timefram, i) >= iLow(_Symbol, timefram, i - j) ||
|
|
iLow(_Symbol, timefram, i) >= iLow(_Symbol, timefram, i + j))
|
|
{
|
|
isLowest = false;
|
|
}
|
|
}
|
|
|
|
// If both conditions are false, break early
|
|
if(!isHighest && !isLowest)
|
|
break;
|
|
}
|
|
|
|
// Update the last highest high and lowest low if conditions are met
|
|
if(isHighest && iHigh(_Symbol, timefram, i) > lastHigh)
|
|
{
|
|
lastHigh = iHigh(_Symbol, timefram, i);
|
|
}
|
|
|
|
if(isLowest && iLow(_Symbol, timefram, i) < lastLow)
|
|
{
|
|
lastLow = iLow(_Symbol, timefram, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Function to calculate differences between consecutive elements |
|
|
//+------------------------------------------------------------------+
|
|
void DifferencesArray(const double &inputArray[], double &outputArray[])
|
|
{
|
|
int inputSize = ArraySize(inputArray);
|
|
|
|
// Ensure the output array has the correct size (one less than the input array)
|
|
ArrayResize(outputArray, inputSize - 1);
|
|
|
|
// Calculate the differences
|
|
for(int i = 0; i < inputSize - 1; i++)
|
|
{
|
|
outputArray[i] = inputArray[i] - inputArray[i+1];
|
|
}
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Function to draw horizontal lines at specified price levels |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void DrawHorizontalLines(double &priceLevels[], color lineColor = clrRed, int lineStyle = STYLE_SOLID, int lineWidth = 1)
|
|
{
|
|
int arraySize = ArraySize(priceLevels);
|
|
//Print("Array Size: ", arraySize); // Debugging: Print the size of the array
|
|
|
|
// Clear existing horizontal lines with the naming pattern "HL_"
|
|
for(int i = ObjectsTotal(0) - 1; i >= 0; i--)
|
|
{
|
|
string name = ObjectName(0,i);
|
|
if(StringFind(name, "HL_") == 0) // If the name starts with "HL_"
|
|
{
|
|
ObjectDelete(0, name);
|
|
}
|
|
}
|
|
|
|
for(int i = 0; i < arraySize; i++)
|
|
{
|
|
// Normalize the price level
|
|
double normalizedPrice = NormalizeDouble(priceLevels[i], _Digits);
|
|
|
|
// Create a unique name for each line
|
|
string lineName = ("HL_" + DoubleToString(normalizedPrice, _Digits));
|
|
|
|
// Check if the line already exists, if not, create it
|
|
if(ObjectFind(0, lineName) == -1)
|
|
{
|
|
// Create a horizontal line
|
|
if(ObjectCreate(0, lineName, OBJ_HLINE, 0, 0, normalizedPrice))
|
|
{
|
|
//Print("Created line: ", lineName, " at price level: ", normalizedPrice); // Debugging: Confirm line creation
|
|
|
|
// Set line properties
|
|
ObjectSetInteger(0, lineName, OBJPROP_COLOR, lineColor);
|
|
ObjectSetInteger(0, lineName, OBJPROP_STYLE, lineStyle);
|
|
ObjectSetInteger(0, lineName, OBJPROP_WIDTH, lineWidth);
|
|
ObjectSetDouble(0, lineName, OBJPROP_PRICE, normalizedPrice);
|
|
}
|
|
else
|
|
{
|
|
int errorcode = GetLastError();
|
|
//Print("Failed to create line: ", lineName, " Error: ", errorcode);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void DrawTwoHorizontalLines(double priceLevel1, double priceLevel2, color lineColor1 = clrBlue, color lineColor2 = clrGreen, int lineStyle = STYLE_SOLID, int lineWidth = 1, string comment = NULL)
|
|
{
|
|
// Normalize the price levels
|
|
double normalizedPrice1 = NormalizeDouble(priceLevel1, _Digits);
|
|
double normalizedPrice2 = NormalizeDouble(priceLevel2, _Digits);
|
|
|
|
// Clear existing horizontal lines with the naming pattern "HL_"
|
|
for(int i = ObjectsTotal(0) - 1; i >= 0; i--)
|
|
{
|
|
string name = ObjectName(0, i);
|
|
if(StringFind(name, "HL__") == 0) // If the name starts with "HL_"
|
|
{
|
|
ObjectDelete(0, name);
|
|
}
|
|
}
|
|
|
|
// Create a unique name for each line
|
|
string lineName1 = "HL__Below" + comment;
|
|
string lineName2 = "HL__Above" + comment;
|
|
|
|
// Check if the first line already exists, if not, create it
|
|
if(ObjectFind(0, lineName1) == -1)
|
|
{
|
|
if(ObjectCreate(0, lineName1, OBJ_HLINE, 0, 0, normalizedPrice1))
|
|
{
|
|
//Print("Created line: ", lineName1, " at price level: ", normalizedPrice1); // Debugging: Confirm line creation
|
|
|
|
// Set line properties
|
|
ObjectSetInteger(0, lineName1, OBJPROP_COLOR, lineColor1);
|
|
ObjectSetInteger(0, lineName1, OBJPROP_STYLE, lineStyle);
|
|
ObjectSetInteger(0, lineName1, OBJPROP_WIDTH, lineWidth);
|
|
ObjectSetDouble(0, lineName1, OBJPROP_PRICE, normalizedPrice1);
|
|
}
|
|
else
|
|
{
|
|
int errorcode = GetLastError();
|
|
//Print("Failed to create line: ", lineName1, " Error: ", errorcode);
|
|
}
|
|
}
|
|
|
|
// Check if the second line already exists, if not, create it
|
|
if(ObjectFind(0, lineName2) == -1)
|
|
{
|
|
if(ObjectCreate(0, lineName2, OBJ_HLINE, 0, 0, normalizedPrice2))
|
|
{
|
|
//Print("Created line: ", lineName2, " at price level: ", normalizedPrice2); // Debugging: Confirm line creation
|
|
|
|
// Set line properties
|
|
ObjectSetInteger(0, lineName2, OBJPROP_COLOR, lineColor2);
|
|
ObjectSetInteger(0, lineName2, OBJPROP_STYLE, lineStyle);
|
|
ObjectSetInteger(0, lineName2, OBJPROP_WIDTH, lineWidth);
|
|
ObjectSetDouble(0, lineName2, OBJPROP_PRICE, normalizedPrice2);
|
|
}
|
|
else
|
|
{
|
|
int errorcode = GetLastError();
|
|
Print("Failed to create line: ", lineName2, " Error: ", errorcode);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Function to find indices where a given value is between two |
|
|
//| consecutive elements in a double array |
|
|
//+------------------------------------------------------------------+
|
|
int FindIndices(double value, double &arr[], int &index1, int &index2)
|
|
{
|
|
int size = ArraySize(arr);
|
|
|
|
// Check if the array has at least two elements
|
|
if(size < 2)
|
|
{
|
|
Print("Array must have at least two elements.");
|
|
// return -1; // Return -1 to indicate an error
|
|
}
|
|
|
|
// Iterate through the array to find the indices
|
|
for(int i = 0; i < size - 1; i++)
|
|
{
|
|
if((value > arr[i] && value < arr[i+1]) || (value < arr[i] && value > arr[i+1]))
|
|
{
|
|
index1 = i;
|
|
index2 = i + 1;
|
|
return 0; // Return 0 to indicate success
|
|
}
|
|
}
|
|
|
|
// If no such indices are found
|
|
Print("No indices found where the value is between two consecutive elements.");
|
|
return -1; // Return -1 to indicate failure
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
int ArrayPeaks(const double &array[], int &peaks[], double min_distance)
|
|
{
|
|
int array_size = ArraySize(array);
|
|
if(array_size < LevelsToCheck)
|
|
return 0; // Return 0 if the array is too small to have peaks
|
|
|
|
int peak_count = 0;
|
|
ArrayResize(peaks, array_size); // Resize peaks array to the maximum possible size initially
|
|
|
|
// Iterate through the array to find peaks
|
|
for(int i = (int)(LevelsToCheck/2); i < array_size - (int)(LevelsToCheck/2); i++) // Start from level index and end at array_size - level index
|
|
{
|
|
bool is_peak = true;
|
|
// Check if the current element is greater than the elements in the range of indices around it
|
|
for(int j = i - (int)(LevelsToCheck/2); j <= i + (int)(LevelsToCheck/2); j++)
|
|
{
|
|
if(j != i && array[i] <= array[j])
|
|
{
|
|
is_peak = false;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if(is_peak)
|
|
{
|
|
bool is_far_enough = true;
|
|
for(int j = 0; j < peak_count; j++)
|
|
{
|
|
if(MathAbs(i - peaks[j]) < min_distance)
|
|
{
|
|
is_far_enough = false;
|
|
break;
|
|
}
|
|
}
|
|
if(is_far_enough)
|
|
{
|
|
peaks[peak_count] = i;
|
|
peak_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
ArrayResize(peaks, peak_count); // Resize peaks array to the actual number of peaks found
|
|
return peak_count; // Return the number of peaks found
|
|
}
|
|
|
|
// create a function that is given a price level array and a volume array and finds the high and low levels where 70% (adjustable) of the volume took place aka value area. it modifies two variables valHigh and valLow
|
|
|
|
// To create a function in MQL5 that calculates the value area where a specified percentage of the volume took place, you can follow these steps. We'll define a function that takes in a price level array, a volume array, and the percentage of volume to consider for the value area. The function will then modify two variables, `valHigh` and `valLow`, to represent the high and low levels of this value area.
|
|
//
|
|
// Here's a sample implementation:
|
|
//
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CalculateValueArea(const double &priceLevels[], const double &volumes[], double &valAreaHigh, double &valAreaLow, double percentage = 70.0)
|
|
{
|
|
int size = ArraySize(volumes);
|
|
if(size <= 0 || size != ArraySize(priceLevels))
|
|
{
|
|
Print("Array Issue: Price Levels and Volumes don't match or are empty.");
|
|
return;
|
|
}
|
|
|
|
// Calculate total volume
|
|
double totalVolume = 0.0;
|
|
for(int i = 0; i < size; i++)
|
|
{
|
|
totalVolume += volumes[i];
|
|
}
|
|
|
|
// Target volume for the value area
|
|
double targetVolume = totalVolume * (percentage / 100.0);
|
|
|
|
// Find the POC
|
|
int pocIndex = 0;
|
|
double maxVolume = volumes[0];
|
|
for(int i = 1; i < size; i++)
|
|
{
|
|
if(volumes[i] > maxVolume)
|
|
{
|
|
maxVolume = volumes[i];
|
|
pocIndex = i;
|
|
}
|
|
}
|
|
|
|
// Initialize the value area with the POC
|
|
valAreaLow = priceLevels[pocIndex];
|
|
valAreaHigh = priceLevels[pocIndex];
|
|
double cumulativeVolume = maxVolume;
|
|
|
|
// Expand the value area around the POC
|
|
int leftIndex = pocIndex - 1;
|
|
int rightIndex = pocIndex + 1;
|
|
|
|
while(cumulativeVolume < targetVolume)
|
|
{
|
|
// Consider both directions and choose the one with larger volume
|
|
if(leftIndex >= 0 && (rightIndex >= size || volumes[leftIndex] >= volumes[rightIndex]))
|
|
{
|
|
cumulativeVolume += volumes[leftIndex];
|
|
valAreaLow = priceLevels[leftIndex];
|
|
leftIndex--;
|
|
}
|
|
else
|
|
if(rightIndex < size)
|
|
{
|
|
cumulativeVolume += volumes[rightIndex];
|
|
valAreaHigh = priceLevels[rightIndex];
|
|
rightIndex++;
|
|
}
|
|
else
|
|
{
|
|
// No more price levels to consider
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// function to extract hour from TimeCurrent() without using TimeHour()
|
|
|
|
// In MQL5, if you want to extract the hour from the current time without using the built-in `TimeHour()` function, you can do so by using the `TimeToStruct()` function. This function converts a `datetime` value into a `MqlDateTime` structure, which contains various components of the time, including the hour. Here is how you can implement this:
|
|
//
|
|
|
|
// Function to extract the hour from the current time
|
|
int GetHour(datetime time)
|
|
{
|
|
// Get the current time
|
|
|
|
// Define a structure to hold the date and time components
|
|
MqlDateTime timeStruct;
|
|
|
|
// Convert the current time into the MqlDateTime structure
|
|
TimeToStruct(time, timeStruct);
|
|
|
|
// Return the hour component from the structure
|
|
return timeStruct.hour;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
int GetMinute(datetime time)
|
|
{
|
|
// Get the current time
|
|
|
|
// Define a structure to hold the date and time components
|
|
MqlDateTime timeStruct;
|
|
|
|
// Convert the current time into the MqlDateTime structure
|
|
TimeToStruct(time, timeStruct);
|
|
|
|
// Return the hour component from the structure
|
|
return timeStruct.min;
|
|
}
|
|
|
|
//
|
|
// In this example:
|
|
//
|
|
// - We define a function `GetCurrentHour()` that retrieves the hour from the current server time.
|
|
// - We use `TimeCurrent()` to get the current time as a `datetime` value.
|
|
// - `MqlDateTime timeStruct;` is defined to hold the date and time components.
|
|
// - `TimeToStruct(currentTime, timeStruct);` converts the `datetime` into `MqlDateTime`, populating `timeStruct`.
|
|
// - We then simply return `timeStruct.hour` to get the hour component.
|
|
//
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Volume Distributuion |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CalculateVolumeDistribution(int Days, double levelStep, datetime startTime, datetime endTime, double &volumeArray[], double &priceLevelArray[])
|
|
{
|
|
// Determine the number of bars in a full day
|
|
int barsInFullDay = 1440; // 1440 minutes in a day
|
|
int barsInDays = Days * barsInFullDay;
|
|
|
|
// Calculate the number of bars that have elapsed today
|
|
datetime currentTime = TimeCurrent();
|
|
datetime startOfDay = currentTime - (currentTime % 86400); // Start of the current day
|
|
int barsElapsedToday = (int)((currentTime - startOfDay) / 60); // Convert seconds to minutes
|
|
|
|
// Total lookback bars
|
|
int lookbackBars = barsInDays + barsElapsedToday;
|
|
|
|
// Initialize variables to store the lowest low and highest high
|
|
double lowestLow = DBL_MAX;
|
|
double highestHigh = -DBL_MAX;
|
|
|
|
// Arrays to store the data
|
|
double lowArray[];
|
|
double highArray[];
|
|
long volumeArrayRaw[];
|
|
datetime timeArray[];
|
|
|
|
// Copy the data for the specified number of bars
|
|
if(CopyLow(_Symbol, PERIOD_M1, 0, lookbackBars, lowArray) <= 0 ||
|
|
CopyHigh(_Symbol, PERIOD_M1, 0, lookbackBars, highArray) <= 0 ||
|
|
CopyTickVolume(_Symbol, PERIOD_M1, 0, lookbackBars, volumeArrayRaw) <= 0 ||
|
|
CopyTime(_Symbol, PERIOD_M1, 0, lookbackBars, timeArray) <= 0)
|
|
{
|
|
Print("Error copying data");
|
|
return;
|
|
}
|
|
|
|
// Convert startTime and endTime to MqlDateTime
|
|
MqlDateTime startDT, endDT;
|
|
TimeToStruct(startTime, startDT);
|
|
TimeToStruct(endTime, endDT);
|
|
|
|
// Loop through the lookback period to find the lowest low and highest high within the specified interval
|
|
for(int i = 0; i < lookbackBars; i++)
|
|
{
|
|
MqlDateTime currentDT;
|
|
TimeToStruct(timeArray[i], currentDT);
|
|
|
|
// Check if the current time is within the specified time interval
|
|
if((currentDT.hour < startDT.hour || (currentDT.hour == startDT.hour && currentDT.min < startDT.min)) ||
|
|
(currentDT.hour > endDT.hour || (currentDT.hour == endDT.hour && currentDT.min > endDT.min)))
|
|
continue;
|
|
|
|
double low = lowArray[i];
|
|
double high = highArray[i];
|
|
|
|
if(low < lowestLow)
|
|
lowestLow = low;
|
|
if(high > highestHigh)
|
|
highestHigh = high;
|
|
}
|
|
//Print("Lowest Level: ", lowestLow);
|
|
//Print("Highest Level: ", highestHigh);
|
|
|
|
// Calculate the number of levels
|
|
int numberOfLevels = int((highestHigh - lowestLow) / levelStep) + 1;
|
|
|
|
// Resize the arrays to hold the data
|
|
ArrayResize(volumeArray, numberOfLevels);
|
|
ArrayResize(priceLevelArray, numberOfLevels);
|
|
|
|
// Initialize arrays
|
|
for(int i = 0; i < numberOfLevels; i++)
|
|
{
|
|
volumeArray[i] = 0;
|
|
priceLevelArray[i] = lowestLow + i * levelStep;
|
|
}
|
|
|
|
// Aggregate volume for each level within the specified interval
|
|
for(int i = 0; i < lookbackBars; i++)
|
|
{
|
|
MqlDateTime currentDT;
|
|
TimeToStruct(timeArray[i], currentDT);
|
|
|
|
// Check if the current time is within the specified time interval
|
|
if((currentDT.hour < startDT.hour || (currentDT.hour == startDT.hour && currentDT.min < startDT.min)) ||
|
|
(currentDT.hour > endDT.hour || (currentDT.hour == endDT.hour && currentDT.min > endDT.min)))
|
|
continue;
|
|
|
|
double low = lowArray[i];
|
|
double high = highArray[i];
|
|
double volume = (double)volumeArrayRaw[i];
|
|
|
|
// Determine which levels the current bar's range covers
|
|
int startLevel = int((low - lowestLow) / levelStep);
|
|
int endLevel = int((high - lowestLow) / levelStep);
|
|
|
|
// Distribute volume across the levels covered by the bar
|
|
for(int level = startLevel; level <= endLevel; level++)
|
|
{
|
|
if(level >= 0 && level < numberOfLevels)
|
|
{
|
|
volumeArray[level] += volume;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| session flag |
|
|
//+------------------------------------------------------------------+
|
|
bool Flag(string comment)
|
|
{
|
|
datetime time = TimeCurrent();
|
|
if(time >= asianStart && time <= asianEnd && comment == "AS")
|
|
{
|
|
return true;
|
|
Print("Asian Sesh");
|
|
}
|
|
if(time >= londonStart && time <= londonEnd && comment == "LD")
|
|
{
|
|
return true;
|
|
Print("London Sesh");
|
|
}
|
|
if(time >= nyStart && time <= nyEnd && comment == "NY")
|
|
{
|
|
return true;
|
|
Print("NY Sesh");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
int BuyPositionsCount()
|
|
{
|
|
int Buys = 0;
|
|
for(int i = PositionsTotal()-1; i>=0; i--)
|
|
{
|
|
PositionGetTicket(i);
|
|
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetString(POSITION_SYMBOL) == _Symbol)
|
|
{
|
|
Buys++;
|
|
}
|
|
}
|
|
return Buys;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
int SellPositionsCount()
|
|
{
|
|
int Sells = 0;
|
|
for(int i = PositionsTotal()-1; i>=0; i--)
|
|
{
|
|
PositionGetTicket(i);
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetString(POSITION_SYMBOL) == _Symbol)
|
|
{
|
|
Sells++;
|
|
}
|
|
}
|
|
return Sells;
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool OtherBuyPosition()
|
|
{
|
|
bool other = false;
|
|
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
|
|
if(PositionSelectByTicket(ticket))
|
|
{
|
|
double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
|
|
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
long positionType = PositionGetInteger(POSITION_TYPE);
|
|
|
|
if(MathAbs(currentPrice - openPrice) < Grid_Step * _Point && positionType == POSITION_TYPE_BUY)
|
|
{
|
|
other = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return other;
|
|
}
|
|
|
|
//write same function for orders instead of positions
|
|
//+------------------------------------------------------------------+
|
|
// To create a similar function for orders instead of positions in MQL5, you can use the `OrdersTotal()`, `OrderGetTicket()`, and `OrderSelect()` functions. Here's how you can write a function that checks for other buy orders with similar conditions:
|
|
//
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool OtherBuyOrder()
|
|
{
|
|
bool other = false;
|
|
|
|
for(int i = OrdersTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = OrderGetTicket(i);
|
|
|
|
if(OrderSelect(ticket))
|
|
{
|
|
double currentPrice = OrderGetDouble(ORDER_PRICE_CURRENT);
|
|
double openPrice = OrderGetDouble(ORDER_PRICE_OPEN);
|
|
long orderType = OrderGetInteger(ORDER_TYPE);
|
|
|
|
if(MathAbs(currentPrice - openPrice) < Grid_Step * _Point && orderType == ORDER_TYPE_BUY)
|
|
{
|
|
other = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return other;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool OtherSellPosition()
|
|
{
|
|
bool other = false;
|
|
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
|
|
if(PositionSelectByTicket(ticket))
|
|
{
|
|
double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
|
|
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
long positionType = PositionGetInteger(POSITION_TYPE);
|
|
|
|
if(MathAbs(currentPrice - openPrice) < Grid_Step * _Point && positionType == POSITION_TYPE_SELL)
|
|
{
|
|
other = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return other;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool OtherSellOrder()
|
|
{
|
|
bool other = false;
|
|
|
|
for(int i = OrdersTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = OrderGetTicket(i);
|
|
|
|
if(OrderSelect(ticket))
|
|
{
|
|
double currentPrice = OrderGetDouble(ORDER_PRICE_CURRENT);
|
|
double openPrice = OrderGetDouble(ORDER_PRICE_OPEN);
|
|
long orderType = OrderGetInteger(ORDER_TYPE);
|
|
|
|
if(MathAbs(currentPrice - openPrice) < Grid_Step * _Point && orderType == ORDER_TYPE_SELL)
|
|
{
|
|
other = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return other;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CloseWinning()
|
|
{
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
if(PositionSelect(PositionGetSymbol(i)))
|
|
{
|
|
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double currentPrice = SymbolInfoDouble(PositionGetSymbol(i), SYMBOL_BID);
|
|
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
long ticket = PositionGetInteger(POSITION_TICKET);
|
|
|
|
if((type == POSITION_TYPE_BUY && currentPrice > openPrice) ||
|
|
(type == POSITION_TYPE_SELL && currentPrice < openPrice))
|
|
{
|
|
trade.PositionClose(ticket);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CloseLosing()
|
|
{
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
if(PositionSelect(PositionGetSymbol(i)))
|
|
{
|
|
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double currentPrice = SymbolInfoDouble(PositionGetSymbol(i), SYMBOL_BID);
|
|
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
long ticket = PositionGetInteger(POSITION_TICKET);
|
|
|
|
if((type == POSITION_TYPE_BUY && currentPrice < openPrice) ||
|
|
(type == POSITION_TYPE_SELL && currentPrice > openPrice))
|
|
{
|
|
trade.PositionClose(ticket);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// function that cancels other pending orders when one pending order is hit
|
|
|
|
// In MQL5, you can create a function to cancel other pending orders when one pending order is hit by utilizing the `OnTradeTransaction` event handler. This event is triggered whenever a trade transaction occurs, such as when a pending order is executed. Here's a basic example of how you might implement such a function:
|
|
//
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| mean of array |
|
|
//+------------------------------------------------------------------+
|
|
long ArrayAverage(const long &array[])
|
|
{
|
|
|
|
int size = ArraySize(array);
|
|
if(size <= 0)
|
|
return 0.0; // Return 0 if the array size is zero or negative
|
|
|
|
long sum = 0.0;
|
|
for(int i = 0; i < size; i++)
|
|
{
|
|
sum += array[i];
|
|
}
|
|
return sum / size;
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update stoploss |
|
|
//+------------------------------------------------------------------+
|
|
void UpdateTrailingStopLoss(double trailingPoints)
|
|
{
|
|
// Check if there are any open positions
|
|
int totalPositions = PositionsTotal();
|
|
if(totalPositions <= 0)
|
|
return;
|
|
|
|
// Get current bid and ask prices
|
|
// Loop through all open positions
|
|
for(int i = 0; i < totalPositions; i++)
|
|
{
|
|
// Get position ticket
|
|
ulong positionTicket = PositionGetTicket(i);
|
|
|
|
// Get position details
|
|
if(PositionSelectByTicket(positionTicket))
|
|
{
|
|
double positionPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double positionStopLoss = PositionGetDouble(POSITION_SL);
|
|
ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
|
|
// Calculate new stop loss based on trailing points
|
|
double newStopLoss;
|
|
if(positionType == POSITION_TYPE_BUY)
|
|
{
|
|
newStopLoss = (bid + positionPrice)/2;
|
|
if(newStopLoss > positionStopLoss)
|
|
{
|
|
// Update stop loss for buy position
|
|
TradePositionModify(positionTicket, newStopLoss, PositionGetDouble(POSITION_TP));
|
|
}
|
|
}
|
|
else
|
|
if(positionType == POSITION_TYPE_SELL)
|
|
{
|
|
newStopLoss = (ask + positionPrice)/2;
|
|
if(newStopLoss < positionStopLoss)
|
|
{
|
|
// Update stop loss for sell position
|
|
TradePositionModify(positionTicket, newStopLoss, PositionGetDouble(POSITION_TP));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void TrailingSL()
|
|
{
|
|
// Check if there are any open positions
|
|
int totalPositions = PositionsTotal();
|
|
if(totalPositions <= 0)
|
|
return;
|
|
|
|
// Get current bid and ask prices
|
|
// Loop through all open positions
|
|
for(int i = 0; i < totalPositions; i++)
|
|
{
|
|
// Get position ticket
|
|
ulong positionTicket = PositionGetTicket(i);
|
|
|
|
// Get position details
|
|
if(PositionSelectByTicket(positionTicket))
|
|
{
|
|
double newStopLoss;
|
|
//double newTakeProfit;
|
|
ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
|
|
if(positionType == POSITION_TYPE_BUY
|
|
//&& bid > Grid_Step*_Point + PositionGetDouble(POSITION_PRICE_OPEN)
|
|
&& bid > (PositionGetDouble(POSITION_TP) + PositionGetDouble(POSITION_PRICE_OPEN))/2
|
|
&& (bid + PositionGetDouble(POSITION_PRICE_OPEN))/2 > PositionGetDouble(POSITION_SL)
|
|
)
|
|
{
|
|
newStopLoss = (ask + PositionGetDouble(POSITION_PRICE_OPEN))/2;
|
|
//newTakeProfit = HlineAbove;
|
|
|
|
TradePositionModify(positionTicket, newStopLoss, PositionGetDouble(POSITION_TP));
|
|
|
|
}
|
|
|
|
else
|
|
if(positionType == POSITION_TYPE_SELL
|
|
//&& ask < PositionGetDouble(POSITION_PRICE_OPEN) - Grid_Step*_Point
|
|
&& ask < (PositionGetDouble(POSITION_TP) + PositionGetDouble(POSITION_PRICE_OPEN))/2
|
|
&& (ask + PositionGetDouble(POSITION_PRICE_OPEN))/2 < PositionGetDouble(POSITION_SL)
|
|
)
|
|
{
|
|
newStopLoss = (bid + PositionGetDouble(POSITION_PRICE_OPEN))/2;
|
|
//newTakeProfit = HlineBelow;
|
|
|
|
TradePositionModify(positionTicket, newStopLoss, PositionGetDouble(POSITION_TP));
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//update stoploss to become a given double value that is normalized and position modified using the following function
|
|
//+------------------------------------------------------------------+
|
|
//| Function to modify a trade position |
|
|
//+------------------------------------------------------------------+
|
|
bool TradePositionModify(ulong ticket, double newStopLoss, double takeProfit)
|
|
{
|
|
MqlTradeRequest request;
|
|
MqlTradeResult result;
|
|
ZeroMemory(request);
|
|
ZeroMemory(result);
|
|
|
|
request.action = TRADE_ACTION_SLTP;
|
|
request.position = ticket;
|
|
request.symbol = _Symbol;
|
|
request.sl = newStopLoss;
|
|
request.tp = takeProfit;
|
|
|
|
if(!OrderSend(request, result))
|
|
{
|
|
Print("Error modifying position: ", result.retcode);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// function that populates a 2 d array with price and volume data from the M1 period.
|
|
// for every minute
|
|
// To create a function that populates a 2D array with price and volume data from the M1 period for every minute, you can use the `CopyRates` function in MQL5. This function retrieves historical price data, including open, high, low, close prices, and volume. Here’s how you can implement such a function:
|
|
//
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Function to populate a 2D array with price and volume data |
|
|
//| from the M1 period for every minute |
|
|
//+------------------------------------------------------------------+
|
|
void PopulatePriceVolumeData(datetime startTime, datetime endTime, double &dataArray[][5])
|
|
{
|
|
// Calculate the number of minutes (bars) between startTime and endTime
|
|
int barsCount = (int)((endTime - startTime) / 60);
|
|
|
|
// Resize the 2D array to hold the data
|
|
ArrayResize(dataArray, barsCount);
|
|
|
|
// Create an array to hold the rates data
|
|
MqlRates rates[];
|
|
|
|
// Copy the rates data from the M1 period
|
|
if(CopyRates(_Symbol, PERIOD_M1, startTime, barsCount, rates) <= 0)
|
|
{
|
|
Print("Error copying rates data");
|
|
return;
|
|
}
|
|
|
|
// Populate the 2D array with price and volume data
|
|
for(int i = 0; i < barsCount; i++)
|
|
{
|
|
dataArray[i][0] = rates[i].open; // Open price
|
|
dataArray[i][1] = rates[i].high; // High price
|
|
dataArray[i][2] = rates[i].low; // Low price
|
|
dataArray[i][3] = rates[i].close; // Close price
|
|
dataArray[i][4] = (double)rates[i].tick_volume; // Volume
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckMoneyForTrade(string symb,double lots,ENUM_ORDER_TYPE type)
|
|
{
|
|
//--- Getting the opening price
|
|
MqlTick mqltick;
|
|
SymbolInfoTick(symb,mqltick);
|
|
double price=mqltick.ask;
|
|
if(type==ORDER_TYPE_SELL)
|
|
price=mqltick.bid;
|
|
//--- values of the required and free margin
|
|
double margin,free_margin=AccountInfoDouble(ACCOUNT_MARGIN_FREE);
|
|
//--- call of the checking function
|
|
if(!OrderCalcMargin(type,symb,lots,price,margin))
|
|
{
|
|
//--- something went wrong, report and return false
|
|
Print("Error in ",__FUNCTION__," code=",GetLastError());
|
|
return(false);
|
|
}
|
|
//--- if there are insufficient funds to perform the operation
|
|
if(margin>free_margin)
|
|
{
|
|
//--- report the error and return false
|
|
Print("Not enough money for ",EnumToString(type)," ",lots," ",symb," Error code=",GetLastError());
|
|
return(false);
|
|
}
|
|
//--- checking successful
|
|
return(true);
|
|
}// The provided code is an MQL5 script for a MetaTrader 5 Expert Advisor (EA) named "ValueAreaEA". This EA is designed to trade based on volume distribution levels, also known as value areas, calculated over a specified lookback period. Here's a breakdown of the key components and functionalities of the script:
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool IsMarketOpen()
|
|
{
|
|
|
|
// Check if the trading status indicates that the market is open
|
|
if(TimeCurrent() > SymbolInfoInteger(_Symbol,SYMBOL_TIME) && TimeCurrent() < SymbolInfoInteger(_Symbol,SYMBOL_START_TIME))
|
|
{
|
|
return false; // Market is open
|
|
}
|
|
else
|
|
{
|
|
return true; // Market is closed
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check the correctness of the order volume |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckVolumeValue(double volume)
|
|
{
|
|
|
|
string description;
|
|
//--- minimal allowed volume for trade operations
|
|
double min_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
|
|
if(volume<min_volume)
|
|
{
|
|
description=StringFormat("Volume is less than the minimal allowed SYMBOL_VOLUME_MIN=%.2f",min_volume);
|
|
return(false);
|
|
}
|
|
|
|
//--- maximal allowed volume of trade operations
|
|
double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
|
|
if(volume>max_volume)
|
|
{
|
|
description=StringFormat("Volume is greater than the maximal allowed SYMBOL_VOLUME_MAX=%.2f",max_volume);
|
|
return(false);
|
|
}
|
|
|
|
//--- get minimal step of volume changing
|
|
double volume_step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP);
|
|
|
|
int ratio=(int)MathRound(volume/volume_step);
|
|
if(MathAbs(ratio*volume_step-volume)>0.0000001)
|
|
{
|
|
description=StringFormat("Volume is not a multiple of the minimal step SYMBOL_VOLUME_STEP=%.2f, the closest correct volume is %.2f",
|
|
volume_step,ratio*volume_step);
|
|
return(false);
|
|
}
|
|
description="Correct volume value";
|
|
return(true);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
bool CheckStopLoss_Takeprofit(ENUM_ORDER_TYPE type,double SL,double TP)
|
|
{
|
|
//--- get the SYMBOL_TRADE_STOPS_LEVEL level
|
|
int stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
|
|
if(stops_level!=0)
|
|
{
|
|
PrintFormat("SYMBOL_TRADE_STOPS_LEVEL=%d: StopLoss and TakeProfit must"+
|
|
" not be nearer than %d points from the closing price",stops_level,stops_level);
|
|
}
|
|
//---
|
|
bool SL_check=false,TP_check=false;
|
|
//--- check only two order types
|
|
switch(type)
|
|
{
|
|
//--- Buy operation
|
|
case ORDER_TYPE_BUY:
|
|
{
|
|
//--- check the StopLoss
|
|
SL_check=(bid-SL>stops_level*_Point);
|
|
if(!SL_check)
|
|
PrintFormat("For order %s StopLoss=%.5f must be less than %.5f"+
|
|
" (Bid=%.5f - SYMBOL_TRADE_STOPS_LEVEL=%d points)",
|
|
EnumToString(type),SL,bid-stops_level*_Point,bid,stops_level);
|
|
//--- check the TakeProfit
|
|
TP_check=(TP-bid>stops_level*_Point);
|
|
if(!TP_check)
|
|
PrintFormat("For order %s TakeProfit=%.5f must be greater than %.5f"+
|
|
" (Bid=%.5f + SYMBOL_TRADE_STOPS_LEVEL=%d points)",
|
|
EnumToString(type),TP,bid+stops_level*_Point,bid,stops_level);
|
|
//--- return the result of checking
|
|
return(SL_check&&TP_check);
|
|
}
|
|
//--- Sell operation
|
|
case ORDER_TYPE_SELL:
|
|
{
|
|
//--- check the StopLoss
|
|
SL_check=(SL-ask>stops_level*_Point);
|
|
if(!SL_check)
|
|
PrintFormat("For order %s StopLoss=%.5f must be greater than %.5f "+
|
|
" (Ask=%.5f + SYMBOL_TRADE_STOPS_LEVEL=%d points)",
|
|
EnumToString(type),SL,ask+stops_level*_Point,ask,stops_level);
|
|
//--- check the TakeProfit
|
|
TP_check=(ask-TP>stops_level*_Point);
|
|
if(!TP_check)
|
|
PrintFormat("For order %s TakeProfit=%.5f must be less than %.5f "+
|
|
" (Ask=%.5f - SYMBOL_TRADE_STOPS_LEVEL=%d points)",
|
|
EnumToString(type),TP,ask-stops_level*_Point,ask,stops_level);
|
|
//--- return the result of checking
|
|
return(TP_check&&SL_check);
|
|
}
|
|
break;
|
|
}
|
|
//--- a slightly different function is required for pending orders
|
|
return false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
// The provided MQL5 code is quite comprehensive, indicating that the writer has a solid understanding of several key aspects of MQL5 programming. Here's an analysis of the skills the writer possesses and what they might need to develop further to create a consistently profitable Expert Advisor (EA):
|
|
//
|
|
// ### Skills the Writer Has:
|
|
// 1. **Basic MQL5 Syntax and Structure:**
|
|
// - Understanding of the basic structure of an MQL5 script, including event handlers like `OnInit()`, `OnDeinit()`, and `OnTick()`.
|
|
//
|
|
// 2. **Using Preprocessor Directives and Includes:**
|
|
// - Use of `#define` for constants and `#include` for importing necessary libraries, such as `Trade\Trade.mqh` for trading operations.
|
|
//
|
|
// 3. **Handling Arrays:**
|
|
// - Proficiency in using arrays to store and manipulate data, such as price levels, volume data, and technical indicators.
|
|
//
|
|
// 4. **Technical Indicator Functions:**
|
|
// - Use of built-in MQL5 functions to calculate technical indicators like ATR, OBV, and RSI.
|
|
//
|
|
// 5. **Time and Date Manipulations:**
|
|
// - Competence in working with `datetime` and converting time using `TimeToStruct()`.
|
|
//
|
|
// 6. **Order and Position Management:**
|
|
// - Skills in handling trading orders and positions, including placing, modifying, and closing trades.
|
|
//
|
|
// 7. **Custom Function Creation:**
|
|
// - Ability to write custom utility functions for specific tasks, such as calculating moving averages, drawing on charts, and finding peaks in an array.
|
|
//
|
|
// 8. **Conditional Logic and Control Structures:**
|
|
// - Use of `if`, `for`, and `switch` statements to control the flow of the program based on conditions.
|
|
//
|
|
// ### Skills the Writer Needs to Develop:
|
|
// 1. **Risk Management:**
|
|
// - Implementing robust risk management strategies, such as dynamically calculating position sizes based on account balance, setting appropriate stop-loss levels, and managing leverage.
|
|
//
|
|
// 2. **Backtesting and Optimization:**
|
|
// - Proficiency in using the MetaTrader Strategy Tester for backtesting the EA with historical data, as well as optimizing parameters for better performance.
|
|
//
|
|
// 3. **Robust Error Handling:**
|
|
// - Implementing comprehensive error handling and logging to diagnose and fix potential issues during live trading.
|
|
//
|
|
// 4. **Advanced Market Analysis:**
|
|
// - Developing a deeper understanding of market dynamics and incorporating more advanced analysis methods, such as machine learning or sentiment analysis.
|
|
//
|
|
// 5. **Performance Monitoring and Adaptation:**
|
|
// - Creating mechanisms to monitor the performance of the EA in real-time and adapt strategies based on changing market conditions.
|
|
//
|
|
// 6. **Strategy Development:**
|
|
// - Focusing on developing and testing various trading strategies to identify ones that can achieve consistent profitability.
|
|
//
|
|
// 7. **Code Optimization:**
|
|
// - Improving code efficiency and minimizing computational overhead, especially for operations that are executed on every tick.
|
|
//
|
|
// By enhancing these areas, the writer could increase the likelihood of developing a consistently profitable EA. Additionally, continuous learning and adaptation to new trading methodologies and technological advancements in algorithmic trading would be beneficial.
|
|
//
|
|
// how would you correct the risk management in this file?
|
|
// To provide a precise correction to risk management code in an MQL5 file, I'll need to understand the existing logic and any specific issues you might be facing. However, I can offer some general advice on implementing and improving risk management in a trading algorithm using MQL5.
|
|
//
|
|
// Here's a basic framework for risk management you might consider incorporating or checking against your existing code:
|
|
//
|
|
// 1. **Determine Account Risk**: Define the percentage of your account balance that you are willing to risk per trade. A common rule is to risk 1-2% of your account balance on a single trade.
|
|
//
|
|
// 2. **Calculate Lot Size**: Use the account risk, stop loss distance, and pip value to calculate the appropriate lot size for the trade.
|
|
//
|
|
// 3. **Set Stop Loss and Take Profit**: Always use a stop loss to limit potential losses and a take profit to secure profits. These can be set based on technical analysis or a fixed risk-reward ratio.
|
|
//
|
|
// 4. **Check Margin Requirements**: Ensure that the trade complies with margin requirements to prevent over-leveraging.
|
|
//
|
|
// 5. **Monitor Maximum Drawdown**: Implement logic to stop trading if the drawdown exceeds a certain percentage of the account balance.
|
|
//
|
|
// 6. **Implement Position Sizing**: Adjust position size dynamically based on account equity or market conditions.
|
|
//
|
|
// Here is a simple example in MQL5 that incorporates some of these principles:
|
|
//
|
|
|
|
// Example parameters
|
|
double accountRiskPercent = 1.0; // Risk 1% of account balance
|
|
double stopLossPips = 50.0; // Stop loss in pips
|
|
double takeProfitPips = 100.0; // Take profit in pips
|
|
|
|
// Function to calculate the lot size
|
|
double CalculateLotSize()
|
|
{
|
|
double riskAmount = AccountInfoDouble(ACCOUNT_BALANCE) * accountRiskPercent / 100.0;
|
|
double pipValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
|
double lotSize = (riskAmount / (Grid_Step * pipValue)) * SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
|
|
return NormalizeDouble(lotSize, 2); // Normalize to 2 decimal places
|
|
}
|
|
|
|
// Example of placing a trade
|
|
void PlaceTrade()
|
|
{
|
|
double lotSize = CalculateLotSize();
|
|
double stopLossPrice = 0.0;
|
|
double takeProfitPrice = 0.0;
|
|
|
|
// Determine if the trade is a buy or sell
|
|
bool isBuy = true; // Example: set to true for buy, false for sell
|
|
|
|
if(isBuy)
|
|
{
|
|
stopLossPrice = ask - stopLossPips * _Point;
|
|
takeProfitPrice = ask + takeProfitPips * _Point;
|
|
|
|
// Place a buy order
|
|
trade.Buy(lotSize, _Symbol, ask, stopLossPrice, takeProfitPrice);
|
|
}
|
|
else
|
|
{
|
|
stopLossPrice = bid + stopLossPips * _Point;
|
|
takeProfitPrice = bid - takeProfitPips * _Point;
|
|
|
|
// Place a sell order
|
|
trade.Sell(lotSize, _Symbol, bid, stopLossPrice, takeProfitPrice);
|
|
}
|
|
}
|
|
|
|
// Example of checking maximum drawdown
|
|
bool CheckMaxDrawdown(double maxDrawdownPercent)
|
|
{
|
|
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
|
|
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
double drawdown = ((balance - equity) / balance) * 100.0;
|
|
return drawdown < maxDrawdownPercent;
|
|
}
|
|
//
|
|
//
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|