Files
MQL5Data/Classes/x-saherelm.x-trade.class.mq5
T
2025-05-13 20:19:58 +03:30

3899 lines
88 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
//
);
}
//
// 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;
//
// Handle Store XPositionInfo ...
if (mSignal.mode == X_ORDER_MODE_MARKET)
{
//
// TODO: Implement this ...
}
}
//
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 &notExecuteds[], // 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 // 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 GetPosition(
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 //
);
}
}
//
// 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[];
//
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();
}
};
//