1420 lines
32 KiB
Plaintext
1420 lines
32 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Class Library
|
|
// --------------------------------------
|
|
// Name: XCXCAEATradeManager
|
|
// Description: provides all Requirements for Managing Trades ...
|
|
//
|
|
//
|
|
// 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.x-alert.class.mq5"
|
|
#include "../../Classes/x-saherelm.x-poi.drawer.class.mq5"
|
|
#include "../../Classes/x-saherelm.x-trade.class.mq5"
|
|
#include "../Classes/xcaea.x-data.collector.class.mq5"
|
|
#include "../Libraries/xcaea.signaller.lib.mq5"
|
|
|
|
//
|
|
// Definitions ...
|
|
|
|
//
|
|
enum ENUM_XCAEA_TRADE_FINALIZATION
|
|
{
|
|
XCAEA_TRADE_FINAL_NONE, // None
|
|
XCAEA_TRADE_FINAL_TP, // TP
|
|
XCAEA_TRADE_FINAL_SL, // SL
|
|
XCAEA_TRADE_FINAL_CLOSE_IN_PROFIT, // Profit
|
|
XCAEA_TRADE_FINAL_CLOSE_IN_LOSE, // Lost
|
|
};
|
|
|
|
string ToString(ENUM_XCAEA_TRADE_FINALIZATION value)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
result = EnumToString(value);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
struct XCAEATrade
|
|
{
|
|
//
|
|
// Props ...
|
|
double swap;
|
|
double profit;
|
|
XSignal signal;
|
|
ulong positionID;
|
|
double commission;
|
|
XCAEAStrategyConditions conditions;
|
|
ENUM_XCAEA_TRADE_FINALIZATION finalize;
|
|
|
|
//
|
|
datetime signaledAt;
|
|
datetime executedAt;
|
|
datetime finalizedAt;
|
|
|
|
//
|
|
// Constructor ...
|
|
XCAEATrade()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
/**
|
|
* Cleanup Model ...
|
|
*/
|
|
void Clean()
|
|
{
|
|
//
|
|
swap = 0;
|
|
profit = 0;
|
|
signal.Clean();
|
|
commission = 0;
|
|
positionID = 0;
|
|
conditions.Clean();
|
|
finalize = XCAEA_TRADE_FINAL_NONE;
|
|
|
|
//
|
|
signaledAt = NULL;
|
|
executedAt = NULL;
|
|
finalizedAt = NULL;
|
|
|
|
//
|
|
ZeroMemory(this);
|
|
}
|
|
|
|
/**
|
|
* Check Has Signal ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool HasSignal()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = signal.IsValid();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check Signal Executed or Not ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsExecuted()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = positionID > 0;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check a Trade is Finalized or not ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool IsFinalized()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = HasSignal() &&
|
|
IsExecuted() &&
|
|
finalize != XCAEA_TRADE_FINAL_NONE;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Generate Summary Info ...
|
|
*
|
|
* @return ( string )
|
|
*/
|
|
string Summary()
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
result =
|
|
//
|
|
ToString("Swap: ", swap) +
|
|
ToString("Profit: ", profit) +
|
|
ToString("Commission: ", commission) +
|
|
ToString("Finalize: ", ToString(finalize)) +
|
|
ToString("PositionID: ", positionID) +
|
|
ToString("----------------") + "\n" +
|
|
ToString("Signaled At: ", ToFormatString(signaledAt)) +
|
|
ToString("Executed At: ", ToFormatString(executedAt)) +
|
|
ToString("Finalized At: ", ToFormatString(finalizedAt))
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get Data Collection File Name ...
|
|
*
|
|
* @return ( string )
|
|
*/
|
|
string GetFileName()
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
bool hasSignal = HasSignal();
|
|
bool isExecuted = IsExecuted();
|
|
bool isFinalized = IsFinalized();
|
|
if (!hasSignal && !isExecuted && !isFinalized)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result =
|
|
//
|
|
signal.symbol + "\\" +
|
|
ToString(signal.type) + "\\" +
|
|
(profit >= 0 ? "Profit" : "Loss") + "\\" +
|
|
ToString(positionID) + "_" +
|
|
ToString(signal.period) + "_" +
|
|
ToFormatString(signaledAt)
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Converts Model to String Representation ...
|
|
*
|
|
* @param onlySignals: Boolean, Specified Represent Only Signal or not ...
|
|
*
|
|
* @return ( string )
|
|
*/
|
|
string ToString(
|
|
bool includeSummary = true,
|
|
bool includeSignal = true,
|
|
bool includeConditions = true //
|
|
)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
// Generating Model Summary ...
|
|
string summary = Summary();
|
|
|
|
//
|
|
// Generating Signal Summary ...
|
|
string signalSummary = signal.ToString();
|
|
|
|
//
|
|
// Generating Conditions Summary ...
|
|
string conditionsSummary = conditions
|
|
.conditions
|
|
.GenerateSummary(
|
|
true,
|
|
true,
|
|
true,
|
|
false // Ignore False Conditions ...
|
|
);
|
|
|
|
//
|
|
if (includeSummary)
|
|
{
|
|
//
|
|
result +=
|
|
ToString("----------------") + "\n" +
|
|
ToString("Summary: ") + "\n" +
|
|
ToString("----------------") + "\n" +
|
|
summary + "\n";
|
|
}
|
|
|
|
//
|
|
if (includeSignal)
|
|
{
|
|
//
|
|
result +=
|
|
ToString("----------------") + "\n" +
|
|
ToString("Signal: ") + "\n" +
|
|
ToString("----------------") + "\n" +
|
|
signalSummary + "\n";
|
|
}
|
|
|
|
//
|
|
if (includeConditions)
|
|
{
|
|
//
|
|
result +=
|
|
ToString("----------------") + "\n" +
|
|
ToString("Conditions: ") + "\n" +
|
|
ToString("----------------") + "\n" +
|
|
conditionsSummary + "\n";
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
};
|
|
|
|
//
|
|
// Implementations ...
|
|
class XCXCAEATradeManager : public XCBaseAlert
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Props ...
|
|
XCTrade *trader;
|
|
bool drawSignal;
|
|
XCPOIDrawer *drawer;
|
|
XCXCAEADataCollector *collector;
|
|
|
|
//
|
|
// Constructors ...
|
|
XCXCAEATradeManager(
|
|
XCTrade *&_trader,
|
|
string _path = NULL // Base Path
|
|
)
|
|
{
|
|
//
|
|
trader = _trader;
|
|
drawSignal = true;
|
|
drawer = new XCPOIDrawer();
|
|
|
|
//
|
|
allowTrade = true;
|
|
allowLongs = true;
|
|
allowShorts = true;
|
|
|
|
//
|
|
reportTrades = true;
|
|
reportSignals = true;
|
|
reportProtector = true;
|
|
reportAfterTradesBalance = true;
|
|
|
|
//
|
|
SaveTrades(false);
|
|
|
|
//
|
|
// Normalizing Collector Path ...
|
|
if (!IsValid(_path))
|
|
{
|
|
_path = "X121XCAEATradeManager" + "\\" + trader.mAccount.GetCompany();
|
|
}
|
|
|
|
//
|
|
collector = new XCXCAEADataCollector();
|
|
collector.Path(_path);
|
|
}
|
|
|
|
//
|
|
// De Constructors ...
|
|
~XCXCAEATradeManager()
|
|
{
|
|
//
|
|
Clean(trades);
|
|
|
|
//
|
|
ZeroMemory(trader);
|
|
ZeroMemory(drawer);
|
|
ZeroMemory(collector);
|
|
}
|
|
|
|
//
|
|
// Properties ...
|
|
|
|
//
|
|
bool SaveTrades()
|
|
{
|
|
return mSaveTrades;
|
|
}
|
|
|
|
//
|
|
void SaveTrades(bool value)
|
|
{
|
|
mSaveTrades = value;
|
|
}
|
|
|
|
//
|
|
// TODO: Implement this ...
|
|
void SetTradeSession(
|
|
string start,
|
|
string end //
|
|
)
|
|
{
|
|
//
|
|
bool has = IsValid(start) ||
|
|
IsValid(end);
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
}
|
|
|
|
//
|
|
void SetTradeReports(
|
|
bool _reportTrades,
|
|
bool _reportSignals,
|
|
bool _reportProtector,
|
|
bool _reportAfterTradesBalance //
|
|
)
|
|
{
|
|
//
|
|
reportTrades = _reportTrades;
|
|
reportSignals = _reportSignals;
|
|
reportProtector = _reportProtector;
|
|
reportAfterTradesBalance = _reportAfterTradesBalance;
|
|
}
|
|
|
|
//
|
|
void SetTradePermissions(
|
|
bool _allowTrade,
|
|
bool _allowLongs,
|
|
bool _allowShorts //
|
|
)
|
|
{
|
|
//
|
|
allowTrade = _allowTrade;
|
|
allowLongs = _allowLongs;
|
|
allowShorts = _allowShorts;
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
/**
|
|
* Do All Position Managing Senaros ...
|
|
*/
|
|
void Manage()
|
|
{
|
|
//
|
|
bool has = HasChild(trades);
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
int count = ArraySize(trades);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
has = trades[i].IsExecuted() &&
|
|
!trades[i].IsFinalized();
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
XPosition iPosition;
|
|
has = trader.GetPosition(
|
|
trades[i].positionID,
|
|
iPosition //
|
|
);
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Update Trade Data ...
|
|
trades[i].swap = iPosition.swap;
|
|
trades[i].profit = iPosition.profit;
|
|
}
|
|
|
|
//
|
|
// After Update All Trades we can Do Protection ...
|
|
Protect();
|
|
}
|
|
|
|
/**
|
|
* Do All Position Protection Here ...
|
|
*/
|
|
void Protect()
|
|
{
|
|
//
|
|
string prefix = "Protector: ";
|
|
int targetDistance = 5;
|
|
|
|
//
|
|
bool has = HasChild(trades);
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
XPosition ownPosition[];
|
|
XPosition ownLongPosition[];
|
|
XPosition ownShortPosition[];
|
|
|
|
//
|
|
int count = ArraySize(trades);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
has =
|
|
trades[i].IsExecuted() &&
|
|
!trades[i].IsFinalized();
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
XPosition iPosition;
|
|
has = trader.GetPosition(
|
|
trades[i].positionID,
|
|
iPosition //
|
|
);
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
ownPosition //
|
|
);
|
|
|
|
//
|
|
if (IsLong(iPosition.type))
|
|
{
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
ownLongPosition //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
ownShortPosition //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
has = HasChild(ownPosition);
|
|
if (!has)
|
|
{
|
|
//
|
|
Clean(ownPosition);
|
|
Clean(ownLongPosition);
|
|
Clean(ownShortPosition);
|
|
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
double swapSum = 0;
|
|
double profitSum = 0;
|
|
double commissionSum = 0;
|
|
|
|
//
|
|
// Calculate Required Data On Collection ...
|
|
count = ArraySize(ownPosition);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = ownPosition[i];
|
|
|
|
//
|
|
swapSum += iPosition.swap;
|
|
profitSum += iPosition.profit;
|
|
commissionSum += iPosition.commission;
|
|
}
|
|
|
|
//
|
|
// Do Collection Protectiong ...
|
|
// TODO: Implement this ...
|
|
|
|
//
|
|
// Do Signle Protections ...
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
// Select Position ...
|
|
XPosition iPosition = ownPosition[i];
|
|
int idx = -1;
|
|
has = HasTrade(
|
|
idx,
|
|
iPosition.ticket //
|
|
);
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isLong = IsLong(iPosition.type);
|
|
double points = GetPoints(iPosition.symbol);
|
|
|
|
//
|
|
// Handle RF On Targets ...
|
|
if (iPosition.profit > 0)
|
|
{
|
|
//
|
|
// Handle Risk Free On Targets ...
|
|
double targets[];
|
|
Copy(
|
|
trades[idx].signal.targets,
|
|
targets //
|
|
);
|
|
int targetsCount = ArraySize(targets);
|
|
has = IsValidSize(targetsCount);
|
|
if (has)
|
|
{
|
|
//
|
|
for (int j = 0; j < targetsCount; j++)
|
|
{
|
|
//
|
|
double jTarget = targets[j];
|
|
|
|
//
|
|
int appliedTargetIDX = -1;
|
|
|
|
//
|
|
// Check Target is Applied Before or Not ...
|
|
has =
|
|
!trades[idx].signal.isTargetApplied
|
|
? true
|
|
: trades[idx].signal.appliedTargetIDX < j;
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Traget Validation ...
|
|
has =
|
|
iPosition.sl == 0
|
|
? true
|
|
: isLong
|
|
? iPosition.sl < jTarget
|
|
: iPosition.sl > jTarget;
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
double targetDelta = isLong
|
|
? jTarget + (targetDistance * points)
|
|
: jTarget - (targetDistance * points);
|
|
|
|
//
|
|
// Can Risk Free ...
|
|
bool canRF = jTarget > 0 &&
|
|
(isLong
|
|
? jTarget > iPosition.entry
|
|
: jTarget < iPosition.entry) &&
|
|
(isLong
|
|
? iPosition.price > targetDelta
|
|
: iPosition.price < targetDelta);
|
|
if (canRF)
|
|
{
|
|
//
|
|
double sl = jTarget;
|
|
double tp = iPosition.tp;
|
|
string comment = "RF On Target ...";
|
|
bool isModified = trader.Modify(
|
|
iPosition.ticket,
|
|
sl,
|
|
tp,
|
|
comment //
|
|
);
|
|
if (isModified)
|
|
{
|
|
//
|
|
trades[idx].signal.appliedTargetIDX = j;
|
|
trades[idx].signal.isTargetApplied = true;
|
|
|
|
//
|
|
string message = prefix +
|
|
ToString(iPosition.type) +
|
|
" Position: " +
|
|
ToString(iPosition.ticket) +
|
|
" RF On Traget: " + ToString(jTarget) +
|
|
" Successfully ...";
|
|
HandleReportProtector(message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handle Partial Close on Specified Targets ...
|
|
has = !trades[idx].signal.isPartiallyClosed &&
|
|
trades[idx].signal.partialCloseOnTPLevel > 0 &&
|
|
trades[idx].signal.partialCloseMultiplier > 0;
|
|
if (has)
|
|
{
|
|
//
|
|
// Check Reward Touched ...
|
|
double iCurrReward = iPosition.CalculateTouchedReward();
|
|
has = iCurrReward >= trades[idx].signal.partialCloseOnTPLevel;
|
|
if (has)
|
|
{
|
|
//
|
|
// Check Volume Multiplier ...
|
|
double volumeMultiplier = trades[idx].signal.partialCloseMultiplier;
|
|
has = volumeMultiplier > 0 &&
|
|
volumeMultiplier <= 1;
|
|
if (has)
|
|
{
|
|
//
|
|
// Do Partial Closing ...
|
|
double volume = iPosition.volume * volumeMultiplier;
|
|
|
|
//
|
|
// Normalizing Volume ...
|
|
volume = NormalizeVolume(
|
|
volume,
|
|
iPosition.symbol //
|
|
);
|
|
|
|
//
|
|
string comment = "PC On Target ...";
|
|
has = trader.ClosePartial(
|
|
iPosition.ticket,
|
|
volume,
|
|
comment //
|
|
);
|
|
if (has)
|
|
{
|
|
//
|
|
// Update Model ...
|
|
trades[idx].signal.isPartiallyClosed = true;
|
|
|
|
//
|
|
string message = prefix +
|
|
ToString(iPosition.type) +
|
|
" Position: " +
|
|
ToString(iPosition.ticket) +
|
|
" PC (" + ToString(volume) + ") On Traget: " + ToString(iCurrReward) +
|
|
" Successfully ...";
|
|
HandleReportProtector(message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check Signal Execution Conditions ...
|
|
*
|
|
* @param signal: XSignal instance reference ...
|
|
* @param executionResult: ENUM_X_SIGNAL_EXECUTION_RESULT member reference ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool CheckSignal(
|
|
XSignal &signal,
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT &executionResult //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Cleanup ...
|
|
executionResult = X_SIGNAL_EXECUTION_UNKNOWN;
|
|
|
|
//
|
|
// Validate Signal ...
|
|
result = signal.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Reporting Signal ...
|
|
if (reportSignals)
|
|
{
|
|
//
|
|
string message = signal.GetMessage("Provided");
|
|
Alert(message);
|
|
}
|
|
|
|
//
|
|
// Check Trade Permission ...
|
|
if (!allowTrade)
|
|
{
|
|
//
|
|
result = false;
|
|
executionResult = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool isLong = IsLong(signal.type);
|
|
bool isShort = IsShort(signal.type);
|
|
|
|
//
|
|
bool isLongAndAllowed =
|
|
isLong &&
|
|
allowLongs;
|
|
|
|
//
|
|
bool isShortAndAllowed =
|
|
isShort &&
|
|
allowShorts;
|
|
|
|
//
|
|
result = isLongAndAllowed ||
|
|
isShortAndAllowed;
|
|
if (!result)
|
|
{
|
|
//
|
|
executionResult = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// TODO: Complete this ...
|
|
// Implementing Trade Session Validating ...
|
|
|
|
//
|
|
// TODO: Complete this ...
|
|
// Implementing Trade Permissions Based on SL(s) Count ...
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Execute Specified Signal ...
|
|
*
|
|
* @param signal: XSignal instance reference ...
|
|
* @param conditions: XCAEAStrategyConditions instance reference ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool Execute(
|
|
XSignal &signal,
|
|
XCAEAStrategyConditions &conditions //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = signal.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int idx = -1;
|
|
bool isExists = HasTrade(
|
|
idx,
|
|
signal //
|
|
);
|
|
result = !isExists;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT executionResult;
|
|
|
|
//
|
|
// Checking Signal For Execution ...
|
|
result = CheckSignal(
|
|
signal,
|
|
executionResult //
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
result = trader.ExecuteSignal(
|
|
signal,
|
|
executionResult //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Alert Signal Execution ...
|
|
if (reportTrades)
|
|
{
|
|
//
|
|
string executionMessage = ToString(executionResult);
|
|
|
|
//
|
|
string message = NULL;
|
|
if (result)
|
|
{
|
|
message = signal.GetMessage("Executed");
|
|
}
|
|
else
|
|
{
|
|
message = signal.GetMessage("Failed due (" + executionMessage + ")");
|
|
}
|
|
|
|
//
|
|
Alert(message);
|
|
}
|
|
|
|
//
|
|
if (result)
|
|
{
|
|
//
|
|
if (drawSignal)
|
|
{
|
|
//
|
|
XCSignalObject *iObj;
|
|
isExists = drawer.DrawSignal(
|
|
signal,
|
|
iObj //
|
|
);
|
|
|
|
//
|
|
if (isExists)
|
|
{
|
|
//
|
|
mObjects.Add(iObj);
|
|
ZeroMemory(iObj);
|
|
}
|
|
}
|
|
|
|
//
|
|
XCAEATrade iTrade;
|
|
|
|
//
|
|
ulong positionID = trader.GetLastOpenPositionTicket();
|
|
double commission = trader.GetPositionCommission(positionID);
|
|
|
|
//
|
|
iTrade.signal = signal;
|
|
iTrade.conditions = conditions;
|
|
iTrade.positionID = positionID;
|
|
iTrade.commission = commission;
|
|
iTrade.signaledAt = signal.time;
|
|
iTrade.executedAt = TimeCurrent();
|
|
|
|
//
|
|
AddRef(
|
|
iTrade,
|
|
trades //
|
|
);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Handle Position Take Profit ...
|
|
*
|
|
* @param deal: XDeal instance reference ...
|
|
*/
|
|
void HandleTP(const XDeal &deal)
|
|
{
|
|
//
|
|
// Check Ticket Exists or not ...
|
|
int idx = -1;
|
|
bool isExists = HasTrade(
|
|
idx,
|
|
deal.positionId //
|
|
);
|
|
if (!isExists)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Apply Required Data ...
|
|
trades[idx].swap = deal.swap;
|
|
trades[idx].profit = deal.profit;
|
|
trades[idx].finalizedAt = TimeCurrent();
|
|
trades[idx].finalize = XCAEA_TRADE_FINAL_TP;
|
|
|
|
//
|
|
tpCount++;
|
|
managedTPCount++;
|
|
|
|
//
|
|
// Save Trade ...
|
|
Save(trades[idx]);
|
|
|
|
//
|
|
// Handle Balance Reporting ...
|
|
string prefix = "TP: " + ToString(trades[idx].profit);
|
|
HandleReportBalance(prefix);
|
|
|
|
//
|
|
RemoveTrade(idx);
|
|
}
|
|
|
|
/**
|
|
* Handle Position Stop Loss ...
|
|
*
|
|
* @param deal: XDeal instance reference ...
|
|
*/
|
|
void HandleSL(const XDeal &deal)
|
|
{
|
|
//
|
|
// Check Ticket Exists or not ...
|
|
int idx = -1;
|
|
bool isExists = HasTrade(
|
|
idx,
|
|
deal.positionId //
|
|
);
|
|
if (!isExists)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Apply Required Data ...
|
|
trades[idx].swap = deal.swap;
|
|
trades[idx].profit = deal.profit;
|
|
trades[idx].finalizedAt = TimeCurrent();
|
|
trades[idx].finalize = XCAEA_TRADE_FINAL_SL;
|
|
|
|
//
|
|
slCount++;
|
|
managedSLCount++;
|
|
|
|
//
|
|
// Save Trade ...
|
|
Save(trades[idx]);
|
|
|
|
//
|
|
// Handle Balance Reporting ...
|
|
string prefix = "SL: " + ToString(trades[idx].profit);
|
|
HandleReportBalance(prefix);
|
|
|
|
//
|
|
RemoveTrade(idx);
|
|
}
|
|
|
|
/**
|
|
* Handle Position Force Close ...
|
|
*
|
|
* @param deal: XDeal instance reference ...
|
|
*/
|
|
void HandleForceClose(const XPosition &position)
|
|
{
|
|
//
|
|
// Check Ticket Exists or not ...
|
|
int idx = -1;
|
|
bool isExists = HasTrade(
|
|
idx,
|
|
position.ticket //
|
|
);
|
|
if (!isExists)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
bool isInProfit = position.profit > 0;
|
|
|
|
//
|
|
// Apply Required Data ...
|
|
trades[idx].swap = position.swap;
|
|
trades[idx].profit = position.profit;
|
|
trades[idx].finalizedAt = TimeCurrent();
|
|
trades[idx].commission = position.commission;
|
|
trades[idx].finalize = isInProfit
|
|
? XCAEA_TRADE_FINAL_CLOSE_IN_PROFIT
|
|
: XCAEA_TRADE_FINAL_CLOSE_IN_LOSE;
|
|
|
|
//
|
|
if (isInProfit)
|
|
{
|
|
//
|
|
tpCount++;
|
|
managedTPCount++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
slCount++;
|
|
managedSLCount++;
|
|
}
|
|
|
|
//
|
|
// Save Trade ...
|
|
Save(trades[idx]);
|
|
|
|
//
|
|
// Handle Balance Reporting ...
|
|
string prefix = "Force Close " +
|
|
(isInProfit
|
|
? "Profit"
|
|
: "Loss") +
|
|
": " +
|
|
ToString(trades[idx].profit);
|
|
HandleReportBalance(prefix);
|
|
|
|
//
|
|
RemoveTrade(idx);
|
|
}
|
|
|
|
//
|
|
// TODO: Complete this ...
|
|
void HandleGuard()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Handle New Day ...
|
|
*/
|
|
void HandleNewDay()
|
|
{
|
|
//
|
|
// Clean Managed SL(s) and TP(s) and Positions ...
|
|
}
|
|
|
|
/**
|
|
* Find Executed Trade by Providing Position Ticket ...
|
|
*
|
|
* @param index: int reference, holding founded item index ...
|
|
* @param ticket: ulong, Specified Position Ticket ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool HasTrade(
|
|
int &index,
|
|
ulong ticket //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
index = -1;
|
|
|
|
//
|
|
result =
|
|
ticket > 0 &&
|
|
HasChild(trades);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int count = ArraySize(trades);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCAEATrade iTrade = trades[i];
|
|
|
|
//
|
|
result =
|
|
iTrade.HasSignal() &&
|
|
iTrade.IsExecuted() &&
|
|
iTrade.positionID == ticket;
|
|
if (result)
|
|
{
|
|
//
|
|
index = i;
|
|
|
|
//
|
|
iTrade.Clean();
|
|
break;
|
|
}
|
|
|
|
//
|
|
iTrade.Clean();
|
|
}
|
|
|
|
//
|
|
result = IsValidIndex(index);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Find Trade Item By Signal ...
|
|
*
|
|
* @param index: int reference, holding founded item index ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool HasTrade(
|
|
int &index,
|
|
XSignal &signal //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
index = -1;
|
|
|
|
//
|
|
result =
|
|
signal.IsValid() &&
|
|
HasChild(trades);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int count = ArraySize(trades);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XCAEATrade iTrade = trades[i];
|
|
|
|
//
|
|
result =
|
|
iTrade.HasSignal() &&
|
|
iTrade.signal.IsSameAs(signal);
|
|
if (result)
|
|
{
|
|
//
|
|
index = i;
|
|
|
|
//
|
|
iTrade.Clean();
|
|
break;
|
|
}
|
|
|
|
//
|
|
iTrade.Clean();
|
|
}
|
|
|
|
//
|
|
result = IsValidIndex(index);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Remove a Trade From List ...
|
|
*
|
|
* @param index: int ...
|
|
*/
|
|
void RemoveTrade(int index)
|
|
{
|
|
//
|
|
bool has =
|
|
HasChild(trades) &&
|
|
IsValidIndex(index) &&
|
|
index < ArraySize(trades);
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
ArrayRemove(
|
|
trades,
|
|
index,
|
|
1 //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
string GetFilePath(XCAEATrade &trade)
|
|
{
|
|
//
|
|
string fileName = trade.GetFileName();
|
|
|
|
//
|
|
return GetFilePath(fileName);
|
|
}
|
|
|
|
//
|
|
string GetFilePath(string fileName)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
string mPath = collector.Path();
|
|
|
|
//
|
|
result =
|
|
//
|
|
mPath + "\\" + "Trades" + "\\" + fileName + ".x121.log"
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
void HandleReportBalance(string prefix = NULL)
|
|
{
|
|
//
|
|
if (!reportAfterTradesBalance)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
double balance = trader.mAccount.GetBalance();
|
|
string currency = trader.mAccount.GetCurrency();
|
|
|
|
//
|
|
string message =
|
|
(IsValid(prefix)
|
|
? prefix + "\n"
|
|
: "") +
|
|
"Account Balance: " + ToString(balance) + currency;
|
|
Alert(message);
|
|
}
|
|
|
|
//
|
|
void HandleReportProtector(string message = NULL)
|
|
{
|
|
//
|
|
if (!reportProtector)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Alert(message);
|
|
}
|
|
|
|
//
|
|
bool Save(
|
|
XCAEATrade &trade,
|
|
bool includeSummary = true,
|
|
bool includeSignal = true,
|
|
bool includeConditions = true //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (!mSaveTrades)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string filePath = GetFilePath(trade);
|
|
result = IsValid(filePath);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string content = trade.ToString(
|
|
includeSummary,
|
|
includeSignal,
|
|
includeConditions //
|
|
);
|
|
|
|
//
|
|
result = collector.Save(
|
|
filePath,
|
|
content //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
bool allowTrade;
|
|
bool allowLongs;
|
|
bool allowShorts;
|
|
|
|
//
|
|
bool mSaveTrades;
|
|
|
|
//
|
|
int slCount;
|
|
int tpCount;
|
|
|
|
//
|
|
int managedSLCount;
|
|
int managedTPCount;
|
|
|
|
//
|
|
bool reportTrades;
|
|
bool reportSignals;
|
|
bool reportProtector;
|
|
bool reportAfterTradesBalance;
|
|
|
|
//
|
|
CArrayObj mObjects;
|
|
|
|
//
|
|
XCAEATrade trades[];
|
|
};
|
|
|
|
//
|