5013 lines
116 KiB
Plaintext
5013 lines
116 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
|
|
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
ENUM_TIMEFRAMES period;
|
|
};
|
|
|
|
//
|
|
// 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;
|
|
};
|
|
|
|
//
|
|
// Model a Position with all of it's Supports ...
|
|
struct XPositionPack
|
|
{
|
|
//
|
|
ulong ticket;
|
|
|
|
//
|
|
XPosition position;
|
|
|
|
//
|
|
int supportOrdersCount;
|
|
int supportPositionsCount;
|
|
|
|
//
|
|
XOrder supportOrders[];
|
|
XPosition supportPositions[];
|
|
};
|
|
|
|
//
|
|
// Model OnTrade Event Handler Result on XCTrade Class ...
|
|
struct XOnTradeHandlerState
|
|
{
|
|
//
|
|
bool hasNewDeal;
|
|
int newDeals;
|
|
|
|
//
|
|
bool hasNewOrder;
|
|
int newOrders;
|
|
|
|
//
|
|
bool hasNewPosition;
|
|
int newPositions;
|
|
|
|
//
|
|
bool hasNewHistoryOrder;
|
|
int newHistoryOrders;
|
|
};
|
|
|
|
//
|
|
typedef void (*TOnDealsChanged)(int count);
|
|
|
|
//
|
|
typedef void (*TOnOrdersChanged)(int count);
|
|
|
|
//
|
|
typedef void (*TOnPositionsChanged)(int count);
|
|
|
|
//
|
|
typedef void (*TOnStopLoss)(const XDeal &deal);
|
|
typedef void (*TOnTakeProfit)(const XDeal &deal);
|
|
|
|
//
|
|
typedef void (*TOnTradeStateChanged)(const XOnTradeHandlerState &state);
|
|
|
|
//
|
|
// 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"
|
|
#include "../Libraries/x-saherelm.seriallizer.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 ...
|
|
//
|
|
mDaysForRead = 1;
|
|
mScanStarted = false;
|
|
|
|
//
|
|
// Reset On Trade Counter ...
|
|
ResetOnTradeContext();
|
|
|
|
//
|
|
// Init On Trade Context ...
|
|
InitOnTradeContext();
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
void ~XSCTrade()
|
|
{
|
|
}
|
|
|
|
//
|
|
// START Event Processing ...
|
|
//
|
|
|
|
//
|
|
// This Must be Called Every time OnTrade Event Happening ...
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Process On Trade Requirement
|
|
void HandleOnTradeEvent(
|
|
XOnTradeHandlerState &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
if (mScanStarted)
|
|
{
|
|
ProcessOnTradeContext(result);
|
|
}
|
|
else
|
|
{
|
|
InitOnTradeContext();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Event Emitters ...
|
|
|
|
//
|
|
// STATE Changed ...
|
|
|
|
//
|
|
// Clear Trade State Changed Event Handlers ...
|
|
void ClearOnTradeStateChangedEventHandlers()
|
|
{
|
|
//
|
|
ArrayFree(mTradeStateChangedEventHandlers);
|
|
|
|
//
|
|
ArrayResize(mTradeStateChangedEventHandlers, 0);
|
|
}
|
|
|
|
//
|
|
// Add New Deals Changed Event Handler ...
|
|
void AddOnTradeStateChangedEventHandler(TOnTradeStateChanged handler)
|
|
{
|
|
//
|
|
ArrayResize(
|
|
mTradeStateChangedEventHandlers,
|
|
ArraySize(mTradeStateChangedEventHandlers) + 1);
|
|
|
|
//
|
|
mTradeStateChangedEventHandlers[ArraySize(mTradeStateChangedEventHandlers) - 1] = handler;
|
|
}
|
|
|
|
//
|
|
// DEALS ...
|
|
|
|
//
|
|
// Clear Deals Changed Event Handlers ...
|
|
void ClearOnDealsChangedEventHandlers()
|
|
{
|
|
//
|
|
ArrayFree(mDealsChangedEventHandlers);
|
|
|
|
//
|
|
ArrayResize(mDealsChangedEventHandlers, 0);
|
|
}
|
|
|
|
//
|
|
// Add New Deals Changed Event Handler ...
|
|
void AddOnDealsChangedEventHandler(TOnDealsChanged handler)
|
|
{
|
|
//
|
|
ArrayResize(
|
|
mDealsChangedEventHandlers,
|
|
ArraySize(mDealsChangedEventHandlers) + 1);
|
|
|
|
//
|
|
mDealsChangedEventHandlers[ArraySize(mDealsChangedEventHandlers) - 1] = handler;
|
|
}
|
|
|
|
//
|
|
// ORDERS ...
|
|
|
|
//
|
|
// Clear Orders Changed Event Handlers ...
|
|
void ClearOnOrdersChangedEventHandlers()
|
|
{
|
|
//
|
|
ArrayFree(mOrdersChangedEventHandlers);
|
|
|
|
//
|
|
ArrayResize(mOrdersChangedEventHandlers, 0);
|
|
}
|
|
|
|
//
|
|
// Add New Orders Changed Event Handler ...
|
|
void AddOnOrdersChangedEventHandler(TOnOrdersChanged handler)
|
|
{
|
|
//
|
|
ArrayResize(
|
|
mOrdersChangedEventHandlers,
|
|
ArraySize(mOrdersChangedEventHandlers) + 1);
|
|
|
|
//
|
|
mOrdersChangedEventHandlers[ArraySize(mOrdersChangedEventHandlers) - 1] = handler;
|
|
}
|
|
|
|
//
|
|
// POSITIONS ...
|
|
|
|
//
|
|
// Clear Positions Changed Event Handlers ...
|
|
void ClearOnPositionsChangedEventHandlers()
|
|
{
|
|
//
|
|
ArrayFree(mPositionsChangedEventHandlers);
|
|
|
|
//
|
|
ArrayResize(mPositionsChangedEventHandlers, 0);
|
|
}
|
|
|
|
//
|
|
// Add New Positions Changed Event Handler ...
|
|
void AddOnPositionsChangedEventHandler(TOnPositionsChanged handler)
|
|
{
|
|
//
|
|
ArrayResize(
|
|
mPositionsChangedEventHandlers,
|
|
ArraySize(mPositionsChangedEventHandlers) + 1);
|
|
|
|
//
|
|
mPositionsChangedEventHandlers[ArraySize(mPositionsChangedEventHandlers) - 1] = handler;
|
|
}
|
|
|
|
//
|
|
// TPSL ...
|
|
|
|
//
|
|
// Clear Stop Loss Event Handlers ...
|
|
void ClearOnStopLossEventHandlers()
|
|
{
|
|
//
|
|
ArrayFree(mStopLossEventHandlers);
|
|
|
|
//
|
|
ArrayResize(mStopLossEventHandlers, 0);
|
|
}
|
|
|
|
//
|
|
// Add New Stop Loss Event Handler ...
|
|
void AddOnStopLossEventHandler(TOnStopLoss handler)
|
|
{
|
|
//
|
|
ArrayResize(
|
|
mStopLossEventHandlers,
|
|
ArraySize(mStopLossEventHandlers) + 1);
|
|
|
|
//
|
|
mStopLossEventHandlers[ArraySize(mStopLossEventHandlers) - 1] = handler;
|
|
}
|
|
|
|
//
|
|
// Clear Take Profit Event Handlers ...
|
|
void ClearOnTakeProfitEventHandlers()
|
|
{
|
|
//
|
|
ArrayFree(mTakeProfitEventHandlers);
|
|
|
|
//
|
|
ArrayResize(mTakeProfitEventHandlers, 0);
|
|
}
|
|
|
|
//
|
|
// Add New Stop Loss Event Handler ...
|
|
void AddOnTakeProfitEventHandler(TOnTakeProfit handler)
|
|
{
|
|
//
|
|
ArrayResize(
|
|
mTakeProfitEventHandlers,
|
|
ArraySize(mTakeProfitEventHandlers) + 1);
|
|
|
|
//
|
|
mTakeProfitEventHandlers[ArraySize(mTakeProfitEventHandlers) - 1] = handler;
|
|
}
|
|
|
|
//
|
|
// END Event Processing ...
|
|
//
|
|
|
|
//
|
|
// START Provided Functions ...
|
|
//
|
|
|
|
//
|
|
// Count Currently Open Positions ...
|
|
int CountPositions(
|
|
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;
|
|
|
|
//
|
|
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)
|
|
{
|
|
//
|
|
result += positionProfit;
|
|
|
|
//
|
|
continue;
|
|
}
|
|
else if (
|
|
type != NULL &&
|
|
StringLen(symbol) > 0)
|
|
{
|
|
//
|
|
if (type == positionType &&
|
|
symbol == positionSymbol)
|
|
{
|
|
//
|
|
if (type == POSITION_TYPE_BUY)
|
|
{
|
|
result += positionProfit;
|
|
}
|
|
else
|
|
{
|
|
result += positionProfit;
|
|
}
|
|
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
else if (StringLen(symbol) > 0)
|
|
{
|
|
//
|
|
if (positionSymbol == symbol)
|
|
{
|
|
result += positionProfit;
|
|
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
else if (type != NULL)
|
|
{
|
|
//
|
|
if (type == positionType)
|
|
{
|
|
//
|
|
if (type == POSITION_TYPE_BUY)
|
|
{
|
|
result += positionProfit;
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
}
|
|
|
|
//
|
|
bool GetPosition(
|
|
XPosition &position, // Holds result ...
|
|
ulong ticket // Required Position Ticket ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
XPosition positions[];
|
|
GetPositions(positions);
|
|
int positionsCount = ArraySize(positions);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
result = iPosition.ticket == ticket;
|
|
if (!result)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
position = iPosition;
|
|
break;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Positions as an Array ...
|
|
void GetPositions(
|
|
XPosition &result[] // Hold Result ...
|
|
)
|
|
{
|
|
//
|
|
Clean(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 ...
|
|
)
|
|
{
|
|
//
|
|
Clean(result);
|
|
|
|
//
|
|
bool hasPeriod = period != NULL;
|
|
bool hasSymbol = symbol != NULL && StringLen(symbol) > 0;
|
|
|
|
//
|
|
int positionsCount = PositionsTotal();
|
|
if (positionsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
XPosition allPositions[];
|
|
GetPositions(allPositions);
|
|
|
|
//
|
|
if (!hasPeriod && !hasSymbol)
|
|
{
|
|
//
|
|
Copy(
|
|
allPositions,
|
|
result);
|
|
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = allPositions[i];
|
|
|
|
//
|
|
if (hasSymbol &&
|
|
symbol != iPosition.symbol)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ENUM_TIMEFRAMES positionPeriod = ExtractPositionPeriod(iPosition.comment);
|
|
if (hasPeriod &&
|
|
period != positionPeriod)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
iPosition,
|
|
result);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Execute a Signal ...
|
|
bool ExecuteSignal(
|
|
XSignal &signal, // Signal ...
|
|
string comment = "", // Position Comment ...
|
|
ENUM_ORDER_TYPE_TIME lifetime = ORDER_TIME_GTC, // Order LifeTime ...
|
|
datetime expiration = 0 // order expiration time
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
double currentEntry = GetEntry(
|
|
signal.symbol,
|
|
signal.type);
|
|
bool isLong = IsLong(signal.type);
|
|
|
|
//
|
|
ResetLastError();
|
|
|
|
//
|
|
if (signal.mode == X_ORDER_MODE_MARKET)
|
|
{
|
|
//
|
|
// Execute MArket Orders ...
|
|
if (isLong)
|
|
{
|
|
//
|
|
result = Buy(
|
|
signal.symbol,
|
|
signal.period,
|
|
signal.volume,
|
|
signal.entry,
|
|
signal.sl,
|
|
signal.tp,
|
|
comment);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
result = Sell(
|
|
signal.symbol,
|
|
signal.period,
|
|
signal.volume,
|
|
signal.entry,
|
|
signal.sl,
|
|
signal.tp,
|
|
comment);
|
|
}
|
|
}
|
|
else if (signal.mode == X_ORDER_MODE_LIMIT)
|
|
{
|
|
//
|
|
// Execute Limit Orders ...
|
|
|
|
//
|
|
// Check Conditions ...
|
|
|
|
//
|
|
// Check Price ...
|
|
result = isLong
|
|
? signal.entry < currentEntry
|
|
: signal.entry > currentEntry;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Expiration Date ...
|
|
result = (lifetime == ORDER_TIME_SPECIFIED || lifetime == ORDER_TIME_SPECIFIED_DAY)
|
|
? expiration > 0
|
|
: true;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
if (isLong)
|
|
{
|
|
//
|
|
result = BuyLimit(
|
|
signal.symbol,
|
|
signal.period,
|
|
signal.volume,
|
|
signal.entry,
|
|
signal.sl,
|
|
signal.tp,
|
|
lifetime,
|
|
expiration,
|
|
comment);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
result = SellLimit(
|
|
signal.symbol,
|
|
signal.period,
|
|
signal.volume,
|
|
signal.entry,
|
|
signal.sl,
|
|
signal.tp,
|
|
lifetime,
|
|
expiration,
|
|
comment);
|
|
}
|
|
}
|
|
else if (signal.mode == X_ORDER_MODE_STOP)
|
|
{
|
|
//
|
|
// Execute Stop Orders ...
|
|
|
|
//
|
|
// Check Conditions ...
|
|
|
|
//
|
|
// Check Price ...
|
|
result = isLong
|
|
? signal.entry > currentEntry
|
|
: signal.entry < currentEntry;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Expiration Date ...
|
|
result = (lifetime == ORDER_TIME_SPECIFIED || lifetime == ORDER_TIME_SPECIFIED_DAY)
|
|
? expiration > 0
|
|
: true;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
if (isLong)
|
|
{
|
|
//
|
|
result = BuyStop(
|
|
signal.symbol,
|
|
signal.period,
|
|
signal.volume,
|
|
signal.entry,
|
|
signal.sl,
|
|
signal.tp,
|
|
lifetime,
|
|
expiration,
|
|
comment);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
result = SellStop(
|
|
signal.symbol,
|
|
signal.period,
|
|
signal.volume,
|
|
signal.entry,
|
|
signal.sl,
|
|
signal.tp,
|
|
lifetime,
|
|
expiration,
|
|
comment);
|
|
}
|
|
}
|
|
|
|
//
|
|
if (!result)
|
|
{
|
|
LogMessage("Error: " + (string)GetLastError() + ", " + GetErrorDescription(GetLastError()));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Retrieve Last Position Ticket ...
|
|
ulong ticket = GetLastOpenPositionTicket();
|
|
|
|
//
|
|
// Check Position have Support Signals or not ...
|
|
int supportsCount = ArraySize(signal.supports);
|
|
if (supportsCount > 0)
|
|
{
|
|
//
|
|
int executed = 0;
|
|
for (int i = 0; i < supportsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSupport = signal.supports[i];
|
|
|
|
//
|
|
int supIndex = i + 1;
|
|
string iComment = GenerateSupportSignalComment(
|
|
ticket,
|
|
supIndex);
|
|
|
|
//
|
|
bool isExecuted = ExecuteSignal(
|
|
iSupport,
|
|
iComment);
|
|
if (isExecuted)
|
|
{
|
|
executed++;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = executed == supportsCount;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Execute a Collection of Signals ...
|
|
int ExecuteSignals(
|
|
const XSignal &signals[], // an Array of Signals ...
|
|
string comment = "", // Position Comment ...
|
|
ENUM_ORDER_TYPE_TIME lifetime = ORDER_TIME_GTC, // Order LifeTime ...
|
|
datetime expiration = 0 // order expiration time
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
if (StringLen(comment) == 0)
|
|
{
|
|
comment = GenerateProvidersString(iSignal);
|
|
}
|
|
|
|
//
|
|
bool isExecuted = ExecuteSignal(
|
|
iSignal,
|
|
comment,
|
|
lifetime,
|
|
expiration);
|
|
|
|
//
|
|
if (isExecuted)
|
|
{
|
|
result++;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve in Profit Positions ...
|
|
void GetInProfitPositions(
|
|
XPosition &result[] // Hold Result ...
|
|
)
|
|
{
|
|
//
|
|
Clean(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 ...
|
|
)
|
|
{
|
|
//
|
|
Clean(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 ...
|
|
int ClosePositions(
|
|
XPosition &positions[], // Positions to Close ...
|
|
string comment = "" // Specified 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;
|
|
}
|
|
|
|
//
|
|
// Retrieve All Orders ...
|
|
void GetOrders(
|
|
XOrder &result[], // Holdes Result ....
|
|
string symbol, // Trading Symbol ...
|
|
ENUM_TIMEFRAMES period, // Trading TimeFrame ...
|
|
ENUM_ORDER_STATE state = NULL // Order State ...
|
|
)
|
|
{
|
|
//
|
|
Clean(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 (!mOrderInfo.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 oSymbol = mOrderInfo.Symbol();
|
|
string comment = mOrderInfo.Comment();
|
|
long positionID = mOrderInfo.PositionId();
|
|
double openPrice = mOrderInfo.PriceOpen();
|
|
datetime setupAt = mOrderInfo.TimeSetup();
|
|
datetime executedAt = mOrderInfo.TimeDone();
|
|
ENUM_ORDER_STATE oState = 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();
|
|
|
|
//
|
|
if (StringLen(symbol) > 0 && symbol != oSymbol)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ENUM_TIMEFRAMES oPeriod = ExtractPositionPeriod(comment);
|
|
if (period != NULL && period != oPeriod)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (state != NULL && oState != state)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// 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.magic = magic;
|
|
iOrder.state = oState;
|
|
iOrder.ticket = ticket;
|
|
iOrder.smybol = oSymbol;
|
|
iOrder.period = oPeriod;
|
|
iOrder.setupAt = setupAt;
|
|
iOrder.comment = comment;
|
|
iOrder.filling = filling;
|
|
iOrder.lifetime = lifetime;
|
|
iOrder.expiredAt = expiredAt;
|
|
iOrder.openPrice = openPrice;
|
|
iOrder.pocitionID = positionID;
|
|
iOrder.executedAt = executedAt;
|
|
iOrder.currentPrice = currentPrice;
|
|
iOrder.initialVolume = initialVolume;
|
|
iOrder.currentVolume = currentVolume;
|
|
iOrder.stopLimitPrice = stopLimitPrice;
|
|
|
|
//
|
|
Add(
|
|
iOrder,
|
|
result);
|
|
}
|
|
}
|
|
|
|
//
|
|
int CountOrders()
|
|
{
|
|
return OrdersTotal();
|
|
}
|
|
|
|
//
|
|
// Cancel Specific Order ...
|
|
bool CancelOrder(ulong ticket)
|
|
{
|
|
return mTrader.OrderDelete(ticket);
|
|
}
|
|
|
|
//
|
|
// Cancel Specific Orders ...
|
|
bool CancelOrders(
|
|
XOrder &orders[])
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
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 = isDeleted;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve All Deals ...
|
|
void GetDeals(XDeal &result[])
|
|
{
|
|
//
|
|
Clean(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);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Retrieve all History Orders of Specific Deal ...
|
|
int GetDealHistory(
|
|
const XDeal &deal, // Specified Deal
|
|
XOrder &history[] // Hold Result
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(history);
|
|
|
|
//
|
|
bool isHistorySelected = HistorySelectByPosition(deal.positionId);
|
|
if (!isHistorySelected)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int ordersCount = HistoryOrdersTotal();
|
|
if (ordersCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < ordersCount; i++)
|
|
{
|
|
//
|
|
if (!mHistoryOrderInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ulong ticket = mHistoryOrderInfo.Ticket();
|
|
|
|
//
|
|
long positionId = mHistoryOrderInfo.PositionId();
|
|
if (positionId != deal.positionId)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Reading Order Info ...
|
|
long magic = mHistoryOrderInfo.Magic();
|
|
double sl = mHistoryOrderInfo.StopLoss();
|
|
double tp = mHistoryOrderInfo.TakeProfit();
|
|
string oSymbol = mHistoryOrderInfo.Symbol();
|
|
string comment = mHistoryOrderInfo.Comment();
|
|
double openPrice = mHistoryOrderInfo.PriceOpen();
|
|
datetime setupAt = mHistoryOrderInfo.TimeSetup();
|
|
datetime executedAt = mHistoryOrderInfo.TimeDone();
|
|
ENUM_ORDER_STATE oState = mHistoryOrderInfo.State();
|
|
ENUM_ORDER_TYPE type = mHistoryOrderInfo.OrderType();
|
|
double currentPrice = mHistoryOrderInfo.PriceCurrent();
|
|
datetime expiredAt = mHistoryOrderInfo.TimeExpiration();
|
|
double initialVolume = mHistoryOrderInfo.VolumeInitial();
|
|
double currentVolume = mHistoryOrderInfo.VolumeCurrent();
|
|
double stopLimitPrice = mHistoryOrderInfo.PriceStopLimit();
|
|
ENUM_ORDER_TYPE_TIME lifetime = mHistoryOrderInfo.TypeTime();
|
|
ENUM_ORDER_TYPE_FILLING filling = mHistoryOrderInfo.TypeFilling();
|
|
|
|
//
|
|
ENUM_TIMEFRAMES oPeriod = ExtractPositionPeriod(comment);
|
|
|
|
//
|
|
// 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.magic = magic;
|
|
iOrder.state = oState;
|
|
iOrder.ticket = ticket;
|
|
iOrder.smybol = oSymbol;
|
|
iOrder.period = oPeriod;
|
|
iOrder.setupAt = setupAt;
|
|
iOrder.comment = comment;
|
|
iOrder.filling = filling;
|
|
iOrder.lifetime = lifetime;
|
|
iOrder.expiredAt = expiredAt;
|
|
iOrder.openPrice = openPrice;
|
|
iOrder.pocitionID = positionId;
|
|
iOrder.executedAt = executedAt;
|
|
iOrder.currentPrice = currentPrice;
|
|
iOrder.initialVolume = initialVolume;
|
|
iOrder.currentVolume = currentVolume;
|
|
iOrder.stopLimitPrice = stopLimitPrice;
|
|
|
|
//
|
|
Add(
|
|
iOrder,
|
|
history);
|
|
}
|
|
|
|
//
|
|
result = ArraySize(history);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Dates History Orders ...
|
|
int GetHistoryOrders(
|
|
XOrder &orders[], // Hold Result
|
|
string symbol = NULL, // Specify Symbol
|
|
ENUM_TIMEFRAMES period = NULL, // Speify Period
|
|
ENUM_ORDER_TYPE type = NULL, // Trading Type
|
|
ENUM_ORDER_STATE state = NULL, // Trading State
|
|
datetime startDate = 0, // Start Date
|
|
datetime endDate = 0 // End Date
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(orders);
|
|
|
|
//
|
|
if (startDate <= 0)
|
|
{
|
|
startDate = TimeCurrent() - (3 * PeriodSeconds(PERIOD_D1));
|
|
}
|
|
|
|
//
|
|
if (endDate <= 0)
|
|
{
|
|
endDate = TimeCurrent();
|
|
}
|
|
|
|
//
|
|
if (endDate <= startDate)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool isSelected = HistorySelect(startDate, endDate);
|
|
if (!isSelected)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int ordersCount = HistoryOrdersTotal();
|
|
if (ordersCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < ordersCount; i++)
|
|
{
|
|
//
|
|
if (!mHistoryOrderInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Reading Order Info ...
|
|
|
|
//
|
|
long magic = mHistoryOrderInfo.Magic();
|
|
if (magic != mMagicNumber)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
string oSymbol = mHistoryOrderInfo.Symbol();
|
|
if (StringLen(symbol) > 0 && symbol != NULL && oSymbol != symbol)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
string comment = mHistoryOrderInfo.Comment();
|
|
ENUM_TIMEFRAMES oPeriod = ExtractPositionPeriod(comment);
|
|
if (period != NULL && oPeriod != period)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ENUM_ORDER_TYPE oType = mHistoryOrderInfo.OrderType();
|
|
if (type != NULL && oType != type)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ENUM_ORDER_STATE oState = mHistoryOrderInfo.State();
|
|
if (state != NULL && oState != state)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
double sl = mHistoryOrderInfo.StopLoss();
|
|
ulong ticket = mHistoryOrderInfo.Ticket();
|
|
double tp = mHistoryOrderInfo.TakeProfit();
|
|
long positionId = mHistoryOrderInfo.PositionId();
|
|
double openPrice = mHistoryOrderInfo.PriceOpen();
|
|
datetime setupAt = mHistoryOrderInfo.TimeSetup();
|
|
datetime executedAt = mHistoryOrderInfo.TimeDone();
|
|
double currentPrice = mHistoryOrderInfo.PriceCurrent();
|
|
datetime expiredAt = mHistoryOrderInfo.TimeExpiration();
|
|
double initialVolume = mHistoryOrderInfo.VolumeInitial();
|
|
double currentVolume = mHistoryOrderInfo.VolumeCurrent();
|
|
double stopLimitPrice = mHistoryOrderInfo.PriceStopLimit();
|
|
ENUM_ORDER_TYPE_TIME lifetime = mHistoryOrderInfo.TypeTime();
|
|
ENUM_ORDER_TYPE_FILLING filling = mHistoryOrderInfo.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.magic = magic;
|
|
iOrder.state = oState;
|
|
iOrder.ticket = ticket;
|
|
iOrder.smybol = oSymbol;
|
|
iOrder.period = oPeriod;
|
|
iOrder.setupAt = setupAt;
|
|
iOrder.comment = comment;
|
|
iOrder.filling = filling;
|
|
iOrder.lifetime = lifetime;
|
|
iOrder.expiredAt = expiredAt;
|
|
iOrder.openPrice = openPrice;
|
|
iOrder.pocitionID = positionId;
|
|
iOrder.executedAt = executedAt;
|
|
iOrder.currentPrice = currentPrice;
|
|
iOrder.initialVolume = initialVolume;
|
|
iOrder.currentVolume = currentVolume;
|
|
iOrder.stopLimitPrice = stopLimitPrice;
|
|
|
|
//
|
|
Add(
|
|
iOrder,
|
|
orders);
|
|
}
|
|
|
|
//
|
|
result = ArraySize(orders);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Dates History Deals ...
|
|
int GetHistoryDeals(
|
|
XDeal &deals[], // Hold Result
|
|
string symbol = NULL, // Specify Symbol
|
|
ENUM_DEAL_TYPE type = NULL, // Specify Deal Type
|
|
ENUM_DEAL_REASON reason = NULL, // Specify Deal Reason
|
|
datetime startDate = 0, // Start Date
|
|
datetime endDate = 0 // End Date
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(deals);
|
|
|
|
//
|
|
if (startDate <= 0)
|
|
{
|
|
startDate = TimeCurrent() - (3 * PeriodSeconds(PERIOD_D1));
|
|
}
|
|
|
|
//
|
|
if (endDate <= 0)
|
|
{
|
|
endDate = TimeCurrent();
|
|
}
|
|
|
|
//
|
|
if (endDate <= startDate)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool isSelected = HistorySelect(startDate, endDate);
|
|
if (!isSelected)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int dealsCount = HistoryDealsTotal();
|
|
if (dealsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < dealsCount; i++)
|
|
{
|
|
//
|
|
if (!mDealInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Reading Deal Info ...
|
|
|
|
//
|
|
long magic = mDealInfo.Magic();
|
|
if (magic != mMagicNumber)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
string oSymbol = mDealInfo.Symbol();
|
|
if (StringLen(symbol) > 0 && symbol != NULL && oSymbol != symbol)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ENUM_DEAL_TYPE oType = mDealInfo.DealType();
|
|
if (type != NULL && oType != type)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ulong ticket = mDealInfo.Ticket();
|
|
ENUM_DEAL_REASON oReason = (ENUM_DEAL_REASON)HistoryDealGetInteger(
|
|
ticket,
|
|
DEAL_REASON);
|
|
if (reason != NULL && oReason != reason)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
double swap = mDealInfo.Swap();
|
|
ulong order = mDealInfo.Order();
|
|
datetime time = mDealInfo.Time();
|
|
double price = mDealInfo.Price();
|
|
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();
|
|
|
|
//
|
|
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,
|
|
deals);
|
|
}
|
|
|
|
//
|
|
result = ArraySize(deals);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Find Deal Period ...
|
|
ENUM_TIMEFRAMES GetDealPeriod(const XDeal &deal)
|
|
{
|
|
//
|
|
ENUM_TIMEFRAMES result = NULL;
|
|
|
|
//
|
|
XOrder history[];
|
|
int historyCount = GetDealHistory(
|
|
deal,
|
|
history);
|
|
if (historyCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < historyCount; i++)
|
|
{
|
|
//
|
|
XOrder iOrder = history[i];
|
|
|
|
//
|
|
if (StringLen(iOrder.comment) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
string periodStr = ExtractPositionPeriodString(iOrder.comment);
|
|
if (StringLen(periodStr) > 0)
|
|
{
|
|
//
|
|
result = ToPeriod(periodStr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Find Deal Position Comment ...
|
|
string GetDealPositionComment(const XDeal &deal)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
XOrder history[];
|
|
int historyCount = GetDealHistory(
|
|
deal,
|
|
history);
|
|
if (historyCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < historyCount; i++)
|
|
{
|
|
//
|
|
XOrder iOrder = history[i];
|
|
|
|
//
|
|
if (StringLen(iOrder.comment) > 0)
|
|
{
|
|
//
|
|
result = iOrder.comment;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate String Representation Of Position's Time Frame ...
|
|
string GeneratePositionPeriodComment(ENUM_TIMEFRAMES period)
|
|
{
|
|
//
|
|
string result = mPeriodPrefix + "(" + ToString(period) + ")";
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string ExtractPositionPeriodString(string comment)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
if (StringLen(comment) <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = ExtractString(
|
|
comment,
|
|
mPeriodPrefix + "(",
|
|
")");
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extract Position Time Frame From it's Comment ...
|
|
ENUM_TIMEFRAMES ExtractPositionPeriod(string comment)
|
|
{
|
|
//
|
|
ENUM_TIMEFRAMES result = NULL;
|
|
|
|
//
|
|
string periodStr = ExtractPositionPeriodString(comment);
|
|
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;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// SUPPORT Signals ...
|
|
|
|
//
|
|
// Retrieve a Position if Exists
|
|
// by all of it's Supported Positon/Order (s) ...
|
|
bool GetPositionPack(
|
|
ulong ticket, // Sepcified Position's Ticket
|
|
XPositionPack &pack // Hold Result ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int positionsCount = CountPositions();
|
|
int ordersCount = CountOrders();
|
|
if (ordersCount <= 0 && positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition position;
|
|
result = GetPosition(
|
|
position,
|
|
ticket);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XOrder supportOrders[];
|
|
XPosition supportPositions[];
|
|
|
|
//
|
|
XOrder orders[];
|
|
GetOrders(
|
|
orders,
|
|
position.symbol,
|
|
position.period,
|
|
ORDER_STATE_PLACED);
|
|
ordersCount = ArraySize(orders);
|
|
|
|
//
|
|
// Select Orders ...
|
|
if (ordersCount > 0)
|
|
{
|
|
//
|
|
for (int i = 0; i < ordersCount; i++)
|
|
{
|
|
//
|
|
XOrder iOrder = orders[i];
|
|
|
|
//
|
|
string params[];
|
|
bool hasSupport = ExtractSupportParams(
|
|
iOrder.comment,
|
|
params);
|
|
if (hasSupport)
|
|
{
|
|
//
|
|
ulong parentTicket = ExtractParentTicket(iOrder.comment);
|
|
if (parentTicket == ticket)
|
|
{
|
|
//
|
|
Add(
|
|
iOrder,
|
|
supportOrders);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
GetPositions(
|
|
positions,
|
|
position.symbol,
|
|
position.period);
|
|
positionsCount = ArraySize(positions);
|
|
|
|
//
|
|
// Select Positions ...
|
|
if (positionsCount > 0)
|
|
{
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
string params[];
|
|
bool hasSupport = ExtractSupportParams(
|
|
iPosition.comment,
|
|
params);
|
|
if (hasSupport)
|
|
{
|
|
//
|
|
ulong parentTicket = ExtractParentTicket(iPosition.comment);
|
|
if (parentTicket == ticket)
|
|
{
|
|
//
|
|
Add(
|
|
iPosition,
|
|
supportPositions);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
int supportOrdersCount = ArraySize(supportOrders);
|
|
int supportPositionsCount = ArraySize(supportPositions);
|
|
|
|
//
|
|
pack.ticket = ticket;
|
|
pack.position = position;
|
|
|
|
//
|
|
Copy(supportOrders,
|
|
pack.supportOrders);
|
|
Copy(supportPositions,
|
|
pack.supportPositions);
|
|
|
|
//
|
|
pack.supportOrdersCount = supportOrdersCount;
|
|
pack.supportPositionsCount = supportPositionsCount;
|
|
|
|
//
|
|
result = true;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extract specific Positions Pack ...
|
|
// in this senario there is no need to Position exists itself ...
|
|
bool GetPositionPack(
|
|
ulong ticket, // Specified Positions Ticket ...
|
|
string symbol, // Specified Position Symbol ...
|
|
ENUM_TIMEFRAMES period, // Specified Position Period ...
|
|
XPositionPack &pack // Hold Result ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// NOTE: We Have to Force Retrieve Position's pack event
|
|
// it's parent Position not Eists ...
|
|
pack.ticket = ticket;
|
|
pack.supportOrdersCount = 0;
|
|
Clean(pack.supportOrders);
|
|
pack.supportPositionsCount = 0;
|
|
Clean(pack.supportPositions);
|
|
|
|
//
|
|
XPosition position;
|
|
bool hasPosition = GetPosition(
|
|
position,
|
|
ticket);
|
|
if (hasPosition)
|
|
{
|
|
pack.position = position;
|
|
}
|
|
|
|
//
|
|
XOrder supportOrders[];
|
|
XPosition supportPositions[];
|
|
|
|
//
|
|
XOrder orders[];
|
|
GetOrders(
|
|
orders,
|
|
symbol, // Specified Symbol ...
|
|
period, // Specified Period
|
|
ORDER_STATE_PLACED // Just UnTriggered Orders ...
|
|
);
|
|
int ordersCount = ArraySize(orders);
|
|
|
|
//
|
|
// Select Orders ...
|
|
if (ordersCount > 0)
|
|
{
|
|
//
|
|
for (int i = 0; i < ordersCount; i++)
|
|
{
|
|
//
|
|
XOrder iOrder = orders[i];
|
|
|
|
//
|
|
string params[];
|
|
bool hasSupport = ExtractSupportParams(
|
|
iOrder.comment,
|
|
params);
|
|
if (hasSupport)
|
|
{
|
|
//
|
|
ulong parentTicket = ExtractParentTicket(iOrder.comment);
|
|
if (parentTicket == ticket)
|
|
{
|
|
//
|
|
Add(
|
|
iOrder,
|
|
supportOrders);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
int supportOrdersCount = ArraySize(supportOrders);
|
|
if (supportOrdersCount > 0)
|
|
{
|
|
//
|
|
pack.supportOrdersCount = supportOrdersCount;
|
|
|
|
//
|
|
Copy(
|
|
supportOrders,
|
|
pack.supportOrders);
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
GetPositions(
|
|
positions,
|
|
symbol, // Specified Symbol ...
|
|
period // Specified Period ...
|
|
);
|
|
int positionsCount = ArraySize(positions);
|
|
|
|
//
|
|
// Select Positions ...
|
|
if (positionsCount > 0)
|
|
{
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
string params[];
|
|
bool hasSupport = ExtractSupportParams(
|
|
iPosition.comment,
|
|
params);
|
|
if (hasSupport)
|
|
{
|
|
//
|
|
ulong parentTicket = ExtractParentTicket(iPosition.comment);
|
|
if (parentTicket == ticket)
|
|
{
|
|
//
|
|
Add(
|
|
iPosition,
|
|
supportPositions);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
int supportPositionsCount = ArraySize(supportPositions);
|
|
if (supportPositionsCount > 0)
|
|
{
|
|
//
|
|
pack.supportPositionsCount = supportPositionsCount;
|
|
|
|
//
|
|
Copy(
|
|
supportPositions,
|
|
pack.supportPositions);
|
|
}
|
|
|
|
//
|
|
result = supportOrdersCount > 0 || supportPositionsCount > 0;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extract Specific Deals Position's Pack ...
|
|
// this used when a Position SL or TP triggered and we want to close
|
|
// all it's Supported Packs ...
|
|
bool GetDealPack(
|
|
const XDeal &deal,
|
|
XPositionPack &pack)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ulong ticket = deal.positionId;
|
|
ENUM_TIMEFRAMES dealPeriod = GetDealPeriod(deal);
|
|
string dealSymbol = deal.symbol;
|
|
|
|
//
|
|
// So We Have to Get Positions Pack ...
|
|
result = GetPositionPack(
|
|
ticket,
|
|
dealSymbol,
|
|
dealPeriod,
|
|
pack);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Provided Functions ...
|
|
//
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Private ...
|
|
|
|
//
|
|
// EVENTS Handlers 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);
|
|
}
|
|
}
|
|
|
|
//
|
|
// TPSL ...
|
|
|
|
//
|
|
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);
|
|
}
|
|
}
|
|
|
|
//
|
|
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);
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
//
|
|
// History Orders ...
|
|
CHistoryOrderInfo mHistoryOrderInfo;
|
|
|
|
//
|
|
// ADDITIONAL Data ...
|
|
|
|
//
|
|
string mPeriodPrefix;
|
|
|
|
//
|
|
// START Trade Processing ...
|
|
//
|
|
|
|
//
|
|
int mDaysForRead;
|
|
|
|
//
|
|
// set the limits of the trade history on the global scope ...
|
|
|
|
//
|
|
// // start date for trade history in cache ...
|
|
datetime mStartDate;
|
|
|
|
//
|
|
// end date for trade history in cache ...
|
|
datetime mEndDate;
|
|
|
|
//
|
|
// global OnTrade counters ...
|
|
|
|
//
|
|
// number of active orders ...
|
|
int mOrdersCount;
|
|
|
|
//
|
|
// number of open positions ...
|
|
int mPositionsCount;
|
|
|
|
//
|
|
// number of deals in the trade history cache ...
|
|
int mDealsCount;
|
|
|
|
//
|
|
// number of orders in the trade history cache ...
|
|
int mHistoryOrdersCount;
|
|
|
|
//
|
|
// flag of counter relevance ...
|
|
bool mScanStarted;
|
|
|
|
//
|
|
// Check Statrt Date In Trade History ...
|
|
void CheckStartDateInTradeHistory()
|
|
{
|
|
//
|
|
// initial interval, if we were to start working right now ..
|
|
datetime currStart = TimeCurrent() - (mDaysForRead * PeriodSeconds(PERIOD_D1));
|
|
|
|
//
|
|
// make sure that the start limit of the trade history has not gone
|
|
// more than 1 day over the intended date ...
|
|
if (currStart - mStartDate > PeriodSeconds(PERIOD_D1))
|
|
{
|
|
//
|
|
// correct the start date of history to be loaded in the cache ...
|
|
mStartDate = currStart;
|
|
|
|
//
|
|
// now reload the trade history for the updated interval ...
|
|
HistorySelect(mStartDate, mEndDate);
|
|
|
|
//
|
|
// correct the deal and order counters in history for further comparison
|
|
mHistoryOrdersCount = HistoryOrdersTotal();
|
|
mDealsCount = HistoryDealsTotal();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Process On Trade Context ...
|
|
void ProcessOnTradeContext(
|
|
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();
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// 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[];
|
|
|
|
//
|
|
// TPSL ...
|
|
TOnStopLoss mStopLossEventHandlers[];
|
|
TOnTakeProfit mTakeProfitEventHandlers[];
|
|
|
|
//
|
|
// END Trade Processing ...
|
|
//
|
|
};
|
|
|
|
//
|
|
// 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 Clean(XDeal &buffer[])
|
|
{
|
|
//
|
|
ArrayFree(buffer);
|
|
ArrayResize(buffer, 0);
|
|
}
|
|
void Clean(XOrder &buffer[])
|
|
{
|
|
//
|
|
ArrayFree(buffer);
|
|
ArrayResize(buffer, 0);
|
|
}
|
|
void Clean(XPosition &buffer[])
|
|
{
|
|
//
|
|
ArrayFree(buffer);
|
|
ArrayResize(buffer, 0);
|
|
}
|
|
|
|
//
|
|
// Generate Identifier Tag ...
|
|
string GenerateTag(XSignal &info)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
result = info.symbol + "," + ToString(info.period);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Convert Position to Signal ...
|
|
XSignal ToSignalInfo(
|
|
XPosition &position)
|
|
{
|
|
//
|
|
XSignal result;
|
|
|
|
//
|
|
result.tp = position.tp;
|
|
result.sl = position.sl;
|
|
result.type = position.type;
|
|
result.entry = position.entry;
|
|
result.symbol = position.symbol;
|
|
result.period = position.period;
|
|
result.volume = position.volume;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// AGE Calculations ...
|
|
//
|
|
|
|
//
|
|
// Retrieve a Position Age ...
|
|
int GetAge(
|
|
XPosition &position, // Selected Position ...
|
|
ENUM_TIMEFRAMES period = NULL // Dest Time Frame ...
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
if (period == NULL)
|
|
{
|
|
period = _Period;
|
|
}
|
|
|
|
//
|
|
result = iBarShift(
|
|
position.symbol,
|
|
period,
|
|
position.openAt);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve an Order Age ...
|
|
int GetAge(
|
|
XOrder &order, // Selected Order ...
|
|
ENUM_TIMEFRAMES period = NULL // Dest Time Frame ...
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
if (period == NULL)
|
|
{
|
|
period = _Period;
|
|
}
|
|
|
|
//
|
|
result = iBarShift(
|
|
order.smybol,
|
|
period,
|
|
order.setupAt);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve a Deal Age ...
|
|
int GetAge(
|
|
XDeal &deal, // Selected Deal ...
|
|
ENUM_TIMEFRAMES period = NULL // Dest Time Frame ...
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
if (period == NULL)
|
|
{
|
|
period = _Period;
|
|
}
|
|
|
|
//
|
|
result = iBarShift(
|
|
deal.symbol,
|
|
period,
|
|
deal.time);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// OLDEST Detection ...
|
|
|
|
//
|
|
// Get Oldest Deal ...
|
|
int GetOldest(
|
|
XDeal &item, // Hold Result ...
|
|
XDeal &items[] // Source Array ...
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
int itemsCount = ArraySize(items);
|
|
if (itemsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int tmpAge = 0;
|
|
XDeal tmpItem;
|
|
for (int i = 0; i < itemsCount; i++)
|
|
{
|
|
//
|
|
XDeal iItem = items[i];
|
|
|
|
//
|
|
int iAge = GetAge(iItem);
|
|
if (iAge > 0 &&
|
|
(tmpAge == 0 || tmpAge < iAge))
|
|
{
|
|
//
|
|
tmpAge = iAge;
|
|
tmpItem = iItem;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (tmpAge > 0)
|
|
{
|
|
//
|
|
result = tmpAge;
|
|
item = tmpItem;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get Youngest Deal ...
|
|
int GetYoungest(
|
|
XDeal &item, // Hold Result ...
|
|
XDeal &items[] // Source Array ...
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
int itemsCount = ArraySize(items);
|
|
if (itemsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int tmpAge = 0;
|
|
XDeal tmpItem;
|
|
for (int i = 0; i < itemsCount; i++)
|
|
{
|
|
//
|
|
XDeal iItem = items[i];
|
|
|
|
//
|
|
int iAge = GetAge(iItem);
|
|
if (iAge > 0 &&
|
|
(tmpAge == 0 || tmpAge > iAge))
|
|
{
|
|
//
|
|
tmpAge = iAge;
|
|
tmpItem = iItem;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (tmpAge > 0)
|
|
{
|
|
//
|
|
result = tmpAge;
|
|
item = tmpItem;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get Oldest Order ...
|
|
int GetOldest(
|
|
XOrder &item, // Hold Result ...
|
|
XOrder &items[] // Source Array ...
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
int itemsCount = ArraySize(items);
|
|
if (itemsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int tmpAge = 0;
|
|
XOrder tmpItem;
|
|
for (int i = 0; i < itemsCount; i++)
|
|
{
|
|
//
|
|
XOrder iItem = items[i];
|
|
|
|
//
|
|
int iAge = GetAge(iItem);
|
|
if (iAge > 0 &&
|
|
(tmpAge == 0 || tmpAge < iAge))
|
|
{
|
|
//
|
|
tmpAge = iAge;
|
|
tmpItem = iItem;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (tmpAge > 0)
|
|
{
|
|
//
|
|
result = tmpAge;
|
|
item = tmpItem;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get Youngest Order ...
|
|
int GetYoungest(
|
|
XOrder &item, // Hold Result ...
|
|
XOrder &items[] // Source Array ...
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
int itemsCount = ArraySize(items);
|
|
if (itemsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int tmpAge = 0;
|
|
XOrder tmpItem;
|
|
for (int i = 0; i < itemsCount; i++)
|
|
{
|
|
//
|
|
XOrder iItem = items[i];
|
|
|
|
//
|
|
int iAge = GetAge(iItem);
|
|
if (iAge > 0 &&
|
|
(tmpAge == 0 || tmpAge > iAge))
|
|
{
|
|
//
|
|
tmpAge = iAge;
|
|
tmpItem = iItem;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (tmpAge > 0)
|
|
{
|
|
//
|
|
result = tmpAge;
|
|
item = tmpItem;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get Oldes Position ...
|
|
int GetOldest(
|
|
XPosition &item, // Hold Result ...
|
|
XPosition &items[] // Source Array ...
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
int itemsCount = ArraySize(items);
|
|
if (itemsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int tmpAge = 0;
|
|
XPosition tmpItem;
|
|
for (int i = 0; i < itemsCount; i++)
|
|
{
|
|
//
|
|
XPosition iItem = items[i];
|
|
|
|
//
|
|
int iAge = GetAge(iItem);
|
|
if (iAge > 0 &&
|
|
(tmpAge == 0 || tmpAge < iAge))
|
|
{
|
|
//
|
|
tmpAge = iAge;
|
|
tmpItem = iItem;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (tmpAge > 0)
|
|
{
|
|
//
|
|
result = tmpAge;
|
|
item = tmpItem;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get Youngest Position ...
|
|
int GetYoungest(
|
|
XPosition &item, // Hold Result ...
|
|
XPosition &items[] // Source Array ...
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
int itemsCount = ArraySize(items);
|
|
if (itemsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int tmpAge = 0;
|
|
XPosition tmpItem;
|
|
for (int i = 0; i < itemsCount; i++)
|
|
{
|
|
//
|
|
XPosition iItem = items[i];
|
|
|
|
//
|
|
int iAge = GetAge(iItem);
|
|
if (iAge > 0 &&
|
|
(tmpAge == 0 || tmpAge > iAge))
|
|
{
|
|
//
|
|
tmpAge = iAge;
|
|
tmpItem = iItem;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (tmpAge > 0)
|
|
{
|
|
//
|
|
result = tmpAge;
|
|
item = tmpItem;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Usefull Functions ...
|
|
//
|