2230 lines
51 KiB
Plaintext
2230 lines
51 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Class Library
|
|
// ----------------------------------------------
|
|
// Name: XSCX121EA
|
|
// Description: provides all X121 EA requirements ...
|
|
// - X5 Provider;
|
|
//
|
|
//
|
|
// Maintainer:
|
|
// ------------
|
|
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
|
|
//
|
|
//////////////////////////////////////////////////////
|
|
//
|
|
// Global Properties ...
|
|
#property library
|
|
#property copyright "Copyright 2023, SaherElm IT Center"
|
|
#property link "https://www.saherelm.ir"
|
|
#property version "1.00"
|
|
#property strict
|
|
|
|
//
|
|
// Imports ...
|
|
#include "../Classes/x-saherelm.xea.class.mq5"
|
|
#include "../Classes/x-saherelm.x121.provider.class.mq5"
|
|
|
|
//
|
|
// Define On Signal Event Handler Type Specified for X5 ...
|
|
typedef void (*TX121OnSignal)(X121ProviderDescriptor &descriptor);
|
|
|
|
//
|
|
// Position Protect Structure ...
|
|
struct XProtectedPosition
|
|
{
|
|
//
|
|
ulong ticket; // Position Ticket
|
|
string symbol; // Position Symbol
|
|
string provider; // Position Signaller
|
|
ENUM_TIMEFRAMES period; // Position Time Frame
|
|
ENUM_POSITION_TYPE type; // Position Type
|
|
double volume; // Position Volume
|
|
double entry; // Position Entry
|
|
double sl; // Position Stop Loss
|
|
double tp; // Position Take Profit
|
|
datetime openAt; // Position Opening Time
|
|
|
|
//
|
|
// Working Variables ...
|
|
double recoveryZoneStep; // Used Recovery Zone Step
|
|
int lastRecoveryLevel; // Last Recovery Level
|
|
datetime lastRecoveryTime; // Last Recovery Time
|
|
double lastVolume; // Last Recovery Volume
|
|
ulong tickets[]; // Recovery Trades Tickets
|
|
|
|
//
|
|
// Constructor ...
|
|
XProtectedPosition()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Initialized ...
|
|
bool Init(
|
|
double mRecoveryZoneStep,
|
|
XPosition &mPosition //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Params ...
|
|
result =
|
|
//
|
|
mPosition.IsValid() &&
|
|
mRecoveryZoneStep > 0
|
|
//
|
|
;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
Clean();
|
|
|
|
//
|
|
sl = mPosition.sl;
|
|
tp = mPosition.tp;
|
|
type = mPosition.type;
|
|
entry = mPosition.entry;
|
|
ticket = mPosition.ticket;
|
|
symbol = mPosition.symbol;
|
|
period = mPosition.period;
|
|
volume = mPosition.volume;
|
|
openAt = mPosition.openAt;
|
|
provider = mPosition.provider;
|
|
recoveryZoneStep = mRecoveryZoneStep;
|
|
|
|
//
|
|
result = IsValid();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Cleanup ...
|
|
void Clean()
|
|
{
|
|
//
|
|
ticket = 0;
|
|
symbol = NULL;
|
|
period = NULL;
|
|
provider = NULL;
|
|
volume = 0;
|
|
entry = 0;
|
|
sl = 0;
|
|
tp = 0;
|
|
openAt = NULL;
|
|
lastRecoveryLevel = 0;
|
|
lastVolume = 0;
|
|
lastRecoveryTime = NULL;
|
|
Clean(tickets);
|
|
}
|
|
|
|
//
|
|
// Validate ...
|
|
bool IsValid()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
//
|
|
ticket > 0 &&
|
|
volume > 0 &&
|
|
entry > 0 &&
|
|
tp > 0 &&
|
|
sl > 0 &&
|
|
IsSpecifiedValid(symbol) &&
|
|
IsSpecifiedValid(provider) &&
|
|
IsSpecifiedValid(period)
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Protected Type ...
|
|
bool IsLong()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = IsLong(this.type);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Tick Based on Protected ...
|
|
bool GetTick(
|
|
MqlTick &tick //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate ...
|
|
result = IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = GetTick(
|
|
this.symbol,
|
|
tick //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Current Exit Price based on Protected ...
|
|
double GetExit()
|
|
{
|
|
//
|
|
double result = GetExit(
|
|
this.symbol,
|
|
this.type //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Current Entry Price based on Protected ...
|
|
double GetEntry()
|
|
{
|
|
//
|
|
double result = GetEntry(
|
|
this.symbol,
|
|
this.type //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate Recovery Step Price ...
|
|
double GetRecoveryAreaPrice()
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
// Validate ...
|
|
if (!IsValid())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double stepPrice = PointToPrice(
|
|
recoveryZoneStep,
|
|
symbol //
|
|
);
|
|
|
|
//
|
|
bool isLong = IsLong(type);
|
|
|
|
//
|
|
result =
|
|
isLong
|
|
? entry - stepPrice
|
|
: entry + stepPrice;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Protected Has Recovery or not ...
|
|
// it it's true ... fill signal by proper info ...
|
|
bool GetRecoverySignal(
|
|
XSignal &signal // Result ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
signal.Clean();
|
|
|
|
//
|
|
// Validate ...
|
|
result = IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Position is Reoverable or not ...
|
|
bool isLong = IsLong();
|
|
double recoveryPrice = GetRecoveryAreaPrice();
|
|
|
|
//
|
|
int currRecoveryLevel = lastRecoveryLevel + 1;
|
|
|
|
//
|
|
double mExit = GetExit();
|
|
double mEntry = GetEntry();
|
|
double recoveryDiff = MathAbs(mEntry - recoveryPrice);
|
|
|
|
//
|
|
// First Check Opposite Direction ...
|
|
bool canOppositDirectionRecover =
|
|
//
|
|
// Check State for Opposit Recovery ...
|
|
lastRecoveryLevel % 2 == 0 &&
|
|
//
|
|
(isLong
|
|
? mExit < recoveryPrice
|
|
: mExit > recoveryPrice)
|
|
//
|
|
;
|
|
|
|
//
|
|
// Check Same Direction Recovery ...
|
|
bool canSameDirectionRecover =
|
|
//
|
|
// Since for Same Direction Recover we have to at leaset 1 Opposite Recovery ...
|
|
lastRecoveryLevel % 2 == 1 &&
|
|
//
|
|
(isLong
|
|
? mEntry >= entry
|
|
: mEntry <= entry)
|
|
//
|
|
;
|
|
|
|
//
|
|
// Check Protected Can Recoverable or not ...
|
|
result =
|
|
//
|
|
lastRecoveryLevel < 5 &&
|
|
(canSameDirectionRecover ||
|
|
canOppositDirectionRecover)
|
|
//
|
|
;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Last Recovery Time ...
|
|
int age =
|
|
!IsValid(this.lastRecoveryTime)
|
|
? -1
|
|
: iBarShift(
|
|
this.symbol,
|
|
this.period,
|
|
this.lastRecoveryTime,
|
|
false //
|
|
);
|
|
result =
|
|
age == -1
|
|
? true
|
|
: age >= 5;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Now we have to Prepare Signal based on Recovery Mode ...
|
|
|
|
//
|
|
// Recovery Position Type ...
|
|
ENUM_POSITION_TYPE mType =
|
|
canSameDirectionRecover
|
|
? this.type
|
|
: GetOpposit(this.type);
|
|
|
|
//
|
|
bool isMLong = IsLong(mType);
|
|
|
|
//
|
|
// Recovery Position Entry Price ...
|
|
mEntry = GetEntry(
|
|
this.symbol,
|
|
mType //
|
|
);
|
|
|
|
//
|
|
// SL and TP ...
|
|
double mTP =
|
|
canSameDirectionRecover
|
|
? this.tp
|
|
: this.sl;
|
|
mTP =
|
|
isMLong
|
|
? mTP + recoveryDiff
|
|
: mTP - recoveryDiff;
|
|
|
|
//
|
|
double mSL =
|
|
canSameDirectionRecover
|
|
? this.sl
|
|
: this.tp;
|
|
mSL =
|
|
isMLong
|
|
? mSL - recoveryDiff
|
|
: mSL + recoveryDiff;
|
|
|
|
//
|
|
// Volume ...
|
|
// For Volume Calculating we have to use Recovery Level ...
|
|
// double mVolume =
|
|
// lastVolume == 0
|
|
// ? volume * 1
|
|
// : lastVolume * 1;
|
|
double mVolume =
|
|
canSameDirectionRecover
|
|
? volume
|
|
: volume * 2;
|
|
|
|
//
|
|
result = signal.Prepare(
|
|
this.symbol,
|
|
this.provider,
|
|
this.period,
|
|
mType,
|
|
X_ORDER_MODE_MARKET,
|
|
mEntry,
|
|
mVolume,
|
|
mSL,
|
|
mTP //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
};
|
|
|
|
//
|
|
// a Position Protector Class ...
|
|
class XSCPositionProtector : public XSCBaseAlert
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
bool enable;
|
|
|
|
//
|
|
// Protected Positions ...
|
|
XProtectedPosition protecteds[];
|
|
|
|
//
|
|
// Trader Class Instance ...
|
|
XSCTrade *mTrader;
|
|
|
|
//
|
|
// Constructor ...
|
|
void XSCPositionProtector()
|
|
{
|
|
//
|
|
Clean(protecteds);
|
|
|
|
//
|
|
SetRecoveryStepDivider(5);
|
|
SetRecoveryStepPeriod(PERIOD_D1);
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
void ~XSCPositionProtector()
|
|
{
|
|
delete mTrader;
|
|
}
|
|
|
|
//
|
|
// Setter(s) / Getter(s) ...
|
|
|
|
//
|
|
void SetRecoveryStepPeriod(ENUM_TIMEFRAMES value)
|
|
{
|
|
this.recoveryStepPeriod = value;
|
|
}
|
|
|
|
//
|
|
ENUM_TIMEFRAMES GetRecoveryStepPeriod()
|
|
{
|
|
return this.recoveryStepPeriod;
|
|
}
|
|
|
|
//
|
|
void SetRecoveryStepDivider(int value)
|
|
{
|
|
//
|
|
if (value <= 3)
|
|
{
|
|
value = 3;
|
|
}
|
|
|
|
//
|
|
this.recoveryStepDivider = value;
|
|
}
|
|
|
|
//
|
|
int GetRecoveryStepDivider()
|
|
{
|
|
return this.recoveryStepDivider;
|
|
}
|
|
|
|
//
|
|
void SetMinRequiredProfitPerTrade(double value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
this.minRequiredProfitPerTrade = value;
|
|
}
|
|
|
|
//
|
|
double GetMinRequiredProfitPerTrade()
|
|
{
|
|
return this.minRequiredProfitPerTrade;
|
|
}
|
|
|
|
//
|
|
void SetMinRequiredProfitPerTradeVolumeFactor(double value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
this.minRequiredProfitPerTradeVolumeFactor = value;
|
|
}
|
|
|
|
//
|
|
double GetMinRequiredProfitPerTradeVolumeFactor()
|
|
{
|
|
return this.minRequiredProfitPerTradeVolumeFactor;
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Initial Class ...
|
|
bool Init(
|
|
bool mEnable,
|
|
XSCTrade *trader,
|
|
double mMinRequiredProfitPerTrade = 0.5,
|
|
double mMinRequiredProfitPerTradeVolumeFactor = 0.01 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
this.enable = mEnable;
|
|
|
|
//
|
|
result = trader != NULL;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
this.mTrader = trader;
|
|
|
|
//
|
|
SetMinRequiredProfitPerTrade(mMinRequiredProfitPerTrade);
|
|
SetMinRequiredProfitPerTradeVolumeFactor(mMinRequiredProfitPerTradeVolumeFactor);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Handle Trades for Processing ...
|
|
// this used for Automating Recovery Positions ...
|
|
// we have to call this in our EA Class ...
|
|
void HandleState(const XOnTradeHandlerState &state)
|
|
{
|
|
//
|
|
if (!enable) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
if (
|
|
!state.hasNewDeal &&
|
|
!state.hasNewOrder &&
|
|
!state.hasNewPosition &&
|
|
!state.hasNewHistoryOrder)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Here we Can Handle new Positions ...
|
|
// To Protect ...
|
|
|
|
//
|
|
// Detect New Positions ...
|
|
if (!state.hasNewPosition)
|
|
{
|
|
//
|
|
// This means a Position SL or TP or Close ...
|
|
return;
|
|
}
|
|
|
|
//
|
|
ulong lastPositionTicket = mTrader.GetLastOpenPositionTicket();
|
|
if (lastPositionTicket <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Retrieve XPosition ...
|
|
XPosition position;
|
|
bool isRetrieved = mTrader.GetPosition(
|
|
lastPositionTicket,
|
|
position //
|
|
);
|
|
if (!isRetrieved)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Now we Have Position Struct ...
|
|
// - First Check Position is new Regular Position or
|
|
// it is a Support Position;
|
|
// - then We have to Decide what to Do ...
|
|
|
|
//
|
|
// Check Position is New Or Not ...
|
|
ulong parentTicket = ExtractEQMSupportedTicket(position.comment);
|
|
if (!NotEmptyZero(parentTicket))
|
|
{
|
|
//
|
|
AddPosition(position);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
UpdatePosition(
|
|
parentTicket,
|
|
position //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Here we Process all Protected Positions ...
|
|
// for Handling Zone Recovery ...
|
|
void Process()
|
|
{
|
|
//
|
|
if (!enable) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
int protectedsCount = ArraySize(protecteds);
|
|
if (IsValidSize(protectedsCount))
|
|
{
|
|
//
|
|
// Loop through Exists ...
|
|
for (int i = 0; i < protectedsCount; i++)
|
|
{
|
|
//
|
|
// Do Protection ...
|
|
HandlePositionsProtecting(protecteds[i]);
|
|
}
|
|
}
|
|
|
|
//
|
|
string comment = XEQMSupportToken + " Hege ...";
|
|
|
|
//
|
|
// Handle Hedging All Positions ...
|
|
bool allowHedge = AllowHedge();
|
|
if (allowHedge)
|
|
{
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(positions);
|
|
if (!IsValidSize(positions) || positionsCount <= 1)
|
|
{
|
|
//
|
|
// Here We Can Close if Still Positions is EQM Support ...
|
|
bool canClosePosition =
|
|
//
|
|
positionsCount == 1 &&
|
|
positions[0].provider == XEQMSupportToken &&
|
|
positions[0].profit > (-1 * positions[0].swap) + (minRequiredProfitPerTrade * (positions[0].volume / minRequiredProfitPerTradeVolumeFactor))
|
|
//
|
|
;
|
|
if (canClosePosition)
|
|
{
|
|
//
|
|
bool isClosed = mTrader.Close(
|
|
positions[0].ticket,
|
|
comment //
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
|
|
//
|
|
double profit = SpecifiedCalculatePositionsProfit(positions);
|
|
double requiredProfit = SpecifiedCalculateRequiredProfitForHedge(
|
|
positions,
|
|
minRequiredProfitPerTrade,
|
|
minRequiredProfitPerTradeVolumeFactor //
|
|
);
|
|
bool canHedge = SpecifiedIsPositionsReadyForHedge(
|
|
positions,
|
|
minRequiredProfitPerTrade,
|
|
minRequiredProfitPerTradeVolumeFactor //
|
|
);
|
|
if (!canHedge)
|
|
{
|
|
//
|
|
ProtectMargin();
|
|
return;
|
|
}
|
|
|
|
//
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT states[];
|
|
int closed = mTrader.Close(
|
|
positions,
|
|
comment //
|
|
);
|
|
if (IsValidSize(closed))
|
|
{
|
|
Clean(protecteds);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sync Positions ...
|
|
|
|
//
|
|
// Remove Support ...
|
|
bool Remove(ulong ticket)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (!enable) {
|
|
return result;
|
|
}
|
|
|
|
|
|
//
|
|
int protectedIDX = FindProtectedIndex(ticket);
|
|
result = protectedIDX >= 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close all Protected Support Positions if Exists ...
|
|
string comment = "EQM Close In Profit ...";
|
|
|
|
//
|
|
int ticketsCount = ArraySize(protecteds[protectedIDX].tickets);
|
|
if (IsValidSize(ticketsCount))
|
|
{
|
|
//
|
|
for (int i = 0; i < ticketsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition;
|
|
bool isRetrieved = mTrader.GetPosition(
|
|
protecteds[protectedIDX].tickets[i],
|
|
iPosition //
|
|
);
|
|
if (!isRetrieved)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isClosed = mTrader.Close(
|
|
iPosition.ticket,
|
|
comment //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
result = ArrayRemove(
|
|
protecteds,
|
|
protectedIDX,
|
|
1 //
|
|
);
|
|
|
|
//
|
|
// TODO: Update Collection here ...
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Props ...
|
|
double minRequiredProfitPerTrade; // Minimum Required Profit Per Trade for Hedging
|
|
double minRequiredProfitPerTradeVolumeFactor; // Calculate Required Profit Based on Volume Factor for Hedging
|
|
int recoveryStepDivider; // Recovery Step Divider
|
|
ENUM_TIMEFRAMES recoveryStepPeriod; // Recovery Step Period
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Here we recieve a new Position and
|
|
// Prepare it for Protecting ...
|
|
void AddPosition(XPosition &position)
|
|
{
|
|
//
|
|
// - Recieve Last Day Up and Low Boundary
|
|
// - Divided to 5
|
|
// - Find Recovery Zone Step
|
|
// - Calculate Recoery Zone for this Specific Position
|
|
// - Prepare it's Structure
|
|
// - Add it to Collection
|
|
|
|
//
|
|
// Validate Params ...
|
|
if (!position.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
double recoveryStep = CalculateRecoveryZoneStep(position);
|
|
if (recoveryStep <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
XProtectedPosition item;
|
|
bool isInited = item.Init(
|
|
recoveryStep,
|
|
position //
|
|
);
|
|
if (!isInited)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
item,
|
|
protecteds //
|
|
);
|
|
|
|
//
|
|
// TODO: Also Here We Can Collect Data ...
|
|
}
|
|
|
|
//
|
|
// Here we recieve a Support Position and
|
|
// need to Update Parent Protected Position's
|
|
// info ...
|
|
void UpdatePosition(
|
|
ulong parentTicket, // Parent Position Ticket
|
|
XPosition &position // Support Position
|
|
)
|
|
{
|
|
//
|
|
Print("Update Protected Position: " + ToString(parentTicket));
|
|
}
|
|
|
|
//
|
|
// Calculate Position Recovery one Step ...
|
|
double CalculateRecoveryZoneStep(XPosition &position)
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
// Validate Position ...
|
|
if (!position.IsValid())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Validate Position must have TP and SL ...
|
|
if (position.tp == 0 || position.sl == 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate Position Risk at Point ...
|
|
double point = GetPoints(position.symbol);
|
|
int digits = GetDigits(position.symbol);
|
|
double riskPoint = NormalizeDouble(MathAbs(position.entry - position.sl), digits) / point;
|
|
|
|
//
|
|
// Retrieve Bar for finding Recovery Step ...
|
|
XOHCL rBar;
|
|
bool isInited = rBar.Init(
|
|
position.symbol,
|
|
recoveryStepPeriod,
|
|
1 //
|
|
);
|
|
if (!isInited)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate Recovery Step ...
|
|
double riskPointRecoveryStep = riskPoint / 2;
|
|
double recoveryStep = (NormalizeDouble(MathAbs(rBar.high - rBar.low), digits) / point) / recoveryStepDivider;
|
|
result = MathMin(riskPointRecoveryStep, recoveryStep);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Here we Implement all Protecting Senarios here ...
|
|
void HandlePositionsProtecting(XProtectedPosition &item)
|
|
{
|
|
//
|
|
// Check protected Validation ...
|
|
if (!item.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check Has Recovery Signal Or Not ...
|
|
XSignal signal;
|
|
bool hasRecovery = item.GetRecoverySignal(signal);
|
|
if (!hasRecovery)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
signal.provider = XEQMSupportToken;
|
|
string comment = GenerateEQMSupportTag(item.ticket);
|
|
signal.comment = comment;
|
|
|
|
//
|
|
// Remove Support Signal TP and SL ...
|
|
signal.tp = 0;
|
|
signal.sl = 0;
|
|
|
|
//
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT state;
|
|
bool isExecuted = mTrader.ExecuteSignal(
|
|
signal,
|
|
state,
|
|
ORDER_TIME_GTC,
|
|
NULL,
|
|
false // Ignore Policies ...
|
|
);
|
|
if (isExecuted)
|
|
{
|
|
//
|
|
// Do What we Want ...
|
|
item.lastRecoveryLevel++;
|
|
item.lastVolume = signal.volume;
|
|
item.lastRecoveryTime = TimeCurrent();
|
|
Add(
|
|
signal.positionId,
|
|
item.tickets //
|
|
);
|
|
|
|
//
|
|
int protectedIDX = FindProtectedIndex(item.ticket);
|
|
if (protectedIDX < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
protecteds[protectedIDX] = item;
|
|
|
|
//
|
|
// TODO: Update Collect here ...
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Hedging is Enabled ...
|
|
bool AllowHedge()
|
|
{
|
|
//
|
|
bool result =
|
|
//
|
|
minRequiredProfitPerTrade > 0 &&
|
|
minRequiredProfitPerTradeVolumeFactor > 0
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Protecting Margin by Free Coveraged Positions ...
|
|
void ProtectMargin()
|
|
{
|
|
//
|
|
double freeMargin = mTrader.mAccount.GetFreeMargin();
|
|
|
|
//
|
|
double balance = mTrader.mAccount.GetBalance();
|
|
double equity = mTrader.mAccount.GetEquity();
|
|
|
|
//
|
|
double selectedBalance = MathMin(balance, equity);
|
|
|
|
//
|
|
// Retrieve All Positions ...
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(positions);
|
|
if (!IsValidSize(positionsCount))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
bool canForceHedging =
|
|
positionsCount >= 25 ||
|
|
freeMargin <= selectedBalance / 2;
|
|
if (!canForceHedging)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// First Check Hedging By Half of Required Profit ...
|
|
double profit = SpecifiedCalculatePositionsProfit(positions);
|
|
double requiredProfit = SpecifiedCalculateRequiredProfitForHedge(
|
|
positions,
|
|
minRequiredProfitPerTrade,
|
|
minRequiredProfitPerTradeVolumeFactor //
|
|
);
|
|
bool canHedge =
|
|
//
|
|
profit > requiredProfit / 2
|
|
//
|
|
;
|
|
if (canHedge)
|
|
{
|
|
//
|
|
string comment = XEQMSupportToken + " Hege ...";
|
|
int closed = mTrader.Close(
|
|
positions,
|
|
comment //
|
|
);
|
|
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// TODO: Enable this if we want ...
|
|
return;
|
|
|
|
//
|
|
// Retrieve In Drawdown Positions ...
|
|
XPosition inDPositions[];
|
|
int inDPositionsCount = ExtractInDrawdownPositions(
|
|
positions,
|
|
inDPositions ///
|
|
);
|
|
if (!IsValidSize(inDPositionsCount))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Retrieve In Profit Positions ...
|
|
XPosition inPPositions[];
|
|
int inPPositionsCount = ExtractInProfitPositions(
|
|
positions,
|
|
inPPositions //
|
|
);
|
|
if (!IsValidSize(inPPositionsCount))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// if we can't Hedge all Positions ...
|
|
// now we are looking to pair Positions for hedge ...
|
|
// - Select Max In Drawdown Position;
|
|
// - Looking for Coverage it inside In Profit Positions;
|
|
// - Clease All of them ...
|
|
int maxInDIDX = FindMaxDrawdownIndex(inDPositions);
|
|
if (maxInDIDX < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
string comment = XEQMSupportToken + " Force Hege ...";
|
|
|
|
//
|
|
XPosition maxInDCoverages[];
|
|
int maxInDCoveragesCount = FindCoverageDrawdownPosition(
|
|
inDPositions[maxInDIDX],
|
|
inPPositions,
|
|
maxInDCoverages,
|
|
minRequiredProfitPerTrade,
|
|
minRequiredProfitPerTradeVolumeFactor //
|
|
);
|
|
if (!IsValidSize(maxInDCoveragesCount))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
bool isClosedMaxInD = mTrader.Close(
|
|
inDPositions[maxInDIDX].ticket,
|
|
comment //
|
|
);
|
|
if (isClosedMaxInD)
|
|
{
|
|
//
|
|
int closed = mTrader.Close(
|
|
maxInDCoverages,
|
|
comment //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Protected Collection Management ...
|
|
|
|
//
|
|
int CountProtecteds()
|
|
{
|
|
return ArraySize(protecteds);
|
|
}
|
|
|
|
//
|
|
int FindProtectedIndex(XProtectedPosition &item)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
if (!item.IsValid())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = FindProtectedIndex(item.ticket);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int FindProtectedIndex(ulong ticket)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
if (!NotEmptyZero(ticket))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int protectedsCount = CountProtecteds();
|
|
if (!IsValidSize(protectedsCount))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < protectedsCount; i++)
|
|
{
|
|
//
|
|
if (protecteds[i].ticket == ticket)
|
|
{
|
|
//
|
|
result = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
};
|
|
|
|
//
|
|
// Class Definition ...
|
|
|
|
class XSCX121EA : public XSCBaseEA
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Constructors ...
|
|
XSCX121EA(
|
|
//
|
|
// XTrade Class Requirements ...
|
|
int slippage, // Specify Slippage
|
|
ulong magicNumber, // Specify Magic Number
|
|
//
|
|
// Time Management ...
|
|
// TODO: ...
|
|
//
|
|
// XSCTrade Event Handlers ...
|
|
TOnStopLoss onStopLossTriggered = NULL, // On StopLoass Triggered Event Handler
|
|
TOnTakeProfit onTakeProfitTriggered = NULL, // On TakeProfit Triggered Event Handler
|
|
TOnDealsChanged onDealsChangedHandler = NULL, // On Deals Changed Event Handler
|
|
TOnOrdersChanged onOrdersChangedHandler = NULL, // On Orders Changed Event Handler
|
|
TOnPositionsChanged onPositionsChangedHandler = NULL, // On Positions Changed Event Handler
|
|
TOnTradeStateChanged onTradeStateChangedHandler = NULL, // On Trade State Changed Event Handler
|
|
//
|
|
// Custom Event Handler ...
|
|
TOnSignal onSignalHandler = NULL // On Signal Event Handler
|
|
) : XSCBaseEA(slippage,
|
|
magicNumber,
|
|
onStopLossTriggered,
|
|
onTakeProfitTriggered,
|
|
onDealsChangedHandler,
|
|
onOrdersChangedHandler,
|
|
onPositionsChangedHandler,
|
|
onTradeStateChangedHandler //
|
|
)
|
|
{
|
|
//
|
|
mProtector = new XSCPositionProtector();
|
|
mProtector.Init(false, mTrader);
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
~XSCX121EA()
|
|
{
|
|
delete mProtector;
|
|
}
|
|
|
|
//
|
|
// Properties Getter(s) / Setter(s) ...
|
|
|
|
//
|
|
// Add X5 Specified Signal Event Handler ...
|
|
void AddOnSignalEventHandler(TX121OnSignal listener)
|
|
{
|
|
//
|
|
Add(
|
|
listener,
|
|
mX121OnSignalEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add Specified X5 Provider ...
|
|
bool AddProvider(X121ProviderDescriptor &descriptor)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Inputs ...
|
|
result = descriptor.Init();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
descriptor,
|
|
mDescriptors //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Overrides ...
|
|
|
|
//
|
|
// Customize Token ...
|
|
string GetToken() override
|
|
{
|
|
return GetSpecificToken(this);
|
|
}
|
|
|
|
//
|
|
string GetTag() override
|
|
{
|
|
return this.GetToken();
|
|
}
|
|
|
|
//
|
|
void Draw() override
|
|
{
|
|
//
|
|
int descriptorsCount = CountDescriptors();
|
|
if (descriptorsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < descriptorsCount; i++)
|
|
{
|
|
//
|
|
X121ProviderDescriptor iDescriptor = mDescriptors[i];
|
|
|
|
//
|
|
iDescriptor.provider.Draw();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Provider for any Guards ...
|
|
bool CheckForGuard(XGuard &guards[]) override
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
Clean(guards);
|
|
|
|
//
|
|
int descriptorsCount = CountDescriptors();
|
|
result = descriptorsCount > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Loop Through Descriptors for Collecting Guards ...
|
|
for (int i = 0; i < descriptorsCount; i++)
|
|
{
|
|
//
|
|
X121ProviderDescriptor iDescriptor = mDescriptors[i];
|
|
|
|
//
|
|
XGuard iGuards[];
|
|
int iGuardsCount = iDescriptor.HasGuard(iGuards);
|
|
if (!IsValidSize(iGuardsCount))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
Copy(
|
|
iGuards,
|
|
guards,
|
|
false //
|
|
);
|
|
}
|
|
|
|
//
|
|
result = ArraySize(guards) > 0;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Request Provider to Collect all
|
|
// Potentially Signals and then
|
|
// filters theme here and passed them
|
|
// for Executing ...
|
|
int RequestForSignal(
|
|
XSignal &signals[] // Holds Signals ...
|
|
) override
|
|
{
|
|
//
|
|
bool result = 0;
|
|
|
|
//
|
|
Clean(signals);
|
|
|
|
//
|
|
int descriptorsCount = CountDescriptors();
|
|
if (descriptorsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < descriptorsCount; i++)
|
|
{
|
|
//
|
|
X121ProviderDescriptor iDescriptor = mDescriptors[i];
|
|
|
|
//
|
|
if (iDescriptor.provider.CanIgnoreProcess())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
int iSignalsCount = iDescriptor.HasSignal();
|
|
if (IsValidSize(iSignalsCount))
|
|
{
|
|
//
|
|
// Here we Can double check Signals by Conditions
|
|
// for Score Base Filtering ...
|
|
iDescriptor.provider.SetWaitsUntilNewBar(true);
|
|
|
|
//
|
|
Copy(
|
|
iDescriptor.signals,
|
|
signals,
|
|
false
|
|
//
|
|
);
|
|
|
|
//
|
|
NotifyX121OnSignalEventHandlers(iDescriptor);
|
|
}
|
|
}
|
|
|
|
//
|
|
result = ArraySize(signals);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Request for Support Signals using Guard ...
|
|
bool RequestForSupport(
|
|
XSignal &support, // Holds Support Signal, if Provided
|
|
string provider, // Positions Provided by Specific Provider
|
|
string symbol, // Trading Symbol
|
|
ENUM_X_POSITION_TYPE type, // Trading Type
|
|
ENUM_TIMEFRAMES period // Specified Time Frame
|
|
) override
|
|
{
|
|
//
|
|
// TODO: Implement this ...
|
|
|
|
//
|
|
// Support Senario ...
|
|
// Check Support Position Exists or not ...
|
|
// Check Positions for Support based on Types ...
|
|
// Update Untriggered Positions ...
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Here we Manage Signals for Executing ...
|
|
// - Check Policies based on their Signaller ...
|
|
// - Check Same Time Open Positions ...
|
|
// - Check Signal Age for new Trade ...
|
|
// and etc ...
|
|
int HandleSignalManagement(XSignal &signals[]) override
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
int signalsCount = ArraySize(signals);
|
|
if (!IsValidSize(signalsCount))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XSignal tmpSignals[];
|
|
Copy(
|
|
signals,
|
|
tmpSignals //
|
|
);
|
|
|
|
//
|
|
Clean(signals);
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = tmpSignals[i];
|
|
|
|
//
|
|
// Find Provider Descriptor Which Issued this Signal ...
|
|
int iProviderIDX = FindDescriptorIndex(
|
|
iSignal.symbol,
|
|
iSignal.period);
|
|
if (!IsValidIndex(iProviderIDX))
|
|
{
|
|
//
|
|
string message = "Couldn't find Signal Descriptor ...";
|
|
Log(message);
|
|
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now we Have to Find Signaller Which Issued this Signal ...
|
|
int iSignallerIDX = mDescriptors[iProviderIDX]
|
|
.FindSignallerIndex(iSignal.provider);
|
|
if (!IsValidIndex(iSignallerIDX))
|
|
{
|
|
//
|
|
string message = "Couldn't find Signal Provider ...";
|
|
Log(message);
|
|
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Position Type is Enabled or not ...
|
|
bool isLong = IsLong(iSignal.type);
|
|
bool isAllowedType = isLong
|
|
? mDescriptors[iProviderIDX].signallers[iSignallerIDX].allowLong
|
|
: mDescriptors[iProviderIDX].signallers[iSignallerIDX].allowShort;
|
|
if (!isAllowedType)
|
|
{
|
|
//
|
|
string message = "ignore Signal due Type Policy ...";
|
|
Log(message);
|
|
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Retrieve Long and Short Signals of same Provider ...
|
|
XPosition longs[];
|
|
XPosition shorts[];
|
|
mTrader.GetPositions(
|
|
longs,
|
|
shorts,
|
|
iSignal.symbol,
|
|
iSignal.provider,
|
|
iSignal.period //
|
|
);
|
|
int longsCount = ArraySize(longs);
|
|
int shortsCount = ArraySize(shorts);
|
|
|
|
//
|
|
// Check Max Number of Positions ...
|
|
bool isMaxNumberOfPositionsPassed = isLong
|
|
? mDescriptors[iProviderIDX].signallers[iSignallerIDX].maxAllowedLong <= 0
|
|
? true
|
|
: longsCount < mDescriptors[iProviderIDX].signallers[iSignallerIDX].maxAllowedLong
|
|
: mDescriptors[iProviderIDX].signallers[iSignallerIDX].maxAllowedShort <= 0
|
|
? true
|
|
: shortsCount < mDescriptors[iProviderIDX].signallers[iSignallerIDX].maxAllowedShort;
|
|
if (!isMaxNumberOfPositionsPassed)
|
|
{
|
|
//
|
|
string message = "ignore Signal due Max Allowed Positions Reached ...";
|
|
Log(message);
|
|
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Delay Between Two Same Type Signals ...
|
|
// Check Open Next Behaviour ...
|
|
if (longsCount > 0 || shortsCount > 0)
|
|
{
|
|
//
|
|
// Check if Signaller Configured for Check Delay between Same type Signals ...
|
|
if (mDescriptors[iProviderIDX].signallers[iSignallerIDX].delayBetweenTwoSameTypeSignals > 0)
|
|
{
|
|
//
|
|
bool isDelayPassed = true;
|
|
|
|
//
|
|
if (longsCount > 0)
|
|
{
|
|
//
|
|
XPosition youngestLong;
|
|
int youngestLongAge = GetYoungest(
|
|
youngestLong,
|
|
longs //
|
|
);
|
|
|
|
//
|
|
if (isLong)
|
|
{
|
|
isDelayPassed = youngestLongAge >= mDescriptors[iProviderIDX].signallers[iSignallerIDX].delayBetweenTwoSameTypeSignals;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (shortsCount > 0)
|
|
{
|
|
//
|
|
XPosition youngestShort;
|
|
int youngetsShortAge = GetYoungest(
|
|
youngestShort,
|
|
shorts //
|
|
);
|
|
|
|
//
|
|
if (!isLong)
|
|
{
|
|
isDelayPassed = youngetsShortAge >= mDescriptors[iProviderIDX].signallers[iSignallerIDX].delayBetweenTwoSameTypeSignals;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (!isDelayPassed)
|
|
{
|
|
//
|
|
string message = "ignore Signal due Delay Between Same Type Policy ...";
|
|
Log(message);
|
|
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Next Position ...
|
|
if (mDescriptors[iProviderIDX].signallers[iSignallerIDX].openNextPositionOnProfit)
|
|
{
|
|
//
|
|
bool isNextPassed = true;
|
|
|
|
//
|
|
if (isLong && longsCount > 0)
|
|
{
|
|
//
|
|
double profit = SpecifiedCalculatePositionsProfit(longs);
|
|
isNextPassed = profit > 0;
|
|
}
|
|
|
|
//
|
|
if (!isLong && shortsCount > 0)
|
|
{
|
|
//
|
|
double profit = SpecifiedCalculatePositionsProfit(shorts);
|
|
isNextPassed = profit > 0;
|
|
}
|
|
|
|
//
|
|
// Check Ignore for Opposit Signals ...
|
|
if (!isNextPassed &&
|
|
((isLong && shortsCount > longsCount) ||
|
|
(!isLong && longsCount > shortsCount)))
|
|
{
|
|
isNextPassed = mDescriptors[iProviderIDX].signallers[iSignallerIDX].ignoreProfitForOppositeSignals;
|
|
}
|
|
|
|
//
|
|
if (!isNextPassed)
|
|
{
|
|
//
|
|
string message = "ignore Signal due Next Must be In Profit Policy ...";
|
|
Log(message);
|
|
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finally Add Filtered Signals into Result ...
|
|
AddRef(
|
|
iSignal,
|
|
signals //
|
|
);
|
|
}
|
|
|
|
//
|
|
result = ArraySize(signals);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Handle State Management ....
|
|
// here we can manage current state ...
|
|
// - Check for Long Positions for each Signaller to Close ...
|
|
// - Handle Hedging Signaller's Positions if it's enabled ...
|
|
// - Force Closing Position when Specified Time reached ...
|
|
// - Check Start and End time or Trading Dates ...
|
|
// - handle Trailing or Risk free Signals based on several conditions ...
|
|
// and etc ...
|
|
bool HandleStateManagement(XSignal &signals[]) override
|
|
{
|
|
//
|
|
const bool result = false;
|
|
|
|
//
|
|
// Here we Implement Account Protector ...
|
|
HandleAccountProtect();
|
|
|
|
//
|
|
// Check Descriptor ...
|
|
int descriptorsCount = CountDescriptors();
|
|
if (!IsValidSize(descriptorsCount))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Loop Through Descriptors ...
|
|
for (int i = 0; i < descriptorsCount; i++)
|
|
{
|
|
//
|
|
// Check Signallers ...
|
|
int signallersCount = mDescriptors[i].CountSignallers();
|
|
if (!IsValidSize(signallersCount))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Loop Through Signallers ...
|
|
for (int j = 0; j < signallersCount; j++)
|
|
{
|
|
//
|
|
// Retrieve Specified Signaller's Position ...
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(
|
|
positions,
|
|
mDescriptors[i].symbol,
|
|
mDescriptors[i].signallers[j].GetName(),
|
|
mDescriptors[i].period);
|
|
if (!IsValidSize(positionsCount))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Signaller Enable Hedge or not and Handle it if enabled ...
|
|
if (positionsCount > 1 && mDescriptors[i].signallers[j].AllowHedge())
|
|
{
|
|
//
|
|
// Check Profits Enough for Hedge or not ...
|
|
double profit = SpecifiedCalculatePositionsProfit(positions);
|
|
bool isReadyForHedge =
|
|
positionsCount > 1 &&
|
|
SpecifiedIsPositionsReadyForHedge(
|
|
positions,
|
|
mDescriptors[i].signallers[j].minRequiredProfitPerTrade,
|
|
mDescriptors[i].signallers[j].minRequiredProfitPerTradeVolumeFactor //
|
|
);
|
|
if (isReadyForHedge)
|
|
{
|
|
//
|
|
string comment = "Close due Hedge ...";
|
|
int closeds = mTrader.Close(
|
|
positions,
|
|
comment //
|
|
);
|
|
|
|
//
|
|
if (closeds > 0)
|
|
{
|
|
//
|
|
string message = "Hedge (" + ToString(closeds) + ") Positions In Profit: " + ToString(profit);
|
|
|
|
//
|
|
Alert(message);
|
|
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Closing Long Age Position's Enabled or not ...
|
|
if (mDescriptors[i].signallers[j].maxAllowedOpenPositionAge > 0)
|
|
{
|
|
//
|
|
// Handle Close Long Time Trades ...
|
|
|
|
//
|
|
// Find Oldest ...
|
|
XPosition oldest;
|
|
int oldestAge = GetOldest(
|
|
oldest,
|
|
positions //
|
|
);
|
|
if (oldestAge >= mDescriptors[i].signallers[j].maxAllowedOpenPositionAge)
|
|
{
|
|
//
|
|
string comment = "Close due Long Age ...";
|
|
bool isClosed = mTrader.Close(
|
|
oldest.ticket,
|
|
comment //
|
|
);
|
|
|
|
//
|
|
if (isClosed)
|
|
{
|
|
//
|
|
string message = "Position (" + ToString(oldest.ticket) + ") Closed due Long Age Policy ...";
|
|
Alert(message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// if Returns true, Signal Execution failed ...
|
|
return result;
|
|
}
|
|
|
|
//
|
|
void OnStopLossTriggered(const XDeal &deal) override
|
|
{
|
|
//
|
|
// Remove Position Protecting ...
|
|
mProtector.Remove(deal.positionId);
|
|
}
|
|
|
|
//
|
|
void OnTakeProfitTriggered(const XDeal &deal) override
|
|
{
|
|
//
|
|
// Remove Position Protecting ...
|
|
mProtector.Remove(deal.positionId);
|
|
}
|
|
|
|
//
|
|
void OnTradeStateChangedHandler(
|
|
const XOnTradeHandlerState &state //
|
|
) override
|
|
{
|
|
//
|
|
// Calling Protector to Handle State ...
|
|
mProtector.HandleState(state);
|
|
}
|
|
|
|
//
|
|
void HandleAccountProtect()
|
|
{
|
|
//
|
|
// Position Protector Calls to Process State ...
|
|
mProtector.Process();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
void NotifyX121OnSignalEventHandlers(X121ProviderDescriptor &descriptor)
|
|
{
|
|
//
|
|
int listenersCount = ArraySize(mX121OnSignalEventHandlers);
|
|
if (listenersCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < listenersCount; i++)
|
|
{
|
|
//
|
|
TX121OnSignal iListener = mX121OnSignalEventHandlers[i];
|
|
|
|
//
|
|
iListener(descriptor);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Protect Specified Position ...
|
|
void ProtectPosition(XPosition &position)
|
|
{
|
|
//
|
|
// Here i Can Protect Position ...
|
|
// This Protect Include One Position in Market Mode ...
|
|
// If Provided ...
|
|
// the Positions Selection must passed Some conditions ...
|
|
|
|
//
|
|
string message = "For (" + position.symbol + ") Max In Drawdown Position is: (" +
|
|
ToString(position.ticket) + "), by Profit: " + ToString(position.profit);
|
|
|
|
//
|
|
mAlert.Alert(message);
|
|
}
|
|
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Collection of Signal Event Listeners ...
|
|
TX121OnSignal mX121OnSignalEventHandlers[];
|
|
|
|
//
|
|
// a Collection of X5 Provider Descriptors ...
|
|
X121ProviderDescriptor mDescriptors[];
|
|
|
|
//
|
|
int CountDescriptors()
|
|
{
|
|
return ArraySize(mDescriptors);
|
|
}
|
|
|
|
//
|
|
// Find Specifc Descriptor ...
|
|
int FindDescriptorIndex(
|
|
string symbol,
|
|
ENUM_TIMEFRAMES period //
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
if (!IsValid(symbol) ||
|
|
!IsValid(period))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int descriptorsCount = CountDescriptors();
|
|
if (!IsValidSize(descriptorsCount))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < descriptorsCount; i++)
|
|
{
|
|
//
|
|
X121ProviderDescriptor iDescriptor = mDescriptors[i];
|
|
|
|
//
|
|
bool isPassed =
|
|
//
|
|
iDescriptor.symbol == symbol &&
|
|
iDescriptor.period == period
|
|
//
|
|
;
|
|
if (isPassed)
|
|
{
|
|
//
|
|
result = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
int FindDescriptorIndex(
|
|
T &item //
|
|
)
|
|
{
|
|
return FindDescriptorIndex(
|
|
item.symbol,
|
|
item.period //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Trails Holding ...
|
|
|
|
//
|
|
XTrail mSLTrails[];
|
|
XTrail mTPTrails[];
|
|
|
|
//
|
|
int CountSLTrails()
|
|
{
|
|
return ArraySize(mSLTrails);
|
|
}
|
|
int CountTPTrails()
|
|
{
|
|
return ArraySize(mTPTrails);
|
|
}
|
|
|
|
//
|
|
bool RemoveTrail(ulong ticket)
|
|
{
|
|
//
|
|
bool isSLRemoved = RemoveSLTrail(ticket);
|
|
bool isTPRemoved = RemoveTPTrail(ticket);
|
|
|
|
//
|
|
bool result = isSLRemoved || isTPRemoved;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
bool RemoveSLTrail(ulong ticket)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int idx = FindSLTrailIndex(ticket);
|
|
result = idx >= 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = ArrayRemove(
|
|
mSLTrails,
|
|
idx,
|
|
1 //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
bool RemoveTPTrail(ulong ticket)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int idx = FindTPTrailIndex(ticket);
|
|
result = idx >= 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = ArrayRemove(
|
|
mTPTrails,
|
|
idx,
|
|
1 //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int FindSLTrailIndex(ulong ticket)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
int trailsCount = CountSLTrails();
|
|
if (ticket <= 0 || trailsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < trailsCount; i++)
|
|
{
|
|
//
|
|
XTrail iTrail = mSLTrails[i];
|
|
|
|
//
|
|
if (iTrail.ticket == ticket)
|
|
{
|
|
//
|
|
result = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
int FindTPTrailIndex(ulong ticket)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
int trailsCount = CountTPTrails();
|
|
if (ticket <= 0 || trailsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < trailsCount; i++)
|
|
{
|
|
//
|
|
XTrail iTrail = mTPTrails[i];
|
|
|
|
//
|
|
if (iTrail.ticket == ticket)
|
|
{
|
|
//
|
|
result = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool GetSLTrail(
|
|
ulong ticket,
|
|
XTrail &trail //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int idx = FindSLTrailIndex(ticket);
|
|
result = idx >= 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
trail = mSLTrails[idx];
|
|
|
|
//
|
|
return result;
|
|
}
|
|
bool GetTPTrail(
|
|
ulong ticket,
|
|
XTrail &trail //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int idx = FindTPTrailIndex(ticket);
|
|
result = idx >= 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
trail = mTPTrails[idx];
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
void AddSLTrail(
|
|
XTrail &trail //
|
|
)
|
|
{
|
|
//
|
|
if (!trail.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
trail,
|
|
mSLTrails //
|
|
);
|
|
}
|
|
void AddTPTrail(
|
|
XTrail &trail //
|
|
)
|
|
{
|
|
//
|
|
if (!trail.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
trail,
|
|
mTPTrails //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Position Protector ...
|
|
XSCPositionProtector *mProtector;
|
|
|
|
//
|
|
};
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
// |