10317 lines
230 KiB
Plaintext
10317 lines
230 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Class Library
|
|
// --------------------------------------
|
|
// Name: XClass
|
|
// Description: provides all classes for use ...
|
|
//
|
|
//
|
|
// 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
|
|
|
|
//
|
|
#include <Trade/Trade.mqh>
|
|
#include <Trade/DealInfo.mqh>
|
|
#include <Trade/OrderInfo.mqh>
|
|
#include <Trade/AccountInfo.mqh>
|
|
#include <Trade/PositionInfo.mqh>
|
|
#include <Trade/HistoryOrderInfo.mqh>
|
|
//
|
|
#include "../Libraries/x-saherelm.draw.lib.mq5"
|
|
#include "../Libraries/x-saherelm.alert.lib.mq5"
|
|
#include "../Libraries/x-saherelm.models.lib.mq5"
|
|
#include "../Libraries/x-saherelm.common.lib.mq5"
|
|
|
|
//
|
|
// START Constant Definitions ...
|
|
//
|
|
|
|
static uchar _md5_PADDING[64] =
|
|
{
|
|
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
|
|
#define _md5_F(x, y, z) (((x) & (y)) | ((~x) & (z)))
|
|
#define _md5_G(x, y, z) (((x) & (z)) | ((y) & (~z)))
|
|
#define _md5_H(x, y, z) ((x) ^ (y) ^ (z))
|
|
#define _md5_I(x, y, z) ((y) ^ ((x) | (~z)))
|
|
#define _md5_ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
|
|
|
|
#define _md5_FF(a, b, c, d, x, s, ac) \
|
|
{ \
|
|
(a) += _md5_F((b), (c), (d)) + (x) + (uint)(ac); \
|
|
(a) = _md5_ROTATE_LEFT((a), (s)); \
|
|
(a) += (b); \
|
|
}
|
|
#define _md5_GG(a, b, c, d, x, s, ac) \
|
|
{ \
|
|
(a) += _md5_G((b), (c), (d)) + (x) + (uint)(ac); \
|
|
(a) = _md5_ROTATE_LEFT((a), (s)); \
|
|
(a) += (b); \
|
|
}
|
|
#define _md5_HH(a, b, c, d, x, s, ac) \
|
|
{ \
|
|
(a) += _md5_H((b), (c), (d)) + (x) + (uint)(ac); \
|
|
(a) = _md5_ROTATE_LEFT((a), (s)); \
|
|
(a) += (b); \
|
|
}
|
|
#define _md5_II(a, b, c, d, x, s, ac) \
|
|
{ \
|
|
(a) += _md5_I((b), (c), (d)) + (x) + (uint)(ac); \
|
|
(a) = _md5_ROTATE_LEFT((a), (s)); \
|
|
(a) += (b); \
|
|
}
|
|
|
|
#define _md5_INIT_STATE_0 0x67452301
|
|
#define _md5_INIT_STATE_1 0xefcdab89
|
|
#define _md5_INIT_STATE_2 0x98badcfe
|
|
#define _md5_INIT_STATE_3 0x10325476
|
|
|
|
#define _md5_S11 7
|
|
#define _md5_S12 12
|
|
#define _md5_S13 17
|
|
#define _md5_S14 22
|
|
|
|
#define _md5_S21 5
|
|
#define _md5_S22 9
|
|
#define _md5_S23 14
|
|
#define _md5_S24 20
|
|
|
|
#define _md5_S31 4
|
|
#define _md5_S32 11
|
|
#define _md5_S33 16
|
|
#define _md5_S34 23
|
|
|
|
#define _md5_S41 6
|
|
#define _md5_S42 10
|
|
#define _md5_S43 15
|
|
#define _md5_S44 21
|
|
|
|
//
|
|
// END Constant Definitions ...
|
|
//
|
|
|
|
//
|
|
// START Model Definitions ...
|
|
//
|
|
|
|
//
|
|
string X_SIGNAL_TP = "TP";
|
|
string X_SIGNAL_SL = "SL";
|
|
string X_SIGNAL_TYPE = "TY";
|
|
string X_SIGNAL_TICKET = "T";
|
|
string X_SIGNAL_RISKFREE_STEP = "RFS";
|
|
string X_SIGNAL_RISKFREE_RATE = "RFR";
|
|
|
|
//
|
|
// Model Signal Handler Result on XCTrade Class ...
|
|
struct XTraderHandlerResult
|
|
{
|
|
//
|
|
// List Of Closed On SL Signals ...
|
|
XSignal sl[];
|
|
|
|
//
|
|
// List Of Closed On TP Signals ...
|
|
XSignal tp[];
|
|
|
|
//
|
|
// List Of Risk Free Signals ...
|
|
XSignal rf[];
|
|
};
|
|
|
|
//
|
|
// Model OnTrade Event Handler Result on XCTrade Class ...
|
|
struct XOnTradeHandlerResult
|
|
{
|
|
//
|
|
bool hasNewDeal;
|
|
int newDeals;
|
|
|
|
//
|
|
bool hasNewOrder;
|
|
int newOrders;
|
|
|
|
//
|
|
bool hasNewPosition;
|
|
int newPositions;
|
|
|
|
//
|
|
bool hasNewHistoryOrder;
|
|
int newHistoryOrders;
|
|
};
|
|
|
|
//
|
|
// Model Guard Action Applied Signals ...
|
|
struct XGuardHandlerResult
|
|
{
|
|
//
|
|
// Critical Guard ...
|
|
XSignal criticals[];
|
|
|
|
//
|
|
// Max Guard ...
|
|
XSignal maxes[];
|
|
|
|
//
|
|
// Old ...
|
|
XSignal olds[];
|
|
|
|
//
|
|
// Determines Stopout Action Cals or not ...
|
|
bool isStopOut;
|
|
};
|
|
|
|
//
|
|
// Define Volume States ...
|
|
struct XVolumeInfo
|
|
{
|
|
//
|
|
// Current Available Static Volume ...
|
|
double staticVolume;
|
|
|
|
//
|
|
// Static Volume Calculation Method ...
|
|
string staticVolumeCalculationMethod;
|
|
|
|
//
|
|
// Current Maximum Appliable Volume ...
|
|
double maxAllowedVolume;
|
|
|
|
//
|
|
// Static Volume Increase Multiplier ...
|
|
double volumeIncreaseMultiplier;
|
|
|
|
//
|
|
// Multiply Volume Based On Balance Growing ...
|
|
double balanceGowingToApplyMultiplier;
|
|
|
|
//
|
|
// Where to Increase MaxAllowed Volume ...
|
|
double balanceGrowingToIncreaseMaxAllowedVolume;
|
|
};
|
|
|
|
//
|
|
// Model a Simple Provider Status ...
|
|
struct XSignallerInfo
|
|
{
|
|
//
|
|
// Provider Name ...
|
|
string name;
|
|
|
|
//
|
|
// Number of Longs ...
|
|
int longs;
|
|
|
|
//
|
|
// Number of Shorts ...
|
|
int shorts;
|
|
};
|
|
|
|
//
|
|
// Model Some of Usefull Signals and Market Data ...
|
|
struct XSummaryInfo
|
|
{
|
|
//
|
|
// Define Max Signal Volume ...
|
|
double maxSignalVolume;
|
|
|
|
//
|
|
// Max DrawDown ...
|
|
double maxDrawDown;
|
|
|
|
//
|
|
// Max DrawUp ...
|
|
double maxDrawUp;
|
|
|
|
//
|
|
// Max Number of Same Time Signals ...
|
|
int maxSameTimeSignals;
|
|
|
|
//
|
|
// Number of Long Trades ...
|
|
int longSignals;
|
|
|
|
//
|
|
// Number of Short Trades ...
|
|
int shortSignals;
|
|
|
|
//
|
|
// Number of TPs Attached ...
|
|
int numberOfTPs;
|
|
|
|
//
|
|
// Number of SLs Attached ...
|
|
int numberOfSLs;
|
|
};
|
|
|
|
//
|
|
// Model Account info ...
|
|
struct XAccountInfo
|
|
{
|
|
//
|
|
// User Account ...
|
|
string user;
|
|
|
|
//
|
|
// Name of Client ...
|
|
string name;
|
|
|
|
//
|
|
// Trade Server Name ...
|
|
string server;
|
|
|
|
//
|
|
// Broker Name ...
|
|
string broker;
|
|
|
|
//
|
|
// Account Trade Mode ...
|
|
ENUM_ACCOUNT_TRADE_MODE mode;
|
|
|
|
//
|
|
// Account Leverage ...
|
|
long leverage;
|
|
|
|
//
|
|
// Currency ...
|
|
string currency;
|
|
|
|
//
|
|
// Deposit ...
|
|
double deposit;
|
|
|
|
//
|
|
// Balance ...
|
|
double balance;
|
|
|
|
//
|
|
// Min Balance for Open Trades ...
|
|
double minBalanceForOpenTrades;
|
|
};
|
|
|
|
//
|
|
// this is a Model Which holds Reports for Specific Time Periods ...
|
|
struct XReport
|
|
{
|
|
//
|
|
datetime start;
|
|
|
|
//
|
|
datetime end;
|
|
|
|
//
|
|
XAccountInfo accountInfo;
|
|
|
|
//
|
|
XVolumeInfo volumeInfo;
|
|
|
|
//
|
|
XSignallerInfo signallersInfo[];
|
|
|
|
//
|
|
XSummaryInfo summaryInfo;
|
|
};
|
|
|
|
//
|
|
// an Enumeration Object which determines Report Type ...
|
|
enum ENUM_XREPORT_TYPES
|
|
{
|
|
//
|
|
// Main Report ...
|
|
XREPORT_MAIN,
|
|
//
|
|
// Daily Report ...
|
|
XREPORT_DAILY,
|
|
//
|
|
// Weekly Report ...
|
|
XREPORT_WEEKLY,
|
|
//
|
|
// Monthly Report ...
|
|
XREPORT_MONTHLY,
|
|
};
|
|
|
|
//
|
|
// a Signal Protection Structure ...
|
|
struct XProtectedSignal
|
|
{
|
|
//
|
|
ulong ticket;
|
|
|
|
//
|
|
datetime time[];
|
|
|
|
//
|
|
double profit[];
|
|
};
|
|
|
|
//
|
|
// Represent Collector State Based on Time ...
|
|
struct XCollector
|
|
{
|
|
//
|
|
// time in checking ...
|
|
datetime time;
|
|
|
|
//
|
|
// Signal Volume ...
|
|
double volume;
|
|
|
|
//
|
|
// Max DrawDown in Check Time ...
|
|
double maxDrawDown;
|
|
|
|
//
|
|
// Max Allowed Volume in Check Time ...
|
|
double maxAllowedVolume;
|
|
|
|
//
|
|
// Minimum required Balance for Opening Trades at Check Time ...
|
|
double minBalanceForOpenTrade;
|
|
};
|
|
|
|
//
|
|
// Cross Signals Methods ...
|
|
enum ENUM_X_CROSS_SIGNAL_METHOD
|
|
{
|
|
//
|
|
// Do Nothing and Ignore them ...
|
|
X_CROSS_SIGNAL_DO_NOTHING,
|
|
//
|
|
// Accept all Cross Signals and Act Usually ...
|
|
X_CROSS_SIGNAL_REGULAR,
|
|
//
|
|
// Accept all Cross Signals and for Directional Act Usually and for Indirectional Ac Important ...
|
|
X_CROSS_SIGNAL_DIRECTION_REGULAR_INDIRECTION_IMPORTANT,
|
|
//
|
|
// Accept all Cross Signals and for Directional Act Important and for Indirectional Ac Usually ...
|
|
X_CROSS_SIGNAL_DIRECTION_IMPORTANT_INDIRECTION_REGULAR,
|
|
//
|
|
// Accept Only Indirectional Signals and Act Usually ...
|
|
X_CROSS_SIGNAL_INDIRECTION_REGULAR,
|
|
//
|
|
// Accept Only Directional Signals and Act Usually ...
|
|
X_CROSS_SIGNAL_DIRECTION_REGULAR,
|
|
//
|
|
// Accept Only Indirectional Signals and Act Important ...
|
|
X_CROSS_SIGNAL_INDIRECTION_IMPORTANT,
|
|
//
|
|
// Accept Only Directional Signals and Act Important ...
|
|
X_CROSS_SIGNAL_DIRECTION_IMPORTANT,
|
|
};
|
|
|
|
//
|
|
// the Action which Guard Handler Can Done when Attacked ...
|
|
enum ENUM_X_GUARD_ACTIONS
|
|
{
|
|
//
|
|
// Do Nothing ...
|
|
X_GUARD_DO_NOTHING,
|
|
//
|
|
// Close All Open Positions ...
|
|
X_GUARD_CLOSE_ALL,
|
|
//
|
|
// Close Max In Drawdown Position ...
|
|
X_GUARD_CLOSE_MAX_IN_DD,
|
|
//
|
|
// Partial Close Max In Drawdown Position by 1% of Volume ...
|
|
X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_1,
|
|
//
|
|
// Partial Close Max In Drawdown Position by 2% of Volume ...
|
|
X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_2,
|
|
//
|
|
// Partial Close Max In Drawdown Position by 3% of Volume ...
|
|
X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_3,
|
|
//
|
|
// Partial Close Max In Drawdown Position by 5% of Volume ...
|
|
X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_5,
|
|
//
|
|
// Partial Close Max In Drawdown Position by 10% of Volume ...
|
|
X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_10,
|
|
//
|
|
// Partial Close Max In Drawdown Position by 15% of Volume ...
|
|
X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_15,
|
|
//
|
|
// Partial Close Max In Drawdown Position by 20% of Volume ...
|
|
X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_20,
|
|
//
|
|
// Partial Close Max In Drawdown Position by 25% of Volume ...
|
|
X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_25,
|
|
//
|
|
// Partial Close Max In Drawdown Position by 30% of Volume ...
|
|
X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_30,
|
|
//
|
|
// Partial Close Max In Drawdown Position by 40% of Volume ...
|
|
X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_40,
|
|
//
|
|
// Partial Close Max In Drawdown Position by 50% of Volume ...
|
|
X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_50,
|
|
//
|
|
// Partial Close Max In Drawdown Position by 60% of Volume ...
|
|
X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_60,
|
|
//
|
|
// Partial Close Max In Drawdown Position by 70% of Volume ...
|
|
X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_70,
|
|
//
|
|
// Partial Close Max In Drawdown Position by 75% of Volume ...
|
|
X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_75,
|
|
};
|
|
|
|
//
|
|
// Guard Reason ...
|
|
enum ENUM_X_GUARD_REASONS
|
|
{
|
|
//
|
|
// Critical ...
|
|
X_GUARDED_CRITICAL,
|
|
//
|
|
// Max ...
|
|
X_GUARDED_MAX,
|
|
//
|
|
// Old ...
|
|
X_GUARDED_OLD
|
|
};
|
|
|
|
//
|
|
// All Suuported Support Positions Guard ...
|
|
enum ENUM_X_GUARD_SUPPORT_METHODS
|
|
{
|
|
//
|
|
// Ignore Supporting ...
|
|
X_SUPPORT_DO_NOTHING,
|
|
//
|
|
// Full Both Side Support ...
|
|
X_SUPPORT_FULL,
|
|
//
|
|
// Indirectional Supports On Drawing Down ...
|
|
X_SUPPORT_IN_DRAWDOWN_INDIRECTIONAL,
|
|
//
|
|
// Directional Supports On Drawing Up ...
|
|
X_SUPPORT_IN_DRAWUP_DIRECTIONAL,
|
|
};
|
|
|
|
//
|
|
// END Model Definitions ...
|
|
//
|
|
|
|
//
|
|
// START Overrides ...
|
|
//
|
|
|
|
//
|
|
// XBase Class ...
|
|
class XCBase
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
void AddItemToBuffer(
|
|
double item, // Specified Item
|
|
double &buffer[] // Specified Buffer
|
|
)
|
|
{
|
|
//
|
|
int bufferSize = ArraySize(buffer);
|
|
|
|
//
|
|
ArrayResize(
|
|
buffer,
|
|
bufferSize + 1);
|
|
|
|
//
|
|
buffer[bufferSize] = item;
|
|
}
|
|
void AddItemToBuffer(
|
|
datetime item, // Specified Item
|
|
datetime &buffer[] // Specified Buffer
|
|
)
|
|
{
|
|
//
|
|
int bufferSize = ArraySize(buffer);
|
|
|
|
//
|
|
ArrayResize(
|
|
buffer,
|
|
bufferSize + 1);
|
|
|
|
//
|
|
buffer[bufferSize] = item;
|
|
}
|
|
void AddItemToBuffer(
|
|
XSignal &item,
|
|
XSignal &buffer[])
|
|
{
|
|
//
|
|
ArrayResize(
|
|
buffer,
|
|
ArraySize(buffer) + 1);
|
|
|
|
//
|
|
buffer[ArraySize(buffer) - 1] = item;
|
|
}
|
|
void AddItemToBuffer(
|
|
XCollector &item, // Specified Item
|
|
XCollector &buffer[] // Specified Buffer
|
|
)
|
|
{
|
|
//
|
|
int bufferSize = ArraySize(buffer);
|
|
|
|
//
|
|
ArrayResize(
|
|
buffer,
|
|
bufferSize + 1);
|
|
|
|
//
|
|
buffer[bufferSize] = item;
|
|
}
|
|
void AddItemToBuffer(
|
|
XSignallerInfo &item,
|
|
XSignallerInfo &buffer[])
|
|
{
|
|
//
|
|
ArrayResize(
|
|
buffer,
|
|
ArraySize(buffer) + 1);
|
|
|
|
//
|
|
buffer[ArraySize(buffer) - 1] = item;
|
|
}
|
|
void AddItemToBuffer(
|
|
XProtectedSignal &item, // Item ...
|
|
XProtectedSignal &buffer[] // Buffer ...
|
|
)
|
|
{
|
|
//
|
|
int bufferSize = ArraySize(buffer);
|
|
|
|
//
|
|
ArrayResize(
|
|
buffer,
|
|
bufferSize + 1);
|
|
buffer[bufferSize] = item;
|
|
}
|
|
|
|
//
|
|
void CleanBuffer(double &buffer[])
|
|
{
|
|
//
|
|
ArrayFree(buffer);
|
|
ArrayResize(buffer, 0);
|
|
}
|
|
void CleanBuffer(XDeal &buffer[])
|
|
{
|
|
//
|
|
ArrayFree(buffer);
|
|
ArrayResize(buffer, 0);
|
|
}
|
|
void CleanBuffer(XOrder &buffer[])
|
|
{
|
|
//
|
|
ArrayFree(buffer);
|
|
ArrayResize(buffer, 0);
|
|
}
|
|
void CleanBuffer(XSignal &buffer[])
|
|
{
|
|
//
|
|
ArrayFree(buffer);
|
|
ArrayResize(buffer, 0);
|
|
}
|
|
void CleanBuffer(XCollector &buffer[])
|
|
{
|
|
//
|
|
ArrayFree(buffer);
|
|
ArrayResize(buffer, 0);
|
|
}
|
|
void CleanBuffer(XSignallerInfo &buffer[])
|
|
{
|
|
//
|
|
ArrayFree(buffer);
|
|
ArrayResize(buffer, 0);
|
|
}
|
|
void CleanBuffer(XProtectedSignal &buffer[])
|
|
{
|
|
//
|
|
ArrayFree(buffer);
|
|
ArrayResize(buffer, 0);
|
|
}
|
|
|
|
//
|
|
void CopyBuffer(
|
|
const XSignallerInfo &source[], // Source Buffer ...
|
|
XSignallerInfo &dest[] // Dest Buffer ...
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(dest);
|
|
|
|
//
|
|
int sourceCount = ArraySize(source);
|
|
if (sourceCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Loop ...
|
|
for (int i = 0; i < sourceCount; i++)
|
|
{
|
|
//
|
|
XSignallerInfo iInfo = source[i];
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
iInfo,
|
|
dest);
|
|
}
|
|
}
|
|
void CopyBuffer(
|
|
XProtectedSignal &source[], // Source Buffer to Copy
|
|
XProtectedSignal &dest[] // Fill this buffer using source
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(dest);
|
|
|
|
//
|
|
int sourceCount = ArraySize(source);
|
|
if (sourceCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < sourceCount; i++)
|
|
{
|
|
//
|
|
XProtectedSignal iItem = source[i];
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
iItem,
|
|
dest);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Private ...
|
|
private:
|
|
};
|
|
|
|
//
|
|
// XCTrade Base Class ...
|
|
class XSCTrade : public CTrade
|
|
{
|
|
public:
|
|
//
|
|
// Modify Specific Position, by Specific Comment ...
|
|
bool PositionModify(
|
|
const string symbol, // Symbol
|
|
const double sl, // Stop Loss
|
|
const double tp, // Take Profit
|
|
const string comment = "" // Comment For Modify
|
|
)
|
|
{
|
|
//
|
|
// check stopped
|
|
if (IsStopped(__FUNCTION__))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check position existence
|
|
if (!SelectPosition(symbol))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// clean
|
|
ClearStructures();
|
|
|
|
//
|
|
// setting request
|
|
m_request.action = TRADE_ACTION_SLTP;
|
|
m_request.symbol = symbol;
|
|
m_request.magic = m_magic;
|
|
m_request.sl = sl;
|
|
m_request.tp = tp;
|
|
m_request.position = PositionGetInteger(POSITION_TICKET);
|
|
|
|
//
|
|
// Specified Comment ...
|
|
if (StringLen(comment) > 0)
|
|
{
|
|
m_request.comment = comment;
|
|
}
|
|
|
|
//
|
|
// action and return the result
|
|
return (OrderSend(m_request, m_result));
|
|
}
|
|
bool PositionModify(
|
|
const ulong ticket, // Position Ticket
|
|
const double sl, // Stop Loss
|
|
const double tp, // Take Profit
|
|
const string comment = "" // Comment For Modify
|
|
)
|
|
{
|
|
//
|
|
// check stopped
|
|
if (IsStopped(__FUNCTION__))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check position existence
|
|
if (!PositionSelectByTicket(ticket))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// clean
|
|
ClearStructures();
|
|
|
|
//
|
|
// setting request
|
|
m_request.action = TRADE_ACTION_SLTP;
|
|
m_request.position = ticket;
|
|
m_request.symbol = PositionGetString(POSITION_SYMBOL);
|
|
m_request.magic = m_magic;
|
|
m_request.sl = sl;
|
|
m_request.tp = tp;
|
|
|
|
//
|
|
// Specified Comment ...
|
|
if (StringLen(comment) > 0)
|
|
{
|
|
m_request.comment = comment;
|
|
}
|
|
|
|
//
|
|
// action and return the result
|
|
return (OrderSend(m_request, m_result));
|
|
}
|
|
|
|
//
|
|
// Close Specific Position, by Specific Comment ...
|
|
bool PositionClose(
|
|
const string symbol, // Symbol
|
|
const ulong deviation, // Deviation
|
|
const string comment = "" // Comment For Close
|
|
)
|
|
{
|
|
//
|
|
bool partial_close = false;
|
|
int retry_count = 10;
|
|
uint retcode = TRADE_RETCODE_REJECT;
|
|
|
|
//
|
|
// check stopped
|
|
if (IsStopped(__FUNCTION__))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// clean
|
|
ClearStructures();
|
|
|
|
//
|
|
// check filling
|
|
if (!FillingCheck(symbol))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
do
|
|
{
|
|
//
|
|
// check
|
|
if (SelectPosition(symbol))
|
|
{
|
|
//
|
|
if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
{
|
|
//
|
|
// prepare request for close BUY position
|
|
m_request.type = ORDER_TYPE_SELL;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// prepare request for close SELL position
|
|
m_request.type = ORDER_TYPE_BUY;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// position not found
|
|
m_result.retcode = retcode;
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// setting request
|
|
m_request.action = TRADE_ACTION_DEAL;
|
|
m_request.symbol = symbol;
|
|
m_request.volume = PositionGetDouble(POSITION_VOLUME);
|
|
m_request.magic = m_magic;
|
|
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
|
|
m_request.position = PositionGetInteger(POSITION_TICKET);
|
|
|
|
//
|
|
// Specify Comment ...
|
|
m_request.comment = comment;
|
|
|
|
//
|
|
// check volume
|
|
double max_volume = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
|
if (m_request.volume > max_volume)
|
|
{
|
|
//
|
|
m_request.volume = max_volume;
|
|
partial_close = true;
|
|
}
|
|
else
|
|
{
|
|
partial_close = false;
|
|
}
|
|
|
|
//
|
|
// hedging? just send order
|
|
if (IsHedging())
|
|
{
|
|
return (OrderSend(m_request, m_result));
|
|
}
|
|
|
|
//
|
|
// order send
|
|
if (!OrderSend(m_request, m_result))
|
|
{
|
|
//
|
|
if (--retry_count != 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (retcode == TRADE_RETCODE_DONE_PARTIAL)
|
|
{
|
|
m_result.retcode = retcode;
|
|
}
|
|
|
|
//
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
//--- WARNING. If position volume exceeds the maximum volume allowed for deal,
|
|
//--- and when the asynchronous trade mode is on, for safety reasons, position is closed not completely,
|
|
//--- but partially. It is decreased by the maximum volume allowed for deal.
|
|
if (m_async_mode)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
retcode = TRADE_RETCODE_DONE_PARTIAL;
|
|
if (partial_close)
|
|
{
|
|
Sleep(1000);
|
|
}
|
|
} while (partial_close);
|
|
|
|
//
|
|
// succeed
|
|
return (true);
|
|
}
|
|
bool PositionClose(
|
|
const ulong ticket, // Position Ticket
|
|
const ulong deviation, // Deviation
|
|
const string comment = "" // Comment For Close
|
|
)
|
|
{
|
|
//
|
|
// check stopped
|
|
if (IsStopped(__FUNCTION__))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check position existence
|
|
if (!PositionSelectByTicket(ticket))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
string symbol = PositionGetString(POSITION_SYMBOL);
|
|
|
|
//
|
|
// clean
|
|
ClearStructures();
|
|
|
|
//
|
|
// check filling
|
|
if (!FillingCheck(symbol))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check
|
|
if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
{
|
|
//
|
|
// prepare request for close BUY position
|
|
m_request.type = ORDER_TYPE_SELL;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// prepare request for close SELL position
|
|
m_request.type = ORDER_TYPE_BUY;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
|
}
|
|
|
|
//
|
|
// setting request
|
|
m_request.action = TRADE_ACTION_DEAL;
|
|
m_request.position = ticket;
|
|
m_request.symbol = symbol;
|
|
m_request.volume = PositionGetDouble(POSITION_VOLUME);
|
|
m_request.magic = m_magic;
|
|
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
|
|
|
|
//
|
|
// Specify Comment ...
|
|
m_request.comment = comment;
|
|
|
|
//
|
|
// close position
|
|
return (OrderSend(m_request, m_result));
|
|
}
|
|
|
|
//
|
|
// Partial Close Specific Position, by Specific Comment ...
|
|
bool PositionClosePartial(
|
|
const string symbol, // Symbol
|
|
const double volume, // Partial Closing Volume
|
|
const ulong deviation, // Deviation
|
|
const string comment = "" // Comment For Close
|
|
)
|
|
{
|
|
//
|
|
uint retcode = TRADE_RETCODE_REJECT;
|
|
|
|
//
|
|
// check stopped
|
|
if (IsStopped(__FUNCTION__))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// for hedging mode only
|
|
if (!IsHedging())
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// clean
|
|
ClearStructures();
|
|
|
|
//
|
|
// check filling
|
|
if (!FillingCheck(symbol))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check
|
|
if (SelectPosition(symbol))
|
|
{
|
|
//
|
|
if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
{
|
|
//
|
|
// prepare request for close BUY position
|
|
m_request.type = ORDER_TYPE_SELL;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// prepare request for close SELL position
|
|
m_request.type = ORDER_TYPE_BUY;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// position not found
|
|
m_result.retcode = retcode;
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check volume
|
|
double position_volume = PositionGetDouble(POSITION_VOLUME);
|
|
if (position_volume > volume)
|
|
{
|
|
position_volume = volume;
|
|
}
|
|
|
|
//
|
|
// setting request
|
|
m_request.action = TRADE_ACTION_DEAL;
|
|
m_request.symbol = symbol;
|
|
m_request.volume = position_volume;
|
|
m_request.magic = m_magic;
|
|
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
|
|
m_request.position = PositionGetInteger(POSITION_TICKET);
|
|
|
|
//
|
|
// Specifieng Comment ...
|
|
m_request.comment = comment;
|
|
|
|
//
|
|
// hedging? just send order
|
|
return (OrderSend(m_request, m_result));
|
|
}
|
|
bool PositionClosePartial(
|
|
const ulong ticket, // Position Ticket
|
|
const double volume, // Partial Closing Volume
|
|
const ulong deviation, // Deviation
|
|
const string comment = "" // Comment For Close
|
|
)
|
|
{
|
|
//
|
|
// check stopped
|
|
if (IsStopped(__FUNCTION__))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// for hedging mode only
|
|
if (!IsHedging())
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check position existence
|
|
if (!PositionSelectByTicket(ticket))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
string symbol = PositionGetString(POSITION_SYMBOL);
|
|
|
|
//
|
|
// clean
|
|
ClearStructures();
|
|
|
|
//
|
|
// check filling
|
|
if (!FillingCheck(symbol))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check
|
|
if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
{
|
|
//
|
|
// prepare request for close BUY position
|
|
m_request.type = ORDER_TYPE_SELL;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// prepare request for close SELL position
|
|
m_request.type = ORDER_TYPE_BUY;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
|
}
|
|
|
|
//
|
|
// check volume
|
|
double position_volume = PositionGetDouble(POSITION_VOLUME);
|
|
if (position_volume > volume)
|
|
{
|
|
position_volume = volume;
|
|
}
|
|
|
|
//
|
|
// setting request
|
|
m_request.action = TRADE_ACTION_DEAL;
|
|
m_request.position = ticket;
|
|
m_request.symbol = symbol;
|
|
m_request.volume = position_volume;
|
|
m_request.magic = m_magic;
|
|
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
|
|
|
|
//
|
|
// Specifieng Comment ...
|
|
m_request.comment = comment;
|
|
|
|
//
|
|
// close position
|
|
return (OrderSend(m_request, m_result));
|
|
}
|
|
};
|
|
//
|
|
// END Overrides ...
|
|
//
|
|
|
|
//
|
|
// START Global Definitions: Variables, Properties and etc ...
|
|
//
|
|
|
|
//
|
|
// XCMD5 a library for Hashing ...
|
|
class XCMD5 : public XCBase
|
|
{
|
|
//
|
|
// Public Provides ...
|
|
public:
|
|
//
|
|
// Protected Provides ...
|
|
|
|
//
|
|
// Constructor ...
|
|
XCMD5(void) {}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
~XCMD5(void) {}
|
|
|
|
//
|
|
// Hash Specified Char Array ...
|
|
string Hash(
|
|
uchar &source[], // Specify Char Array to Hash
|
|
int length = 0 // Specify Length of Char Array which required to hash
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
int sourceCount = ArraySize(source);
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
sourceCount <= 0 ||
|
|
(sourceCount > 0 && length > sourceCount))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Normalize Args ...
|
|
if (length == 0)
|
|
{
|
|
length = sourceCount;
|
|
}
|
|
|
|
//
|
|
// Init MD5 ...
|
|
MD5Init();
|
|
|
|
//
|
|
// Update Buffer ...
|
|
MD5Update(source, length);
|
|
|
|
//
|
|
// Calculate Result ...
|
|
result = MD5Final();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Hash Specified String ...
|
|
string Hash(
|
|
string source // Specified String
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Converts String to Char Array ...
|
|
uchar bytes[];
|
|
StringToCharArray(
|
|
source,
|
|
bytes,
|
|
0,
|
|
StringLen(source));
|
|
|
|
//
|
|
result = Hash(
|
|
bytes,
|
|
ArraySize(bytes));
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
protected:
|
|
//
|
|
// Private Provides ...
|
|
private:
|
|
//
|
|
uint m_lMD5[4];
|
|
uint m_nCount[2];
|
|
uchar m_lpszBuffer[64];
|
|
|
|
//
|
|
// Convert Byte to DWord ...
|
|
void ByteToDWord(int &out[], uint &in[], uint len)
|
|
{
|
|
//
|
|
uint i = 0;
|
|
uint j = 0;
|
|
|
|
//
|
|
for (; j < len; i++, j += 4)
|
|
{
|
|
out[i] = (int)in[j] | (int)in[j + 1] << 8 | (int)in[j + 2] << 16 | (int)in[j + 3] << 24;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Convert DWord to Byte ...
|
|
void DWordToByte(uchar &out[], int &in[], uint len)
|
|
{
|
|
//
|
|
uint i = 0;
|
|
uint j = 0;
|
|
|
|
//
|
|
for (; j < len; i++, j += 4)
|
|
{
|
|
//
|
|
out[j] = (uchar)(in[i] & 0xff);
|
|
out[j + 1] = (uchar)((in[i] >> 8) & 0xff);
|
|
out[j + 2] = (uchar)((in[i] >> 16) & 0xff);
|
|
out[j + 3] = (uchar)((in[i] >> 24) & 0xff);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Init MD5 Array ...
|
|
void MD5Init()
|
|
{
|
|
//
|
|
ArrayInitialize(m_lpszBuffer, 64);
|
|
|
|
//
|
|
m_nCount[0] = m_nCount[1] = 0;
|
|
m_lMD5[0] = _md5_INIT_STATE_0;
|
|
m_lMD5[1] = _md5_INIT_STATE_1;
|
|
m_lMD5[2] = _md5_INIT_STATE_2;
|
|
m_lMD5[3] = _md5_INIT_STATE_3;
|
|
}
|
|
|
|
//
|
|
// Update MD5 ...
|
|
void MD5Update(uchar &inBuf[], uint inLen)
|
|
{
|
|
//
|
|
int i, ii;
|
|
int mdi;
|
|
|
|
//
|
|
uint in[16];
|
|
int i0 = 0;
|
|
|
|
//
|
|
mdi = (int)((m_nCount[0] >> 3) & 0x3F);
|
|
|
|
//
|
|
if ((m_nCount[0] + ((uint)inLen << 3)) < m_nCount[0])
|
|
{
|
|
m_nCount[1]++;
|
|
}
|
|
|
|
//
|
|
m_nCount[0] += ((uint)inLen << 3);
|
|
m_nCount[1] += ((uint)inLen >> 29);
|
|
|
|
//
|
|
while ((inLen--) > 0)
|
|
{
|
|
//
|
|
m_lpszBuffer[mdi++] = inBuf[i0++];
|
|
if (mdi == 0x40)
|
|
{
|
|
//
|
|
for (i = 0, ii = 0; i < 16; i++, ii += 4)
|
|
{
|
|
in[i] = (((uint)m_lpszBuffer[ii + 3]) << 24) | (((uint)m_lpszBuffer[ii + 2]) << 16) | (((uint)m_lpszBuffer[ii + 1]) << 8) | ((uint)m_lpszBuffer[ii]);
|
|
}
|
|
|
|
//
|
|
Transform(m_lMD5, in);
|
|
|
|
//
|
|
mdi = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finalize an MD5 Expression ...
|
|
string MD5Final()
|
|
{
|
|
//
|
|
uchar bits[8];
|
|
int nIndex;
|
|
uint nPadLen;
|
|
const int nMD5Size = 16;
|
|
uchar lpszMD5[16];
|
|
string temp;
|
|
string out = "";
|
|
int i;
|
|
|
|
//
|
|
DWordToByte(bits, m_nCount, 8);
|
|
nIndex = (int)((m_nCount[0] >> 3) & 0x3f);
|
|
nPadLen = (nIndex < 56) ? (56 - nIndex) : (120 - nIndex);
|
|
MD5Update(_md5_PADDING, nPadLen);
|
|
MD5Update(bits, 8);
|
|
DWordToByte(lpszMD5, m_lMD5, nMD5Size);
|
|
|
|
//
|
|
for (i = 0; i < nMD5Size; i++)
|
|
{
|
|
//
|
|
if (lpszMD5[i] == 0)
|
|
{
|
|
temp = "00";
|
|
}
|
|
else if (lpszMD5[i] <= 15)
|
|
{
|
|
temp = StringFormat("0%x", lpszMD5[i]);
|
|
}
|
|
else
|
|
{
|
|
temp = StringFormat("%x", lpszMD5[i]);
|
|
}
|
|
|
|
//
|
|
out += temp;
|
|
}
|
|
|
|
//
|
|
lpszMD5[0] = '\0';
|
|
|
|
//
|
|
return (out);
|
|
}
|
|
|
|
//
|
|
// Transform Buffers ...
|
|
void Transform(uint &buf[], uint &in[])
|
|
{
|
|
//
|
|
uint a = buf[0], b = buf[1], c = buf[2], d = buf[3];
|
|
|
|
//
|
|
_md5_FF(a, b, c, d, in[0], _md5_S11, 0xD76AA478);
|
|
_md5_FF(d, a, b, c, in[1], _md5_S12, 0xE8C7B756);
|
|
_md5_FF(c, d, a, b, in[2], _md5_S13, 0x242070DB);
|
|
_md5_FF(b, c, d, a, in[3], _md5_S14, 0xC1BDCEEE);
|
|
_md5_FF(a, b, c, d, in[4], _md5_S11, 0xF57C0FAF);
|
|
_md5_FF(d, a, b, c, in[5], _md5_S12, 0x4787C62A);
|
|
_md5_FF(c, d, a, b, in[6], _md5_S13, 0xA8304613);
|
|
_md5_FF(b, c, d, a, in[7], _md5_S14, 0xFD469501);
|
|
_md5_FF(a, b, c, d, in[8], _md5_S11, 0x698098D8);
|
|
_md5_FF(d, a, b, c, in[9], _md5_S12, 0x8B44F7AF);
|
|
_md5_FF(c, d, a, b, in[10], _md5_S13, 0xFFFF5BB1);
|
|
_md5_FF(b, c, d, a, in[11], _md5_S14, 0x895CD7BE);
|
|
_md5_FF(a, b, c, d, in[12], _md5_S11, 0x6B901122);
|
|
_md5_FF(d, a, b, c, in[13], _md5_S12, 0xFD987193);
|
|
_md5_FF(c, d, a, b, in[14], _md5_S13, 0xA679438E);
|
|
_md5_FF(b, c, d, a, in[15], _md5_S14, 0x49B40821);
|
|
|
|
//
|
|
_md5_GG(a, b, c, d, in[1], _md5_S21, 0xF61E2562);
|
|
_md5_GG(d, a, b, c, in[6], _md5_S22, 0xC040B340);
|
|
_md5_GG(c, d, a, b, in[11], _md5_S23, 0x265E5A51);
|
|
_md5_GG(b, c, d, a, in[0], _md5_S24, 0xE9B6C7AA);
|
|
_md5_GG(a, b, c, d, in[5], _md5_S21, 0xD62F105D);
|
|
_md5_GG(d, a, b, c, in[10], _md5_S22, 0x02441453);
|
|
_md5_GG(c, d, a, b, in[15], _md5_S23, 0xD8A1E681);
|
|
_md5_GG(b, c, d, a, in[4], _md5_S24, 0xE7D3FBC8);
|
|
_md5_GG(a, b, c, d, in[9], _md5_S21, 0x21E1CDE6);
|
|
_md5_GG(d, a, b, c, in[14], _md5_S22, 0xC33707D6);
|
|
_md5_GG(c, d, a, b, in[3], _md5_S23, 0xF4D50D87);
|
|
_md5_GG(b, c, d, a, in[8], _md5_S24, 0x455A14ED);
|
|
_md5_GG(a, b, c, d, in[13], _md5_S21, 0xA9E3E905);
|
|
_md5_GG(d, a, b, c, in[2], _md5_S22, 0xFCEFA3F8);
|
|
_md5_GG(c, d, a, b, in[7], _md5_S23, 0x676F02D9);
|
|
_md5_GG(b, c, d, a, in[12], _md5_S24, 0x8D2A4C8A);
|
|
|
|
//
|
|
_md5_HH(a, b, c, d, in[5], _md5_S31, 0xFFFA3942);
|
|
_md5_HH(d, a, b, c, in[8], _md5_S32, 0x8771F681);
|
|
_md5_HH(c, d, a, b, in[11], _md5_S33, 0x6D9D6122);
|
|
_md5_HH(b, c, d, a, in[14], _md5_S34, 0xFDE5380C);
|
|
_md5_HH(a, b, c, d, in[1], _md5_S31, 0xA4BEEA44);
|
|
_md5_HH(d, a, b, c, in[4], _md5_S32, 0x4BDECFA9);
|
|
_md5_HH(c, d, a, b, in[7], _md5_S33, 0xF6BB4B60);
|
|
_md5_HH(b, c, d, a, in[10], _md5_S34, 0xBEBFBC70);
|
|
_md5_HH(a, b, c, d, in[13], _md5_S31, 0x289B7EC6);
|
|
_md5_HH(d, a, b, c, in[0], _md5_S32, 0xEAA127FA);
|
|
_md5_HH(c, d, a, b, in[3], _md5_S33, 0xD4EF3085);
|
|
_md5_HH(b, c, d, a, in[6], _md5_S34, 0x04881D05);
|
|
_md5_HH(a, b, c, d, in[9], _md5_S31, 0xD9D4D039);
|
|
_md5_HH(d, a, b, c, in[12], _md5_S32, 0xE6DB99E5);
|
|
_md5_HH(c, d, a, b, in[15], _md5_S33, 0x1FA27CF8);
|
|
_md5_HH(b, c, d, a, in[2], _md5_S34, 0xC4AC5665);
|
|
|
|
//
|
|
_md5_II(a, b, c, d, in[0], _md5_S41, 0xF4292244);
|
|
_md5_II(d, a, b, c, in[7], _md5_S42, 0x432AFF97);
|
|
_md5_II(c, d, a, b, in[14], _md5_S43, 0xAB9423A7);
|
|
_md5_II(b, c, d, a, in[5], _md5_S44, 0xFC93A039);
|
|
_md5_II(a, b, c, d, in[12], _md5_S41, 0x655B59C3);
|
|
_md5_II(d, a, b, c, in[3], _md5_S42, 0x8F0CCC92);
|
|
_md5_II(c, d, a, b, in[10], _md5_S43, 0xFFEFF47D);
|
|
_md5_II(b, c, d, a, in[1], _md5_S44, 0x85845DD1);
|
|
_md5_II(a, b, c, d, in[8], _md5_S41, 0x6FA87E4F);
|
|
_md5_II(d, a, b, c, in[15], _md5_S42, 0xFE2CE6E0);
|
|
_md5_II(c, d, a, b, in[6], _md5_S43, 0xA3014314);
|
|
_md5_II(b, c, d, a, in[13], _md5_S44, 0x4E0811A1);
|
|
_md5_II(a, b, c, d, in[4], _md5_S41, 0xF7537E82);
|
|
_md5_II(d, a, b, c, in[11], _md5_S42, 0xBD3AF235);
|
|
_md5_II(c, d, a, b, in[2], _md5_S43, 0x2AD7D2BB);
|
|
_md5_II(b, c, d, a, in[9], _md5_S44, 0xEB86D391);
|
|
|
|
//
|
|
buf[0] += a;
|
|
buf[1] += b;
|
|
buf[2] += c;
|
|
buf[3] += d;
|
|
}
|
|
};
|
|
|
|
//
|
|
// XSaherElm Account Info Class ...
|
|
class XCAccountInfo : public XCBase
|
|
{
|
|
//
|
|
// Public properties ...
|
|
public:
|
|
//
|
|
// Constructro ...
|
|
void XCAccountInfo()
|
|
{
|
|
//
|
|
mAccountInfo = new CAccountInfo();
|
|
|
|
//
|
|
mInitialBalance = mAccountInfo.Balance();
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
void ~XCAccountInfo()
|
|
{
|
|
//
|
|
mInitialBalance = 0;
|
|
}
|
|
|
|
//
|
|
// User Account ...
|
|
long GetUserAccount()
|
|
{
|
|
return mAccountInfo.Login();
|
|
}
|
|
|
|
//
|
|
// Account Leverage ...
|
|
long GetLeverage()
|
|
{
|
|
return mAccountInfo.Leverage();
|
|
}
|
|
|
|
//
|
|
// Get Trade Expert State ...
|
|
bool CanExpertTrade()
|
|
{
|
|
return mAccountInfo.TradeExpert();
|
|
}
|
|
|
|
//
|
|
// User Account Trade Mopde (ENUM_ACCOUNT_TRADE_MODE) ...
|
|
// --------------------------
|
|
// ACCOUNT_TRADE_MODE_DEMO
|
|
// ACCOUNT_TRADE_MODE_CONTEST
|
|
// ACCOUNT_TRADE_MODE_REAL
|
|
ENUM_ACCOUNT_TRADE_MODE GetTradeMode()
|
|
{
|
|
return mAccountInfo.TradeMode();
|
|
}
|
|
|
|
//
|
|
// Get Account Balance ...
|
|
double GetBalance()
|
|
{
|
|
return mAccountInfo.Balance();
|
|
}
|
|
|
|
//
|
|
// Get Initial Deposit Balance ...
|
|
double GetInitialBalance()
|
|
{
|
|
return mInitialBalance;
|
|
}
|
|
|
|
//
|
|
// Get the amount of give Credit ...
|
|
double GetCredit()
|
|
{
|
|
return mAccountInfo.Credit();
|
|
}
|
|
|
|
//
|
|
// Get the amount of current Profit on account ...
|
|
double GetProfit()
|
|
{
|
|
return mAccountInfo.Profit();
|
|
}
|
|
|
|
//
|
|
// Get the amount of current Equity on account ...
|
|
double GetEquity()
|
|
{
|
|
return mAccountInfo.Equity();
|
|
}
|
|
|
|
//
|
|
// Get the amount of reserved Margin ...
|
|
double GetMargin()
|
|
{
|
|
return mAccountInfo.Margin();
|
|
}
|
|
|
|
//
|
|
// Get the amount of free Margin ...
|
|
double GetFreeMargin()
|
|
{
|
|
return mAccountInfo.FreeMargin();
|
|
}
|
|
|
|
//
|
|
// Get the Level of Margin ...
|
|
double GetMarginLevel()
|
|
{
|
|
return mAccountInfo.MarginLevel();
|
|
}
|
|
|
|
//
|
|
// Get the Level Of Margin for a Deposit ...
|
|
double GetMarginCall()
|
|
{
|
|
return mAccountInfo.MarginCall();
|
|
}
|
|
|
|
//
|
|
// Get the Level of Margin for Stop out ...
|
|
double GetMarginStopOut()
|
|
{
|
|
return mAccountInfo.MarginStopOut();
|
|
}
|
|
|
|
//
|
|
// Get the Client Name ...
|
|
string GetName()
|
|
{
|
|
return mAccountInfo.Name();
|
|
}
|
|
|
|
//
|
|
// Get the Trade Server Name ...
|
|
string GetServerName()
|
|
{
|
|
return mAccountInfo.Server();
|
|
}
|
|
|
|
//
|
|
// Get deposit Currency Name ...
|
|
string GetCurrency()
|
|
{
|
|
return mAccountInfo.Currency();
|
|
}
|
|
|
|
//
|
|
// Get the Company Name that serves an Account ...
|
|
string GetCompany()
|
|
{
|
|
return mAccountInfo.Company();
|
|
}
|
|
|
|
//
|
|
// Calculate Profits for the current account based on passed parameters ...
|
|
double CalculateTradeProfit(
|
|
const string symbol, // trading symbol
|
|
ENUM_ORDER_TYPE type, // order type
|
|
double volume, // volume
|
|
double entry, // open price
|
|
double exit // close price
|
|
)
|
|
{
|
|
//
|
|
double result = mAccountInfo.OrderProfitCheck(
|
|
symbol,
|
|
type,
|
|
volume,
|
|
entry,
|
|
exit);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate amount of margin which required for trade operation ...
|
|
double CalculateMarging(
|
|
const string symbol, // trading symbol
|
|
ENUM_ORDER_TYPE type, // order type
|
|
double volume, // volume
|
|
double entry // open price
|
|
)
|
|
{
|
|
//
|
|
double result = mAccountInfo.MarginCheck(
|
|
symbol,
|
|
type,
|
|
volume,
|
|
entry);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate amount of free margin left after trade operation ...
|
|
double CalculateFreeMarging(
|
|
const string symbol, // trading symbol
|
|
ENUM_ORDER_TYPE type, // order type
|
|
double volume, // volume
|
|
double entry // open price
|
|
)
|
|
{
|
|
//
|
|
double result = mAccountInfo.FreeMarginCheck(
|
|
symbol,
|
|
type,
|
|
volume,
|
|
entry);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate the Maximum possible volume of trade operation ...
|
|
double CalculateMaxVolume(
|
|
const string symbol, // trading symbol
|
|
ENUM_ORDER_TYPE type, // order type
|
|
double entry, // open price
|
|
double percent = 100 // percent of available margin
|
|
)
|
|
{
|
|
//
|
|
double result = mAccountInfo.MaxLotCheck(
|
|
symbol,
|
|
type,
|
|
entry,
|
|
percent);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Protected properties ...
|
|
protected:
|
|
//
|
|
// Private properties ...
|
|
private:
|
|
//
|
|
// Initial Account Balance ...
|
|
double mInitialBalance;
|
|
|
|
//
|
|
// Account Info ...
|
|
CAccountInfo mAccountInfo;
|
|
};
|
|
|
|
//
|
|
// XSaherElm Report Info Class ...
|
|
class XCReporter : public XCBase
|
|
{
|
|
//
|
|
// Public Provided ...
|
|
public:
|
|
//
|
|
// Constructro ...
|
|
void XCReporter()
|
|
{
|
|
//
|
|
mAccountInfo = new XCAccountInfo();
|
|
|
|
//
|
|
Reset();
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
void ~XCReporter()
|
|
{
|
|
}
|
|
|
|
//
|
|
// Reset Specific Report ...
|
|
void Reset(
|
|
ENUM_XREPORT_TYPES type // Specify Report Type
|
|
)
|
|
{
|
|
CleanReport(type);
|
|
}
|
|
|
|
//
|
|
// Reset All Reports ...
|
|
void Reset()
|
|
{
|
|
//
|
|
CleanReport(XREPORT_MAIN);
|
|
CleanReport(XREPORT_MONTHLY);
|
|
CleanReport(XREPORT_WEEKLY);
|
|
CleanReport(XREPORT_DAILY);
|
|
}
|
|
|
|
//
|
|
// Update Volume Info ...
|
|
void UpdateReportVolumeInfo(
|
|
ENUM_XREPORT_TYPES type, // Specified Report Type
|
|
//
|
|
double staticVolume, // Current Available Static Volume
|
|
string staticVolumeCalculationMethod, // Static Volume Calculation Method
|
|
double maxAllowedVolume, // Current Maximum Appliable Volume
|
|
double volumeIncreaseMultiplier, // Static Volume Increase Multiplier
|
|
double balanceGowingToApplyMultiplier, // Multiply Volume Based On Balance Growing
|
|
double balanceGrowingToIncreaseMaxAllowedVolume // Where to Increase MaxAllowed Volume
|
|
)
|
|
{
|
|
//
|
|
// Filling Account Info ...
|
|
FillReportAccountInfo(type);
|
|
|
|
//
|
|
switch (type)
|
|
{
|
|
//
|
|
// Main ...
|
|
case XREPORT_MAIN:
|
|
//
|
|
FillingVolumeInfo(
|
|
mMainReport,
|
|
staticVolume,
|
|
staticVolumeCalculationMethod,
|
|
maxAllowedVolume,
|
|
volumeIncreaseMultiplier,
|
|
balanceGowingToApplyMultiplier,
|
|
balanceGrowingToIncreaseMaxAllowedVolume);
|
|
break;
|
|
|
|
//
|
|
// Monthly ...
|
|
case XREPORT_MONTHLY:
|
|
//
|
|
FillingVolumeInfo(
|
|
mMonthlyReport,
|
|
staticVolume,
|
|
staticVolumeCalculationMethod,
|
|
maxAllowedVolume,
|
|
volumeIncreaseMultiplier,
|
|
balanceGowingToApplyMultiplier,
|
|
balanceGrowingToIncreaseMaxAllowedVolume);
|
|
break;
|
|
|
|
//
|
|
// Weekly ...
|
|
case XREPORT_WEEKLY:
|
|
//
|
|
FillingVolumeInfo(
|
|
mWeeklyReport,
|
|
staticVolume,
|
|
staticVolumeCalculationMethod,
|
|
maxAllowedVolume,
|
|
volumeIncreaseMultiplier,
|
|
balanceGowingToApplyMultiplier,
|
|
balanceGrowingToIncreaseMaxAllowedVolume);
|
|
break;
|
|
|
|
//
|
|
// Daily ...
|
|
case XREPORT_DAILY:
|
|
//
|
|
FillingVolumeInfo(
|
|
mDailyReport,
|
|
staticVolume,
|
|
staticVolumeCalculationMethod,
|
|
maxAllowedVolume,
|
|
volumeIncreaseMultiplier,
|
|
balanceGowingToApplyMultiplier,
|
|
balanceGrowingToIncreaseMaxAllowedVolume);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update Volume Info on All Reports ...
|
|
void UpdateReportsVolumeInfo(
|
|
double staticVolume, // Current Available Static Volume
|
|
string staticVolumeCalculationMethod, // Static Volume Calculation Method
|
|
double maxAllowedVolume, // Current Maximum Appliable Volume
|
|
double volumeIncreaseMultiplier, // Static Volume Increase Multiplier
|
|
double balanceGowingToApplyMultiplier, // Multiply Volume Based On Balance Growing
|
|
double balanceGrowingToIncreaseMaxAllowedVolume // Where to Increase MaxAllowed Volume
|
|
)
|
|
{
|
|
//
|
|
// Main ...
|
|
UpdateReportVolumeInfo(
|
|
XREPORT_MAIN,
|
|
staticVolume,
|
|
staticVolumeCalculationMethod,
|
|
maxAllowedVolume,
|
|
volumeIncreaseMultiplier,
|
|
balanceGowingToApplyMultiplier,
|
|
balanceGrowingToIncreaseMaxAllowedVolume);
|
|
|
|
//
|
|
// Monthly ...
|
|
UpdateReportVolumeInfo(
|
|
XREPORT_MONTHLY,
|
|
staticVolume,
|
|
staticVolumeCalculationMethod,
|
|
maxAllowedVolume,
|
|
volumeIncreaseMultiplier,
|
|
balanceGowingToApplyMultiplier,
|
|
balanceGrowingToIncreaseMaxAllowedVolume);
|
|
|
|
//
|
|
// Weekly ...
|
|
UpdateReportVolumeInfo(
|
|
XREPORT_WEEKLY,
|
|
staticVolume,
|
|
staticVolumeCalculationMethod,
|
|
maxAllowedVolume,
|
|
volumeIncreaseMultiplier,
|
|
balanceGowingToApplyMultiplier,
|
|
balanceGrowingToIncreaseMaxAllowedVolume);
|
|
|
|
//
|
|
// Daily ...
|
|
UpdateReportVolumeInfo(
|
|
XREPORT_DAILY,
|
|
staticVolume,
|
|
staticVolumeCalculationMethod,
|
|
maxAllowedVolume,
|
|
volumeIncreaseMultiplier,
|
|
balanceGowingToApplyMultiplier,
|
|
balanceGrowingToIncreaseMaxAllowedVolume);
|
|
}
|
|
|
|
//
|
|
// Add Specific Signal to Report ...
|
|
void AddSignalToReport(
|
|
ENUM_XREPORT_TYPES type, // Specified Report Type
|
|
const XSignal &signal, // Specified Signal
|
|
const string &providers[] // Specified Signal Providers
|
|
)
|
|
{
|
|
//
|
|
//
|
|
// Filling Account Info ...
|
|
FillReportAccountInfo(type);
|
|
|
|
//
|
|
switch (type)
|
|
{
|
|
//
|
|
// Main ...
|
|
case XREPORT_MAIN:
|
|
AddSignalToReport(
|
|
mMainReport,
|
|
signal,
|
|
providers);
|
|
break;
|
|
|
|
//
|
|
// Monthly ...
|
|
case XREPORT_MONTHLY:
|
|
AddSignalToReport(
|
|
mMonthlyReport,
|
|
signal,
|
|
providers);
|
|
break;
|
|
|
|
//
|
|
// Weekly ...
|
|
case XREPORT_WEEKLY:
|
|
AddSignalToReport(
|
|
mWeeklyReport,
|
|
signal,
|
|
providers);
|
|
break;
|
|
|
|
//
|
|
// Daily ...
|
|
case XREPORT_DAILY:
|
|
AddSignalToReport(
|
|
mDailyReport,
|
|
signal,
|
|
providers);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add Specific Signal to All Reports ...
|
|
void AddSignalToReports(
|
|
const XSignal &signal, // Specified Signal
|
|
const string &providers[] // Specified Signal Providers
|
|
)
|
|
{
|
|
//
|
|
// Main ...
|
|
AddSignalToReport(
|
|
XREPORT_MAIN,
|
|
signal,
|
|
providers);
|
|
|
|
//
|
|
// Monthly ...
|
|
AddSignalToReport(
|
|
XREPORT_MONTHLY,
|
|
signal,
|
|
providers);
|
|
|
|
//
|
|
// Weekly ...
|
|
AddSignalToReport(
|
|
XREPORT_WEEKLY,
|
|
signal,
|
|
providers);
|
|
|
|
//
|
|
// Daily ...
|
|
AddSignalToReport(
|
|
XREPORT_DAILY,
|
|
signal,
|
|
providers);
|
|
}
|
|
|
|
//
|
|
// Report a TP for Specific Report ...
|
|
void AddTradeTPInReport(
|
|
ENUM_XREPORT_TYPES type // Specified Report Type
|
|
)
|
|
{
|
|
//
|
|
switch (type)
|
|
{
|
|
//
|
|
// Main ...
|
|
case XREPORT_MAIN:
|
|
AddTradeTPInReport(mMainReport);
|
|
break;
|
|
|
|
//
|
|
// Monthly ...
|
|
case XREPORT_MONTHLY:
|
|
AddTradeTPInReport(mMonthlyReport);
|
|
break;
|
|
|
|
//
|
|
// Weekly ...
|
|
case XREPORT_WEEKLY:
|
|
AddTradeTPInReport(mWeeklyReport);
|
|
break;
|
|
|
|
//
|
|
// Daily ...
|
|
case XREPORT_DAILY:
|
|
AddTradeTPInReport(mDailyReport);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Report a TP for All Reports ...
|
|
void AddTradeTPInReports()
|
|
{
|
|
//
|
|
// Main ...
|
|
AddTradeTPInReport(XREPORT_MAIN);
|
|
|
|
//
|
|
// Monthly ...
|
|
AddTradeTPInReport(XREPORT_MONTHLY);
|
|
|
|
//
|
|
// Weekly ...
|
|
AddTradeTPInReport(XREPORT_WEEKLY);
|
|
|
|
//
|
|
// Daily ...
|
|
AddTradeTPInReport(XREPORT_DAILY);
|
|
}
|
|
|
|
//
|
|
// Report a SL for Specific Report ...
|
|
void AddTradeSLInReport(
|
|
ENUM_XREPORT_TYPES type // Specified Report Type
|
|
)
|
|
{
|
|
//
|
|
switch (type)
|
|
{
|
|
//
|
|
// Main ...
|
|
case XREPORT_MAIN:
|
|
AddTradeSLInReport(mMainReport);
|
|
break;
|
|
|
|
//
|
|
// Monthly ...
|
|
case XREPORT_MONTHLY:
|
|
AddTradeSLInReport(mMonthlyReport);
|
|
break;
|
|
|
|
//
|
|
// Weekly ...
|
|
case XREPORT_WEEKLY:
|
|
AddTradeSLInReport(mWeeklyReport);
|
|
break;
|
|
|
|
//
|
|
// Daily ...
|
|
case XREPORT_DAILY:
|
|
AddTradeSLInReport(mDailyReport);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Report a SL for All Reports ...
|
|
void AddTradeSLInReports()
|
|
{
|
|
//
|
|
// Main ...
|
|
AddTradeSLInReport(XREPORT_MAIN);
|
|
|
|
//
|
|
// Monthly ...
|
|
AddTradeSLInReport(XREPORT_MONTHLY);
|
|
|
|
//
|
|
// Weekly ...
|
|
AddTradeSLInReport(XREPORT_WEEKLY);
|
|
|
|
//
|
|
// Daily ...
|
|
AddTradeSLInReport(XREPORT_DAILY);
|
|
}
|
|
|
|
//
|
|
// Update Number of Same Time Trades in Specific Report ...
|
|
void UpdateMaxSameTimeTradesInReport(
|
|
ENUM_XREPORT_TYPES type, // Specified Report Type
|
|
int value // number of same time trades
|
|
)
|
|
{
|
|
//
|
|
switch (type)
|
|
{
|
|
//
|
|
// Main ...
|
|
case XREPORT_MAIN:
|
|
UpdateMaxSameTimeTradesInReport(
|
|
mMainReport,
|
|
value);
|
|
break;
|
|
|
|
//
|
|
// Monthly ...
|
|
case XREPORT_MONTHLY:
|
|
UpdateMaxSameTimeTradesInReport(
|
|
mMonthlyReport,
|
|
value);
|
|
break;
|
|
|
|
//
|
|
// Weekly ...
|
|
case XREPORT_WEEKLY:
|
|
UpdateMaxSameTimeTradesInReport(
|
|
mWeeklyReport,
|
|
value);
|
|
break;
|
|
|
|
//
|
|
// Daily ...
|
|
case XREPORT_DAILY:
|
|
UpdateMaxSameTimeTradesInReport(
|
|
mDailyReport,
|
|
value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update Number of Same Time Trades in All Report ...
|
|
void UpdateMaxSameTimeTradesInReports(
|
|
int value // number of same time trades
|
|
)
|
|
{
|
|
//
|
|
// Main ...
|
|
UpdateMaxSameTimeTradesInReport(
|
|
XREPORT_MAIN,
|
|
value);
|
|
|
|
//
|
|
// Monthly ...
|
|
UpdateMaxSameTimeTradesInReport(
|
|
XREPORT_MONTHLY,
|
|
value);
|
|
|
|
//
|
|
// Weekly ...
|
|
UpdateMaxSameTimeTradesInReport(
|
|
XREPORT_WEEKLY,
|
|
value);
|
|
|
|
//
|
|
// Daily ...
|
|
UpdateMaxSameTimeTradesInReport(
|
|
XREPORT_DAILY,
|
|
value);
|
|
}
|
|
|
|
//
|
|
// Update Max DrawDown in Specific Report ...
|
|
void UpdateMaxDrawDownInReport(
|
|
ENUM_XREPORT_TYPES type, // Specified Report Type
|
|
double value // draw down value
|
|
)
|
|
{
|
|
//
|
|
switch (type)
|
|
{
|
|
//
|
|
// Main ...
|
|
case XREPORT_MAIN:
|
|
UpdateMaxDrawDownInReport(
|
|
mMainReport,
|
|
value);
|
|
break;
|
|
|
|
//
|
|
// Monthly ...
|
|
case XREPORT_MONTHLY:
|
|
UpdateMaxDrawDownInReport(
|
|
mMonthlyReport,
|
|
value);
|
|
break;
|
|
|
|
//
|
|
// Weekly ...
|
|
case XREPORT_WEEKLY:
|
|
UpdateMaxDrawDownInReport(
|
|
mWeeklyReport,
|
|
value);
|
|
break;
|
|
|
|
//
|
|
// Daily ...
|
|
case XREPORT_DAILY:
|
|
UpdateMaxDrawDownInReport(
|
|
mDailyReport,
|
|
value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update Max DrawDown in All Report ...
|
|
void UpdateMaxDrawDownInReports(
|
|
double value // draw down value
|
|
)
|
|
{
|
|
//
|
|
// Main ...
|
|
UpdateMaxDrawDownInReport(
|
|
XREPORT_MAIN,
|
|
value);
|
|
|
|
//
|
|
// Monthly ...
|
|
UpdateMaxDrawDownInReport(
|
|
XREPORT_MONTHLY,
|
|
value);
|
|
|
|
//
|
|
// Weekly ...
|
|
UpdateMaxDrawDownInReport(
|
|
XREPORT_WEEKLY,
|
|
value);
|
|
|
|
//
|
|
// Daily ...
|
|
UpdateMaxDrawDownInReport(
|
|
XREPORT_DAILY,
|
|
value);
|
|
}
|
|
|
|
//
|
|
// Update Max DrawUp in Specific Report ...
|
|
void UpdateMaxDrawUpInReport(
|
|
ENUM_XREPORT_TYPES type, // Specified Report Type
|
|
double value // draw up value
|
|
)
|
|
{
|
|
//
|
|
switch (type)
|
|
{
|
|
//
|
|
// Main ...
|
|
case XREPORT_MAIN:
|
|
UpdateMaxDrawUpInReport(
|
|
mMainReport,
|
|
value);
|
|
break;
|
|
|
|
//
|
|
// Monthly ...
|
|
case XREPORT_MONTHLY:
|
|
UpdateMaxDrawUpInReport(
|
|
mMonthlyReport,
|
|
value);
|
|
break;
|
|
|
|
//
|
|
// Weekly ...
|
|
case XREPORT_WEEKLY:
|
|
UpdateMaxDrawUpInReport(
|
|
mWeeklyReport,
|
|
value);
|
|
break;
|
|
|
|
//
|
|
// Daily ...
|
|
case XREPORT_DAILY:
|
|
UpdateMaxDrawUpInReport(
|
|
mDailyReport,
|
|
value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update Max DrawUp in All Report ...
|
|
void UpdateMaxDrawUpInReports(
|
|
double value // draw up value
|
|
)
|
|
{
|
|
//
|
|
// Main ...
|
|
UpdateMaxDrawUpInReport(
|
|
XREPORT_MAIN,
|
|
value);
|
|
|
|
//
|
|
// Monthly ...
|
|
UpdateMaxDrawUpInReport(
|
|
XREPORT_MONTHLY,
|
|
value);
|
|
|
|
//
|
|
// Weekly ...
|
|
UpdateMaxDrawUpInReport(
|
|
XREPORT_WEEKLY,
|
|
value);
|
|
|
|
//
|
|
// Daily ...
|
|
UpdateMaxDrawUpInReport(
|
|
XREPORT_DAILY,
|
|
value);
|
|
}
|
|
|
|
//
|
|
// Update Min Balance for Open Trades in Specific Report ...
|
|
void UpdateMinBalanceForOpenTradesInReport(
|
|
ENUM_XREPORT_TYPES type, // Specified Report Type
|
|
double value // min balance for open trades
|
|
)
|
|
{
|
|
//
|
|
switch (type)
|
|
{
|
|
//
|
|
// Main ...
|
|
case XREPORT_MAIN:
|
|
UpdateMinBalanceForOpenTradesInReport(
|
|
mMainReport,
|
|
value);
|
|
break;
|
|
|
|
//
|
|
// Monthly ...
|
|
case XREPORT_MONTHLY:
|
|
UpdateMinBalanceForOpenTradesInReport(
|
|
mMonthlyReport,
|
|
value);
|
|
break;
|
|
|
|
//
|
|
// Weekly ...
|
|
case XREPORT_WEEKLY:
|
|
UpdateMinBalanceForOpenTradesInReport(
|
|
mWeeklyReport,
|
|
value);
|
|
break;
|
|
|
|
//
|
|
// Daily ...
|
|
case XREPORT_DAILY:
|
|
UpdateMinBalanceForOpenTradesInReport(
|
|
mDailyReport,
|
|
value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update Min Balance for Open Trades in All Report ...
|
|
void UpdateMinBalanceForOpenTradesInReports(
|
|
double value // min balance for open trades
|
|
)
|
|
{
|
|
//
|
|
// Main ...
|
|
UpdateMinBalanceForOpenTradesInReport(
|
|
XREPORT_MAIN,
|
|
value);
|
|
|
|
//
|
|
// Monthly ...
|
|
UpdateMinBalanceForOpenTradesInReport(
|
|
XREPORT_MONTHLY,
|
|
value);
|
|
|
|
//
|
|
// Weekly ...
|
|
UpdateMinBalanceForOpenTradesInReport(
|
|
XREPORT_WEEKLY,
|
|
value);
|
|
|
|
//
|
|
// Daily ...
|
|
UpdateMinBalanceForOpenTradesInReport(
|
|
XREPORT_DAILY,
|
|
value);
|
|
}
|
|
|
|
//
|
|
// Gnerate Specific Type Of Report String Representation ...
|
|
string GenerateReportString(
|
|
ENUM_XREPORT_TYPES type, // Specified Report Type
|
|
string separator = "\n" // Separator
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
switch (type)
|
|
{
|
|
//
|
|
// Main ...
|
|
case XREPORT_MAIN:
|
|
result = GenerateReportString(
|
|
mMainReport,
|
|
separator);
|
|
break;
|
|
|
|
//
|
|
// Monthly ...
|
|
case XREPORT_MONTHLY:
|
|
result = GenerateReportString(
|
|
mMonthlyReport,
|
|
separator);
|
|
break;
|
|
|
|
//
|
|
// Weekly ...
|
|
case XREPORT_WEEKLY:
|
|
result = GenerateReportString(
|
|
mWeeklyReport,
|
|
separator);
|
|
break;
|
|
|
|
//
|
|
// Daily ...
|
|
case XREPORT_DAILY:
|
|
result = GenerateReportString(
|
|
mDailyReport,
|
|
separator);
|
|
break;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Protected Provided ...
|
|
protected:
|
|
//
|
|
// Private Provided ...
|
|
private:
|
|
//
|
|
// Account Info Instance ...
|
|
XCAccountInfo mAccountInfo;
|
|
|
|
//
|
|
// Main Report ...
|
|
XReport mMainReport;
|
|
|
|
//
|
|
// Monthly Report ...
|
|
XReport mMonthlyReport;
|
|
|
|
//
|
|
// Weekly Report ...
|
|
XReport mWeeklyReport;
|
|
|
|
//
|
|
// Daily Report ...
|
|
XReport mDailyReport;
|
|
|
|
//
|
|
// Clean Specific Report ...
|
|
void CleanReport(
|
|
XReport &report // Specified Report
|
|
)
|
|
{
|
|
//
|
|
datetime time = TimeCurrent();
|
|
|
|
//
|
|
// Reset Times ...
|
|
report.start = time;
|
|
report.end = -1;
|
|
|
|
//
|
|
// Reset Account Info ...
|
|
FillReportAccountInfo(report);
|
|
|
|
//
|
|
// Reset Summary Info ...
|
|
report.summaryInfo.maxDrawUp = 0;
|
|
report.summaryInfo.maxDrawDown = 0;
|
|
report.summaryInfo.longSignals = 0;
|
|
report.summaryInfo.numberOfTPs = 0;
|
|
report.summaryInfo.numberOfSLs = 0;
|
|
report.summaryInfo.shortSignals = 0;
|
|
report.summaryInfo.maxSignalVolume = 0;
|
|
report.summaryInfo.maxSameTimeSignals = 0;
|
|
|
|
//
|
|
// Reset Volume Info ...
|
|
report.volumeInfo.staticVolume = 0;
|
|
report.volumeInfo.maxAllowedVolume = 0;
|
|
report.volumeInfo.volumeIncreaseMultiplier = 0;
|
|
report.volumeInfo.balanceGowingToApplyMultiplier = 0;
|
|
report.volumeInfo.staticVolumeCalculationMethod = NULL;
|
|
report.volumeInfo.balanceGrowingToIncreaseMaxAllowedVolume = 0;
|
|
|
|
//
|
|
// Reset Signallers Info ...
|
|
CleanBuffer(report.signallersInfo);
|
|
}
|
|
void CleanReport(
|
|
ENUM_XREPORT_TYPES type // Specify Report Type
|
|
)
|
|
{
|
|
//
|
|
switch (type)
|
|
{
|
|
//
|
|
// Main ...
|
|
case XREPORT_MAIN:
|
|
CleanReport(mMainReport);
|
|
break;
|
|
|
|
//
|
|
// Monthly ...
|
|
case XREPORT_MONTHLY:
|
|
CleanReport(mMonthlyReport);
|
|
break;
|
|
|
|
//
|
|
// Weekly ...
|
|
case XREPORT_WEEKLY:
|
|
CleanReport(mWeeklyReport);
|
|
break;
|
|
|
|
//
|
|
// Daily ...
|
|
case XREPORT_DAILY:
|
|
CleanReport(mDailyReport);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update Specific Reports End Time ...
|
|
void FillReportEndTime(
|
|
XReport &report // Specified Report
|
|
)
|
|
{
|
|
//
|
|
datetime time = TimeCurrent();
|
|
report.end = time;
|
|
}
|
|
|
|
//
|
|
// Filleing Report Account Info ...
|
|
void FillReportAccountInfo(
|
|
XReport &report // Specified Report
|
|
)
|
|
{
|
|
//
|
|
FillReportEndTime(report);
|
|
|
|
//
|
|
report.accountInfo.user = (string)mAccountInfo.GetUserAccount();
|
|
report.accountInfo.name = mAccountInfo.GetName();
|
|
report.accountInfo.server = mAccountInfo.GetServerName();
|
|
report.accountInfo.broker = mAccountInfo.GetCompany();
|
|
report.accountInfo.mode = mAccountInfo.GetTradeMode();
|
|
report.accountInfo.leverage = mAccountInfo.GetLeverage();
|
|
report.accountInfo.currency = mAccountInfo.GetCurrency();
|
|
report.accountInfo.deposit = mAccountInfo.GetInitialBalance();
|
|
report.accountInfo.balance = mAccountInfo.GetBalance();
|
|
}
|
|
void FillReportAccountInfo(
|
|
ENUM_XREPORT_TYPES type // Specify Report Type
|
|
)
|
|
{
|
|
//
|
|
switch (type)
|
|
{
|
|
//
|
|
// Main ...
|
|
case XREPORT_MAIN:
|
|
FillReportAccountInfo(mMainReport);
|
|
break;
|
|
|
|
//
|
|
// Monthly ...
|
|
case XREPORT_MONTHLY:
|
|
FillReportAccountInfo(mMonthlyReport);
|
|
break;
|
|
|
|
//
|
|
// Weekly ...
|
|
case XREPORT_WEEKLY:
|
|
FillReportAccountInfo(mWeeklyReport);
|
|
break;
|
|
|
|
//
|
|
// Daily ...
|
|
case XREPORT_DAILY:
|
|
FillReportAccountInfo(mDailyReport);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Filling Volume Info ...
|
|
void FillingVolumeInfo(
|
|
XReport &report, // Specified Report
|
|
//
|
|
double staticVolume, // Current Available Static Volume
|
|
string staticVolumeCalculationMethod, // Static Volume Calculation Method
|
|
double maxAllowedVolume, // Current Maximum Appliable Volume
|
|
double volumeIncreaseMultiplier, // Static Volume Increase Multiplier
|
|
double balanceGowingToApplyMultiplier, // Multiply Volume Based On Balance Growing
|
|
double balanceGrowingToIncreaseMaxAllowedVolume // Where to Increase MaxAllowed Volume
|
|
)
|
|
{
|
|
//
|
|
FillReportEndTime(report);
|
|
|
|
//
|
|
report.volumeInfo.staticVolume = staticVolume;
|
|
report.volumeInfo.maxAllowedVolume = maxAllowedVolume;
|
|
report.volumeInfo.volumeIncreaseMultiplier = volumeIncreaseMultiplier;
|
|
report.volumeInfo.staticVolumeCalculationMethod = staticVolumeCalculationMethod;
|
|
report.volumeInfo.balanceGowingToApplyMultiplier = balanceGowingToApplyMultiplier;
|
|
report.volumeInfo.balanceGrowingToIncreaseMaxAllowedVolume = balanceGrowingToIncreaseMaxAllowedVolume;
|
|
}
|
|
|
|
//
|
|
// Add Signal to Report ...
|
|
void AddSignalToReport(
|
|
XReport &report, // Specified Report
|
|
const XSignal &signal, // Specified Signal
|
|
const string &providers[] // Specified Signal Providers
|
|
)
|
|
{
|
|
//
|
|
FillReportEndTime(report);
|
|
|
|
//
|
|
double volume = signal.volume;
|
|
bool isLong = signal.type == X_SIGNAL_LONG;
|
|
|
|
//
|
|
// Update Longs and Shorts ...
|
|
if (isLong)
|
|
{
|
|
report.summaryInfo.longSignals++;
|
|
}
|
|
else
|
|
{
|
|
report.summaryInfo.shortSignals++;
|
|
}
|
|
|
|
//
|
|
// Chack Max Volume ...
|
|
if (volume > report.summaryInfo.maxSignalVolume)
|
|
{
|
|
report.summaryInfo.maxSignalVolume = volume;
|
|
}
|
|
|
|
//
|
|
// Update Signallers Info ...
|
|
for (int i = 0; i < ArraySize(providers); i++)
|
|
{
|
|
//
|
|
string iProvider = providers[i];
|
|
|
|
//
|
|
AddOrUpdateSignaller(
|
|
report,
|
|
iProvider,
|
|
signal);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add or Update Signaller Info of Specific Report ...
|
|
void AddOrUpdateSignaller(
|
|
XReport &report, // Specified Report
|
|
const string provider, // Signal Provider name
|
|
const XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
int providerIdx = -1;
|
|
int signallersInfoCount = 0;
|
|
bool isLong = signal.type == X_SIGNAL_LONG;
|
|
|
|
//
|
|
// Prepare New One Provider ...
|
|
XSignallerInfo info = {};
|
|
info.name = provider;
|
|
if (isLong)
|
|
{
|
|
info.longs = 1;
|
|
}
|
|
else
|
|
{
|
|
info.shorts = 1;
|
|
}
|
|
|
|
//
|
|
// Find Provider idx if Exists ...
|
|
providerIdx = FindProviderInSignallersInfo(
|
|
provider,
|
|
report.signallersInfo);
|
|
|
|
//
|
|
// Check IDX ...
|
|
if (providerIdx > -1)
|
|
{
|
|
//
|
|
// Update Exists ...
|
|
if (isLong)
|
|
{
|
|
report.signallersInfo[providerIdx].longs++;
|
|
}
|
|
else
|
|
{
|
|
report.signallersInfo[providerIdx].shorts++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Add New One ...
|
|
AddItemToBuffer(
|
|
info,
|
|
report.signallersInfo);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add TP in Report ...
|
|
void AddTradeTPInReport(
|
|
XReport &report // Specified Report
|
|
)
|
|
{
|
|
//
|
|
FillReportEndTime(report);
|
|
|
|
//
|
|
report.summaryInfo.numberOfTPs++;
|
|
}
|
|
|
|
//
|
|
// Add SL in Report ...
|
|
void AddTradeSLInReport(
|
|
XReport &report // Specified Report
|
|
)
|
|
{
|
|
//
|
|
FillReportEndTime(report);
|
|
|
|
//
|
|
report.summaryInfo.numberOfSLs++;
|
|
}
|
|
|
|
//
|
|
// Update Number of Same Time Trades in Report ...
|
|
void UpdateMaxSameTimeTradesInReport(
|
|
XReport &report, // Specified Report
|
|
int value // number of same time trades
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
if (value <= report.summaryInfo.maxSameTimeSignals)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
FillReportEndTime(report);
|
|
|
|
//
|
|
report.summaryInfo.maxSameTimeSignals = value;
|
|
}
|
|
|
|
//
|
|
// Update Number of Same Time Trades in Report ...
|
|
void UpdateMaxDrawUpInReport(
|
|
XReport &report, // Specified Report
|
|
double value // draw up value
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
if (value <= report.summaryInfo.maxDrawUp)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
FillReportEndTime(report);
|
|
|
|
//
|
|
report.summaryInfo.maxDrawUp = value;
|
|
}
|
|
|
|
//
|
|
// Update Number of Same Time Trades in Report ...
|
|
void UpdateMaxDrawDownInReport(
|
|
XReport &report, // Specified Report
|
|
double value // draw down value
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
if (value <= report.summaryInfo.maxDrawDown)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
FillReportEndTime(report);
|
|
|
|
//
|
|
report.summaryInfo.maxDrawDown = value;
|
|
}
|
|
|
|
//
|
|
// Update Min Balance for Open Trades Value in Report ...
|
|
void UpdateMinBalanceForOpenTradesInReport(
|
|
XReport &report, // Specified Report
|
|
double value // min balance for open trades
|
|
)
|
|
{
|
|
//
|
|
// Validate Arg ...
|
|
if (value <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
FillReportEndTime(report);
|
|
|
|
//
|
|
report.accountInfo.minBalanceForOpenTrades = value;
|
|
}
|
|
|
|
//
|
|
// Generate Specific Report String Representation ...
|
|
string GenerateReportString(
|
|
XReport &report, // Specified Report
|
|
string separator = "\n" // Separator
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Start and End Date ...
|
|
result += "Start: " + (string)report.start + separator;
|
|
result += "End: " + (string)report.end + separator;
|
|
|
|
//
|
|
// Account Info ...
|
|
string accountStr = GenerateAccountInfoReportString(report.accountInfo);
|
|
result += "Account: " + separator + accountStr + separator;
|
|
|
|
//
|
|
// Summary Info ...
|
|
string summaryStr = GenerateSummaryInfoReportString(report.summaryInfo);
|
|
result += "Summary: " + separator + summaryStr + separator;
|
|
|
|
//
|
|
// Volume Info ...
|
|
string volumeStr = GenerateVolumeInfoReportString(report.volumeInfo);
|
|
result += "Volume: " + separator + volumeStr + separator;
|
|
|
|
//
|
|
// Signallers Info ...
|
|
string signallersStr = GenerateSignallersInfoReportString(report.signallersInfo);
|
|
result += "Signallers: " + separator + signallersStr + separator;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Account info String Representation ...
|
|
string GenerateAccountInfoReportString(
|
|
XAccountInfo &info, // Specified Account Info
|
|
string separator = "\n" // Separator
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Generate Report ...
|
|
result += "user: " + info.user + separator;
|
|
result += "name: " + info.name + separator;
|
|
result += "server: " + info.server + separator;
|
|
result += "broker: " + info.broker + separator;
|
|
result += "mode: " + EnumToString(info.mode) + separator;
|
|
result += "leverage: " + (string)info.leverage + separator;
|
|
result += "currency: " + info.currency + separator;
|
|
result += "deposit: " + (string)info.deposit + separator;
|
|
result += "balance: " + (string)info.balance + separator;
|
|
result += "minBalanceForTrading: " + (string)info.minBalanceForOpenTrades + separator;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Summary info String Representation ...
|
|
string GenerateSummaryInfoReportString(
|
|
XSummaryInfo &info, // Specified Summary Info
|
|
string separator = "\n" // Separator
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Generate Report ...
|
|
result += "longSignals: " + (string)info.longSignals + separator;
|
|
result += "shortSignals: " + (string)info.shortSignals + separator;
|
|
result += "numberOfTps: " + (string)info.numberOfTPs + separator;
|
|
result += "numberOfSLs: " + (string)info.numberOfSLs + separator;
|
|
result += "maxDrawUp: " + (string)info.maxDrawUp + separator;
|
|
result += "maxDrawDown: " + (string)info.maxDrawDown + separator;
|
|
result += "maxSignalVolume: " + (string)info.maxSignalVolume + separator;
|
|
result += "maxSameTimeSignals: " + (string)info.maxSameTimeSignals + separator;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Volume info String Representation ...
|
|
string GenerateVolumeInfoReportString(
|
|
XVolumeInfo &info, // Specified Volume Info
|
|
string separator = "\n" // Separator
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Generate Report ...
|
|
result += "staticVolume: " + (string)info.staticVolume + separator;
|
|
result += "staticVolumeCalculationMethod: " + info.staticVolumeCalculationMethod + separator;
|
|
result += "maxAllowedVolume: " + (string)info.maxAllowedVolume + separator;
|
|
result += "volumeIncreaseMultiplier: " + (string)info.volumeIncreaseMultiplier + separator;
|
|
result += "balanceGowingToApplyMultiplier: " + (string)info.balanceGowingToApplyMultiplier + separator;
|
|
result += "balanceGrowingToIncreaseMaxAllowedVolume: " + (string)info.balanceGrowingToIncreaseMaxAllowedVolume + separator;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Signallers info String Representation ...
|
|
string GenerateSignallersInfoReportString(
|
|
XSignallerInfo &infos[], // Specified Signallers Info
|
|
string separator = "\n" // Separator
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Signallers Array Size ...
|
|
int signallersCount = ArraySize(infos);
|
|
if (signallersCount <= 0)
|
|
{
|
|
//
|
|
result += "EMPTY" + separator;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Report ...
|
|
for (int i = 0; i < signallersCount; i++)
|
|
{
|
|
//
|
|
XSignallerInfo iSignaller = infos[i];
|
|
|
|
//
|
|
result += "name: " + iSignaller.name + separator;
|
|
result += "longs: " + (string)iSignaller.longs + separator;
|
|
result += "shorts: " + (string)iSignaller.shorts + separator;
|
|
|
|
//
|
|
if (i < signallersCount - 1)
|
|
{
|
|
result += separator;
|
|
}
|
|
}
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Find a Provider Index in SignallersInfo ...
|
|
int FindProviderInSignallersInfo(
|
|
string provider, // Specified Provider Name ...
|
|
XSignallerInfo &infoBuffer[] // Specified XSignallerInfo Buffer ...
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
int bufferSize = ArraySize(infoBuffer);
|
|
if (bufferSize <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Loop through buffer to find provider index ...
|
|
for (int i = 0; i < bufferSize; i++)
|
|
{
|
|
//
|
|
XSignallerInfo iInfo = infoBuffer[i];
|
|
|
|
//
|
|
if (iInfo.name != provider)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
result = i;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
};
|
|
|
|
//
|
|
// a Class For Collectiong Info ...
|
|
class XCCollector : public XCBase
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Constructor ...
|
|
XCCollector()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
~XCCollector() {}
|
|
|
|
//
|
|
// Reset All Collectors ...
|
|
void Reset()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Count Data ...
|
|
int Count()
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
result = ArraySize(mTimes);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// TODO: Implement Business Logic here ...
|
|
|
|
//
|
|
// Add Specific item to Collection ...
|
|
bool Add(
|
|
XCollector &item // Specified Info
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
result = IsValid(
|
|
item,
|
|
false);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Add Items Values to Buffers ...
|
|
//
|
|
AddItemToBuffer(
|
|
item.time,
|
|
mTimes);
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
item.volume,
|
|
mVolumes);
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
item.maxDrawDown,
|
|
mMaxDrawDowns);
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
item.maxAllowedVolume,
|
|
mMaxAllowedVolumes);
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
item.minBalanceForOpenTrade,
|
|
mMinBalanceForTradePrices);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Item ...
|
|
bool Get(
|
|
XCollector &item, // Hold's Result
|
|
int index = 0 // Specified Index to Retrieve
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = IsValidIndex(index);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Filling Item ...
|
|
item.time = mTimes[index];
|
|
item.volume = mVolumes[index];
|
|
item.maxDrawDown = mMaxDrawDowns[index];
|
|
item.maxAllowedVolume = mMaxAllowedVolumes[index];
|
|
item.minBalanceForOpenTrade = mMinBalanceForTradePrices[index];
|
|
|
|
//
|
|
return result;
|
|
}
|
|
XCollector Get(
|
|
int index = 0 // Specified Index to Retrieve
|
|
)
|
|
{
|
|
//
|
|
XCollector result = {};
|
|
|
|
//
|
|
bool isValidIndex = IsValidIndex(index);
|
|
if (!isValidIndex)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Filling Item ...
|
|
result.time = mTimes[index];
|
|
result.volume = mVolumes[index];
|
|
result.maxDrawDown = mMaxDrawDowns[index];
|
|
result.maxAllowedVolume = mMaxAllowedVolumes[index];
|
|
result.minBalanceForOpenTrade = mMinBalanceForTradePrices[index];
|
|
|
|
//
|
|
return result;
|
|
}
|
|
bool Get(
|
|
XCollector &item, // Hold's Result
|
|
datetime time // Specified Index to Retrieve
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (time <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int timesCount = ArraySize(mTimes);
|
|
result = timesCount > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int index = -1;
|
|
for (int i = 0; i < timesCount; i++)
|
|
{
|
|
//
|
|
datetime iTime = mTimes[i];
|
|
|
|
//
|
|
if (iTime == time)
|
|
{
|
|
//
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = index >= 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = Get(
|
|
item,
|
|
index);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
XCollector Get(
|
|
datetime time // Specified Index to Retrieve
|
|
)
|
|
{
|
|
//
|
|
XCollector result = {};
|
|
|
|
//
|
|
if (time <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int timesCount = ArraySize(mTimes);
|
|
if (timesCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int index = -1;
|
|
for (int i = 0; i < timesCount; i++)
|
|
{
|
|
//
|
|
datetime iTime = mTimes[i];
|
|
|
|
//
|
|
if (iTime == time)
|
|
{
|
|
//
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (index <= -1)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = Get(
|
|
index);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve All ...
|
|
void GetAll(
|
|
XCollector &result[], // Hold's Result
|
|
bool reversal = false // Extract Direction
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
int timesCount = ArraySize(mTimes);
|
|
if (timesCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
if (!reversal)
|
|
{
|
|
//
|
|
for (int i = 0; i < timesCount; i++)
|
|
{
|
|
//
|
|
XCollector iCollector = Get(i);
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
iCollector,
|
|
result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
for (int i = timesCount - 1; i >= 0; i--)
|
|
{
|
|
//
|
|
XCollector iCollector = Get(i);
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
iCollector,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Extract History ...
|
|
void Extract(
|
|
XCollector &result[], // Hold's Result
|
|
int start, // Start Index
|
|
int end // End Index
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
end < 0 ||
|
|
start < 0 ||
|
|
(start == 0 && end == 0) ||
|
|
MathAbs(start - end == 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Validate Start and End Index ...
|
|
bool isValidEnd = IsValidIndex(end);
|
|
bool isValidStart = IsValidIndex(start);
|
|
if (!isValidEnd || !isValidStart)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
bool direction = end > start;
|
|
if (direction)
|
|
{
|
|
//
|
|
for (int i = start; i < end - 1; i++)
|
|
{
|
|
//
|
|
XCollector iCollect = Get(i);
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
iCollect,
|
|
result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
for (int i = end - 1; i >= start; i--)
|
|
{
|
|
//
|
|
XCollector iCollect = Get(i);
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
iCollect,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Validate an Item ...
|
|
bool IsValid(
|
|
XCollector &item, // Specified Info
|
|
bool ignoreTime = true // Ignore Times Checking ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check Each Items contains Correct Value ...
|
|
//
|
|
result = item.volume >= 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = item.maxDrawDown >= 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = item.maxAllowedVolume >= 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = item.minBalanceForOpenTrade >= 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Time has Proper Value ...
|
|
result = item.time > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check time bigger than last Time ...
|
|
int timesCount = ArraySize(mTimes);
|
|
if (
|
|
!ignoreTime &&
|
|
timesCount >= 0)
|
|
{
|
|
//
|
|
// Retrieve Last Added Times ...
|
|
datetime lastTime;
|
|
result = GetLastItem(lastTime, mTimes);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Compare Last Time by current Time ...
|
|
result = item.time > lastTime;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Logging String Representation ...
|
|
string GenerateString(
|
|
XCollector &item, // Specified Info
|
|
string separator = "\n" // Separator Character
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
result += "time: " + (string)item.time + separator;
|
|
result += "volume: " + (string)item.volume + separator;
|
|
result += "maxDrawDown: " + (string)item.maxDrawDown + separator;
|
|
result += "maxAllowedVolume: " + (string)item.maxAllowedVolume + separator;
|
|
result += "minBalanceForOpenTrade: " + (string)item.minBalanceForOpenTrade + separator;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Max Draw Up touched till now ...
|
|
double mMaxDrawUp;
|
|
|
|
//
|
|
// Max Draw Down touched till now ...
|
|
double mMaxDrawDown;
|
|
|
|
//
|
|
// Last Time for Checks ...
|
|
datetime mTimes[];
|
|
|
|
//
|
|
// Volume at the Check Time ...
|
|
double mVolumes[];
|
|
|
|
//
|
|
// DrawDown for each Check ...
|
|
double mMaxDrawDowns[];
|
|
|
|
//
|
|
// Max Allowed Volume for each check ...
|
|
double mMaxAllowedVolumes[];
|
|
|
|
//
|
|
// Min Balance for Open Trades for Each Check ...
|
|
double mMinBalanceForTradePrices[];
|
|
|
|
//
|
|
// Reset ...
|
|
void Clean()
|
|
{
|
|
//
|
|
mMaxDrawUp = 0;
|
|
mMaxDrawDown = 0;
|
|
|
|
//
|
|
CleanBuffer(mTimes);
|
|
CleanBuffer(mVolumes);
|
|
CleanBuffer(mMaxDrawDowns);
|
|
CleanBuffer(mMaxAllowedVolumes);
|
|
CleanBuffer(mMinBalanceForTradePrices);
|
|
}
|
|
|
|
//
|
|
// Validate Index ...
|
|
bool IsValidIndex(
|
|
int index // Specified Index
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int bufferSize = ArraySize(mTimes);
|
|
if (bufferSize <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = index <= bufferSize - 1;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
};
|
|
|
|
//
|
|
// a Class for Managing Volumes ...
|
|
class XCVolumeManager : public XCBase
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Constructor ...
|
|
XCVolumeManager(
|
|
double staticVolumeRate, // Static Volume Calculation Mutiplier
|
|
ENUM_STATIC_VOLUME_METHODS volumeMethod = X_STATIC_VOLUME_BASED_ON_DEPOSIT, // How to Calculate Static Volume
|
|
double volumeMultiplier = 0, // Volume Multiplier
|
|
double volumeMultiplierAppliedRate = 0, // Apply Volume Multiplier when Balance Increased by this rate
|
|
double maxAllowedVolume = 0, // Max Allowed Volume
|
|
double increaseMaxAllowedVolumeRate = 0 // Increase Max Allowed Volumes when Balance Increased by this rate
|
|
)
|
|
{
|
|
//
|
|
mAccountInfo = new XCAccountInfo();
|
|
|
|
//
|
|
ResetForceVolumeMultiplier();
|
|
|
|
//
|
|
mVolumeMethod = volumeMethod;
|
|
mStaticVoluemRate = staticVolumeRate;
|
|
mVolumeMultiplier = volumeMultiplier;
|
|
mMaxAllowedVolume = maxAllowedVolume;
|
|
mVolumeMultiplierAppliedRate = volumeMultiplierAppliedRate;
|
|
mIncreaseMaxAllowedVolumeRate = increaseMaxAllowedVolumeRate;
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
~XCVolumeManager(void) {}
|
|
|
|
//
|
|
// Check Volume Multiplier ...
|
|
bool CanUseVolumeMultiplier()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
mVolumeMultiplier > 0 &&
|
|
mVolumeMultiplierAppliedRate > 0;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Max Volume ...
|
|
bool CanUseMaxVolume()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
mMaxAllowedVolume > 0;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate Volume for Position ...
|
|
double CalculateVolume(
|
|
int numberOfOpenPositions = 0, // Number Of Open Signals
|
|
string symbol = NULL // Specify Symbol
|
|
)
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
// Normalize Symbol ...
|
|
if (StringLen(symbol) == 0)
|
|
{
|
|
symbol = _Symbol;
|
|
}
|
|
|
|
//
|
|
// Static Volume ...
|
|
result = GetStaticVolume();
|
|
|
|
//
|
|
double multiplier = GetVolumeMultiplier();
|
|
result *= multiplier;
|
|
|
|
//
|
|
// Check Max Allowed Volume Exists ...
|
|
double maxAllowedVolume = GetMaxAllowedVolume();
|
|
if (maxAllowedVolume > 0)
|
|
{
|
|
//
|
|
result =
|
|
result > maxAllowedVolume
|
|
? maxAllowedVolume
|
|
: result;
|
|
}
|
|
|
|
//
|
|
// Check Number of Open Positions ...
|
|
if (numberOfOpenPositions >= 0)
|
|
{
|
|
//
|
|
// Decrease Volume ...
|
|
result /= (1 + numberOfOpenPositions);
|
|
}
|
|
|
|
//
|
|
// Apply Force Volume Multiplier ...
|
|
result *= mForceVolumeMultiplier;
|
|
|
|
//
|
|
// Retrieve Symbol Volume Info ...
|
|
result = NormalizeVolume(result, symbol, 2);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate Volume Multiplier ...
|
|
double GetVolumeMultiplier()
|
|
{
|
|
//
|
|
double result = 1;
|
|
|
|
//
|
|
double deposit = mAccountInfo.GetInitialBalance();
|
|
double balance = mAccountInfo.GetBalance();
|
|
bool canUseVolumeMultiplier = CanUseVolumeMultiplier();
|
|
if (
|
|
balance < deposit ||
|
|
!canUseVolumeMultiplier)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Apply Multiplier to Volume ...
|
|
double baseGrowBalance = deposit * mVolumeMultiplierAppliedRate;
|
|
double balanceGrows = balance - deposit;
|
|
if (balanceGrows < baseGrowBalance)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double applyMultiplierTimes = balanceGrows / baseGrowBalance;
|
|
double roundedApplyMultiplierTimes = MathRound(applyMultiplierTimes);
|
|
|
|
//
|
|
if (applyMultiplierTimes < roundedApplyMultiplierTimes)
|
|
{
|
|
applyMultiplierTimes -= (1 - (roundedApplyMultiplierTimes - applyMultiplierTimes));
|
|
}
|
|
|
|
//
|
|
// Calculate result ...
|
|
result = applyMultiplierTimes * mVolumeMultiplier;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Required Balance Grows for Increase Max Allowed Volume ...
|
|
double GetIncreaseMaxAllowedVolumeBalance()
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
if (
|
|
mMaxAllowedVolume <= 0 ||
|
|
mIncreaseMaxAllowedVolumeRate <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = mAccountInfo.GetInitialBalance() * mIncreaseMaxAllowedVolumeRate;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate Max Allowed Volume ...
|
|
double GetMaxAllowedVolume()
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
// Validate Args ...
|
|
result =
|
|
mMaxAllowedVolume <= 0
|
|
? 0
|
|
: mMaxAllowedVolume;
|
|
|
|
//
|
|
double deposit = mAccountInfo.GetInitialBalance();
|
|
double balance = mAccountInfo.GetBalance();
|
|
bool canUseMaxAllowedVolume = CanUseMaxVolume();
|
|
if (
|
|
balance <= deposit ||
|
|
!canUseMaxAllowedVolume ||
|
|
mIncreaseMaxAllowedVolumeRate <= 0)
|
|
{
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Apply Multiplier to Volume ...
|
|
double baseGrowBalance = GetIncreaseMaxAllowedVolumeBalance();
|
|
double balanceGrows = balance - deposit;
|
|
if (balanceGrows < baseGrowBalance)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double applyMultiplierTimes = balanceGrows / baseGrowBalance;
|
|
double roundedApplyMultiplierTimes = MathRound(applyMultiplierTimes);
|
|
|
|
//
|
|
if (applyMultiplierTimes < roundedApplyMultiplierTimes)
|
|
{
|
|
applyMultiplierTimes -= (1 - (roundedApplyMultiplierTimes - applyMultiplierTimes));
|
|
}
|
|
|
|
//
|
|
result *= applyMultiplierTimes;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get Static Volume ...
|
|
double GetStaticVolume()
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
double deposit = mAccountInfo.GetInitialBalance();
|
|
double balance = mAccountInfo.GetBalance();
|
|
|
|
//
|
|
switch (mVolumeMethod)
|
|
{
|
|
//
|
|
case X_STATIC_VOLUME_BASED_ON_DEPOSIT:
|
|
result = mStaticVoluemRate * deposit;
|
|
break;
|
|
|
|
//
|
|
case X_STATIC_VOLUME_BASED_ON_BALANCE:
|
|
result = mStaticVoluemRate * balance;
|
|
break;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Reset Froce Volume Multiplier ...
|
|
void ResetForceVolumeMultiplier()
|
|
{
|
|
mForceVolumeMultiplier = 1;
|
|
}
|
|
|
|
//
|
|
// Retrieve Current Force Volume Multiplier ...
|
|
double GetCurrentForceVolumeMultiplier()
|
|
{
|
|
return mForceVolumeMultiplier;
|
|
}
|
|
|
|
//
|
|
// Retrieve Next Increase Multiplier Price ...
|
|
double GetIncreaseMultiplierBalance()
|
|
{
|
|
return mAccountInfo.GetInitialBalance() * mVolumeMultiplierAppliedRate;
|
|
}
|
|
|
|
//
|
|
// Change Force Volume Multiplier ...
|
|
void ChangeForceVolumeMultiplier(
|
|
double rate // Change rate
|
|
)
|
|
{
|
|
//
|
|
// Normalize rate ...
|
|
if (rate <= 1)
|
|
{
|
|
rate = 1;
|
|
}
|
|
|
|
//
|
|
mForceVolumeMultiplier = rate;
|
|
}
|
|
|
|
//
|
|
// Increase Force Volume Multiplier ...
|
|
void IncreaseForceVolumeMultiplier()
|
|
{
|
|
mForceVolumeMultiplier += 0.5;
|
|
}
|
|
|
|
//
|
|
// Decrease Force Volume Multiplier ...
|
|
void DecreaseForceVolumeMultiplier()
|
|
{
|
|
//
|
|
if (mForceVolumeMultiplier <= 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
mForceVolumeMultiplier -= 0.5;
|
|
}
|
|
|
|
//
|
|
// Generate Current State Log String ...
|
|
string GenerateStateString(
|
|
string separator = "\n" // Log Separator ...
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Normallize Separator ...
|
|
if (StringLen(separator) == 0)
|
|
{
|
|
separator = "\n";
|
|
}
|
|
|
|
//
|
|
result += "ForceVolumeMultiplier: " + (string)mForceVolumeMultiplier + separator;
|
|
//
|
|
result += "StaticVoluemRate: " + (string)mStaticVoluemRate + separator;
|
|
result += "StaticVoluem: " + (string)GetStaticVolume() + separator;
|
|
result += "VolumeMethod: " + EnumToString(mVolumeMethod) + separator;
|
|
//
|
|
result += "VolumeMultiplier: " + (string)mVolumeMultiplier + separator;
|
|
result += "VolumeMultiplierAppliedRate: " + (string)mVolumeMultiplierAppliedRate + separator;
|
|
//
|
|
result += "MaxAllowedVolume: " + (string)mMaxAllowedVolume + separator;
|
|
result += "IncreaseMaxAllowedVolumeRate: " + (string)mIncreaseMaxAllowedVolumeRate + separator;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// an Instance of Account Info for reading Balance and Deposit and etc ...
|
|
XCAccountInfo mAccountInfo;
|
|
|
|
//
|
|
// Force Volume Rate Multiplier ...
|
|
double mForceVolumeMultiplier;
|
|
|
|
//
|
|
// Static Volume Calculation Mutiplier ...
|
|
double mStaticVoluemRate;
|
|
|
|
//
|
|
// How to Calculate Static Volume ...
|
|
ENUM_STATIC_VOLUME_METHODS mVolumeMethod;
|
|
|
|
//
|
|
// Volume Multiplier ...
|
|
double mVolumeMultiplier;
|
|
|
|
//
|
|
// Apply Volume Multiplier when Balance Increased by this rate ...
|
|
double mVolumeMultiplierAppliedRate;
|
|
|
|
//
|
|
// Max Allowed Volume
|
|
double mMaxAllowedVolume;
|
|
|
|
//
|
|
// Increase Max Allowed Volumes when Balance Increased by this rate
|
|
double mIncreaseMaxAllowedVolumeRate;
|
|
};
|
|
|
|
//
|
|
// a Class For Managing Signals Additional Data ...
|
|
class XCSignalManager : public XCBase
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Constructor ...
|
|
XCSignalManager(void)
|
|
{
|
|
CleanData();
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
~XCSignalManager(void) {}
|
|
|
|
//
|
|
void Reset()
|
|
{
|
|
CleanData();
|
|
}
|
|
|
|
//
|
|
// Retrieve All Signals ...
|
|
void GetAll(
|
|
XSignal &result[] // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
int signalsCount = ArraySize(mSignals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = mSignals[i];
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Count Items ...
|
|
int Count()
|
|
{
|
|
return ArraySize(mSignals);
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Signal by Ticket ...
|
|
XSignal Get(
|
|
ulong ticket // Signals Ticket ...
|
|
)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
// Check Exists ...
|
|
bool isExists = IsExists(ticket);
|
|
if (!isExists)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int signalsCount = ArraySize(mSignals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = mSignals[i];
|
|
|
|
//
|
|
if (iSignal.ticket == ticket)
|
|
{
|
|
//
|
|
result = iSignal;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Signal Exists or not ...
|
|
bool IsExists(
|
|
ulong ticket // Specified Signal Ticket
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int signalsCount = ArraySize(mSignals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = mSignals[i];
|
|
|
|
//
|
|
if (iSignal.ticket == ticket)
|
|
{
|
|
//
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
bool IsExists(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int signalsCount = ArraySize(mSignals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = mSignals[i];
|
|
|
|
//
|
|
if (iSignal.ticket == signal.ticket)
|
|
{
|
|
//
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Add Signal ...
|
|
bool Add(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Chekc Signal Exists ...
|
|
bool isExists = IsExists(signal);
|
|
if (isExists)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
signal,
|
|
mSignals);
|
|
result = true;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Update Specific Buffer ...
|
|
bool Update(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Chekc Signal Exists ...
|
|
bool isExists = IsExists(signal);
|
|
if (!isExists)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Remove Signal ...
|
|
Remove(signal);
|
|
|
|
//
|
|
// Add new One ...
|
|
AddItemToBuffer(
|
|
signal,
|
|
mSignals);
|
|
|
|
//
|
|
result = true;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Remove Signal ...
|
|
bool Remove(
|
|
ulong ticket // Specified Signal Ticket
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check Signal Exists ...
|
|
bool isExists = IsExists(ticket);
|
|
if (!isExists)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int signalsCount = ArraySize(mSignals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XSignal signals[];
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = mSignals[i];
|
|
|
|
//
|
|
if (iSignal.ticket != ticket)
|
|
{
|
|
AddItemToBuffer(
|
|
iSignal,
|
|
signals);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now we have to Clean All Signals and Replace them by signals[] buffer ...
|
|
UpdateData(signals);
|
|
|
|
//
|
|
result = true;
|
|
return result;
|
|
}
|
|
bool Remove(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check Signal Exists ...
|
|
bool isExists = IsExists(signal);
|
|
if (!isExists)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int signalsCount = ArraySize(mSignals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XSignal signals[];
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = mSignals[i];
|
|
|
|
//
|
|
if (iSignal.ticket != signal.ticket)
|
|
{
|
|
AddItemToBuffer(
|
|
iSignal,
|
|
signals);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now we have to Clean All Signals and Replace them by signals[] buffer ...
|
|
UpdateData(signals);
|
|
|
|
//
|
|
result = true;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Add Or Update ...
|
|
bool AddOrUpdate(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Chekc Signal Exists ...
|
|
bool isExists = IsExists(signal);
|
|
if (!isExists)
|
|
{
|
|
result = Add(signal);
|
|
}
|
|
else
|
|
{
|
|
result = Update(signal);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Holds Signals ...
|
|
XSignal mSignals[];
|
|
|
|
//
|
|
// Clean All Exists Data ...
|
|
void CleanData()
|
|
{
|
|
CleanBuffer(mSignals);
|
|
}
|
|
|
|
//
|
|
// Update Data ...
|
|
void UpdateData(
|
|
XSignal &source[] // a Buffer which needs to Replace ...
|
|
)
|
|
{
|
|
//
|
|
CleanData();
|
|
|
|
//
|
|
int sourceCount = ArraySize(source);
|
|
if (sourceCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < sourceCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = source[i];
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
iSignal,
|
|
mSignals);
|
|
}
|
|
}
|
|
};
|
|
|
|
//
|
|
// XSaherElm Trade Class ...
|
|
class XCTrade : public XCBase
|
|
{
|
|
//
|
|
// all public features ...
|
|
public:
|
|
//
|
|
// Constructor ...
|
|
void XCTrade(
|
|
string tag, // Specify a Tag for Trader instance
|
|
string symbol, // Specify Trader Symbol
|
|
int slippage, // Specify Slippage
|
|
ulong magicNumber, // Specify Magic Number
|
|
bool useVirtualTPSL, // Use Virtual TP SL
|
|
int maxAllowedTrades, // Specify Max Allowed Trades
|
|
double minBallanceForOpenTrades, // Specify Minimum Balance for Open Trades
|
|
double minAllowedFreeMarginForOpenTrades, // Specify Minimum Free Marging for Open Trades
|
|
bool useSignalProtector = true // Specify Use Signal Protector or not
|
|
)
|
|
{
|
|
//
|
|
mTag = tag;
|
|
mSymbol = symbol;
|
|
mSlippage = slippage;
|
|
mMagicNumber = magicNumber;
|
|
mUseVirtualTPSL = useVirtualTPSL;
|
|
mMaxAllowedTrades = maxAllowedTrades;
|
|
mMinBallanceForOpenTrades = minBallanceForOpenTrades;
|
|
mMinAllowedFreeMarginForOpenTrades = minAllowedFreeMarginForOpenTrades;
|
|
|
|
//
|
|
mTrader = new XSCTrade();
|
|
|
|
//
|
|
mTrader.SetAsyncMode(false);
|
|
mTrader.SetDeviationInPoints(mSlippage);
|
|
mTrader.SetExpertMagicNumber(mMagicNumber);
|
|
|
|
//
|
|
// Setting Log Level ...
|
|
mTrader.LogLevel(LOG_LEVEL_ERRORS);
|
|
|
|
//
|
|
mSignalManager = new XCSignalManager();
|
|
|
|
//
|
|
Reset();
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
void ~XCTrade()
|
|
{
|
|
}
|
|
|
|
//
|
|
// START Configuring Class Functions ...
|
|
//
|
|
|
|
//
|
|
// Reset all Variables ...
|
|
void Reset()
|
|
{
|
|
//
|
|
mSignalManager.Reset();
|
|
|
|
//
|
|
mDaysForRead = 1;
|
|
mScanStarted = false;
|
|
|
|
//
|
|
// Reset On Trade Counter ...
|
|
ResetOnTradeContext();
|
|
|
|
//
|
|
// Init On Trade Context ...
|
|
InitOnTradeContext();
|
|
}
|
|
|
|
//
|
|
// END Configuring Class Functions ...
|
|
//
|
|
|
|
//
|
|
// START Count ...
|
|
//
|
|
|
|
//
|
|
// Count Open Positions ...
|
|
int Count()
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
int total = PositionsTotal();
|
|
for (int i = 0; i < total; i++)
|
|
{
|
|
//
|
|
if (!mPositionInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Magic() != mMagicNumber)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Symbol() != mSymbol)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
result++;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Count Longs ...
|
|
int CountLongs()
|
|
{
|
|
int result = CountByType(X_SIGNAL_LONG);
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Count Shorts ...
|
|
int CountShorts()
|
|
{
|
|
int result = CountByType(X_SIGNAL_SHORT);
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Count Specific Type Of Positions ...
|
|
int CountByType(ENUM_POSITION_TYPE type)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
int total = PositionsTotal();
|
|
for (int i = 0; i < total; i++)
|
|
{
|
|
//
|
|
if (!mPositionInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Symbol() != mSymbol)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.PositionType() != type)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
result++;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Count By Type ...
|
|
int CountByType(ENUM_X_SIGNAL_TYPE type)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ENUM_POSITION_TYPE mType = POSITION_TYPE_BUY;
|
|
if (type == X_SIGNAL_LONG)
|
|
{
|
|
mType = POSITION_TYPE_BUY;
|
|
}
|
|
else if (type == X_SIGNAL_SHORT)
|
|
{
|
|
mType = POSITION_TYPE_SELL;
|
|
}
|
|
|
|
//
|
|
int result = CountByType(mType);
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Count ...
|
|
//
|
|
|
|
//
|
|
// START Retrieve ...
|
|
//
|
|
|
|
//
|
|
// Retrieve Specified Position by it's Ticket ...
|
|
XSignal GetSignal(
|
|
ulong ticket // Specified Ticket
|
|
)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
result = PositionToSignal(ticket);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get Last Signal ...
|
|
XSignal GetLastSignal()
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(signals);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = signals[0];
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Last Executed Signal ...
|
|
bool GetLastSignal(XSignal &signal)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(signals);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
signal = signals[0];
|
|
result = true;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve All Positions ...
|
|
void GetSignals(
|
|
XSignal &result[] // Holds Result ...
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
int totalPositions = PositionsTotal();
|
|
for (int i = 0; i < totalPositions; i++)
|
|
{
|
|
//
|
|
if (!mPositionInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Magic() != mMagicNumber)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Symbol() != mSymbol)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Position to Signal ...
|
|
XSignal signal = PositionToSignal(i);
|
|
Add(
|
|
signal,
|
|
result);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Retrieve Type Specific Signals ...
|
|
void GetSignals(
|
|
XSignal &result[], // Holds Result ...
|
|
ENUM_X_SIGNAL_TYPE type // Specified Sginal Type ...
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(signals);
|
|
|
|
//
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Loop ...
|
|
for (int i = signalsCount - 1; i >= 0; i--)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
if (type == X_SIGNAL_UNKNOWN)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
else if (iSignal.type == type)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Filter Signals By Searching Comments ...
|
|
void FilterSignals(
|
|
XSignal &result[], // Holds Result ...
|
|
const string query, // Search in Comment Qeury ...
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specified Sginal Type ...
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(
|
|
signals,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
StringLen(query) <= 0 ||
|
|
signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Loop ...
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
int queryPos = StringFind(
|
|
iSignal.comment,
|
|
query);
|
|
bool isContains = queryPos > -1;
|
|
if (!isContains)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get All Trades Which Candle Passed after Open ...
|
|
void GetOldSignals(
|
|
XSignal &result[], // Holds Result
|
|
int life, // Max Candle Passed after Trades Open
|
|
ENUM_TIMEFRAMES period = NULL, // Calculate Candles based on time frame
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
// Normalize Period ...
|
|
if (period == NULL)
|
|
{
|
|
period = _Period;
|
|
}
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(
|
|
signals,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Current Time Frame Candle Index ...
|
|
int currentBarIndex = 0;
|
|
|
|
//
|
|
// Loop through Positions ...
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
// Retrieve Trade Open Bar Index based on Current Period ...
|
|
int tradeOpenBarIndex = iBarShift(
|
|
mSymbol,
|
|
period,
|
|
iSignal.time);
|
|
|
|
//
|
|
// Calculate Trade Life ...
|
|
int diff = MathAbs(currentBarIndex - tradeOpenBarIndex);
|
|
|
|
//
|
|
bool isPassed = diff >= life;
|
|
if (isPassed)
|
|
{
|
|
//
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get In Profit Signals ...
|
|
void GetInProfitSignals(
|
|
XSignal &result[], // Holds Result
|
|
double minProfit = 0, // Minimum Profit To Close Trades
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(
|
|
signals,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Loop ...
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
bool isPassed = minProfit <= 0 && iSignal.profit > 0
|
|
? true
|
|
: iSignal.profit >= minProfit;
|
|
if (isPassed)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get In Drawdown Signals ...
|
|
void GetInDrawdownSignals(
|
|
XSignal &result[], // Holds Result
|
|
double maxDrawDown = 0, // Minimum Profit To Close Trades
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(
|
|
signals,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Loop ...
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
bool isPassed = maxDrawDown <= 0 && iSignal.profit < 0
|
|
? true
|
|
: iSignal.profit >= (-1 * maxDrawDown);
|
|
if (isPassed)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Retrieve Max In Draw Down Trade ...
|
|
XSignal GetMaxInDrawdownSignal(
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetInDrawdownSignals(
|
|
signals,
|
|
0,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Loop ...
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
result =
|
|
(result.ticket <= 0 &&
|
|
result.profit == 0) ||
|
|
result.profit > iSignal.profit
|
|
? iSignal
|
|
: result;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate Profits Of all Open Signals ...
|
|
double GetSignalsProfit(
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(signals, type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Loop ...
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
result += iSignal.profit;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Retrieve ...
|
|
//
|
|
|
|
//
|
|
// START Signal Execution Actions ...
|
|
//
|
|
|
|
//
|
|
// Execute a Signal in Market ...
|
|
ENUM_EXECUTION_SIGNAL_RESULT ExecuteSignal(
|
|
XSignal &signal, // Specified Signal
|
|
bool ignoreNumberOfTraes = false, // Ignore Number of Trades
|
|
bool checkAccountConditions = true // Check Account Balance, Equity and Free Margin for Signal Execution
|
|
)
|
|
{
|
|
//
|
|
ENUM_EXECUTION_SIGNAL_RESULT result = X_TRADER_UNKNOWN_ERROR;
|
|
|
|
//
|
|
// Validate Signal ...
|
|
bool isValid = IsValidSignal(signal);
|
|
if (!isValid)
|
|
{
|
|
//
|
|
result = X_TRADER_INVALID_SIGNAL_ERROR;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Account Equity ...
|
|
bool isEquityPassed = !checkAccountConditions
|
|
? true
|
|
: IsEquityReadyForTrade();
|
|
if (!isEquityPassed)
|
|
{
|
|
//
|
|
result = X_TRADER_NOT_ENOUGH_EQUITY_ERROR;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Account Balance ...
|
|
bool isBalancePassed = !checkAccountConditions
|
|
? true
|
|
: IsBalanceReadyForTrade();
|
|
if (!isBalancePassed)
|
|
{
|
|
//
|
|
result = X_TRADER_NOT_ENOUGH_BALANCE_ERROR;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Account FreeMargin ...
|
|
bool isFreeMarginPassed = !checkAccountConditions
|
|
? true
|
|
: IsFreeMarginReadyForTrade();
|
|
if (!isFreeMarginPassed)
|
|
{
|
|
//
|
|
result = X_TRADER_NOT_ENOUGH_MARIGIN_ERROR;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Number of Open Trades ...
|
|
int openTradesCount = Count();
|
|
bool isTradeCountPassed = ignoreNumberOfTraes
|
|
? true
|
|
: openTradesCount < mMaxAllowedTrades;
|
|
if (!isTradeCountPassed)
|
|
{
|
|
//
|
|
result = X_TRADER_MAX_TRADES_REACHED_ERROR;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool isExecuted =
|
|
signal.type == X_SIGNAL_LONG
|
|
? Buy(
|
|
signal.volume,
|
|
signal.entry,
|
|
mUseVirtualTPSL
|
|
? 0
|
|
: signal.sl,
|
|
mUseVirtualTPSL
|
|
? 0
|
|
: signal.tp,
|
|
signal.comment)
|
|
: signal.type == X_SIGNAL_SHORT
|
|
? Sell(
|
|
signal.volume,
|
|
signal.entry,
|
|
mUseVirtualTPSL
|
|
? 0
|
|
: signal.sl,
|
|
mUseVirtualTPSL
|
|
? 0
|
|
: signal.tp,
|
|
signal.comment)
|
|
: false;
|
|
if (isExecuted)
|
|
{
|
|
//
|
|
ulong ticket = PositionGetTicket(PositionsTotal() - 1);
|
|
|
|
//
|
|
signal.id = ticket;
|
|
signal.ticket = ticket;
|
|
|
|
//
|
|
AddOrUpdateSignalInfo(signal);
|
|
|
|
//
|
|
result = X_TRADER_SUCCEED_EXECUTION;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// This Function, Handling Following Works on Open Positions:
|
|
// - Close On SL if Reached ...
|
|
// - Close On TP if Reached ...
|
|
// - Make Risk Free Trades ...
|
|
bool HandleSignals(
|
|
XTraderHandlerResult &handlerResult, // Holds Result
|
|
bool ignoreRiskFree = false // Ignore Risk Free Trdaes
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Cleanup Result ...
|
|
CleanBuffer(handlerResult.sl);
|
|
CleanBuffer(handlerResult.tp);
|
|
CleanBuffer(handlerResult.rf);
|
|
|
|
//
|
|
// Reading Open Positions ...
|
|
XSignal signals[];
|
|
GetSignals(signals);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double deviation = mSlippage * GetPoints(mSymbol);
|
|
|
|
//
|
|
// Loop ...
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
bool isLong = iSignal.type == X_SIGNAL_LONG;
|
|
double entry = GetEntry(iSignal.symbol, iSignal.type);
|
|
double exit = GetExit(iSignal.symbol, iSignal.type);
|
|
double spread = GetSpread(iSignal.symbol);
|
|
double appliedTPPrice =
|
|
iSignal.tp > 0
|
|
? isLong
|
|
? iSignal.tp - deviation
|
|
: iSignal.tp + deviation
|
|
: 0;
|
|
double appliedSLPrice =
|
|
iSignal.sl > 0
|
|
? isLong
|
|
? iSignal.sl + deviation
|
|
: iSignal.sl - deviation
|
|
: 0;
|
|
|
|
//
|
|
double usedPrice = exit;
|
|
|
|
//
|
|
bool isSLTouched =
|
|
isLong
|
|
? usedPrice <= appliedSLPrice
|
|
: usedPrice >= appliedSLPrice;
|
|
|
|
//
|
|
bool isTPTouched =
|
|
isLong
|
|
? usedPrice >= appliedTPPrice
|
|
: usedPrice <= appliedTPPrice;
|
|
|
|
//
|
|
// Handle SL ...
|
|
if (
|
|
//
|
|
iSignal.sl > 0
|
|
//
|
|
&&
|
|
//
|
|
isSLTouched
|
|
//
|
|
&&
|
|
//
|
|
mUseVirtualTPSL)
|
|
{
|
|
//
|
|
string comment = GenerateSignalSLComment(iSignal);
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iSignal.ticket,
|
|
comment);
|
|
if (isClosed)
|
|
{
|
|
AddItemToBuffer(
|
|
iSignal,
|
|
handlerResult.sl);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handle TP ...
|
|
if (
|
|
//
|
|
iSignal.tp > 0
|
|
//
|
|
&&
|
|
//
|
|
isTPTouched
|
|
//
|
|
&&
|
|
//
|
|
mUseVirtualTPSL)
|
|
{
|
|
//
|
|
string comment = GenerateSignalTPComment(iSignal);
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iSignal.ticket,
|
|
comment);
|
|
if (isClosed)
|
|
{
|
|
AddItemToBuffer(
|
|
iSignal,
|
|
handlerResult.tp);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handle Risk Free ...
|
|
if (
|
|
//
|
|
!ignoreRiskFree
|
|
//
|
|
&&
|
|
//
|
|
iSignal.riskFreeStep > 0
|
|
//
|
|
&&
|
|
//
|
|
iSignal.riskFreeRate > 0
|
|
//
|
|
)
|
|
{
|
|
//
|
|
// Make Signals Risk Free Only if Signal In Profit ...
|
|
if (iSignal.profit <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Base Price is ENTRY, then Last Level, in each level SL is Last Level ...
|
|
//
|
|
// Calculate Risk Free Level/Multiplier ...
|
|
double level = 0;
|
|
double roundedLevel = 0;
|
|
double rStepPrice = PipsToPrice(iSignal.riskFreeStep);
|
|
double rBasePrice =
|
|
iSignal.sl > 0 &&
|
|
iSignal.sl > iSignal.entry
|
|
? iSignal.sl
|
|
: iSignal.entry;
|
|
|
|
//
|
|
double exit = GetExit(iSignal.type);
|
|
|
|
//
|
|
// Check Base Price for Signal reached with Exit Price ...
|
|
bool isExitPriceReachedBasePrice =
|
|
isLong
|
|
? exit > rBasePrice
|
|
: exit < rBasePrice;
|
|
if (!isExitPriceReachedBasePrice)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Calculate Risk Free Step Multiplier ...
|
|
level = MathAbs(exit - rBasePrice) / rStepPrice;
|
|
|
|
//
|
|
// Check Level Must Bigger than 1 ...
|
|
if (level < 1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Normalize Level ...
|
|
roundedLevel = MathRound(level);
|
|
if (level < roundedLevel)
|
|
{
|
|
level -= (1 - (roundedLevel - level));
|
|
}
|
|
else if (level > roundedLevel)
|
|
{
|
|
level -= (level - roundedLevel);
|
|
}
|
|
|
|
//
|
|
// Generate Comment ...
|
|
string comment = GenerateSignalRFComment(iSignal, (int)level);
|
|
|
|
//
|
|
// Calculate SL ...
|
|
double slDistance = spread;
|
|
double sl =
|
|
isLong
|
|
? exit - slDistance
|
|
: exit + slDistance;
|
|
|
|
//
|
|
// Increase TP One Level if Exists ...
|
|
double tp = iSignal.tp;
|
|
if (iSignal.tp > 0 && (isLong
|
|
? iSignal.tp < rBasePrice + rStepPrice
|
|
: iSignal.tp > rBasePrice - rStepPrice))
|
|
{
|
|
//
|
|
tp =
|
|
isLong
|
|
? iSignal.tp + rStepPrice
|
|
: iSignal.tp - rStepPrice;
|
|
}
|
|
|
|
//
|
|
double rVolume = NormalizeVolume((iSignal.volume * iSignal.riskFreeRate), iSignal.symbol);
|
|
|
|
//
|
|
// Update Signal Volume ...
|
|
double volume = iSignal.volume - rVolume;
|
|
|
|
//
|
|
bool isClosedPartial =
|
|
ClosePartial(
|
|
iSignal.ticket,
|
|
rVolume,
|
|
comment);
|
|
if (isClosedPartial)
|
|
{
|
|
//
|
|
bool isModified = true;
|
|
if (!mUseVirtualTPSL)
|
|
{
|
|
//
|
|
isModified = Modify(
|
|
iSignal.ticket,
|
|
sl,
|
|
tp,
|
|
comment);
|
|
}
|
|
|
|
//
|
|
if (isModified)
|
|
{
|
|
//
|
|
// Try to Update Signal ...
|
|
XSignal s = mSignalManager.Get(iSignal.ticket);
|
|
|
|
//
|
|
// Apply Changes on Signal ...
|
|
s.sl = sl;
|
|
s.tp = tp;
|
|
s.volume = volume;
|
|
|
|
//
|
|
// Update Signal ...
|
|
bool isUpdated = false;
|
|
bool isRemoved = false;
|
|
if (volume == 0)
|
|
{
|
|
isRemoved = mSignalManager.Remove(s);
|
|
}
|
|
else if (volume > 0)
|
|
{
|
|
isUpdated = mSignalManager.AddOrUpdate(s);
|
|
}
|
|
if (
|
|
isUpdated ||
|
|
isRemoved)
|
|
{
|
|
//
|
|
AddItemToBuffer(
|
|
s,
|
|
handlerResult.rf);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mSignalManager.Remove(iSignal.ticket);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
result =
|
|
ArraySize(handlerResult.tp) > 0 ||
|
|
ArraySize(handlerResult.sl) > 0 ||
|
|
ArraySize(handlerResult.rf) > 0;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close All Trades ...
|
|
void CloseSignals(
|
|
XSignal &result[], // Holds Result
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(
|
|
signals,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
string comment = GenerateSignalForceCloseComment(iSignal);
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iSignal.ticket,
|
|
comment);
|
|
if (isClosed)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close Pyramid Signals ...
|
|
void CloseInPyramidSignals(
|
|
XSignal &result[], // Holds Result
|
|
double pyramid = 0, // Close Pyramid Profit
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(
|
|
signals,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
double profit = 0;
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
profit += iSignal.profit;
|
|
}
|
|
|
|
//
|
|
// Normalize Pyramid ...
|
|
if (pyramid < 0)
|
|
{
|
|
pyramid = 0;
|
|
}
|
|
|
|
//
|
|
if (profit > pyramid)
|
|
{
|
|
CloseSignals(result);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close All Trades Which Candle Passed after Open ...
|
|
void CloseOldSignals(
|
|
XSignal &result[], // Holds Result
|
|
int life, // Max Candle Passed after Trades Open
|
|
ENUM_TIMEFRAMES period = NULL, // Calculate Candles based on time frame
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetOldSignals(
|
|
signals,
|
|
life,
|
|
period,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
string comment = GenerateSignalAgeComment(iSignal);
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iSignal.ticket,
|
|
comment);
|
|
if (isClosed)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close In Profit Signals ...
|
|
void CloseInProfitSignals(
|
|
XSignal &result[], // Holds Result
|
|
double minProfit = 0, // Minimum Profit To Close Trades
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetInProfitSignals(
|
|
signals,
|
|
minProfit,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
string comment = GenerateSignalProfitComment(iSignal);
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iSignal.ticket,
|
|
comment);
|
|
if (isClosed)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close In Drawdown Signals ...
|
|
void CloseInDrawdownSignals(
|
|
XSignal &result[], // Holds Result
|
|
double maxDrawDown = 0, // Minimum Profit To Close Trades
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetInDrawdownSignals(
|
|
signals,
|
|
maxDrawDown,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
string comment = GenerateSignalProfitComment(iSignal);
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iSignal.ticket,
|
|
comment);
|
|
if (isClosed)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// END Signal Execution Actions ...
|
|
//
|
|
|
|
//
|
|
// START Actions ...
|
|
//
|
|
|
|
//
|
|
// Force Open a Buy/Long Position ...
|
|
bool Buy(
|
|
double volume, // position volume
|
|
double price, // execution price
|
|
double sl = 0, // stop loss price
|
|
double tp = 0, // take profit price
|
|
const string comment = "" // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = mTrader.Buy(
|
|
volume,
|
|
mSymbol,
|
|
price,
|
|
sl,
|
|
tp,
|
|
comment);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Send BUY LIMIT order ...
|
|
bool BuyLimit(
|
|
double volume, // order volume
|
|
double price, // order price
|
|
double sl = 0.0, // stop loss price
|
|
double tp = 0.0, // take profit price
|
|
ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
|
|
datetime expiration = 0, // order expiration time
|
|
string comment = "" // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = mTrader.BuyLimit(
|
|
volume,
|
|
price,
|
|
mSymbol,
|
|
sl,
|
|
tp,
|
|
typeTime,
|
|
expiration,
|
|
comment);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Send BUY STOP order ...
|
|
bool BuyStop(
|
|
double volume, // order volume
|
|
double price, // order price
|
|
double sl = 0.0, // stop loss price
|
|
double tp = 0.0, // take profit price
|
|
ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
|
|
datetime expiration = 0, // order expiration time
|
|
string comment = "" // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = mTrader.BuyStop(
|
|
volume,
|
|
price,
|
|
mSymbol,
|
|
sl,
|
|
tp,
|
|
typeTime,
|
|
expiration,
|
|
comment);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Force Open a Sell/Short Position ...
|
|
bool Sell(
|
|
double volume, // position volume
|
|
double price, // execution price
|
|
double sl = 0, // stop loss price
|
|
double tp = 0, // take profit price
|
|
const string comment = "" // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = mTrader.Sell(
|
|
volume,
|
|
mSymbol,
|
|
price,
|
|
sl,
|
|
tp,
|
|
comment);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Send SELL LIMIT order ...
|
|
bool SellLimit(
|
|
double volume, // order volume
|
|
double price, // order price
|
|
double sl = 0.0, // stop loss price
|
|
double tp = 0.0, // take profit price
|
|
ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
|
|
datetime expiration = 0, // order expiration time
|
|
string comment = "" // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = mTrader.SellLimit(
|
|
volume,
|
|
price,
|
|
mSymbol,
|
|
sl,
|
|
tp,
|
|
typeTime,
|
|
expiration,
|
|
comment);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Send SELL STOP order ...
|
|
bool SellStop(
|
|
double volume, // order volume
|
|
double price, // order price
|
|
double sl = 0.0, // stop loss price
|
|
double tp = 0.0, // take profit price
|
|
ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
|
|
datetime expiration = 0, // order expiration time
|
|
string comment = "" // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = mTrader.SellStop(
|
|
volume,
|
|
price,
|
|
mSymbol,
|
|
sl,
|
|
tp,
|
|
typeTime,
|
|
expiration,
|
|
comment);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Modify Position ...
|
|
bool Modify(
|
|
const ulong ticket, // position ticket
|
|
double sl = 0, // stop loss price
|
|
double tp = 0, // take profit
|
|
const string comment = "" // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (tp <= 0 && sl <= 0)
|
|
{
|
|
result = false;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = mTrader.PositionModify(
|
|
ticket,
|
|
sl,
|
|
tp,
|
|
comment);
|
|
if (result)
|
|
{
|
|
//
|
|
if (mSignalManager.IsExists(ticket))
|
|
{
|
|
//
|
|
XSignal signal = GetSignal(ticket);
|
|
|
|
//
|
|
mSignalManager.Update(signal);
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close Position By Ticket ...
|
|
bool Close(
|
|
ulong ticket, // Position Ticket
|
|
string comment = "" // Close Position By Specific Comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Try to Select Position ...
|
|
if (!mPositionInfo.SelectByTicket(ticket))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close Position By Specific Comment ...
|
|
result = mTrader.PositionClose(
|
|
ticket,
|
|
mSlippage,
|
|
comment);
|
|
if (result)
|
|
{
|
|
//
|
|
if (mSignalManager.IsExists(ticket))
|
|
{
|
|
mSignalManager.Remove(ticket);
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close Partial By Ticket ...
|
|
bool ClosePartial(
|
|
ulong ticket, // Position Ticket
|
|
double volume, // Volume Size for Partial Closing
|
|
string comment = "" // Close Position By Specific Comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (!mPositionInfo.SelectByTicket(ticket))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = mTrader.PositionClosePartial(
|
|
ticket,
|
|
volume,
|
|
mSlippage,
|
|
comment);
|
|
if (result)
|
|
{
|
|
//
|
|
if (mSignalManager.IsExists(ticket))
|
|
{
|
|
//
|
|
XSignal signal = GetSignal(ticket);
|
|
|
|
//
|
|
mSignalManager.Update(signal);
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Actions ...
|
|
//
|
|
|
|
//
|
|
// START Account Related ...
|
|
//
|
|
|
|
//
|
|
// Retrieve Minimum Balance For Open Trades ...
|
|
double GetMinBalanceForOpenTrades()
|
|
{
|
|
//
|
|
double result = mMinBallanceForOpenTrades;
|
|
|
|
//
|
|
double balance = mAccountInfo.GetBalance();
|
|
double deposit = mAccountInfo.GetInitialBalance();
|
|
if (balance > deposit)
|
|
{
|
|
//
|
|
double rate = mMinBallanceForOpenTrades / deposit;
|
|
|
|
//
|
|
double ratedBalance = balance * rate;
|
|
|
|
//
|
|
result = MathMax(mMinBallanceForOpenTrades, ratedBalance);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Account Has Minimum Balance For Trade ...
|
|
bool IsBalanceReadyForTrade()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
double minBalance = GetMinBalanceForOpenTrades();
|
|
result = mAccountInfo.GetBalance() >= minBalance;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Free Margin Has Enough For Trade ...
|
|
bool IsFreeMarginReadyForTrade()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
double freeMargin = mAccountInfo.GetFreeMargin();
|
|
|
|
//
|
|
result = freeMargin >= mMinAllowedFreeMarginForOpenTrades;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Equity Has Enough For Trade ...
|
|
bool IsEquityReadyForTrade()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
double equity = mAccountInfo.GetEquity();
|
|
|
|
//
|
|
result = equity >= mMinAllowedFreeMarginForOpenTrades;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Account Related ...
|
|
//
|
|
|
|
//
|
|
// START Signal Related ...
|
|
//
|
|
|
|
//
|
|
// Validate Signal ...
|
|
bool IsValidSignal(
|
|
XSignal &signal // Specified Signal for Execution
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Signal Type ...
|
|
result = signal.type != X_SIGNAL_UNKNOWN;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Symbol ...
|
|
result = StringLen(signal.symbol) == 0 ? true : signal.symbol == mSymbol;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check magic number ...
|
|
result = signal.magicNumber <= 0 ? true : signal.magicNumber == mMagicNumber;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Entry ...
|
|
result = signal.entry > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
// //
|
|
// // Check SL ...
|
|
// // For Fix RiskFree Issues Ignore this ...
|
|
// result = signal.sl == 0 ? true : signal.type == X_SIGNAL_LONG ? signal.sl < signal.entry
|
|
// : signal.sl > signal.entry;
|
|
// if (!result)
|
|
// {
|
|
// return result;
|
|
// }
|
|
|
|
//
|
|
// Check TP ...
|
|
result = signal.tp == 0 ? true : signal.type == X_SIGNAL_LONG ? signal.tp > signal.entry
|
|
: signal.tp < signal.entry;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Volume ...
|
|
result = signal.volume > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Time ...
|
|
result = signal.time > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get Signal InDirection (Reversal) Type ...
|
|
ENUM_X_SIGNAL_TYPE GetSignalIndirectionType(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
ENUM_X_SIGNAL_TYPE result = X_SIGNAL_UNKNOWN;
|
|
|
|
//
|
|
switch (signal.type)
|
|
{
|
|
//
|
|
case X_SIGNAL_LONG:
|
|
result = X_SIGNAL_SHORT;
|
|
break;
|
|
|
|
//
|
|
case X_SIGNAL_SHORT:
|
|
result = X_SIGNAL_LONG;
|
|
break;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get Signal InDirection (Reversal) Type ...
|
|
ENUM_X_SIGNAL_TYPE GetIndirectionSignalType(
|
|
ENUM_X_SIGNAL_TYPE type // Specified Type
|
|
)
|
|
{
|
|
//
|
|
ENUM_X_SIGNAL_TYPE result = X_SIGNAL_UNKNOWN;
|
|
|
|
//
|
|
switch (type)
|
|
{
|
|
//
|
|
case X_SIGNAL_LONG:
|
|
result = X_SIGNAL_SHORT;
|
|
break;
|
|
|
|
//
|
|
case X_SIGNAL_SHORT:
|
|
result = X_SIGNAL_LONG;
|
|
break;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate SignalAge ...
|
|
int GetSignalAge(
|
|
datetime time // Signal Execution Time
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (time <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int sIndex = iBarShift(
|
|
mSymbol,
|
|
_Period,
|
|
time);
|
|
|
|
//
|
|
datetime cTime = iTime(
|
|
mSymbol,
|
|
_Period,
|
|
0);
|
|
int cIndex = iBarShift(
|
|
mSymbol,
|
|
_Period,
|
|
cTime);
|
|
|
|
//
|
|
result = MathAbs(cIndex - sIndex);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
int GetSignalAge(
|
|
datetime time, // Signal Execution Time
|
|
ENUM_TIMEFRAMES period // Specify Time Frame
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (time <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int sIndex = iBarShift(
|
|
mSymbol,
|
|
period,
|
|
time);
|
|
|
|
//
|
|
datetime cTime = iTime(
|
|
mSymbol,
|
|
period,
|
|
0);
|
|
int cIndex = iBarShift(
|
|
mSymbol,
|
|
_Period,
|
|
cTime);
|
|
|
|
//
|
|
result = MathAbs(cIndex - sIndex);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
int GetSignalAge(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (
|
|
signal.time <= 0 ||
|
|
!IsValid(signal, mMagicNumber))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = GetSignalAge(signal.time);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
int GetSignalAge(
|
|
XSignal &signal, // Specify Signal
|
|
ENUM_TIMEFRAMES period // Specify Time Frame
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (
|
|
signal.time <= 0 ||
|
|
!IsValid(signal, mMagicNumber))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = GetSignalAge(
|
|
signal.time,
|
|
period);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Prepare a Signall ...
|
|
XSignal GenerateSignal(
|
|
ENUM_X_SIGNAL_TYPE type, // Signal Type
|
|
double tp = 0, // Take Profit
|
|
double sl = 0, // Stop Loss
|
|
double volume = 0, // Volume
|
|
double riskFreeStep = 0, // Risk Free Step Price
|
|
double riskFreeRate = 0 // Risk Free Rate
|
|
)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (type == X_SIGNAL_UNKNOWN)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Signal Type ...
|
|
bool isLong = type == X_SIGNAL_LONG;
|
|
|
|
//
|
|
// Retrieve entry Price ...
|
|
double entry = GetEntry(mSymbol, type);
|
|
double spread = GetSpread(mSymbol);
|
|
|
|
//
|
|
result.tp = tp;
|
|
result.sl = sl;
|
|
result.type = type;
|
|
result.entry = entry;
|
|
result.symbol = mSymbol;
|
|
result.magicNumber = mMagicNumber;
|
|
result.riskFreeStep = riskFreeStep;
|
|
result.riskFreeRate = riskFreeRate;
|
|
result.time = iTime(mSymbol, _Period, 0);
|
|
result.volume = NormalizeVolume(volume, mSymbol);
|
|
|
|
//
|
|
// Since we Use signal instance to Generate Comment, this must be at the end ...
|
|
result.comment = GenerateSignalComment(result);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Convert a Signal to String Representation ...
|
|
string ToString(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
if (!IsValid(signal, mMagicNumber) || signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// TICKET ...
|
|
// it is very importants since they hold position info ...
|
|
result += X_SIGNAL_TICKET + "(" + (string)signal.ticket + ")";
|
|
|
|
//
|
|
// TYPE ...
|
|
string typeStr = GetSignalType(signal.type);
|
|
result += "_" + X_SIGNAL_TYPE + "(" + typeStr + ")";
|
|
|
|
//
|
|
// TP ...
|
|
result += "_" + X_SIGNAL_TP + "(" + (string)signal.tp + ")";
|
|
|
|
//
|
|
// SL ...
|
|
result += "_" + X_SIGNAL_SL + "(" + (string)signal.sl + ")";
|
|
|
|
//
|
|
// RFS ...
|
|
result += "_" + X_SIGNAL_RISKFREE_STEP + "(" + (string)signal.riskFreeStep + ")";
|
|
|
|
//
|
|
// RFR ...
|
|
result += "_" + X_SIGNAL_RISKFREE_RATE + "(" + (string)signal.riskFreeRate + ")";
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Comments for Specific Signal ...
|
|
string GenerateSignalComment(
|
|
ENUM_X_SIGNAL_TYPE type, // Specify Kind Signal Type
|
|
double volume, // Signal Volume
|
|
bool isCrossSignal = false // Specify Signal Cross
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
string typeStr = GetSignalType(type);
|
|
if (
|
|
volume <= 0 ||
|
|
StringLen(typeStr) == 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
volume = NormalizeVolume(volume, mSymbol);
|
|
|
|
//
|
|
result = mTag + (!isCrossSignal ? "" : "_Cross_") + "_" + typeStr;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
string GenerateSignalComment(
|
|
XSignal &signal, // Specified Signal
|
|
bool isCrossSignal = false // Specify Signal Cross
|
|
)
|
|
{
|
|
//
|
|
string result = GenerateSignalComment(
|
|
signal.type,
|
|
signal.volume,
|
|
isCrossSignal
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Signal RF TP Comment ...
|
|
string GenerateSignalRFComment(
|
|
XSignal &signal, // Specified Signal
|
|
int level // Which Step of RFS
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Signal and it's Ticket ...
|
|
if (!IsValidSignal(signal) || signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double exit = NormalizePrice(GetExit(signal.symbol, signal.type), signal.symbol);
|
|
|
|
//
|
|
string prefix = X_SIGNAL_RISKFREE_STEP + "(" + (string)signal.ticket + ")";
|
|
//
|
|
result =
|
|
//
|
|
level > 0
|
|
? prefix + "_" + (string)level + ": " + (string)exit
|
|
: prefix + ": " + (string)exit;
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Signal TP Comment ...
|
|
string GenerateSignalTPComment(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Signal and it's Ticket ...
|
|
if (!IsValidSignal(signal) || signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double exit = NormalizePrice(GetExit(signal.symbol, signal.type), signal.symbol);
|
|
|
|
//
|
|
result =
|
|
//
|
|
X_SIGNAL_TP + "(" + (string)signal.ticket + "): " + (string)exit;
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Signal SL Comment ...
|
|
string GenerateSignalSLComment(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Signal and it's Ticket ...
|
|
if (!IsValidSignal(signal) || signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double exit = NormalizePrice(GetExit(signal.symbol, signal.type), signal.symbol);
|
|
|
|
//
|
|
result =
|
|
//
|
|
X_SIGNAL_SL + "(" + (string)signal.ticket + "): " + (string)exit;
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Signal Profit Comment ...
|
|
string GenerateSignalProfitComment(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Signal and it's Ticket ...
|
|
if (!IsValidSignal(signal) || signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result =
|
|
//
|
|
"Profit(" + (string)signal.ticket + "): " + (string)signal.profit;
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Signal Force Close Comment ...
|
|
string GenerateSignalForceCloseComment(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Signal and it's Ticket ...
|
|
if (!IsValidSignal(signal) || signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result =
|
|
//
|
|
"FC(" + (string)signal.ticket + "): " + (string)signal.profit;
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Signal Age Comment ...
|
|
string GenerateSignalAgeComment(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Signal and it's Ticket ...
|
|
if (!IsValidSignal(signal) || signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int age = GetSignalAge(signal);
|
|
|
|
//
|
|
result =
|
|
//
|
|
"(" + (string)signal.ticket + ") A: " + (string)age + ", P:" + (string)signal.profit;
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate a Signal Object name for Draw Events On Chart ...
|
|
string GenerateSignalObjectName(
|
|
ulong ticket // Specify Ticket
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// TICKET ...
|
|
// it is very importants since they hold position info ...
|
|
result += "T(" + (string)ticket + ")";
|
|
|
|
//
|
|
return result;
|
|
}
|
|
string GenerateSignalObjectName(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (!IsValid(signal, mMagicNumber))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = GenerateSignalObjectName(signal.ticket);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Genrate a Signal Event Object Name ...
|
|
string GenerateSignalEventObjectName(
|
|
ulong ticket // Specify Ticket
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
result = GenerateSignalObjectName(ticket) + "_Event";
|
|
|
|
//
|
|
return result;
|
|
}
|
|
string GenerateSignalEventObjectName(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
result = GenerateSignalEventObjectName(signal.ticket);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Signal Related ...
|
|
//
|
|
|
|
//
|
|
// START Deals ...
|
|
//
|
|
|
|
//
|
|
// Count Deals ...
|
|
int DealsCount(
|
|
datetime start = -1, // Start Date
|
|
datetime end = -1 // End Date
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
XDeal deals[];
|
|
GetDeals(
|
|
deals,
|
|
start,
|
|
end);
|
|
result = ArraySize(deals);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve All Deals ...
|
|
void GetDeals(
|
|
XDeal &result[], // Hold's Result
|
|
datetime start = -1, // Start Date
|
|
datetime end = -1 // End Date
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
// Select History if Start and End date provided ...
|
|
if (start > -1)
|
|
{
|
|
//
|
|
// Normalize End date ...
|
|
if (end == -1)
|
|
{
|
|
end = TimeCurrent();
|
|
}
|
|
|
|
//
|
|
bool isSelect = HistorySelect(start, end);
|
|
if (!isSelect)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
int total = HistoryDealsTotal();
|
|
if (total <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Loop ...
|
|
for (int i = total - 1; i >= 0; i--)
|
|
{
|
|
//
|
|
// Select by Index ...
|
|
bool isSelect = mDealInfo.SelectByIndex(i);
|
|
if (!isSelect)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Magic ...
|
|
ulong magic = mDealInfo.Magic();
|
|
bool isMagicSame = magic == mMagicNumber;
|
|
if (!isMagicSame)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Symbol ...
|
|
string symbol = mDealInfo.Symbol();
|
|
bool isSymbolSame = symbol == mSymbol;
|
|
if (!isSymbolSame)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
XDeal deal = ToXDealByIndex(i);
|
|
|
|
//
|
|
Add(
|
|
deal,
|
|
result);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Retrieve Last Deal ...
|
|
XDeal GetLastDeal()
|
|
{
|
|
//
|
|
XDeal result = {};
|
|
|
|
//
|
|
XDeal deals[];
|
|
GetDeals(deals);
|
|
int dealsCount = ArraySize(deals);
|
|
if (dealsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = deals[0];
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Deals ...
|
|
//
|
|
|
|
//
|
|
// START Orders ...
|
|
//
|
|
|
|
//
|
|
// Count Orders ...
|
|
int OrdersCount(
|
|
datetime start = -1, // Start Date
|
|
datetime end = -1 // End Date
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
XOrder orders[];
|
|
GetOrders(
|
|
orders,
|
|
start,
|
|
end);
|
|
result = ArraySize(orders);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Orders ...
|
|
void GetOrders(
|
|
XOrder &result[], // Hold's Result
|
|
datetime start = -1, // Start Date
|
|
datetime end = -1 // End Date
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
// Select History if Start and End date provided ...
|
|
if (start > -1)
|
|
{
|
|
//
|
|
// Normalize End date ...
|
|
if (end == -1)
|
|
{
|
|
end = TimeCurrent();
|
|
}
|
|
|
|
//
|
|
bool isSelect = HistorySelect(start, end);
|
|
if (!isSelect)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
int total = HistoryOrdersTotal();
|
|
if (total <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Loop ...
|
|
for (int i = total - 1; i >= 0; i--)
|
|
{
|
|
//
|
|
// Select by Index ...
|
|
bool isSelect = mOrderInfo.SelectByIndex(i);
|
|
if (!isSelect)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Magic ...
|
|
ulong magic = mOrderInfo.Magic();
|
|
bool isMagicSame = magic == mMagicNumber;
|
|
if (!isMagicSame)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Symbol ...
|
|
string symbol = mOrderInfo.Symbol();
|
|
bool isSymbolSame = symbol == mSymbol;
|
|
if (!isSymbolSame)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
XOrder order = ToXOrderByIndex(i);
|
|
|
|
//
|
|
Add(
|
|
order,
|
|
result);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Retrieve Last Order ...
|
|
XOrder GetLastOrder()
|
|
{
|
|
//
|
|
XOrder result = {};
|
|
|
|
//
|
|
XOrder orders[];
|
|
GetOrders(orders);
|
|
int ordersCount = ArraySize(orders);
|
|
if (ordersCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = orders[0];
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Orders ...
|
|
//
|
|
|
|
//
|
|
// START OnTrade Handlers ...
|
|
//
|
|
|
|
//
|
|
// Handle OnTrade Event ...
|
|
void HandleOnTradeEvent(
|
|
XOnTradeHandlerResult &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
if (mScanStarted)
|
|
{
|
|
ProcessOnTradeContext(result);
|
|
}
|
|
else
|
|
{
|
|
InitOnTradeContext();
|
|
}
|
|
}
|
|
|
|
//
|
|
// END OnTrade Handlers ...
|
|
//
|
|
|
|
//
|
|
// Retrieve Magic Number ...
|
|
ulong GetMagicNumber()
|
|
{
|
|
return mMagicNumber;
|
|
}
|
|
|
|
//
|
|
// all protected features ...
|
|
protected:
|
|
//
|
|
// all private features ...
|
|
private:
|
|
//
|
|
// Specified Unique Tag ...
|
|
string mTag;
|
|
|
|
//
|
|
// which Symbol ...
|
|
string mSymbol;
|
|
|
|
//
|
|
// using deviation, it must be in Point ...
|
|
int mSlippage;
|
|
|
|
//
|
|
// using magic number ...
|
|
ulong mMagicNumber;
|
|
|
|
//
|
|
// Detect Using Virtual TP or SL ...
|
|
bool mUseVirtualTPSL;
|
|
|
|
//
|
|
// Specify Max Allowed Trades
|
|
int mMaxAllowedTrades;
|
|
|
|
//
|
|
// Minimum Free Marging for Open Trades
|
|
double mMinAllowedFreeMarginForOpenTrades;
|
|
|
|
//
|
|
// Minimum Balance for Open Trades
|
|
double mMinBallanceForOpenTrades;
|
|
|
|
//
|
|
// using CTrade instance ...
|
|
XSCTrade mTrader;
|
|
|
|
//
|
|
// Deals Info ...
|
|
CDealInfo mDealInfo;
|
|
|
|
//
|
|
// Order Info ...
|
|
COrderInfo mOrderInfo;
|
|
|
|
//
|
|
// An Instance Of XCAccount Info for Handling Account Related Actions ...
|
|
XCAccountInfo mAccountInfo;
|
|
|
|
//
|
|
// using PositionInfo instance ...
|
|
CPositionInfo mPositionInfo;
|
|
|
|
//
|
|
// Signal Manager ...
|
|
XCSignalManager mSignalManager;
|
|
|
|
//
|
|
// START OnTrade Handlers ...
|
|
//
|
|
|
|
//
|
|
int mDaysForRead;
|
|
|
|
//
|
|
// set the limits of the trade history on the global scope ...
|
|
|
|
//
|
|
// // start date for trade history in cache ...
|
|
datetime mStartDate;
|
|
|
|
//
|
|
// end date for trade history in cache ...
|
|
datetime mEndDate;
|
|
|
|
//
|
|
// global OnTrade counters ...
|
|
|
|
//
|
|
// number of active orders ...
|
|
int mOrdersCount;
|
|
|
|
//
|
|
// number of open positions ...
|
|
int mPositionsCount;
|
|
|
|
//
|
|
// number of deals in the trade history cache ...
|
|
int mDealsCount;
|
|
|
|
//
|
|
// number of orders in the trade history cache ...
|
|
int mHistoryOrdersCount;
|
|
|
|
//
|
|
// flag of counter relevance ...
|
|
bool mScanStarted;
|
|
|
|
//
|
|
// Check Statrt Date In Trade History ...
|
|
void CheckStartDateInTradeHistory()
|
|
{
|
|
//
|
|
// initial interval, if we were to start working right now ..
|
|
datetime currStart = TimeCurrent() - (mDaysForRead * PeriodSeconds(PERIOD_D1));
|
|
|
|
//
|
|
// make sure that the start limit of the trade history has not gone
|
|
// more than 1 day over the intended date ...
|
|
if (currStart - mStartDate > PeriodSeconds(PERIOD_D1))
|
|
{
|
|
//
|
|
// correct the start date of history to be loaded in the cache ...
|
|
mStartDate = currStart;
|
|
|
|
//
|
|
// now reload the trade history for the updated interval ...
|
|
HistorySelect(mStartDate, mEndDate);
|
|
|
|
//
|
|
// correct the deal and order counters in history for further comparison
|
|
mHistoryOrdersCount = HistoryOrdersTotal();
|
|
mDealsCount = HistoryDealsTotal();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Process On Trade Context ...
|
|
void ProcessOnTradeContext(
|
|
XOnTradeHandlerResult &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
mEndDate = TimeCurrent();
|
|
|
|
//
|
|
ResetLastError();
|
|
|
|
//
|
|
// download trading history from the specified interval to the program cache ...
|
|
bool selected = HistorySelect(mStartDate, mEndDate);
|
|
if (!selected)
|
|
{
|
|
//
|
|
LogMessage("Failed to Load OnTrade Context History ...");
|
|
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// get the current values ...
|
|
int currOrders = OrdersTotal();
|
|
int currPositions = PositionsTotal();
|
|
int currDeals = HistoryDealsTotal();
|
|
int currHistoryOrders = HistoryOrdersTotal();
|
|
|
|
//
|
|
// Orders ...
|
|
// check if the number of active orders has been changed ...
|
|
if (currOrders != mOrdersCount)
|
|
{
|
|
//
|
|
// number of active orders has been changed ...
|
|
result.hasNewOrder = true;
|
|
result.newOrders = currOrders - mOrdersCount;
|
|
|
|
//
|
|
// update the value ...
|
|
mOrdersCount = currOrders;
|
|
}
|
|
|
|
//
|
|
// Positions ...
|
|
// changes in the number of open positions ...
|
|
if (currPositions != mPositionsCount)
|
|
{
|
|
//
|
|
// number of open positions has been changed ...
|
|
result.hasNewPosition = true;
|
|
result.newPositions = currPositions - mPositionsCount;
|
|
|
|
//
|
|
// update the value ...
|
|
mPositionsCount = currPositions;
|
|
}
|
|
|
|
//
|
|
// Deals ...
|
|
// changes in the number of deals in the trade history cache ...
|
|
if (currDeals != mDealsCount)
|
|
{
|
|
//
|
|
// number of deals in the trade history cache has been changed ...
|
|
result.hasNewDeal = true;
|
|
result.newDeals = currDeals - mDealsCount;
|
|
|
|
//
|
|
// update the value ...
|
|
mDealsCount = currDeals;
|
|
}
|
|
|
|
//
|
|
// History Orders ...
|
|
// changes in the number of history orders in the trade history cache ...
|
|
if (currHistoryOrders != mHistoryOrdersCount)
|
|
{
|
|
//
|
|
// number of history orders in the trade history cache has been changed ...
|
|
result.hasNewHistoryOrder = true;
|
|
result.newHistoryOrders = currHistoryOrders - mHistoryOrdersCount;
|
|
|
|
//
|
|
// update the value ...
|
|
mHistoryOrdersCount = currHistoryOrders;
|
|
}
|
|
|
|
//
|
|
// checking if it is necessary to change the limits of the trade history to be requested in cache ...
|
|
CheckStartDateInTradeHistory();
|
|
}
|
|
|
|
//
|
|
// Reset On Trade Scanner Context ...
|
|
void ResetOnTradeContext()
|
|
{
|
|
//
|
|
mEndDate = TimeCurrent();
|
|
mStartDate = mEndDate - (mDaysForRead * PeriodSeconds(PERIOD_D1));
|
|
}
|
|
|
|
//
|
|
// Initial Context Of Trade Scanner ...
|
|
void InitOnTradeContext()
|
|
{
|
|
//
|
|
ResetLastError();
|
|
|
|
//
|
|
// load history ...
|
|
bool selected = HistorySelect(mStartDate, mEndDate);
|
|
if (!selected)
|
|
{
|
|
//
|
|
LogMessage("Failed to Load OnTrade Context History ...");
|
|
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// get the current value ...
|
|
mOrdersCount = OrdersTotal();
|
|
mPositionsCount = PositionsTotal();
|
|
mDealsCount = HistoryDealsTotal();
|
|
mHistoryOrdersCount = HistoryOrdersTotal();
|
|
|
|
//
|
|
mScanStarted = true;
|
|
}
|
|
|
|
//
|
|
// END OnTrade Handlers ...
|
|
//
|
|
|
|
//
|
|
// START Signal Private ...
|
|
//
|
|
|
|
//
|
|
// Add Or Update Signal ...
|
|
void AddOrUpdateSignalInfo(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
bool isExists = mSignalManager.IsExists(signal);
|
|
if (isExists)
|
|
{
|
|
mSignalManager.Remove(signal.ticket);
|
|
}
|
|
|
|
//
|
|
bool isAddOrUpdated = mSignalManager.AddOrUpdate(signal);
|
|
if (isAddOrUpdated)
|
|
{
|
|
//
|
|
color longColor = clrAqua;
|
|
color shortColor = clrFuchsia;
|
|
|
|
//
|
|
DrawSignalShape(
|
|
signal,
|
|
longColor,
|
|
shortColor);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Retrieve Signal String From Chart Event ...
|
|
XSignal RetrieveSignal(
|
|
ulong ticket // Specify Position Ticket
|
|
)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
if (ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = mSignalManager.Get(ticket);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
void RetrieveSignal(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
|
|
//
|
|
XSignal tSignal = mSignalManager.Get(signal.ticket);
|
|
|
|
//
|
|
// Fill signal by tSignal Data ...
|
|
// since tese properties may change in signal manipulation times ...
|
|
signal.tp = tSignal.tp;
|
|
signal.sl = tSignal.sl;
|
|
signal.riskFreeStep = tSignal.riskFreeStep;
|
|
signal.riskFreeRate = tSignal.riskFreeRate;
|
|
}
|
|
|
|
//
|
|
// Convert Position to Signal by Index ...
|
|
XSignal PositionToSignal(int index)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
if (!mPositionInfo.SelectByIndex(index))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ulong ticket = mPositionInfo.Ticket();
|
|
result = PositionToSignal(ticket);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Position To Signal by Ticket ...
|
|
XSignal PositionToSignal(ulong ticket)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
if (!mPositionInfo.SelectByTicket(ticket))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool isExists = mSignalManager.IsExists(ticket);
|
|
if (isExists)
|
|
{
|
|
result = RetrieveSignal(ticket);
|
|
}
|
|
|
|
//
|
|
// Ticket ...
|
|
result.ticket = mPositionInfo.Ticket();
|
|
|
|
//
|
|
// ID ...
|
|
result.id = mPositionInfo.Identifier();
|
|
|
|
//
|
|
// Symbol ...
|
|
result.symbol = mPositionInfo.Symbol();
|
|
|
|
//
|
|
// Type ...
|
|
ENUM_POSITION_TYPE pType = (ENUM_POSITION_TYPE)mPositionInfo.PositionType();
|
|
ENUM_X_SIGNAL_TYPE type = pType == POSITION_TYPE_BUY ? X_SIGNAL_LONG : X_SIGNAL_SHORT;
|
|
result.type = type;
|
|
|
|
//
|
|
// MagicNumber ...
|
|
result.magicNumber = mPositionInfo.Magic();
|
|
|
|
//
|
|
// Entry/Open Price ...
|
|
result.entry = mPositionInfo.PriceOpen();
|
|
|
|
//
|
|
// Take Profit ...
|
|
result.tp = result.tp <= 0 && mPositionInfo.TakeProfit() > 0
|
|
? mPositionInfo.TakeProfit()
|
|
: result.tp;
|
|
|
|
//
|
|
// Stop Loss ...
|
|
result.sl = result.sl <= 0 && mPositionInfo.StopLoss() > 0
|
|
? mPositionInfo.StopLoss()
|
|
: result.sl;
|
|
|
|
//
|
|
// Profit ...
|
|
result.profit = mPositionInfo.Profit();
|
|
|
|
//
|
|
// Time ...
|
|
result.time = mPositionInfo.Time();
|
|
|
|
//
|
|
// Volume ...
|
|
result.volume = mPositionInfo.Volume();
|
|
|
|
//
|
|
// Comment ...
|
|
result.comment = mPositionInfo.Comment();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Converts a Deal to XDeal by Index ...
|
|
XDeal ToXDealByIndex(int index)
|
|
{
|
|
//
|
|
XDeal result = {};
|
|
|
|
//
|
|
bool isSelect = mDealInfo.SelectByIndex(index);
|
|
if (!isSelect)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Read Deal Ticket ...
|
|
ulong ticket = mDealInfo.Ticket();
|
|
result = ToXDealByTicket(ticket);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Converts a Deal to XDeal by Ticket ...
|
|
XDeal ToXDealByTicket(ulong ticket)
|
|
{
|
|
//
|
|
XDeal result = {};
|
|
|
|
//
|
|
mDealInfo.Ticket(ticket);
|
|
|
|
//
|
|
result.time = mDealInfo.Time();
|
|
result.swap = mDealInfo.Swap();
|
|
result.magic = mDealInfo.Magic();
|
|
result.order = mDealInfo.Order();
|
|
result.entry = mDealInfo.Entry();
|
|
result.price = mDealInfo.Price();
|
|
result.ticket = mDealInfo.Ticket();
|
|
result.symbol = mDealInfo.Symbol();
|
|
result.type = mDealInfo.DealType();
|
|
result.profit = mDealInfo.Profit();
|
|
result.volume = mDealInfo.Volume();
|
|
result.comment = mDealInfo.Comment();
|
|
result.positionId = mDealInfo.PositionId();
|
|
result.commission = mDealInfo.Commission();
|
|
result.reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(
|
|
ticket,
|
|
DEAL_REASON);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Convert an Order to XOrder by Index ...
|
|
XOrder ToXOrderByIndex(int index)
|
|
{
|
|
//
|
|
XOrder result = {};
|
|
|
|
//
|
|
bool isSelect = mOrderInfo.SelectByIndex(index);
|
|
if (!isSelect)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ulong ticket = mOrderInfo.Ticket();
|
|
result = ToXOrderByTicket(ticket);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Convert an Order to XOrder by Ticket ...
|
|
XOrder ToXOrderByTicket(ulong ticket)
|
|
{
|
|
//
|
|
XOrder result = {};
|
|
|
|
//
|
|
bool isSelect = mOrderInfo.Select(ticket);
|
|
if (!isSelect)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result.state = mOrderInfo.State();
|
|
result.magic = mOrderInfo.Magic();
|
|
result.ticket = mOrderInfo.Ticket();
|
|
result.symbol = mOrderInfo.Symbol();
|
|
result.type = mOrderInfo.OrderType();
|
|
result.comment = mOrderInfo.Comment();
|
|
result.stopLoss = mOrderInfo.StopLoss();
|
|
result.typeTime = mOrderInfo.TypeTime();
|
|
result.timeDone = mOrderInfo.TimeDone();
|
|
result.timeSetup = mOrderInfo.TimeSetup();
|
|
result.positionId = mOrderInfo.PositionId();
|
|
result.priceOpen = mOrderInfo.PriceOpen();
|
|
result.takeProfit = mOrderInfo.TakeProfit();
|
|
result.typeFilling = mOrderInfo.TypeFilling();
|
|
result.priceCurrent = mOrderInfo.PriceCurrent();
|
|
result.volumeInitial = mOrderInfo.VolumeInitial();
|
|
result.volumeCurrent = mOrderInfo.VolumeCurrent();
|
|
result.timeExpiration = mOrderInfo.TimeExpiration();
|
|
result.priceStopLimit = mOrderInfo.PriceStopLimit();
|
|
result.reason = (ENUM_ORDER_REASON)HistoryOrderGetInteger(
|
|
ticket,
|
|
ORDER_REASON);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
//
|
|
// END Signal Private ...
|
|
//
|
|
};
|
|
|
|
//
|
|
// a Class For Guard Account ...
|
|
class XCGuard : public XCBase
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Constructor ...
|
|
XCGuard(
|
|
int oldAge, // Age for Old Trades
|
|
ENUM_X_GUARD_ACTIONS oldGuardAction, // Guard Action for Too Old State
|
|
double criticalFactor, // Critical DrawDown Factor based On Balance
|
|
ENUM_X_GUARD_ACTIONS criticalAction, // Guard Action for Critical State
|
|
double maxFactor, // Max DrawDown Factor based on Balance
|
|
ENUM_X_GUARD_ACTIONS maxAction, // Guard Action for Max State
|
|
int GuardStopoutFiredAfterTouches, // Guard Stopout if Max State touched
|
|
ENUM_X_GUARD_SUPPORT_METHODS supportMethod // How to Support Signals
|
|
)
|
|
{
|
|
//
|
|
mSupportPrefix = "SPT";
|
|
mDirectional = "SPD";
|
|
mInDirectional = "SPIND";
|
|
|
|
//
|
|
mOldAge = oldAge;
|
|
mOldGuardAction = oldGuardAction;
|
|
|
|
//
|
|
mMaxFactor = maxFactor;
|
|
mMaxAction = maxAction;
|
|
mCriticalFactor = criticalFactor;
|
|
mCriticalAction = criticalAction;
|
|
mGuardStopoutFiredAfterTouches = GuardStopoutFiredAfterTouches;
|
|
|
|
//
|
|
mSupportMethod = supportMethod;
|
|
|
|
//
|
|
mAccountInfo = new XCAccountInfo();
|
|
|
|
//
|
|
double deposit = mAccountInfo.GetInitialBalance();
|
|
|
|
//
|
|
if (
|
|
maxFactor > 0 &&
|
|
maxFactor < 1)
|
|
{
|
|
mMaxValue = maxFactor * deposit;
|
|
}
|
|
|
|
//
|
|
if (
|
|
criticalFactor > 0 &&
|
|
criticalFactor < 1)
|
|
{
|
|
mCriticalValue = criticalFactor * deposit;
|
|
}
|
|
|
|
//
|
|
Reset();
|
|
ResetMaxDrawDownTouches();
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
~XCGuard() {}
|
|
|
|
//
|
|
// Reset List ...
|
|
void Reset()
|
|
{
|
|
//
|
|
CleanBuffer(mGuardedSignals);
|
|
CleanBuffer(mSupportedSignals);
|
|
}
|
|
|
|
//
|
|
// Reset Consecutive Max DrawDown Touches ...
|
|
void ResetMaxDrawDownTouches()
|
|
{
|
|
mOccuredconsecutiveMaxDraDownTouches = 0;
|
|
}
|
|
|
|
//
|
|
// Calculate and Retrieve Critical DrawDown Value ...
|
|
double GetCriticalValue()
|
|
{
|
|
//
|
|
double result = mCriticalValue;
|
|
if (mCriticalFactor <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double deposit = mAccountInfo.GetInitialBalance();
|
|
double balance = mAccountInfo.GetBalance();
|
|
|
|
//
|
|
if (balance > deposit)
|
|
{
|
|
//
|
|
double balanceBasedCriticalDrawDown = mCriticalFactor * balance;
|
|
|
|
//
|
|
result = MathMax(
|
|
mCriticalValue,
|
|
balanceBasedCriticalDrawDown);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate and Retrieve Max Allowed DrawDown Value ...
|
|
double GetMaxValue()
|
|
{
|
|
//
|
|
double result = mMaxValue;
|
|
if (mMaxValue <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double deposit = mAccountInfo.GetInitialBalance();
|
|
double balance = mAccountInfo.GetBalance();
|
|
|
|
//
|
|
if (balance > deposit)
|
|
{
|
|
//
|
|
double balanceBasedMaxAllwedDrawDown = mMaxFactor * balance;
|
|
|
|
//
|
|
result = MathMax(
|
|
mMaxValue,
|
|
balanceBasedMaxAllwedDrawDown);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Guard Account Protection ...
|
|
bool Guard(
|
|
XGuardHandlerResult &guardResult, // Hold's Message
|
|
string &message, // Hold's Message
|
|
XCTrade &trader, // Specified Trader
|
|
ENUM_TIMEFRAMES period = NULL // Specified Time Frame for Detecting Signal Ages
|
|
)
|
|
{
|
|
//
|
|
message = "";
|
|
|
|
//
|
|
// Clean Result of Guard ...
|
|
CleanBuffer(guardResult.olds);
|
|
CleanBuffer(guardResult.maxes);
|
|
CleanBuffer(guardResult.criticals);
|
|
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// So Important is to Guard DrawDowns ...
|
|
result = GuardDrawDown(
|
|
guardResult,
|
|
message,
|
|
trader);
|
|
if (result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Then we have to Guard Old Trades ...
|
|
result = GuardOlds(
|
|
guardResult,
|
|
message,
|
|
trader);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Handle Guard Old Trades ...
|
|
bool GuardOlds(
|
|
XGuardHandlerResult &guardResult, // Hold's Message
|
|
string &message, // Hold's Message
|
|
XCTrade &trader, // Specified Trader
|
|
ENUM_TIMEFRAMES period = NULL // Specified Time Frame for Detecting Signal Ages
|
|
)
|
|
{
|
|
//
|
|
// Cleanup Message ...
|
|
message = "";
|
|
|
|
//
|
|
// Clean Result of Guard ...
|
|
CleanBuffer(guardResult.olds);
|
|
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
mOldAge <= 0 ||
|
|
mOldGuardAction == X_GUARD_DO_NOTHING)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Normalize Args ...
|
|
if (period == NULL)
|
|
{
|
|
period = _Period;
|
|
}
|
|
|
|
//
|
|
// Retrieve Old Trades ...
|
|
XSignal oldSignals[];
|
|
trader.GetOldSignals(
|
|
oldSignals,
|
|
mOldAge,
|
|
period);
|
|
int oldSignalsCount = ArraySize(oldSignals);
|
|
if (oldSignalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = DoGuardAction(
|
|
guardResult,
|
|
message,
|
|
trader,
|
|
mOldGuardAction,
|
|
X_GUARDED_OLD, // Specify Guard Reason
|
|
period // Specified Running Time Frame for Signal Age Calculations
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Hnadle Guard DrawDown ...
|
|
bool GuardDrawDown(
|
|
XGuardHandlerResult &guardResult, // Hold's Message
|
|
string &message, // Hold's Message
|
|
XCTrade &trader // Specified Trader
|
|
)
|
|
{
|
|
//
|
|
message = "";
|
|
|
|
//
|
|
// Clean Result of Guard ...
|
|
CleanBuffer(guardResult.maxes);
|
|
CleanBuffer(guardResult.criticals);
|
|
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check State for Guard ...
|
|
bool isCriticalGuardEnabled = mCriticalFactor > 0;
|
|
bool isMaxGuardEnabled = mMaxFactor > 0;
|
|
bool isGuardEnabled =
|
|
isMaxGuardEnabled;
|
|
|
|
//
|
|
if (!isGuardEnabled)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double maxValue = GetMaxValue();
|
|
double criticalValue = GetCriticalValue();
|
|
|
|
//
|
|
double profitSums = trader.GetSignalsProfit();
|
|
|
|
//
|
|
bool isMaxReached =
|
|
profitSums < 0 &&
|
|
isMaxGuardEnabled &&
|
|
MathAbs(profitSums) >= maxValue;
|
|
bool isCriticalReached =
|
|
profitSums < 0 &&
|
|
isCriticalGuardEnabled &&
|
|
MathAbs(profitSums) >= criticalValue;
|
|
|
|
//
|
|
if (!isMaxReached && !isCriticalReached)
|
|
{
|
|
//
|
|
// Reset Consecutive Max DrawDown Touches ...
|
|
ResetMaxDrawDownTouches();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check For Critical Guard ...
|
|
if (isCriticalReached && !isMaxReached)
|
|
{
|
|
//
|
|
result = DoGuardAction(
|
|
guardResult,
|
|
message,
|
|
trader,
|
|
mCriticalAction,
|
|
X_GUARDED_CRITICAL);
|
|
}
|
|
else if (isMaxReached)
|
|
{
|
|
//
|
|
result = DoGuardAction(
|
|
guardResult,
|
|
message,
|
|
trader,
|
|
mMaxAction,
|
|
X_GUARDED_MAX);
|
|
|
|
//
|
|
// Handle Guard StopOut Action ...
|
|
mOccuredconsecutiveMaxDraDownTouches++;
|
|
if (
|
|
mMaxAction != X_GUARD_DO_NOTHING &&
|
|
mMaxAction != X_GUARD_CLOSE_ALL &&
|
|
mGuardStopoutFiredAfterTouches > 0 &&
|
|
mOccuredconsecutiveMaxDraDownTouches >= mGuardStopoutFiredAfterTouches)
|
|
{
|
|
//
|
|
XSignal closed[];
|
|
trader.CloseSignals(closed);
|
|
int closedCount = ArraySize(closed);
|
|
if (closedCount > 0)
|
|
{
|
|
//
|
|
for (int i = 0; i < closedCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = closed[i];
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
iSignal,
|
|
guardResult.maxes);
|
|
}
|
|
|
|
//
|
|
message = "Guard StopOut Action Closed (" + (string)closedCount + ") Signals and Release Account ...";
|
|
|
|
//
|
|
guardResult.isStopOut = true;
|
|
|
|
//
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Do Signal Protections and apply Support ...
|
|
bool ProtectSignals(
|
|
XSignal &mResult[], // Hold's Result
|
|
XCTrade &trader, // Specified Trader
|
|
int numberOfItemsCheck = 30 // Number of Items to check
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
CleanBuffer(mResult);
|
|
|
|
//
|
|
// Check Supports Enabled ...
|
|
result = IsSupportEnabled();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Update Supports ...
|
|
XSignal signals[];
|
|
trader.GetSignals(signals);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount > 0)
|
|
{
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
bool isExists = IsSupportExists(iSignal.ticket);
|
|
if (isExists)
|
|
{
|
|
UpdateSupport(iSignal);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Count Supporteds ...
|
|
XProtectedSignal supporteds[];
|
|
GetAllSupports(supporteds);
|
|
int supportedCount = ArraySize(supporteds);
|
|
result = supportedCount > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Loop through ...
|
|
for (int i = 0; i < supportedCount; i++)
|
|
{
|
|
//
|
|
XProtectedSignal iP = supporteds[i];
|
|
|
|
//
|
|
bool canDoDirectional = CanDoDirectionalSupport(
|
|
trader,
|
|
iP.ticket,
|
|
numberOfItemsCheck);
|
|
bool canDoInDirectional = CanDoInDirectionalSupport(
|
|
trader,
|
|
iP.ticket,
|
|
numberOfItemsCheck);
|
|
|
|
//
|
|
if (
|
|
!canDoDirectional &&
|
|
!canDoInDirectional)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Do Directional ...
|
|
if (canDoDirectional)
|
|
{
|
|
//
|
|
// Generate Propper Signal ...
|
|
XSignal sdSignal = GenerateDirectionalSupportSignal(
|
|
iP.ticket,
|
|
trader);
|
|
|
|
//
|
|
ENUM_EXECUTION_SIGNAL_RESULT executionResult = trader.ExecuteSignal(
|
|
sdSignal,
|
|
true,
|
|
false);
|
|
bool isExecuted = executionResult == X_TRADER_SUCCEED_EXECUTION;
|
|
if (isExecuted)
|
|
{
|
|
//
|
|
XSignal eSignal = trader.GetSignal(sdSignal.ticket);
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
eSignal,
|
|
mResult);
|
|
}
|
|
}
|
|
//
|
|
// Do Indirectional ...
|
|
else if (canDoInDirectional)
|
|
{
|
|
//
|
|
// Generate Propper Signal ...
|
|
XSignal sindSignal = GenerateDirectionalSupportSignal(
|
|
iP.ticket,
|
|
trader);
|
|
|
|
//
|
|
ENUM_EXECUTION_SIGNAL_RESULT executionResult = trader.ExecuteSignal(
|
|
sindSignal,
|
|
true,
|
|
false);
|
|
bool isExecuted = executionResult == X_TRADER_SUCCEED_EXECUTION;
|
|
if (isExecuted)
|
|
{
|
|
//
|
|
XSignal eSignal = trader.GetSignal(sindSignal.ticket);
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
eSignal,
|
|
mResult);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
result = ArraySize(mResult) > 0;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Validate State ...
|
|
bool Validate()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
mMaxFactor > 0 &&
|
|
(mCriticalFactor > 0
|
|
? mCriticalFactor < mMaxFactor
|
|
: true);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// START Guarded Signals Functions ...
|
|
//
|
|
|
|
//
|
|
// Add new Protecte Signal ...
|
|
bool Add(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
bool isExists = IsExists(signal.ticket);
|
|
if (isExists)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XProtectedSignal item;
|
|
|
|
//
|
|
datetime time = TimeCurrent();
|
|
|
|
//
|
|
item.ticket = signal.ticket;
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
time,
|
|
item.time);
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
signal.profit,
|
|
item.profit);
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
item,
|
|
mGuardedSignals);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get All ...
|
|
void GetAll(
|
|
XProtectedSignal &result[] // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
CopyBuffer(
|
|
mGuardedSignals,
|
|
result);
|
|
}
|
|
|
|
//
|
|
// Get ...
|
|
XProtectedSignal Get(
|
|
ulong ticket // Specified Signal Ticket
|
|
)
|
|
{
|
|
//
|
|
XProtectedSignal result;
|
|
|
|
//
|
|
bool isExists = IsExists(ticket);
|
|
if (!isExists)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int protectedCounts = ArraySize(mGuardedSignals);
|
|
if (protectedCounts <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < protectedCounts; i++)
|
|
{
|
|
//
|
|
XProtectedSignal iProtected = mGuardedSignals[i];
|
|
|
|
//
|
|
if (iProtected.ticket == ticket)
|
|
{
|
|
//
|
|
result = iProtected;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Remove ...
|
|
bool Remove(
|
|
ulong ticket // Specified Signal Ticket
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = IsExists(ticket);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int protectedCounts = ArraySize(mGuardedSignals);
|
|
result = protectedCounts > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XProtectedSignal cleared[];
|
|
for (int i = 0; i < protectedCounts; i++)
|
|
{
|
|
//
|
|
XProtectedSignal iProtected = mGuardedSignals[i];
|
|
|
|
//
|
|
if (iProtected.ticket != ticket)
|
|
{
|
|
AddItemToBuffer(
|
|
iProtected,
|
|
cleared);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update Source by Cleared ...
|
|
CopyBuffer(
|
|
cleared,
|
|
mGuardedSignals);
|
|
|
|
//
|
|
result = true;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Update ...
|
|
bool Update(
|
|
ulong ticket, // Specified Signal Ticket
|
|
datetime time, // Specified Last Checked time
|
|
double profit // Specified Signal Profit
|
|
// XProtectedSignal &item // Specified which object to Update
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int protectedCounts = ArraySize(mGuardedSignals);
|
|
result = protectedCounts > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = IsExists(ticket);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Struct ...
|
|
XProtectedSignal item = Get(ticket);
|
|
AddItemToBuffer(
|
|
time,
|
|
item.time);
|
|
AddItemToBuffer(
|
|
profit,
|
|
item.profit);
|
|
|
|
//
|
|
result = Remove(ticket);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
item,
|
|
mGuardedSignals);
|
|
result = true;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
bool Update(
|
|
XProtectedSignal &item // Specified Protected Item
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
datetime time;
|
|
result = GetLastItem(
|
|
time,
|
|
item.time);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double profit;
|
|
result = GetLastItem(
|
|
profit,
|
|
item.profit);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = Update(
|
|
item.ticket, time, profit);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
bool Update(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ulong ticket = signal.ticket;
|
|
datetime time = TimeCurrent();
|
|
double profit = signal.profit;
|
|
|
|
//
|
|
result = Update(
|
|
ticket,
|
|
time,
|
|
profit);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Add Or Update ...
|
|
bool AddOrUpdate(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
bool isExists = IsExists(signal.ticket);
|
|
if (!isExists)
|
|
{
|
|
result = Add(signal);
|
|
}
|
|
else
|
|
{
|
|
result = Update(signal);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Exists ...
|
|
bool IsExists(
|
|
ulong ticket // Specified Signal Ticket
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int protectedCounts = ArraySize(mGuardedSignals);
|
|
if (protectedCounts <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < protectedCounts; i++)
|
|
{
|
|
//
|
|
XProtectedSignal iProtected = mGuardedSignals[i];
|
|
|
|
//
|
|
if (iProtected.ticket == ticket)
|
|
{
|
|
//
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Count Protecteds ...
|
|
int Count()
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
result = ArraySize(mGuardedSignals);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Guarded Signals Functions ...
|
|
//
|
|
|
|
//
|
|
// START Supported Signals Functions ...
|
|
//
|
|
|
|
//
|
|
// Check can Handle Support Signals or not ...
|
|
bool IsSupportEnabled()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
mSupportMethod != X_SUPPORT_DO_NOTHING;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve List Of Supported Signals ...
|
|
void GetSupportSignals(
|
|
XSignal &result[], // Hold's Result ...
|
|
XCTrade &trader // Specified Trader class Instance ...
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
trader.GetSignals(signals);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = -0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
bool isSupport = IsSupport(iSignal.comment);
|
|
if (isSupport)
|
|
{
|
|
AddItemToBuffer(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Retrieve List Of Specific Signals Supported Signals ...
|
|
void GetSignalSupports(
|
|
XSignal &result[], // Hold's Result ...
|
|
XCTrade &trader, // Specified Trader class Instance ...
|
|
ulong ticket // Specified Signal Ticket ...
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (ticket <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check Signal Exists using Trader ...
|
|
XSignal parentSignal = trader.GetSignal(ticket);
|
|
bool isValid =
|
|
parentSignal.ticket > 0 &&
|
|
parentSignal.ticket == ticket &&
|
|
StringLen(parentSignal.comment) > 0;
|
|
if (!isValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
XSignal supportSignals[];
|
|
GetSupportSignals(
|
|
supportSignals,
|
|
trader);
|
|
int supportSignalsCount = ArraySize(supportSignals);
|
|
if (supportSignalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < supportSignalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = supportSignals[i];
|
|
|
|
//
|
|
bool isBelongsToParent = IsBelongsToParent(
|
|
ticket,
|
|
iSignal.comment);
|
|
if (isBelongsToParent)
|
|
{
|
|
AddItemToBuffer(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Specific Support Conditions for Opening Directional Support ...
|
|
bool CanDoDirectionalSupport(
|
|
XCTrade &trader, // Specified Trader class Instance ...
|
|
ulong ticket, // Specified Signal Ticket ...
|
|
int numberOfItemsCheck = 30 // Number of Items to check ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check Supports Enabled ...
|
|
result = IsSupportEnabled();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Support Method Allows Directional ...
|
|
result =
|
|
mSupportMethod == X_SUPPORT_FULL ||
|
|
mSupportMethod == X_SUPPORT_IN_DRAWUP_DIRECTIONAL;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Validate Ticket ...
|
|
result = ticket > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Supports Count ...
|
|
int supportedCount = ArraySize(mSupportedSignals);
|
|
result = supportedCount > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Ticket Exists in Supports ...
|
|
result = IsSupportExists(ticket);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check signal Doesn't have any Support ...
|
|
XSignal supports[];
|
|
GetSignalSupports(
|
|
supports,
|
|
trader,
|
|
ticket);
|
|
int supportsCount = ArraySize(supports);
|
|
result = supportsCount <= 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Support Model ...
|
|
XProtectedSignal sInfo = GetSupport(ticket);
|
|
|
|
//
|
|
// Validate Model ...
|
|
int sTimesCount = ArraySize(sInfo.time);
|
|
int sProfitsCount = ArraySize(sInfo.profit);
|
|
result =
|
|
sInfo.ticket == ticket &&
|
|
sTimesCount == sProfitsCount &&
|
|
sTimesCount >= numberOfItemsCheck &&
|
|
sProfitsCount >= numberOfItemsCheck;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Validate Signal Age ...
|
|
XSignal sSignal = trader.GetSignal(ticket);
|
|
int sSignalAge = trader.GetSignalAge(sSignal.time);
|
|
result = sSignalAge >= numberOfItemsCheck;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extrat Profits ...
|
|
double lastProfits[];
|
|
for (int i = sProfitsCount - 1; i >= (sProfitsCount - numberOfItemsCheck); i++)
|
|
{
|
|
//
|
|
double iProfit = sInfo.profit[i];
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
iProfit,
|
|
lastProfits);
|
|
}
|
|
|
|
//
|
|
string logMsg = ToString(lastProfits);
|
|
LogMessage("" + (string)ticket + " lastProfits: \n" + logMsg);
|
|
|
|
//
|
|
result = false;
|
|
|
|
//
|
|
// Now implement Business Logics ...
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Specific Support Conditions for Opening InDirectional Support ...
|
|
bool CanDoInDirectionalSupport(
|
|
XCTrade &trader, // Specified Trader class Instance ...
|
|
ulong ticket, // Specified Signal Ticket ...
|
|
int numberOfItemsCheck = 30 // Number of Items to check ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check Supports Enabled ...
|
|
result = IsSupportEnabled();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Support Method Allows Directional ...
|
|
result =
|
|
mSupportMethod == X_SUPPORT_FULL ||
|
|
mSupportMethod == X_SUPPORT_IN_DRAWDOWN_INDIRECTIONAL;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Validate Ticket ...
|
|
result = ticket > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Supports Count ...
|
|
int supportedCount = ArraySize(mSupportedSignals);
|
|
result = supportedCount > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Ticket Exists in Supports ...
|
|
result = IsSupportExists(ticket);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check signal Doesn't have any Support ...
|
|
XSignal supports[];
|
|
GetSignalSupports(
|
|
supports,
|
|
trader,
|
|
ticket);
|
|
int supportsCount = ArraySize(supports);
|
|
result = supportsCount <= 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Support Model ...
|
|
XProtectedSignal sInfo = GetSupport(ticket);
|
|
|
|
//
|
|
// Validate Model ...
|
|
int sTimesCount = ArraySize(sInfo.time);
|
|
int sProfitsCount = ArraySize(sInfo.profit);
|
|
result =
|
|
sInfo.ticket == ticket &&
|
|
sTimesCount == sProfitsCount &&
|
|
sTimesCount >= numberOfItemsCheck &&
|
|
sProfitsCount >= numberOfItemsCheck;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Validate Signal Age ...
|
|
XSignal sSignal = trader.GetSignal(ticket);
|
|
int sSignalAge = trader.GetSignalAge(sSignal.time);
|
|
result = sSignalAge >= numberOfItemsCheck;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extrat Profits ...
|
|
double lastProfits[];
|
|
for (int i = sProfitsCount - 1; i >= (sProfitsCount - numberOfItemsCheck); i++)
|
|
{
|
|
//
|
|
double iProfit = sInfo.profit[i];
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
iProfit,
|
|
lastProfits);
|
|
}
|
|
|
|
//
|
|
string logMsg = ToString(lastProfits);
|
|
LogMessage("" + (string)ticket + " lastProfits: \n" + logMsg);
|
|
|
|
//
|
|
result = false;
|
|
|
|
//
|
|
// Now implement Business Logics ...
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Directional Support Signal for Parent Signal ...
|
|
XSignal GenerateDirectionalSupportSignal(
|
|
ulong ticket, // Specified Parent Signal Ticket ...
|
|
XCTrade &trader // Specified Trader Class ...
|
|
)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XSignal pSignal = trader.GetSignal(ticket);
|
|
bool isValidParent =
|
|
pSignal.ticket > 0 &&
|
|
pSignal.ticket == ticket &&
|
|
StringLen(pSignal.comment) > 0;
|
|
if (!isValidParent)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ENUM_X_SIGNAL_TYPE type = pSignal.type;
|
|
bool isLong = IsLong(type);
|
|
|
|
//
|
|
double entry = GetEntry(
|
|
pSignal.symbol,
|
|
type);
|
|
double exit = GetExit(
|
|
pSignal.symbol,
|
|
type);
|
|
|
|
//
|
|
double reward = PipsToPrice(
|
|
pSignal.symbol,
|
|
6);
|
|
double risk = PipsToPrice(
|
|
pSignal.symbol,
|
|
1);
|
|
|
|
//
|
|
double tp =
|
|
isLong
|
|
? entry + reward
|
|
: entry - reward;
|
|
|
|
//
|
|
double sl =
|
|
isLong
|
|
? exit - risk
|
|
: exit + risk;
|
|
|
|
//
|
|
double rfStep = 2;
|
|
double rfRate = 0.5;
|
|
|
|
//
|
|
double volume = NormalizeVolume(
|
|
pSignal.volume / 2,
|
|
pSignal.symbol,
|
|
2);
|
|
|
|
//
|
|
// Generate Signal using Trader Class Instance ...
|
|
result = trader.GenerateSignal(
|
|
//
|
|
type,
|
|
tp,
|
|
sl,
|
|
volume,
|
|
rfStep,
|
|
rfRate
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate InDirectional Support Signal for Parent Signal ...
|
|
XSignal GenerateInDirectionalSupportSignal(
|
|
ulong ticket, // Specified Parent Signal Ticket ...
|
|
XCTrade &trader // Specified Trader Class ...
|
|
)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XSignal pSignal = trader.GetSignal(ticket);
|
|
bool isValidParent =
|
|
pSignal.ticket > 0 &&
|
|
pSignal.ticket == ticket &&
|
|
StringLen(pSignal.comment) > 0;
|
|
if (!isValidParent)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ENUM_X_SIGNAL_TYPE type = trader
|
|
.GetIndirectionSignalType(pSignal.type);
|
|
bool isLong = IsLong(type);
|
|
|
|
//
|
|
double entry = GetEntry(
|
|
pSignal.symbol,
|
|
type);
|
|
double exit = GetExit(
|
|
pSignal.symbol,
|
|
type);
|
|
|
|
//
|
|
double reward = PipsToPrice(
|
|
pSignal.symbol,
|
|
6);
|
|
double risk = PipsToPrice(
|
|
pSignal.symbol,
|
|
1);
|
|
|
|
//
|
|
double tp =
|
|
isLong
|
|
? entry + reward
|
|
: entry - reward;
|
|
|
|
//
|
|
double sl =
|
|
isLong
|
|
? exit - risk
|
|
: exit + risk;
|
|
|
|
//
|
|
double rfStep = 2;
|
|
double rfRate = 0.5;
|
|
|
|
//
|
|
double volume = NormalizeVolume(
|
|
pSignal.volume / 2,
|
|
pSignal.symbol,
|
|
2);
|
|
|
|
//
|
|
// Generate Signal using Trader Class Instance ...
|
|
result = trader.GenerateSignal(
|
|
//
|
|
type,
|
|
tp,
|
|
sl,
|
|
volume,
|
|
rfStep,
|
|
rfRate
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Specific Signals Support Signal Comment ...
|
|
string GenerateSupportSignalComment(
|
|
ulong ticket, // Specified Parent Signal Ticket
|
|
ENUM_X_SIGNAL_TYPE type, // Specified Support Signal Type
|
|
bool isInDirectional = false // Specified Support Signal Direction
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
if (ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string typeString = GetSignalType(type);
|
|
string directionStr = GenerateSupportDirection(isInDirectional);
|
|
|
|
//
|
|
result =
|
|
mSupportPrefix + "(" + (string)ticket + ")_" +
|
|
X_SIGNAL_TYPE + "(" + typeString + ")_" +
|
|
directionStr;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get Support Direction String Representation ...
|
|
string GenerateSupportDirection(
|
|
bool isInDirectional = false // Specified Support Signal Direction
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
result =
|
|
isInDirectional
|
|
? mInDirectional
|
|
: mDirectional;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check an String Is Directional Or not ...
|
|
bool IsDirectionalSupport(
|
|
string comment // Specified Comment ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = IsSupport(comment);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int sPos = StringFind(
|
|
comment,
|
|
mDirectional);
|
|
result = sPos >= 0;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check an String Is In Directional Or not ...
|
|
bool IsInDirectionalSupport(
|
|
string comment // Specified Comment ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = IsSupport(comment);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int sPos = StringFind(
|
|
comment,
|
|
mInDirectional);
|
|
result = sPos >= 0;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check an String Is Support or not ...
|
|
bool IsSupport(
|
|
string comment // Specified Comment ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int sPos = StringFind(
|
|
comment,
|
|
mSupportPrefix);
|
|
result = sPos >= 0;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extract Parent of Support Signal Ticket from a comment ...
|
|
ulong ExtractParentTicket(
|
|
string comment // Specified Comment ...
|
|
)
|
|
{
|
|
//
|
|
ulong result = 0;
|
|
|
|
//
|
|
bool isSupport = IsSupport(comment);
|
|
if (!isSupport)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// TICKET ...
|
|
string ticketStr = ExtractString(
|
|
comment,
|
|
mSupportPrefix + "(",
|
|
")");
|
|
if (StringLen(ticketStr) > 0)
|
|
{
|
|
result = (ulong)ticketStr;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check a Comment belongs to Support Signal or not ...
|
|
bool IsBelongsToParent(
|
|
ulong ticket, // Specified Parent Signal Ticket
|
|
string comment // Specified Support Signal Comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = IsSupport(comment);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ulong spTicket = ExtractParentTicket(comment);
|
|
result = spTicket == ticket;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Add new Protecte Signal ...
|
|
bool AddSupport(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
bool isExists = IsSupportExists(signal.ticket);
|
|
if (isExists)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XProtectedSignal item;
|
|
|
|
//
|
|
datetime time = TimeCurrent();
|
|
|
|
//
|
|
item.ticket = signal.ticket;
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
time,
|
|
item.time);
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
signal.profit,
|
|
item.profit);
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
item,
|
|
mSupportedSignals);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get All ...
|
|
void GetAllSupports(
|
|
XProtectedSignal &result[] // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
CopyBuffer(
|
|
mSupportedSignals,
|
|
result);
|
|
}
|
|
|
|
//
|
|
// Get ...
|
|
XProtectedSignal GetSupport(
|
|
ulong ticket // Specified Signal Ticket
|
|
)
|
|
{
|
|
//
|
|
XProtectedSignal result;
|
|
|
|
//
|
|
bool isExists = IsSupportExists(ticket);
|
|
if (!isExists)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int protectedCounts = ArraySize(mSupportedSignals);
|
|
if (protectedCounts <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < protectedCounts; i++)
|
|
{
|
|
//
|
|
XProtectedSignal iProtected = mSupportedSignals[i];
|
|
|
|
//
|
|
if (iProtected.ticket == ticket)
|
|
{
|
|
//
|
|
result = iProtected;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Remove ...
|
|
bool RemoveSupport(
|
|
ulong ticket // Specified Signal Ticket
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = IsSupportExists(ticket);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int protectedCounts = ArraySize(mSupportedSignals);
|
|
result = protectedCounts > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XProtectedSignal cleared[];
|
|
for (int i = 0; i < protectedCounts; i++)
|
|
{
|
|
//
|
|
XProtectedSignal iProtected = mSupportedSignals[i];
|
|
|
|
//
|
|
if (iProtected.ticket != ticket)
|
|
{
|
|
AddItemToBuffer(
|
|
iProtected,
|
|
cleared);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update Source by Cleared ...
|
|
CopyBuffer(
|
|
cleared,
|
|
mSupportedSignals);
|
|
|
|
//
|
|
result = true;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Update ...
|
|
bool UpdateSupport(
|
|
ulong ticket, // Specified Signal Ticket
|
|
datetime time, // Specified Last Checked time
|
|
double profit // Specified Signal Profit
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int protectedCounts = ArraySize(mSupportedSignals);
|
|
result = protectedCounts > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = IsSupportExists(ticket);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Struct ...
|
|
XProtectedSignal item = GetSupport(ticket);
|
|
AddItemToBuffer(
|
|
time,
|
|
item.time);
|
|
AddItemToBuffer(
|
|
profit,
|
|
item.profit);
|
|
|
|
//
|
|
result = RemoveSupport(ticket);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
AddItemToBuffer(
|
|
item,
|
|
mSupportedSignals);
|
|
result = true;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
bool UpdateSupport(
|
|
XProtectedSignal &item // Specified Protected Item
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
datetime time;
|
|
result = GetLastItem(
|
|
time,
|
|
item.time);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double profit;
|
|
result = GetLastItem(
|
|
profit,
|
|
item.profit);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = UpdateSupport(
|
|
item.ticket, time, profit);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
bool UpdateSupport(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ulong ticket = signal.ticket;
|
|
datetime time = TimeCurrent();
|
|
double profit = signal.profit;
|
|
|
|
//
|
|
result = UpdateSupport(
|
|
ticket,
|
|
time,
|
|
profit);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Add Or Update ...
|
|
bool AddOrUpdateSupport(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
bool isExists = IsSupportExists(signal.ticket);
|
|
if (!isExists)
|
|
{
|
|
result = Add(signal);
|
|
}
|
|
else
|
|
{
|
|
result = Update(signal);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Exists ...
|
|
bool IsSupportExists(
|
|
ulong ticket // Specified Signal Ticket
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int protectedCounts = ArraySize(mSupportedSignals);
|
|
if (protectedCounts <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < protectedCounts; i++)
|
|
{
|
|
//
|
|
XProtectedSignal iProtected = mSupportedSignals[i];
|
|
|
|
//
|
|
if (iProtected.ticket == ticket)
|
|
{
|
|
//
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Count Protecteds ...
|
|
int CountSupports()
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
result = ArraySize(mSupportedSignals);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Suppoprted Signals Functions ...
|
|
//
|
|
|
|
//
|
|
// START Getter/Setter(s) ...
|
|
//
|
|
|
|
//
|
|
int GetOldAge()
|
|
{
|
|
return mOldAge;
|
|
}
|
|
|
|
//
|
|
bool SetOldAge(int value)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
value > 0 &&
|
|
mOldAge != value;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
mOldAge = value;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ENUM_X_GUARD_ACTIONS GetOldGuardAction()
|
|
{
|
|
return mOldGuardAction;
|
|
}
|
|
|
|
//
|
|
bool SetOldGuardAction(ENUM_X_GUARD_ACTIONS value)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = value != mOldGuardAction;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
mOldGuardAction = value;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double GetCriticalFactor()
|
|
{
|
|
return mCriticalFactor;
|
|
}
|
|
|
|
//
|
|
bool SetCriticalFactor(bool value)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
value > 0 &&
|
|
value < 1 &&
|
|
mCriticalFactor != value;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
mCriticalFactor = value;
|
|
|
|
//
|
|
double deposit = mAccountInfo.GetInitialBalance();
|
|
mCriticalValue = deposit * value;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ENUM_X_GUARD_ACTIONS GetCriticalAction()
|
|
{
|
|
return mCriticalAction;
|
|
}
|
|
|
|
//
|
|
bool SetCriticalAction(ENUM_X_GUARD_ACTIONS value)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
value != mCriticalAction;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
mCriticalAction = value;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double GetMaxFactor()
|
|
{
|
|
return mMaxFactor;
|
|
}
|
|
|
|
//
|
|
bool SetMaxFactor(bool value)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
value > 0 &&
|
|
value < 1 &&
|
|
mMaxFactor != value;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
mMaxFactor = value;
|
|
|
|
//
|
|
double deposit = mAccountInfo.GetInitialBalance();
|
|
mMaxValue = deposit * value;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ENUM_X_GUARD_ACTIONS GetMaxAction()
|
|
{
|
|
return mMaxAction;
|
|
}
|
|
|
|
//
|
|
bool SetMaxAction(ENUM_X_GUARD_ACTIONS value)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
value != mMaxAction;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
mMaxAction = value;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ENUM_X_GUARD_SUPPORT_METHODS GetSupportMethod()
|
|
{
|
|
return mSupportMethod;
|
|
}
|
|
|
|
//
|
|
bool SetSupportMethod(ENUM_X_GUARD_SUPPORT_METHODS value)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result =
|
|
value != mSupportMethod;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
mSupportMethod = value;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Getter/Setter(s) ...
|
|
//
|
|
|
|
//
|
|
// Generate Signal Guard Attacked Message ...
|
|
string GenerateGuardedSignalComment(
|
|
XSignal &signal, // Specified Signal
|
|
ENUM_X_GUARD_REASONS reason // Specified Guard Reason
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
string reasonMessage =
|
|
(
|
|
//
|
|
reason == X_GUARDED_OLD
|
|
? "Because of Old Issue"
|
|
: reason == X_GUARDED_CRITICAL
|
|
? "Because Of Crtical Drawdown Isuue"
|
|
: reason == X_GUARDED_MAX
|
|
? "Because of Max Drawdown Issue"
|
|
: ""
|
|
//
|
|
) +
|
|
" On Profit: " + (string)signal.profit;
|
|
|
|
//
|
|
result += "Guard Attacked (" + (string)signal.ticket + "), " + reasonMessage + " ...";
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
string mSupportPrefix;
|
|
string mDirectional;
|
|
string mInDirectional;
|
|
|
|
//
|
|
XProtectedSignal mGuardedSignals[];
|
|
XProtectedSignal mSupportedSignals[];
|
|
|
|
//
|
|
int mOldAge;
|
|
ENUM_X_GUARD_ACTIONS mOldGuardAction;
|
|
|
|
//
|
|
// Critical DrawDown Factor based On Balance ...
|
|
double mCriticalFactor;
|
|
double mCriticalValue;
|
|
|
|
//
|
|
// Guard Action for Critical State ...
|
|
ENUM_X_GUARD_ACTIONS mCriticalAction;
|
|
|
|
//
|
|
// Max DrawDown Factor based on Balance ...
|
|
double mMaxFactor;
|
|
double mMaxValue;
|
|
//
|
|
// Guard Stopout if Max State touched ...
|
|
int mOccuredconsecutiveMaxDraDownTouches;
|
|
int mGuardStopoutFiredAfterTouches;
|
|
|
|
//
|
|
// Guard Action for Max State ...
|
|
ENUM_X_GUARD_ACTIONS mMaxAction;
|
|
|
|
//
|
|
// How to Support Signals ...
|
|
ENUM_X_GUARD_SUPPORT_METHODS mSupportMethod;
|
|
|
|
//
|
|
// Account Info Instance ...
|
|
XCAccountInfo mAccountInfo;
|
|
|
|
//
|
|
// Get Volume Multiplier for Specified Guard Action ...
|
|
double GetGuardActionVolumeMultiplier(
|
|
ENUM_X_GUARD_ACTIONS action = X_GUARD_DO_NOTHING // Specified Action
|
|
)
|
|
{
|
|
//
|
|
// Partial Close Max In Drawdown Trade ...
|
|
double result = 0.0;
|
|
if (action == X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_1)
|
|
{
|
|
result = 0.01;
|
|
}
|
|
else if (action == X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_2)
|
|
{
|
|
result = 0.02;
|
|
}
|
|
else if (action == X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_3)
|
|
{
|
|
result = 0.03;
|
|
}
|
|
else if (action == X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_5)
|
|
{
|
|
result = 0.05;
|
|
}
|
|
else if (action == X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_10)
|
|
{
|
|
result = 0.10;
|
|
}
|
|
else if (action == X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_15)
|
|
{
|
|
result = 0.15;
|
|
}
|
|
else if (action == X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_20)
|
|
{
|
|
result = 0.20;
|
|
}
|
|
else if (action == X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_25)
|
|
{
|
|
result = 0.25;
|
|
}
|
|
else if (action == X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_30)
|
|
{
|
|
result = 0.30;
|
|
}
|
|
else if (action == X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_40)
|
|
{
|
|
result = 0.40;
|
|
}
|
|
else if (action == X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_50)
|
|
{
|
|
result = 0.50;
|
|
}
|
|
else if (action == X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_60)
|
|
{
|
|
result = 0.60;
|
|
}
|
|
else if (action == X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_70)
|
|
{
|
|
result = 0.70;
|
|
}
|
|
else if (action == X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_75)
|
|
{
|
|
result = 0.75;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Do Specified Guard Action ...
|
|
bool DoGuardAction(
|
|
XGuardHandlerResult &guardResult, // Hold's Result ...
|
|
string &message, // Hold's Message ...
|
|
XCTrade &trader, // Specified Trader Class ...
|
|
ENUM_X_GUARD_ACTIONS action, // Specified Which Action to Do ...
|
|
ENUM_X_GUARD_REASONS reason, // Specified Guard Reason ...
|
|
ENUM_TIMEFRAMES period = NULL // Specified Time Frame for Detecting Signal Ages ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
message = "";
|
|
|
|
//
|
|
string reasonString =
|
|
reason == X_GUARDED_OLD
|
|
? "Because of Age Issue"
|
|
: "Because of DD " + (
|
|
//
|
|
reason == X_GUARDED_CRITICAL ? "Critical" : "Max"
|
|
//
|
|
) +
|
|
" Issue";
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (action == X_GUARD_DO_NOTHING)
|
|
{
|
|
//
|
|
message = "Max Allowed Drawdown Reached, but nothing Happens ...";
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Normalize Period ...
|
|
if (period == NULL)
|
|
{
|
|
period = _Period;
|
|
}
|
|
|
|
//
|
|
// Close All Signals ...
|
|
XSignal closed[];
|
|
XSignal maxInDDSignal;
|
|
|
|
//
|
|
// Handle Perform Guard Action ...
|
|
switch (action)
|
|
{
|
|
//
|
|
// Close All ...
|
|
case X_GUARD_CLOSE_ALL:
|
|
{
|
|
//
|
|
trader.CloseSignals(closed);
|
|
int closedCount = ArraySize(closed);
|
|
result = closedCount > 0;
|
|
if (!result)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
message = "Closed (" + (string)closedCount + ") Trades by Guard, " + reasonString + " ...";
|
|
}
|
|
break;
|
|
|
|
//
|
|
case X_GUARD_CLOSE_MAX_IN_DD:
|
|
case X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_1:
|
|
case X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_2:
|
|
case X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_3:
|
|
case X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_5:
|
|
case X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_10:
|
|
case X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_15:
|
|
case X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_20:
|
|
case X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_25:
|
|
case X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_30:
|
|
case X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_40:
|
|
case X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_50:
|
|
case X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_60:
|
|
case X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_70:
|
|
case X_GUARD_PARTIAL_CLOSE_MAX_IN_DD_75:
|
|
{
|
|
//
|
|
// Retrieve Max In Drawdown Trade ...
|
|
maxInDDSignal = trader.GetMaxInDrawdownSignal();
|
|
bool isValid = IsValid(maxInDDSignal, trader.GetMagicNumber()) && maxInDDSignal.ticket > 0;
|
|
if (!isValid)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Close Max In Drawdown Trade ...
|
|
if (action == X_GUARD_CLOSE_MAX_IN_DD)
|
|
{
|
|
//
|
|
string comment = "Signal (" + (string)maxInDDSignal.ticket + ") Closed by Guard ...";
|
|
result = trader.Close(
|
|
maxInDDSignal.ticket,
|
|
comment);
|
|
if (result)
|
|
{
|
|
message = comment;
|
|
}
|
|
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
int signalAge = 0;
|
|
bool canGuard = true;
|
|
bool isGuardedBefore = false;
|
|
|
|
//
|
|
// Do Some Additional Verifications if Guard for Olds Trades ...
|
|
if (reason == X_GUARDED_OLD)
|
|
{
|
|
//
|
|
// Retrieve Signal Age ...
|
|
signalAge = trader.GetSignalAge(
|
|
maxInDDSignal,
|
|
period);
|
|
|
|
//
|
|
// Check Guarded Before Or Not ...
|
|
isGuardedBefore = IsExists(maxInDDSignal.ticket);
|
|
if (isGuardedBefore)
|
|
{
|
|
//
|
|
// Now We Have to Check Signal Age growse required times to apply Guard Action again ...
|
|
// First Calculate Guard Grow Step ...
|
|
int ageGrowStep = mOldAge / 10;
|
|
|
|
//
|
|
XProtectedSignal guardItem = Get(maxInDDSignal.ticket);
|
|
datetime lastGuardTime = 0;
|
|
if (GetLastItem(lastGuardTime, guardItem.time))
|
|
{
|
|
//
|
|
int lastGuardIndex = iBarShift(
|
|
maxInDDSignal.symbol,
|
|
period,
|
|
lastGuardTime);
|
|
|
|
//
|
|
canGuard =
|
|
MathAbs(lastGuardIndex) >= ageGrowStep;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check Signal age ...
|
|
canGuard = signalAge >= mOldAge;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Prevent Moving Forward ...
|
|
if (!canGuard)
|
|
{
|
|
//
|
|
result = false;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Partial Close Max In Drawdown Trade ...
|
|
double volumeRate = GetGuardActionVolumeMultiplier(action);
|
|
|
|
//
|
|
double volume = NormalizeVolume(
|
|
maxInDDSignal.volume * volumeRate,
|
|
maxInDDSignal.symbol);
|
|
|
|
//
|
|
string commentPrefix = "Signal (" + (string)maxInDDSignal.ticket + ") Partial Closed (" + (string)volumeRate + ") by Guard";
|
|
string comment = commentPrefix + " ...";
|
|
|
|
//
|
|
result = trader.ClosePartial(
|
|
maxInDDSignal.ticket,
|
|
volume,
|
|
comment);
|
|
if (result)
|
|
{
|
|
//
|
|
reasonString =
|
|
reasonString +
|
|
(reason == X_GUARDED_OLD
|
|
? " (" + (string)signalAge + ")"
|
|
: " (" + (string)maxInDDSignal.profit + ")");
|
|
|
|
//
|
|
// Update Guarded Signals ...
|
|
if (isGuardedBefore)
|
|
{
|
|
//
|
|
XSignal tSignal = trader.GetSignal(maxInDDSignal.ticket);
|
|
|
|
//
|
|
if (tSignal.ticket > 0 && tSignal.volume > 0)
|
|
{
|
|
//
|
|
// Update In Guardeds ...
|
|
Update(tSignal);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Remove From Guardeds ...
|
|
Remove(tSignal.ticket);
|
|
}
|
|
}
|
|
else if (reason == X_GUARDED_OLD)
|
|
{
|
|
//
|
|
// Added to Guardeds ...
|
|
Add(maxInDDSignal);
|
|
}
|
|
|
|
//
|
|
message = commentPrefix + reasonString + " ...";
|
|
}
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Nothing ...
|
|
// Default ...
|
|
default:
|
|
message = "Max Allowed Drawdown Reached ...";
|
|
break;
|
|
}
|
|
|
|
//
|
|
result = StringLen(message) > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
if (result)
|
|
{
|
|
//
|
|
if (reason == X_GUARDED_OLD)
|
|
{
|
|
//
|
|
int closedCount = ArraySize(closed);
|
|
if (closedCount > 0)
|
|
{
|
|
//
|
|
for (int i = 0; i < closedCount; i++)
|
|
{
|
|
AddItemToBuffer(
|
|
closed[i],
|
|
guardResult.olds);
|
|
}
|
|
}
|
|
|
|
//
|
|
if (maxInDDSignal.ticket > 0)
|
|
{
|
|
AddItemToBuffer(
|
|
maxInDDSignal,
|
|
guardResult.olds);
|
|
}
|
|
}
|
|
else if (reason == X_GUARDED_CRITICAL)
|
|
{
|
|
//
|
|
int closedCount = ArraySize(closed);
|
|
if (closedCount > 0)
|
|
{
|
|
//
|
|
for (int i = 0; i < closedCount; i++)
|
|
{
|
|
AddItemToBuffer(
|
|
closed[i],
|
|
guardResult.criticals);
|
|
}
|
|
}
|
|
|
|
//
|
|
if (maxInDDSignal.ticket > 0)
|
|
{
|
|
AddItemToBuffer(
|
|
maxInDDSignal,
|
|
guardResult.criticals);
|
|
}
|
|
}
|
|
else if (reason == X_GUARDED_MAX)
|
|
{
|
|
//
|
|
int closedCount = ArraySize(closed);
|
|
if (closedCount > 0)
|
|
{
|
|
//
|
|
for (int i = 0; i < closedCount; i++)
|
|
{
|
|
AddItemToBuffer(
|
|
closed[i],
|
|
guardResult.maxes);
|
|
}
|
|
}
|
|
|
|
//
|
|
if (maxInDDSignal.ticket > 0)
|
|
{
|
|
AddItemToBuffer(
|
|
maxInDDSignal,
|
|
guardResult.maxes);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
};
|
|
|
|
//
|
|
// END Global Definitions: Variables, Properties and etc ...
|
|
//
|