6626 lines
156 KiB
Plaintext
6626 lines
156 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Class Library
|
|
// ----------------------------------------------
|
|
// Name: XSCTrade
|
|
// Description: provides all Trade requirements ...
|
|
//
|
|
//
|
|
// Maintainer:
|
|
// ------------
|
|
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
|
|
//
|
|
//////////////////////////////////////////////////////
|
|
//
|
|
// Global Properties ...
|
|
#property library
|
|
#property copyright "Copyright 2023, SaherElm IT Center"
|
|
#property link "https://www.saherelm.ir"
|
|
#property version "1.00"
|
|
#property strict
|
|
|
|
//
|
|
// Imports ...
|
|
#include "../Classes/x-saherelm.base.class.mq5"
|
|
#include "../Libraries/x-saherelm.tools.lib.mq5"
|
|
#include "../Classes/x-saherelm.xalert.class.mq5"
|
|
#include "../Libraries/x-saherelm.xtrade.lib.mq5"
|
|
#include "../Classes/x-saherelm.xaccount.class.mq5"
|
|
|
|
//
|
|
// Definitions ...
|
|
|
|
//
|
|
// 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));
|
|
}
|
|
};
|
|
|
|
//
|
|
// Implementation ...
|
|
|
|
//
|
|
// a Class For Manage Trades and Handle Trading Actions ...
|
|
class XSCTrade : public XSCBase
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Props ...
|
|
XSCAccount *mAccount; // Account Info Provider
|
|
|
|
//
|
|
// Constructors ...
|
|
void XSCTrade(
|
|
int slippage, // Specify Slippage
|
|
ulong magicNumber, // Specify Magic Number
|
|
double maxAllowedSpread = 0, // Max Allowed Spred for Opening Trades
|
|
int maxAllowedPositions = 0, // Max Allowed Positions
|
|
double maxAllowedDrawdownFactor = 0 // Max Allowed Drawdown Factor
|
|
)
|
|
{
|
|
//
|
|
// Setting Props ...
|
|
mSlippage = slippage;
|
|
mMagicNumber = magicNumber;
|
|
|
|
//
|
|
mMaxAllowedSpread = maxAllowedSpread;
|
|
mMaxAllowedPositions = maxAllowedPositions;
|
|
mMaxAllowedDrawdownFactor = maxAllowedDrawdownFactor;
|
|
|
|
//
|
|
mAccount = new XSCAccount();
|
|
|
|
//
|
|
// Instance Base Trader Class ...
|
|
mTrader = new XSCTradeBase();
|
|
|
|
//
|
|
// Configuring Base Trader Class ...
|
|
mTrader.SetAsyncMode(false);
|
|
mTrader.SetDeviationInPoints(mSlippage);
|
|
mTrader.SetExpertMagicNumber(mMagicNumber);
|
|
|
|
//
|
|
// Setting Log Level ...
|
|
mTrader.LogLevel(LOG_LEVEL_ERRORS);
|
|
|
|
//
|
|
// OnTrade Context ...
|
|
|
|
//
|
|
mDaysForRead = 1;
|
|
mScanStarted = false;
|
|
|
|
//
|
|
// Reset On Trade Counter ...
|
|
ResetOnTradeContext();
|
|
|
|
//
|
|
// Init On Trade Context ...
|
|
InitOnTradeContext();
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
~XSCTrade()
|
|
{
|
|
//
|
|
// Remove Pointer ...
|
|
delete mTrader;
|
|
delete mAccount;
|
|
|
|
//
|
|
Clean(mOnModifyEventHandlers);
|
|
Clean(mStopLossEventHandlers);
|
|
Clean(mTakeProfitEventHandlers);
|
|
Clean(mOnForceCloseEventHandlers);
|
|
Clean(mDealsChangedEventHandlers);
|
|
Clean(mOrdersChangedEventHandlers);
|
|
Clean(mOnPartialCloseEventHandlers);
|
|
Clean(mPositionsChangedEventHandlers);
|
|
Clean(mTradeStateChangedEventHandlers);
|
|
}
|
|
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Retrieve Slippage ...
|
|
int GetSlippage()
|
|
{
|
|
return mSlippage;
|
|
}
|
|
|
|
//
|
|
// Retrieve Magic Number ...
|
|
ulong GetMagicNumber()
|
|
{
|
|
return mMagicNumber;
|
|
}
|
|
|
|
//
|
|
double GetMaxAllowedSpread()
|
|
{
|
|
return mMaxAllowedSpread;
|
|
}
|
|
|
|
//
|
|
int GetMaxAllowedPositions()
|
|
{
|
|
return mMaxAllowedPositions;
|
|
}
|
|
|
|
//
|
|
void SetMaxAllowedPositions(int value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mMaxAllowedPositions = value;
|
|
}
|
|
|
|
//
|
|
double GetMaxAllowedDrawdownFactor()
|
|
{
|
|
return mMaxAllowedDrawdownFactor;
|
|
}
|
|
|
|
//
|
|
// Add Event Listeners ...
|
|
|
|
//
|
|
void AddOnModifyPositionEventHandler(TOnModify handler)
|
|
{
|
|
//
|
|
Add(
|
|
handler,
|
|
mOnModifyEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
void AddOnForceClosePositionEventHandler(TOnForceClose handler)
|
|
{
|
|
//
|
|
Add(
|
|
handler,
|
|
mOnForceCloseEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
void AddOnPartialClosePositionEventHandler(TOnPartialClose handler)
|
|
{
|
|
//
|
|
Add(
|
|
handler,
|
|
mOnPartialCloseEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add New Deals Changed Event Handler ...
|
|
void AddOnTradeStateChangedEventHandler(TOnTradeStateChanged handler)
|
|
{
|
|
//
|
|
Add(
|
|
handler,
|
|
mTradeStateChangedEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add New Deals Changed Event Handler ...
|
|
void AddOnDealsChangedEventHandler(TOnDealsChanged handler)
|
|
{
|
|
//
|
|
Add(
|
|
handler,
|
|
mDealsChangedEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add New Orders Changed Event Handler ...
|
|
void AddOnOrdersChangedEventHandler(TOnOrdersChanged handler)
|
|
{
|
|
//
|
|
Add(
|
|
handler,
|
|
mOrdersChangedEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add New Positions Changed Event Handler ...
|
|
void AddOnPositionsChangedEventHandler(TOnPositionsChanged handler)
|
|
{
|
|
//
|
|
Add(
|
|
handler,
|
|
mPositionsChangedEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add New Stop Loss Event Handler ...
|
|
void AddOnStopLossEventHandler(TOnStopLoss handler)
|
|
{
|
|
//
|
|
Add(
|
|
handler,
|
|
mStopLossEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add New Stop Loss Event Handler ...
|
|
void AddOnTakeProfitEventHandler(TOnTakeProfit handler)
|
|
{
|
|
//
|
|
Add(
|
|
handler,
|
|
mTakeProfitEventHandlers
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
// Functions ...
|
|
|
|
//
|
|
// Handlers ...
|
|
|
|
//
|
|
// Process On Trade Requirement ...
|
|
// this Only Used when we Have to Proccess OnTrade Event in EA(s) ...
|
|
void HandleOnTradeEvent(
|
|
XOnTradeHandlerState &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
result.Clean();
|
|
|
|
//
|
|
if (mScanStarted)
|
|
{
|
|
//
|
|
ProcessOnTradeContext(result);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Init Trade Context ...
|
|
InitOnTradeContext();
|
|
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Here we Can Check the Start ...
|
|
bool isValid =
|
|
//
|
|
result.IsValid() &&
|
|
//
|
|
!result.hasNewDeal &&
|
|
!result.hasNewOrder &&
|
|
!result.hasNewPosition &&
|
|
!result.hasNewHistoryOrder
|
|
//
|
|
;
|
|
if (!isValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// State ...
|
|
|
|
//
|
|
// Notfy Trade State Changed Event ...
|
|
NotifyTradeStateChangeEvent(result);
|
|
|
|
//
|
|
// Deals ...
|
|
if (result.hasNewDeal)
|
|
{
|
|
//
|
|
// Notfy Deals Changed Event ...
|
|
NotifyDealsChangeEvent(result.newDeals);
|
|
|
|
//
|
|
// Retrieve Last Deal ...
|
|
XDeal deals[];
|
|
int dealsCount = GetDeals(deals);
|
|
if (dealsCount > 0)
|
|
{
|
|
//
|
|
XDeal lastDeal = deals[0];
|
|
|
|
//
|
|
if (lastDeal.reason == DEAL_REASON_TP)
|
|
{
|
|
NotifyTakeProfitEvent(lastDeal);
|
|
}
|
|
else if (lastDeal.reason == DEAL_REASON_SL)
|
|
{
|
|
NotifyStopLossEvent(lastDeal);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Orders ...
|
|
if (result.hasNewOrder || result.hasNewHistoryOrder)
|
|
{
|
|
//
|
|
// Notfy Orders Changed Event ...
|
|
NotifyOrdersChangeEvent(result.newOrders + result.newHistoryOrders);
|
|
}
|
|
|
|
//
|
|
// Positions ...
|
|
if (result.hasNewPosition)
|
|
{
|
|
//
|
|
// Notfy Positions Changed Event ...
|
|
NotifyPositionsChangeEvent(result.newPositions);
|
|
}
|
|
}
|
|
|
|
//
|
|
// This Must be Called Every time OnTrade Event Happening ...
|
|
// this Used for Implementing Custom Senarios ...
|
|
XOnTradeHandlerState HandleOnTrade()
|
|
{
|
|
//
|
|
// Handle OnTrade Event ...
|
|
XOnTradeHandlerState state;
|
|
HandleOnTradeEvent(state);
|
|
if (
|
|
!state.hasNewDeal &&
|
|
!state.hasNewOrder &&
|
|
!state.hasNewPosition &&
|
|
!state.hasNewHistoryOrder)
|
|
{
|
|
return state;
|
|
}
|
|
|
|
//
|
|
// State ...
|
|
|
|
//
|
|
// Notfy Trade State Changed Event ...
|
|
NotifyTradeStateChangeEvent(state);
|
|
|
|
//
|
|
// DEALS ...
|
|
if (state.hasNewDeal)
|
|
{
|
|
//
|
|
// Notfy Deals Changed Event ...
|
|
NotifyDealsChangeEvent(state.newDeals);
|
|
|
|
//
|
|
// Retrieve Last Deal ...
|
|
XDeal deals[];
|
|
GetDeals(deals);
|
|
int dealsCount = ArraySize(deals);
|
|
if (dealsCount > 0)
|
|
{
|
|
//
|
|
XDeal lastDeal = deals[0];
|
|
|
|
//
|
|
if (lastDeal.reason == DEAL_REASON_TP)
|
|
{
|
|
NotifyTakeProfitEvent(lastDeal);
|
|
}
|
|
else if (lastDeal.reason == DEAL_REASON_SL)
|
|
{
|
|
NotifyStopLossEvent(lastDeal);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// ORDERS ...
|
|
if (state.hasNewOrder || state.hasNewHistoryOrder)
|
|
{
|
|
//
|
|
// Notfy Orders Changed Event ...
|
|
NotifyOrdersChangeEvent(state.newOrders + state.newHistoryOrders);
|
|
}
|
|
|
|
//
|
|
// POSITIONS ...
|
|
if (state.hasNewPosition)
|
|
{
|
|
//
|
|
// Notfy Positions Changed Event ...
|
|
NotifyPositionsChangeEvent(state.newPositions);
|
|
}
|
|
|
|
//
|
|
return state;
|
|
}
|
|
|
|
//
|
|
// Trade Actions ...
|
|
|
|
//
|
|
// Main ...
|
|
|
|
//
|
|
// Policies ...
|
|
|
|
//
|
|
// Check Acocunt Balance is Allow new Trades or not ...
|
|
bool CheckEquityForTrade()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (mMaxAllowedDrawdownFactor <= 0)
|
|
{
|
|
//
|
|
result = true;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double equity = mAccount.GetEquity();
|
|
double balance = mAccount.GetBalance();
|
|
|
|
//
|
|
double selectedBalance = MathMax(equity, balance) - MathMin(equity, balance);
|
|
|
|
//
|
|
double maxAllowedBalanceDrawDown = GetMaxAllowedEquity();
|
|
|
|
//
|
|
result = equity > balance ||
|
|
(equity <= balance &&
|
|
selectedBalance <= maxAllowedBalanceDrawDown);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Max Allowed Positions Policy for Trade ...
|
|
bool CheckPositionsForTrade()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (mMaxAllowedPositions <= 0)
|
|
{
|
|
//
|
|
result = true;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = GetPositions(positions);
|
|
if (positionsCount <= 0)
|
|
{
|
|
//
|
|
result = true;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = positionsCount < mMaxAllowedPositions;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Spread is Ok for Executing Specific Signal ...
|
|
bool CheckSpreadForSignalExecution(XSignal &mSignal)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
bool isSpreadCheckEnable = mMaxAllowedSpread > 0;
|
|
if (!isSpreadCheckEnable)
|
|
{
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Retrieve Signal Spread ...
|
|
double spread = mSignal.GetSpread();
|
|
result = spread <= mMaxAllowedSpread;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Account Policy ...
|
|
bool CheckAccountPolicy(
|
|
XSignal &mSignal, // For Executing Signal
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT &state // State Holder
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check Positions For Trade ...
|
|
result = CheckPositionsForTrade();
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_MAX_REACHED;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Equity For Trade ...
|
|
result = CheckEquityForTrade();
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_NO_EQUITY;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Spread for Trade ...
|
|
result = CheckSpreadForSignalExecution(mSignal);
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_SPREAD;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Execute a Signal ...
|
|
bool ExecuteSignal(
|
|
XSignal &mSignal, // Signal
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT &state, // Result
|
|
ENUM_ORDER_TYPE_TIME lifetime = ORDER_TIME_GTC, // Order LifeTime
|
|
datetime expiration = NULL, // order expiration time
|
|
bool checkPolicies = true // Check Account Policies
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
state = X_SIGNAL_EXECUTION_UNKNOWN;
|
|
|
|
//
|
|
double currentEntry = GetEntry(
|
|
mSignal.symbol,
|
|
mSignal.type
|
|
//
|
|
);
|
|
bool isLong = IsLong(mSignal.type);
|
|
|
|
//
|
|
if (!mSignal.IsValid())
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_INVALID_PARAMS;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Prepare Signal Comment ...
|
|
string mSComment = mSignal.GenerateComment();
|
|
if (!Contains(mSComment, mSignal.comment))
|
|
{
|
|
mSignal.comment += mSComment;
|
|
}
|
|
|
|
//
|
|
// Handle Market Execution ...
|
|
if (mSignal.mode == X_ORDER_MODE_MARKET)
|
|
{
|
|
//
|
|
// Do Apply Policies here ...
|
|
bool hasPolicy = !checkPolicies
|
|
? true
|
|
: CheckAccountPolicy(mSignal, state);
|
|
if (!hasPolicy)
|
|
{
|
|
result = false;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
if (isLong)
|
|
{
|
|
//
|
|
result = Buy(
|
|
mSignal.symbol,
|
|
mSignal.period,
|
|
mSignal.volume,
|
|
mSignal.entry,
|
|
mSignal.sl,
|
|
mSignal.tp,
|
|
mSignal.comment
|
|
//
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
result = Sell(
|
|
mSignal.symbol,
|
|
mSignal.period,
|
|
mSignal.volume,
|
|
mSignal.entry,
|
|
mSignal.sl,
|
|
mSignal.tp,
|
|
mSignal.comment
|
|
//
|
|
);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Handle Stop Execution ...
|
|
else if (mSignal.mode == X_ORDER_MODE_STOP)
|
|
{
|
|
|
|
//
|
|
// Check Conditions ...
|
|
|
|
//
|
|
// Check Price ...
|
|
result = isLong
|
|
? mSignal.entry > currentEntry
|
|
: mSignal.entry < currentEntry;
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_PRICE_ERROR;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Execute Order ...
|
|
if (isLong)
|
|
{
|
|
//
|
|
result = BuyStop(
|
|
mSignal.symbol,
|
|
mSignal.period,
|
|
mSignal.volume,
|
|
mSignal.entry,
|
|
mSignal.sl,
|
|
mSignal.tp,
|
|
lifetime,
|
|
expiration,
|
|
mSignal.comment
|
|
//
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
result = SellStop(
|
|
mSignal.symbol,
|
|
mSignal.period,
|
|
mSignal.volume,
|
|
mSignal.entry,
|
|
mSignal.sl,
|
|
mSignal.tp,
|
|
lifetime,
|
|
expiration,
|
|
mSignal.comment
|
|
//
|
|
);
|
|
}
|
|
}
|
|
//
|
|
// Handle Limit Execution ...
|
|
else if (mSignal.mode == X_ORDER_MODE_LIMIT)
|
|
{
|
|
//
|
|
// Check Conditions ...
|
|
|
|
//
|
|
// Check Price ...
|
|
result = isLong
|
|
? mSignal.entry < currentEntry
|
|
: mSignal.entry > currentEntry;
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_PRICE_ERROR;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Execute Order ...
|
|
if (isLong)
|
|
{
|
|
//
|
|
result = BuyLimit(
|
|
mSignal.symbol,
|
|
mSignal.period,
|
|
mSignal.volume,
|
|
mSignal.entry,
|
|
mSignal.sl,
|
|
mSignal.tp,
|
|
lifetime,
|
|
expiration,
|
|
mSignal.comment
|
|
//
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
result = SellLimit(
|
|
mSignal.symbol,
|
|
mSignal.period,
|
|
mSignal.volume,
|
|
mSignal.entry,
|
|
mSignal.sl,
|
|
mSignal.tp,
|
|
lifetime,
|
|
expiration,
|
|
mSignal.comment
|
|
//
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handle Ticket ...
|
|
if (result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_SUCCEED;
|
|
|
|
//
|
|
ulong ticket;
|
|
if (mSignal.mode == X_ORDER_MODE_MARKET)
|
|
{
|
|
ticket = GetLastOpenPositionTicket();
|
|
}
|
|
else
|
|
{
|
|
ticket = GetLastPlacedOrderTicket();
|
|
}
|
|
|
|
//
|
|
mSignal.positionId = ticket;
|
|
|
|
//
|
|
// Handle Store XPositionInfo ...
|
|
if (mSignal.mode == X_ORDER_MODE_MARKET)
|
|
{
|
|
//
|
|
// TODO: Implement this ...
|
|
}
|
|
|
|
//
|
|
// Handle Executing Support Signals ...
|
|
int supportsCount = ArraySize(mSignal.supports);
|
|
if (supportsCount > 0)
|
|
{
|
|
//
|
|
int executed = 0;
|
|
for (int i = 0; i < supportsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSupport = mSignal.supports[i];
|
|
|
|
//
|
|
int supIndex = i + 1;
|
|
string iComment = GenerateSupportTag(ticket);
|
|
iSupport.comment = iComment;
|
|
|
|
//
|
|
bool isExecuted = ExecuteSignal(
|
|
iSupport,
|
|
state,
|
|
lifetime,
|
|
expiration
|
|
//
|
|
);
|
|
if (isExecuted)
|
|
{
|
|
executed++;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = executed == supportsCount;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Execute a Collection Of Signals ...
|
|
int ExecuteSignals(
|
|
XSignal &signals[], // Collection of Signal for Execution
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT &states[], // Signal Execution Result
|
|
ENUM_ORDER_TYPE_TIME lifetime = ORDER_TIME_GTC, // Order LifeTime
|
|
datetime expiration = NULL, // order expiration time
|
|
bool checkPolicies = true // Check Account Policies
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(states);
|
|
|
|
//
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ArrayResize(states, signalsCount);
|
|
ArrayInitialize(states, X_SIGNAL_EXECUTION_UNKNOWN);
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
bool isExecuted = ExecuteSignal(
|
|
signals[i],
|
|
states[i],
|
|
lifetime,
|
|
expiration,
|
|
checkPolicies
|
|
//
|
|
);
|
|
|
|
//
|
|
if (isExecuted)
|
|
{
|
|
result++;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int ExecuteSpecifiedSignals(
|
|
XSignal &signals[], // Collection of Signal for Execution
|
|
XSignal ¬Executeds[], // Not Executed Signal
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT &states[], // Signal Execution Result
|
|
ENUM_ORDER_TYPE_TIME lifetime = ORDER_TIME_GTC, // Order LifeTime
|
|
datetime expiration = NULL, // order expiration time
|
|
bool checkPolicies = true // Check Account Policies
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(states);
|
|
Clean(notExecuteds);
|
|
|
|
//
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ArrayResize(states, signalsCount);
|
|
ArrayInitialize(states, X_SIGNAL_EXECUTION_UNKNOWN);
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
bool isExecuted = ExecuteSignal(
|
|
signals[i],
|
|
states[i],
|
|
lifetime,
|
|
expiration
|
|
//
|
|
);
|
|
|
|
//
|
|
if (isExecuted)
|
|
{
|
|
result++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
AddRef(
|
|
signals[i],
|
|
notExecuteds //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Regular Trade Actions ...
|
|
|
|
//
|
|
// Buy ...
|
|
|
|
//
|
|
// Force Open a Buy/Long Position ...
|
|
bool Buy(
|
|
string symbol, // Specified Symbol
|
|
ENUM_TIMEFRAMES period, // Specified Trade's Period
|
|
double volume, // position volume
|
|
double price, // execution price
|
|
double sl = 0, // stop loss price
|
|
double tp = 0, // take profit price
|
|
string comment = NULL // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
|
|
if (!IsValid(comment) ||
|
|
!IsValid(mPeriod))
|
|
{
|
|
//
|
|
string mPeriodStr = GeneratePeriodTag(period);
|
|
|
|
//
|
|
comment += mPeriodStr;
|
|
}
|
|
|
|
//
|
|
result = mTrader.Buy(
|
|
volume,
|
|
symbol,
|
|
price,
|
|
sl,
|
|
tp,
|
|
comment
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Send BUY LIMIT order ...
|
|
bool BuyLimit(
|
|
string symbol, // Specified Symbol
|
|
ENUM_TIMEFRAMES period, // Specified Trade's Period
|
|
double volume, // order volume
|
|
double price, // order price
|
|
double sl = 0.0, // stop loss price
|
|
double tp = 0.0, // take profit price
|
|
ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
|
|
datetime expiration = 0, // order expiration time
|
|
string comment = NULL // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
|
|
if (!IsValid(comment) ||
|
|
!IsValid(mPeriod))
|
|
{
|
|
//
|
|
string mPeriodStr = GeneratePeriodTag(period);
|
|
|
|
//
|
|
comment += mPeriodStr;
|
|
}
|
|
|
|
//
|
|
result = mTrader.BuyLimit(
|
|
volume,
|
|
price,
|
|
symbol,
|
|
sl,
|
|
tp,
|
|
typeTime,
|
|
expiration,
|
|
comment
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Send BUY STOP order ...
|
|
bool BuyStop(
|
|
string symbol, // Specified Symbol
|
|
ENUM_TIMEFRAMES period, // Specified Trade's Period
|
|
double volume, // order volume
|
|
double price, // order price
|
|
double sl = 0.0, // stop loss price
|
|
double tp = 0.0, // take profit price
|
|
ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
|
|
datetime expiration = 0, // order expiration time
|
|
string comment = NULL // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
|
|
if (!IsValid(comment) ||
|
|
!IsValid(mPeriod))
|
|
{
|
|
//
|
|
string mPeriodStr = GeneratePeriodTag(period);
|
|
|
|
//
|
|
comment += mPeriodStr;
|
|
}
|
|
|
|
//
|
|
result = mTrader.BuyStop(
|
|
volume,
|
|
price,
|
|
symbol,
|
|
sl,
|
|
tp,
|
|
typeTime,
|
|
expiration,
|
|
comment
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Sell ...
|
|
|
|
//
|
|
// Force Open a Sell/Short Position ...
|
|
bool Sell(
|
|
string symbol, // Specified Symbol
|
|
ENUM_TIMEFRAMES period, // Specified Trade's Period
|
|
double volume, // position volume
|
|
double price, // execution price
|
|
double sl = 0, // stop loss price
|
|
double tp = 0, // take profit price
|
|
string comment = NULL // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
|
|
if (!IsValid(comment) ||
|
|
!IsValid(mPeriod))
|
|
{
|
|
//
|
|
string mPeriodStr = GeneratePeriodTag(period);
|
|
|
|
//
|
|
comment += mPeriodStr;
|
|
}
|
|
|
|
//
|
|
result = mTrader.Sell(
|
|
volume,
|
|
symbol,
|
|
price,
|
|
sl,
|
|
tp,
|
|
comment
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Send SELL LIMIT order ...
|
|
bool SellLimit(
|
|
string symbol, // Specified Symbol
|
|
ENUM_TIMEFRAMES period, // Specified Trade's Period
|
|
double volume, // order volume
|
|
double price, // order price
|
|
double sl = 0.0, // stop loss price
|
|
double tp = 0.0, // take profit price
|
|
ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
|
|
datetime expiration = 0, // order expiration time
|
|
string comment = NULL // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
|
|
if (!IsValid(comment) ||
|
|
!IsValid(mPeriod))
|
|
{
|
|
//
|
|
string mPeriodStr = GeneratePeriodTag(period);
|
|
|
|
//
|
|
comment += mPeriodStr;
|
|
}
|
|
|
|
//
|
|
result = mTrader.SellLimit(
|
|
volume,
|
|
price,
|
|
symbol,
|
|
sl,
|
|
tp,
|
|
typeTime,
|
|
expiration,
|
|
comment
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Send SELL STOP order ...
|
|
bool SellStop(
|
|
string symbol, // Specified Symbol
|
|
ENUM_TIMEFRAMES period, // Specified Trade's Period
|
|
double volume, // order volume
|
|
double price, // order price
|
|
double sl = 0.0, // stop loss price
|
|
double tp = 0.0, // take profit price
|
|
ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
|
|
datetime expiration = 0, // order expiration time
|
|
string comment = NULL // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
|
|
if (!IsValid(comment) ||
|
|
!IsValid(mPeriod))
|
|
{
|
|
//
|
|
string mPeriodStr = GeneratePeriodTag(period);
|
|
|
|
//
|
|
comment += mPeriodStr;
|
|
}
|
|
|
|
//
|
|
result = mTrader.SellStop(
|
|
volume,
|
|
price,
|
|
symbol,
|
|
sl,
|
|
tp,
|
|
typeTime,
|
|
expiration,
|
|
comment
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check a Position is Exists Open or not ...
|
|
bool IsOpen(
|
|
ulong ticket // Specified Position Ticket
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = mPositionInfo
|
|
.SelectByTicket(ticket);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Modify Position ...
|
|
bool Modify(
|
|
const ulong ticket, // position ticket
|
|
double sl = 0, // stop loss price
|
|
double tp = 0, // take profit
|
|
string comment = NULL // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
XPosition position;
|
|
result =
|
|
GetPosition(
|
|
ticket,
|
|
position //
|
|
) &&
|
|
(tp >= 0 || sl >= 0);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Use Position Comment when there isn't new One ...
|
|
if (!IsValid(comment) == 0)
|
|
{
|
|
//
|
|
// if there isn't provided Comment
|
|
// Use Position Comment for default ...
|
|
comment = "Modify " + GetPositionComment(ticket);
|
|
}
|
|
|
|
//
|
|
result = mTrader.PositionModify(
|
|
ticket,
|
|
sl,
|
|
tp,
|
|
comment
|
|
//
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
NotifyModifyEvent(
|
|
ticket,
|
|
position.profit,
|
|
comment //
|
|
);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close Position By Ticket ...
|
|
bool Close(
|
|
ulong ticket, // Position Ticket
|
|
string comment = NULL // Close Position By Specific Comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check Position Open ...
|
|
XPosition position;
|
|
result = GetPosition(
|
|
ticket,
|
|
position //
|
|
);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Use Default Position Comment if it's Not Provided ...
|
|
if (!IsValid(comment))
|
|
{
|
|
comment = "Close " + GetPositionComment(ticket);
|
|
}
|
|
|
|
//
|
|
// Close Position By Specific Comment ...
|
|
result = mTrader.PositionClose(
|
|
ticket,
|
|
mSlippage,
|
|
comment
|
|
//
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
NotifyForceCloseEvent(
|
|
ticket,
|
|
position,
|
|
comment //
|
|
);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close Specified Positions ...
|
|
int Close(
|
|
string comment = NULL, // Close Position By Specific Comment
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
|
|
bool filterByMagic = true // Get Only Self Open Positions
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = GetPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
filterByMagic);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iPosition.ticket,
|
|
comment
|
|
//
|
|
);
|
|
if (isClosed)
|
|
{
|
|
result++;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close a Collection of Positions ...
|
|
int Close(
|
|
XPosition &positions[], // Collection of Positions
|
|
string comment // Close Comment
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
int positionsCount = ArraySize(positions);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iPosition.ticket,
|
|
comment
|
|
//
|
|
);
|
|
if (isClosed)
|
|
{
|
|
result++;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close Partial By Ticket ...
|
|
bool ClosePartial(
|
|
ulong ticket, // Position Ticket
|
|
double volume, // Volume Size for Partial Closing
|
|
string comment = NULL // Close Position By Specific Comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
XPosition position;
|
|
result = GetPosition(
|
|
ticket,
|
|
position //
|
|
);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Use Default Partial Close Comment ...
|
|
if (!IsValid(comment))
|
|
{
|
|
//
|
|
comment += "PClose " + GetPositionComment(ticket);
|
|
}
|
|
|
|
//
|
|
result = mTrader.PositionClosePartial(
|
|
ticket,
|
|
volume,
|
|
mSlippage,
|
|
comment
|
|
//
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
NotifyPartialCloseEvent(
|
|
ticket,
|
|
position.profit,
|
|
comment //
|
|
);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Cancel Specific Order ...
|
|
bool CancelOrder(ulong ticket)
|
|
{
|
|
return mTrader
|
|
.OrderDelete(ticket);
|
|
}
|
|
|
|
//
|
|
// Cancel Specific Orders ...
|
|
int CancelOrders(
|
|
XOrder &orders[] // Specified Orders ...
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
int ordersCount = ArraySize(orders);
|
|
if (ordersCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < ordersCount; i++)
|
|
{
|
|
//
|
|
XOrder iOrder = orders[i];
|
|
|
|
//
|
|
bool isDeleted = mTrader
|
|
.OrderDelete(iOrder.ticket);
|
|
if (isDeleted && !result)
|
|
{
|
|
result++;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extractors ...
|
|
|
|
//
|
|
// Positions ...
|
|
|
|
//
|
|
// Calculate Positions Profit ...
|
|
double Profit(
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
|
|
bool filterByMagic = true // Get Only Self Open Positions
|
|
)
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
// Retrieve Positions ...
|
|
XPosition positions[];
|
|
int positionsCount = GetPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
filterByMagic
|
|
//
|
|
);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
result += positions[i].profit;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Position by Index ...
|
|
bool GetPosition(
|
|
int index, // Specified Index
|
|
XPosition &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
return result
|
|
.ByIndex(index);
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Position by Ticket ...
|
|
bool GetPosition(
|
|
ulong ticket, // Specified Ticket
|
|
XPosition &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
return result
|
|
.ByTicket(ticket);
|
|
}
|
|
|
|
//
|
|
double GetPositionCommission(
|
|
ulong ticket // Specified Ticket
|
|
)
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
XPosition position;
|
|
bool hasPosition = GetPosition(
|
|
ticket,
|
|
position //
|
|
);
|
|
if (!hasPosition)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XDeal deals[];
|
|
int dealsCount = GetDeals(
|
|
deals,
|
|
position.symbol,
|
|
position.provider,
|
|
position.period,
|
|
NULL,
|
|
DEAL_ENTRY_IN //
|
|
);
|
|
if (IsValidSize(dealsCount))
|
|
{
|
|
//
|
|
for (int i = 0; i < dealsCount; i++)
|
|
{
|
|
//
|
|
XDeal iDeal = deals[i];
|
|
if (iDeal.positionId == ticket)
|
|
{
|
|
//
|
|
result = iDeal.commission;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Positions ...
|
|
int GetPositions(
|
|
XPosition &result[], // Hold's Result
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
|
|
bool filterByMagic = true, // Get Only Self Open Positions
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
int before = ArraySize(result);
|
|
|
|
//
|
|
if (forceClean)
|
|
{
|
|
Clean(result);
|
|
}
|
|
|
|
//
|
|
// Count Positions ...
|
|
int itemsCount = PositionsTotal();
|
|
if (itemsCount <= 0)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Loop Through all Positions ...
|
|
for (int i = itemsCount - 1; i >= 0; i--)
|
|
{
|
|
//
|
|
XPosition iItem;
|
|
bool isLoaded = iItem.ByIndex(i);
|
|
if (!isLoaded)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now we have to Check Given Filters ...
|
|
bool isFiltersPassed =
|
|
iItem.IsFiltersPassed(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
mMagicNumber,
|
|
filterByMagic
|
|
//
|
|
);
|
|
if (!isFiltersPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add Filtered Item into Result ...
|
|
AddRef(
|
|
iItem,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
int after = ArraySize(result);
|
|
|
|
//
|
|
mResult = after - before;
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
void GetPositions(
|
|
XPosition &longs[], // Hold's Longs Result
|
|
XPosition &shorts[], // Hold's Shorts Result
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
bool filterByMagic = true, // Get Only Self Open Positions
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
// Retrieve Long Positions ...
|
|
GetPositions(
|
|
longs,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
X_POSITION_TYPE_LONG,
|
|
filterByMagic,
|
|
forceClean //
|
|
);
|
|
|
|
//
|
|
// Retrieve Short Positions ...
|
|
GetPositions(
|
|
shorts,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
X_POSITION_TYPE_SHORT,
|
|
filterByMagic,
|
|
forceClean //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Select In Profit Positions ...
|
|
int GetInProfitPositions(
|
|
XPosition &result[], // Hold's Result
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
|
|
ENUM_X_POSITION_SELECT_METHODS method = NULL, // Selection Method
|
|
bool filterByMagic = true, // Get Only Self Open Positions
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
int before = ArraySize(result);
|
|
|
|
//
|
|
if (forceClean)
|
|
{
|
|
Clean(result);
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = GetPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
filterByMagic,
|
|
forceClean);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
XPosition min;
|
|
XPosition max;
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
if (iPosition.profit <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Calculate Min and Max ...
|
|
|
|
//
|
|
// Min ...
|
|
if (!min.IsValid() || min.profit <= 0 || min.profit > iPosition.profit)
|
|
{
|
|
min = iPosition;
|
|
}
|
|
|
|
//
|
|
// Max ...
|
|
if (!max.IsValid() || max.profit <= 0 || max.profit < iPosition.profit)
|
|
{
|
|
max = iPosition;
|
|
}
|
|
|
|
//
|
|
bool isSelectionMethodFilterPassed =
|
|
//
|
|
(method == NULL ||
|
|
method == X_POSITION_SELECT_ALL)
|
|
? true
|
|
: false;
|
|
//
|
|
;
|
|
if (!isSelectionMethodFilterPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
if (min.IsValid() &&
|
|
(method == X_POSITION_SELECT_MIN ||
|
|
method == X_POSITION_SELECT_BOTH))
|
|
{
|
|
//
|
|
AddRef(
|
|
min,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
if (max.IsValid() &&
|
|
(method == X_POSITION_SELECT_MAX ||
|
|
method == X_POSITION_SELECT_BOTH))
|
|
{
|
|
//
|
|
AddRef(
|
|
max,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
int after = ArraySize(result);
|
|
|
|
//
|
|
mResult = after - before;
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Select In Drawdown Positions ...
|
|
int GetInDrawdownPositions(
|
|
XPosition &result[], // Hold's Result
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
|
|
ENUM_X_POSITION_SELECT_METHODS method = NULL, // Selection Method
|
|
bool filterByMagic = true, // Get Only Self Open Positions
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
int before = ArraySize(result);
|
|
|
|
//
|
|
if (forceClean)
|
|
{
|
|
Clean(result);
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = GetPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
filterByMagic,
|
|
forceClean);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
XPosition min;
|
|
XPosition max;
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
if (iPosition.profit >= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Calculate Min and Max ...
|
|
|
|
//
|
|
// Min ...
|
|
if (!min.IsValid() || min.profit <= 0 || min.profit > iPosition.profit)
|
|
{
|
|
min = iPosition;
|
|
}
|
|
|
|
//
|
|
// Max ...
|
|
if (!max.IsValid() || max.profit <= 0 || max.profit < iPosition.profit)
|
|
{
|
|
max = iPosition;
|
|
}
|
|
|
|
//
|
|
bool isSelectionMethodFilterPassed =
|
|
//
|
|
(method == NULL ||
|
|
method == X_POSITION_SELECT_ALL)
|
|
? true
|
|
: false;
|
|
//
|
|
;
|
|
if (!isSelectionMethodFilterPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
if (min.IsValid() &&
|
|
(method == X_POSITION_SELECT_MIN ||
|
|
method == X_POSITION_SELECT_BOTH))
|
|
{
|
|
//
|
|
AddRef(
|
|
min,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
if (max.IsValid() &&
|
|
(method == X_POSITION_SELECT_MAX ||
|
|
method == X_POSITION_SELECT_BOTH))
|
|
{
|
|
//
|
|
AddRef(
|
|
max,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
int after = ArraySize(result);
|
|
|
|
//
|
|
mResult = after - before;
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Orders ...
|
|
|
|
//
|
|
// Retrieve Specific Order by Index ...
|
|
bool GetOrder(
|
|
int index, // Specified Index
|
|
XOrder &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
return result
|
|
.ByIndex(index);
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Positions Orders ...
|
|
int GetOrders(
|
|
ulong ticket, // Positions Ticket
|
|
XOrder &result[], // Hold's Result
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
// Select History by Specified Ticket ...
|
|
bool isHistorySelected = HistorySelectByPosition(ticket);
|
|
if (!isHistorySelected)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
int ordersCount = HistoryOrdersTotal();
|
|
if (ordersCount <= 0)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
int before = ArraySize(result);
|
|
|
|
//
|
|
if (forceClean)
|
|
{
|
|
Clean(result);
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < ordersCount; i++)
|
|
{
|
|
//
|
|
XOrder iOrder;
|
|
bool isLoaded = iOrder.HistoryByIndex(i);
|
|
if (!isLoaded)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isTicketFilterPassed =
|
|
//
|
|
iOrder.ticket = ticket
|
|
//
|
|
;
|
|
if (!isTicketFilterPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
iOrder,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
int after = ArraySize(result);
|
|
|
|
//
|
|
mResult = after - before;
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Retrieve All Orders ...
|
|
int GetOrders(
|
|
XOrder &result[], // Hold's Result
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
|
|
ENUM_ORDER_STATE state = NULL, // Order State
|
|
bool filterByMagic = true, // Get Only Self Open Positions
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
int before = ArraySize(result);
|
|
|
|
//
|
|
if (forceClean)
|
|
{
|
|
Clean(result);
|
|
}
|
|
|
|
//
|
|
// Count Positions ...
|
|
int itemsCount = OrdersTotal();
|
|
if (itemsCount <= 0)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Loop Through all Positions ...
|
|
for (int i = itemsCount - 1; i >= 0; i--)
|
|
{
|
|
//
|
|
XOrder iItem;
|
|
bool isLoaded = iItem.ByIndex(i);
|
|
if (!isLoaded)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now we have to Check Given Filters ...
|
|
bool isFiltersPassed =
|
|
iItem.IsFiltersPassed(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
state,
|
|
mMagicNumber,
|
|
filterByMagic
|
|
//
|
|
);
|
|
if (!isFiltersPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add Filtered Item into Result ...
|
|
AddRef(
|
|
iItem,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
int after = ArraySize(result);
|
|
|
|
//
|
|
mResult = after - before;
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Retrieve Historical Orders ...
|
|
int GetOrders(
|
|
XOrder &result[], // Hold's Result
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
|
|
ENUM_ORDER_STATE state = NULL, // Order State
|
|
datetime startDate = NULL, // Specify Start Date
|
|
datetime endDate = NULL, // Specify End Date
|
|
bool filterByMagic = true, // Get Only Self Open Positions
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
endDate = NormalizeTime(endDate);
|
|
|
|
//
|
|
if (!IsValid(startDate))
|
|
{
|
|
startDate = endDate - PeriodSeconds(PERIOD_D1);
|
|
}
|
|
|
|
//
|
|
bool isHistorySelected = HistorySelect(
|
|
startDate,
|
|
endDate);
|
|
if (!isHistorySelected)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
int before = ArraySize(result);
|
|
|
|
//
|
|
if (forceClean)
|
|
{
|
|
Clean(result);
|
|
}
|
|
|
|
//
|
|
// Count Positions ...
|
|
int itemsCount = OrdersTotal();
|
|
if (itemsCount <= 0)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Loop Through all Positions ...
|
|
for (int i = itemsCount - 1; i >= 0; i--)
|
|
{
|
|
//
|
|
XOrder iItem;
|
|
bool isLoaded = iItem.HistoryByIndex(i);
|
|
if (!isLoaded)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now we have to Check Given Filters ...
|
|
bool isFiltersPassed =
|
|
iItem.IsFiltersPassed(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
state,
|
|
mMagicNumber,
|
|
filterByMagic
|
|
//
|
|
);
|
|
if (!isFiltersPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add Filtered Item into Result ...
|
|
AddRef(
|
|
iItem,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
int after = ArraySize(result);
|
|
|
|
//
|
|
mResult = after - before;
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Deals ...
|
|
|
|
//
|
|
// Retrieve Specific Deal by Index ...
|
|
bool GetDeal(
|
|
int index, // Specified Index
|
|
XDeal &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
return result
|
|
.ByIndex(index);
|
|
}
|
|
|
|
//
|
|
bool GetLastDeal(
|
|
XDeal &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
bool mResult = false;
|
|
|
|
//
|
|
mResult = HistorySelect(0, TimeCurrent());
|
|
if (!mResult)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
int lastDealIndex = HistoryDealsTotal() - 1;
|
|
|
|
//
|
|
mResult = GetDeal(
|
|
lastDealIndex,
|
|
result //
|
|
);
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specified Deal's History ...
|
|
int GetDealsHistory(
|
|
XDeal &deal, // Specified Deal
|
|
XOrder &history[], // Deals History
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
if (!deal.IsValid())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = GetOrders(
|
|
deal.positionId,
|
|
history,
|
|
forceClean);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve All Deals ...
|
|
int GetDeals(
|
|
XDeal &result[], // Hold's Result
|
|
string symbol = NULL, // Trading Symbol
|
|
string provider = NULL, // Signal Provider
|
|
ENUM_TIMEFRAMES period = NULL, // Trading Period
|
|
ENUM_DEAL_TYPE type = NULL, // Deal Type
|
|
ENUM_DEAL_ENTRY entry = NULL, // Deal Entry
|
|
ENUM_DEAL_REASON reason = NULL, // Deal Reason
|
|
datetime startDate = NULL, // Specify Start Date
|
|
datetime endDate = NULL, // Specify End Date
|
|
bool filterByMagic = true, // Get Only Self Open Positions
|
|
bool forceClean = true // Clean Result Array
|
|
)
|
|
{
|
|
//
|
|
int mResult = 0;
|
|
|
|
//
|
|
endDate = NormalizeTime(endDate);
|
|
|
|
//
|
|
if (!IsValid(startDate))
|
|
{
|
|
startDate = endDate - PeriodSeconds(PERIOD_D1);
|
|
}
|
|
|
|
//
|
|
bool isHistorySelected = HistorySelect(
|
|
startDate,
|
|
endDate);
|
|
if (!isHistorySelected)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
int before = ArraySize(result);
|
|
|
|
//
|
|
if (forceClean)
|
|
{
|
|
Clean(result);
|
|
}
|
|
|
|
//
|
|
// Count Positions ...
|
|
int itemsCount = HistoryDealsTotal();
|
|
if (itemsCount <= 0)
|
|
{
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Loop Through all Positions ...
|
|
for (int i = itemsCount - 1; i >= 0; i--)
|
|
{
|
|
//
|
|
XDeal iItem;
|
|
bool isLoaded = iItem.ByIndex(i);
|
|
if (!isLoaded)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now we have to Check Given Filters ...
|
|
bool isFiltersPassed =
|
|
iItem.IsFiltersPassed(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type,
|
|
entry,
|
|
reason,
|
|
mMagicNumber,
|
|
filterByMagic
|
|
//
|
|
);
|
|
if (!isFiltersPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add Filtered Item into Result ...
|
|
AddRef(
|
|
iItem,
|
|
result
|
|
//
|
|
);
|
|
}
|
|
|
|
//
|
|
int after = ArraySize(result);
|
|
|
|
//
|
|
mResult = after - before;
|
|
|
|
//
|
|
return mResult;
|
|
}
|
|
|
|
//
|
|
// Support Functions ...
|
|
|
|
//
|
|
// Check a Position has Support or not ...
|
|
bool HasSupport(
|
|
ulong ticket,
|
|
bool forceOpen = true //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = ticket > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
if (forceOpen)
|
|
{
|
|
//
|
|
result = IsOpen(ticket);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = GetPositions(positions);
|
|
result = IsValidSize(positionsCount);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extract Support Positions ...
|
|
XPosition supports[];
|
|
int supportsCount = ExtractSupports(
|
|
positions,
|
|
supports //
|
|
);
|
|
result = IsValidSize(supportsCount);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Loop Through Supports ...
|
|
for (int i = 0; i < supportsCount; i++)
|
|
{
|
|
//
|
|
XPosition iSupport = supports[i];
|
|
|
|
//
|
|
ulong supportedTicket = ExtractSupportedTicket(iSupport.comment);
|
|
ulong eqmSupportedTicket = ExtractEQMSupportedTicket(iSupport.comment);
|
|
|
|
//
|
|
result =
|
|
//
|
|
(supportedTicket > 0 && ticket == supportedTicket)
|
|
//
|
|
||
|
|
//
|
|
(eqmSupportedTicket > 0 && ticket == eqmSupportedTicket)
|
|
//
|
|
;
|
|
if (result)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Position's Supports ...
|
|
int GetSupports(
|
|
ulong ticket,
|
|
XPosition &dest[], // Result ...
|
|
bool forceOpen = true //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(dest);
|
|
|
|
//
|
|
bool hasSupport = HasSupport(
|
|
ticket,
|
|
forceOpen //
|
|
);
|
|
if (!hasSupport)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = GetPositions(positions);
|
|
if (!IsValidSize(positionsCount))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extract Support Positions ...
|
|
XPosition supports[];
|
|
int supportsCount = ExtractSupports(
|
|
positions,
|
|
supports //
|
|
);
|
|
if (!IsValidSize(supportsCount))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Loop Through Supports ...
|
|
for (int i = 0; i < supportsCount; i++)
|
|
{
|
|
//
|
|
XPosition iSupport = supports[i];
|
|
|
|
//
|
|
ulong supportedTicket = ExtractSupportedTicket(iSupport.comment);
|
|
ulong eqmSupportedTicket = ExtractEQMSupportedTicket(iSupport.comment);
|
|
|
|
//
|
|
bool isOwn =
|
|
//
|
|
(supportedTicket > 0 && ticket == supportedTicket)
|
|
//
|
|
||
|
|
//
|
|
(eqmSupportedTicket > 0 && ticket == eqmSupportedTicket)
|
|
//
|
|
;
|
|
if (isOwn)
|
|
{
|
|
//
|
|
AddRef(
|
|
iSupport,
|
|
dest //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
result = ArraySize(dest);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Specific Position's Supports ...
|
|
int GetSupports(
|
|
ulong ticket,
|
|
XOrder &dest[], // Result ...
|
|
bool forceOpen = true //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(dest);
|
|
|
|
//
|
|
bool hasSupport = HasSupport(
|
|
ticket,
|
|
forceOpen //
|
|
);
|
|
if (!hasSupport)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XOrder orders[];
|
|
int ordersCount = GetOrders(
|
|
orders,
|
|
NULL, // All Symbols ...
|
|
NULL, // All Providers ...
|
|
NULL, // All Periods ...
|
|
X_POSITION_TYPE_ALL, // All Types ...
|
|
ORDER_STATE_PLACED, // Untriggere Orders ...
|
|
true //
|
|
);
|
|
if (!IsValidSize(ordersCount))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extract Support Positions ...
|
|
XOrder supports[];
|
|
int supportsCount = ExtractSupports(
|
|
orders,
|
|
supports //
|
|
);
|
|
if (!IsValidSize(supportsCount))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Loop Through Supports ...
|
|
for (int i = 0; i < supportsCount; i++)
|
|
{
|
|
//
|
|
XOrder iSupport = supports[i];
|
|
|
|
//
|
|
ulong supportedTicket = ExtractSupportedTicket(iSupport.comment);
|
|
ulong eqmSupportedTicket = ExtractEQMSupportedTicket(iSupport.comment);
|
|
|
|
//
|
|
bool isOwn =
|
|
//
|
|
(supportedTicket > 0 && ticket == supportedTicket)
|
|
//
|
|
||
|
|
//
|
|
(eqmSupportedTicket > 0 && ticket == eqmSupportedTicket)
|
|
//
|
|
;
|
|
if (isOwn)
|
|
{
|
|
//
|
|
AddRef(
|
|
iSupport,
|
|
dest //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
result = ArraySize(dest);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Retrieve Last Open Position Ticket ...
|
|
ulong GetLastOpenPositionTicket()
|
|
{
|
|
//
|
|
ulong result = 0;
|
|
|
|
int positionsCount = PositionsTotal();
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = PositionGetTicket(PositionsTotal() - 1);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Last Placed Order Ticket ...
|
|
ulong GetLastPlacedOrderTicket()
|
|
{
|
|
//
|
|
ulong result = 0;
|
|
|
|
int ordersCount = OrdersTotal();
|
|
if (ordersCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
if (mOrderInfo.SelectByIndex(ordersCount - 1))
|
|
{
|
|
result = mOrderInfo.Ticket();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Prepare Dynamic Volume ...
|
|
double GetDynamicVolume(
|
|
string _symbol,
|
|
double _balanceFactor,
|
|
double _volumeStep //
|
|
)
|
|
{
|
|
//
|
|
double result = 0;
|
|
|
|
//
|
|
// Validate Inputs ...
|
|
if (!IsValid(_symbol) ||
|
|
_volumeStep <= 0 ||
|
|
_balanceFactor <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Account Balance ...
|
|
double balance = mAccount.GetBalance();
|
|
|
|
//
|
|
double bStep = balance / _balanceFactor;
|
|
bStep = MathFloor(bStep);
|
|
if (bStep == 0)
|
|
{
|
|
bStep = 1;
|
|
}
|
|
|
|
//
|
|
result = bStep * _volumeStep;
|
|
|
|
//
|
|
result = NormalizeVolume(
|
|
result,
|
|
_symbol //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Event Listener Notifiers ...
|
|
|
|
//
|
|
// State ...
|
|
void NotifyTradeStateChangeEvent(const XOnTradeHandlerState &state)
|
|
{
|
|
//
|
|
int count = ArraySize(mTradeStateChangedEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnTradeStateChanged iHandler = mTradeStateChangedEventHandlers[i];
|
|
iHandler(state);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Deals ...
|
|
void NotifyDealsChangeEvent(int changes)
|
|
{
|
|
//
|
|
int count = ArraySize(mDealsChangedEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnDealsChanged iHandler = mDealsChangedEventHandlers[i];
|
|
iHandler(changes);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Orders ...
|
|
void NotifyOrdersChangeEvent(int changes)
|
|
{
|
|
//
|
|
int count = ArraySize(mOrdersChangedEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnOrdersChanged iHandler = mOrdersChangedEventHandlers[i];
|
|
iHandler(changes);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Positions ...
|
|
void NotifyPositionsChangeEvent(int changes)
|
|
{
|
|
//
|
|
int count = ArraySize(mPositionsChangedEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnPositionsChanged iHandler = mPositionsChangedEventHandlers[i];
|
|
iHandler(changes);
|
|
}
|
|
}
|
|
|
|
//
|
|
// StopLoss ...
|
|
void NotifyStopLossEvent(const XDeal &deal)
|
|
{
|
|
//
|
|
int count = ArraySize(mStopLossEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnStopLoss iHandler = mStopLossEventHandlers[i];
|
|
iHandler(deal);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Take Profit ...
|
|
void NotifyTakeProfitEvent(const XDeal &deal)
|
|
{
|
|
//
|
|
int count = ArraySize(mTakeProfitEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnTakeProfit iHandler = mTakeProfitEventHandlers[i];
|
|
iHandler(deal);
|
|
}
|
|
}
|
|
|
|
//
|
|
void NotifyModifyEvent(
|
|
ulong ticket,
|
|
double profit,
|
|
string comment //
|
|
)
|
|
{
|
|
//
|
|
int count = ArraySize(mOnModifyEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnModify iHandler = mOnModifyEventHandlers[i];
|
|
iHandler(
|
|
ticket,
|
|
profit,
|
|
comment //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
void NotifyForceCloseEvent(
|
|
ulong ticket,
|
|
XPosition &position,
|
|
string comment //
|
|
)
|
|
{
|
|
//
|
|
int count = ArraySize(mOnForceCloseEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnForceClose iHandler = mOnForceCloseEventHandlers[i];
|
|
iHandler(
|
|
ticket,
|
|
position,
|
|
comment //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
void NotifyPartialCloseEvent(
|
|
ulong ticket,
|
|
double profit,
|
|
string comment //
|
|
)
|
|
{
|
|
//
|
|
int count = ArraySize(mOnPartialCloseEventHandlers);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
TOnModify iHandler = mOnPartialCloseEventHandlers[i];
|
|
iHandler(
|
|
ticket,
|
|
profit,
|
|
comment //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Retrieve Specified Position Comment ...
|
|
string GetPositionComment(ulong ticket)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
if (!IsOpen(ticket))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
XPosition position;
|
|
bool isLoaded = position.ByTicket(ticket);
|
|
if (!isLoaded)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = position.comment;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ENUM_TIMEFRAMES GetDealPeriod(XDeal &deal)
|
|
{
|
|
//
|
|
ENUM_TIMEFRAMES result = NULL;
|
|
|
|
//
|
|
XOrder orders[];
|
|
int ordersCount = GetDealsHistory(
|
|
deal,
|
|
orders);
|
|
if (!ordersCount)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < ordersCount; i++)
|
|
{
|
|
//
|
|
XOrder iOrder = orders[i];
|
|
|
|
//
|
|
if (IsValid(iOrder.comment))
|
|
{
|
|
//
|
|
ENUM_TIMEFRAMES iPeriod = ExtractPeriod(iOrder.comment);
|
|
if (IsValid(iPeriod))
|
|
{
|
|
result = iPeriod;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double GetMaxAllowedEquity()
|
|
{
|
|
//
|
|
double equity = mAccount.GetEquity();
|
|
double balance = mAccount.GetBalance();
|
|
|
|
//
|
|
double balanceForFactor = MathAbs(equity - balance);
|
|
if (balanceForFactor <= 0)
|
|
{
|
|
balanceForFactor = balance;
|
|
}
|
|
else
|
|
{
|
|
balanceForFactor = equity;
|
|
}
|
|
|
|
//
|
|
double result = balanceForFactor * mMaxAllowedDrawdownFactor;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
int mSlippage; // Slippage ...
|
|
ulong mMagicNumber; // Magic Number (Unique Identifier) ...
|
|
|
|
//
|
|
double mMaxAllowedSpread; // Max Allowed Spred for Opening Trades
|
|
int mMaxAllowedPositions; // Max Allowed Same Positions
|
|
double mMaxAllowedDrawdownFactor; // Max Allowed Drawdown Factor
|
|
|
|
//
|
|
XSCTradeBase *mTrader; // Base CTrade Manipulated Instance ...
|
|
|
|
//
|
|
CDealInfo mDealInfo; // Deals Info ...
|
|
COrderInfo mOrderInfo; // Order Info ...
|
|
CPositionInfo mPositionInfo; // Positions Info
|
|
CHistoryOrderInfo mHistoryOrderInfo; // History Orders Info ...
|
|
|
|
//
|
|
// Position Info Collector ...
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// OnTrade Event Handlers Section ...
|
|
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
int mDaysForRead; // Reading Dates for Trade History Checker ...
|
|
datetime mEndDate; // End Date for Trade history Checking ...
|
|
datetime mStartDate; // Start Date for Trade history Checking ... ...
|
|
int mOrdersCount; // Number of Active orders ...
|
|
int mPositionsCount; // Number of Open positions ...
|
|
int mDealsCount; // Number of Deals in the Trade History Checking ...
|
|
int mHistoryOrdersCount; // Number of Orders in the Trade History Checking ...
|
|
bool mScanStarted; // a Flag of counter relevance ...
|
|
|
|
//
|
|
// Event Listeners ...
|
|
|
|
//
|
|
// Deals Changed Event Handlers ...
|
|
TOnDealsChanged mDealsChangedEventHandlers[];
|
|
|
|
//
|
|
// Orders Changed Event Handlers ...
|
|
TOnOrdersChanged mOrdersChangedEventHandlers[];
|
|
|
|
//
|
|
// Positions Changed Event Handlers ...
|
|
TOnPositionsChanged mPositionsChangedEventHandlers[];
|
|
|
|
//
|
|
// Trade State Changed Event Handlers ...
|
|
TOnTradeStateChanged mTradeStateChangedEventHandlers[];
|
|
|
|
//
|
|
TOnModify mOnModifyEventHandlers[];
|
|
TOnForceClose mOnForceCloseEventHandlers[];
|
|
TOnPartialClose mOnPartialCloseEventHandlers[];
|
|
|
|
//
|
|
// TPSL ...
|
|
TOnStopLoss mStopLossEventHandlers[];
|
|
TOnTakeProfit mTakeProfitEventHandlers[];
|
|
|
|
//
|
|
// Reset On Trade Scanner Context ...
|
|
void ResetOnTradeContext()
|
|
{
|
|
//
|
|
mEndDate = TimeCurrent();
|
|
mStartDate = mEndDate - (mDaysForRead * PeriodSeconds(PERIOD_D1));
|
|
}
|
|
|
|
//
|
|
// Initial Context Of Trade Scanner ...
|
|
void InitOnTradeContext()
|
|
{
|
|
//
|
|
ResetLastError();
|
|
|
|
//
|
|
mOrdersCount = OrdersTotal();
|
|
mPositionsCount = PositionsTotal();
|
|
|
|
//
|
|
// load history ...
|
|
bool selected = HistorySelect(mStartDate, mEndDate);
|
|
if (!selected)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// get the current value ...
|
|
mDealsCount = HistoryDealsTotal();
|
|
mHistoryOrdersCount = HistoryOrdersTotal();
|
|
|
|
//
|
|
mScanStarted = true;
|
|
}
|
|
|
|
//
|
|
// Check Statrt Date In Trade History ...
|
|
void CheckStartDateInTradeHistory()
|
|
{
|
|
//
|
|
// initial interval, if we were to start working right now ..
|
|
datetime currStart = TimeCurrent() - (mDaysForRead * PeriodSeconds(PERIOD_D1));
|
|
|
|
//
|
|
// make sure that the start limit of the trade history has not gone
|
|
// more than 1 day over the intended date ...
|
|
if (currStart - mStartDate > PeriodSeconds(PERIOD_D1))
|
|
{
|
|
//
|
|
// correct the start date of history to be loaded in the cache ...
|
|
mStartDate = currStart;
|
|
|
|
//
|
|
// now reload the trade history for the updated interval ...
|
|
HistorySelect(mStartDate, mEndDate);
|
|
|
|
//
|
|
// correct the deal and order counters in history for further comparison
|
|
mHistoryOrdersCount = HistoryOrdersTotal();
|
|
mDealsCount = HistoryDealsTotal();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Process On Trade Context ...
|
|
void ProcessOnTradeContext(
|
|
XOnTradeHandlerState &result // Hold's Result
|
|
)
|
|
{
|
|
//
|
|
mEndDate = TimeCurrent();
|
|
|
|
//
|
|
ResetLastError();
|
|
|
|
//
|
|
// download trading history from the specified interval to the program cache ...
|
|
bool selected = HistorySelect(mStartDate, mEndDate);
|
|
if (!selected)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// get the current values ...
|
|
int currOrders = OrdersTotal();
|
|
int currPositions = PositionsTotal();
|
|
int currDeals = HistoryDealsTotal();
|
|
int currHistoryOrders = HistoryOrdersTotal();
|
|
|
|
//
|
|
// Orders ...
|
|
// check if the number of active orders has been changed ...
|
|
if (currOrders != mOrdersCount)
|
|
{
|
|
//
|
|
// number of active orders has been changed ...
|
|
result.hasNewOrder = true;
|
|
result.newOrders = currOrders - mOrdersCount;
|
|
|
|
//
|
|
// update the value ...
|
|
mOrdersCount = currOrders;
|
|
}
|
|
|
|
//
|
|
// Positions ...
|
|
// changes in the number of open positions ...
|
|
if (currPositions != mPositionsCount)
|
|
{
|
|
//
|
|
// number of open positions has been changed ...
|
|
result.hasNewPosition = true;
|
|
result.newPositions = currPositions - mPositionsCount;
|
|
|
|
//
|
|
// update the value ...
|
|
mPositionsCount = currPositions;
|
|
}
|
|
|
|
//
|
|
// Deals ...
|
|
// changes in the number of deals in the trade history cache ...
|
|
if (currDeals != mDealsCount)
|
|
{
|
|
//
|
|
// number of deals in the trade history cache has been changed ...
|
|
result.hasNewDeal = true;
|
|
result.newDeals = currDeals - mDealsCount;
|
|
|
|
//
|
|
// update the value ...
|
|
mDealsCount = currDeals;
|
|
}
|
|
|
|
//
|
|
// History Orders ...
|
|
// changes in the number of history orders in the trade history cache ...
|
|
if (currHistoryOrders != mHistoryOrdersCount)
|
|
{
|
|
//
|
|
// number of history orders in the trade history cache has been changed ...
|
|
result.hasNewHistoryOrder = true;
|
|
result.newHistoryOrders = currHistoryOrders - mHistoryOrdersCount;
|
|
|
|
//
|
|
// update the value ...
|
|
mHistoryOrdersCount = currHistoryOrders;
|
|
}
|
|
|
|
//
|
|
// checking if it is necessary to change the limits of the trade history to be requested in cache ...
|
|
CheckStartDateInTradeHistory();
|
|
}
|
|
};
|
|
|
|
//
|
|
// a Class For Read and Write Trade Info Data in Files ...
|
|
class XTradeCollector
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Constructor(s) ...
|
|
void XTradeCollector(
|
|
string _path = NULL // Base Path
|
|
)
|
|
{
|
|
//
|
|
mAccount = new XSCAccount();
|
|
|
|
//
|
|
if (IsValid(_path))
|
|
{
|
|
mPath = _path;
|
|
}
|
|
else
|
|
{
|
|
mPath = "XTradeData" + "\\" + mAccount.GetCompany();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
void ~XTradeCollector() {}
|
|
|
|
//
|
|
bool IsExists(XTradeData &item)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
int mHandler = GetFileHandlerForRead(item);
|
|
result = mHandler != INVALID_HANDLE;
|
|
FileClose(mHandler);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool Save(XTradeData &item)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check info is Valid ...
|
|
result = item.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string content = item.ToString();
|
|
content += "\n" + "-----------" + "\n" + "Signal: " + "\n" + "-----------" + "\n" + item.ToString(true);
|
|
|
|
//
|
|
result = IsValid(content);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int mHandler = GetFileHandlerForWrite(item);
|
|
result = mHandler != INVALID_HANDLE;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
FileWrite(mHandler, content);
|
|
FileFlush(mHandler);
|
|
FileClose(mHandler);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool SaveSignal(XTradeData &item)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
string content = item.ToString(true);
|
|
result = IsValid(content);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int mHandler = GetSignalFileHandlerForWrite(item);
|
|
result = mHandler != INVALID_HANDLE;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
FileWrite(mHandler, content);
|
|
FileFlush(mHandler);
|
|
FileClose(mHandler);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Conditions only save for Loss Signals ...
|
|
// this means the profit must be Lower than Zero ...
|
|
// ans also message Contains SL ...
|
|
bool SaveConditions(XTradeData &item)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Item ...
|
|
result =
|
|
//
|
|
item.profit < 0 &&
|
|
Contains("SL", item.message)
|
|
//
|
|
;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string content = item.signal.conditions;
|
|
result = IsValid(content);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int mHandler = GetConditionsFileHandlerForWrite(item);
|
|
result = mHandler != INVALID_HANDLE;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
FileSeek(mHandler, 0, SEEK_END);
|
|
FileWrite(mHandler, content);
|
|
FileFlush(mHandler);
|
|
FileClose(mHandler);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
string mPath; // Base Path ...
|
|
|
|
//
|
|
XSCAccount *mAccount;
|
|
|
|
//
|
|
string GetFilePath(XTradeData &item)
|
|
{
|
|
//
|
|
string fileName = item.GetFileName();
|
|
|
|
//
|
|
return GetFilePath(fileName);
|
|
}
|
|
string GetFilePath(string fileName)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
result =
|
|
//
|
|
mPath + "\\" + "Trades" + "\\" + fileName + ".x121.log"
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string GetSignalFilePath(XTradeData &item)
|
|
{
|
|
//
|
|
string fileName = item.GetSignalFileName();
|
|
|
|
//
|
|
return GetSignalFilePath(fileName);
|
|
}
|
|
string GetSignalFilePath(string fileName)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
result =
|
|
//
|
|
mPath + "\\" + "Signals" + "\\" + fileName + ".x121.log"
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string GetConditionsFilePath(XTradeData &item)
|
|
{
|
|
//
|
|
bool isLong = IsLong(item.type);
|
|
|
|
//
|
|
string fileName =
|
|
item.symbol + "\\" +
|
|
(isLong
|
|
? "Longs"
|
|
: "Shorts");
|
|
|
|
//
|
|
return GetConditionsFilePath(fileName);
|
|
}
|
|
string GetConditionsFilePath(string fileName)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
result =
|
|
//
|
|
mPath + "\\" + "Conditions" + "\\" + fileName + ".x121.log"
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int GetFileHandlerForRead(XTradeData &item)
|
|
{
|
|
//
|
|
int result = INVALID_HANDLE;
|
|
|
|
//
|
|
string filePath = GetFilePath(item);
|
|
if (!IsValid(filePath))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = FileOpen(
|
|
filePath,
|
|
FILE_READ | FILE_TXT //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
int GetFileHandlerForWrite(XTradeData &item)
|
|
{
|
|
//
|
|
int result = INVALID_HANDLE;
|
|
|
|
//
|
|
string filePath = GetFilePath(item);
|
|
if (!IsValid(filePath))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = FileOpen(
|
|
filePath,
|
|
FILE_READ | FILE_WRITE | FILE_TXT //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int GetSignalFileHandlerForRead(XTradeData &item)
|
|
{
|
|
//
|
|
int result = INVALID_HANDLE;
|
|
|
|
//
|
|
string filePath = GetSignalFilePath(item);
|
|
if (!IsValid(filePath))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = FileOpen(
|
|
filePath,
|
|
FILE_READ | FILE_TXT //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
int GetSignalFileHandlerForWrite(XTradeData &item)
|
|
{
|
|
//
|
|
int result = INVALID_HANDLE;
|
|
|
|
//
|
|
string filePath = GetSignalFilePath(item);
|
|
if (!IsValid(filePath))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = FileOpen(
|
|
filePath,
|
|
FILE_READ | FILE_WRITE | FILE_TXT //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int GetConditionsFileHandlerForRead(XTradeData &item)
|
|
{
|
|
//
|
|
int result = INVALID_HANDLE;
|
|
|
|
//
|
|
string filePath = GetConditionsFilePath(item);
|
|
if (!IsValid(filePath))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = FileOpen(
|
|
filePath,
|
|
FILE_READ | FILE_TXT //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
int GetConditionsFileHandlerForWrite(XTradeData &item)
|
|
{
|
|
//
|
|
int result = INVALID_HANDLE;
|
|
|
|
//
|
|
string filePath = GetConditionsFilePath(item);
|
|
if (!IsValid(filePath))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = FileOpen(
|
|
filePath,
|
|
FILE_READ | FILE_WRITE | FILE_TXT //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
};
|
|
|
|
//
|
|
// Trade Handler Class ...
|
|
class XSCXTradeHandler : public XSCBaseAlert
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
|
|
//
|
|
// Constructor(s) ...
|
|
void XSCXTradeHandler(XSCTrade *trader)
|
|
{
|
|
//
|
|
mTrader = trader;
|
|
mCollector = new XTradeCollector();
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
void ~XSCXTradeHandler()
|
|
{
|
|
//
|
|
delete mTrader;
|
|
delete mCollector;
|
|
}
|
|
|
|
//
|
|
// Getter(s) / Setter(s) ...
|
|
|
|
//
|
|
bool SaveSignals()
|
|
{
|
|
return mSaveSignals;
|
|
}
|
|
|
|
//
|
|
void SaveSignals(bool value)
|
|
{
|
|
mSaveSignals = value;
|
|
}
|
|
|
|
//
|
|
bool SaveTrades()
|
|
{
|
|
return mSaveTrades;
|
|
}
|
|
|
|
//
|
|
void SaveTrades(bool value)
|
|
{
|
|
mSaveTrades = value;
|
|
}
|
|
|
|
//
|
|
bool SaveConditions()
|
|
{
|
|
return mSaveConditions;
|
|
}
|
|
|
|
//
|
|
void SaveConditions(bool value)
|
|
{
|
|
mSaveConditions = value;
|
|
}
|
|
|
|
//
|
|
int MaxSameTimeTrades()
|
|
{
|
|
return maxSameTimeTrades;
|
|
}
|
|
|
|
//
|
|
bool AllowLong()
|
|
{
|
|
return mAllowLong;
|
|
}
|
|
|
|
//
|
|
void AllowLong(bool value)
|
|
{
|
|
mAllowLong = value;
|
|
}
|
|
|
|
//
|
|
bool AllowShort()
|
|
{
|
|
return mAllowShort;
|
|
}
|
|
|
|
//
|
|
void AllowShort(bool value)
|
|
{
|
|
mAllowShort = value;
|
|
}
|
|
|
|
//
|
|
int MaxAllowedLongs()
|
|
{
|
|
return mMaxAllowedLongs;
|
|
}
|
|
|
|
//
|
|
void MaxAllowedLongs(int value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mMaxAllowedLongs = value;
|
|
}
|
|
|
|
//
|
|
int MaxAllowedShorts()
|
|
{
|
|
return mMaxAllowedShorts;
|
|
}
|
|
|
|
//
|
|
void MaxAllowedShorts(int value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mMaxAllowedShorts = value;
|
|
}
|
|
|
|
//
|
|
bool UseMaxAllowedTradesPerSymbol()
|
|
{
|
|
return mUseMaxAllowedTradesPerSymbol;
|
|
}
|
|
|
|
//
|
|
void UseMaxAllowedTradesPerSymbol(bool value)
|
|
{
|
|
mUseMaxAllowedTradesPerSymbol = value;
|
|
}
|
|
|
|
//
|
|
double MaxDrawdownPercentForOpenTrades()
|
|
{
|
|
return mMaxDrawdownPercentForOpenTrades;
|
|
}
|
|
|
|
//
|
|
void MaxDrawdownPercentForOpenTrades(double value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mMaxDrawdownPercentForOpenTrades = value;
|
|
}
|
|
|
|
//
|
|
// Trailling ...
|
|
|
|
//
|
|
bool AllowTrailStops()
|
|
{
|
|
return mAllowTrailStops;
|
|
}
|
|
|
|
//
|
|
void AllowTrailStops(bool value)
|
|
{
|
|
mAllowTrailStops = value;
|
|
}
|
|
|
|
//
|
|
bool IgnoreTPOnTrail()
|
|
{
|
|
return mIgnoreTPOnTrail;
|
|
}
|
|
|
|
//
|
|
void IgnoreTPOnTrail(bool value)
|
|
{
|
|
mIgnoreTPOnTrail = value;
|
|
}
|
|
|
|
//
|
|
double TrailStartInPoint()
|
|
{
|
|
return mTrailStartInPoint;
|
|
}
|
|
|
|
//
|
|
void TrailStartInPoint(double value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mTrailStartInPoint = value;
|
|
}
|
|
|
|
//
|
|
double TrailStepInPoint()
|
|
{
|
|
return mTrailStepInPoint;
|
|
}
|
|
|
|
//
|
|
void TrailStepInPoint(double value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mTrailStepInPoint = value;
|
|
}
|
|
|
|
//
|
|
bool TrailBasedOnProfit()
|
|
{
|
|
return mTrailBasedOnProfit;
|
|
}
|
|
|
|
//
|
|
void TrailBasedOnProfit(bool value)
|
|
{
|
|
mTrailBasedOnProfit = value;
|
|
}
|
|
|
|
//
|
|
// Recovery ...
|
|
|
|
//
|
|
bool AllowRecover()
|
|
{
|
|
return mAllowRecover;
|
|
}
|
|
|
|
//
|
|
void AllowRecover(bool value)
|
|
{
|
|
mAllowRecover = value;
|
|
}
|
|
|
|
//
|
|
double RecoveryTPPoint()
|
|
{
|
|
return mRecoveryTPPoint;
|
|
}
|
|
|
|
//
|
|
void RecoveryTPPoint(double value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mRecoveryTPPoint = value;
|
|
}
|
|
|
|
//
|
|
double RecoverySLPoint()
|
|
{
|
|
return mRecoverySLPoint;
|
|
}
|
|
|
|
//
|
|
void RecoverySLPoint(double value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mRecoverySLPoint = value;
|
|
}
|
|
|
|
//
|
|
int MaxAllowedRecover()
|
|
{
|
|
return mMaxAllowedRecover;
|
|
}
|
|
|
|
//
|
|
void MaxAllowedRecover(int value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mMaxAllowedRecover = value;
|
|
}
|
|
|
|
//
|
|
double MaxAllowedRecoveryVolume()
|
|
{
|
|
return mMaxAllowedRecoveryVolume;
|
|
}
|
|
|
|
//
|
|
void MaxAllowedRecoveryVolume(double value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mMaxAllowedRecoveryVolume = value;
|
|
}
|
|
|
|
//
|
|
double RecoveryVolumeMultiplier()
|
|
{
|
|
return mRecoveryVolumeMultiplier;
|
|
}
|
|
|
|
//
|
|
void RecoveryVolumeMultiplier(double value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mRecoveryVolumeMultiplier = value;
|
|
}
|
|
|
|
//
|
|
bool ForcePressuresInRecovery()
|
|
{
|
|
return mForcePressuresInRecovery;
|
|
}
|
|
|
|
//
|
|
void ForcePressuresInRecovery(bool value)
|
|
{
|
|
mForcePressuresInRecovery = value;
|
|
}
|
|
|
|
//
|
|
// Hedging ...
|
|
|
|
//
|
|
bool AllowHedging()
|
|
{
|
|
return mAllowHedging;
|
|
}
|
|
|
|
//
|
|
void AllowHedging(bool value)
|
|
{
|
|
mAllowHedging = value;
|
|
}
|
|
|
|
//
|
|
double HedgeVolumeFactor()
|
|
{
|
|
return mHedgeVolumeFactor;
|
|
}
|
|
|
|
//
|
|
void HedgeVolumeFactor(double value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mHedgeVolumeFactor = value;
|
|
}
|
|
|
|
//
|
|
int MinTradesForHedge()
|
|
{
|
|
return mMinTradesForHedge;
|
|
}
|
|
|
|
//
|
|
void MinTradesForHedge(int value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mMinTradesForHedge = value;
|
|
}
|
|
|
|
//
|
|
double HedgeMinProfitPerVolumeFactor()
|
|
{
|
|
return mHedgeMinProfitPerVolumeFactor;
|
|
}
|
|
|
|
//
|
|
void HedgeMinProfitPerVolumeFactor(double value)
|
|
{
|
|
//
|
|
if (value < 0)
|
|
{
|
|
value = 0;
|
|
}
|
|
|
|
//
|
|
mHedgeMinProfitPerVolumeFactor = value;
|
|
}
|
|
|
|
//
|
|
// Position Protecting ...
|
|
|
|
//
|
|
bool AllowProtectPositions()
|
|
{
|
|
return mAllowProtectPositions;
|
|
}
|
|
|
|
//
|
|
void AllowProtectPositions(bool value)
|
|
{
|
|
mAllowProtectPositions = value;
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Calculate Max Drawdown and it's Percent ...
|
|
double GetMaxDrawdown()
|
|
{
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(positions);
|
|
if (IsValidSize(positionsCount))
|
|
{
|
|
double mEquity = mTrader.mAccount.GetEquity();
|
|
if (mEquity > maxDrawdown)
|
|
{
|
|
maxDrawdown = mEquity;
|
|
}
|
|
|
|
//
|
|
drawdownPercent = (maxDrawdown - mEquity) / (staticBalanceForCalculateDrawdown > 0 ? staticBalanceForCalculateDrawdown : maxDrawdown) * 100;
|
|
drawdownPercent = NormalizeDouble(drawdownPercent, 3);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
maxDrawdown = 0.0;
|
|
drawdownPercent = 0.0;
|
|
}
|
|
|
|
//
|
|
return drawdownPercent;
|
|
}
|
|
|
|
//
|
|
// Trade Handling Functions ...
|
|
|
|
//
|
|
// First Step of Trade Handling ...
|
|
// Since Must Call when a Signal Executed ...
|
|
void AddData(
|
|
XSignal &signal, // Executed Signal
|
|
double commission = 0, // Commission
|
|
bool forceTrail = false, // Force Signal Trailling Stop
|
|
bool forceRecover = false // Force Signal Recovery
|
|
)
|
|
{
|
|
//
|
|
// Check Signal Valid ...
|
|
if (!signal.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check Signal Support or Recovery ...
|
|
bool isSupport = IsSupport(signal.comment);
|
|
bool isRecovery = ExtractRecoveredTicket(signal.comment) > 0;
|
|
|
|
//
|
|
XTradeData item;
|
|
bool isFilled = item.Fill(signal);
|
|
if (!isFilled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
item.commission = commission;
|
|
|
|
//
|
|
// Trailling Configuration ...
|
|
|
|
//
|
|
// Force Trail used to implement Trailing for
|
|
// Custom Positions ...
|
|
if (mAllowTrailStops || forceTrail)
|
|
{
|
|
//
|
|
if (!isSupport && !isRecovery)
|
|
{
|
|
item.allowTrailStop = true;
|
|
}
|
|
|
|
//
|
|
item.ignoreTPOnTrail = mIgnoreTPOnTrail;
|
|
item.trailStepInPoint = mTrailStepInPoint;
|
|
item.trailStartInPoint = mTrailStartInPoint;
|
|
item.trailBasedOnProfit = mTrailBasedOnProfit;
|
|
}
|
|
|
|
//
|
|
// Recovery Configuration ...
|
|
|
|
//
|
|
// Force Recovery used to implement Recovery for
|
|
// Custom Positions ...
|
|
if (mAllowRecover || forceRecover)
|
|
{
|
|
//
|
|
if (!isSupport && !isRecovery)
|
|
{
|
|
item.allowRecover = true;
|
|
}
|
|
|
|
//
|
|
item.recoveryTPPoint = mRecoveryTPPoint;
|
|
item.recoverySLPoint = mRecoverySLPoint;
|
|
item.maxAllowedRecover = mMaxAllowedRecover;
|
|
item.recoveryVolumeMultiplier = mRecoveryVolumeMultiplier;
|
|
|
|
//
|
|
ENUM_POSITION_TYPE _type;
|
|
bool isConverted = ToPositionType(
|
|
_type,
|
|
item.type //
|
|
);
|
|
if (isConverted)
|
|
{
|
|
item.recoveryLookingType = GetOppositPositionType(_type);
|
|
}
|
|
}
|
|
|
|
//
|
|
Add(item);
|
|
|
|
//
|
|
SaveSignal(item);
|
|
}
|
|
|
|
//
|
|
void Finish(const XDeal &deal)
|
|
{
|
|
//
|
|
int idx = -1;
|
|
bool hasItem = HasItem(
|
|
deal.positionId,
|
|
idx //
|
|
);
|
|
if (!hasItem)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Update Item Info ...
|
|
mData[idx].swap = deal.swap;
|
|
mData[idx].endTime = deal.time;
|
|
mData[idx].profit = deal.profit;
|
|
|
|
//
|
|
mData[idx].message =
|
|
deal.reason == DEAL_REASON_TP
|
|
? "TP"
|
|
: "SL";
|
|
|
|
//
|
|
Save(idx);
|
|
}
|
|
|
|
//
|
|
void Finish(
|
|
const ulong ticket,
|
|
const XPosition &position,
|
|
const string comment //
|
|
)
|
|
{
|
|
//
|
|
int idx = -1;
|
|
bool hasItem = HasItem(
|
|
ticket,
|
|
idx //
|
|
);
|
|
if (!hasItem)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Update Item Info ...
|
|
mData[idx].swap = position.swap;
|
|
mData[idx].endTime = TimeCurrent();
|
|
mData[idx].profit = position.profit;
|
|
|
|
//
|
|
mData[idx].message = comment;
|
|
|
|
//
|
|
Save(idx);
|
|
}
|
|
|
|
//
|
|
// this Method call's by a Timer,
|
|
// or in OnTick for Update Positions,
|
|
// Data ...
|
|
void UpdateData()
|
|
{
|
|
//
|
|
XPosition positions[];
|
|
int count = mTrader.GetPositions(positions);
|
|
if (!IsValidSize(count))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
int idx = -1;
|
|
bool isExists = HasItem(
|
|
iPosition.ticket,
|
|
idx //
|
|
);
|
|
if (!isExists)
|
|
{
|
|
//
|
|
// Add New Data ...
|
|
|
|
//
|
|
// Find Executed Signal Deal History ...
|
|
XDeal deals[];
|
|
double commission = 0;
|
|
int dealsCount = mTrader.GetDeals(
|
|
deals,
|
|
iPosition.symbol,
|
|
iPosition.provider,
|
|
iPosition.period,
|
|
NULL,
|
|
DEAL_ENTRY_IN //
|
|
);
|
|
if (IsValidSize(dealsCount))
|
|
{
|
|
//
|
|
for (int i = 0; i < dealsCount; i++)
|
|
{
|
|
//
|
|
XDeal iDeal = deals[i];
|
|
if (iDeal.positionId == iPosition.ticket)
|
|
{
|
|
//
|
|
commission = iDeal.commission;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
XTradeData iData;
|
|
iData.Fill(
|
|
iPosition //
|
|
);
|
|
|
|
//
|
|
iData.ticket = iPosition.ticket;
|
|
|
|
//
|
|
Add(iData);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Update Exists ...
|
|
mData[idx].Update(iPosition);
|
|
}
|
|
}
|
|
|
|
//
|
|
count = Count();
|
|
if (count > maxSameTimeTrades)
|
|
{
|
|
maxSameTimeTrades = count;
|
|
}
|
|
|
|
//
|
|
GetMaxDrawdown();
|
|
}
|
|
|
|
//
|
|
void UpdateSignal(XSignal &signal)
|
|
{
|
|
//
|
|
if (!signal.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
int count = Count();
|
|
if (!IsValidSize(count))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
bool isOwn = mData[i].IsOwn(
|
|
signal.symbol,
|
|
signal.provider,
|
|
signal.period,
|
|
ToPositionType(signal.type) //
|
|
);
|
|
if (isOwn)
|
|
{
|
|
mData[i].Fill(signal);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void RemoveSignal(XSignal &signal)
|
|
{
|
|
//
|
|
if (!signal.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
int signalIDX = -1;
|
|
ENUM_X_POSITION_TYPES xType = ToPositionType(signal.type);
|
|
bool hasSignal = HasItem(
|
|
signal.symbol,
|
|
signal.provider,
|
|
signal.period,
|
|
xType,
|
|
signalIDX //
|
|
);
|
|
if (!hasSignal || !IsValidIndex(signalIDX))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Remove Item From List ...
|
|
ArrayRemove(
|
|
mData,
|
|
signalIDX,
|
|
1 //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Do all Protections ...
|
|
void HandleProtection()
|
|
{
|
|
//
|
|
int count = Count();
|
|
if (!IsValidSize(count))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Do Hedging ...
|
|
if (mAllowHedging)
|
|
{
|
|
//
|
|
// Retrieve Positions ...
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(
|
|
positions //
|
|
);
|
|
|
|
//
|
|
bool isParamsValid =
|
|
mHedgeVolumeFactor > 0 &&
|
|
mHedgeMinProfitPerVolumeFactor > 0;
|
|
|
|
//
|
|
bool isCountValid = mMinTradesForHedge <= 0
|
|
? positionsCount >= 1
|
|
: positionsCount >= mMinTradesForHedge;
|
|
|
|
//
|
|
// Validate State ...
|
|
if (isParamsValid && isCountValid)
|
|
{
|
|
//
|
|
double swaps = 0;
|
|
double profits = 0;
|
|
double volumes = 0;
|
|
double commissions = 0;
|
|
double requiredProfitsForHedge = 0;
|
|
CalculateHedgeData(
|
|
positions,
|
|
swaps,
|
|
profits,
|
|
volumes,
|
|
commissions,
|
|
requiredProfitsForHedge //
|
|
);
|
|
|
|
//
|
|
bool canHedge = profits >= requiredProfitsForHedge;
|
|
if (canHedge)
|
|
{
|
|
//
|
|
string comment = "Hedging ...";
|
|
|
|
//
|
|
if (IsValidSize(positionsCount))
|
|
{
|
|
//
|
|
int closeds = mTrader.Close(
|
|
positions,
|
|
comment //
|
|
);
|
|
if (IsValidSize(closeds))
|
|
{
|
|
//
|
|
string msg = ToString(positionsCount) + " Closed Due Hedge Action in: " + ToString(profits) + " ...";
|
|
Alert(msg);
|
|
|
|
//
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Do Force Closing ...
|
|
|
|
//
|
|
// Loop through Data ...
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
ulong ticket = mData[i].ticket;
|
|
double profit = mData[i].profit;
|
|
double volume = mData[i].volume;
|
|
string symbol = mData[i].symbol;
|
|
string provider = mData[i].provider;
|
|
bool isLong = IsLong(mData[i].type);
|
|
ENUM_TIMEFRAMES period = mData[i].period;
|
|
double points = GetPoints(mData[i].symbol);
|
|
bool ignoreTPOnTrail = mData[i].ignoreTPOnTrail;
|
|
double recoveryTPPoint = mData[i].recoveryTPPoint;
|
|
double recoverySLPoint = mData[i].recoverySLPoint;
|
|
int maxAllowedRecover = mData[i].maxAllowedRecover;
|
|
double trailStepInPoints = mData[i].trailStepInPoint;
|
|
double trailStartInPoint = mData[i].trailStartInPoint;
|
|
bool trailBasedOnProfit = mData[i].trailBasedOnProfit;
|
|
double recoveryVolumeMultiplier = mData[i].recoveryVolumeMultiplier;
|
|
|
|
//
|
|
XPosition iPosition;
|
|
bool hasPosition = mTrader.GetPosition(
|
|
ticket,
|
|
iPosition //
|
|
);
|
|
if (!hasPosition)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Allow Trail Only for Positions Which
|
|
// doesnt Have Any Support ...
|
|
bool iHasSupport = mTrader.HasSupport(
|
|
ticket //
|
|
);
|
|
if (iHasSupport)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Try to Detect Recovered Positions ...
|
|
|
|
//
|
|
XPosition positions[];
|
|
XPosition recoveries[];
|
|
int recoveriesCount = 0;
|
|
int positionsCount = mTrader.GetPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period //
|
|
);
|
|
if (positionsCount > 0)
|
|
{
|
|
//
|
|
recoveriesCount = ExtractRecoveries(
|
|
ticket,
|
|
positions,
|
|
recoveries //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Handle Recovery Break Event if Hedging is Disabled ...
|
|
if (mData[i].allowRecover)
|
|
{
|
|
//
|
|
if (mData[i].recoveryLevel <= 0)
|
|
{
|
|
//
|
|
if (profit > 0 && iPosition.tp == 0)
|
|
{
|
|
//
|
|
double proftiInPoints = profit / volume;
|
|
bool canClose = proftiInPoints >= recoveryTPPoint;
|
|
if (canClose)
|
|
{
|
|
//
|
|
string comment = "Recovery TP ...";
|
|
bool isClosed = mTrader.Close(
|
|
ticket,
|
|
comment //
|
|
);
|
|
if (isClosed)
|
|
{
|
|
//
|
|
string msg = "Position (" + ToString(ticket) + ") Hit's Recovery TP ...";
|
|
Alert(msg);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
if (recoveriesCount > 0 && !mAllowHedging)
|
|
{
|
|
//
|
|
XPosition mustClose[];
|
|
AddRef(
|
|
iPosition,
|
|
mustClose //
|
|
);
|
|
|
|
//
|
|
// Summary Calculations ...
|
|
double rVolume = volume;
|
|
double rProfit = profit;
|
|
bool hasMaxVolume = false;
|
|
double rSwap = mData[i].swap;
|
|
double rCommission = mData[i].commission;
|
|
for (int j = 0; j < recoveriesCount; j++)
|
|
{
|
|
//
|
|
rSwap += recoveries[j].swap;
|
|
rProfit += recoveries[j].profit;
|
|
rVolume += recoveries[j].volume;
|
|
rCommission += mTrader.GetPositionCommission(recoveries[j].ticket);
|
|
|
|
//
|
|
if (!hasMaxVolume &&
|
|
mMaxAllowedRecoveryVolume > 0 &&
|
|
recoveries[j].volume >= mMaxAllowedRecoveryVolume)
|
|
{
|
|
hasMaxVolume = true;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
recoveries[j],
|
|
mustClose //
|
|
);
|
|
}
|
|
double profitSummary =
|
|
rProfit + (-1 * rSwap) - MathAbs(rCommission);
|
|
|
|
//
|
|
double requiredProfitPoints = recoveriesCount * recoveryTPPoint;
|
|
// if (mData[i].recoveryLevel >= 2)
|
|
// {
|
|
// //
|
|
// requiredProfitPoints /= mData[i].recoveryLevel;
|
|
// if (requiredProfitPoints < recoveryTPPoint)
|
|
// {
|
|
// requiredProfitPoints = recoveryTPPoint;
|
|
// }
|
|
// }
|
|
|
|
//
|
|
if (profitSummary > 0)
|
|
{
|
|
//
|
|
double rProfitInPoints = profitSummary / volume;
|
|
|
|
//
|
|
bool canFinishRecovery = rProfitInPoints >= requiredProfitPoints;
|
|
if (canFinishRecovery)
|
|
{
|
|
//
|
|
// Here we Have to close All Recoveries
|
|
// and Position it Self ...
|
|
|
|
//
|
|
string comment = "Recovery Done ...";
|
|
|
|
//
|
|
int closeds = mTrader.Close(
|
|
mustClose,
|
|
comment //
|
|
);
|
|
if (closeds > 0)
|
|
{
|
|
//
|
|
string msg = "EQM Done Recovery (" + ToString(closeds) + ") Positions ...";
|
|
Alert(msg);
|
|
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
Clean(mustClose);
|
|
Clean(positions);
|
|
Clean(recoveries);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Retriece CIndex Bar ...
|
|
XOHCL cBar;
|
|
bool isBarInited = cBar.Init(
|
|
symbol,
|
|
period,
|
|
1 //
|
|
);
|
|
if (!isBarInited)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// For Trend ...
|
|
XOHCL tmpHSW[];
|
|
XOHCL tmpLSW[];
|
|
|
|
//
|
|
// Detect Bullish/Bearish Patterns and Trends ...
|
|
|
|
//
|
|
// Bullish ...
|
|
bool hasBullishPower = cBar.HasBullishPower();
|
|
bool hasBullishPattern = HasBullishPattern(cBar);
|
|
bool hasBullishPressure = cBar.HasBullishPressure();
|
|
bool isBullishTrend = cBar.HasBullishTrend(
|
|
tmpHSW,
|
|
tmpLSW,
|
|
true //
|
|
);
|
|
bool hasBullishSigns =
|
|
//
|
|
// isBullishTrend &&
|
|
cBar.IsBullish() &&
|
|
hasBullishPattern &&
|
|
(hasBullishPower &&
|
|
hasBullishPressure)
|
|
//
|
|
;
|
|
|
|
//
|
|
// Bearish ...
|
|
bool hasBearishPower = cBar.HasBearishPower();
|
|
bool hasBearishPattern = HasBearishPattern(cBar);
|
|
bool hasBearishPressure = cBar.HasBearishPressure();
|
|
bool isBearishTrend = cBar.HasBearishTrend(
|
|
tmpHSW,
|
|
tmpLSW,
|
|
true //
|
|
);
|
|
bool hasBearishSigns =
|
|
//
|
|
// isBearishTrend &&
|
|
cBar.IsBearish() &&
|
|
hasBearishPressure &&
|
|
(hasBearishPower &&
|
|
hasBearishPattern)
|
|
//
|
|
;
|
|
|
|
//
|
|
// Trail ...
|
|
if (mData[i].allowTrailStop)
|
|
{
|
|
//
|
|
// Do Trail ...
|
|
|
|
//
|
|
// Only Can Trail if Position In Profit ...
|
|
if (profit > 0)
|
|
{
|
|
//
|
|
// Current Profit ...
|
|
double profitInPoints = (profit / volume);
|
|
|
|
//
|
|
// Check Trailling Method ...
|
|
if (trailBasedOnProfit)
|
|
{
|
|
//
|
|
// if Trailling Method is Based on Profits Growing
|
|
// these Values must Greater than 0 ...
|
|
if (trailStartInPoint > 0 && trailStepInPoints > 0)
|
|
{
|
|
//
|
|
double requiredProfit = trailStartInPoint + (mData[i].trailLevel * trailStepInPoints);
|
|
bool isProfitPassed = profitInPoints >= requiredProfit;
|
|
if (isProfitPassed)
|
|
{
|
|
//
|
|
// Calculate new Level SL ...
|
|
double sl =
|
|
isLong
|
|
? iPosition.entry + (mData[i].trailLevel * (trailStepInPoints * points))
|
|
: iPosition.entry - (mData[i].trailLevel * (trailStepInPoints * points));
|
|
sl = NormalizePrice(
|
|
sl,
|
|
symbol //
|
|
);
|
|
|
|
//
|
|
// Calculate TP ...
|
|
double tp =
|
|
ignoreTPOnTrail
|
|
? 0
|
|
: iPosition.tp;
|
|
|
|
//
|
|
if (sl != iPosition.sl)
|
|
{
|
|
//
|
|
bool isModified = mTrader.Modify(
|
|
ticket,
|
|
sl,
|
|
tp //
|
|
);
|
|
if (isModified)
|
|
{
|
|
//
|
|
// Increase Trail Level ...
|
|
mData[i].trailLevel++;
|
|
|
|
//
|
|
string msg = "Position (" + ToString(ticket) + ") Stops in Level: " + ToString(mData[i].trailLevel) + " Trailed Successfully ...";
|
|
Alert(msg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Handle Trailling Based On Bars ...
|
|
|
|
//
|
|
bool canTrail = true;
|
|
if (trailStartInPoint > 0)
|
|
{
|
|
//
|
|
// Check Profit ...
|
|
canTrail = profitInPoints >= trailStartInPoint;
|
|
}
|
|
|
|
//
|
|
if (canTrail)
|
|
{
|
|
//
|
|
// Zero Index Bar ...
|
|
XOHCL zBar;
|
|
bool isInited = zBar.Init(
|
|
symbol,
|
|
period,
|
|
0 //
|
|
);
|
|
if (isInited)
|
|
{
|
|
//
|
|
// Get Previous Bar ...
|
|
XOHCL cBar;
|
|
isInited = zBar.GetPreviousBar(cBar);
|
|
if (isInited)
|
|
{
|
|
//
|
|
// Get Prev of Previous Bar ...
|
|
XOHCL pBar;
|
|
isInited = cBar.GetPreviousBar(pBar);
|
|
if (isInited)
|
|
{
|
|
//
|
|
// Here we have to Check Bar Closes for
|
|
// Trailling Stops ...
|
|
canTrail =
|
|
//
|
|
isLong
|
|
? cBar.IsBullish() &&
|
|
cBar.close > pBar.open
|
|
: cBar.IsBearish() &&
|
|
cBar.close < pBar.open
|
|
//
|
|
;
|
|
if (canTrail)
|
|
{
|
|
//
|
|
// Calculate new Level SL ...
|
|
double sl =
|
|
isLong
|
|
? cBar.low
|
|
: cBar.high;
|
|
sl = NormalizePrice(
|
|
sl,
|
|
symbol //
|
|
);
|
|
|
|
//
|
|
// Calculate TP ...
|
|
double tp =
|
|
ignoreTPOnTrail
|
|
? 0
|
|
: iPosition.tp;
|
|
|
|
//
|
|
if (sl != iPosition.sl)
|
|
{
|
|
//
|
|
bool isModified = mTrader.Modify(
|
|
ticket,
|
|
sl,
|
|
tp //
|
|
);
|
|
if (isModified)
|
|
{
|
|
//
|
|
// Increase Trail Level ...
|
|
mData[i].trailLevel++;
|
|
|
|
//
|
|
string msg = "Position (" + ToString(ticket) + ") Stops in Level: " + ToString(mData[i].trailLevel) + " Trailed Successfully ...";
|
|
Alert(msg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Recovery ...
|
|
if (mData[i].allowRecover)
|
|
{
|
|
//
|
|
// Do Recovery ...
|
|
|
|
//
|
|
// Check Looking for Direction ...
|
|
ENUM_X_POSITION_TYPES recoveryLookingType = mData[i].recoveryLookingType;
|
|
if (recoveryLookingType == X_POSITION_TYPE_ALL ||
|
|
recoveryLookingType == X_POSITION_TYPE_NONE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isDirectional = recoveryLookingType == mData[i].type;
|
|
|
|
//
|
|
bool hasPressure =
|
|
!mForcePressuresInRecovery
|
|
? true
|
|
: isDirectional
|
|
//
|
|
// Looking For Directional Pressure ...
|
|
? isLong
|
|
? hasBullishSigns
|
|
: hasBearishSigns
|
|
//
|
|
// Looking For InDirectional Pressure ...
|
|
: isLong
|
|
? hasBearishSigns
|
|
: hasBullishSigns;
|
|
|
|
//
|
|
// Check Required Properties Validation ...
|
|
if (
|
|
recoveryTPPoint > 0 &&
|
|
recoverySLPoint > 0 &&
|
|
(maxAllowedRecover <= 0
|
|
? true
|
|
: mData[i].recoveryLevel < maxAllowedRecover))
|
|
{
|
|
//
|
|
// Calculate requirements ...
|
|
double distanceInPoints = (MathAbs(profit) / volume);
|
|
// double _volumeMultiplier = ((mData[i].recoveryLevel + 1) * recoveryVolumeMultiplier);
|
|
// double _volume =
|
|
// volume * (_volumeMultiplier <= 0 ? 1 : _volumeMultiplier);
|
|
double _volume =
|
|
mData[i].recoveryVolume <= 0
|
|
? volume * recoveryVolumeMultiplier
|
|
: mData[i].recoveryVolume;
|
|
if (mMaxAllowedRecoveryVolume > 0 && _volume > mMaxAllowedRecoveryVolume)
|
|
{
|
|
_volume = mMaxAllowedRecoveryVolume;
|
|
}
|
|
|
|
//
|
|
string _comment = GenerateRecoveryTag(ticket);
|
|
|
|
//
|
|
XSignal rSignal;
|
|
bool isPrepared = false;
|
|
|
|
//
|
|
if (profit < 0 &&
|
|
hasPressure &&
|
|
!isDirectional &&
|
|
distanceInPoints >= recoverySLPoint)
|
|
{
|
|
//
|
|
FixRecoveredPositionTPSL(iPosition);
|
|
|
|
//
|
|
ENUM_POSITION_TYPE _type =
|
|
isLong
|
|
? POSITION_TYPE_SELL
|
|
: POSITION_TYPE_BUY;
|
|
double _entry = GetEntry(symbol, _type);
|
|
|
|
//
|
|
isPrepared = rSignal.Prepare(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
_type,
|
|
X_ORDER_MODE_MARKET,
|
|
_entry,
|
|
_volume,
|
|
0,
|
|
0 //
|
|
);
|
|
}
|
|
else if (profit >= 0 &&
|
|
hasPressure &&
|
|
isDirectional)
|
|
{
|
|
//
|
|
FixRecoveredPositionTPSL(iPosition);
|
|
|
|
//
|
|
ENUM_POSITION_TYPE _type =
|
|
isLong
|
|
? POSITION_TYPE_BUY
|
|
: POSITION_TYPE_SELL;
|
|
double _entry = GetEntry(symbol, _type);
|
|
|
|
//
|
|
isPrepared = rSignal.Prepare(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
_type,
|
|
X_ORDER_MODE_MARKET,
|
|
_entry,
|
|
_volume,
|
|
0,
|
|
0 //
|
|
);
|
|
}
|
|
|
|
//
|
|
if (isPrepared && rSignal.IsValid())
|
|
{
|
|
//
|
|
// Attach Recovery Tag ...
|
|
rSignal.comment = _comment;
|
|
|
|
//
|
|
// Execute Recovery Signal ...
|
|
bool isExecuted = ExecuteRecoverySignal(rSignal);
|
|
if (isExecuted)
|
|
{
|
|
//
|
|
// Increase Recovery Level ...
|
|
mData[i].recoveryLevel++;
|
|
mData[i].recoveryVolume = _volume * recoveryVolumeMultiplier;
|
|
|
|
//
|
|
// Try to Reverse Looking for Next Recovery
|
|
// Position Type ...
|
|
ENUM_POSITION_TYPE _type;
|
|
bool isConverted = ToPositionType(
|
|
_type,
|
|
recoveryLookingType //
|
|
);
|
|
if (isConverted)
|
|
{
|
|
mData[i].recoveryLookingType = GetOppositPositionType(_type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Do Position Protecting ...
|
|
void HandlePositionProtecting()
|
|
{
|
|
//
|
|
if (!mAllowProtectPositions)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
XPosition mainPositions[];
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(
|
|
positions,
|
|
NULL, // All Symbols ...
|
|
NULL, // All Providers ...
|
|
NULL, // All Periods ...
|
|
NULL, // All Types ...
|
|
true // Filter By Magic ...
|
|
);
|
|
if (!IsValidSize(positions))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Filter Main Positions ...
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
if (!iPosition.IsValid())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Support or Recovery ...
|
|
bool isSupport = IsSupport(iPosition.comment);
|
|
bool isRecovery = ExtractRecoveredTicket(iPosition.comment) > 0;
|
|
if (isSupport || isRecovery)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
mainPositions //
|
|
);
|
|
}
|
|
|
|
//
|
|
int mainPositionsCount = ArraySize(mainPositions);
|
|
if (!IsValidSize(mainPositionsCount))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Loop Through Main Positions ...
|
|
for (int i = 0; i < mainPositionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = mainPositions[i];
|
|
if (!iPosition.IsValid())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Find iPosition Related TradeData ...
|
|
int idx = -1;
|
|
bool hasItem = HasItem(
|
|
iPosition.ticket,
|
|
idx //
|
|
);
|
|
if (!hasItem)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
XTradeData iData = mData[idx];
|
|
if (!iData.IsValid())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now we Have a Main Positions and
|
|
// we Make Sure this Position has a TradeData Model ...
|
|
// Next things is Extract it's Support Positions ...
|
|
XPosition supports[];
|
|
for (int j = 0; j < positionsCount; j++)
|
|
{
|
|
//
|
|
XPosition jPosition = positions[j];
|
|
|
|
//
|
|
bool isSupport = IsSupport(jPosition.comment);
|
|
if (!isSupport)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
ulong parentTicket = ExtractSupportedTicket(jPosition.comment);
|
|
if (parentTicket != iPosition.ticket)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
jPosition,
|
|
supports //
|
|
);
|
|
}
|
|
|
|
//
|
|
int supportsCount = ArraySize(supports);
|
|
|
|
//
|
|
// Collect Main Position Related Data ...
|
|
|
|
//
|
|
// Hold a Position and All of it's Supports ...
|
|
XPosition iPack[];
|
|
|
|
//
|
|
// Fill Pack ...
|
|
AddRef(
|
|
iPosition,
|
|
iPack //
|
|
);
|
|
|
|
//
|
|
if (IsValidSize(supportsCount))
|
|
{
|
|
//
|
|
Copy(
|
|
supports,
|
|
iPack //
|
|
);
|
|
}
|
|
|
|
//
|
|
int iPackCount = ArraySize(iPack);
|
|
if (!IsValidSize(iPackCount))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
double volume = 0;
|
|
double profit = 0;
|
|
double commission = 0;
|
|
|
|
//
|
|
// Calculate Required Datas ...
|
|
for (int j = 0; j < iPackCount; j++)
|
|
{
|
|
//
|
|
XPosition jPosition = iPack[j];
|
|
if (!jPosition.IsValid())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
XPosition uPosition;
|
|
bool hasPosition = mTrader.GetPosition(
|
|
jPosition.ticket,
|
|
uPosition //
|
|
);
|
|
if (!hasPosition)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
volume += uPosition.volume;
|
|
profit += uPosition.profit;
|
|
|
|
//
|
|
double jCommission = mTrader.GetPositionCommission(jPosition.ticket);
|
|
commission += jCommission;
|
|
}
|
|
|
|
//
|
|
// here we have to Check Protection Conditions ...
|
|
|
|
//
|
|
// Check Position State ...
|
|
bool isInProfit = profit > 0;
|
|
|
|
//
|
|
// Based On Single Position ...
|
|
if (iPackCount == 1)
|
|
{
|
|
//
|
|
// Based on Profit ...
|
|
if (isInProfit)
|
|
{
|
|
}
|
|
//
|
|
// Based On DrawDown ...
|
|
else
|
|
{
|
|
}
|
|
}
|
|
//
|
|
// Multiple Protected Positions ...
|
|
else
|
|
{
|
|
//
|
|
// Based on Profit ...
|
|
if (isInProfit)
|
|
{
|
|
}
|
|
//
|
|
// Based On DrawDown ...
|
|
else
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Validate Signal For Execution ...
|
|
bool CanExecute(XSignal &signal)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Signal ...
|
|
result = signal.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// TODO:
|
|
// Here we can apply Same type or Opposit Type
|
|
// Signals behaviour ...
|
|
// also check For Market Open ...
|
|
|
|
//
|
|
// Check Market ...
|
|
// TODO: Fix this ...
|
|
// result = mSymbolSession.CanTrade(signal.symbol);
|
|
// if (!result) {
|
|
// return result;
|
|
// }
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool ExecuteSignal(
|
|
XSignal &signal, // Signal for Execution
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT &state, // Execution State
|
|
bool ignorePolicies = false // Ignore Execution Policies
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check Signal Validation ...
|
|
result = signal.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Filter Signals if Necessary ...
|
|
result = CanExecute(signal);
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_INVALID_PARAMS;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Policies ...
|
|
if (!ignorePolicies)
|
|
{
|
|
//
|
|
bool isLong = IsLong(signal.type);
|
|
|
|
//
|
|
// Check Allow Trade Type ...
|
|
result =
|
|
isLong
|
|
? mAllowLong
|
|
: mAllowShort;
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Trades Count ...
|
|
if (mMaxAllowedLongs > 0 || mMaxAllowedShorts > 0)
|
|
{
|
|
//
|
|
XPosition longs[];
|
|
XPosition shorts[];
|
|
|
|
//
|
|
// Counting Positions ...
|
|
if (mUseMaxAllowedTradesPerSymbol)
|
|
{
|
|
//
|
|
mTrader.GetPositions(
|
|
longs,
|
|
shorts,
|
|
signal.symbol //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
mTrader.GetPositions(
|
|
longs,
|
|
shorts,
|
|
NULL // All Symbols ...
|
|
);
|
|
}
|
|
|
|
//
|
|
int longsCount = ArraySize(longs);
|
|
int shortsCount = ArraySize(shorts);
|
|
|
|
//
|
|
result =
|
|
//
|
|
// Long ...
|
|
isLong
|
|
? mMaxAllowedLongs <= 0
|
|
? true
|
|
: longsCount < mMaxAllowedLongs
|
|
//
|
|
// Short ...
|
|
: mMaxAllowedShorts <= 0
|
|
? true
|
|
: shortsCount < mMaxAllowedShorts
|
|
//
|
|
;
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_MAX_REACHED;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Drawdown ...
|
|
if (mMaxDrawdownPercentForOpenTrades > 0)
|
|
{
|
|
//
|
|
drawdownPercent = GetMaxDrawdown();
|
|
|
|
//
|
|
result =
|
|
drawdownPercent <= 0
|
|
? true
|
|
: drawdownPercent < mMaxDrawdownPercentForOpenTrades;
|
|
if (!result)
|
|
{
|
|
//
|
|
state = X_SIGNAL_EXECUTION_FAILED_NO_EQUITY;
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Execute Signal ...
|
|
result = mTrader.ExecuteSignal(
|
|
signal,
|
|
state //
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
double commission = mTrader.GetPositionCommission(signal.positionId);
|
|
|
|
//
|
|
// Add Signal to Trade Handler ...
|
|
AddData(
|
|
signal,
|
|
commission //
|
|
);
|
|
|
|
//
|
|
string msg = (signal.type == POSITION_TYPE_BUY ? "Long" : "Short") +
|
|
" Signal Provided by: " + signal.provider +
|
|
(signal.pushers > 1 ? " Pushers: " + ToString(signal.pushers) : "") +
|
|
" on: " + signal.symbol +
|
|
" in: " + ToString(signal.period) +
|
|
" Executed Successfully ...";
|
|
Alert(msg);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
bool mSaveSignals; // Save Signals
|
|
bool mSaveTrades; // Save Trades
|
|
bool mSaveConditions; // Save SL Conditions
|
|
|
|
//
|
|
XSymbolSessionParser mSymbolSession; // Check Symbol Session
|
|
|
|
//
|
|
bool mAllowLong; // Allow Long/Buy Trade Type
|
|
bool mAllowShort; // Allow Short/Sell Trade Type
|
|
int mMaxAllowedLongs; // Max Allowe Long/Buy Trades
|
|
int mMaxAllowedShorts; // Max Allowe Short/Sell Trades
|
|
bool mUseMaxAllowedTradesPerSymbol; // Use Max Allowed Trades Per Symbol
|
|
double mMaxDrawdownPercentForOpenTrades; // Max Drawdown Percent for Open Trades
|
|
|
|
//
|
|
XSCTrade *mTrader; // Instance of Trader Class
|
|
XTradeCollector *mCollector; // Instance of Trade Collector Class
|
|
|
|
//
|
|
// Trailling ...
|
|
bool mAllowTrailStops; // Trail All Trades Stops
|
|
bool mIgnoreTPOnTrail; // Ignore SL Trailed TP
|
|
bool mTrailBasedOnProfit; // Do Trail Based on Profit Points
|
|
double mTrailStartInPoint; // Trail Starts in Points of Profit
|
|
double mTrailStepInPoint; // Profit Must Grows Point to do Trailling
|
|
|
|
//
|
|
// Recovery ...
|
|
bool mAllowRecover; // Allow Signal Recovery
|
|
int mMaxAllowedRecover; // Max Allowed Recovery
|
|
double mRecoveryTPPoint; // Recovery TP in Points
|
|
double mRecoverySLPoint; // Recovery SL in Points
|
|
bool mForcePressuresInRecovery; // Use Direction Pressures In Recovery
|
|
double mRecoveryVolumeMultiplier; // Recovery Volume Multiplier
|
|
double mMaxAllowedRecoveryVolume; // Max Allowed Recovery Volume
|
|
|
|
//
|
|
// Hedging ...
|
|
bool mAllowHedging; // Hedge Trades
|
|
int mMinTradesForHedge; // Minimum Open Trades for Do Hedge
|
|
double mHedgeVolumeFactor; // Hedging Volume Factor
|
|
double mHedgeMinProfitPerVolumeFactor; // Hedging Min Required Profit Per Volume Factor
|
|
|
|
//
|
|
// Position Protecting ...
|
|
|
|
//
|
|
bool mAllowProtectPositions; // Allow Protect Positions
|
|
|
|
//
|
|
XTradeData mData[]; // Hold Trade Data
|
|
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Props ...
|
|
int maxSameTimeTrades; // Max Same Time Trades
|
|
double maxDrawdown; // Max Drawdown
|
|
double drawdownPercent; // Drawdown Percent
|
|
double staticBalanceForCalculateDrawdown; // Static Balance for Max Drawdown Calculation
|
|
|
|
//
|
|
// Count Data ...
|
|
int Count()
|
|
{
|
|
return ArraySize(mData);
|
|
}
|
|
|
|
//
|
|
// Add Item ...
|
|
bool Add(XTradeData &item)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = item.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int index = -1;
|
|
if (item.ticket > 0)
|
|
{
|
|
//
|
|
result = !HasItem(
|
|
item.ticket,
|
|
index //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
result = !HasItem(
|
|
item.symbol,
|
|
item.provider,
|
|
item.period,
|
|
item.type,
|
|
index //
|
|
);
|
|
}
|
|
if (!result || IsValidIndex(index))
|
|
{
|
|
//
|
|
result = false;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
item,
|
|
mData //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Find Item Index ...
|
|
bool HasItem(
|
|
ulong ticket,
|
|
int &index //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
index = -1;
|
|
|
|
//
|
|
int count = Count();
|
|
result = IsValidSize(count);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
result = mData[i].IsOwn(ticket);
|
|
if (result)
|
|
{
|
|
//
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Find Item Index ...
|
|
bool HasItem(
|
|
string symbol,
|
|
string provider,
|
|
ENUM_TIMEFRAMES period,
|
|
ENUM_X_POSITION_TYPES type,
|
|
int &index //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
index = -1;
|
|
|
|
//
|
|
// Validate ...
|
|
result =
|
|
//
|
|
IsValid(symbol) &&
|
|
IsValid(period) &&
|
|
IsValid(provider) &&
|
|
type != X_POSITION_TYPE_ALL &&
|
|
type != X_POSITION_TYPE_NONE
|
|
//
|
|
;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int count = Count();
|
|
result = IsValidSize(count);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
result = mData[i].IsOwn(
|
|
symbol,
|
|
provider,
|
|
period,
|
|
type //
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
void Save(int index)
|
|
{
|
|
//
|
|
int count = Count();
|
|
if (!IsValidIndex(index) || index > count - 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Save Trade ...
|
|
if (mSaveTrades)
|
|
{
|
|
mCollector.Save(mData[index]);
|
|
}
|
|
|
|
//
|
|
if (mSaveConditions)
|
|
{
|
|
mCollector.SaveConditions(mData[index]);
|
|
}
|
|
|
|
//
|
|
// Remove Item From List ...
|
|
ArrayRemove(
|
|
mData,
|
|
index,
|
|
1 //
|
|
);
|
|
}
|
|
|
|
//
|
|
void SaveSignal(XTradeData &item)
|
|
{
|
|
//
|
|
if (!mSaveSignals)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Save Signal ...
|
|
mCollector.SaveSignal(item);
|
|
}
|
|
|
|
//
|
|
// Calculate Required Data for Hedging ...
|
|
void CalculateHedgeData(
|
|
double &profits,
|
|
double &commissions,
|
|
double &requiredProfitsForHedge //
|
|
)
|
|
{
|
|
//
|
|
// Reset Values ...
|
|
profits = 0;
|
|
commissions = 0;
|
|
requiredProfitsForHedge = 0;
|
|
|
|
//
|
|
// Validate Configurations ...
|
|
if (mHedgeVolumeFactor <= 0 ||
|
|
mHedgeMinProfitPerVolumeFactor <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check Positions Exists For Hedging ...
|
|
int count = Count();
|
|
if (!IsValidSize(count))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
double swaps = 0;
|
|
double volumes = 0;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
swaps += mData[i].swap;
|
|
profits += mData[i].profit;
|
|
volumes += mData[i].volume;
|
|
commissions += mData[i].commission;
|
|
}
|
|
|
|
//
|
|
// Calculate required Profits for Hedging ...
|
|
requiredProfitsForHedge = ((volumes / mHedgeVolumeFactor) * mHedgeMinProfitPerVolumeFactor);
|
|
requiredProfitsForHedge += -1 * (swaps);
|
|
requiredProfitsForHedge -= commissions;
|
|
}
|
|
|
|
//
|
|
// Calculate Required Data for Hedging ...
|
|
void CalculateHedgeData(
|
|
XPosition &positions[],
|
|
double &swaps,
|
|
double &profits,
|
|
double &volumes,
|
|
double &commissions,
|
|
double &requiredProfitsForHedge //
|
|
)
|
|
{
|
|
//
|
|
// Reset Values ...
|
|
swaps = 0;
|
|
profits = 0;
|
|
volumes = 0;
|
|
commissions = 0;
|
|
requiredProfitsForHedge = 0;
|
|
|
|
//
|
|
// Validate Configurations ...
|
|
if (mHedgeVolumeFactor <= 0 ||
|
|
mHedgeMinProfitPerVolumeFactor <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check Positions Exists For Hedging ...
|
|
int count = ArraySize(positions);
|
|
if (!IsValidSize(count))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
double iCommission = mTrader.GetPositionCommission(iPosition.ticket);
|
|
|
|
//
|
|
swaps += iPosition.swap;
|
|
profits += iPosition.profit;
|
|
volumes += iPosition.volume;
|
|
commissions += MathAbs(iCommission);
|
|
}
|
|
|
|
//
|
|
// Calculate required Profits for Hedging ...
|
|
requiredProfitsForHedge = ((volumes / mHedgeVolumeFactor) * mHedgeMinProfitPerVolumeFactor);
|
|
requiredProfitsForHedge += commissions;
|
|
requiredProfitsForHedge += -1 * (swaps);
|
|
}
|
|
|
|
//
|
|
bool FixRecoveredPositionTPSL(XPosition &position)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = position.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double points = GetPoints(position.symbol);
|
|
|
|
// //
|
|
// // Try to Remove SL and TP of Recovery Signals ...
|
|
// if (position.sl > 0 || position.tp > 0)
|
|
// {
|
|
// //
|
|
// string comment = "EQM Recover Remove TP/SL";
|
|
// result = mTrader.Modify(
|
|
// position.ticket,
|
|
// 0,
|
|
// 0,
|
|
// comment //
|
|
// );
|
|
// }
|
|
|
|
//
|
|
// Try to Remove TP of Recovery Signals ...
|
|
if (position.tp > 0)
|
|
{
|
|
//
|
|
double tp = 0;
|
|
bool isLong = IsLong(position.type);
|
|
double reward = MathAbs(position.tp - position.entry) / points;
|
|
if (reward <= mRecoveryTPPoint)
|
|
{
|
|
//
|
|
reward = (mRecoveryTPPoint * 2 * points);
|
|
tp =
|
|
isLong
|
|
? position.entry + reward
|
|
: position.entry - reward;
|
|
tp = NormalizePrice(tp, position.symbol);
|
|
}
|
|
|
|
//
|
|
string comment = "EQM Recover Fix TP/SL";
|
|
result = mTrader.Modify(
|
|
position.ticket,
|
|
position.sl,
|
|
tp,
|
|
comment //
|
|
);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool ExecuteRecoverySignal(XSignal &signal)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT state;
|
|
result = mTrader.ExecuteSignal(
|
|
signal,
|
|
state,
|
|
ORDER_TIME_GTC,
|
|
NULL,
|
|
false //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
};
|
|
|
|
// |