This commit is contained in:
zhutoutoutousan
2025-04-11 13:57:57 +08:00
parent 6f6b94d377
commit 4337eefaea
3 changed files with 520 additions and 1 deletions
+456
View File
@@ -0,0 +1,456 @@
//+------------------------------------------------------------------+
//| ScoringTrade.mq5 |
//| Generated by ChatGPT |
//| |
//+------------------------------------------------------------------+
#property strict
#include <Trade\Trade.mqh>
// Input parameters
input int MagicNumber = 42;
input int scoreThreshold = 5200; // Score threshold for trade entry
input int slopeThreshold = 93; // EMA slope threshold
input double maxScore = 7900; // Max score value for clamping
input int cooldownMinutes = 18; // Cooldown period in minutes (37 minutes)
input int tradeCooldownMinutes = 24; // Trade debounce cooldown period (5 minutes)
input ENUM_TIMEFRAMES emaTimeFrame = PERIOD_H1; // EMA Timeframe
input double delayClampAbsolute = 1690;
input int emaPeriod = 64; // EMA period
input double crossOverStep = 950;
input double slopeThresholdStep = 635;
input double emaDistanceStep = 150;
input double emaDecayStep = 0;
input double decayMultiplier = 0.08; // Decay multiplier
input double distanceThreshold = 28.5; // Set your distance threshold (adjust as necessary)
input double atrMultiplier = 7.6; // Multiplier for dynamic SL and TP calculation
input double TrailingStop = 5;
input bool ApplyTrailingStop = true;
input int maxCrossoverTrades = 4; // Maximum number of trades per crossover
input double max_drawdown = 0.1; // Maximum drawdown percentage
input bool resetCrossoverTradeOnDistance = false;
input int resetCrossoverNumber = 0;
input double minimumLotSize = 0.01;
input int maxTimeInPosition = 9;
input int tradeLengthThreshold = 98;
input int reverseTP = 32;
input int reverseLotSizeMultiplier = 15;
input int secondaryPositionHoldTime = 32;
// Global variables
int emaHandle; // EMA handle
double prevScore = 0; // Previous score
double currentScore = 0; // Current score
double emaPrevValue = 0; // Previous EMA value
double emaCurrentValue = 0; // Current EMA value
double emaSlope = 0; // EMA slope value
CTrade trade; // Trading object
datetime lastCrossoverTime = 0; // Time of last crossover
datetime lastTradeTime = 0; // Time of last trade
int crossoverTradeCount = 0; // Count of trades after each crossover
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
// Create EMA handle (e.g., 14-period EMA on the closing price)
emaHandle = iMA(Symbol(), emaTimeFrame, emaPeriod, 0, MODE_EMA, PRICE_CLOSE);
if (emaHandle == INVALID_HANDLE) {
Print("Failed to create EMA handle");
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
// Release the EMA handle
if (emaHandle != INVALID_HANDLE) {
ExpertRemove();
}
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
// Buffer to hold the EMA values
double emaBuffer[];
// Get dynamic lot size based on current balance and max drawdown
double lotSize = CalculateLotSize();
if(lotSize < minimumLotSize) {
lotSize = minimumLotSize;
}
// Get the current Ask and Bid prices
double Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
double Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
// Copy the last 2 EMA values (current and previous)
int copied = CopyBuffer(emaHandle, 0, 0, 2, emaBuffer);
if (copied < 2) {
Print("Failed to copy EMA values. Error code: ", GetLastError());
return;
}
// Get the current and previous EMA values
emaPrevValue = emaBuffer[1]; // Previous EMA value (index 1)
emaCurrentValue = emaBuffer[0]; // Current EMA value (index 0)
// Calculate the EMA slope (change in EMA values)
emaSlope = - (emaCurrentValue - emaPrevValue) * 100;
// Check for price action crossover with EMA
double closePrev = iClose(Symbol(), Period(), 1); // Close of previous bar
double closeCurr = iClose(Symbol(), Period(), 0); // Close of current bar
// Check if enough time has passed for the cooldown (cooldownMinutes)
if (TimeCurrent() - lastCrossoverTime >= cooldownMinutes * 60) {
if (closePrev < emaPrevValue && closeCurr > emaCurrentValue) { // Bullish crossover
Print("Bullish crossover");
currentScore += crossOverStep;
crossoverTradeCount = 0; // Reset trade count after new crossover
lastCrossoverTime = TimeCurrent(); // Update the last crossover time
}
else if (closePrev > emaPrevValue && closeCurr < emaCurrentValue) { // Bearish crossover
Print("Bearish crossover");
currentScore -= crossOverStep;
crossoverTradeCount = 0; // Reset trade count after new crossover
lastCrossoverTime = TimeCurrent(); // Update the last crossover time
}
}
// Check EMA slope
if (emaSlope > slopeThreshold) { // Positive slope
currentScore += slopeThresholdStep;
}
else if (emaSlope < -slopeThreshold) { // Negative slope
currentScore -= slopeThresholdStep;
}
else {
if (MathAbs(currentScore) > delayClampAbsolute) {
currentScore *= decayMultiplier;
}
}
if(ApplyTrailingStop) {
ApplyTrailingStop();
}
// Calculate distance to EMA and adjust score
double priceToEmaDistance = closeCurr - emaCurrentValue; // Distance between the current price and the EMA
if (MathAbs(priceToEmaDistance) > distanceThreshold) {
if (priceToEmaDistance > 0) { // Bullish (price above EMA)
currentScore += emaDistanceStep;
Print("Bullish distance score added. Price: ", closeCurr, " EMA: ", emaCurrentValue);
}
else if (priceToEmaDistance < 0) { // Bearish (price below EMA)
currentScore -= emaDistanceStep;
Print("Bearish distance score added. Price: ", closeCurr, " EMA: ", emaCurrentValue);
}
}
else {
if (currentScore > 0) {
currentScore -= emaDecayStep;
}
else {
currentScore += emaDecayStep;
}
}
// Close all positions if score crosses zero
if ((prevScore > 0 && currentScore <= 0) || (prevScore < 0 && currentScore >= 0)) {
Close_Position_MN(MagicNumber);
}
// Update the previous score
prevScore = currentScore;
if (crossoverTradeCount > maxCrossoverTrades) {
return;
}
// Debounce check: Ensure enough time has passed since the last trade
if (TimeCurrent() - lastTradeTime >= tradeCooldownMinutes * 60) {
// Calculate ATR (Average True Range) for stop loss calculation
double atrArray[];
int atrPeriod = 14; // ATR period (can be adjusted)
int copied = CopyBuffer(iATR(Symbol(), Period(), atrPeriod), 0, 0, 1, atrArray);
if (copied < 1) {
Print("Failed to get ATR values. Error code: ", GetLastError());
return;
}
// Get the current price (using Bid price)
double currentPrice = Bid;
// Get ATR value
double atrValue = atrArray[0]; // Latest ATR value
// Get the minimum stop level and freeze level for the symbol
int stopLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL);
int freezeLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_FREEZE_LEVEL);
// Calculate the minimum stop loss in price units (converted from pips)
double minStopLoss = stopLevel * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
double minFreezeLevel = freezeLevel * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
// Dynamic Stop Loss and Take Profit calculation based on ATR
double dynamicSL = atrValue * atrMultiplier;
double dynamicTP = atrValue * atrMultiplier;
// Adjust SL and TP if they are smaller than the minimum stop level
dynamicSL = MathMax(dynamicSL, minStopLoss);
dynamicTP = MathMax(dynamicTP, dynamicSL); // Ensure TP is at least the same as SL
// Trade logic based on the score
if (currentScore > scoreThreshold) { // Buy signal
if (PositionSelect(Symbol()) == false || !PositionGetInteger(POSITION_MAGIC) == MagicNumber && crossoverTradeCount < maxCrossoverTrades) {
Print("maxCrossover");
Print(crossoverTradeCount);
// Open buy position with dynamic SL and TP
trade.SetExpertMagicNumber(MagicNumber);
if (trade.Buy(lotSize, Symbol(), currentPrice, Bid - dynamicSL, 0)) {
Print("Buy order executed with score: ", currentScore);
crossoverTradeCount++; // Increment trade count
lastTradeTime = TimeCurrent(); // Update the last trade time
}
}
}
else if (currentScore < -scoreThreshold) { // Sell signal
if (PositionSelect(Symbol()) == false || !PositionGetInteger(POSITION_MAGIC) == MagicNumber && crossoverTradeCount < maxCrossoverTrades) {
Print("maxCrossover");
Print(crossoverTradeCount);
// Open sell position with dynamic SL and TP
trade.SetExpertMagicNumber(MagicNumber);
if (trade.Sell(lotSize, Symbol(), currentPrice, Ask + dynamicSL, 0)) {
Print("Sell order executed with score: ", currentScore);
crossoverTradeCount++; // Increment trade count
lastTradeTime = TimeCurrent(); // Update the last trade time
}
}
}
} else {
Print("Trade skipped due to debounce: ", currentScore);
}
// Check existing positions for profit and place reverse trade if needed
CheckPositions();
}
//+------------------------------------------------------------------+
//| Check existing positions for profit and place reverse trade if needed |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Check existing positions for duration and place reverse trade if needed |
//+------------------------------------------------------------------+
void CheckPositions() {
// Check if there are any open positions
if (PositionsTotal() > 0) {
// Check if there are exactly 2 open positions
if (PositionsTotal() == 2) {
for (int i = 0; i < PositionsTotal(); i++) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket)) {
datetime openTime = PositionGetInteger(POSITION_TIME);
int tradeLength = TimeCurrent() - openTime; // Trade duration in seconds
// Check if the trade has been open for more than the secondaryPositionHoldTime
if (tradeLength > secondaryPositionHoldTime * 60) { // Convert threshold to seconds
// Close all positions
CloseAllPositions();
Print("All positions closed due to exceeding secondaryPositionHoldTime");
return; // Exit the function after closing all positions
}
}
}
} else if (PositionsTotal() < 2) {
for (int i = 0; i < PositionsTotal(); i++) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket)) {
double profit = PositionGetDouble(POSITION_PROFIT);
datetime openTime = PositionGetInteger(POSITION_TIME);
int tradeLength = TimeCurrent() - openTime; // Trade duration in seconds
// Check if the trade has been open for more than the tradeLengthThreshold
if (tradeLength > tradeLengthThreshold * 60) { // Convert threshold to seconds
double lotSize = PositionGetDouble(POSITION_VOLUME);
double newLotSize = lotSize * reverseLotSizeMultiplier; // 10 times the original lot size
crossoverTradeCount = maxCrossoverTrades + 1;
// Place a reverse trade
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
trade.SetExpertMagicNumber(MagicNumber);
if (trade.Sell(newLotSize, Symbol(), SymbolInfoDouble(Symbol(), SYMBOL_BID))) {
Print("Reversal sell order executed with increased lot size");
} else {
Print("Failed to execute reversal sell order");
}
} else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
trade.SetExpertMagicNumber(MagicNumber);
if (trade.Buy(newLotSize, Symbol(), SymbolInfoDouble(Symbol(), SYMBOL_ASK))) {
Print("Reversal buy order executed with increased lot size");
} else {
Print("Failed to execute reversal buy order");
}
}
}
// Close the trade if profit meets the take profit level
if (profit >= reverseTP) {
Close_Position_MN(MagicNumber);
CloseAllPositions();
}
// Check if there is only one position and its volume is lotSize * reverseLotSizeMultiplier
if (PositionsTotal() == 1 && PositionGetDouble(POSITION_VOLUME) == minimumLotSize * reverseLotSizeMultiplier) {
trade.PositionClose(ticket);
Print("Single position with volume equal to lotSize * reverseLotSizeMultiplier closed");
}
// Get the current Ask and Bid prices
double Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
double Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
// Check if the double down trade is exited by stop loss
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetDouble(POSITION_SL) > 0 && Bid <= PositionGetDouble(POSITION_SL)) {
// Close the original trade
CloseOriginalTrade();
} else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetDouble(POSITION_SL) > 0 && Ask >= PositionGetDouble(POSITION_SL)) {
// Close the original trade
CloseOriginalTrade();
}
}
}
}
}
}
// Function to close the original trade
void CloseOriginalTrade() {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket)) {
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
trade.PositionClose(ticket);
Print("Original buy position closed due to double down stop loss.");
} else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
trade.PositionClose(ticket);
Print("Original sell position closed due to double down stop loss.");
}
}
}
}
//+------------------------------------------------------------------+
//| Function to close all positions |
//+------------------------------------------------------------------+
void CloseAllPositions() {
// Loop through all positions and close them
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket)) {
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
trade.PositionClose(ticket);
Print("Buy position closed at score crossover.");
}
else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
trade.PositionClose(ticket);
Print("Sell position closed at score crossover.");
}
}
}
}
void ApplyTrailingStop()
{
for(int i=PositionsTotal()-1; i>=0; i--)
{
string symbol = PositionGetSymbol(i);
ulong PositionTicket = PositionGetTicket(i);
long trade_type = PositionGetInteger(POSITION_TYPE);
if(!PositionGetInteger(POSITION_MAGIC) == MagicNumber) {
return;
}
double POINT = SymbolInfoDouble( symbol, SYMBOL_POINT );
int DIGIT = (int) SymbolInfoInteger( symbol, SYMBOL_DIGITS );
if(trade_type == 0)
{
double Bid = NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_BID),DIGIT);
if(Bid-PositionGetDouble(POSITION_PRICE_OPEN) > NormalizeDouble(POINT * TrailingStop,DIGIT))
{
if(PositionGetDouble(POSITION_SL) < NormalizeDouble(Bid - POINT * TrailingStop,DIGIT))
{
trade.PositionModify(PositionTicket,NormalizeDouble(Bid - POINT * TrailingStop,DIGIT),PositionGetDouble(POSITION_TP));
}
}
}
if(trade_type == 1)
{
double Ask = NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_ASK),DIGIT);
if((PositionGetDouble(POSITION_PRICE_OPEN) - Ask) > NormalizeDouble( POINT * TrailingStop,DIGIT))
{
if((PositionGetDouble(POSITION_SL) > NormalizeDouble(Ask + POINT * TrailingStop,DIGIT)) || (PositionGetDouble(POSITION_SL)==0))
{
trade.PositionModify(PositionTicket,NormalizeDouble(Ask + POINT * TrailingStop,DIGIT),PositionGetDouble(POSITION_TP));
}
}
}
}
}
void Close_Position_MN(ulong magicNumber)
{
int total = PositionsTotal();
for(int i = total - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
// Use PositionSelect by symbol instead of ticket
string symbol = PositionGetSymbol(i);
if(PositionSelect(symbol))
{
if (PositionGetInteger(POSITION_MAGIC) == magicNumber && PositionGetInteger(POSITION_TICKET) == ticket)
{
if(symbol == _Symbol) // Verify the symbol
{
Print("MN " + magicNumber);
trade.PositionClose(ticket);
}
}
}
else
{
int errorCode = GetLastError();
Print("aaaa PositionSelect failed with error code: ", errorCode);
}
}
}
//+------------------------------------------------------------------+
//| Calculate the dynamic lot size based on max drawdown |
//+------------------------------------------------------------------+
double CalculateLotSize()
{
double balance = AccountInfoDouble(ACCOUNT_BALANCE); // Get account balance
double allowedDrawdown = balance * max_drawdown; // Calculate allowed drawdown in account currency
double baseDrawdownPerLot = 150; // Assumed drawdown per 0.01 lots as per backtest
// Calculate lot size based on maximum drawdown
double lotSize = (allowedDrawdown / baseDrawdownPerLot) * 0.01;
return NormalizeDouble(lotSize, 2); // Normalize lot size to 2 decimal places
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 321 KiB

+64 -1
View File
@@ -71,7 +71,70 @@ A strategy that combines RSI (Relative Strength Index) divergence detection with
| Equity Drawdown Relative | 15.85% ($250.78) |
**Balance Sheet:**
![Balance Sheet](RSIDivergenceRebound/test-balance.jpg)
![RSI Balance Sheet](RSIDivergenceRebound/test-balance.jpg)
### 2. EMA Crossover Skirmish
A strategy that uses Exponential Moving Average (EMA) crossovers with advanced scoring and position management.
**Key Features:**
- EMA crossover detection
- Advanced scoring system
- Trailing stop management
- Position scaling and reversal capabilities
**Strategy Settings:**
- Symbol: XAUUSD
- Period: H1 (2021.01.01 - 2025.04.11)
- Magic Number: 42
- Score Threshold: 5200
- Slope Threshold: 93
- Max Score: 7900.0
- Cooldown Minutes: 18
- Trade Cooldown Minutes: 24
- EMA Time Frame: 16385
- EMA Period: 64
- Cross Over Step: 950.0
- Slope Threshold Step: 635.0
- EMA Distance Step: 150.0
- ATR Multiplier: 7.6
- Trailing Stop: 5.0
- Max Crossover Trades: 4
- Max Drawdown: 10%
- Minimum Lot Size: 0.01
- Max Time in Position: 9 hours
- Trade Length Threshold: 98
- Reverse TP: 32
- Reverse Lot Size Multiplier: 15
- Secondary Position Hold Time: 32
**Performance Metrics:**
| Metric | Value |
|--------|-------|
| Total Net Profit | $388.52 |
| Gross Profit | $391.65 |
| Gross Loss | -$3.13 |
| Profit Factor | 125.13 |
| Recovery Factor | 11.66 |
| Expected Payoff | $0.72 |
| Sharpe Ratio | 41.49 |
| AHPR | 1.0006 (0.06%) |
| GHPR | 1.0006 (0.06%) |
**Trade Statistics:**
| Statistic | Value |
|-----------|-------|
| History Quality | 82% real ticks |
| Total Bars | 25,283 |
| Total Ticks | 165,999,507 |
| Balance Drawdown Absolute | $0.00 |
| Equity Drawdown Absolute | $0.10 |
| Balance Drawdown Maximal | $0.69 (0.05%) |
| Equity Drawdown Maximal | $33.32 (2.56%) |
| Balance Drawdown Relative | 0.05% ($0.69) |
| Equity Drawdown Relative | 2.92% ($30.23) |
**Balance Sheet:**
![EMA Balance Sheet](EMACrossOverSkirmish/test-balance.jpg)
## Technical Details
Each EA is implemented in MQL5 and includes: