4112 lines
96 KiB
Plaintext
4112 lines
96 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;
|
|
};
|
|
|
|
//
|
|
// 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 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 (*OnDealsChanged)(int count);
|
|
|
|
//
|
|
typedef void (*OnOrdersChanged)(int count);
|
|
|
|
//
|
|
typedef void (*OnPositionsChanged)(int count);
|
|
|
|
//
|
|
typedef void (*OnStopLoss)(const XDeal &deal);
|
|
typedef void (*OnTakeProfit)(const XDeal &deal);
|
|
|
|
//
|
|
typedef void (*OnTradeStateChanged)(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(OnTradeStateChanged 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(OnDealsChanged 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(OnOrdersChanged 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(OnPositionsChanged 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(OnStopLoss 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(OnTakeProfit handler)
|
|
{
|
|
//
|
|
ArrayResize(
|
|
mTakeProfitEventHandlers,
|
|
ArraySize(mTakeProfitEventHandlers) + 1);
|
|
|
|
//
|
|
mTakeProfitEventHandlers[ArraySize(mTakeProfitEventHandlers) - 1] = handler;
|
|
}
|
|
|
|
//
|
|
// END Event Processing ...
|
|
//
|
|
|
|
//
|
|
// START Provided Functions ...
|
|
//
|
|
|
|
//
|
|
// Count Currently Open Positions ...
|
|
int PositionsCount(
|
|
string symbol = "", // Specified Symbol
|
|
ENUM_POSITION_TYPE type = NULL // Specified Type
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
int allPositions = 0;
|
|
int longPositions = 0;
|
|
int shortPositions = 0;
|
|
|
|
//
|
|
int positionsCount = PositionsTotal();
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
if (!mPositionInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Magic() != mMagicNumber)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
string positionSymbol = mPositionInfo.Symbol();
|
|
|
|
//
|
|
ENUM_POSITION_TYPE positionType = mPositionInfo.PositionType();
|
|
|
|
//
|
|
if (
|
|
type == NULL &&
|
|
StringLen(symbol) <= 0)
|
|
{
|
|
//
|
|
allPositions++;
|
|
|
|
//
|
|
continue;
|
|
}
|
|
else if (
|
|
type != NULL &&
|
|
StringLen(symbol) > 0)
|
|
{
|
|
//
|
|
if (type == positionType &&
|
|
symbol == positionSymbol)
|
|
{
|
|
//
|
|
if (type == POSITION_TYPE_BUY)
|
|
{
|
|
longPositions++;
|
|
}
|
|
else
|
|
{
|
|
shortPositions++;
|
|
}
|
|
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
else if (StringLen(symbol) > 0)
|
|
{
|
|
//
|
|
if (positionSymbol == symbol)
|
|
{
|
|
//
|
|
allPositions++;
|
|
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
else if (type != NULL)
|
|
{
|
|
//
|
|
if (type == positionType)
|
|
{
|
|
//
|
|
if (type == POSITION_TYPE_BUY)
|
|
{
|
|
longPositions++;
|
|
}
|
|
else
|
|
{
|
|
shortPositions++;
|
|
}
|
|
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Specify Result ...
|
|
if (
|
|
type == NULL &&
|
|
StringLen(symbol) <= 0)
|
|
{
|
|
//
|
|
result = allPositions;
|
|
}
|
|
else if (
|
|
type != NULL &&
|
|
StringLen(symbol) > 0)
|
|
{
|
|
//
|
|
if (type == POSITION_TYPE_BUY)
|
|
{
|
|
result = longPositions;
|
|
}
|
|
else
|
|
{
|
|
result = shortPositions++;
|
|
}
|
|
}
|
|
else if (StringLen(symbol) > 0)
|
|
{
|
|
//
|
|
result = allPositions;
|
|
}
|
|
else if (type != NULL)
|
|
{
|
|
//
|
|
if (type == POSITION_TYPE_BUY)
|
|
{
|
|
result = longPositions;
|
|
}
|
|
else
|
|
{
|
|
result = shortPositions;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Max In Profit Position Ticket ...
|
|
ulong GetMaxInProfitPosition(
|
|
string symbol = "", // Specified Symbol
|
|
ENUM_POSITION_TYPE type = NULL // Specified Type
|
|
)
|
|
{
|
|
//
|
|
ulong result = -1;
|
|
|
|
//
|
|
double profit = 0;
|
|
|
|
//
|
|
int positionsCount = PositionsTotal();
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
if (!mPositionInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Magic() != mMagicNumber)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ulong positionTicket = mPositionInfo.Ticket();
|
|
|
|
//
|
|
string positionSymbol = mPositionInfo.Symbol();
|
|
|
|
//
|
|
double positionProfit = mPositionInfo.Profit();
|
|
double positionAbsProfit = MathAbs(positionProfit);
|
|
if (positionProfit <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ENUM_POSITION_TYPE positionType = mPositionInfo.PositionType();
|
|
|
|
//
|
|
if (
|
|
type == NULL &&
|
|
StringLen(symbol) <= 0)
|
|
{
|
|
//
|
|
if (profit < positionAbsProfit)
|
|
{
|
|
//
|
|
result = positionTicket;
|
|
profit = positionAbsProfit;
|
|
}
|
|
|
|
//
|
|
continue;
|
|
}
|
|
else if (
|
|
type != NULL &&
|
|
StringLen(symbol) > 0)
|
|
{
|
|
//
|
|
if (type == positionType &&
|
|
symbol == positionSymbol)
|
|
{
|
|
//
|
|
if (type == POSITION_TYPE_BUY)
|
|
{
|
|
//
|
|
if (profit < positionAbsProfit)
|
|
{
|
|
//
|
|
result = positionTicket;
|
|
profit = positionAbsProfit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
if (profit < positionAbsProfit)
|
|
{
|
|
//
|
|
result = positionTicket;
|
|
profit = positionAbsProfit;
|
|
}
|
|
}
|
|
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
else if (StringLen(symbol) > 0)
|
|
{
|
|
//
|
|
if (positionSymbol == symbol)
|
|
{
|
|
//
|
|
if (profit < positionAbsProfit)
|
|
{
|
|
//
|
|
result = positionTicket;
|
|
profit = positionAbsProfit;
|
|
}
|
|
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
else if (type != NULL)
|
|
{
|
|
//
|
|
if (type == positionType)
|
|
{
|
|
//
|
|
if (type == POSITION_TYPE_BUY)
|
|
{
|
|
//
|
|
if (profit < positionAbsProfit)
|
|
{
|
|
//
|
|
result = positionTicket;
|
|
profit = positionAbsProfit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
if (profit < positionAbsProfit)
|
|
{
|
|
//
|
|
result = positionTicket;
|
|
profit = positionAbsProfit;
|
|
}
|
|
}
|
|
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Max In DrawDown Position Ticket ...
|
|
ulong GetMaxInDrawDownPosition(
|
|
string symbol = "", // Specified Symbol
|
|
ENUM_POSITION_TYPE type = NULL // Specified Type
|
|
)
|
|
{
|
|
//
|
|
ulong result = -1;
|
|
|
|
//
|
|
double profit = 0;
|
|
|
|
//
|
|
int positionsCount = PositionsTotal();
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
if (!mPositionInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Magic() != mMagicNumber)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ulong positionTicket = mPositionInfo.Ticket();
|
|
|
|
//
|
|
string positionSymbol = mPositionInfo.Symbol();
|
|
|
|
//
|
|
double positionProfit = mPositionInfo.Profit();
|
|
double positionAbsProfit = MathAbs(positionProfit);
|
|
if (positionProfit >= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ENUM_POSITION_TYPE positionType = mPositionInfo.PositionType();
|
|
|
|
//
|
|
if (
|
|
type == NULL &&
|
|
StringLen(symbol) <= 0)
|
|
{
|
|
//
|
|
if (profit < positionAbsProfit)
|
|
{
|
|
//
|
|
result = positionTicket;
|
|
profit = positionAbsProfit;
|
|
}
|
|
|
|
//
|
|
continue;
|
|
}
|
|
else if (
|
|
type != NULL &&
|
|
StringLen(symbol) > 0)
|
|
{
|
|
//
|
|
if (type == positionType &&
|
|
symbol == positionSymbol)
|
|
{
|
|
//
|
|
if (type == POSITION_TYPE_BUY)
|
|
{
|
|
//
|
|
if (profit < positionAbsProfit)
|
|
{
|
|
//
|
|
result = positionTicket;
|
|
profit = positionAbsProfit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
if (profit < positionAbsProfit)
|
|
{
|
|
//
|
|
result = positionTicket;
|
|
profit = positionAbsProfit;
|
|
}
|
|
}
|
|
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
else if (StringLen(symbol) > 0)
|
|
{
|
|
//
|
|
if (positionSymbol == symbol)
|
|
{
|
|
//
|
|
if (profit < positionAbsProfit)
|
|
{
|
|
//
|
|
result = positionTicket;
|
|
profit = positionAbsProfit;
|
|
}
|
|
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
else if (type != NULL)
|
|
{
|
|
//
|
|
if (type == positionType)
|
|
{
|
|
//
|
|
if (type == POSITION_TYPE_BUY)
|
|
{
|
|
//
|
|
if (profit < positionAbsProfit)
|
|
{
|
|
//
|
|
result = positionTicket;
|
|
profit = positionAbsProfit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
if (profit < positionAbsProfit)
|
|
{
|
|
//
|
|
result = positionTicket;
|
|
profit = positionAbsProfit;
|
|
}
|
|
}
|
|
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Position's Profit ...
|
|
double GetPositionProfit(
|
|
const ulong ticket // Specified Position Ticket
|
|
)
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
int positionsCount = PositionsTotal();
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
if (!mPositionInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Magic() != mMagicNumber)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ulong positionTicket = mPositionInfo.Ticket();
|
|
if (positionTicket != ticket)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
result = mPositionInfo.Profit();
|
|
break;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Position's OpenDate ...
|
|
datetime GetPositionOpenDate(
|
|
const ulong ticket // Specified Position Ticket
|
|
)
|
|
{
|
|
//
|
|
datetime result = 0;
|
|
|
|
//
|
|
int positionsCount = PositionsTotal();
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
if (!mPositionInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Magic() != mMagicNumber)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ulong positionTicket = mPositionInfo.Ticket();
|
|
if (positionTicket != ticket)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
result = mPositionInfo.Time();
|
|
break;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Position's Comment ...
|
|
string GetPositionComment(
|
|
const ulong ticket // Specified Position Ticket
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
int positionsCount = PositionsTotal();
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
if (!mPositionInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Magic() != mMagicNumber)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ulong positionTicket = mPositionInfo.Ticket();
|
|
if (positionTicket != ticket)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
result = mPositionInfo.Comment();
|
|
break;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve All Open Positions Profit ...
|
|
double GetPositionsProfit(
|
|
string symbol = "", // Specified Symbol
|
|
ENUM_POSITION_TYPE type = NULL // Specified Type
|
|
)
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
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);
|
|
|
|
//
|
|
int positionsCount = PositionsTotal();
|
|
if (positionsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
XPosition allPositions[];
|
|
GetPositions(allPositions);
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = allPositions[i];
|
|
|
|
//
|
|
if (symbol != iPosition.symbol)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ENUM_TIMEFRAMES positionPeriod = ExtractPositionPeriod(iPosition.comment);
|
|
if (period != positionPeriod)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
iPosition,
|
|
result);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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()));
|
|
}
|
|
|
|
//
|
|
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];
|
|
|
|
//
|
|
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 ...
|
|
void ClosePositions(
|
|
XPosition &positions[], // Positions to Close ...
|
|
string comment = "" // Specified Comment ...
|
|
)
|
|
{
|
|
//
|
|
int positionsCount = ArraySize(positions);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
Close(
|
|
iPosition.ticket,
|
|
comment);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Retrieve All Orders ...
|
|
void GetOrders(
|
|
XOrder &result[], // 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 pocitionID = 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.setupAt = setupAt;
|
|
iOrder.comment = comment;
|
|
iOrder.filling = filling;
|
|
iOrder.lifetime = lifetime;
|
|
iOrder.expiredAt = expiredAt;
|
|
iOrder.openPrice = openPrice;
|
|
iOrder.pocitionID = pocitionID;
|
|
iOrder.executedAt = executedAt;
|
|
iOrder.currentPrice = currentPrice;
|
|
iOrder.initialVolume = initialVolume;
|
|
iOrder.currentVolume = currentVolume;
|
|
iOrder.stopLimitPrice = stopLimitPrice;
|
|
|
|
//
|
|
Add(
|
|
iOrder,
|
|
result);
|
|
}
|
|
}
|
|
|
|
//
|
|
int OrdersCount()
|
|
{
|
|
return OrdersTotal();
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Generate String Representation Of Position's Time Frame ...
|
|
string GeneratePositionPeriodComment(ENUM_TIMEFRAMES period)
|
|
{
|
|
//
|
|
string result = mPeriodPrefix + "(" + ToString(period) + ")";
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extract Position Time Frame From it's Comment ...
|
|
ENUM_TIMEFRAMES ExtractPositionPeriod(string comment)
|
|
{
|
|
//
|
|
ENUM_TIMEFRAMES result = _Period;
|
|
|
|
//
|
|
if (StringLen(comment) <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string periodStr = ExtractString(
|
|
comment,
|
|
mPeriodPrefix + "(",
|
|
")");
|
|
if (StringLen(periodStr) <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = ToPeriod(periodStr);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Last Open Position Ticket ...
|
|
ulong GetLastOpenPositionTicket()
|
|
{
|
|
//
|
|
ulong result = 0;
|
|
|
|
int positionsCount = PositionsTotal();
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = PositionGetTicket(PositionsTotal() - 1);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// 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 ...
|
|
|
|
//
|
|
// Find an Specific Position Has Support Signal Or not ...
|
|
bool HasSupport(ulong ticket)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int count = PositionsCount();
|
|
if (count <= 1)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition position;
|
|
result = GetPosition(
|
|
position,
|
|
ticket);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
GetPositions(positions);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
string supportTicketStr = ExtractParentTicketOfSupport(iPosition.comment);
|
|
if (StringLen(supportTicketStr) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ulong parentTicket = (ulong)supportTicketStr;
|
|
|
|
//
|
|
result =
|
|
parentTicket == ticket &&
|
|
iPosition.ticket != parentTicket;
|
|
if (result)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get All Support Positions for Specific Position ...
|
|
bool GetSupportPositions(
|
|
ulong ticket, // Parent Position Ticket
|
|
XPosition &supports[] // Holds Result ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
Clean(supports);
|
|
|
|
//
|
|
int count = PositionsCount();
|
|
if (count <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition parentPosition;
|
|
bool positionExist = GetPosition(
|
|
parentPosition,
|
|
ticket);
|
|
if (!positionExist)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool hasSupport = HasSupport(ticket);
|
|
if (!hasSupport)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
GetPositions(positions);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
if (iPosition.ticket == ticket)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
string pTicketStr = ExtractParentTicketOfSupport(iPosition.comment);
|
|
if (StringLen(pTicketStr) <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ulong pTicket = (ulong)pTicketStr;
|
|
if (pTicket != ticket)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
iPosition,
|
|
supports);
|
|
}
|
|
|
|
//
|
|
result = ArraySize(supports) > 0;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool GetSupportOrders(
|
|
ulong ticket, // Parent Position Ticket
|
|
XOrder &supports[], // Holds Result ...
|
|
string symbol, // Trading Symbol ...
|
|
ENUM_TIMEFRAMES period, // Trading TimeFrame ...
|
|
ENUM_ORDER_STATE state = NULL // Order State ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
Clean(supports);
|
|
|
|
//
|
|
XOrder orders[];
|
|
GetOrders(
|
|
orders,
|
|
symbol,
|
|
period,
|
|
state);
|
|
int ordersCount = ArraySize(orders);
|
|
result = ordersCount > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < ordersCount; i++)
|
|
{
|
|
//
|
|
XOrder iOrder = orders[i];
|
|
|
|
//
|
|
string pTicketStr = ExtractParentTicketOfSupport(iOrder.comment);
|
|
if (StringLen(pTicketStr) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ulong pTicket = (ulong)pTicketStr;
|
|
if (pTicket != ticket)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
iOrder,
|
|
supports);
|
|
}
|
|
|
|
//
|
|
result = ArraySize(supports) > 0;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate SL Support Signal ...
|
|
bool GenerateSLSupportSignal(
|
|
XSignal &signal, // Holds Result ....
|
|
ulong ticket, // Specific Position Ticket ...
|
|
double slGapPoint = 10, // Which Distance to SL for Enabling Signal ...
|
|
double volumeMultiplier = 2 // Support Signals Volume Multiplier ...
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int count = PositionsCount();
|
|
if (count <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool hasSupport = HasSupport(ticket);
|
|
if (hasSupport)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition position;
|
|
result = GetPosition(
|
|
position,
|
|
ticket);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool isLong =
|
|
IsLong(position.type);
|
|
|
|
//
|
|
string symbol = position.symbol;
|
|
ENUM_TIMEFRAMES period = ExtractPositionPeriod(position.comment);
|
|
double volume = position.volume * volumeMultiplier;
|
|
volume = NormalizeVolume(
|
|
volume,
|
|
symbol);
|
|
|
|
ENUM_POSITION_TYPE type = isLong
|
|
? POSITION_TYPE_SELL
|
|
: POSITION_TYPE_BUY;
|
|
ENUM_X_ORDER_MODES mode = X_ORDER_MODE_STOP;
|
|
|
|
//
|
|
double slGapPrice = PointToPrice(
|
|
symbol,
|
|
slGapPoint);
|
|
double riskPrice = MathAbs(position.entry - position.sl);
|
|
|
|
//
|
|
double entry =
|
|
isLong
|
|
? position.sl + slGapPrice
|
|
: position.sl - slGapPrice;
|
|
entry = NormalizePrice(entry, symbol);
|
|
|
|
//
|
|
double sl =
|
|
isLong
|
|
? entry + riskPrice
|
|
: entry - riskPrice;
|
|
sl = NormalizePrice(sl, symbol);
|
|
|
|
//
|
|
double tp =
|
|
isLong
|
|
? entry - riskPrice
|
|
: entry + riskPrice;
|
|
tp = NormalizePrice(tp, symbol);
|
|
|
|
//
|
|
signal.sl = sl;
|
|
signal.tp = tp;
|
|
signal.type = type;
|
|
signal.mode = mode;
|
|
signal.entry = entry;
|
|
signal.volume = volume;
|
|
signal.symbol = symbol;
|
|
signal.period = period;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close a Position and it's Supports on a Profit ...
|
|
bool ClosePositionAndSupports(
|
|
ulong ticket, // Position Ticket
|
|
double minProfit = 4 // Minimum Profit For Closing
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (minProfit <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int count = PositionsCount();
|
|
if (count <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition position;
|
|
bool isExist = GetPosition(
|
|
position,
|
|
ticket);
|
|
if (!isExist)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool hasSupport = HasSupport(ticket);
|
|
if (!hasSupport)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition supports[];
|
|
hasSupport = GetSupportPositions(
|
|
ticket,
|
|
supports);
|
|
int supportsCount = ArraySize(supports);
|
|
if (!hasSupport || supportsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double profitSummary = 0;
|
|
for (int i = 0; i < supportsCount; i++)
|
|
{
|
|
//
|
|
XPosition iSupport = supports[i];
|
|
|
|
//
|
|
profitSummary += iSupport.profit;
|
|
}
|
|
|
|
//
|
|
result = profitSummary > 0 && profitSummary >= minProfit;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
position,
|
|
supports);
|
|
|
|
//
|
|
string comment = "Close Position (" + (string)ticket + ") and it's Supports in Profit: " + (string)profitSummary + " ...";
|
|
ClosePositions(
|
|
supports,
|
|
comment);
|
|
|
|
//
|
|
if (result)
|
|
{
|
|
LogMessage(comment);
|
|
}
|
|
|
|
//
|
|
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++)
|
|
{
|
|
//
|
|
OnTradeStateChanged 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++)
|
|
{
|
|
//
|
|
OnDealsChanged 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++)
|
|
{
|
|
//
|
|
OnOrdersChanged 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++)
|
|
{
|
|
//
|
|
OnPositionsChanged 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++)
|
|
{
|
|
//
|
|
OnStopLoss 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++)
|
|
{
|
|
//
|
|
OnTakeProfit 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;
|
|
|
|
//
|
|
// 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 ...
|
|
OnDealsChanged mDealsChangedEventHandlers[];
|
|
|
|
//
|
|
// Orders Changed Event Handlers ...
|
|
OnOrdersChanged mOrdersChangedEventHandlers[];
|
|
|
|
//
|
|
// Positions Changed Event Handlers ...
|
|
OnPositionsChanged mPositionsChangedEventHandlers[];
|
|
|
|
//
|
|
// Trade State Changed Event Handlers ...
|
|
OnTradeStateChanged mTradeStateChangedEventHandlers[];
|
|
|
|
//
|
|
// TPSL ...
|
|
OnStopLoss mStopLossEventHandlers[];
|
|
OnTakeProfit 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;
|
|
}
|
|
|
|
//
|
|
// END Usefull Functions ...
|
|
//
|