also i have to implement it's usage on XCExpert Class ... extends XTarget approaches and add support to Distances ...
3946 lines
89 KiB
Plaintext
3946 lines
89 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Class Library
|
|
// ----------------------------------------------
|
|
// Name: XSCTrade
|
|
// Description: provides all Trade requirements ...
|
|
//
|
|
//
|
|
// Maintainer:
|
|
// ------------
|
|
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
|
|
//
|
|
//////////////////////////////////////////////////////
|
|
//
|
|
// Global Properties ...
|
|
#property library
|
|
#property copyright "Copyright 2023, SaherElm IT Center"
|
|
#property link "https://www.saherelm.ir"
|
|
#property version "1.00"
|
|
#property strict
|
|
|
|
//
|
|
// Imports ...
|
|
#include "../Classes/x-saherelm.base.class.mq5"
|
|
#include "../Classes/x-saherelm.x-account.class.mq5"
|
|
#include "../Classes/x-saherelm.x-alert.class.mq5"
|
|
#include "../Libraries/x-saherelm.x-trade.lib.mq5"
|
|
|
|
//
|
|
// Definitions ...
|
|
|
|
//
|
|
// Overrides ...
|
|
|
|
//
|
|
// XCTrade Base Class ...
|
|
class XCTradeBase : 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));
|
|
}
|
|
};
|
|
|
|
//
|
|
// Implementation ...
|
|
|
|
//
|
|
// a Class For Manage Trades and Handle Trading Actions ...
|
|
class XCTrade : public XCBase
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Props ...
|
|
XCAccount *mAccount; // Account Info Provider
|
|
|
|
//
|
|
// Constructors ...
|
|
void XCTrade(
|
|
int slippage, // Specify Slippage
|
|
ulong magicNumber, // Specify Magic Number
|
|
double maxAllowedSpread = 0, // Max Allowed Spred for Opening Trades
|
|
int maxAllowedPositions = 0, // Max Allowed Positions
|
|
double maxAllowedDrawdownFactor = 0 // Max Allowed Drawdown Factor
|
|
)
|
|
{
|
|
//
|
|
// Setting Props ...
|
|
mSlippage = slippage;
|
|
mMagicNumber = magicNumber;
|
|
|
|
//
|
|
mMaxAllowedSpread = maxAllowedSpread;
|
|
mMaxAllowedPositions = maxAllowedPositions;
|
|
mMaxAllowedDrawdownFactor = maxAllowedDrawdownFactor;
|
|
|
|
//
|
|
mAccount = new XCAccount();
|
|
|
|
//
|
|
// Instance Base Trader Class ...
|
|
mTrader = new XCTradeBase();
|
|
|
|
//
|
|
// Configuring Base Trader Class ...
|
|
mTrader.SetAsyncMode(false);
|
|
mTrader.SetDeviationInPoints(mSlippage);
|
|
mTrader.SetExpertMagicNumber(mMagicNumber);
|
|
|
|
//
|
|
// Setting Log Level ...
|
|
mTrader.LogLevel(LOG_LEVEL_ERRORS);
|
|
|
|
//
|
|
// OnTrade Context ...
|
|
|
|
//
|
|
mDaysForRead = 1;
|
|
mScanStarted = false;
|
|
|
|
//
|
|
// Reset On Trade Counter ...
|
|
ResetOnTradeContext();
|
|
|
|
//
|
|
// Init On Trade Context ...
|
|
InitOnTradeContext();
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
~XCTrade()
|
|
{
|
|
//
|
|
// Remove Pointer ...
|
|
delete mTrader;
|
|
delete mAccount;
|
|
|
|
//
|
|
ZeroMemory(mTrader);
|
|
ZeroMemory(mAccount);
|
|
|
|
//
|
|
Clean(mOnModifyEventHandlers);
|
|
Clean(mStopLossEventHandlers);
|
|
Clean(mTakeProfitEventHandlers);
|
|
Clean(mOnForceCloseEventHandlers);
|
|
Clean(mDealsChangedEventHandlers);
|
|
Clean(mOrdersChangedEventHandlers);
|
|
Clean(mOnPartialCloseEventHandlers);
|
|
Clean(mPositionsChangedEventHandlers);
|
|
Clean(mTradeStateChangedEventHandlers);
|
|
}
|
|
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Retrieve Slippage ...
|
|
int GetSlippage()
|
|
{
|
|
return mSlippage;
|
|
}
|
|
|
|
//
|
|
// Retrieve Magic Number ...
|
|
ulong GetMagicNumber()
|
|
{
|
|
return mMagicNumber;
|
|
}
|
|
|
|
//
|
|
double GetMaxAllowedSpread()
|
|
{
|
|
return mMaxAllowedSpread;
|
|
}
|
|
|
|
//
|
|
int GetMaxAllowedPositions()
|
|
{
|
|
return mMaxAllowedPositions;
|
|
}
|
|
|
|
//
|
|
void SetMaxAllowedPositions(int value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mMaxAllowedPositions = value;
|
|
}
|
|
|
|
//
|
|
double GetMaxAllowedDrawdownFactor()
|
|
{
|
|
return mMaxAllowedDrawdownFactor;
|
|
}
|
|
|
|
//
|
|
// Add Event Listeners ...
|
|
|
|
//
|
|
void AddOnModifyPositionEventHandler(TOnModify handler)
|
|
{
|
|
//
|
|
if (handler == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
handler,
|
|
mOnModifyEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
void AddOnForceClosePositionEventHandler(TOnForceClose handler)
|
|
{
|
|
//
|
|
if (handler == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
handler,
|
|
mOnForceCloseEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
void AddOnPartialClosePositionEventHandler(TOnPartialClose handler)
|
|
{
|
|
//
|
|
if (handler == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
handler,
|
|
mOnPartialCloseEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add New Deals Changed Event Handler ...
|
|
void AddOnTradeStateChangedEventHandler(TOnTradeStateChanged handler)
|
|
{
|
|
//
|
|
if (handler == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
handler,
|
|
mTradeStateChangedEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add New Deals Changed Event Handler ...
|
|
void AddOnDealsChangedEventHandler(TOnDealsChanged handler)
|
|
{
|
|
//
|
|
if (handler == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
handler,
|
|
mDealsChangedEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add New Orders Changed Event Handler ...
|
|
void AddOnOrdersChangedEventHandler(TOnOrdersChanged handler)
|
|
{
|
|
//
|
|
if (handler == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
handler,
|
|
mOrdersChangedEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add New Positions Changed Event Handler ...
|
|
void AddOnPositionsChangedEventHandler(TOnPositionsChanged handler)
|
|
{
|
|
//
|
|
if (handler == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
handler,
|
|
mPositionsChangedEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add New Stop Loss Event Handler ...
|
|
void AddOnStopLossEventHandler(TOnStopLoss handler)
|
|
{
|
|
//
|
|
if (handler == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
handler,
|
|
mStopLossEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add New Stop Loss Event Handler ...
|
|
void AddOnTakeProfitEventHandler(TOnTakeProfit handler)
|
|
{
|
|
//
|
|
if (handler == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
handler,
|
|
mTakeProfitEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add New Signal Executed Event Handler ...
|
|
void AddOnSignalExecutedEventHandler(TOnSignal handler)
|
|
{
|
|
//
|
|
if (handler == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
handler,
|
|
mOnSignalExecutedEventHandlers //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Functions ...
|
|
|
|
//
|
|
// Handlers ...
|
|
|
|
//
|
|
// Process On Trade Requirement ...
|
|
// this Only Used when we Have to Proccess OnTrade Event in EA(s) ...
|
|
void HandleOnTradeEvent(
|
|
XOnTradeHandlerState &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
result.Clean();
|
|
|
|
//
|
|
if (mScanStarted)
|
|
{
|
|
//
|
|
ProcessOnTradeContext(result);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Init Trade Context ...
|
|
InitOnTradeContext();
|
|
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Here we Can Check the Start ...
|
|
bool isValid =
|
|
//
|
|
result.IsValid() &&
|
|
//
|
|
!result.hasNewDeal &&
|
|
!result.hasNewOrder &&
|
|
!result.hasNewPosition &&
|
|
!result.hasNewHistoryOrder
|
|
//
|
|
;
|
|
if (!isValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// State ...
|
|
|
|
//
|
|
// Notfy Trade State Changed Event ...
|
|
NotifyTradeStateChangeEvent(result);
|
|
|
|
//
|
|
// Deals ...
|
|
if (result.hasNewDeal)
|
|
{
|
|
//
|
|
// Notfy Deals Changed Event ...
|
|
NotifyDealsChangeEvent(result.newDeals);
|
|
|
|
//
|
|
// Retrieve Last Deal ...
|
|
XDeal deals[];
|
|
int dealsCount = GetDeals(deals);
|
|
if (dealsCount > 0)
|
|
{
|
|
//
|
|
XDeal lastDeal = deals[0];
|
|
|
|
//
|
|
if (lastDeal.reason == DEAL_REASON_TP)
|
|
{
|
|
NotifyTakeProfitEvent(lastDeal);
|
|
}
|
|
else if (lastDeal.reason == DEAL_REASON_SL)
|
|
{
|
|
NotifyStopLossEvent(lastDeal);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Orders ...
|
|
if (result.hasNewOrder || result.hasNewHistoryOrder)
|
|
{
|
|
//
|
|
// Notfy Orders Changed Event ...
|
|
NotifyOrdersChangeEvent(result.newOrders + result.newHistoryOrders);
|
|
}
|
|
|
|
//
|
|
// Positions ...
|
|
if (result.hasNewPosition)
|
|
{
|
|
//
|
|
// Notfy Positions Changed Event ...
|
|
NotifyPositionsChangeEvent(result.newPositions);
|
|
}
|
|
}
|
|
|
|
//
|
|
// This Must be Called Every time OnTrade Event Happening ...
|
|
// this Used for Implementing Custom Senarios ...
|
|
XOnTradeHandlerState HandleOnTrade()
|
|
{
|
|
//
|
|
// Handle OnTrade Event ...
|
|
XOnTradeHandlerState state;
|
|
HandleOnTradeEvent(state);
|
|
if (
|
|
!state.hasNewDeal &&
|
|
!state.hasNewOrder &&
|
|
!state.hasNewPosition &&
|
|
!state.hasNewHistoryOrder)
|
|
{
|
|
return state;
|
|
}
|
|
|
|
//
|
|
// State ...
|
|
|
|
//
|
|
// Notfy Trade State Changed Event ...
|
|
NotifyTradeStateChangeEvent(state);
|
|
|
|
//
|
|
// DEALS ...
|
|
if (state.hasNewDeal)
|
|
{
|
|
//
|
|
// Notfy Deals Changed Event ...
|
|
NotifyDealsChangeEvent(state.newDeals);
|
|
|
|
//
|
|
// Retrieve Last Deal ...
|
|
XDeal deals[];
|
|
GetDeals(deals);
|
|
int dealsCount = ArraySize(deals);
|
|
if (dealsCount > 0)
|
|
{
|
|
//
|
|
XDeal lastDeal = deals[0];
|
|
|
|
//
|
|
if (lastDeal.reason == DEAL_REASON_TP)
|
|
{
|
|
NotifyTakeProfitEvent(lastDeal);
|
|
}
|
|
else if (lastDeal.reason == DEAL_REASON_SL)
|
|
{
|
|
NotifyStopLossEvent(lastDeal);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// ORDERS ...
|
|
if (state.hasNewOrder || state.hasNewHistoryOrder)
|
|
{
|
|
//
|
|
// Notfy Orders Changed Event ...
|
|
NotifyOrdersChangeEvent(state.newOrders + state.newHistoryOrders);
|
|
}
|
|
|
|
//
|
|
// POSITIONS ...
|
|
if (state.hasNewPosition)
|
|
{
|
|
//
|
|
// Notfy Positions Changed Event ...
|
|
NotifyPositionsChangeEvent(state.newPositions);
|
|
}
|
|
|
|
//
|
|
return state;
|
|
}
|
|
|
|
//
|
|
// Trade Actions ...
|
|
|
|
//
|
|
// Main ...
|
|
|
|
//
|
|
// Policies ...
|
|
|
|
//
|
|
// Check Acocunt Balance is Allow new Trades or not ...
|
|
bool CheckEquityForTrade()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (mMaxAllowedDrawdownFactor <= 0)
|
|
{
|
|
//
|
|
result = true;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double equity = mAccount.GetEquity();
|
|
double balance = mAccount.GetBalance();
|
|
|
|
//
|
|
double selectedBalance = MathMax(equity, balance) - MathMin(equity, balance);
|
|
|
|
//
|
|
double maxAllowedBalanceDrawDown = GetMaxAllowedEquity();
|
|
|
|
//
|
|
result = equity > balance ||
|
|
(equity <= balance &&
|
|
selectedBalance <= maxAllowedBalanceDrawDown);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Max Allowed Positions Policy for Trade ...
|
|
bool CheckPositionsForTrade()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (mMaxAllowedPositions <= 0)
|
|
{
|
|
//
|
|
result = true;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = GetPositions(positions);
|
|
if (positionsCount <= 0)
|
|
{
|
|
//
|
|
result = true;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = positionsCount < mMaxAllowedPositions;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Spread is Ok for Executing Specific Signal ...
|
|
bool CheckSpreadForSignalExecution(XSignal &mSignal)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
bool isSpreadCheckEnable = mMaxAllowedSpread > 0;
|
|
if (!isSpreadCheckEnable)
|
|
{
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Retrieve Signal Spread ...
|
|
double spread = GetSpread(mSignal.symbol);
|
|
result = spread <= mMaxAllowedSpread;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Account Policy ...
|
|
bool CheckAccountPolicy(
|
|
XSignal &mSignal, // For Executing Signal
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT &state // State Holder
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check Positions For Trade ...
|
|
result = CheckPositionsForTrade();
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_MAX_REACHED;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Equity For Trade ...
|
|
result = CheckEquityForTrade();
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_NO_EQUITY;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Spread for Trade ...
|
|
result = CheckSpreadForSignalExecution(mSignal);
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_SPREAD;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Execute a Signal ...
|
|
bool ExecuteSignal(
|
|
XSignal &mSignal, // Signal
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT &state, // Result
|
|
ENUM_ORDER_TYPE_TIME lifetime = ORDER_TIME_GTC, // Order LifeTime
|
|
datetime expiration = NULL, // order expiration time
|
|
bool checkPolicies = true // Check Account Policies
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
state = X_SIGNAL_EXECUTION_UNKNOWN;
|
|
|
|
//
|
|
double currentEntry = GetEntry(
|
|
mSignal.symbol,
|
|
mSignal.type
|
|
//
|
|
);
|
|
bool isLong = IsLong(mSignal.type);
|
|
|
|
//
|
|
if (!mSignal.IsValid())
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_INVALID_PARAMS;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Prepare Signal Comment ...
|
|
string mSComment = mSignal.GenerateComment();
|
|
if (!Contains(mSComment, mSignal.comment))
|
|
{
|
|
mSignal.comment += mSComment;
|
|
}
|
|
|
|
//
|
|
// Handle Market Execution ...
|
|
if (mSignal.mode == X_ORDER_MODE_MARKET)
|
|
{
|
|
//
|
|
// Do Apply Policies here ...
|
|
bool hasPolicy = !checkPolicies
|
|
? true
|
|
: CheckAccountPolicy(mSignal, state);
|
|
if (!hasPolicy)
|
|
{
|
|
result = false;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
if (isLong)
|
|
{
|
|
//
|
|
result = Buy(
|
|
mSignal.symbol,
|
|
mSignal.period,
|
|
mSignal.volume,
|
|
mSignal.entry,
|
|
mSignal.sl,
|
|
mSignal.tp,
|
|
mSignal.comment
|
|
//
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
result = Sell(
|
|
mSignal.symbol,
|
|
mSignal.period,
|
|
mSignal.volume,
|
|
mSignal.entry,
|
|
mSignal.sl,
|
|
mSignal.tp,
|
|
mSignal.comment
|
|
//
|
|
);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Handle Stop Execution ...
|
|
else if (mSignal.mode == X_ORDER_MODE_STOP)
|
|
{
|
|
|
|
//
|
|
// Check Conditions ...
|
|
|
|
//
|
|
// Check Price ...
|
|
result = isLong
|
|
? mSignal.entry > currentEntry
|
|
: mSignal.entry < currentEntry;
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_PRICE_ERROR;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Execute Order ...
|
|
if (isLong)
|
|
{
|
|
//
|
|
result = BuyStop(
|
|
mSignal.symbol,
|
|
mSignal.period,
|
|
mSignal.volume,
|
|
mSignal.entry,
|
|
mSignal.sl,
|
|
mSignal.tp,
|
|
lifetime,
|
|
expiration,
|
|
mSignal.comment
|
|
//
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
result = SellStop(
|
|
mSignal.symbol,
|
|
mSignal.period,
|
|
mSignal.volume,
|
|
mSignal.entry,
|
|
mSignal.sl,
|
|
mSignal.tp,
|
|
lifetime,
|
|
expiration,
|
|
mSignal.comment
|
|
//
|
|
);
|
|
}
|
|
}
|
|
//
|
|
// Handle Limit Execution ...
|
|
else if (mSignal.mode == X_ORDER_MODE_LIMIT)
|
|
{
|
|
//
|
|
// Check Conditions ...
|
|
|
|
//
|
|
// Check Price ...
|
|
result = isLong
|
|
? mSignal.entry < currentEntry
|
|
: mSignal.entry > currentEntry;
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_PRICE_ERROR;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Execute Order ...
|
|
if (isLong)
|
|
{
|
|
//
|
|
result = BuyLimit(
|
|
mSignal.symbol,
|
|
mSignal.period,
|
|
mSignal.volume,
|
|
mSignal.entry,
|
|
mSignal.sl,
|
|
mSignal.tp,
|
|
lifetime,
|
|
expiration,
|
|
mSignal.comment
|
|
//
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
result = SellLimit(
|
|
mSignal.symbol,
|
|
mSignal.period,
|
|
mSignal.volume,
|
|
mSignal.entry,
|
|
mSignal.sl,
|
|
mSignal.tp,
|
|
lifetime,
|
|
expiration,
|
|
mSignal.comment
|
|
//
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handle Ticket ...
|
|
if (result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_SUCCEED;
|
|
|
|
//
|
|
ulong ticket;
|
|
if (mSignal.mode == X_ORDER_MODE_MARKET)
|
|
{
|
|
ticket = GetLastOpenPositionTicket();
|
|
}
|
|
else
|
|
{
|
|
ticket = GetLastPlacedOrderTicket();
|
|
}
|
|
|
|
//
|
|
mSignal.positionId = ticket;
|
|
|
|
//
|
|
// Notify Signal Execution ...
|
|
if (mSignal.mode == X_ORDER_MODE_MARKET)
|
|
{
|
|
NotifySignalExecuted(mSignal);
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Execute a Collection Of Signals ...
|
|
int ExecuteSignals(
|
|
XSignal &signals[], // Collection of Signal for Execution
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT &states[], // Signal Execution Result
|
|
ENUM_ORDER_TYPE_TIME lifetime = ORDER_TIME_GTC, // Order LifeTime
|
|
datetime expiration = NULL, // order expiration time
|
|
bool checkPolicies = true // Check Account Policies
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(states);
|
|
|
|
//
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ArrayResize(states, signalsCount);
|
|
ArrayInitialize(states, X_SIGNAL_EXECUTION_UNKNOWN);
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
bool isExecuted = ExecuteSignal(
|
|
signals[i],
|
|
states[i],
|
|
lifetime,
|
|
expiration,
|
|
checkPolicies
|
|
//
|
|
);
|
|
|
|
//
|
|
if (isExecuted)
|
|
{
|
|
result++;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int ExecuteSpecifiedSignals(
|
|
XSignal &signals[], // Collection of Signal for Execution
|
|
XSignal ¬Executeds[], // Not Executed Signal
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT &states[], // Signal Execution Result
|
|
ENUM_ORDER_TYPE_TIME lifetime = ORDER_TIME_GTC, // Order LifeTime
|
|
datetime expiration = NULL, // order expiration time
|
|
bool checkPolicies = true // Check Account Policies
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(states);
|
|
Clean(notExecuteds);
|
|
|
|
//
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ArrayResize(states, signalsCount);
|
|
ArrayInitialize(states, X_SIGNAL_EXECUTION_UNKNOWN);
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
bool isExecuted = ExecuteSignal(
|
|
signals[i],
|
|
states[i],
|
|
lifetime,
|
|
expiration
|
|
//
|
|
);
|
|
|
|
//
|
|
if (isExecuted)
|
|
{
|
|
result++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
AddRef(
|
|
signals[i],
|
|
notExecuteds //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Regular Trade Actions ...
|
|
|
|
//
|
|
// Buy ...
|
|
|
|
//
|
|
// Force Open a Buy/Long Position ...
|
|
bool Buy(
|
|
string symbol, // Specified Symbol
|
|
ENUM_TIMEFRAMES period, // Specified Trade's Period
|
|
double volume, // position volume
|
|
double price, // execution price
|
|
double sl = 0, // stop loss price
|
|
double tp = 0, // take profit price
|
|
string comment = NULL // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
|
|
if (!IsValid(comment) ||
|
|
!IsValid(mPeriod))
|
|
{
|
|
//
|
|
string mPeriodStr = GeneratePeriodTag(period);
|
|
|
|
//
|
|
comment += mPeriodStr;
|
|
}
|
|
|
|
//
|
|
result = mTrader.Buy(
|
|
volume,
|
|
symbol,
|
|
price,
|
|
sl,
|
|
tp,
|
|
comment
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Send BUY LIMIT order ...
|
|
bool BuyLimit(
|
|
string symbol, // Specified Symbol
|
|
ENUM_TIMEFRAMES period, // Specified Trade's Period
|
|
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 = NULL // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
|
|
if (!IsValid(comment) ||
|
|
!IsValid(mPeriod))
|
|
{
|
|
//
|
|
string mPeriodStr = GeneratePeriodTag(period);
|
|
|
|
//
|
|
comment += mPeriodStr;
|
|
}
|
|
|
|
//
|
|
result = mTrader.BuyLimit(
|
|
volume,
|
|
price,
|
|
symbol,
|
|
sl,
|
|
tp,
|
|
typeTime,
|
|
expiration,
|
|
comment
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Send BUY STOP order ...
|
|
bool BuyStop(
|
|
string symbol, // Specified Symbol
|
|
ENUM_TIMEFRAMES period, // Specified Trade's Period
|
|
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 = NULL // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
|
|
if (!IsValid(comment) ||
|
|
!IsValid(mPeriod))
|
|
{
|
|
//
|
|
string mPeriodStr = GeneratePeriodTag(period);
|
|
|
|
//
|
|
comment += mPeriodStr;
|
|
}
|
|
|
|
//
|
|
result = mTrader.BuyStop(
|
|
volume,
|
|
price,
|
|
symbol,
|
|
sl,
|
|
tp,
|
|
typeTime,
|
|
expiration,
|
|
comment
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Sell ...
|
|
|
|
//
|
|
// Force Open a Sell/Short Position ...
|
|
bool Sell(
|
|
string symbol, // Specified Symbol
|
|
ENUM_TIMEFRAMES period, // Specified Trade's Period
|
|
double volume, // position volume
|
|
double price, // execution price
|
|
double sl = 0, // stop loss price
|
|
double tp = 0, // take profit price
|
|
string comment = NULL // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
|
|
if (!IsValid(comment) ||
|
|
!IsValid(mPeriod))
|
|
{
|
|
//
|
|
string mPeriodStr = GeneratePeriodTag(period);
|
|
|
|
//
|
|
comment += mPeriodStr;
|
|
}
|
|
|
|
//
|
|
result = mTrader.Sell(
|
|
volume,
|
|
symbol,
|
|
price,
|
|
sl,
|
|
tp,
|
|
comment
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Send SELL LIMIT order ...
|
|
bool SellLimit(
|
|
string symbol, // Specified Symbol
|
|
ENUM_TIMEFRAMES period, // Specified Trade's Period
|
|
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 = NULL // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
|
|
if (!IsValid(comment) ||
|
|
!IsValid(mPeriod))
|
|
{
|
|
//
|
|
string mPeriodStr = GeneratePeriodTag(period);
|
|
|
|
//
|
|
comment += mPeriodStr;
|
|
}
|
|
|
|
//
|
|
result = mTrader.SellLimit(
|
|
volume,
|
|
price,
|
|
symbol,
|
|
sl,
|
|
tp,
|
|
typeTime,
|
|
expiration,
|
|
comment
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Send SELL STOP order ...
|
|
bool SellStop(
|
|
string symbol, // Specified Symbol
|
|
ENUM_TIMEFRAMES period, // Specified Trade's Period
|
|
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 = NULL // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
|
|
if (!IsValid(comment) ||
|
|
!IsValid(mPeriod))
|
|
{
|
|
//
|
|
string mPeriodStr = GeneratePeriodTag(period);
|
|
|
|
//
|
|
comment += mPeriodStr;
|
|
}
|
|
|
|
//
|
|
result = mTrader.SellStop(
|
|
volume,
|
|
price,
|
|
symbol,
|
|
sl,
|
|
tp,
|
|
typeTime,
|
|
expiration,
|
|
comment
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check a Position is Exists Open or not ...
|
|
bool IsOpen(
|
|
ulong ticket // Specified Position Ticket
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = mPositionInfo
|
|
.SelectByTicket(ticket);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Modify Position ...
|
|
bool Modify(
|
|
const ulong ticket, // position ticket
|
|
double sl = 0, // stop loss price
|
|
double tp = 0, // take profit
|
|
string comment = NULL // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
XPosition position;
|
|
result =
|
|
GetPosition(
|
|
ticket,
|
|
position //
|
|
) &&
|
|
(tp >= 0 || sl >= 0);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Use Position Comment when there isn't new One ...
|
|
if (!IsValid(comment) == 0)
|
|
{
|
|
//
|
|
// if there isn't provided Comment
|
|
// Use Position Comment for default ...
|
|
comment = "Modify " + GetPositionComment(ticket);
|
|
}
|
|
|
|
//
|
|
result = mTrader.PositionModify(
|
|
ticket,
|
|
sl,
|
|
tp,
|
|
comment
|
|
//
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
NotifyModifyEvent(
|
|
ticket,
|
|
position.profit,
|
|
comment //
|
|
);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close Position By Ticket ...
|
|
bool Close(
|
|
ulong ticket, // Position Ticket
|
|
string comment = NULL // Close Position By Specific Comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check Position Open ...
|
|
XPosition position;
|
|
result = GetPosition(
|
|
ticket,
|
|
position //
|
|
);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Use Default Position Comment if it's Not Provided ...
|
|
if (!IsValid(comment))
|
|
{
|
|
comment = "Close " + GetPositionComment(ticket);
|
|
}
|
|
|
|
//
|
|
// Close Position By Specific Comment ...
|
|
result = mTrader.PositionClose(
|
|
ticket,
|
|
mSlippage,
|
|
comment
|
|
//
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
NotifyForceCloseEvent(
|
|
ticket,
|
|
position,
|
|
comment //
|
|
);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close Specified Positions ...
|
|
int Close(
|
|
string comment = NULL, // Close Position By Specific Comment
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
|
|
bool filterByMagic = true // Get Only Self Open Positions
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = GetPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
filterByMagic);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iPosition.ticket,
|
|
comment
|
|
//
|
|
);
|
|
if (isClosed)
|
|
{
|
|
result++;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close a Collection of Positions ...
|
|
int Close(
|
|
XPosition &positions[], // Collection of Positions
|
|
string comment = NULL // Close Comment
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
int positionsCount = ArraySize(positions);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iPosition.ticket,
|
|
comment
|
|
//
|
|
);
|
|
if (isClosed)
|
|
{
|
|
result++;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close Partial By Ticket ...
|
|
bool ClosePartial(
|
|
ulong ticket, // Position Ticket
|
|
double volume, // Volume Size for Partial Closing
|
|
string comment = NULL // Close Position By Specific Comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
XPosition position;
|
|
result = GetPosition(
|
|
ticket,
|
|
position //
|
|
);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Use Default Partial Close Comment ...
|
|
if (!IsValid(comment))
|
|
{
|
|
//
|
|
comment += "PClose " + GetPositionComment(ticket);
|
|
}
|
|
|
|
//
|
|
result = mTrader.PositionClosePartial(
|
|
ticket,
|
|
volume,
|
|
mSlippage,
|
|
comment
|
|
//
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
NotifyPartialCloseEvent(
|
|
ticket,
|
|
position.profit,
|
|
comment //
|
|
);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Cancel Specific Order ...
|
|
bool CancelOrder(ulong ticket)
|
|
{
|
|
return mTrader
|
|
.OrderDelete(ticket);
|
|
}
|
|
|
|
//
|
|
// Cancel Specific Orders ...
|
|
int CancelOrders(
|
|
XOrder &orders[] // Specified Orders ...
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
int ordersCount = ArraySize(orders);
|
|
if (ordersCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < ordersCount; i++)
|
|
{
|
|
//
|
|
XOrder iOrder = orders[i];
|
|
|
|
//
|
|
bool isDeleted = mTrader
|
|
.OrderDelete(iOrder.ticket);
|
|
if (isDeleted && !result)
|
|
{
|
|
result++;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extractors ...
|
|
|
|
//
|
|
// Positions ...
|
|
|
|
//
|
|
// Calculate Positions Profit ...
|
|
double Profit(
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
|
|
bool filterByMagic = true // Get Only Self Open Positions
|
|
)
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
// Retrieve Positions ...
|
|
XPosition positions[];
|
|
int positionsCount = GetPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
filterByMagic
|
|
//
|
|
);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
result += positions[i].profit;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Position by Index ...
|
|
bool GetPositionByIndex(
|
|
int index, // Specified Index
|
|
XPosition &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
bool isInited = result
|
|
.ByIndex(index);
|
|
|
|
//
|
|
return isInited;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Position by Ticket ...
|
|
bool GetPosition(
|
|
ulong ticket, // Specified Ticket
|
|
XPosition &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
bool isInited = result
|
|
.ByTicket(ticket);
|
|
|
|
//
|
|
return isInited;
|
|
}
|
|
|
|
//
|
|
double GetPositionCommission(
|
|
ulong ticket // Specified Ticket
|
|
)
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
XPosition position;
|
|
bool hasPosition = GetPosition(
|
|
ticket,
|
|
position //
|
|
);
|
|
if (!hasPosition)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XDeal deals[];
|
|
int dealsCount = GetDeals(
|
|
deals,
|
|
position.symbol,
|
|
position.provider,
|
|
position.period,
|
|
NULL,
|
|
DEAL_ENTRY_IN //
|
|
);
|
|
if (IsValidSize(dealsCount))
|
|
{
|
|
//
|
|
for (int i = 0; i < dealsCount; i++)
|
|
{
|
|
//
|
|
XDeal iDeal = deals[i];
|
|
if (iDeal.positionId == ticket)
|
|
{
|
|
//
|
|
result = iDeal.commission;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Positions ...
|
|
int GetPositions(
|
|
XPosition &result[], // Hold's Result
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
|
|
bool filterByMagic = true, // Get Only Self Open Positions
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
int before = ArraySize(result);
|
|
|
|
//
|
|
if (forceClean)
|
|
{
|
|
Clean(result);
|
|
}
|
|
|
|
//
|
|
// Count Positions ...
|
|
int itemsCount = PositionsTotal();
|
|
if (itemsCount <= 0)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Loop Through all Positions ...
|
|
for (int i = itemsCount - 1; i >= 0; i--)
|
|
{
|
|
//
|
|
XPosition iItem;
|
|
bool isLoaded = iItem.ByIndex(i);
|
|
if (!isLoaded)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now we have to Check Given Filters ...
|
|
bool isFiltersPassed =
|
|
iItem.IsFiltersPassed(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
mMagicNumber,
|
|
filterByMagic
|
|
//
|
|
);
|
|
if (!isFiltersPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add Filtered Item into Result ...
|
|
AddRef(
|
|
iItem,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
int after = ArraySize(result);
|
|
|
|
//
|
|
mResult = after - before;
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
void GetPositions(
|
|
XPosition &longs[], // Hold's Longs Result
|
|
XPosition &shorts[], // Hold's Shorts Result
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
bool filterByMagic = true, // Get Only Self Open Positions
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
// Retrieve Long Positions ...
|
|
GetPositions(
|
|
longs,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
X_POSITION_TYPE_LONG,
|
|
filterByMagic,
|
|
forceClean //
|
|
);
|
|
|
|
//
|
|
// Retrieve Short Positions ...
|
|
GetPositions(
|
|
shorts,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
X_POSITION_TYPE_SHORT,
|
|
filterByMagic,
|
|
forceClean //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Select In Profit Positions ...
|
|
int GetInProfitPositions(
|
|
XPosition &result[], // Hold's Result
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
|
|
ENUM_X_POSITION_SELECT_METHODS method = NULL, // Selection Method
|
|
bool filterByMagic = true, // Get Only Self Open Positions
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
int before = ArraySize(result);
|
|
|
|
//
|
|
if (forceClean)
|
|
{
|
|
Clean(result);
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = GetPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
filterByMagic,
|
|
forceClean);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
XPosition min;
|
|
XPosition max;
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
if (iPosition.profit <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Calculate Min and Max ...
|
|
|
|
//
|
|
// Min ...
|
|
if (!min.IsValid() || min.profit <= 0 || min.profit > iPosition.profit)
|
|
{
|
|
min = iPosition;
|
|
}
|
|
|
|
//
|
|
// Max ...
|
|
if (!max.IsValid() || max.profit <= 0 || max.profit < iPosition.profit)
|
|
{
|
|
max = iPosition;
|
|
}
|
|
|
|
//
|
|
bool isSelectionMethodFilterPassed =
|
|
//
|
|
(method == NULL ||
|
|
method == X_POSITION_SELECT_ALL)
|
|
? true
|
|
: false;
|
|
//
|
|
;
|
|
if (!isSelectionMethodFilterPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
if (min.IsValid() &&
|
|
(method == X_POSITION_SELECT_MIN ||
|
|
method == X_POSITION_SELECT_BOTH))
|
|
{
|
|
//
|
|
AddRef(
|
|
min,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
if (max.IsValid() &&
|
|
(method == X_POSITION_SELECT_MAX ||
|
|
method == X_POSITION_SELECT_BOTH))
|
|
{
|
|
//
|
|
AddRef(
|
|
max,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
int after = ArraySize(result);
|
|
|
|
//
|
|
mResult = after - before;
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Select In Drawdown Positions ...
|
|
int GetInDrawdownPositions(
|
|
XPosition &result[], // Hold's Result
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
|
|
ENUM_X_POSITION_SELECT_METHODS method = NULL, // Selection Method
|
|
bool filterByMagic = true, // Get Only Self Open Positions
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
int before = ArraySize(result);
|
|
|
|
//
|
|
if (forceClean)
|
|
{
|
|
Clean(result);
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = GetPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
filterByMagic,
|
|
forceClean);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
XPosition min;
|
|
XPosition max;
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
if (iPosition.profit >= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Calculate Min and Max ...
|
|
|
|
//
|
|
// Min ...
|
|
if (!min.IsValid() || min.profit <= 0 || min.profit > iPosition.profit)
|
|
{
|
|
min = iPosition;
|
|
}
|
|
|
|
//
|
|
// Max ...
|
|
if (!max.IsValid() || max.profit <= 0 || max.profit < iPosition.profit)
|
|
{
|
|
max = iPosition;
|
|
}
|
|
|
|
//
|
|
bool isSelectionMethodFilterPassed =
|
|
//
|
|
(method == NULL ||
|
|
method == X_POSITION_SELECT_ALL)
|
|
? true
|
|
: false;
|
|
//
|
|
;
|
|
if (!isSelectionMethodFilterPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
if (min.IsValid() &&
|
|
(method == X_POSITION_SELECT_MIN ||
|
|
method == X_POSITION_SELECT_BOTH))
|
|
{
|
|
//
|
|
AddRef(
|
|
min,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
if (max.IsValid() &&
|
|
(method == X_POSITION_SELECT_MAX ||
|
|
method == X_POSITION_SELECT_BOTH))
|
|
{
|
|
//
|
|
AddRef(
|
|
max,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
int after = ArraySize(result);
|
|
|
|
//
|
|
mResult = after - before;
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Orders ...
|
|
|
|
//
|
|
// Retrieve Specific Order by Index ...
|
|
bool GetOrder(
|
|
int index, // Specified Index
|
|
XOrder &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
return result
|
|
.ByIndex(index);
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Positions Orders ...
|
|
int GetOrders(
|
|
ulong ticket, // Positions Ticket
|
|
XOrder &result[], // Hold's Result
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
// Select History by Specified Ticket ...
|
|
bool isHistorySelected = HistorySelectByPosition(ticket);
|
|
if (!isHistorySelected)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
int ordersCount = HistoryOrdersTotal();
|
|
if (ordersCount <= 0)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
int before = ArraySize(result);
|
|
|
|
//
|
|
if (forceClean)
|
|
{
|
|
Clean(result);
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < ordersCount; i++)
|
|
{
|
|
//
|
|
XOrder iOrder;
|
|
bool isLoaded = iOrder.HistoryByIndex(i);
|
|
if (!isLoaded)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isTicketFilterPassed =
|
|
//
|
|
iOrder.ticket = ticket
|
|
//
|
|
;
|
|
if (!isTicketFilterPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
iOrder,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
int after = ArraySize(result);
|
|
|
|
//
|
|
mResult = after - before;
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Retrieve All Orders ...
|
|
int GetOrders(
|
|
XOrder &result[], // Hold's Result
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
|
|
ENUM_ORDER_STATE state = NULL, // Order State
|
|
bool filterByMagic = true, // Get Only Self Open Positions
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
int before = ArraySize(result);
|
|
|
|
//
|
|
if (forceClean)
|
|
{
|
|
Clean(result);
|
|
}
|
|
|
|
//
|
|
// Count Positions ...
|
|
int itemsCount = OrdersTotal();
|
|
if (itemsCount <= 0)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Loop Through all Positions ...
|
|
for (int i = itemsCount - 1; i >= 0; i--)
|
|
{
|
|
//
|
|
XOrder iItem;
|
|
bool isLoaded = iItem.ByIndex(i);
|
|
if (!isLoaded)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now we have to Check Given Filters ...
|
|
bool isFiltersPassed =
|
|
iItem.IsFiltersPassed(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
state,
|
|
mMagicNumber,
|
|
filterByMagic
|
|
//
|
|
);
|
|
if (!isFiltersPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add Filtered Item into Result ...
|
|
AddRef(
|
|
iItem,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
int after = ArraySize(result);
|
|
|
|
//
|
|
mResult = after - before;
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Retrieve Historical Orders ...
|
|
int GetOrders(
|
|
XOrder &result[], // Hold's Result
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
|
|
ENUM_ORDER_STATE state = NULL, // Order State
|
|
datetime startDate = NULL, // Specify Start Date
|
|
datetime endDate = NULL, // Specify End Date
|
|
bool filterByMagic = true, // Get Only Self Open Positions
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
endDate = NormalizeTime(endDate);
|
|
|
|
//
|
|
if (!IsValid(startDate))
|
|
{
|
|
startDate = endDate - PeriodSeconds(PERIOD_D1);
|
|
}
|
|
|
|
//
|
|
bool isHistorySelected = HistorySelect(
|
|
startDate,
|
|
endDate);
|
|
if (!isHistorySelected)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
int before = ArraySize(result);
|
|
|
|
//
|
|
if (forceClean)
|
|
{
|
|
Clean(result);
|
|
}
|
|
|
|
//
|
|
// Count Positions ...
|
|
int itemsCount = OrdersTotal();
|
|
if (itemsCount <= 0)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Loop Through all Positions ...
|
|
for (int i = itemsCount - 1; i >= 0; i--)
|
|
{
|
|
//
|
|
XOrder iItem;
|
|
bool isLoaded = iItem.HistoryByIndex(i);
|
|
if (!isLoaded)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now we have to Check Given Filters ...
|
|
bool isFiltersPassed =
|
|
iItem.IsFiltersPassed(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
state,
|
|
mMagicNumber,
|
|
filterByMagic
|
|
//
|
|
);
|
|
if (!isFiltersPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add Filtered Item into Result ...
|
|
AddRef(
|
|
iItem,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
int after = ArraySize(result);
|
|
|
|
//
|
|
mResult = after - before;
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Deals ...
|
|
|
|
//
|
|
// Retrieve Specific Deal by Index ...
|
|
bool GetDeal(
|
|
int index, // Specified Index
|
|
XDeal &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
return result
|
|
.ByIndex(index);
|
|
}
|
|
|
|
//
|
|
bool GetLastDeal(
|
|
XDeal &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
bool mResult = false;
|
|
|
|
//
|
|
mResult = HistorySelect(0, TimeCurrent());
|
|
if (!mResult)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
int lastDealIndex = HistoryDealsTotal() - 1;
|
|
|
|
//
|
|
mResult = GetDeal(
|
|
lastDealIndex,
|
|
result //
|
|
);
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specified Deal's History ...
|
|
int GetDealsHistory(
|
|
XDeal &deal, // Specified Deal
|
|
XOrder &history[], // Deals History
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
if (!deal.IsValid())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = GetOrders(
|
|
deal.positionId,
|
|
history,
|
|
forceClean);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve All Deals ...
|
|
int GetDeals(
|
|
XDeal &result[], // Hold's Result
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_DEAL_TYPE type = NULL, // Deal Type
|
|
ENUM_DEAL_ENTRY entry = NULL, // Deal Entry
|
|
ENUM_DEAL_REASON reason = NULL, // Deal Reason
|
|
datetime startDate = NULL, // Specify Start Date
|
|
datetime endDate = NULL, // Specify End Date
|
|
bool filterByMagic = true, // Get Only Self Open Positions
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
endDate = NormalizeTime(endDate);
|
|
|
|
//
|
|
if (!IsValid(startDate))
|
|
{
|
|
startDate = endDate - PeriodSeconds(PERIOD_D1);
|
|
}
|
|
|
|
//
|
|
bool isHistorySelected = HistorySelect(
|
|
startDate,
|
|
endDate);
|
|
if (!isHistorySelected)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
int before = ArraySize(result);
|
|
|
|
//
|
|
if (forceClean)
|
|
{
|
|
Clean(result);
|
|
}
|
|
|
|
//
|
|
// Count Positions ...
|
|
int itemsCount = HistoryDealsTotal();
|
|
if (itemsCount <= 0)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Loop Through all Positions ...
|
|
for (int i = itemsCount - 1; i >= 0; i--)
|
|
{
|
|
//
|
|
XDeal iItem;
|
|
bool isLoaded = iItem.ByIndex(i);
|
|
if (!isLoaded)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now we have to Check Given Filters ...
|
|
bool isFiltersPassed =
|
|
iItem.IsFiltersPassed(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
entry,
|
|
reason,
|
|
mMagicNumber,
|
|
filterByMagic
|
|
//
|
|
);
|
|
if (!isFiltersPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add Filtered Item into Result ...
|
|
AddRef(
|
|
iItem,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
int after = ArraySize(result);
|
|
|
|
//
|
|
mResult = after - before;
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Support Functions ...
|
|
|
|
//
|
|
// Check a Position has Support or not ...
|
|
bool HasSupport(
|
|
ulong ticket,
|
|
bool forceOpen = true //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = ticket > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
if (forceOpen)
|
|
{
|
|
//
|
|
result = IsOpen(ticket);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = GetPositions(positions);
|
|
result = IsValidSize(positionsCount);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extract Support Positions ...
|
|
XPosition supports[];
|
|
int supportsCount = ExtractSupports(
|
|
positions,
|
|
supports //
|
|
);
|
|
result = IsValidSize(supportsCount);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Loop Through Supports ...
|
|
for (int i = 0; i < supportsCount; i++)
|
|
{
|
|
//
|
|
XPosition iSupport = supports[i];
|
|
|
|
//
|
|
ulong supportedTicket = ExtractSupportedTicket(iSupport.comment);
|
|
ulong eqmSupportedTicket = ExtractEQMSupportedTicket(iSupport.comment);
|
|
|
|
//
|
|
result =
|
|
//
|
|
(supportedTicket > 0 && ticket == supportedTicket)
|
|
//
|
|
||
|
|
//
|
|
(eqmSupportedTicket > 0 && ticket == eqmSupportedTicket)
|
|
//
|
|
;
|
|
if (result)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Position's Supports ...
|
|
int GetSupports(
|
|
ulong ticket,
|
|
XPosition &dest[], // Result ...
|
|
bool forceOpen = true //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(dest);
|
|
|
|
//
|
|
bool hasSupport = HasSupport(
|
|
ticket,
|
|
forceOpen //
|
|
);
|
|
if (!hasSupport)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = GetPositions(positions);
|
|
if (!IsValidSize(positionsCount))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extract Support Positions ...
|
|
XPosition supports[];
|
|
int supportsCount = ExtractSupports(
|
|
positions,
|
|
supports //
|
|
);
|
|
if (!IsValidSize(supportsCount))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Loop Through Supports ...
|
|
for (int i = 0; i < supportsCount; i++)
|
|
{
|
|
//
|
|
XPosition iSupport = supports[i];
|
|
|
|
//
|
|
ulong supportedTicket = ExtractSupportedTicket(iSupport.comment);
|
|
ulong eqmSupportedTicket = ExtractEQMSupportedTicket(iSupport.comment);
|
|
|
|
//
|
|
bool isOwn =
|
|
//
|
|
(supportedTicket > 0 && ticket == supportedTicket)
|
|
//
|
|
||
|
|
//
|
|
(eqmSupportedTicket > 0 && ticket == eqmSupportedTicket)
|
|
//
|
|
;
|
|
if (isOwn)
|
|
{
|
|
//
|
|
AddRef(
|
|
iSupport,
|
|
dest //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
result = ArraySize(dest);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Position's Supports ...
|
|
int GetSupports(
|
|
ulong ticket,
|
|
XOrder &dest[], // Result ...
|
|
bool forceOpen = true //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(dest);
|
|
|
|
//
|
|
bool hasSupport = HasSupport(
|
|
ticket,
|
|
forceOpen //
|
|
);
|
|
if (!hasSupport)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XOrder orders[];
|
|
int ordersCount = GetOrders(
|
|
orders,
|
|
NULL, // All Symbols ...
|
|
NULL, // All Providers ...
|
|
NULL, // All Periods ...
|
|
X_POSITION_TYPE_ALL, // All Types ...
|
|
ORDER_STATE_PLACED, // Untriggere Orders ...
|
|
true //
|
|
);
|
|
if (!IsValidSize(ordersCount))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extract Support Positions ...
|
|
XOrder supports[];
|
|
int supportsCount = ExtractSupports(
|
|
orders,
|
|
supports //
|
|
);
|
|
if (!IsValidSize(supportsCount))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Loop Through Supports ...
|
|
for (int i = 0; i < supportsCount; i++)
|
|
{
|
|
//
|
|
XOrder iSupport = supports[i];
|
|
|
|
//
|
|
ulong supportedTicket = ExtractSupportedTicket(iSupport.comment);
|
|
ulong eqmSupportedTicket = ExtractEQMSupportedTicket(iSupport.comment);
|
|
|
|
//
|
|
bool isOwn =
|
|
//
|
|
(supportedTicket > 0 && ticket == supportedTicket)
|
|
//
|
|
||
|
|
//
|
|
(eqmSupportedTicket > 0 && ticket == eqmSupportedTicket)
|
|
//
|
|
;
|
|
if (isOwn)
|
|
{
|
|
//
|
|
AddRef(
|
|
iSupport,
|
|
dest //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
result = ArraySize(dest);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Retrieve Last Open Position Ticket ...
|
|
ulong GetLastOpenPositionTicket()
|
|
{
|
|
//
|
|
ulong result = 0;
|
|
|
|
int positionsCount = PositionsTotal();
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = PositionGetTicket(PositionsTotal() - 1);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Last Placed Order Ticket ...
|
|
ulong GetLastPlacedOrderTicket()
|
|
{
|
|
//
|
|
ulong result = 0;
|
|
|
|
int ordersCount = OrdersTotal();
|
|
if (ordersCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
if (mOrderInfo.SelectByIndex(ordersCount - 1))
|
|
{
|
|
result = mOrderInfo.Ticket();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Prepare Dynamic Volume ...
|
|
double GetDynamicVolume(
|
|
string _symbol,
|
|
double _balanceFactor,
|
|
double _volumeStep //
|
|
)
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
// Validate Inputs ...
|
|
if (!IsValid(_symbol) ||
|
|
_volumeStep <= 0 ||
|
|
_balanceFactor <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Account Balance ...
|
|
double balance = mAccount.GetBalance();
|
|
|
|
//
|
|
double bStep = balance / _balanceFactor;
|
|
bStep = MathFloor(bStep);
|
|
if (bStep == 0)
|
|
{
|
|
bStep = 1;
|
|
}
|
|
|
|
//
|
|
result = bStep * _volumeStep;
|
|
|
|
//
|
|
result = NormalizeVolume(
|
|
result,
|
|
_symbol //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Event Listener Notifiers ...
|
|
|
|
//
|
|
// State ...
|
|
void NotifyTradeStateChangeEvent(const XOnTradeHandlerState &state)
|
|
{
|
|
//
|
|
int count = ArraySize(mTradeStateChangedEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnTradeStateChanged iHandler = mTradeStateChangedEventHandlers[i];
|
|
iHandler(state);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Deals ...
|
|
void NotifyDealsChangeEvent(int changes)
|
|
{
|
|
//
|
|
int count = ArraySize(mDealsChangedEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnDealsChanged iHandler = mDealsChangedEventHandlers[i];
|
|
iHandler(changes);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Orders ...
|
|
void NotifyOrdersChangeEvent(int changes)
|
|
{
|
|
//
|
|
int count = ArraySize(mOrdersChangedEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnOrdersChanged iHandler = mOrdersChangedEventHandlers[i];
|
|
iHandler(changes);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Positions ...
|
|
void NotifyPositionsChangeEvent(int changes)
|
|
{
|
|
//
|
|
int count = ArraySize(mPositionsChangedEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnPositionsChanged iHandler = mPositionsChangedEventHandlers[i];
|
|
iHandler(changes);
|
|
}
|
|
}
|
|
|
|
//
|
|
// StopLoss ...
|
|
void NotifyStopLossEvent(const XDeal &deal)
|
|
{
|
|
//
|
|
int count = ArraySize(mStopLossEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnStopLoss iHandler = mStopLossEventHandlers[i];
|
|
iHandler(deal);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Take Profit ...
|
|
void NotifyTakeProfitEvent(const XDeal &deal)
|
|
{
|
|
//
|
|
int count = ArraySize(mTakeProfitEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnTakeProfit iHandler = mTakeProfitEventHandlers[i];
|
|
iHandler(deal);
|
|
}
|
|
}
|
|
|
|
//
|
|
void NotifyModifyEvent(
|
|
ulong ticket,
|
|
double profit,
|
|
string comment //
|
|
)
|
|
{
|
|
//
|
|
int count = ArraySize(mOnModifyEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnModify iHandler = mOnModifyEventHandlers[i];
|
|
iHandler(
|
|
ticket,
|
|
profit,
|
|
comment //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
void NotifyForceCloseEvent(
|
|
ulong ticket,
|
|
XPosition &position,
|
|
string comment //
|
|
)
|
|
{
|
|
//
|
|
int count = ArraySize(mOnForceCloseEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnForceClose iHandler = mOnForceCloseEventHandlers[i];
|
|
iHandler(
|
|
ticket,
|
|
position,
|
|
comment //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
void NotifyPartialCloseEvent(
|
|
ulong ticket,
|
|
double profit,
|
|
string comment //
|
|
)
|
|
{
|
|
//
|
|
int count = ArraySize(mOnPartialCloseEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnModify iHandler = mOnPartialCloseEventHandlers[i];
|
|
iHandler(
|
|
ticket,
|
|
profit,
|
|
comment //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
void NotifySignalExecuted(XSignal &signal)
|
|
{
|
|
//
|
|
bool has = signal.IsValid() &&
|
|
signal.IsExecuted();
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
int count = ArraySize(mOnSignalExecutedEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnSignal iHandler = mOnSignalExecutedEventHandlers[i];
|
|
iHandler(signal);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Retrieve Specified Position Comment ...
|
|
string GetPositionComment(ulong ticket)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
if (!IsOpen(ticket))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition position;
|
|
bool isLoaded = position.ByTicket(ticket);
|
|
if (!isLoaded)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = position.comment;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ENUM_TIMEFRAMES GetDealPeriod(XDeal &deal)
|
|
{
|
|
//
|
|
ENUM_TIMEFRAMES result = NULL;
|
|
|
|
//
|
|
XOrder orders[];
|
|
int ordersCount = GetDealsHistory(
|
|
deal,
|
|
orders);
|
|
if (!ordersCount)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < ordersCount; i++)
|
|
{
|
|
//
|
|
XOrder iOrder = orders[i];
|
|
|
|
//
|
|
if (IsValid(iOrder.comment))
|
|
{
|
|
//
|
|
ENUM_TIMEFRAMES iPeriod = ExtractPeriod(iOrder.comment);
|
|
if (IsValid(iPeriod))
|
|
{
|
|
result = iPeriod;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double GetMaxAllowedEquity()
|
|
{
|
|
//
|
|
double equity = mAccount.GetEquity();
|
|
double balance = mAccount.GetBalance();
|
|
|
|
//
|
|
double balanceForFactor = MathAbs(equity - balance);
|
|
if (balanceForFactor <= 0)
|
|
{
|
|
balanceForFactor = balance;
|
|
}
|
|
else
|
|
{
|
|
balanceForFactor = equity;
|
|
}
|
|
|
|
//
|
|
double result = balanceForFactor * mMaxAllowedDrawdownFactor;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
int mSlippage; // Slippage ...
|
|
ulong mMagicNumber; // Magic Number (Unique Identifier) ...
|
|
|
|
//
|
|
double mMaxAllowedSpread; // Max Allowed Spred for Opening Trades
|
|
int mMaxAllowedPositions; // Max Allowed Same Positions
|
|
double mMaxAllowedDrawdownFactor; // Max Allowed Drawdown Factor
|
|
|
|
//
|
|
XCTradeBase *mTrader; // Base CTrade Manipulated Instance ...
|
|
|
|
//
|
|
CDealInfo mDealInfo; // Deals Info ...
|
|
COrderInfo mOrderInfo; // Order Info ...
|
|
CPositionInfo mPositionInfo; // Positions Info
|
|
CHistoryOrderInfo mHistoryOrderInfo; // History Orders Info ...
|
|
|
|
//
|
|
// Position Info Collector ...
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// OnTrade Event Handlers Section ...
|
|
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
int mDaysForRead; // Reading Dates for Trade History Checker ...
|
|
datetime mEndDate; // End Date for Trade history Checking ...
|
|
datetime mStartDate; // Start Date for Trade history Checking ... ...
|
|
int mOrdersCount; // Number of Active orders ...
|
|
int mPositionsCount; // Number of Open positions ...
|
|
int mDealsCount; // Number of Deals in the Trade History Checking ...
|
|
int mHistoryOrdersCount; // Number of Orders in the Trade History Checking ...
|
|
bool mScanStarted; // a Flag of counter relevance ...
|
|
|
|
//
|
|
// Event Listeners ...
|
|
|
|
//
|
|
// Deals Changed Event Handlers ...
|
|
TOnDealsChanged mDealsChangedEventHandlers[];
|
|
|
|
//
|
|
// Orders Changed Event Handlers ...
|
|
TOnOrdersChanged mOrdersChangedEventHandlers[];
|
|
|
|
//
|
|
// Positions Changed Event Handlers ...
|
|
TOnPositionsChanged mPositionsChangedEventHandlers[];
|
|
|
|
//
|
|
// Trade State Changed Event Handlers ...
|
|
TOnTradeStateChanged mTradeStateChangedEventHandlers[];
|
|
|
|
//
|
|
// Signal Executed Event Handlers ...
|
|
TOnSignal mOnSignalExecutedEventHandlers[];
|
|
|
|
//
|
|
TOnModify mOnModifyEventHandlers[];
|
|
TOnForceClose mOnForceCloseEventHandlers[];
|
|
TOnPartialClose mOnPartialCloseEventHandlers[];
|
|
|
|
//
|
|
// TPSL ...
|
|
TOnStopLoss mStopLossEventHandlers[];
|
|
TOnTakeProfit mTakeProfitEventHandlers[];
|
|
|
|
//
|
|
// Reset On Trade Scanner Context ...
|
|
void ResetOnTradeContext()
|
|
{
|
|
//
|
|
mEndDate = TimeCurrent();
|
|
mStartDate = mEndDate - (mDaysForRead * PeriodSeconds(PERIOD_D1));
|
|
}
|
|
|
|
//
|
|
// Initial Context Of Trade Scanner ...
|
|
void InitOnTradeContext()
|
|
{
|
|
//
|
|
ResetLastError();
|
|
|
|
//
|
|
mOrdersCount = OrdersTotal();
|
|
mPositionsCount = PositionsTotal();
|
|
|
|
//
|
|
// load history ...
|
|
bool selected = HistorySelect(mStartDate, mEndDate);
|
|
if (!selected)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// get the current value ...
|
|
mDealsCount = HistoryDealsTotal();
|
|
mHistoryOrdersCount = HistoryOrdersTotal();
|
|
|
|
//
|
|
mScanStarted = true;
|
|
}
|
|
|
|
//
|
|
// 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(
|
|
XOnTradeHandlerState &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)
|
|
{
|
|
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();
|
|
}
|
|
};
|
|
|
|
// |