3906 lines
81 KiB
Plaintext
3906 lines
81 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Class Library
|
|
// ----------------------------------------------
|
|
// Name: XSCBaseEA
|
|
// Description: provides all Base EA requirements ...
|
|
//
|
|
//
|
|
// 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 <Generic/HashMap.mqh>
|
|
#include "../Classes/x-saherelm.xalert.class.mq5"
|
|
#include "../Classes/x-saherelm.xtrade.class.mq5"
|
|
|
|
//
|
|
// Define On Signal Event Handler Type ...
|
|
typedef void (*TOnSignal)(XSignal &signals[]);
|
|
|
|
//
|
|
// Tokens ...
|
|
string XINProfitToken = "INP";
|
|
string XINDrawdownToken = "IND";
|
|
string XProfitsToken = "PFS";
|
|
string XTimesToken = "TMS";
|
|
string XPricessToken = "PCS";
|
|
|
|
//
|
|
enum ENUM_X_PROFIT_TRACK_FIELDS
|
|
{
|
|
X_PROFIT_TRACK_PROFIT,
|
|
X_PROFIT_TRACK_LONGS_PROFIT,
|
|
X_PROFIT_TRACK_SHORTS_PROFIT,
|
|
};
|
|
|
|
//
|
|
// Model Account Profits ...
|
|
struct XProfitTrack
|
|
{
|
|
//
|
|
int longs; // Number of Long ...
|
|
int shorts; // Number of Short ...
|
|
|
|
//
|
|
double profit; // Profit of All ...
|
|
double longsProfit; // Profits of Longs ...
|
|
double shortsProfit; // Profits of Shorts ...
|
|
|
|
//
|
|
double profitFrom; // Profit of All ...
|
|
double longsProfitFrom; // Profits of Longs ...
|
|
double shortsProfitFrom; // Profits of Shorts ...
|
|
|
|
//
|
|
double volume; // Volume ...
|
|
double longsVolume; // Volume of Longs ...
|
|
double shortsVolume; // Volume of Shorts ...
|
|
|
|
//
|
|
datetime time; // Check Time ...
|
|
|
|
//
|
|
XProfitTrack()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Initialization ...
|
|
bool Init(XSCTrade *mTrader)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(positions);
|
|
result = positionsCount > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
bool isLong = IsLong(iPosition.type);
|
|
|
|
//
|
|
if (isLong)
|
|
{
|
|
//
|
|
longs++;
|
|
longsVolume += iPosition.volume;
|
|
longsProfit += iPosition.profit;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
shorts++;
|
|
shortsVolume += iPosition.volume;
|
|
shortsProfit += iPosition.profit;
|
|
}
|
|
|
|
//
|
|
profit += iPosition.profit;
|
|
volume += iPosition.volume;
|
|
}
|
|
|
|
//
|
|
result = IsValid();
|
|
if (result)
|
|
{
|
|
time = TimeCurrent();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double SelectField(ENUM_X_PROFIT_TRACK_FIELDS field)
|
|
{
|
|
//
|
|
double result = profit;
|
|
|
|
//
|
|
switch (field)
|
|
{
|
|
//
|
|
case X_PROFIT_TRACK_LONGS_PROFIT:
|
|
result = longsProfit;
|
|
break;
|
|
|
|
//
|
|
case X_PROFIT_TRACK_SHORTS_PROFIT:
|
|
result = shortsProfit;
|
|
break;
|
|
|
|
//
|
|
default:
|
|
case X_PROFIT_TRACK_PROFIT:
|
|
result = profit;
|
|
break;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Cleanup ...
|
|
void Clean()
|
|
{
|
|
//
|
|
longs = 0;
|
|
shorts = 0;
|
|
profit = 0;
|
|
volume = 0;
|
|
profitFrom = 0;
|
|
longsVolume = 0;
|
|
longsProfit = 0;
|
|
shortsVolume = 0;
|
|
shortsProfit = 0;
|
|
longsProfitFrom = 0;
|
|
shortsProfitFrom = 0;
|
|
|
|
//
|
|
time = NULL;
|
|
}
|
|
|
|
//
|
|
// Validate ...
|
|
bool IsValid()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
//
|
|
(longs > 0 || shorts > 0)
|
|
//
|
|
&&
|
|
//
|
|
volume > 0
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
};
|
|
|
|
//
|
|
// Track Specific Position ...
|
|
struct XPositionTrack
|
|
{
|
|
//
|
|
ulong ticket; // Position Ticket
|
|
string symbol; // Position Symbol
|
|
ENUM_POSITION_TYPE type; // Position Type
|
|
ENUM_TIMEFRAMES period; // Position Period
|
|
|
|
//
|
|
double price[]; // Check Price ...
|
|
double profit[]; // Check Profit ...
|
|
datetime time[]; // Check Time ...
|
|
|
|
//
|
|
double tp; // Current TP
|
|
double sl; // Current SL
|
|
double volume; // Current Volume
|
|
double entry; // Position Entry
|
|
datetime openAt; // Position Open Time
|
|
|
|
//
|
|
int inProfitCount; // Number of Cross in DrawDown
|
|
int inDrawdownCount; // Number of Crossed in Profit
|
|
|
|
//
|
|
// Constructor ...
|
|
XPositionTrack()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Initialization ...
|
|
bool Init(
|
|
XPosition &position // Source Position
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
Clean();
|
|
|
|
//
|
|
result = position.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ticket = position.ticket;
|
|
symbol = position.symbol;
|
|
period = position.period;
|
|
type = position.type;
|
|
tp = position.tp;
|
|
sl = position.sl;
|
|
entry = position.entry;
|
|
volume = position.volume;
|
|
openAt = position.openAt;
|
|
|
|
//
|
|
datetime cTime = TimeCurrent();
|
|
Add(
|
|
cTime,
|
|
time //
|
|
);
|
|
|
|
//
|
|
Add(
|
|
position.profit,
|
|
profit //
|
|
);
|
|
|
|
//
|
|
Add(
|
|
position.price,
|
|
price //
|
|
);
|
|
|
|
//
|
|
result = IsValid();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool Update(
|
|
XPosition &position // Source Position
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check Current Model is Valid or not ...
|
|
result = IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Position is a Valid Model ...
|
|
result = position.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Current Model belongs to Given Position ...
|
|
result =
|
|
//
|
|
entry == position.entry &&
|
|
openAt == position.openAt &&
|
|
symbol == position.symbol &&
|
|
period == position.period &&
|
|
type == position.type &&
|
|
ticket == position.ticket
|
|
//
|
|
;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
tp = position.tp;
|
|
sl = position.sl;
|
|
volume = position.volume;
|
|
|
|
//
|
|
datetime cTime = TimeCurrent();
|
|
|
|
//
|
|
SetArrayStates(false);
|
|
|
|
//
|
|
Add(
|
|
cTime,
|
|
time //
|
|
);
|
|
|
|
//
|
|
Add(
|
|
position.profit,
|
|
profit //
|
|
);
|
|
|
|
//
|
|
Add(
|
|
position.price,
|
|
price //
|
|
);
|
|
|
|
//
|
|
SetArrayStates(true);
|
|
|
|
//
|
|
result = IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Fill In Profit or Drawdown Counters ...
|
|
|
|
//
|
|
bool isCrossInProfit = IsCrossedInProfit();
|
|
if (isCrossInProfit)
|
|
{
|
|
inProfitCount++;
|
|
}
|
|
|
|
//
|
|
bool isCrossedInDrawdown = IsCrossedInDrawdown();
|
|
if (isCrossedInDrawdown)
|
|
{
|
|
inDrawdownCount++;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Cleanup ...
|
|
void
|
|
Clean()
|
|
{
|
|
//
|
|
ticket = 0;
|
|
type = NULL;
|
|
symbol = NULL;
|
|
period = NULL;
|
|
|
|
//
|
|
tp = 0;
|
|
sl = 0;
|
|
volume = 0;
|
|
entry = 0;
|
|
openAt = NULL;
|
|
|
|
//
|
|
inProfitCount = 0;
|
|
inDrawdownCount = 0;
|
|
|
|
//
|
|
Clean(price);
|
|
Clean(profit);
|
|
Clean(time);
|
|
}
|
|
|
|
//
|
|
// Validate ...
|
|
bool IsValid()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
//
|
|
ticket > 0 &&
|
|
type != NULL &&
|
|
period != NULL &&
|
|
volume > 0 &&
|
|
entry > 0 &&
|
|
openAt > 0
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
void SetArrayStates(bool state)
|
|
{
|
|
//
|
|
ArraySetAsSeries(price, state);
|
|
ArraySetAsSeries(profit, state);
|
|
ArraySetAsSeries(time, state);
|
|
}
|
|
|
|
//
|
|
bool IsCrossedInProfit()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = ArraySize(time) > 1;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result =
|
|
//
|
|
profit[0] > 0 &&
|
|
profit[1] < 0
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool IsCrossedInDrawdown()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = ArraySize(time) > 1;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result =
|
|
//
|
|
profit[0] < 0 &&
|
|
profit[1] > 0
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Collector Tools ...
|
|
|
|
//
|
|
string GetToken()
|
|
{
|
|
return GetToken(this);
|
|
}
|
|
|
|
//
|
|
bool IsModelValid()
|
|
{
|
|
return IsValid();
|
|
}
|
|
|
|
//
|
|
bool IsSameAs(XPositionTrack &track)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
//
|
|
track.IsValid() &&
|
|
type == track.type &&
|
|
ticket == track.ticket &&
|
|
symbol == track.symbol &&
|
|
period == track.period &&
|
|
openAt == track.openAt
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int FindIndex(
|
|
const XPositionTrack &values[] // Collection
|
|
)
|
|
{
|
|
//
|
|
return FindIndex(
|
|
this,
|
|
values
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Converts a Model to String Representation ...
|
|
string ToModelString()
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
if (!IsValid())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// For Modeling a PositionTrack Object ...
|
|
// - tp
|
|
// - sl
|
|
// - type
|
|
// - entry
|
|
// - ticket
|
|
// - symbol
|
|
// - period
|
|
// - openAt
|
|
// - volume
|
|
// - inProfitCount
|
|
// - inDrawdownCount
|
|
// - time[]
|
|
// - price[]
|
|
// - profit[]
|
|
|
|
//
|
|
// Ticket ...
|
|
string ticketStr = Surround(XIDToken, ticket);
|
|
|
|
//
|
|
// Symbol ...
|
|
string SymbolStr = Surround(XSymbolToken, symbol);
|
|
|
|
//
|
|
// Type ...
|
|
string typeStr = Surround(XTypeToken, type);
|
|
|
|
//
|
|
// Period ...
|
|
int iPeriodInt = (int)period;
|
|
string periodStr = Surround(XPeriodToken, iPeriodInt);
|
|
|
|
//
|
|
// TP ...
|
|
string tpStr = Surround(XTPToken, tp);
|
|
|
|
//
|
|
// SL ...
|
|
string slStr = Surround(XSLToken, sl);
|
|
|
|
//
|
|
// Volume ...
|
|
string volumeStr = Surround(XVolumeToken, volume);
|
|
|
|
//
|
|
// Entry ...
|
|
string entryStr = Surround(XEntryToken, entry);
|
|
|
|
//
|
|
// OpenAt ...
|
|
string timeStr = Surround(XTimeToken, openAt);
|
|
|
|
//
|
|
// InProfitCount ...
|
|
string inProfitCountStr = Surround(XINProfitToken, inProfitCount);
|
|
|
|
//
|
|
// InDrawdownCount ...
|
|
string inDrawdownCountStr = Surround(XINDrawdownToken, inDrawdownCount);
|
|
|
|
//
|
|
// Time(s) ...
|
|
string timesContentStr = ToString(time);
|
|
string timesStr = Surround(XTimesToken, timesContentStr);
|
|
|
|
//
|
|
// Price(s) ...
|
|
string pricesContentStr = ToString(price);
|
|
string pricesStr = Surround(XPricessToken, pricesContentStr);
|
|
|
|
//
|
|
// Profit(s) ...
|
|
string profitsContentStr = ToString(profit);
|
|
string profitsStr = Surround(XProfitsToken, profitsContentStr);
|
|
|
|
//
|
|
string token = GetToken();
|
|
|
|
//
|
|
result =
|
|
//
|
|
token +
|
|
"[" +
|
|
//
|
|
ticketStr +
|
|
SymbolStr +
|
|
typeStr +
|
|
periodStr +
|
|
tpStr +
|
|
slStr +
|
|
volumeStr +
|
|
entryStr +
|
|
timeStr +
|
|
inProfitCountStr +
|
|
inDrawdownCountStr +
|
|
timesStr +
|
|
pricesStr +
|
|
profitsStr +
|
|
//
|
|
"]"
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Pres and Fill Model based on it's String Representation ...
|
|
bool ParseModel(string value)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
Clean();
|
|
|
|
//
|
|
result = IsValid(value);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// For Modeling a PositionTrack Object ...
|
|
// - tp
|
|
// - sl
|
|
// - type
|
|
// - entry
|
|
// - ticket
|
|
// - symbol
|
|
// - period
|
|
// - openAt
|
|
// - volume
|
|
// - inProfitCount
|
|
// - inDrawdownCount
|
|
// - time[]
|
|
// - price[]
|
|
// - profit[]
|
|
|
|
//
|
|
// TP ...
|
|
tp = ParseDoubleSurrounded(value, XTPToken);
|
|
|
|
//
|
|
// SL ...
|
|
tp = ParseDoubleSurrounded(value, XSLToken);
|
|
|
|
//
|
|
// Type ...
|
|
type = ((ENUM_POSITION_TYPE)ParseIntSurrounded(value, XTypeToken));
|
|
|
|
//
|
|
// Entry ...
|
|
entry = ParseDoubleSurrounded(value, XEntryToken);
|
|
|
|
//
|
|
// Ticket ...
|
|
ticket = ParseLongSurrounded(value, XIDToken);
|
|
|
|
//
|
|
// Symbol ...
|
|
symbol = ParseStringSurrounded(value, XSymbolToken);
|
|
|
|
//
|
|
// Period ...
|
|
period = ((ENUM_TIMEFRAMES)ParseIntSurrounded(value, XPeriodToken));
|
|
|
|
//
|
|
// Open At ...
|
|
openAt = ParseTimeSurrounded(value, XTimeToken);
|
|
|
|
//
|
|
// Volume ...
|
|
volume = ParseDoubleSurrounded(value, XVolumeToken);
|
|
|
|
//
|
|
// InProfitCount ...
|
|
inProfitCount = ParseIntSurrounded(value, XINProfitToken);
|
|
|
|
//
|
|
// InDrawdownCount ...
|
|
inDrawdownCount = ParseIntSurrounded(value, XINDrawdownToken);
|
|
|
|
//
|
|
// Time(s) ...
|
|
ParseTimeArraySurrounded(
|
|
time,
|
|
value,
|
|
XTimesToken
|
|
//
|
|
);
|
|
|
|
//
|
|
// Price(s) ...
|
|
ParseDoubleArraySurrounded(
|
|
price,
|
|
value,
|
|
XPricessToken
|
|
//
|
|
);
|
|
|
|
//
|
|
// Profit(s) ...
|
|
ParseDoubleArraySurrounded(
|
|
profit,
|
|
value,
|
|
XProfitsToken
|
|
//
|
|
);
|
|
|
|
//
|
|
result = IsValid();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
};
|
|
|
|
//
|
|
// Type Definitions for XPosition Tracker Events ...
|
|
typedef void (*TOnPositionState)(const XPositionTrack &track);
|
|
|
|
//
|
|
// an Struct for Holding Event Handled Items for Position Tracker ...
|
|
struct XPositionTrackerState
|
|
{
|
|
//
|
|
XPositionTrack inProfits[];
|
|
XPositionTrack inDrawdowns[];
|
|
|
|
//
|
|
XPositionTrack onProfits[];
|
|
XPositionTrack onDrawdowns[];
|
|
|
|
//
|
|
XPositionTrackerState()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Cleanup ...
|
|
void Clean()
|
|
{
|
|
//
|
|
Clean(inProfits);
|
|
Clean(inDrawdowns);
|
|
|
|
//
|
|
Clean(onProfits);
|
|
Clean(onDrawdowns);
|
|
}
|
|
};
|
|
|
|
//
|
|
// Position Tracker Class ...
|
|
class XSCPositionTracker
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Constructor ...
|
|
void XSCPositionTracker(bool cleanStore = false)
|
|
{
|
|
//
|
|
XSCDataCollector<XPositionTrack> mBase;
|
|
|
|
//
|
|
if (cleanStore)
|
|
{
|
|
mBase.Clear();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Cleaning Store ...
|
|
void Clear()
|
|
{
|
|
//
|
|
XSCDataCollector<XPositionTrack> mBase;
|
|
|
|
//
|
|
mBase.Clear();
|
|
}
|
|
|
|
//
|
|
// Collect All Store ...
|
|
int Collect(XPositionTrack &result[])
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
XSCDataCollector<XPositionTrack> mBase;
|
|
|
|
//
|
|
mResult = mBase.Collect(result);
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Add Item To Store ...
|
|
bool Add(XPositionTrack &item)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
XSCDataCollector<XPositionTrack> mBase;
|
|
|
|
//
|
|
result = mBase.Add(item);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Add Item(s) To Store ...
|
|
int Add(XPositionTrack &items[])
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
XSCDataCollector<XPositionTrack> mBase;
|
|
|
|
//
|
|
result = mBase.Add(items);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Remove an Item from Store ...
|
|
bool Remove(XPositionTrack &item)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
XSCDataCollector<XPositionTrack> mBase;
|
|
|
|
//
|
|
result = mBase.Remove(item);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Custom Functions ...
|
|
|
|
//
|
|
// Sync all Content using Specific Trader Class ...
|
|
int Sync(
|
|
XPositionTrackerState &state, // Hold all Changes State ...
|
|
XSCTrade *mTrader // XSCTrade Instance for Synchronize ...
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(positions);
|
|
|
|
//
|
|
ulong trackTickets[];
|
|
XPositionTrack trackers[];
|
|
int trackersCount = Collect(trackers);
|
|
|
|
//
|
|
// Nothing to Do ...
|
|
if (positionsCount <= 0 && trackersCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Collect Exists Trackers Ticket's For Managing ...
|
|
for (int i = 0; i < trackersCount; i++)
|
|
{
|
|
//
|
|
XPositionTrack iTrack = trackers[i];
|
|
|
|
//
|
|
bool isValid = iTrack.IsValid();
|
|
if (isValid)
|
|
{
|
|
//
|
|
AddSpecific(
|
|
iTrack.ticket,
|
|
trackTickets //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clear Tracker Store for Update Later ...
|
|
Clear();
|
|
|
|
//
|
|
// Collect All Required to Add/Update Trackers here ...
|
|
XPositionTrack syncedTracks[];
|
|
|
|
//
|
|
// a Flag for Synchronization Performance ...
|
|
bool hasTracker = trackersCount > 0;
|
|
|
|
//
|
|
// Loop through Position for Managing Trackers ...
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
// if there isn't any Track before ...
|
|
if (!hasTracker)
|
|
{
|
|
//
|
|
XPositionTrack iTrack;
|
|
bool isInited = iTrack.Init(iPosition);
|
|
if (isInited)
|
|
{
|
|
//
|
|
AddRef(
|
|
iTrack,
|
|
syncedTracks //
|
|
);
|
|
|
|
//
|
|
FillState(
|
|
iTrack,
|
|
state //
|
|
);
|
|
}
|
|
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// if there was some tracks ...
|
|
|
|
//
|
|
// Try to Find Position Ticket in Track Tickets ...
|
|
int iTicketIdx = FindIndex(
|
|
iPosition.ticket,
|
|
trackTickets //
|
|
);
|
|
bool isTrackedBefore = iTicketIdx >= 0;
|
|
|
|
//
|
|
// Add new Track ...
|
|
if (!isTrackedBefore)
|
|
{
|
|
//
|
|
XPositionTrack iTrack;
|
|
bool isInited = iTrack.Init(iPosition);
|
|
if (isInited)
|
|
{
|
|
//
|
|
AddRef(
|
|
iTrack,
|
|
syncedTracks //
|
|
);
|
|
|
|
//
|
|
FillState(
|
|
iTrack,
|
|
state //
|
|
);
|
|
}
|
|
}
|
|
//
|
|
// Update Exists Track ...
|
|
else
|
|
{
|
|
//
|
|
XPositionTrack iTrack = trackers[iTicketIdx];
|
|
bool isUpdated = iTrack.Update(iPosition);
|
|
if (isUpdated)
|
|
{
|
|
//
|
|
AddRef(
|
|
iTrack,
|
|
syncedTracks //
|
|
);
|
|
|
|
//
|
|
FillState(
|
|
iTrack,
|
|
state //
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Synced Tracks ...
|
|
int syncedTracksCount = ArraySize(syncedTracks);
|
|
if (syncedTracksCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = Add(syncedTracks);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Event Handlers ...
|
|
|
|
//
|
|
// Add Profit Event Handler ...
|
|
void AddOnProfitEventHandler(TOnPositionState handler)
|
|
{
|
|
//
|
|
AddSpecific(
|
|
handler,
|
|
mOnProfitEventHandlers //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add Drawdown Event Handler ...
|
|
void AddOnDrawdownEventHandler(TOnPositionState handler)
|
|
{
|
|
//
|
|
AddSpecific(
|
|
handler,
|
|
mOnDrawdownEventHandlers //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Event Handlers ...
|
|
|
|
//
|
|
// OnProfit ...
|
|
void NotifyOnProfitEvent(XPositionTrack &track)
|
|
{
|
|
//
|
|
int count = ArraySize(mOnProfitEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnPositionState iHandler = mOnProfitEventHandlers[i];
|
|
iHandler(track);
|
|
}
|
|
}
|
|
|
|
//
|
|
// OnDrawdown ...
|
|
void NotifyOnDrawdownEvent(XPositionTrack &track)
|
|
{
|
|
//
|
|
int count = ArraySize(mOnDrawdownEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnPositionState iHandler = mOnDrawdownEventHandlers[i];
|
|
iHandler(track);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
TOnPositionState mOnProfitEventHandlers[];
|
|
TOnPositionState mOnDrawdownEventHandlers[];
|
|
|
|
//
|
|
void FillState(
|
|
XPositionTrack &track,
|
|
XPositionTrackerState &state //
|
|
)
|
|
{
|
|
//
|
|
// In Profit ...
|
|
bool isInProfit = track.profit[0] > 0;
|
|
if (isInProfit)
|
|
{
|
|
//
|
|
AddRef(
|
|
track,
|
|
state.inProfits //
|
|
);
|
|
}
|
|
|
|
//
|
|
// In Drawdown ...
|
|
bool isInDrawdown = track.profit[0] < 0;
|
|
if (isInDrawdown)
|
|
{
|
|
//
|
|
AddRef(
|
|
track,
|
|
state.inDrawdowns //
|
|
);
|
|
}
|
|
|
|
//
|
|
// On Profit ...
|
|
bool isOnProfit = track.IsCrossedInProfit();
|
|
if (isOnProfit)
|
|
{
|
|
//
|
|
AddRef(
|
|
track,
|
|
state.onProfits //
|
|
);
|
|
}
|
|
|
|
//
|
|
// On Drawdown ...
|
|
bool isOnDrawdown = track.IsCrossedInDrawdown();
|
|
if (isOnDrawdown)
|
|
{
|
|
//
|
|
AddRef(
|
|
track,
|
|
state.onDrawdowns //
|
|
);
|
|
}
|
|
}
|
|
};
|
|
|
|
//
|
|
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;
|
|
}
|
|
|
|
//
|
|
bool GetFarestEntry(
|
|
XPosition &item, // Result
|
|
string symbol = NULL,
|
|
string provider = NULL,
|
|
ENUM_TIMEFRAMES period = NULL,
|
|
ENUM_POSITION_TYPE type = NULL //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
item.Clean();
|
|
|
|
//
|
|
int itemsCount = CountItems();
|
|
result = IsValidSize(itemsCount);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < itemsCount; i++)
|
|
{
|
|
//
|
|
XPosition iItem = mItems[i];
|
|
|
|
//
|
|
bool isFiltersPassed =
|
|
//
|
|
iItem.IsFiltersPassed(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type //
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
if (!isFiltersPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Do Additional Filtering ...
|
|
bool isValidEntry =
|
|
//
|
|
!item.IsValid()
|
|
? true
|
|
: item.entry < iItem.entry
|
|
//
|
|
;
|
|
if (isValidEntry)
|
|
{
|
|
//
|
|
item = iItem;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = item.IsValid();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool GetNearestEntry(
|
|
XPosition &item, // Result
|
|
string symbol = NULL,
|
|
string provider = NULL,
|
|
ENUM_TIMEFRAMES period = NULL,
|
|
ENUM_POSITION_TYPE type = NULL //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
item.Clean();
|
|
|
|
//
|
|
int itemsCount = CountItems();
|
|
result = IsValidSize(itemsCount);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < itemsCount; i++)
|
|
{
|
|
//
|
|
XPosition iItem = mItems[i];
|
|
|
|
//
|
|
bool isFiltersPassed =
|
|
//
|
|
iItem.IsFiltersPassed(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type //
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
if (!isFiltersPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Do Additional Filtering ...
|
|
bool isValidEntry =
|
|
//
|
|
!item.IsValid()
|
|
? true
|
|
: item.entry > iItem.entry
|
|
//
|
|
;
|
|
if (isValidEntry)
|
|
{
|
|
//
|
|
item = iItem;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = item.IsValid();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool GetFarestSL(
|
|
XPosition &item, // Result
|
|
string symbol = NULL,
|
|
string provider = NULL,
|
|
ENUM_TIMEFRAMES period = NULL,
|
|
ENUM_POSITION_TYPE type = NULL //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
item.Clean();
|
|
|
|
//
|
|
int itemsCount = CountItems();
|
|
result = IsValidSize(itemsCount);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < itemsCount; i++)
|
|
{
|
|
//
|
|
XPosition iItem = mItems[i];
|
|
|
|
//
|
|
bool isFiltersPassed =
|
|
//
|
|
iItem.IsFiltersPassed(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type //
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
if (!isFiltersPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Do Additional Filtering ...
|
|
bool isValidEntry =
|
|
//
|
|
!item.IsValid()
|
|
? true
|
|
: item.sl < iItem.sl
|
|
//
|
|
;
|
|
if (isValidEntry)
|
|
{
|
|
//
|
|
item = iItem;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = item.IsValid();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool GetNearestSL(
|
|
XPosition &item, // Result
|
|
string symbol = NULL,
|
|
string provider = NULL,
|
|
ENUM_TIMEFRAMES period = NULL,
|
|
ENUM_POSITION_TYPE type = NULL //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
item.Clean();
|
|
|
|
//
|
|
int itemsCount = CountItems();
|
|
result = IsValidSize(itemsCount);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < itemsCount; i++)
|
|
{
|
|
//
|
|
XPosition iItem = mItems[i];
|
|
|
|
//
|
|
bool isFiltersPassed =
|
|
//
|
|
iItem.IsFiltersPassed(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type //
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
if (!isFiltersPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Do Additional Filtering ...
|
|
bool isValidEntry =
|
|
//
|
|
!item.IsValid()
|
|
? true
|
|
: item.sl > iItem.sl
|
|
//
|
|
;
|
|
if (isValidEntry)
|
|
{
|
|
//
|
|
item = iItem;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = item.IsValid();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool GetFarestTP(
|
|
XPosition &item, // Result
|
|
string symbol = NULL,
|
|
string provider = NULL,
|
|
ENUM_TIMEFRAMES period = NULL,
|
|
ENUM_POSITION_TYPE type = NULL //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
item.Clean();
|
|
|
|
//
|
|
int itemsCount = CountItems();
|
|
result = IsValidSize(itemsCount);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < itemsCount; i++)
|
|
{
|
|
//
|
|
XPosition iItem = mItems[i];
|
|
|
|
//
|
|
bool isFiltersPassed =
|
|
//
|
|
iItem.IsFiltersPassed(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type //
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
if (!isFiltersPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Do Additional Filtering ...
|
|
bool isValidEntry =
|
|
//
|
|
!item.IsValid()
|
|
? true
|
|
: item.tp < iItem.tp
|
|
//
|
|
;
|
|
if (isValidEntry)
|
|
{
|
|
//
|
|
item = iItem;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = item.IsValid();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool GetNearestTP(
|
|
XPosition &item, // Result
|
|
string symbol = NULL,
|
|
string provider = NULL,
|
|
ENUM_TIMEFRAMES period = NULL,
|
|
ENUM_POSITION_TYPE type = NULL //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
item.Clean();
|
|
|
|
//
|
|
int itemsCount = CountItems();
|
|
result = IsValidSize(itemsCount);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < itemsCount; i++)
|
|
{
|
|
//
|
|
XPosition iItem = mItems[i];
|
|
|
|
//
|
|
bool isFiltersPassed =
|
|
//
|
|
iItem.IsFiltersPassed(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type //
|
|
)
|
|
//
|
|
;
|
|
|
|
//
|
|
if (!isFiltersPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Do Additional Filtering ...
|
|
bool isValidEntry =
|
|
//
|
|
!item.IsValid()
|
|
? true
|
|
: item.tp > iItem.tp
|
|
//
|
|
;
|
|
if (isValidEntry)
|
|
{
|
|
//
|
|
item = iItem;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = item.IsValid();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Props ...
|
|
XPosition mItems[];
|
|
};
|
|
|
|
//
|
|
// Class Definition ...
|
|
|
|
class XSCBaseEA : public XSCBaseAlert
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Constructor ...
|
|
XSCBaseEA(
|
|
//
|
|
// XTrade Class Requirements ...
|
|
int slippage, // Specify Slippage
|
|
ulong magicNumber, // Specify Magic Number
|
|
int manageInterval, // Manager Check Intervals Seconds
|
|
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
|
|
bool allowShort = true, // Allow Short Trades
|
|
double minProfitPerTrade = 0, // Min Profit Per Trade based On Volume Factor (Hedge)
|
|
double minProfitPerVolumeFactor = 0, // Min Volume Factor for Calculating Profit (Hedge)
|
|
//
|
|
// 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
|
|
//
|
|
// Log Handler ...
|
|
bool enableAlerts = true, // Enable Alerts
|
|
bool logAlerts = true, // Log Alerts
|
|
bool terminalAlerts = false, // Terminal Alerts
|
|
bool mailAlerts = false, // Mail Alerts
|
|
bool pushAlerts = false // Push Alerts
|
|
)
|
|
{
|
|
//
|
|
// Instance XSCTrade Class ...
|
|
mTrader = new XSCTrade(
|
|
slippage,
|
|
magicNumber,
|
|
manageInterval,
|
|
maxAllowedSpread,
|
|
maxAllowedPositions,
|
|
maxAllowedDrawdownFactor
|
|
//
|
|
);
|
|
|
|
//
|
|
mIsInTestMode = IsRunningOnTestMode();
|
|
|
|
//
|
|
// Position Management ...
|
|
mAllowLong = allowLong;
|
|
mAllowShort = allowShort;
|
|
mMinProfitPerTrade = minProfitPerTrade;
|
|
mMinProfitPerVolumeFactor = minProfitPerVolumeFactor;
|
|
mStaticVolume = staticVolume;
|
|
|
|
//
|
|
// Set Event Handlers ...
|
|
mOnStopLossTriggered = onStopLossTriggered;
|
|
mOnTakeProfitTriggered = onTakeProfitTriggered;
|
|
mOnDealsChangedHandler = onDealsChangedHandler;
|
|
mOnOrdersChangedHandler = onOrdersChangedHandler;
|
|
mOnPositionsChangedHandler = onPositionsChangedHandler;
|
|
mOnTradeStateChangedHandler = onTradeStateChangedHandler;
|
|
|
|
//
|
|
// Add Event Listeners to XTrade Class if they Provided ...
|
|
if (mOnStopLossTriggered != NULL)
|
|
{
|
|
mTrader.AddOnStopLossEventHandler(mOnStopLossTriggered);
|
|
}
|
|
|
|
//
|
|
if (mOnTakeProfitTriggered != NULL)
|
|
{
|
|
mTrader.AddOnTakeProfitEventHandler(mOnTakeProfitTriggered);
|
|
}
|
|
|
|
//
|
|
if (mOnDealsChangedHandler != NULL)
|
|
{
|
|
mTrader.AddOnDealsChangedEventHandler(mOnDealsChangedHandler);
|
|
}
|
|
|
|
//
|
|
if (mOnOrdersChangedHandler != NULL)
|
|
{
|
|
mTrader.AddOnOrdersChangedEventHandler(mOnOrdersChangedHandler);
|
|
}
|
|
|
|
//
|
|
if (mOnPositionsChangedHandler != NULL)
|
|
{
|
|
mTrader.AddOnPositionsChangedEventHandler(mOnPositionsChangedHandler);
|
|
}
|
|
|
|
//
|
|
if (mOnTradeStateChangedHandler != NULL)
|
|
{
|
|
mTrader.AddOnTradeStateChangedEventHandler(mOnTradeStateChangedHandler);
|
|
}
|
|
|
|
//
|
|
// Instantiate XPositionTracker Class ...
|
|
mPositionTracker = new XSCPositionTracker();
|
|
|
|
//
|
|
// Do Synchronisation after Instantiate everything ...
|
|
XPositionTrackerState state;
|
|
mPositionTracker.Sync(
|
|
state,
|
|
mTrader //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Deconstractor ...
|
|
~XSCBaseEA()
|
|
{
|
|
//
|
|
delete mTrader;
|
|
delete mPositionTracker;
|
|
}
|
|
|
|
//
|
|
// Properties Getter(s) / Setter(s) ...
|
|
|
|
//
|
|
// Retrieve Slippage ...
|
|
int GetSlippage()
|
|
{
|
|
return mTrader.GetSlippage();
|
|
}
|
|
|
|
//
|
|
// Retrieve Magic Number ...
|
|
ulong GetMagicNumber()
|
|
{
|
|
return mTrader.GetMagicNumber();
|
|
}
|
|
|
|
//
|
|
// Retrieve Max Allowed Spread for Trading ...
|
|
double GetMaxAllowedSpread()
|
|
{
|
|
return mTrader.GetMaxAllowedSpread();
|
|
}
|
|
|
|
//
|
|
// Retrieve Max Allowed Same Time Positions Count ...
|
|
int GetMaxAllowedPositions()
|
|
{
|
|
return mTrader.GetMaxAllowedPositions();
|
|
}
|
|
|
|
//
|
|
// Retrieve Max Allowed Drawdown for Opening new Trades ...
|
|
double GetMaxAllowedDrawdownFactor()
|
|
{
|
|
return mTrader.GetMaxAllowedDrawdownFactor();
|
|
}
|
|
|
|
//
|
|
double GetStaticVolume()
|
|
{
|
|
return mStaticVolume;
|
|
}
|
|
|
|
//
|
|
void SetStaticVolume(double value)
|
|
{
|
|
//
|
|
if (value <= 0)
|
|
{
|
|
value = 0.01;
|
|
}
|
|
|
|
//
|
|
mStaticVolume = value;
|
|
}
|
|
|
|
//
|
|
bool GetAllowLong()
|
|
{
|
|
return mAllowLong;
|
|
}
|
|
|
|
//
|
|
void SetAllowLong(bool value)
|
|
{
|
|
mAllowLong = value;
|
|
}
|
|
|
|
//
|
|
bool GetAllowShort()
|
|
{
|
|
return mAllowShort;
|
|
}
|
|
|
|
//
|
|
void SetAllowShort(bool value)
|
|
{
|
|
mAllowShort = value;
|
|
}
|
|
|
|
//
|
|
double GetMinProfitPerTrade()
|
|
{
|
|
return mMinProfitPerTrade;
|
|
}
|
|
|
|
//
|
|
void SetMinProfitPerTrade(double value)
|
|
{
|
|
//
|
|
if (value <= 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
if (value == mMinProfitPerTrade)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
mMinProfitPerTrade = value;
|
|
}
|
|
|
|
//
|
|
double GetMinProfitPerVolumeFactor()
|
|
{
|
|
return mMinProfitPerVolumeFactor;
|
|
}
|
|
|
|
//
|
|
void SetMinProfitPerVolumeFactor(double value)
|
|
{
|
|
//
|
|
if (value <= 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
if (value == mMinProfitPerVolumeFactor)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
mMinProfitPerVolumeFactor = value;
|
|
}
|
|
|
|
//
|
|
bool IsHedgeEnable()
|
|
{
|
|
//
|
|
bool result =
|
|
//
|
|
mMinProfitPerTrade > 0 &&
|
|
mMinProfitPerVolumeFactor > 0
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Overrides ...
|
|
|
|
//
|
|
// Customize Token ...
|
|
virtual string GetToken()
|
|
{
|
|
return GetSpecificToken(this);
|
|
}
|
|
|
|
//
|
|
virtual string GetTag()
|
|
{
|
|
return this.GetToken();
|
|
}
|
|
|
|
//
|
|
// Virtual Functions ...
|
|
|
|
//
|
|
// Handle Chart Events ...
|
|
virtual void OnChartEvent(
|
|
const int id, // event ID
|
|
const long &lparam, // long type event parameter
|
|
const double &dparam, // double type event parameter
|
|
const string &sparam // string type event parameter
|
|
)
|
|
{
|
|
}
|
|
|
|
//
|
|
// OnTick Handler ...
|
|
virtual void OnTick()
|
|
{
|
|
//
|
|
// Draw ...
|
|
Draw();
|
|
|
|
//
|
|
// Check Trade Events ...
|
|
OnTrade();
|
|
|
|
//
|
|
HandleProfitManageMent();
|
|
|
|
//
|
|
// First Check For Any Guard Actions ...
|
|
// then Handle Guards if Provided ...
|
|
XGuard guards[];
|
|
bool hasGuard = CheckForGuard(guards);
|
|
if (hasGuard)
|
|
{
|
|
DoGuards(guards);
|
|
}
|
|
|
|
//
|
|
XSignal signals[];
|
|
|
|
//
|
|
// Second Check for Position Management or Equity Management
|
|
// Actions like Supports or etc ...
|
|
bool isIgnored = HandleStateManagement(signals);
|
|
if (isIgnored)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Second Check For Signals ...
|
|
// then Execute Signals if Provided ...
|
|
int signalsCount = RequestForSignal(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Execute Signals ...
|
|
HandleSignalsExecution(signals);
|
|
}
|
|
|
|
//
|
|
// Handle StopLoss Event ...
|
|
virtual void OnStopLossTriggered(const XDeal &deal) {}
|
|
|
|
//
|
|
// Handle TakeProfit Event ...
|
|
virtual void OnTakeProfitTriggered(const XDeal &deal) {}
|
|
|
|
//
|
|
// Handle Deals Changed Event ...
|
|
virtual void OnDealsChangedHandler(int count) {}
|
|
|
|
//
|
|
// Handle Order Changed Event ...
|
|
virtual void OnOrdersChangedHandler(int count) {}
|
|
|
|
//
|
|
// Handle Position Changed Event ...
|
|
virtual void OnPositionsChangedHandler(int count) {}
|
|
|
|
//
|
|
// Handle Trade State Changed ...
|
|
virtual void OnTradeStateChangedHandler(const XOnTradeHandlerState &state) {}
|
|
|
|
//
|
|
// Handle On Trade ...
|
|
virtual void OnTrade()
|
|
{
|
|
//
|
|
XOnTradeHandlerState state = mTrader.HandleOnTrade();
|
|
|
|
//
|
|
if (
|
|
!state.hasNewDeal &&
|
|
!state.hasNewOrder &&
|
|
!state.hasNewPosition &&
|
|
!state.hasNewHistoryOrder)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// STATE ...
|
|
|
|
//
|
|
// Notfy Trade State Changed Event ...
|
|
OnTradeStateChangedHandler(state);
|
|
|
|
//
|
|
// DEALS ...
|
|
if (state.hasNewDeal)
|
|
{
|
|
//
|
|
// Notfy Deals Changed Event ...
|
|
OnDealsChangedHandler(state.newDeals);
|
|
|
|
//
|
|
// Retrieve Last Deal ...
|
|
XDeal deals[];
|
|
mTrader.GetDeals(deals);
|
|
int dealsCount = ArraySize(deals);
|
|
if (dealsCount > 0)
|
|
{
|
|
//
|
|
XDeal lastDeal = deals[0];
|
|
|
|
//
|
|
if (lastDeal.reason == DEAL_REASON_TP)
|
|
{
|
|
OnTakeProfitTriggered(lastDeal);
|
|
}
|
|
else if (lastDeal.reason == DEAL_REASON_SL)
|
|
{
|
|
OnStopLossTriggered(lastDeal);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// ORDERS ...
|
|
if (state.hasNewOrder || state.hasNewHistoryOrder)
|
|
{
|
|
//
|
|
// Notfy Orders Changed Event ...
|
|
OnOrdersChangedHandler(state.newOrders + state.newHistoryOrders);
|
|
}
|
|
|
|
//
|
|
// POSITIONS ...
|
|
if (state.hasNewPosition)
|
|
{
|
|
//
|
|
// Notfy Positions Changed Event ...
|
|
OnPositionsChangedHandler(state.newPositions);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Retrieve Dept Of Market ...
|
|
virtual void OnBookEvent(const string &symbol)
|
|
{
|
|
//
|
|
// TODO: Implement this ...
|
|
}
|
|
|
|
//
|
|
// Draw On Chart if anything is required ...
|
|
virtual void Draw() {}
|
|
|
|
//
|
|
// Check For any Guard Actions ...
|
|
virtual bool CheckForGuard(XGuard &guards[])
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// When a Guard Notified to Do Support Signal ...
|
|
// this Method Calls For Retrieve Support Signal ...
|
|
// if it's Provided, Execute it ...
|
|
virtual bool RequestForSupport(
|
|
XSignal &support, // Holds Support Signal, if Provided
|
|
string provider, // Positions Provided by Specific Provider
|
|
string symbol, // Trading Symbol
|
|
ENUM_POSITION_TYPE type, // Trading Type
|
|
ENUM_TIMEFRAMES period // Specified Time Frame
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Handle Position(s)/ Order(s) or EQM Supporting Action ...
|
|
// if returns false, prevent for process Tick ...
|
|
virtual bool HandleStateManagement(XSignal &signals[])
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Regulary Filtered Signals passed to this
|
|
// and here we can do Additional Signal Filtering
|
|
// for Signal Management such as (Providers and etc) ...
|
|
virtual int HandleSignalManagement(XSignal &signals[])
|
|
{
|
|
//
|
|
int result = ArraySize(signals);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Here we Have to Check Market based
|
|
// on our Strategy for Trading ...
|
|
// and if a Good Entry founded ...
|
|
// model it as an XSignal struct and passed it to result ...
|
|
virtual int RequestForSignal(
|
|
XSignal &signals[] // Holds Signals ...
|
|
)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// When Some Errors happens in Executing Signals ...
|
|
virtual void ManageUnExecutedSignals(
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT &states[],
|
|
XSignal &unExecutedSignals[] //
|
|
)
|
|
{
|
|
}
|
|
|
|
//
|
|
// Execute Provided Signals ...
|
|
virtual void HandleSignalsExecution(XSignal &signals[])
|
|
{
|
|
//
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Filter Signals Based on their Types ...
|
|
XSignal filteredSignals[];
|
|
|
|
//
|
|
// Try to Filter Signals ...
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
bool isLong = IsLong(iSignal.type);
|
|
|
|
//
|
|
if ((isLong && !GetAllowLong()) || (!isLong && !GetAllowShort()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
iSignal,
|
|
filteredSignals //
|
|
);
|
|
}
|
|
|
|
//
|
|
int filteredSignalsCount = ArraySize(filteredSignals);
|
|
if (filteredSignalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
int managedSignalsCount = HandleSignalManagement(filteredSignals);
|
|
if (managedSignalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Try to Execute Signals ...
|
|
XSignal notExecuteds[];
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT reasons[];
|
|
int executedSignalsCount = mTrader.ExecuteSpecifiedSignals(
|
|
filteredSignals,
|
|
notExecuteds, // Not Executed Signals
|
|
reasons
|
|
//
|
|
);
|
|
|
|
//
|
|
string message = NULL;
|
|
if (executedSignalsCount == 0)
|
|
{
|
|
//
|
|
message = "(" + ToString(filteredSignalsCount) + ") Signals Execution Failed ...";
|
|
|
|
//
|
|
for (int j = 0; j < ArraySize(reasons); j++)
|
|
{
|
|
//
|
|
string iReason = ToString(reasons[j]);
|
|
|
|
//
|
|
message += "\n" + ToString(j) + ": " + iReason;
|
|
}
|
|
}
|
|
else if (executedSignalsCount < filteredSignalsCount)
|
|
{
|
|
//
|
|
int diff = filteredSignalsCount - executedSignalsCount;
|
|
|
|
//
|
|
message = "Failed to Execute (" + ToString(diff) + ") Signals ...";
|
|
}
|
|
else if (executedSignalsCount == filteredSignalsCount)
|
|
{
|
|
message = "(" + ToString(executedSignalsCount) + ") Signals Executed Successfully ...";
|
|
}
|
|
|
|
//
|
|
if (ArraySize(notExecuteds) > 0)
|
|
{
|
|
//
|
|
ManageUnExecutedSignals(
|
|
reasons,
|
|
notExecuteds //
|
|
);
|
|
}
|
|
|
|
//
|
|
if (IsValid(message))
|
|
{
|
|
mAlert.Alert(message);
|
|
}
|
|
}
|
|
|
|
//
|
|
virtual void OnGoingToProfit(XProfitTrack &track) {}
|
|
virtual void OnLongsGoingToProfit(XProfitTrack &track) {}
|
|
virtual void OnShortsGoingToProfit(XProfitTrack &track) {}
|
|
|
|
//
|
|
virtual void OnGoingToDrawdown(XProfitTrack &track) {}
|
|
virtual void OnLongsGoingToDrawdown(XProfitTrack &track) {}
|
|
virtual void OnShortsGoingToDrawdown(XProfitTrack &track) {}
|
|
|
|
//
|
|
virtual void OnPositionGoingInProfit(XPositionTrack &track) {}
|
|
virtual void OnPositionGoingToDrawdown(XPositionTrack &track) {}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Trade Class Instance ...
|
|
XSCTrade *mTrader;
|
|
XSCPositionTracker *mPositionTracker;
|
|
|
|
//
|
|
// Check in Test Mode or not ...
|
|
bool mIsInTestMode;
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Signal Event Listeners ...
|
|
void NotifyOnSignalEventHandlers(XSignal &signals[])
|
|
{
|
|
//
|
|
int listenersCount = ArraySize(mOnSignalEventHandlers);
|
|
if (listenersCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < listenersCount; i++)
|
|
{
|
|
//
|
|
TOnSignal iListener = mOnSignalEventHandlers[i];
|
|
|
|
//
|
|
iListener(signals);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Position Management ...
|
|
|
|
//
|
|
// Calculate Required Profit for Hedging ...
|
|
double CalculateRequiredProfitForHedge(
|
|
XPosition &positions[] // Source
|
|
)
|
|
{
|
|
//
|
|
double result = SpecifiedCalculateRequiredProfitForHedge(
|
|
positions,
|
|
mMinProfitPerTrade,
|
|
mMinProfitPerVolumeFactor //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate Positions Profit Summary ...
|
|
double CalculatePositionsProfit(
|
|
XPosition &positions[] // Source
|
|
)
|
|
{
|
|
//
|
|
double result = SpecifiedCalculatePositionsProfit(positions);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Profit Tracker ...
|
|
|
|
//
|
|
XProfitTrack mProfitTracks[];
|
|
|
|
//
|
|
bool GetProfitTrack(
|
|
XProfitTrack &track,
|
|
int index = 0 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (index < 0)
|
|
{
|
|
index = 0;
|
|
}
|
|
|
|
//
|
|
int tracksCount = CountProfitTracks();
|
|
result = tracksCount > 0 && index < tracksCount;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
track.Clean();
|
|
|
|
//
|
|
track = mProfitTracks[index];
|
|
|
|
//
|
|
result = track.IsValid();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int CountProfitTracks()
|
|
{
|
|
return ArraySize(mProfitTracks);
|
|
}
|
|
|
|
//
|
|
void AddProfitTrack()
|
|
{
|
|
//
|
|
XProfitTrack track;
|
|
bool isValid = track.Init(mTrader);
|
|
if (!isValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
ArraySetAsSeries(mProfitTracks, false);
|
|
|
|
//
|
|
AddRef(
|
|
track,
|
|
mProfitTracks //
|
|
);
|
|
|
|
//
|
|
ArraySetAsSeries(mProfitTracks, true);
|
|
}
|
|
|
|
//
|
|
// Extract Specified Field Vales of XProfit Track Struct ...
|
|
int ExtractProfitTrackField(
|
|
double &result[],
|
|
ENUM_X_PROFIT_TRACK_FIELDS field,
|
|
int start = 0,
|
|
int count = 10 //
|
|
)
|
|
{
|
|
//
|
|
Clean(result);
|
|
|
|
//
|
|
if (start < 0)
|
|
{
|
|
start = 0;
|
|
}
|
|
|
|
//
|
|
int end = start + count;
|
|
|
|
//
|
|
int tracksCount = CountProfitTracks();
|
|
if (end > tracksCount)
|
|
{
|
|
end = tracksCount;
|
|
}
|
|
|
|
//
|
|
for (int i = start; i < end; i++)
|
|
{
|
|
//
|
|
XProfitTrack iTrack;
|
|
bool isValid = GetProfitTrack(
|
|
iTrack,
|
|
i //
|
|
);
|
|
|
|
//
|
|
if (!isValid)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
double iValue = iTrack.SelectField(field);
|
|
Add(
|
|
iValue,
|
|
result //
|
|
);
|
|
}
|
|
|
|
//
|
|
int mResult = ArraySize(result);
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
double AverageProfitTrackField(
|
|
ENUM_X_PROFIT_TRACK_FIELDS field,
|
|
int start = 0,
|
|
int count = 10 //
|
|
)
|
|
{
|
|
//
|
|
double result = EMPTY_VALUE;
|
|
|
|
//
|
|
double items[];
|
|
int itemsCount = ExtractProfitTrackField(
|
|
items,
|
|
field,
|
|
start,
|
|
count //
|
|
);
|
|
if (itemsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = GetAverage(items);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool IsProfitTrackFieldIncreasing(
|
|
ENUM_X_PROFIT_TRACK_FIELDS field,
|
|
int start = 0,
|
|
int count = 10 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
double items[];
|
|
int itemsCount = ExtractProfitTrackField(
|
|
items,
|
|
field,
|
|
start,
|
|
count //
|
|
);
|
|
if (itemsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int from = itemsCount - 1;
|
|
int to = 0;
|
|
|
|
//
|
|
result = IsIncreasing(
|
|
from,
|
|
to,
|
|
items //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool IsProfitTrackFieldDecreasing(
|
|
ENUM_X_PROFIT_TRACK_FIELDS field,
|
|
int start = 0,
|
|
int count = 10 //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
double items[];
|
|
int itemsCount = ExtractProfitTrackField(
|
|
items,
|
|
field,
|
|
start,
|
|
count //
|
|
);
|
|
if (itemsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int from = itemsCount - 1;
|
|
int to = 0;
|
|
|
|
//
|
|
result = IsDecreasing(
|
|
from,
|
|
to,
|
|
items //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
void HandleProfitManageMent()
|
|
{
|
|
//
|
|
// Check Tracking Interval ...
|
|
|
|
//
|
|
static datetime lastProfitTracked = NULL;
|
|
datetime cTime = TimeCurrent();
|
|
|
|
//
|
|
int profitManagementInterval = PeriodSeconds(PERIOD_M5);
|
|
int timeDiff = (int)cTime - (int)lastProfitTracked;
|
|
|
|
//
|
|
bool canManage =
|
|
//
|
|
lastProfitTracked == NULL
|
|
? true
|
|
: timeDiff >= profitManagementInterval
|
|
//
|
|
;
|
|
if (!canManage)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(positions);
|
|
if (!IsValidSize(positionsCount))
|
|
{
|
|
//
|
|
Clean(mProfitTracks);
|
|
return;
|
|
}
|
|
|
|
//
|
|
lastProfitTracked = cTime;
|
|
|
|
//
|
|
// Do Track ...
|
|
|
|
//
|
|
AddProfitTrack();
|
|
|
|
//
|
|
int profitsCount = CountProfitTracks();
|
|
if (profitsCount <= 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
XProfitTrack cT;
|
|
bool isValid = GetProfitTrack(
|
|
cT,
|
|
0 //
|
|
);
|
|
if (!isValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
XProfitTrack pT;
|
|
isValid = GetProfitTrack(
|
|
pT,
|
|
1 //
|
|
);
|
|
if (!isValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
cT.profitFrom = pT.profit;
|
|
cT.longsProfitFrom = pT.longsProfit;
|
|
cT.shortsProfitFrom = pT.shortsProfit;
|
|
|
|
//
|
|
bool isGoningToProfit =
|
|
//
|
|
cT.profit > 0 &&
|
|
pT.profit <= 0
|
|
//
|
|
;
|
|
if (isGoningToProfit)
|
|
{
|
|
//
|
|
OnGoingToProfit(cT);
|
|
}
|
|
|
|
//
|
|
bool isLongsGoningToProfit =
|
|
//
|
|
cT.longsProfit > 0 &&
|
|
pT.longsProfit <= 0
|
|
//
|
|
;
|
|
if (isLongsGoningToProfit)
|
|
{
|
|
OnLongsGoingToProfit(cT);
|
|
}
|
|
|
|
//
|
|
bool isShortsGoningToProfit =
|
|
//
|
|
cT.shortsProfit > 0 &&
|
|
pT.shortsProfit <= 0
|
|
//
|
|
;
|
|
if (isShortsGoningToProfit)
|
|
{
|
|
OnShortsGoingToProfit(cT);
|
|
}
|
|
|
|
//
|
|
bool isGoningToDrawdown =
|
|
//
|
|
cT.profit < 0 &&
|
|
pT.profit >= 0
|
|
//
|
|
;
|
|
if (isGoningToDrawdown)
|
|
{
|
|
OnGoingToDrawdown(cT);
|
|
}
|
|
|
|
bool isLongsGoningToDrawdown =
|
|
//
|
|
cT.longsProfit < 0 &&
|
|
pT.longsProfit >= 0
|
|
//
|
|
;
|
|
if (isLongsGoningToDrawdown)
|
|
{
|
|
OnLongsGoingToDrawdown(cT);
|
|
}
|
|
|
|
bool isShortsGoningToDrawdown =
|
|
//
|
|
cT.shortsProfit < 0 &&
|
|
pT.shortsProfit >= 0
|
|
//
|
|
;
|
|
if (isShortsGoningToDrawdown)
|
|
{
|
|
OnShortsGoingToDrawdown(cT);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Synchronize Positions and Position Tracker ...
|
|
void SyncPositionTracker()
|
|
{
|
|
//
|
|
// Check Syncing Interval ...
|
|
|
|
//
|
|
static datetime lastSyncPositionTracker = NULL;
|
|
datetime cTime = TimeCurrent();
|
|
|
|
//
|
|
int syncInterval = PeriodSeconds(PERIOD_M5);
|
|
int lastDiff = (int)cTime - (int)lastSyncPositionTracker;
|
|
|
|
//
|
|
bool canSync =
|
|
//
|
|
lastSyncPositionTracker == NULL
|
|
? true
|
|
: lastDiff >= syncInterval
|
|
//
|
|
;
|
|
if (!canSync)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
lastSyncPositionTracker = cTime;
|
|
|
|
//
|
|
// Do Sync ...
|
|
|
|
//
|
|
XPositionTrackerState state;
|
|
int synced = mPositionTracker.Sync(
|
|
state,
|
|
mTrader //
|
|
);
|
|
if (synced > 0)
|
|
{
|
|
//
|
|
// Check State and Do What we Want ...
|
|
int onProfitsCount = ArraySize(state.onProfits);
|
|
int onDrawdownCount = ArraySize(state.onDrawdowns);
|
|
|
|
//
|
|
// Do What we want whe a Position Dropped to Prefit ...
|
|
if (onProfitsCount > 0)
|
|
{
|
|
//
|
|
for (int i = 0; i < onProfitsCount; i++)
|
|
{
|
|
//
|
|
XPositionTrack iTrack = state.onProfits[i];
|
|
|
|
//
|
|
OnPositionGoingInProfit(iTrack);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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];
|
|
|
|
//
|
|
OnPositionGoingToDrawdown(iTrack);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Guards ...
|
|
|
|
//
|
|
// Do All Provided Guards ...
|
|
void DoGuards(XGuard &guards[])
|
|
{
|
|
//
|
|
int guardsCount = ArraySize(guards);
|
|
if (guardsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < guardsCount; i++)
|
|
{
|
|
//
|
|
XGuard iGuard = guards[i];
|
|
|
|
//
|
|
switch (iGuard.action)
|
|
{
|
|
//
|
|
// Hedge In Profit Specified Positions ...
|
|
case X_GUARD_ACTION_HEDGE:
|
|
HandleGuardHedgeAction(
|
|
iGuard.provider,
|
|
iGuard.symbol,
|
|
iGuard.type,
|
|
iGuard.period);
|
|
break;
|
|
|
|
//
|
|
// Trail Stop Loss in Specified Positions ...
|
|
case X_GUARD_ACTION_TRAIL_STOP:
|
|
HandleGuardTrailingStopAction(
|
|
iGuard.provider,
|
|
iGuard.symbol,
|
|
iGuard.type,
|
|
iGuard.period,
|
|
iGuard.dblPayLoad);
|
|
break;
|
|
|
|
//
|
|
// Force Close Specified Positions ...
|
|
case X_GUARD_ACTION_FORCE_CLOSE:
|
|
HandleGuardForceCloseAction(
|
|
iGuard.provider,
|
|
iGuard.symbol,
|
|
iGuard.type,
|
|
iGuard.period);
|
|
break;
|
|
|
|
//
|
|
// Generate and Place Support Signals ...
|
|
case X_GUARD_ACTION_ADD_SUPPORT_SIGNAL:
|
|
HandleGuardAddSupportPositionAction(
|
|
iGuard.provider,
|
|
iGuard.symbol,
|
|
iGuard.type,
|
|
iGuard.period);
|
|
break;
|
|
|
|
//
|
|
// Delete All Placed Pending Orders ...
|
|
case X_GUARD_ACTION_CANCEL_PENDING_ORDERS:
|
|
HandleGuardCancelPendingOrdersAction(
|
|
iGuard.provider,
|
|
iGuard.symbol,
|
|
iGuard.type,
|
|
iGuard.period);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close All Specified Provider's Positions in Profit Summary ...
|
|
void HandleGuardHedgeAction(
|
|
string provider, // Positions Provided by Specific Provider
|
|
string symbol, // Trading Symbol
|
|
ENUM_POSITION_TYPE type, // Trading Type
|
|
ENUM_TIMEFRAMES period, // Specified Time Frame
|
|
double minProfitForHedging = 1 // Specified Profit for Hedge
|
|
)
|
|
{
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader
|
|
.GetPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type
|
|
//
|
|
);
|
|
if (positionsCount <= 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
double profit = CalculatePositionsProfit(positions);
|
|
double requiredProfit = CalculateRequiredProfitForHedge(positions);
|
|
|
|
//
|
|
bool isReadyForHedge = profit >= requiredProfit;
|
|
if (!isReadyForHedge)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
string comment = "Guard Hedge " + provider;
|
|
int closed = mTrader.Close(
|
|
positions,
|
|
comment
|
|
//
|
|
);
|
|
if (closed > 0)
|
|
{
|
|
//
|
|
string message = "Guard Hedge " + provider + "'s Positions on Profit: " + ToString(profit);
|
|
|
|
//
|
|
mAlert.Alert(message);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close Max In Profit Trade Guard Action ...
|
|
void HandleGuardCloseInProfitAction(
|
|
string provider, // Positions Provided by Specific Provider
|
|
string symbol, // Trading Symbol
|
|
ENUM_POSITION_TYPE type, // Trading Type
|
|
ENUM_TIMEFRAMES period, // Specified Time Frame
|
|
ENUM_X_POSITION_SELECT_METHODS method = NULL // Select Method
|
|
)
|
|
{
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader
|
|
.GetInProfitPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
method
|
|
//
|
|
);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
string comment = "Close InProfit ...";
|
|
mTrader.Close(
|
|
positions,
|
|
comment
|
|
//
|
|
);
|
|
|
|
//
|
|
string message = "Guard Close (" + ToString(positionsCount) + ") Postion(s) In Profit ...";
|
|
|
|
//
|
|
mAlert.Alert(message);
|
|
}
|
|
|
|
//
|
|
// Close Max In Drawdown Trade Guard Action ...
|
|
void HandleGuardCloseInDrawdownAction(
|
|
string provider, // Positions Provided by Specific Provider
|
|
string symbol, // Trading Symbol
|
|
ENUM_POSITION_TYPE type, // Trading Type
|
|
ENUM_TIMEFRAMES period, // Specified Time Frame
|
|
ENUM_X_POSITION_SELECT_METHODS method = NULL // Select Method
|
|
)
|
|
{
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader
|
|
.GetInDrawdownPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
method
|
|
//
|
|
);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
string comment = "Close InDrawdown ...";
|
|
mTrader.Close(
|
|
positions,
|
|
comment
|
|
//
|
|
);
|
|
|
|
//
|
|
string message = "Guard Close (" + ToString(positionsCount) + ") Position(s) In Drawdown ...";
|
|
|
|
//
|
|
mAlert.Alert(message);
|
|
}
|
|
|
|
//
|
|
// Force Close Specified Set Of Position ...
|
|
void HandleGuardForceCloseAction(
|
|
string provider, // Positions Provided by Specific Provider
|
|
string symbol, // Trading Symbol
|
|
ENUM_POSITION_TYPE type, // Trading Type
|
|
ENUM_TIMEFRAMES period, // Specified Time Frame
|
|
ulong ticket = 0 // Specific Ticket
|
|
)
|
|
{
|
|
//
|
|
string comment = "";
|
|
|
|
//
|
|
// Close All Positions ...
|
|
if (ticket == 0)
|
|
{
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type
|
|
//
|
|
);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
comment = provider + " Force Close Guard ...";
|
|
|
|
//
|
|
mTrader.Close(
|
|
positions,
|
|
comment
|
|
//
|
|
);
|
|
}
|
|
//
|
|
// Close Specific Position ...
|
|
else
|
|
{
|
|
//
|
|
XPosition position;
|
|
bool hasPosition = mTrader
|
|
.GetPosition(
|
|
ticket,
|
|
position
|
|
//
|
|
);
|
|
|
|
//
|
|
if (hasPosition)
|
|
{
|
|
//
|
|
comment = provider + "(" + ToString(ticket) + ")" + " Force Close Guard ...";
|
|
|
|
//
|
|
mTrader.Close(
|
|
ticket,
|
|
comment
|
|
//
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
if (IsValid(comment))
|
|
{
|
|
mAlert.Alert(comment);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Trailing Specified Set Of Positions Stop Losses ...
|
|
void HandleGuardTrailingStopAction(
|
|
string provider, // Positions Provided by Specific Provider
|
|
string symbol, // Trading Symbol
|
|
ENUM_POSITION_TYPE type, // Trading Type
|
|
ENUM_TIMEFRAMES period, // Specified Time Frame
|
|
double sl // Desired SL Value ...
|
|
)
|
|
{
|
|
//
|
|
if (sl <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
NormalizePrice(
|
|
sl,
|
|
symbol
|
|
//
|
|
);
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader
|
|
.GetPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type
|
|
//
|
|
);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
int modified = 0;
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
string comment = PrepareSLTrailTag(iPosition.comment);
|
|
|
|
//
|
|
if (iPosition.sl == sl)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isModified = mTrader.Modify(
|
|
iPosition.ticket,
|
|
sl,
|
|
iPosition.tp,
|
|
comment);
|
|
if (isModified)
|
|
{
|
|
modified++;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (modified > 0)
|
|
{
|
|
//
|
|
string message = "(" + ToString(modified) + ") Position's Stop Trailed Successfully ...";
|
|
|
|
//
|
|
mAlert.Alert(message);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add Support Signal for Specified Positions ...
|
|
void HandleGuardAddSupportPositionAction(
|
|
string provider, // Positions Provided by Specific Provider
|
|
string symbol, // Trading Symbol
|
|
ENUM_POSITION_TYPE type, // Trading Type
|
|
ENUM_TIMEFRAMES period // Specified Time Frame
|
|
)
|
|
{
|
|
//
|
|
XSignal support;
|
|
bool hasSupport = RequestForSupport(
|
|
support,
|
|
provider,
|
|
symbol,
|
|
type,
|
|
period
|
|
//
|
|
);
|
|
|
|
//
|
|
if (!hasSupport || !support.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// TODO: May be need to add Support Tag to comments ...
|
|
|
|
//
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT reason = NULL;
|
|
bool isExecuted = mTrader.ExecuteSignal(
|
|
support,
|
|
reason);
|
|
|
|
//
|
|
string comment = NULL;
|
|
if (!isExecuted)
|
|
{
|
|
comment = "Support Execution Failed doue " + ToString(reason);
|
|
}
|
|
else
|
|
{
|
|
comment = "Guard Support Signal Executed Successfully ...";
|
|
}
|
|
|
|
//
|
|
if (IsValid(comment))
|
|
{
|
|
mAlert.Alert(comment);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cancel all Placed Orders ...
|
|
void HandleGuardCancelPendingOrdersAction(
|
|
string provider, // Positions Provided by Specific Provider
|
|
string symbol, // Trading Symbol
|
|
ENUM_POSITION_TYPE type, // Trading Type
|
|
ENUM_TIMEFRAMES period // Specified Time Frame
|
|
)
|
|
{
|
|
//
|
|
XOrder orders[];
|
|
int ordersCount = mTrader.GetOrders(
|
|
orders,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
ToOrderType(type),
|
|
ORDER_STATE_PLACED,
|
|
true // Filter by Magic ...
|
|
//
|
|
);
|
|
if (ordersCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
int canceledOrdersCount = mTrader.CancelOrders(orders);
|
|
|
|
//
|
|
if (canceledOrdersCount > 0)
|
|
{
|
|
//
|
|
string message = "Guard Cancel (" + ToString(canceledOrdersCount) + ") Pending Orders ...";
|
|
|
|
//
|
|
mAlert.Alert(message);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Position Filter ...
|
|
|
|
//
|
|
int FilterSymbolBasedPositions(
|
|
string &symbols[], // Hold Symbols ...
|
|
XSCPositionHolder *&holders[] // Holder Class Instances ...
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(
|
|
positions //
|
|
);
|
|
if (!IsValidSize(positionsCount))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Create a HashMap for Holding Positions ...
|
|
CHashMap<string, XSCPositionHolder *> symbolMap;
|
|
|
|
//
|
|
// Loop Through Positions ...
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
bool hasIKey = symbolMap.ContainsKey(iPosition.symbol);
|
|
|
|
//
|
|
// Empty Class Pointer ...
|
|
XSCPositionHolder *mHolder = NULL;
|
|
|
|
//
|
|
// Add new to Map ...
|
|
if (!hasIKey)
|
|
{
|
|
//
|
|
mHolder = new XSCPositionHolder();
|
|
if (symbolMap.TrySetValue(iPosition.symbol, mHolder))
|
|
{
|
|
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 result;
|
|
}
|
|
|
|
//
|
|
result = symbolMap.CopyTo(
|
|
symbols,
|
|
holders //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Signal Event Listeners ...
|
|
TOnSignal mOnSignalEventHandlers[];
|
|
|
|
//
|
|
// Position Management ...
|
|
bool mAllowLong; // Allow Long Trades
|
|
bool mAllowShort; // Allow Short Trades
|
|
double mMinProfitPerTrade; // Min Profit Per Trade based On Volume Factor (Hedge)
|
|
double mMinProfitPerVolumeFactor; // Min Volume Factor for Calculating Profit (Hedge)
|
|
double mStaticVolume; // Static Volume for Positions
|
|
|
|
//
|
|
// Event Handlers ...
|
|
TOnStopLoss mOnStopLossTriggered;
|
|
TOnTakeProfit mOnTakeProfitTriggered;
|
|
TOnDealsChanged mOnDealsChangedHandler;
|
|
TOnOrdersChanged mOnOrdersChangedHandler;
|
|
TOnPositionsChanged mOnPositionsChangedHandler;
|
|
TOnTradeStateChanged mOnTradeStateChangedHandler;
|
|
};
|
|
|
|
// |