1736 lines
34 KiB
Plaintext
1736 lines
34 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Class Library
|
|
// ----------------------------------------------
|
|
// Name: X121SCTradeHandler
|
|
// Description: provides all Trade Handling 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 "../Classes/x-saherelm.xtrade.class.mq5"
|
|
|
|
//
|
|
// Definitions ...
|
|
|
|
//
|
|
// an Structure for Holding Positions Data ...
|
|
struct X121TradeData
|
|
{
|
|
//
|
|
datetime time; // Issue Time (Open Position)
|
|
string symbol; // Trading Symbol
|
|
double entry; // Entry Price
|
|
double volume; // Volume
|
|
string provider; // Signaller
|
|
ENUM_TIMEFRAMES period; // Trading Timeframe
|
|
ENUM_X_POSITION_TYPES type; // Position Type
|
|
|
|
//
|
|
ulong ticket; // Position Ticket
|
|
double swap; // Swap
|
|
double profit; // Profit on Close
|
|
double commission; // Commission
|
|
double maxDrawdown; // Max Position Drawdown
|
|
string message; // Close Reason
|
|
datetime endTime; // End Time
|
|
|
|
//
|
|
XSignal signal; // Signal Object
|
|
int pushers; // Signal Pushers
|
|
string conditions; // Signal Conditions
|
|
|
|
//
|
|
// Constructor ...
|
|
void XTradeInfo()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Filling Trade Handler ...
|
|
bool Fill(XSignal &_signal)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
Clean();
|
|
|
|
//
|
|
// Check Signal Validation ...
|
|
result = _signal.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
signal = _signal;
|
|
time = _signal.time;
|
|
entry = _signal.entry;
|
|
symbol = _signal.symbol;
|
|
volume = _signal.volume;
|
|
period = _signal.period;
|
|
pushers = _signal.pushers;
|
|
provider = _signal.provider;
|
|
conditions = _signal.conditions;
|
|
type = ToPositionType(_signal.type);
|
|
|
|
//
|
|
if (_signal.IsExecuted())
|
|
{
|
|
ticket = _signal.positionId;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Filling Trade Handler ...
|
|
bool Fill(XPosition &_position)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
Clean();
|
|
|
|
//
|
|
// Check Signal Validation ...
|
|
result = _position.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
time = _position.openAt;
|
|
symbol = _position.symbol;
|
|
entry = _position.entry;
|
|
volume = _position.volume;
|
|
provider = _position.provider;
|
|
period = _position.period;
|
|
type = ToPositionType(_position.type);
|
|
|
|
//
|
|
ticket = _position.ticket;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Update Data ...
|
|
bool Update(XPosition &_position)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = _position.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
profit = _position.profit;
|
|
swap = _position.swap;
|
|
|
|
//
|
|
if (profit < 0 &&
|
|
(maxDrawdown == 0 ||
|
|
MathAbs(profit) > MathAbs(maxDrawdown)))
|
|
{
|
|
maxDrawdown = profit;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Cleanup ...
|
|
void Clean()
|
|
{
|
|
//
|
|
time = NULL;
|
|
type = NULL;
|
|
symbol = NULL;
|
|
period = NULL;
|
|
endTime = NULL;
|
|
message = NULL;
|
|
provider = NULL;
|
|
|
|
//
|
|
swap = 0;
|
|
entry = 0;
|
|
ticket = 0;
|
|
profit = 0;
|
|
volume = 0;
|
|
commission = 0;
|
|
maxDrawdown = 0;
|
|
|
|
//
|
|
signal.Clean();
|
|
}
|
|
|
|
//
|
|
// Validation ...
|
|
bool IsValid()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
//
|
|
ticket > 0 &&
|
|
IsValid(time) &&
|
|
IsValid(symbol) &&
|
|
IsValid(period)
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Trade Data Age ...
|
|
int GetAge()
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
if (!IsValid(symbol) ||
|
|
!IsValid(time) ||
|
|
!IsValid(endTime) ||
|
|
!IsValid(period))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int startIndex = iBarShift(
|
|
symbol,
|
|
period,
|
|
time,
|
|
false //
|
|
);
|
|
|
|
//
|
|
int endIndex = iBarShift(
|
|
symbol,
|
|
period,
|
|
endTime,
|
|
false //
|
|
);
|
|
|
|
//
|
|
result = MathAbs(startIndex - endIndex);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Own of Trade Data ...
|
|
// based on Ticket ...
|
|
bool IsOwn(
|
|
ulong _ticket //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = _ticket == ticket;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Own of Trade Data ...
|
|
// based on Symbol/Provider and Period ...
|
|
bool IsOwn(
|
|
string _symbol,
|
|
string _provider,
|
|
ENUM_TIMEFRAMES _period,
|
|
ENUM_X_POSITION_TYPES _type //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
//
|
|
IsValid(_symbol) &&
|
|
IsValid(_period) &&
|
|
IsValid(_provider) &&
|
|
type != X_POSITION_TYPE_ALL &&
|
|
type != X_POSITION_TYPE_NONE &&
|
|
//
|
|
type == _type &&
|
|
period == _period &&
|
|
symbol == _symbol &&
|
|
provider == _provider
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Data Collector ...
|
|
|
|
//
|
|
// Get Data File Name ...
|
|
string GetFileName()
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
if (!IsValid())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result =
|
|
//
|
|
symbol + "\\" +
|
|
ToString(type) + "\\" +
|
|
(profit >= 0 ? "Profit" : "Loss") + "\\" +
|
|
ToString(ticket) + "_" +
|
|
ToString(period) + "_" +
|
|
ToFormatString(time)
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get Signal File Name ...
|
|
string GetSignalFileName()
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
result =
|
|
//
|
|
symbol + "\\" +
|
|
ToString(type) + "\\" +
|
|
provider + "_" +
|
|
ToFormatString(time)
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Convert(s) To String Representation(s) ...
|
|
string ToString(
|
|
bool onlySignals = false //
|
|
)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
int age = GetAge();
|
|
|
|
//
|
|
result =
|
|
//
|
|
(onlySignals ? "" : ToString("Ticket", ticket)) +
|
|
ToString("Symbol", symbol) +
|
|
ToString("Period", period) +
|
|
ToString("Entry", entry) +
|
|
ToString("Provider", provider) +
|
|
ToString("Type", ToString(type)) +
|
|
ToString("Time", time) +
|
|
//
|
|
// Attach Trade Info ...
|
|
(onlySignals ? "" :
|
|
//
|
|
"-------------" + "\n" +
|
|
ToString("Volume", volume) +
|
|
ToString("Profit", profit) +
|
|
ToString("Commission", commission) +
|
|
ToString("Swap", swap) +
|
|
ToString("Max Drawdown", maxDrawdown) +
|
|
ToString("End Time", endTime) +
|
|
ToString("Age", age) +
|
|
ToString("Message", message) +
|
|
""
|
|
//
|
|
) +
|
|
//
|
|
// Attach Conditions to Signals ...
|
|
(!onlySignals ? "" :
|
|
//
|
|
"-------------" + "\n" +
|
|
ToString("Pushers", pushers) +
|
|
"Conditions:" + "\n" +
|
|
"-------------" + "\n" +
|
|
conditions +
|
|
""
|
|
//
|
|
) +
|
|
//
|
|
""
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
};
|
|
|
|
//
|
|
// Implementation ...
|
|
|
|
//
|
|
// a Class For Read and Write Trade Info Data in Files ...
|
|
class X121TradeCollector
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Constructor(s) ...
|
|
void X121TradeCollector(
|
|
string _path = NULL // Base Path
|
|
)
|
|
{
|
|
//
|
|
mAccount = new XSCAccount();
|
|
|
|
//
|
|
if (IsValid(_path))
|
|
{
|
|
mPath = _path;
|
|
}
|
|
else
|
|
{
|
|
mPath = "X121TradeData" + "\\" + mAccount.GetCompany();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
void ~X121TradeCollector()
|
|
{
|
|
}
|
|
|
|
//
|
|
bool IsExists(X121TradeData &item)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int mHandler = GetFileHandlerForRead(item);
|
|
result = mHandler != INVALID_HANDLE;
|
|
FileClose(mHandler);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool Save(X121TradeData &item)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check info is Valid ...
|
|
result = item.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string content = item.ToString();
|
|
content += "\n" + "-----------" + "\n" + "Signal: " + "\n" + "-----------" + "\n" + item.ToString(true);
|
|
|
|
//
|
|
result = IsValid(content);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int mHandler = GetFileHandlerForWrite(item);
|
|
result = mHandler != INVALID_HANDLE;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
FileWrite(mHandler, content);
|
|
FileFlush(mHandler);
|
|
FileClose(mHandler);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool SaveSignal(X121TradeData &item)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
string content = item.ToString(true);
|
|
result = IsValid(content);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int mHandler = GetSignalFileHandlerForWrite(item);
|
|
result = mHandler != INVALID_HANDLE;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
FileWrite(mHandler, content);
|
|
FileFlush(mHandler);
|
|
FileClose(mHandler);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Conditions only save for Loss Signals ...
|
|
// this means the profit must be Lower than Zero ...
|
|
// ans also message Contains SL ...
|
|
bool SaveConditions(X121TradeData &item)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Item ...
|
|
result =
|
|
//
|
|
item.profit < 0 &&
|
|
Contains("SL", item.message)
|
|
//
|
|
;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string content = item.signal.conditions;
|
|
result = IsValid(content);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int mHandler = GetConditionsFileHandlerForWrite(item);
|
|
result = mHandler != INVALID_HANDLE;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
FileSeek(mHandler, 0, SEEK_END);
|
|
FileWrite(mHandler, content);
|
|
FileFlush(mHandler);
|
|
FileClose(mHandler);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
string mPath; // Base Path ...
|
|
|
|
//
|
|
XSCAccount *mAccount;
|
|
|
|
//
|
|
string GetFilePath(X121TradeData &item)
|
|
{
|
|
//
|
|
string fileName = item.GetFileName();
|
|
|
|
//
|
|
return GetFilePath(fileName);
|
|
}
|
|
string GetFilePath(string fileName)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
result =
|
|
//
|
|
mPath + "\\" + "Trades" + "\\" + fileName + ".x121.log"
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string GetSignalFilePath(X121TradeData &item)
|
|
{
|
|
//
|
|
string fileName = item.GetSignalFileName();
|
|
|
|
//
|
|
return GetSignalFilePath(fileName);
|
|
}
|
|
string GetSignalFilePath(string fileName)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
result =
|
|
//
|
|
mPath + "\\" + "Signals" + "\\" + fileName + ".x121.log"
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string GetConditionsFilePath(X121TradeData &item)
|
|
{
|
|
//
|
|
bool isLong = IsLong(item.type);
|
|
|
|
//
|
|
string fileName =
|
|
item.symbol + "\\" +
|
|
(isLong ? "Longs" : "Shorts");
|
|
|
|
//
|
|
return GetConditionsFilePath(fileName);
|
|
}
|
|
string GetConditionsFilePath(string fileName)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
result =
|
|
//
|
|
mPath + "\\" + "Conditions" + "\\" + fileName + ".x121.log"
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int GetFileHandlerForRead(X121TradeData &item)
|
|
{
|
|
//
|
|
int result = INVALID_HANDLE;
|
|
|
|
//
|
|
string filePath = GetFilePath(item);
|
|
if (!IsValid(filePath))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = FileOpen(
|
|
filePath,
|
|
FILE_READ | FILE_TXT //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
int GetFileHandlerForWrite(X121TradeData &item)
|
|
{
|
|
//
|
|
int result = INVALID_HANDLE;
|
|
|
|
//
|
|
string filePath = GetFilePath(item);
|
|
if (!IsValid(filePath))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = FileOpen(
|
|
filePath,
|
|
FILE_READ | FILE_WRITE | FILE_TXT //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int GetSignalFileHandlerForRead(X121TradeData &item)
|
|
{
|
|
//
|
|
int result = INVALID_HANDLE;
|
|
|
|
//
|
|
string filePath = GetSignalFilePath(item);
|
|
if (!IsValid(filePath))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = FileOpen(
|
|
filePath,
|
|
FILE_READ | FILE_TXT //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
int GetSignalFileHandlerForWrite(X121TradeData &item)
|
|
{
|
|
//
|
|
int result = INVALID_HANDLE;
|
|
|
|
//
|
|
string filePath = GetSignalFilePath(item);
|
|
if (!IsValid(filePath))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = FileOpen(
|
|
filePath,
|
|
FILE_READ | FILE_WRITE | FILE_TXT //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int GetConditionsFileHandlerForRead(X121TradeData &item)
|
|
{
|
|
//
|
|
int result = INVALID_HANDLE;
|
|
|
|
//
|
|
string filePath = GetConditionsFilePath(item);
|
|
if (!IsValid(filePath))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = FileOpen(
|
|
filePath,
|
|
FILE_READ | FILE_TXT //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
int GetConditionsFileHandlerForWrite(X121TradeData &item)
|
|
{
|
|
//
|
|
int result = INVALID_HANDLE;
|
|
|
|
//
|
|
string filePath = GetConditionsFilePath(item);
|
|
if (!IsValid(filePath))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = FileOpen(
|
|
filePath,
|
|
FILE_READ | FILE_WRITE | FILE_TXT //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
};
|
|
|
|
//
|
|
// Trade Handler Class ...
|
|
class X121SCTradeHandler : public XSCBaseAlert
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
|
|
//
|
|
// Constructor(s) ...
|
|
void X121SCTradeHandler(XSCTrade *trader)
|
|
{
|
|
//
|
|
mTrader = trader;
|
|
mCollector = new X121TradeCollector();
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
void ~X121SCTradeHandler()
|
|
{
|
|
//
|
|
delete mTrader;
|
|
delete mCollector;
|
|
}
|
|
|
|
//
|
|
// Getter(s) / Setter(s) ...
|
|
|
|
//
|
|
bool SaveSignals()
|
|
{
|
|
return mSaveSignals;
|
|
}
|
|
|
|
//
|
|
void SaveSignals(bool value)
|
|
{
|
|
mSaveSignals = value;
|
|
}
|
|
|
|
//
|
|
bool SaveTrades()
|
|
{
|
|
return mSaveTrades;
|
|
}
|
|
|
|
//
|
|
void SaveTrades(bool value)
|
|
{
|
|
mSaveTrades = value;
|
|
}
|
|
|
|
//
|
|
bool SaveConditions()
|
|
{
|
|
return mSaveConditions;
|
|
}
|
|
|
|
//
|
|
void SaveConditions(bool value)
|
|
{
|
|
mSaveConditions = value;
|
|
}
|
|
|
|
//
|
|
bool AllowLong()
|
|
{
|
|
return mAllowLong;
|
|
}
|
|
|
|
//
|
|
void AllowLong(bool value)
|
|
{
|
|
mAllowLong = value;
|
|
}
|
|
|
|
//
|
|
bool AllowShort()
|
|
{
|
|
return mAllowShort;
|
|
}
|
|
|
|
//
|
|
void AllowShort(bool value)
|
|
{
|
|
mAllowShort = value;
|
|
}
|
|
|
|
//
|
|
int MaxAllowedLongs()
|
|
{
|
|
return mMaxAllowedLongs;
|
|
}
|
|
|
|
//
|
|
void MaxAllowedLongs(int value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mMaxAllowedLongs = value;
|
|
}
|
|
|
|
//
|
|
int MaxAllowedShorts()
|
|
{
|
|
return mMaxAllowedShorts;
|
|
}
|
|
|
|
//
|
|
void MaxAllowedShorts(int value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mMaxAllowedShorts = value;
|
|
}
|
|
|
|
//
|
|
bool UseMaxAllowedTradesPerSymbol()
|
|
{
|
|
return mUseMaxAllowedTradesPerSymbol;
|
|
}
|
|
|
|
//
|
|
void UseMaxAllowedTradesPerSymbol(bool value)
|
|
{
|
|
mUseMaxAllowedTradesPerSymbol = value;
|
|
}
|
|
|
|
//
|
|
double MaxDrawdownPercentForOpenTrades()
|
|
{
|
|
return mMaxDrawdownPercentForOpenTrades;
|
|
}
|
|
|
|
//
|
|
void MaxDrawdownPercentForOpenTrades(double value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mMaxDrawdownPercentForOpenTrades = value;
|
|
}
|
|
|
|
//
|
|
// Read Only Props ...
|
|
|
|
//
|
|
int MaxSameTimeTrades()
|
|
{
|
|
return maxSameTimeTrades;
|
|
}
|
|
|
|
//
|
|
// Calculate Max Drawdown and it's Percent ...
|
|
double GetMaxDrawdown()
|
|
{
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(positions);
|
|
if (IsValidSize(positionsCount))
|
|
{
|
|
double mEquity = mTrader.mAccount.GetEquity();
|
|
if (mEquity > maxDrawdown)
|
|
{
|
|
maxDrawdown = mEquity;
|
|
}
|
|
|
|
//
|
|
drawdownPercent = (maxDrawdown - mEquity) / (staticBalanceForCalculateDrawdown > 0 ? staticBalanceForCalculateDrawdown : maxDrawdown) * 100;
|
|
drawdownPercent = NormalizeDouble(drawdownPercent, 3);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
maxDrawdown = 0.0;
|
|
drawdownPercent = 0.0;
|
|
}
|
|
|
|
//
|
|
return drawdownPercent;
|
|
}
|
|
|
|
//
|
|
// Trade Handling Functions ...
|
|
|
|
//
|
|
// First Step of Trade Handling ...
|
|
// Since Must Call when a Signal Executed ...
|
|
void AddData(
|
|
XSignal &signal, // Executed Signal
|
|
double commission = 0 // Commission
|
|
)
|
|
{
|
|
//
|
|
// Check Signal Valid ...
|
|
if (!signal.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check Signal Support or Recovery ...
|
|
bool isSupport = IsSupport(signal.comment);
|
|
bool isRecovery = ExtractRecoveredTicket(signal.comment) > 0;
|
|
|
|
//
|
|
X121TradeData item;
|
|
bool isFilled = item.Fill(signal);
|
|
if (!isFilled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
item.commission = commission;
|
|
|
|
//
|
|
Add(item);
|
|
|
|
//
|
|
SaveSignal(item);
|
|
}
|
|
|
|
//
|
|
void Finish(const XDeal &deal)
|
|
{
|
|
//
|
|
int idx = -1;
|
|
bool hasItem = HasItem(
|
|
deal.positionId,
|
|
idx //
|
|
);
|
|
if (!hasItem)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Update Item Info ...
|
|
mData[idx].swap = deal.swap;
|
|
mData[idx].endTime = deal.time;
|
|
mData[idx].profit = deal.profit;
|
|
|
|
//
|
|
mData[idx].message =
|
|
deal.reason == DEAL_REASON_TP ? "TP" : "SL";
|
|
|
|
//
|
|
Save(idx);
|
|
}
|
|
|
|
//
|
|
void Finish(
|
|
const ulong ticket,
|
|
const XPosition &position,
|
|
const string comment //
|
|
)
|
|
{
|
|
//
|
|
int idx = -1;
|
|
bool hasItem = HasItem(
|
|
ticket,
|
|
idx //
|
|
);
|
|
if (!hasItem)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Update Item Info ...
|
|
mData[idx].swap = position.swap;
|
|
mData[idx].endTime = TimeCurrent();
|
|
mData[idx].profit = position.profit;
|
|
|
|
//
|
|
mData[idx].message = comment;
|
|
|
|
//
|
|
Save(idx);
|
|
}
|
|
|
|
//
|
|
// this Method call's by a Timer,
|
|
// or in OnTick for Update Positions,
|
|
// Data ...
|
|
void UpdateData()
|
|
{
|
|
//
|
|
XPosition positions[];
|
|
int count = mTrader.GetPositions(positions);
|
|
if (!IsValidSize(count))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
int idx = -1;
|
|
bool isExists = HasItem(
|
|
iPosition.ticket,
|
|
idx //
|
|
);
|
|
if (!isExists)
|
|
{
|
|
//
|
|
// Add New Data ...
|
|
|
|
//
|
|
// Find Executed Signal Deal History ...
|
|
XDeal deals[];
|
|
double commission = 0;
|
|
int dealsCount = mTrader.GetDeals(
|
|
deals,
|
|
iPosition.symbol,
|
|
iPosition.provider,
|
|
iPosition.period,
|
|
NULL,
|
|
DEAL_ENTRY_IN //
|
|
);
|
|
if (IsValidSize(dealsCount))
|
|
{
|
|
//
|
|
for (int i = 0; i < dealsCount; i++)
|
|
{
|
|
//
|
|
XDeal iDeal = deals[i];
|
|
if (iDeal.positionId == iPosition.ticket)
|
|
{
|
|
//
|
|
commission = iDeal.commission;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
X121TradeData iData;
|
|
iData.Fill(
|
|
iPosition //
|
|
);
|
|
|
|
//
|
|
iData.ticket = iPosition.ticket;
|
|
|
|
//
|
|
Add(iData);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Update Exists ...
|
|
mData[idx].Update(iPosition);
|
|
}
|
|
}
|
|
|
|
//
|
|
count = Count();
|
|
if (count > maxSameTimeTrades)
|
|
{
|
|
maxSameTimeTrades = count;
|
|
}
|
|
|
|
//
|
|
GetMaxDrawdown();
|
|
}
|
|
|
|
//
|
|
void UpdateSignal(XSignal &signal)
|
|
{
|
|
//
|
|
if (!signal.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
int count = Count();
|
|
if (!IsValidSize(count))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
bool isOwn = mData[i].IsOwn(
|
|
signal.symbol,
|
|
signal.provider,
|
|
signal.period,
|
|
ToPositionType(signal.type) //
|
|
);
|
|
if (isOwn)
|
|
{
|
|
mData[i].Fill(signal);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void RemoveSignal(XSignal &signal)
|
|
{
|
|
//
|
|
if (!signal.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
int signalIDX = -1;
|
|
ENUM_X_POSITION_TYPES xType = ToPositionType(signal.type);
|
|
bool hasSignal = HasItem(
|
|
signal.symbol,
|
|
signal.provider,
|
|
signal.period,
|
|
xType,
|
|
signalIDX //
|
|
);
|
|
if (!hasSignal || !IsValidIndex(signalIDX))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Remove Item From List ...
|
|
ArrayRemove(
|
|
mData,
|
|
signalIDX,
|
|
1 //
|
|
);
|
|
}
|
|
|
|
/**
|
|
* this Method Synchronize
|
|
* all Exists Positions and parse them
|
|
* and add them into mData Collection if
|
|
* their not Exists ...
|
|
*/
|
|
void Sync()
|
|
{
|
|
//
|
|
// TODO: Implement here ...
|
|
}
|
|
|
|
/**
|
|
* Validate Signal For Execution ...
|
|
*/
|
|
bool CanExecute(XSignal &signal)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Signal ...
|
|
result = signal.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// TODO:
|
|
// Here we can apply Same type or Opposit Type
|
|
// Signals behaviour ...
|
|
// also check For Market Open ...
|
|
|
|
//
|
|
// Check Market ...
|
|
// TODO: Fix this ...
|
|
// result = mSymbolSession.CanTrade(signal.symbol);
|
|
// if (!result) {
|
|
// return result;
|
|
// }
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Execute Specific Signal using Trade Handler ...
|
|
*/
|
|
bool ExecuteSignal(
|
|
XSignal &signal, // Signal for Execution
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT &state, // Execution State
|
|
bool ignorePolicies = false // Ignore Execution Policies
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check Signal Validation ...
|
|
result = signal.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Filter Signals if Necessary ...
|
|
result = CanExecute(signal);
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_INVALID_PARAMS;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Policies ...
|
|
if (!ignorePolicies)
|
|
{
|
|
//
|
|
bool isLong = IsLong(signal.type);
|
|
|
|
//
|
|
// Check Allow Trade Type ...
|
|
result =
|
|
isLong
|
|
? mAllowLong
|
|
: mAllowShort;
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Trades Count ...
|
|
if (mMaxAllowedLongs > 0 || mMaxAllowedShorts > 0)
|
|
{
|
|
//
|
|
XPosition longs[];
|
|
XPosition shorts[];
|
|
|
|
//
|
|
// Counting Positions ...
|
|
if (mUseMaxAllowedTradesPerSymbol)
|
|
{
|
|
//
|
|
mTrader.GetPositions(
|
|
longs,
|
|
shorts,
|
|
signal.symbol //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
mTrader.GetPositions(
|
|
longs,
|
|
shorts,
|
|
NULL // All Symbols ...
|
|
);
|
|
}
|
|
|
|
//
|
|
int longsCount = ArraySize(longs);
|
|
int shortsCount = ArraySize(shorts);
|
|
|
|
//
|
|
result =
|
|
//
|
|
// Long ...
|
|
isLong
|
|
? mMaxAllowedLongs <= 0
|
|
? true
|
|
: longsCount < mMaxAllowedLongs
|
|
//
|
|
// Short ...
|
|
: mMaxAllowedShorts <= 0
|
|
? true
|
|
: shortsCount < mMaxAllowedShorts
|
|
//
|
|
;
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_MAX_REACHED;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Drawdown ...
|
|
if (mMaxDrawdownPercentForOpenTrades > 0)
|
|
{
|
|
//
|
|
drawdownPercent = GetMaxDrawdown();
|
|
|
|
//
|
|
result =
|
|
drawdownPercent <= 0
|
|
? true
|
|
: drawdownPercent < mMaxDrawdownPercentForOpenTrades;
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_NO_EQUITY;
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Execute Signal ...
|
|
result = mTrader.ExecuteSignal(
|
|
signal,
|
|
state //
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
double commission = mTrader.GetPositionCommission(signal.positionId);
|
|
|
|
//
|
|
// Add Signal to Trade Handler ...
|
|
AddData(
|
|
signal,
|
|
commission //
|
|
);
|
|
|
|
//
|
|
string msg = (signal.type == POSITION_TYPE_BUY ? "Long" : "Short") +
|
|
" Signal Provided by: " + signal.provider +
|
|
(signal.pushers > 1 ? " Pushers: " + ToString(signal.pushers) : "") +
|
|
" on: " + signal.symbol +
|
|
" in: " + ToString(signal.period) +
|
|
" Executed Successfully ...";
|
|
Alert(msg);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Handle Positiona Protections
|
|
*/
|
|
void HandleProtection()
|
|
{
|
|
//
|
|
// TODO: Implement this ...
|
|
}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
XSCTrade *mTrader; // Instance of Trader Class
|
|
X121TradeCollector *mCollector; // Instance of Trade Collector Class
|
|
|
|
//
|
|
X121TradeData mData[]; // Hold Trade Data
|
|
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Read Only ...
|
|
int maxSameTimeTrades; // Max Same Time Trades
|
|
double maxDrawdown; // Max Drawdown
|
|
double drawdownPercent; // Drawdown Percent
|
|
double staticBalanceForCalculateDrawdown; // Static Balance for Max Drawdown Calculation
|
|
|
|
//
|
|
bool mSaveSignals; // Save Signals
|
|
bool mSaveTrades; // Save Trades
|
|
bool mSaveConditions; // Save SL Conditions
|
|
|
|
//
|
|
bool mAllowLong; // Allow Long/Buy Trade Type
|
|
bool mAllowShort; // Allow Short/Sell Trade Type
|
|
int mMaxAllowedLongs; // Max Allowe Long/Buy Trades
|
|
int mMaxAllowedShorts; // Max Allowe Short/Sell Trades
|
|
bool mUseMaxAllowedTradesPerSymbol; // Use Max Allowed Trades Per Symbol
|
|
double mMaxDrawdownPercentForOpenTrades; // Max Drawdown Percent for Open Trades
|
|
|
|
//
|
|
// Count Data ...
|
|
int Count()
|
|
{
|
|
return ArraySize(mData);
|
|
}
|
|
|
|
//
|
|
// Add Item ...
|
|
bool Add(X121TradeData &item)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = item.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int index = -1;
|
|
if (item.ticket > 0)
|
|
{
|
|
//
|
|
result = !HasItem(
|
|
item.ticket,
|
|
index //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
result = !HasItem(
|
|
item.symbol,
|
|
item.provider,
|
|
item.period,
|
|
item.type,
|
|
index //
|
|
);
|
|
}
|
|
if (!result || IsValidIndex(index))
|
|
{
|
|
//
|
|
result = false;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
item,
|
|
mData //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Find Item Index ...
|
|
bool HasItem(
|
|
ulong ticket,
|
|
int &index //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
index = -1;
|
|
|
|
//
|
|
int count = Count();
|
|
result = IsValidSize(count);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
result = mData[i].IsOwn(ticket);
|
|
if (result)
|
|
{
|
|
//
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Find Item Index ...
|
|
bool HasItem(
|
|
string symbol,
|
|
string provider,
|
|
ENUM_TIMEFRAMES period,
|
|
ENUM_X_POSITION_TYPES type,
|
|
int &index //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
index = -1;
|
|
|
|
//
|
|
// Validate ...
|
|
result =
|
|
//
|
|
IsValid(symbol) &&
|
|
IsValid(period) &&
|
|
IsValid(provider) &&
|
|
type != X_POSITION_TYPE_ALL &&
|
|
type != X_POSITION_TYPE_NONE
|
|
//
|
|
;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int count = Count();
|
|
result = IsValidSize(count);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
result = mData[i].IsOwn(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type //
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
void Save(int index)
|
|
{
|
|
//
|
|
int count = Count();
|
|
if (!IsValidIndex(index) || index > count - 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Save Trade ...
|
|
if (mSaveTrades)
|
|
{
|
|
mCollector.Save(mData[index]);
|
|
}
|
|
|
|
//
|
|
if (mSaveConditions)
|
|
{
|
|
mCollector.SaveConditions(mData[index]);
|
|
}
|
|
|
|
//
|
|
// Remove Item From List ...
|
|
ArrayRemove(
|
|
mData,
|
|
index,
|
|
1 //
|
|
);
|
|
}
|
|
|
|
//
|
|
void SaveSignal(X121TradeData &item)
|
|
{
|
|
//
|
|
if (!mSaveSignals)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Save Signal ...
|
|
mCollector.SaveSignal(item);
|
|
}
|
|
|
|
//
|
|
};
|
|
|
|
// |