Files
xMQL5/MQLTestWorkspace/BKPS/BKP/x-saherelm.xtrade.before.ontrade.handlers.class.mq5
T

2839 lines
68 KiB
Plaintext

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Expert 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
//
// START Definitions ...
//
//
// Model an Open Position ...
struct XPosition
{
//
// Magic Number ...
ulong magic;
//
// Ticket ID ...
ulong ticket;
//
// Tradinng Symbol ...
string symbol;
//
// Trading Period ...
ENUM_TIMEFRAMES period;
//
// Position Type ...
ENUM_POSITION_TYPE type;
//
// Take Profit ...
double tp;
//
// Stop Loss ...
double sl;
//
// Position Current Profit ...
double profit;
//
// Position Open Price ...
double entry;
//
// Current Symbol Price ...
double price;
//
// Position Swap ...
double swap;
//
// Position Commission ...
double commission;
//
// Volume ...
double volume;
//
// Position Open Time ...
datetime openAt;
//
// Position Comment ...
string comment;
};
//
// Model an Order ...
struct XOrder
{
//
// Time of order placement ...
datetime setupAt;
//
// Time of order expiration ...
datetime expiredAt;
//
// Time of order execution or cancellation ...
datetime executedAt;
//
// Order type ...
ENUM_ORDER_TYPE type;
//
// Order state ...
ENUM_ORDER_STATE state;
//
// Type of execution by remainder ...
ENUM_ORDER_TYPE_FILLING filling;
//
// Order lifetime ...
ENUM_ORDER_TYPE_TIME lifetime;
//
// Order ticket ...
ulong ticket;
//
// Magic Number ...
long magic;
//
// Position id, that is placed on order,
// when it is executed. Each executed order invokes a
// deal, that opens new or changes existing
// position. Id of that position is placed on
// executed order in this moment.
long pocitionID;
//
// Initial volume on order placement ...
double initialVolume;
//
// Unfilled volume ...
double currentVolume;
//
// Price, specified in the order ...
double openPrice;
//
// Current price by order symbol ...
double currentPrice;
//
// Price of placing Limit order when StopLimit order is triggered ...
double stopLimitPrice;
//
// Take Profit level ...
double tp;
//
// Stop Loss level ...
double sl;
//
// Trading Symbol ...
string smybol;
//
// Order Comment ...
string comment;
};
//
// Model a Deal ...
struct XDeal
{
//
// the ID of the Expert Advisor, that executed the deal ...
ulong magic;
//
// the ID of position, in which the deal was involved ...
ulong positionId;
//
// Ticket ...
ulong ticket;
//
// the name of the deal symbol ...
string symbol;
//
// order by which the deal is executed ...
ulong order;
//
// the time of deal execution ...
datetime time;
//
// Deal price ...
double price;
//
// the financial result of the deal (in deposit currency) ...
double profit;
//
// the amount of swap when position is closed ...
double swap;
//
// the amount of commission of the deal ...
double commission;
//
// the volume of deal ...
double volume;
//
// the deal comment ...
string comment;
//
// the deal type ...
// ------------------------------------------------------------------
// DEAL_TYPE_BUY => Buy ...
// DEAL_TYPE_SELL => Sell ...
// DEAL_TYPE_BUY_CANCELED => Canceled buy deal ...
// There can be a situation when a previously executed buy deal is canceled. In this case,
// the type of the previously executed deal (DEAL_TYPE_BUY) is changed to DEAL_TYPE_BUY_CANCELED,
// and its profit/loss is zeroized. Previously obtained profit/loss is charged/withdrawn using a separated balance operation ...
//
// DEAL_TYPE_SELL_CANCELED => Canceled sell deal ...
// There can be a situation when a previously executed sell deal is canceled. In this case,
// the type of the previously executed deal (DEAL_TYPE_SELL) is changed to DEAL_TYPE_SELL_CANCELED,
// and its profit/loss is zeroized. Previously obtained profit/loss is charged/withdrawn using a separated balance operation
//
// DEAL_TYPE_BALANCE => Balance ...
// DEAL_TYPE_CREDIT => Credit ...
// DEAL_TYPE_CHARGE => Additional charge ...
// DEAL_TYPE_CORRECTION => Correction ...
// DEAL_TAX => Tax charges ...
// DEAL_TYPE_BONUS => Bonus ...
// DEAL_TYPE_INTEREST => Interest rate ...
// DEAL_DIVIDEND => Dividend operations...
// DEAL_DIVIDEND_FRANKED => Franked (non-taxable) dividend operations ...
// DEAL_TYPE_COMMISSION => Additional commission ...
// DEAL_TYPE_COMMISSION_DAILY => Daily commission ...
// DEAL_TYPE_COMMISSION_MONTHLY => Monthly commission ...
// DEAL_TYPE_COMMISSION_AGENT_DAILY => Daily agent commission ...
// DEAL_TYPE_COMMISSION_AGENT_MONTHLY => Monthly agent commission
ENUM_DEAL_TYPE type;
//
// the deal direction ...
// ----------------------------------
// DEAL_ENTRY_IN => Entry in ...
// DEAL_ENTRY_OUT => Entry out ...
// DEAL_ENTRY_INOUT => Reverse ...
// DEAL_ENTRY_OUT_BY => Close a position by an opposite one ...
ENUM_DEAL_ENTRY entry;
//
// deal reson ...
// ---------------------
// DEAL_REASON_SL => The deal was executed as a result of Stop Loss activation ...
// DEAL_REASON_TP => The deal was executed as a result of Take Profit activation ...
// DEAL_REASON_SO => The deal was executed as a result of the Stop Out event ...
// DEAL_REASON_WEB => The deal was executed as a result of activation of an order placed from the web platform ...
// DEAL_REASON_SPLIT => The deal was executed after the split (price reduction) of an instrument, which had an open position during split announcement ...
// DEAL_REASON_CLIENT => The deal was executed as a result of activation of an order placed from a desktop terminal ...
// DEAL_REASON_MOBILE => The deal was executed as a result of activation of an order placed from a mobile application ...
// DEAL_REASON_EXPERT => The deal was executed as a result of activation of an order placed from an MQL5 program, i.e. an Expert Advisor or a script ...
// DEAL_REASON_VMARGIN => The deal was executed after charging the variation margin ...
// DEAL_REASON_ROLLOVER => The deal was executed due to a rollover ...
ENUM_DEAL_REASON reason;
};
//
// END Definitions ...
//
//
// START Import and Inclused requirements ...
//
//
// Includes ...
#include <Trade/Trade.mqh>
#include "../Libraries/x-saherelm.log.lib.mq5"
#include "../Libraries/x-saherelm.common.lib.mq5"
//
// END Import and Inclused requirements ...
//
//
// START Overrides ...
//
//
// XCTrade Base Class ...
class XSCTradeBase : public CTrade
{
public:
//
// Modify Specific Position, by Specific Comment ...
bool PositionModify(
const string symbol, // Symbol
const double sl, // Stop Loss
const double tp, // Take Profit
const string comment = "" // Comment For Modify
)
{
//
// check stopped
if (IsStopped(__FUNCTION__))
{
return (false);
}
//
// check position existence
if (!SelectPosition(symbol))
{
return (false);
}
//
// clean
ClearStructures();
//
// setting request
m_request.action = TRADE_ACTION_SLTP;
m_request.symbol = symbol;
m_request.magic = m_magic;
m_request.sl = sl;
m_request.tp = tp;
m_request.position = PositionGetInteger(POSITION_TICKET);
//
// Specified Comment ...
if (StringLen(comment) > 0)
{
m_request.comment = comment;
}
//
// action and return the result
return (OrderSend(m_request, m_result));
}
bool PositionModify(
const ulong ticket, // Position Ticket
const double sl, // Stop Loss
const double tp, // Take Profit
const string comment = "" // Comment For Modify
)
{
//
// check stopped
if (IsStopped(__FUNCTION__))
{
return (false);
}
//
// check position existence
if (!PositionSelectByTicket(ticket))
{
return (false);
}
//
// clean
ClearStructures();
//
// setting request
m_request.action = TRADE_ACTION_SLTP;
m_request.position = ticket;
m_request.symbol = PositionGetString(POSITION_SYMBOL);
m_request.magic = m_magic;
m_request.sl = sl;
m_request.tp = tp;
//
// Specified Comment ...
if (StringLen(comment) > 0)
{
m_request.comment = comment;
}
//
// action and return the result
return (OrderSend(m_request, m_result));
}
//
// Close Specific Position, by Specific Comment ...
bool PositionClose(
const string symbol, // Symbol
const ulong deviation, // Deviation
const string comment = "" // Comment For Close
)
{
//
bool partial_close = false;
int retry_count = 10;
uint retcode = TRADE_RETCODE_REJECT;
//
// check stopped
if (IsStopped(__FUNCTION__))
{
return (false);
}
//
// clean
ClearStructures();
//
// check filling
if (!FillingCheck(symbol))
{
return (false);
}
//
do
{
//
// check
if (SelectPosition(symbol))
{
//
if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
//
// prepare request for close BUY position
m_request.type = ORDER_TYPE_SELL;
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
}
else
{
//
// prepare request for close SELL position
m_request.type = ORDER_TYPE_BUY;
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
}
}
else
{
//
// position not found
m_result.retcode = retcode;
return (false);
}
//
// setting request
m_request.action = TRADE_ACTION_DEAL;
m_request.symbol = symbol;
m_request.volume = PositionGetDouble(POSITION_VOLUME);
m_request.magic = m_magic;
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
m_request.position = PositionGetInteger(POSITION_TICKET);
//
// Specify Comment ...
m_request.comment = comment;
//
// check volume
double max_volume = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
if (m_request.volume > max_volume)
{
//
m_request.volume = max_volume;
partial_close = true;
}
else
{
partial_close = false;
}
//
// hedging? just send order
if (IsHedging())
{
return (OrderSend(m_request, m_result));
}
//
// order send
if (!OrderSend(m_request, m_result))
{
//
if (--retry_count != 0)
{
continue;
}
//
if (retcode == TRADE_RETCODE_DONE_PARTIAL)
{
m_result.retcode = retcode;
}
//
return (false);
}
//
//--- WARNING. If position volume exceeds the maximum volume allowed for deal,
//--- and when the asynchronous trade mode is on, for safety reasons, position is closed not completely,
//--- but partially. It is decreased by the maximum volume allowed for deal.
if (m_async_mode)
{
break;
}
//
retcode = TRADE_RETCODE_DONE_PARTIAL;
if (partial_close)
{
Sleep(1000);
}
} while (partial_close);
//
// succeed
return (true);
}
bool PositionClose(
const ulong ticket, // Position Ticket
const ulong deviation, // Deviation
const string comment = "" // Comment For Close
)
{
//
// check stopped
if (IsStopped(__FUNCTION__))
{
return (false);
}
//
// check position existence
if (!PositionSelectByTicket(ticket))
{
return (false);
}
//
string symbol = PositionGetString(POSITION_SYMBOL);
//
// clean
ClearStructures();
//
// check filling
if (!FillingCheck(symbol))
{
return (false);
}
//
// check
if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
//
// prepare request for close BUY position
m_request.type = ORDER_TYPE_SELL;
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
}
else
{
//
// prepare request for close SELL position
m_request.type = ORDER_TYPE_BUY;
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
}
//
// setting request
m_request.action = TRADE_ACTION_DEAL;
m_request.position = ticket;
m_request.symbol = symbol;
m_request.volume = PositionGetDouble(POSITION_VOLUME);
m_request.magic = m_magic;
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
//
// Specify Comment ...
m_request.comment = comment;
//
// close position
return (OrderSend(m_request, m_result));
}
//
// Partial Close Specific Position, by Specific Comment ...
bool PositionClosePartial(
const string symbol, // Symbol
const double volume, // Partial Closing Volume
const ulong deviation, // Deviation
const string comment = "" // Comment For Close
)
{
//
uint retcode = TRADE_RETCODE_REJECT;
//
// check stopped
if (IsStopped(__FUNCTION__))
{
return (false);
}
//
// for hedging mode only
if (!IsHedging())
{
return (false);
}
//
// clean
ClearStructures();
//
// check filling
if (!FillingCheck(symbol))
{
return (false);
}
//
// check
if (SelectPosition(symbol))
{
//
if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
//
// prepare request for close BUY position
m_request.type = ORDER_TYPE_SELL;
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
}
else
{
//
// prepare request for close SELL position
m_request.type = ORDER_TYPE_BUY;
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
}
}
else
{
//
// position not found
m_result.retcode = retcode;
return (false);
}
//
// check volume
double position_volume = PositionGetDouble(POSITION_VOLUME);
if (position_volume > volume)
{
position_volume = volume;
}
//
// setting request
m_request.action = TRADE_ACTION_DEAL;
m_request.symbol = symbol;
m_request.volume = position_volume;
m_request.magic = m_magic;
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
m_request.position = PositionGetInteger(POSITION_TICKET);
//
// Specifieng Comment ...
m_request.comment = comment;
//
// hedging? just send order
return (OrderSend(m_request, m_result));
}
bool PositionClosePartial(
const ulong ticket, // Position Ticket
const double volume, // Partial Closing Volume
const ulong deviation, // Deviation
const string comment = "" // Comment For Close
)
{
//
// check stopped
if (IsStopped(__FUNCTION__))
{
return (false);
}
//
// for hedging mode only
if (!IsHedging())
{
return (false);
}
//
// check position existence
if (!PositionSelectByTicket(ticket))
{
return (false);
}
//
string symbol = PositionGetString(POSITION_SYMBOL);
//
// clean
ClearStructures();
//
// check filling
if (!FillingCheck(symbol))
{
return (false);
}
//
// check
if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
//
// prepare request for close BUY position
m_request.type = ORDER_TYPE_SELL;
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
}
else
{
//
// prepare request for close SELL position
m_request.type = ORDER_TYPE_BUY;
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
}
//
// check volume
double position_volume = PositionGetDouble(POSITION_VOLUME);
if (position_volume > volume)
{
position_volume = volume;
}
//
// setting request
m_request.action = TRADE_ACTION_DEAL;
m_request.position = ticket;
m_request.symbol = symbol;
m_request.volume = position_volume;
m_request.magic = m_magic;
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
//
// Specifieng Comment ...
m_request.comment = comment;
//
// close position
return (OrderSend(m_request, m_result));
}
};
//
// END Overrides ...
//
//
// a Class for Manage Trades ...
class XSCTrade
{
//
// Public ...
public:
//
// Constructor ...
void XSCTrade(
int slippage, // Specify Slippage
ulong magicNumber // Specify Magic Number
)
{
//
mSlippage = slippage;
mMagicNumber = magicNumber;
//
mPeriodPrefix = "PR";
//
mTrader = new XSCTradeBase();
//
mTrader.SetAsyncMode(false);
mTrader.SetDeviationInPoints(mSlippage);
mTrader.SetExpertMagicNumber(mMagicNumber);
//
// Setting Log Level ...
mTrader.LogLevel(LOG_LEVEL_ERRORS);
//
// Prepare Trade Event Materials ...
mStartDate = 0;
mLastOrdersCount = OrdersTotal();
mLastDealsCount = HistoryDealsTotal();
mLastPositionsCount = PositionsTotal();
//
GetDeals(mLastDeals);
GetOrders(mLastOrders);
GetPositions(mLastPositions);
}
//
// Deconstructor ...
void ~XSCTrade()
{
}
//
// START Event Processing ...
//
//
// This Must be Called Every time OnTrade Event Happening ...
void HandleOnTrade()
{
//
int ordersCount = OrdersTotal();
int dealsCount = HistoryDealsTotal();
int positionsCount = PositionsTotal();
//
// LogMessage("Salam");
//
GetDeals(mDeals);
GetOrders(mOrders);
GetPositions(mPositions);
//
datetime currentTime = TimeCurrent();
HistorySelect(mStartDate, currentTime);
//
// Processing Positions ...
if (positionsCount > mLastPositionsCount)
{
//
// Position Appeared ...
LogMessage("Position Appeared ...");
//
// Update Deals ...
dealsCount = HistoryDealsTotal();
GetDeals(mDeals);
}
else if (positionsCount < mLastPositionsCount)
{
//
// Position Disappeared ...
LogMessage("Position Disappeared ...");
//
// Update Deals ...
dealsCount = HistoryDealsTotal();
GetDeals(mDeals);
}
//
// Processing Order Changes ...
if (ordersCount > mLastOrdersCount)
{
//
// Orders Appeared ...
LogMessage("Orders Appeared ...");
}
else if (ordersCount < mLastOrdersCount)
{
//
// Orders Disappeared ...
LogMessage("Orders Disappeared ...");
}
//
// Processing Deal Changes ...
if (dealsCount > mLastDealsCount)
{
//
// Deals Appeared ...
LogMessage("Deals Appeared ...");
}
else if (dealsCount > mLastDealsCount)
{
//
// Deals Disappeard ...
LogMessage("Deals Disappeard ...");
}
//
// Update Date after processing ...
//
mLastOrdersCount = OrdersTotal();
mLastDealsCount = HistoryDealsTotal();
mLastPositionsCount = PositionsTotal();
//
GetDeals(mLastDeals);
GetOrders(mLastOrders);
GetPositions(mLastPositions);
}
//
// END Event Processing ...
//
//
// START Provided Functions ...
//
//
// Count Currently Open Positions ...
int PositionsCount(
string symbol = "", // Specified Symbol
ENUM_POSITION_TYPE type = NULL // Specified Type
)
{
//
int result = 0;
//
int allPositions = 0;
int longPositions = 0;
int shortPositions = 0;
//
int positionsCount = PositionsTotal();
if (positionsCount <= 0)
{
return result;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
if (!mPositionInfo.SelectByIndex(i))
{
continue;
}
//
if (mPositionInfo.Magic() != mMagicNumber)
{
continue;
}
//
string positionSymbol = mPositionInfo.Symbol();
//
ENUM_POSITION_TYPE positionType = mPositionInfo.PositionType();
//
if (
type == NULL &&
StringLen(symbol) <= 0)
{
//
allPositions++;
//
continue;
}
else if (
type != NULL &&
StringLen(symbol) > 0)
{
//
if (type == positionType &&
symbol == positionSymbol)
{
//
if (type == POSITION_TYPE_BUY)
{
longPositions++;
}
else
{
shortPositions++;
}
//
continue;
}
}
else if (StringLen(symbol) > 0)
{
//
if (positionSymbol == symbol)
{
//
allPositions++;
//
continue;
}
}
else if (type != NULL)
{
//
if (type == positionType)
{
//
if (type == POSITION_TYPE_BUY)
{
longPositions++;
}
else
{
shortPositions++;
}
//
continue;
}
}
}
//
// Specify Result ...
if (
type == NULL &&
StringLen(symbol) <= 0)
{
//
result = allPositions;
}
else if (
type != NULL &&
StringLen(symbol) > 0)
{
//
if (type == POSITION_TYPE_BUY)
{
result = longPositions;
}
else
{
result = shortPositions++;
}
}
else if (StringLen(symbol) > 0)
{
//
result = allPositions;
}
else if (type != NULL)
{
//
if (type == POSITION_TYPE_BUY)
{
result = longPositions;
}
else
{
result = shortPositions;
}
}
//
return result;
}
//
// Retrieve Max In Profit Position Ticket ...
ulong GetMaxInProfitPosition(
string symbol = "", // Specified Symbol
ENUM_POSITION_TYPE type = NULL // Specified Type
)
{
//
ulong result = -1;
//
double profit = 0;
//
int positionsCount = PositionsTotal();
if (positionsCount <= 0)
{
return result;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
if (!mPositionInfo.SelectByIndex(i))
{
continue;
}
//
if (mPositionInfo.Magic() != mMagicNumber)
{
continue;
}
//
ulong positionTicket = mPositionInfo.Ticket();
//
string positionSymbol = mPositionInfo.Symbol();
//
double positionProfit = mPositionInfo.Profit();
double positionAbsProfit = MathAbs(positionProfit);
if (positionProfit <= 0)
{
continue;
}
//
ENUM_POSITION_TYPE positionType = mPositionInfo.PositionType();
//
if (
type == NULL &&
StringLen(symbol) <= 0)
{
//
if (profit < positionAbsProfit)
{
//
result = positionTicket;
profit = positionAbsProfit;
}
//
continue;
}
else if (
type != NULL &&
StringLen(symbol) > 0)
{
//
if (type == positionType &&
symbol == positionSymbol)
{
//
if (type == POSITION_TYPE_BUY)
{
//
if (profit < positionAbsProfit)
{
//
result = positionTicket;
profit = positionAbsProfit;
}
}
else
{
//
if (profit < positionAbsProfit)
{
//
result = positionTicket;
profit = positionAbsProfit;
}
}
//
continue;
}
}
else if (StringLen(symbol) > 0)
{
//
if (positionSymbol == symbol)
{
//
if (profit < positionAbsProfit)
{
//
result = positionTicket;
profit = positionAbsProfit;
}
//
continue;
}
}
else if (type != NULL)
{
//
if (type == positionType)
{
//
if (type == POSITION_TYPE_BUY)
{
//
if (profit < positionAbsProfit)
{
//
result = positionTicket;
profit = positionAbsProfit;
}
}
else
{
//
if (profit < positionAbsProfit)
{
//
result = positionTicket;
profit = positionAbsProfit;
}
}
//
continue;
}
}
}
//
return result;
}
//
// Retrieve Max In DrawDown Position Ticket ...
ulong GetMaxInDrawDownPosition(
string symbol = "", // Specified Symbol
ENUM_POSITION_TYPE type = NULL // Specified Type
)
{
//
ulong result = -1;
//
double profit = 0;
//
int positionsCount = PositionsTotal();
if (positionsCount <= 0)
{
return result;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
if (!mPositionInfo.SelectByIndex(i))
{
continue;
}
//
if (mPositionInfo.Magic() != mMagicNumber)
{
continue;
}
//
ulong positionTicket = mPositionInfo.Ticket();
//
string positionSymbol = mPositionInfo.Symbol();
//
double positionProfit = mPositionInfo.Profit();
double positionAbsProfit = MathAbs(positionProfit);
if (positionProfit >= 0)
{
continue;
}
//
ENUM_POSITION_TYPE positionType = mPositionInfo.PositionType();
//
if (
type == NULL &&
StringLen(symbol) <= 0)
{
//
if (profit < positionAbsProfit)
{
//
result = positionTicket;
profit = positionAbsProfit;
}
//
continue;
}
else if (
type != NULL &&
StringLen(symbol) > 0)
{
//
if (type == positionType &&
symbol == positionSymbol)
{
//
if (type == POSITION_TYPE_BUY)
{
//
if (profit < positionAbsProfit)
{
//
result = positionTicket;
profit = positionAbsProfit;
}
}
else
{
//
if (profit < positionAbsProfit)
{
//
result = positionTicket;
profit = positionAbsProfit;
}
}
//
continue;
}
}
else if (StringLen(symbol) > 0)
{
//
if (positionSymbol == symbol)
{
//
if (profit < positionAbsProfit)
{
//
result = positionTicket;
profit = positionAbsProfit;
}
//
continue;
}
}
else if (type != NULL)
{
//
if (type == positionType)
{
//
if (type == POSITION_TYPE_BUY)
{
//
if (profit < positionAbsProfit)
{
//
result = positionTicket;
profit = positionAbsProfit;
}
}
else
{
//
if (profit < positionAbsProfit)
{
//
result = positionTicket;
profit = positionAbsProfit;
}
}
//
continue;
}
}
}
//
return result;
}
//
// Retrieve Specific Position's Profit ...
double GetPositionProfit(
const ulong ticket // Specified Position Ticket
)
{
//
double result = 0;
//
int positionsCount = PositionsTotal();
if (positionsCount <= 0)
{
return result;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
if (!mPositionInfo.SelectByIndex(i))
{
continue;
}
//
if (mPositionInfo.Magic() != mMagicNumber)
{
continue;
}
//
ulong positionTicket = mPositionInfo.Ticket();
if (positionTicket != ticket)
{
continue;
}
//
result = mPositionInfo.Profit();
break;
}
//
return result;
}
//
// Retrieve Specific Position's OpenDate ...
datetime GetPositionOpenDate(
const ulong ticket // Specified Position Ticket
)
{
//
datetime result = 0;
//
int positionsCount = PositionsTotal();
if (positionsCount <= 0)
{
return result;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
if (!mPositionInfo.SelectByIndex(i))
{
continue;
}
//
if (mPositionInfo.Magic() != mMagicNumber)
{
continue;
}
//
ulong positionTicket = mPositionInfo.Ticket();
if (positionTicket != ticket)
{
continue;
}
//
result = mPositionInfo.Time();
break;
}
//
return result;
}
//
// Retrieve Specific Position's Comment ...
string GetPositionComment(
const ulong ticket // Specified Position Ticket
)
{
//
string result = "";
//
int positionsCount = PositionsTotal();
if (positionsCount <= 0)
{
return result;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
if (!mPositionInfo.SelectByIndex(i))
{
continue;
}
//
if (mPositionInfo.Magic() != mMagicNumber)
{
continue;
}
//
ulong positionTicket = mPositionInfo.Ticket();
if (positionTicket != ticket)
{
continue;
}
//
result = mPositionInfo.Comment();
break;
}
//
return result;
}
//
// Retrieve All Open Positions Profit ...
double GetPositionsProfit(
string symbol = "", // Specified Symbol
ENUM_POSITION_TYPE type = NULL // Specified Type
)
{
//
double result = 0;
//
double profit = 0;
//
int positionsCount = PositionsTotal();
if (positionsCount <= 0)
{
return result;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
if (!mPositionInfo.SelectByIndex(i))
{
continue;
}
//
if (mPositionInfo.Magic() != mMagicNumber)
{
continue;
}
//
ulong positionTicket = mPositionInfo.Ticket();
//
string positionSymbol = mPositionInfo.Symbol();
//
double positionProfit = mPositionInfo.Profit();
//
ENUM_POSITION_TYPE positionType = mPositionInfo.PositionType();
//
if (
type == NULL &&
StringLen(symbol) <= 0)
{
//
if (profit < positionProfit)
{
//
result += positionProfit;
}
//
continue;
}
else if (
type != NULL &&
StringLen(symbol) > 0)
{
//
if (type == positionType &&
symbol == positionSymbol)
{
//
if (type == POSITION_TYPE_BUY)
{
//
if (profit < positionProfit)
{
//
result += positionProfit;
}
}
else
{
//
if (profit < positionProfit)
{
//
result += positionProfit;
}
}
//
continue;
}
}
else if (StringLen(symbol) > 0)
{
//
if (positionSymbol == symbol)
{
//
if (profit < positionProfit)
{
//
result += positionProfit;
}
//
continue;
}
}
else if (type != NULL)
{
//
if (type == positionType)
{
//
if (type == POSITION_TYPE_BUY)
{
//
if (profit < positionProfit)
{
//
result += positionProfit;
}
}
else
{
//
if (profit < positionProfit)
{
//
result += positionProfit;
}
}
//
continue;
}
}
}
//
return result;
}
//
// Find Oldes Position ...
ulong GetOldestPosition(
string symbol = "", // Specified Symbol
ENUM_POSITION_TYPE type = NULL // Specified Type
)
{
//
ulong result = 0;
//
int age = 0;
//
int positionsCount = PositionsTotal();
if (positionsCount <= 0)
{
return result;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
if (!mPositionInfo.SelectByIndex(i))
{
continue;
}
//
if (mPositionInfo.Magic() != mMagicNumber)
{
continue;
}
//
ulong positionTicket = mPositionInfo.Ticket();
//
string positionSymbol = mPositionInfo.Symbol();
//
double positionProfit = mPositionInfo.Profit();
double positionAbsProfit = MathAbs(positionProfit);
if (positionProfit >= 0)
{
continue;
}
//
ENUM_POSITION_TYPE positionType = mPositionInfo.PositionType();
//
string positionComment = mPositionInfo.Comment();
ENUM_TIMEFRAMES positionPeriod = ExtractPositionPeriod(positionComment);
//
// Calculate Position's Age ...
datetime positionOpenTime = mPositionInfo.Time();
//
// Retrieve Trade Open Bar Index based on Current Period ...
int positionOpenBarIndex = iBarShift(
positionSymbol,
positionPeriod,
positionOpenTime);
//
// Calculate Trade Life ...
int positionAge = MathAbs(0 - positionOpenBarIndex);
//
if (
type == NULL &&
StringLen(symbol) <= 0)
{
//
if (age < positionAge)
{
//
age = positionAge;
result = positionTicket;
}
//
continue;
}
else if (
type != NULL &&
StringLen(symbol) > 0)
{
//
if (type == positionType &&
symbol == positionSymbol)
{
//
if (type == POSITION_TYPE_BUY)
{
//
if (age < positionAge)
{
//
age = positionAge;
result = positionTicket;
}
}
else
{
//
if (age < positionAge)
{
//
age = positionAge;
result = positionTicket;
}
}
//
continue;
}
}
else if (StringLen(symbol) > 0)
{
//
if (positionSymbol == symbol)
{
//
if (age < positionAge)
{
//
age = positionAge;
result = positionTicket;
}
//
continue;
}
}
else if (type != NULL)
{
//
if (type == positionType)
{
//
if (type == POSITION_TYPE_BUY)
{
//
if (age < positionAge)
{
//
age = positionAge;
result = positionTicket;
}
}
else
{
//
if (age < positionAge)
{
//
age = positionAge;
result = positionTicket;
}
}
//
continue;
}
}
}
//
return result;
}
//
// 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 = "" // comment
)
{
//
bool result = false;
//
string mPeriodStr = GeneratePositionPeriodComment(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 = "" // comment
)
{
//
bool result = false;
//
string mPeriodStr = GeneratePositionPeriodComment(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 = "" // comment
)
{
//
bool result = false;
//
string mPeriodStr = GeneratePositionPeriodComment(period);
//
comment += mPeriodStr;
//
result = mTrader.BuyStop(
volume,
price,
symbol,
sl,
tp,
typeTime,
expiration,
comment);
//
return result;
}
//
// 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 = "" // comment
)
{
//
bool result = false;
//
string mPeriodStr = GeneratePositionPeriodComment(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 = "" // comment
)
{
//
bool result = false;
//
string mPeriodStr = GeneratePositionPeriodComment(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 = "" // comment
)
{
//
bool result = false;
//
string mPeriodStr = GeneratePositionPeriodComment(period);
//
comment += mPeriodStr;
//
result = mTrader.SellStop(
volume,
price,
symbol,
sl,
tp,
typeTime,
expiration,
comment);
//
return result;
}
//
// Modify Position ...
bool Modify(
const ulong ticket, // position ticket
double sl = 0, // stop loss price
double tp = 0, // take profit
string comment = "" // comment
)
{
//
bool result = false;
//
// Validate Args ...
if (tp <= 0 && sl <= 0)
{
result = false;
return result;
}
//
// Use Position Comment when there isn't new One ...
if (StringLen(comment) == 0)
{
comment = GetPositionComment(ticket);
}
//
result = mTrader.PositionModify(
ticket,
sl,
tp,
comment);
//
return result;
}
//
// Close Position By Ticket ...
bool Close(
ulong ticket, // Position Ticket
string comment = "" // Close Position By Specific Comment
)
{
//
bool result = false;
//
// Try to Select Position ...
if (!mPositionInfo.SelectByTicket(ticket))
{
return result;
}
//
// Close Position By Specific Comment ...
result = mTrader.PositionClose(
ticket,
mSlippage,
comment);
//
return result;
}
//
// Close All Positions ...
void CloseAllPositions(
string comment = "" // Close Position By Specific Comment
)
{
//
int positionsCount = PositionsTotal();
if (positionsCount <= 0)
{
return;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
if (!mPositionInfo.SelectByIndex(i))
{
continue;
}
//
if (mPositionInfo.Magic() != mMagicNumber)
{
continue;
}
//
ulong positionTicket = mPositionInfo.Ticket();
Close(
positionTicket,
comment);
}
}
//
// Close Partial By Ticket ...
bool ClosePartial(
ulong ticket, // Position Ticket
double volume, // Volume Size for Partial Closing
string comment = "" // Close Position By Specific Comment
)
{
//
bool result = false;
//
if (!mPositionInfo.SelectByTicket(ticket))
{
return result;
}
//
result = mTrader.PositionClosePartial(
ticket,
volume,
mSlippage,
comment);
//
return result;
}
//
// Find Position Open Bar Index ...
int GetPositionOpenBarIndex(
const ulong ticket // position ticket
)
{
//
int result = -1;
//
int positionsCount = PositionsTotal();
if (positionsCount <= 0)
{
return result;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
if (!mPositionInfo.SelectByIndex(i))
{
continue;
}
//
if (mPositionInfo.Magic() != mMagicNumber)
{
continue;
}
//
ulong positionTicket = mPositionInfo.Ticket();
if (positionTicket != ticket)
{
continue;
}
//
string symbol = mPositionInfo.Symbol();
datetime openAt = mPositionInfo.Time();
string comment = mPositionInfo.Comment();
ENUM_TIMEFRAMES period = ExtractPositionPeriod(comment);
//
result = iBarShift(
symbol,
period,
openAt);
}
//
return result;
}
//
// Retrieve Positions as an Array ...
void GetPositions(
XPosition &result[] // Hold Result ...
)
{
//
Clear(result);
//
int positionsCount = PositionsTotal();
if (positionsCount <= 0)
{
return;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
if (!mPositionInfo.SelectByIndex(i))
{
continue;
}
//
if (mPositionInfo.Magic() != mMagicNumber)
{
continue;
}
//
ulong magic = mPositionInfo.Magic();
ulong ticket = mPositionInfo.Ticket();
string symbol = mPositionInfo.Symbol();
datetime openAt = mPositionInfo.Time();
double swap = mPositionInfo.Swap();
double sl = mPositionInfo.StopLoss();
double tp = mPositionInfo.TakeProfit();
double profit = mPositionInfo.Profit();
double volume = mPositionInfo.Volume();
string comment = mPositionInfo.Comment();
double entry = mPositionInfo.PriceOpen();
double price = mPositionInfo.PriceCurrent();
double commission = mPositionInfo.Commission();
ENUM_POSITION_TYPE type = mPositionInfo.PositionType();
ENUM_TIMEFRAMES period = ExtractPositionPeriod(comment);
//
XPosition iPosition = {};
//
iPosition.tp = tp;
iPosition.sl = sl;
iPosition.swap = swap;
iPosition.type = type;
iPosition.magic = magic;
iPosition.entry = entry;
iPosition.price = price;
iPosition.ticket = ticket;
iPosition.symbol = symbol;
iPosition.period = period;
iPosition.volume = volume;
iPosition.openAt = openAt;
iPosition.profit = profit;
iPosition.comment = comment;
iPosition.commission = commission;
//
Add(
iPosition,
result);
}
}
//
void GetPositions(
XPosition &result[], // Hold Result ...
string symbol, // Trading Symbol ...
ENUM_TIMEFRAMES period // Trading Period ...
)
{
//
Clear(result);
//
int positionsCount = PositionsTotal();
if (positionsCount <= 0)
{
return;
}
//
XPosition allPositions[];
GetPositions(allPositions);
//
for (int i = 0; i < positionsCount; i++)
{
//
XPosition iPosition = allPositions[i];
//
if (symbol != iPosition.symbol)
{
continue;
}
//
ENUM_TIMEFRAMES positionPeriod = ExtractPositionPeriod(iPosition.comment);
if (period != positionPeriod)
{
continue;
}
//
Add(
iPosition,
result);
}
}
//
// Retrieve in Profit Positions ...
void GetInProfitPositions(
XPosition &result[] // Hold Result ...
)
{
//
Clear(result);
//
XPosition allPositions[];
GetPositions(allPositions);
int positionsCount = ArraySize(allPositions);
if (positionsCount <= 0)
{
return;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
XPosition iPosition = allPositions[i];
//
if (iPosition.profit > 0)
{
Add(
iPosition,
result);
}
}
}
//
// Retrieve in Drawdown Positions ...
void GetInDrawdownPositions(
XPosition &result[] // Hold Result ...
)
{
//
Clear(result);
//
XPosition allPositions[];
GetPositions(allPositions);
int positionsCount = ArraySize(allPositions);
if (positionsCount <= 0)
{
return;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
XPosition iPosition = allPositions[i];
//
if (iPosition.profit < 0)
{
Add(
iPosition,
result);
}
}
}
//
// Close a Collection Of Positions ...
void ClosePositions(
XPosition &positions[], // Positions to Close ...
string comment = "" // Specified Comment ...
)
{
//
int positionsCount = ArraySize(positions);
if (positionsCount <= 0)
{
return;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
XPosition iPosition = positions[i];
//
Close(
iPosition.ticket,
comment);
}
}
//
// Retrieve All Orders ...
void GetOrders(XOrder &result[])
{
//
Clear(result);
//
int ordersCount = OrdersTotal();
if (ordersCount <= 0)
{
return;
}
//
// Since Orders may be so more than positions ...
// start Orders Collectiong Loops reverse ...
for (int i = ordersCount - 1; i >= 0; i--)
{
//
if (!mPositionInfo.SelectByIndex(i))
{
continue;
}
//
if (mOrderInfo.Magic() != mMagicNumber)
{
continue;
}
//
// Reading Order Info ...
long magic = mOrderInfo.Magic();
double sl = mOrderInfo.StopLoss();
ulong ticket = mOrderInfo.Ticket();
double tp = mOrderInfo.TakeProfit();
string smybol = mOrderInfo.Symbol();
string comment = mOrderInfo.Comment();
long pocitionID = mOrderInfo.PositionId();
double openPrice = mOrderInfo.PriceOpen();
datetime setupAt = mOrderInfo.TimeSetup();
datetime executedAt = mOrderInfo.TimeDone();
ENUM_ORDER_STATE state = mOrderInfo.State();
ENUM_ORDER_TYPE type = mOrderInfo.OrderType();
double currentPrice = mOrderInfo.PriceCurrent();
datetime expiredAt = mOrderInfo.TimeExpiration();
double initialVolume = mOrderInfo.VolumeInitial();
double currentVolume = mOrderInfo.VolumeCurrent();
double stopLimitPrice = mOrderInfo.PriceStopLimit();
ENUM_ORDER_TYPE_TIME lifetime = mOrderInfo.TypeTime();
ENUM_ORDER_TYPE_FILLING filling = mOrderInfo.TypeFilling();
//
// Now Create an instance of XOrder struct and fill it by
// retrieved data, and add it to results ...
XOrder iOrder = {};
//
iOrder.sl = sl;
iOrder.tp = tp;
iOrder.type = type;
iOrder.state = state;
iOrder.magic = magic;
iOrder.ticket = ticket;
iOrder.smybol = smybol;
iOrder.setupAt = setupAt;
iOrder.comment = comment;
iOrder.filling = filling;
iOrder.lifetime = lifetime;
iOrder.expiredAt = expiredAt;
iOrder.openPrice = openPrice;
iOrder.pocitionID = pocitionID;
iOrder.executedAt = executedAt;
iOrder.currentPrice = currentPrice;
iOrder.initialVolume = initialVolume;
iOrder.currentVolume = currentVolume;
iOrder.stopLimitPrice = stopLimitPrice;
//
Add(
iOrder,
result);
}
}
//
// Retrieve All Deals ...
void GetDeals(XDeal &result[])
{
//
Clear(result);
//
int dealsCount = HistoryDealsTotal();
if (dealsCount <= 0)
{
return;
}
//
for (int i = dealsCount - 1; i >= 0; i--)
{
//
if (!mDealInfo.SelectByIndex(i))
{
continue;
}
//
if (mDealInfo.Magic() != mMagicNumber)
{
continue;
}
//
// Reading Order Info ...
double swap = mDealInfo.Swap();
ulong magic = mDealInfo.Magic();
ulong order = mDealInfo.Order();
ulong ticket = mDealInfo.Ticket();
datetime time = mDealInfo.Time();
double price = mDealInfo.Price();
string symbol = mDealInfo.Symbol();
double profit = mDealInfo.Profit();
double volume = mDealInfo.Volume();
string comment = mDealInfo.Comment();
ulong positionId = mDealInfo.PositionId();
ENUM_DEAL_ENTRY entry = mDealInfo.Entry();
double commission = mDealInfo.Commission();
ENUM_DEAL_TYPE type = mDealInfo.DealType();
ENUM_DEAL_REASON reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(
ticket,
DEAL_REASON);
//
XDeal iDeal = {};
//
iDeal.swap = swap;
iDeal.time = time;
iDeal.type = type;
iDeal.magic = magic;
iDeal.entry = entry;
iDeal.order = order;
iDeal.price = price;
iDeal.ticket = ticket;
iDeal.symbol = symbol;
iDeal.profit = profit;
iDeal.volume = volume;
iDeal.reason = reason;
iDeal.comment = comment;
iDeal.positionId = positionId;
iDeal.commission = commission;
//
Add(
iDeal,
result);
}
}
//
// Generate String Representation Of Position's Time Frame ...
string GeneratePositionPeriodComment(ENUM_TIMEFRAMES period)
{
//
string result = mPeriodPrefix + "(" + ToString(period) + ")";
//
return result;
}
//
// Extract Position Time Frame From it's Comment ...
ENUM_TIMEFRAMES ExtractPositionPeriod(string comment)
{
//
ENUM_TIMEFRAMES result = _Period;
//
if (StringLen(comment) <= 0)
{
return result;
}
//
string periodStr = ExtractString(
comment,
mPeriodPrefix + "(",
")");
if (StringLen(periodStr) <= 0)
{
return result;
}
//
result = ToPeriod(periodStr);
//
return result;
}
//
// Retrieve Last Open Position Ticket ...
ulong GetLastOpenPositionTicket()
{
//
ulong result = 0;
int positionsCount = PositionsTotal();
if (positionsCount <= 0)
{
return result;
}
//
result = PositionGetTicket(PositionsTotal() - 1);
//
return result;
}
//
// END Provided Functions ...
//
//
// Protected ...
protected:
//
// Private ...
private:
//
// using deviation, it must be in Point ...
int mSlippage;
//
// using magic number ...
ulong mMagicNumber;
//
// using CTrade instance ...
XSCTradeBase mTrader;
//
// Deals Info ...
CDealInfo mDealInfo;
//
// Order Info ...
COrderInfo mOrderInfo;
//
// using PositionInfo instance ...
CPositionInfo mPositionInfo;
//
// ADDITIONAL Data ...
//
string mPeriodPrefix;
//
// Trade Processing ...
datetime mStartDate;
int mLastDealsCount;
int mLastOrdersCount;
int mLastPositionsCount;
//
XDeal mDeals[];
XOrder mOrders[];
XPosition mPositions[];
//
XDeal mLastDeals[];
XOrder mLastOrders[];
XPosition mLastPositions[];
};
//
// START Usefull Functions ...
//
//
// Add Specific XPosition to Specified Array ...
void Add(
XDeal &item,
XDeal &buffer[])
{
//
ArrayResize(
buffer,
ArraySize(buffer) + 1);
//
buffer[ArraySize(buffer) - 1] = item;
}
void Add(
XOrder &item,
XOrder &buffer[])
{
//
ArrayResize(
buffer,
ArraySize(buffer) + 1);
//
buffer[ArraySize(buffer) - 1] = item;
}
void Add(
XPosition &item,
XPosition &buffer[])
{
//
ArrayResize(
buffer,
ArraySize(buffer) + 1);
//
buffer[ArraySize(buffer) - 1] = item;
}
//
// Clear All Items Inside Array ...
void Clear(XDeal &buffer[])
{
//
ArrayFree(buffer);
ArrayResize(buffer, 0);
}
void Clear(XOrder &buffer[])
{
//
ArrayFree(buffer);
ArrayResize(buffer, 0);
}
void Clear(XPosition &buffer[])
{
//
ArrayFree(buffer);
ArrayResize(buffer, 0);
}
//
// END Usefull Functions ...
//