This commit is contained in:
2024-05-27 09:06:04 +03:30
parent c1aa4fe888
commit 30dd2f1aaf
13 changed files with 2337 additions and 368 deletions
+759 -78
View File
@@ -22,6 +22,7 @@
//
// Imports ...
#include <Generic/HashMap.mqh>
#include "../Classes/x-saherelm.xea.class.mq5"
#include "../Classes/x-saherelm.x121.provider.class.mq5"
@@ -29,6 +30,450 @@
// Define On Signal Event Handler Type Specified for X5 ...
typedef void (*TX121OnSignal)(X121ProviderDescriptor &descriptor);
//
class XSCPositionHolder
{
//
// Public ...
public:
//
// Props ...
//
// Constructor(s) ...
//
XSCPositionHolder()
{
Clear();
}
//
// Tools ...
//
// Cleanup ...
void Clear()
{
Clean(mItems);
}
//
bool AddItem(XPosition &item)
{
//
bool result = false;
//
result = item.IsValid();
if (!result)
{
return result;
}
//
result = !HasItem(item);
if (!result)
{
return result;
}
//
AddRef(
item,
mItems //
);
//
return result;
}
//
int AddItems(XPosition &items[])
{
//
int result = 0;
//
int itemsCount = ArraySize(items);
if (itemsCount <= 0)
{
return result;
}
//
for (int i = 0; i < itemsCount; i++)
{
//
XPosition iItem = items[i];
//
bool isAdded = AddItem(iItem);
if (isAdded)
{
result++;
}
}
//
return result;
}
//
bool AddOrUpdateItem(XPosition &item)
{
//
bool result = false;
//
result = item.IsValid();
if (!result)
{
return result;
}
//
bool hasItem = HasItem(item);
if (!hasItem)
{
result = AddItem(item);
}
else
{
//
result = RemoveItem(item);
if (result)
{
result = AddItem(item);
}
}
//
return result;
}
//
int AddOrUpdateItem(XPosition &items[])
{
//
int result = 0;
//
int itemsCount = ArraySize(items);
if (itemsCount <= 0)
{
return result;
}
//
for (int i = 0; i < itemsCount; i++)
{
//
XPosition iItem = items[i];
//
bool isAddOrUpdated = AddOrUpdateItem(iItem);
if (isAddOrUpdated)
{
result++;
}
}
//
return result;
}
//
bool HasItem(XPosition &item)
{
//
bool result = false;
//
int itemIDX = FindItem(item);
result = itemIDX >= 0;
//
return result;
}
//
int FindItem(XPosition &item)
{
//
int result = -1;
//
if (!item.IsValid())
{
return result;
}
//
int itemsCount = CountItems();
if (itemsCount <= 0)
{
return result;
}
//
for (int i = 0; i < itemsCount; i++)
{
//
XPosition iItem = mItems[i];
//
bool isSame = IsItemSameAs(item, iItem);
if (isSame)
{
//
result = i;
break;
}
}
//
return result;
}
//
bool GetByIndex(
int index,
XPosition &item //
)
{
//
bool result = false;
//
NormalizeIndex(
index,
mItems //
);
//
item = mItems[index];
//
result = item.IsValid();
//
return result;
}
//
bool RemoveItem(XPosition &item)
{
//
bool result = false;
//
result = HasItem(item);
if (!result)
{
return result;
}
//
int itemIDX = FindItem(item);
result = ArrayRemove(
mItems,
itemIDX,
1 //
);
//
return result;
}
//
int RemoveItems(XPosition &items[])
{
//
int result = 0;
//
int itemsCount = ArraySize(items);
if (itemsCount <= 0)
{
return result;
}
//
for (int i = 0; i < itemsCount; i++)
{
//
XPosition iItem = items[i];
//
bool isRemoved = RemoveItem(iItem);
if (isRemoved)
{
result++;
}
}
//
return result;
}
//
int CountItems()
{
return ArraySize(mItems);
}
//
bool IsItemSameAs(
XPosition &source,
XPosition &dest // Compare Source with this ...
)
{
//
bool result = false;
//
result =
//
dest.IsValid() &&
source.IsValid() &&
source.symbol == dest.symbol &&
source.period == dest.period &&
source.ticket == dest.ticket &&
source.openAt == dest.openAt &&
source.provider == dest.provider
//
;
//
return result;
}
//
// Custom Functions ...
//
bool GetMaxInDrawdown(
XPosition &item // Result
)
{
//
bool result = false;
//
item.Clean();
//
int itemsCount = CountItems();
result = IsValidSize(itemsCount);
if (!result)
{
return result;
}
//
int maxIDX = -1;
for (int i = 0; i < itemsCount; i++)
{
//
XPosition iItem = mItems[i];
//
bool canSelect =
//
iItem.profit < 0
//
&&
//
(
//
(maxIDX == -1 && !item.IsValid())
? true
: item.profit < iItem.profit
//
)
//
;
if (canSelect)
{
//
maxIDX = i;
item = iItem;
}
}
//
result = maxIDX > -1 && item.IsValid();
//
return result;
}
//
bool GetMaxInProfit(
XPosition &item // Result
)
{
//
bool result = false;
//
item.Clean();
//
int itemsCount = CountItems();
result = IsValidSize(itemsCount);
if (!result)
{
return result;
}
//
int maxIDX = -1;
for (int i = 0; i < itemsCount; i++)
{
//
XPosition iItem = mItems[i];
//
bool canSelect =
//
iItem.profit > 0
//
&&
//
(
//
(maxIDX == -1 && !item.IsValid())
? true
: item.profit < iItem.profit
//
)
//
;
if (canSelect)
{
//
maxIDX = i;
item = iItem;
}
}
//
result = maxIDX > -1 && item.IsValid();
//
return result;
}
//
// Private ...
private:
//
// Props ...
XPosition mItems[];
};
//
// Class Definition ...
@@ -51,6 +496,7 @@ public:
double maxAllowedSpread, // Max Allowed Spred for Opening Trades
int maxAllowedPositions, // Max Allowed Positions
double maxAllowedDrawdownFactor, // Max Allowed Drawdown Factor
double staticVolume, // Static Volume for Positions
//
// Position Management ...
bool allowLong = true, // Allow Long Trades
@@ -80,6 +526,7 @@ public:
maxAllowedSpread,
maxAllowedPositions,
maxAllowedDrawdownFactor,
staticVolume,
allowLong,
allowShort,
minProfitPerTrade,
@@ -501,9 +948,8 @@ public:
// TODO:
// Make them Configurable ...
int levelsOnClose = 6;
int requiredAgeForSLTrail = 0;
int requiredDistanceForSLTrail = 100;
return;
int requiredAgeForTrail = 0;
int requiredDistanceForTrail = 5;
//
int positionsCount = ArraySize(positions);
@@ -531,7 +977,7 @@ public:
if (canManageState)
{
//
// Check Position in Profit or not ...
// When Position in Profit, Trail SL ...
if (iPosition.profit > 0)
{
//
@@ -550,7 +996,7 @@ public:
//
// Check Position Age ...
int iPositionAge = iPosition.GetAge(iPosition.period);
if (iPositionAge < requiredAgeForSLTrail)
if (iPositionAge < requiredAgeForTrail)
{
continue;
}
@@ -559,7 +1005,7 @@ public:
// Check Position Profit Distance ...
double priceDistanceInPoint = PriceToPoint(iPosition.profit / iPosition.volume, iPosition.symbol);
double priceDistance = PointToPrice(priceDistanceInPoint, iPosition.symbol);
if (priceDistance < requiredDistanceForSLTrail)
if (priceDistance < requiredDistanceForTrail)
{
continue;
}
@@ -568,17 +1014,17 @@ public:
double slDiff = PointToPrice(priceDistance, iPosition.symbol);
double appliedSL = slDiff / 2;
// //
// double sl =
// IsLong(iPosition.type)
// ? iPosition.sl + slDiff
// : iPosition.sl - slDiff;
//
double sl =
IsLong(iPosition.type)
? iPosition.entry + appliedSL
: iPosition.entry - appliedSL;
? iPosition.sl + slDiff
: iPosition.sl - slDiff;
//
// double sl =
// IsLong(iPosition.type)
// ? iPosition.entry + appliedSL
// : iPosition.entry - appliedSL;
//
bool isInit = trail.Init(
@@ -614,7 +1060,7 @@ public:
trail.time,
false //
);
if (age < requiredAgeForSLTrail)
if (age < requiredAgeForTrail)
{
continue;
}
@@ -622,7 +1068,7 @@ public:
//
double priceDistanceInPoint = PriceToPoint((iPosition.profit - trail.profit) / iPosition.volume, iPosition.symbol);
double priceDistance = PointToPrice(priceDistanceInPoint, iPosition.symbol);
if (priceDistance < (requiredDistanceForSLTrail / 2))
if (priceDistance < (requiredDistanceForTrail / 2))
{
// //
// // int positionAge = iPosition.GetAge(iPosition.period);
@@ -660,15 +1106,18 @@ public:
double appliedSL = slDiff / 2;
//
double selectedValue =
//
trail.level == 1 || (isLong
? iPosition.sl < iPosition.entry
: iPosition.sl > iPosition.entry)
? iPosition.entry
: iPosition.sl
//
;
double selectedValue = iPosition.sl;
// //
// double selectedValue =
// //
// trail.level == 1 || (isLong
// ? iPosition.sl < iPosition.entry
// : iPosition.sl > iPosition.entry)
// ? iPosition.entry
// : iPosition.sl
// //
// ;
//
double sl =
@@ -713,6 +1162,142 @@ public:
}
}
}
//
// When Position in Drawdown Trail TP ...
if (iPosition.profit < 0)
{
//
XTrail trail;
bool isTrailedBefore = GetTPTrail(
iPosition.ticket,
trail //
);
//
if (!isTrailedBefore)
{
//
// Add First Time ...
//
// Check Position Age ...
int iPositionAge = iPosition.GetAge(iPosition.period);
if (iPositionAge < requiredAgeForTrail)
{
continue;
}
//
// Check Position Profit Distance ...
double priceDistanceInPoint = PriceToPoint(MathAbs(iPosition.profit) / iPosition.volume, iPosition.symbol);
double priceDistance = PointToPrice(priceDistanceInPoint, iPosition.symbol);
if (priceDistance < requiredDistanceForTrail)
{
continue;
}
//
double tpDiff = PointToPrice(priceDistance, iPosition.symbol);
//
double tp =
IsLong(iPosition.type)
? iPosition.tp - tpDiff
: iPosition.tp + tpDiff;
//
bool isInit = trail.Init(
iPosition.ticket,
iPosition.type,
iPosition.profit,
iPosition.tp,
tp //
);
if (!isInit)
{
continue;
}
}
else
{
//
// Update Exists ...
//
// Check Profit ...
bool isProfitPassed = MathAbs(iPosition.profit) > MathAbs(trail.profit);
if (!isProfitPassed)
{
continue;
}
//
// Check Age Distance from Last ...
int age = iBarShift(
iPosition.symbol,
iPosition.period,
trail.time,
false //
);
if (age < requiredAgeForTrail)
{
continue;
}
//
double priceDistanceInPoint = PriceToPoint((MathAbs(iPosition.profit) - MathAbs(trail.profit)) / iPosition.volume, iPosition.symbol);
double priceDistance = PointToPrice(priceDistanceInPoint, iPosition.symbol);
//
bool isLong = IsLong(iPosition.type);
//
double tpDiff = PointToPrice(priceDistance, iPosition.symbol);
//
double tp =
isLong
? iPosition.tp - tpDiff
: iPosition.tp + tpDiff;
//
// Update Trail Values ...
bool isUpdated = trail.Update(
tp,
iPosition.profit //
);
if (!isUpdated)
{
continue;
}
}
//
if (trail.IsValid())
{
//
string comment = trail.GenerateTag();
bool isModified = mTrader.Modify(
trail.ticket,
iPosition.sl,
trail.after,
comment //
);
if (isModified)
{
//
RemoveTrail(trail.ticket);
//
AddTPTrail(trail);
//
string message = "Position (" + ToString(trail.ticket) + ") Trailed TP on Level: " + ToString(trail.level);
mAlert.Alert(message);
}
}
}
}
}
}
@@ -745,8 +1330,6 @@ public:
// Cleaning Source ...
Clean(signals);
//
//
// Loop through Signals for Management ...
for (int i = 0; i < tmpsCount; i++)
@@ -755,6 +1338,7 @@ public:
XSignal iSignal = tmps[i];
//
// dont Manage NON Signal Provider's signals ...
string provider = iSignal.provider;
if (!IsValid(provider))
{
@@ -777,13 +1361,23 @@ public:
NULL, // All Periods ...
iSignal.type //
);
XPosition symbolPositions[];
int positionsCount = mTrader.GetPositions(
positions,
iSignal.symbol,
NULL, // All Providers ...
NULL, // All Periods ...
iSignal.type //
);
double profit = CalculatePositionsProfit(positions);
double symbolProfit = CalculatePositionsProfit(symbolPositions);
if (positionsCount <= 0)
{
//
// First Signal in a While of Specific Provider ...
//
iSignal.volume *= 2;
// iSignal.volume *= 2;
//
AddRef(
@@ -808,6 +1402,14 @@ public:
//
continue;
}
else if (profit > 0 && symbolProfit > 0)
{
//
AddRef(
iSignal,
signals //
);
}
}
//
@@ -838,9 +1440,15 @@ public:
return false;
}
//
// Tools ...
//
// Management Tracker/Profit etc ...
//
// Handle Position Changed Event ...
void OnPositionsChangedHandler(int count)
void OnPositionsChangedHandler(int count) override
{
//
// Synchronize Tracker ...
@@ -848,77 +1456,135 @@ public:
}
//
// Tools ...
//
// Position Tracker ...
//
// Synchronize Positions and Position Tracker ...
void SyncPositionTracker()
// Called when a Position Going in Profit ...
void OnPositionGoingInProfit(XPositionTrack &track) override
{
//
static datetime lastSyncPositionTracker = NULL;
datetime cTime = TimeCurrent();
string message = "Position (" + ToString(track.ticket) + ", " + track.symbol + ") Dropped in Profit ...";
mAlert.Alert(message);
}
//
// Called when a Position Going to Drawdown ...
void OnPositionGoingToDrawdown(XPositionTrack &track) override
{
//
string message = "Position (" + ToString(track.ticket) + ", " + track.symbol + ") Dropped in Drawdown ...";
mAlert.Alert(message);
}
//
// Called when Account Going in Profit ...
void OnGoingToProfit(XProfitTrack &track) override
{
//
string message = "Account In Profit ...";
//
int syncInterval = PeriodSeconds(PERIOD_M5);
int lastDiff = (int)cTime - (int)lastSyncPositionTracker;
mAlert.Alert(message);
}
//
// Called when Account Going in Drawdown ...
void OnGoingToDrawdown(XProfitTrack &track) override
{
//
string message = "Account In Drawdown ...";
//
bool canSync =
lastSyncPositionTracker == NULL
? true
: lastDiff >= syncInterval;
if (!canSync)
mAlert.Alert(message);
//
// Here i Have to Detect In DrawDown Positions ...
//
XPosition positions[];
int positionsCount = mTrader.GetInDrawdownPositions(
positions);
if (!IsValidSize(positionsCount))
{
return;
}
//
XPositionTrackerState state;
int synced = mPositionTracker.Sync(
state,
mTrader //
);
if (synced > 0)
// Create a HashMap for Holding Positions ...
CHashMap<string, XSCPositionHolder *> symbolMap;
//
// Loop Through Positions ...
for (int i = 0; i < positionsCount; i++)
{
//
// Check State and Do What we Want ...
int onProfitsCount = ArraySize(state.onProfits);
int onDrawdownCount = ArraySize(state.onDrawdowns);
XPosition iPosition = positions[i];
//
// Do What we want whe a Position Dropped to Prefit ...
if (onProfitsCount > 0)
bool hasIKey = symbolMap.ContainsKey(iPosition.symbol);
//
// Empty Class Pointer ...
XSCPositionHolder *mHolder = NULL;
//
// Add new to Map ...
if (!hasIKey)
{
//
for (int i = 0; i < onProfitsCount; i++)
mHolder = new XSCPositionHolder();
if (symbolMap.TrySetValue(iPosition.symbol, mHolder))
{
//
XPositionTrack iTrack = state.onProfits[i];
//
string message = "Position (" + ToString(iTrack.ticket) + ", " + iTrack.symbol + ") Dropped in Profit ...";
mAlert.Alert(message);
mHolder.AddOrUpdateItem(iPosition);
}
}
//
// Update Exists in Map ...
else
{
//
if (symbolMap.TryGetValue(iPosition.symbol, mHolder))
{
mHolder.AddOrUpdateItem(iPosition);
}
}
}
//
// Now we Have a Map which Filled Based on Separate Symbols and InDrawDown Positions ...
int mapCount = symbolMap.Count();
if (!IsValidSize(mapCount))
{
return;
}
//
string mSymbols[];
XSCPositionHolder *mHolders[];
mapCount = symbolMap.CopyTo(
mSymbols,
mHolders //
);
if (!IsValidSize(mapCount))
{
return;
}
//
for (int i = 0; i < mapCount; i++)
{
//
string iSymbol = mSymbols[i];
XSCPositionHolder *iHolder = mHolders[i];
//
XPosition iMaxInDP;
bool hasMaxInDP = iHolder.GetMaxInDrawdown(iMaxInDP);
if (!hasMaxInDP)
{
continue;
}
//
// Do what we want when a Position Dropped to Drawdown ...
if (onDrawdownCount > 0)
{
//
for (int i = 0; i < onDrawdownCount; i++)
{
//
XPositionTrack iTrack = state.onDrawdowns[i];
//
string message = "Position (" + ToString(iTrack.ticket) + ", " + iTrack.symbol + ") Dropped in Drawdown ...";
mAlert.Alert(message);
}
}
// Try to Protect a Position ...
ProtectPosition(iMaxInDP);
}
}
@@ -949,6 +1615,21 @@ protected:
}
}
//
// Protect Specified Position ...
void ProtectPosition(XPosition &position)
{
//
// Here i Can Protect Position ...
//
string message = "For (" + position.symbol + ") Max In Drawdown Position is: (" +
ToString(position.ticket) + "), by Profit: " + ToString(position.profit);
//
mAlert.Alert(message);
}
//
// Private ...
private: