Files
xMQL5/BKPS/14030823/BKPs/14030802-X121/Classes/x-saherelm.xtrade.class.mq5
T
2024-11-15 10:50:11 +03:30

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 &notExecuteds[], // Not Executed Signal
ENUM_X_SIGNAL_EXECUTION_RESULT &states[], // Signal Execution Result
ENUM_ORDER_TYPE_TIME lifetime = ORDER_TIME_GTC, // Order LifeTime
datetime expiration = NULL, // order expiration time
bool checkPolicies = true // Check Account Policies
)
{
//
int result = 0;
//
Clean(states);
Clean(notExecuteds);
//
int signalsCount = ArraySize(signals);
if (signalsCount <= 0)
{
return result;
}
//
ArrayResize(states, signalsCount);
ArrayInitialize(states, X_SIGNAL_EXECUTION_UNKNOWN);
for (int i = 0; i < signalsCount; i++)
{
//
bool isExecuted = ExecuteSignal(
signals[i],
states[i],
lifetime,
expiration
//
);
//
if (isExecuted)
{
result++;
}
else
{
//
AddRef(
signals[i],
notExecuteds //
);
}
}
//
return result;
}
//
// Regular Trade Actions ...
//
// Buy ...
//
// Force Open a Buy/Long Position ...
bool Buy(
string symbol, // Specified Symbol
ENUM_TIMEFRAMES period, // Specified Trade's Period
double volume, // position volume
double price, // execution price
double sl = 0, // stop loss price
double tp = 0, // take profit price
string comment = NULL // comment
)
{
//
bool result = false;
//
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
if (!IsValid(comment) ||
!IsValid(mPeriod))
{
//
string mPeriodStr = GeneratePeriodTag(period);
//
comment += mPeriodStr;
}
//
result = mTrader.Buy(
volume,
symbol,
price,
sl,
tp,
comment
//
);
//
return result;
}
//
// Send BUY LIMIT order ...
bool BuyLimit(
string symbol, // Specified Symbol
ENUM_TIMEFRAMES period, // Specified Trade's Period
double volume, // order volume
double price, // order price
double sl = 0.0, // stop loss price
double tp = 0.0, // take profit price
ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
datetime expiration = 0, // order expiration time
string comment = NULL // comment
)
{
//
bool result = false;
//
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
if (!IsValid(comment) ||
!IsValid(mPeriod))
{
//
string mPeriodStr = GeneratePeriodTag(period);
//
comment += mPeriodStr;
}
//
result = mTrader.BuyLimit(
volume,
price,
symbol,
sl,
tp,
typeTime,
expiration,
comment
//
);
//
return result;
}
//
// Send BUY STOP order ...
bool BuyStop(
string symbol, // Specified Symbol
ENUM_TIMEFRAMES period, // Specified Trade's Period
double volume, // order volume
double price, // order price
double sl = 0.0, // stop loss price
double tp = 0.0, // take profit price
ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
datetime expiration = 0, // order expiration time
string comment = NULL // comment
)
{
//
bool result = false;
//
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
if (!IsValid(comment) ||
!IsValid(mPeriod))
{
//
string mPeriodStr = GeneratePeriodTag(period);
//
comment += mPeriodStr;
}
//
result = mTrader.BuyStop(
volume,
price,
symbol,
sl,
tp,
typeTime,
expiration,
comment
//
);
//
return result;
}
//
// Sell ...
//
// Force Open a Sell/Short Position ...
bool Sell(
string symbol, // Specified Symbol
ENUM_TIMEFRAMES period, // Specified Trade's Period
double volume, // position volume
double price, // execution price
double sl = 0, // stop loss price
double tp = 0, // take profit price
string comment = NULL // comment
)
{
//
bool result = false;
//
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
if (!IsValid(comment) ||
!IsValid(mPeriod))
{
//
string mPeriodStr = GeneratePeriodTag(period);
//
comment += mPeriodStr;
}
//
result = mTrader.Sell(
volume,
symbol,
price,
sl,
tp,
comment
//
);
//
return result;
}
//
// Send SELL LIMIT order ...
bool SellLimit(
string symbol, // Specified Symbol
ENUM_TIMEFRAMES period, // Specified Trade's Period
double volume, // order volume
double price, // order price
double sl = 0.0, // stop loss price
double tp = 0.0, // take profit price
ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
datetime expiration = 0, // order expiration time
string comment = NULL // comment
)
{
//
bool result = false;
//
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
if (!IsValid(comment) ||
!IsValid(mPeriod))
{
//
string mPeriodStr = GeneratePeriodTag(period);
//
comment += mPeriodStr;
}
//
result = mTrader.SellLimit(
volume,
price,
symbol,
sl,
tp,
typeTime,
expiration,
comment
//
);
//
return result;
}
//
// Send SELL STOP order ...
bool SellStop(
string symbol, // Specified Symbol
ENUM_TIMEFRAMES period, // Specified Trade's Period
double volume, // order volume
double price, // order price
double sl = 0.0, // stop loss price
double tp = 0.0, // take profit price
ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
datetime expiration = 0, // order expiration time
string comment = NULL // comment
)
{
//
bool result = false;
//
ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
if (!IsValid(comment) ||
!IsValid(mPeriod))
{
//
string mPeriodStr = GeneratePeriodTag(period);
//
comment += mPeriodStr;
}
//
result = mTrader.SellStop(
volume,
price,
symbol,
sl,
tp,
typeTime,
expiration,
comment
//
);
//
return result;
}
//
// Check a Position is Exists Open or not ...
bool IsOpen(
ulong ticket // Specified Position Ticket
)
{
//
bool result = false;
//
result = mPositionInfo
.SelectByTicket(ticket);
//
return result;
}
//
// Modify Position ...
bool Modify(
const ulong ticket, // position ticket
double sl = 0, // stop loss price
double tp = 0, // take profit
string comment = NULL // comment
)
{
//
bool result = false;
//
// Validate Args ...
XPosition position;
result =
GetPosition(
ticket,
position //
) &&
(tp >= 0 || sl >= 0);
if (!result)
{
return result;
}
//
// Use Position Comment when there isn't new One ...
if (!IsValid(comment) == 0)
{
//
// if there isn't provided Comment
// Use Position Comment for default ...
comment = "Modify " + GetPositionComment(ticket);
}
//
result = mTrader.PositionModify(
ticket,
sl,
tp,
comment
//
);
if (result)
{
//
NotifyModifyEvent(
ticket,
position.profit,
comment //
);
}
//
return result;
}
//
// Close Position By Ticket ...
bool Close(
ulong ticket, // Position Ticket
string comment = NULL // Close Position By Specific Comment
)
{
//
bool result = false;
//
// Check Position Open ...
XPosition position;
result = GetPosition(
ticket,
position //
);
if (!result)
{
return result;
}
//
// Use Default Position Comment if it's Not Provided ...
if (!IsValid(comment))
{
comment = "Close " + GetPositionComment(ticket);
}
//
// Close Position By Specific Comment ...
result = mTrader.PositionClose(
ticket,
mSlippage,
comment
//
);
if (result)
{
//
NotifyForceCloseEvent(
ticket,
position,
comment //
);
}
//
return result;
}
//
// Close Specified Positions ...
int Close(
string comment = NULL, // Close Position By Specific Comment
string symbol = NULL, // Trading Symbol
string provider = NULL, // Signal Provider
ENUM_TIMEFRAMES period = NULL, // Trading Period
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
bool filterByMagic = true // Get Only Self Open Positions
)
{
//
int result = 0;
//
XPosition positions[];
int positionsCount = GetPositions(
positions,
symbol,
provider,
period,
type,
filterByMagic);
if (positionsCount <= 0)
{
return result;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
XPosition iPosition = positions[i];
//
bool isClosed = Close(
iPosition.ticket,
comment
//
);
if (isClosed)
{
result++;
}
}
//
return result;
}
//
// Close a Collection of Positions ...
int Close(
XPosition &positions[], // Collection of Positions
string comment // Close Comment
)
{
//
int result = 0;
//
int positionsCount = ArraySize(positions);
if (positionsCount <= 0)
{
return result;
}
//
for (int i = 0; i < positionsCount; i++)
{
//
XPosition iPosition = positions[i];
//
bool isClosed = Close(
iPosition.ticket,
comment
//
);
if (isClosed)
{
result++;
}
}
//
return result;
}
//
// Close Partial By Ticket ...
bool ClosePartial(
ulong ticket, // Position Ticket
double volume, // Volume Size for Partial Closing
string comment = NULL // Close Position By Specific Comment
)
{
//
bool result = false;
//
XPosition position;
result = GetPosition(
ticket,
position //
);
if (!result)
{
return result;
}
//
// Use Default Partial Close Comment ...
if (!IsValid(comment))
{
//
comment += "PClose " + GetPositionComment(ticket);
}
//
result = mTrader.PositionClosePartial(
ticket,
volume,
mSlippage,
comment
//
);
if (result)
{
//
NotifyPartialCloseEvent(
ticket,
position.profit,
comment //
);
}
//
return result;
}
//
// Cancel Specific Order ...
bool CancelOrder(ulong ticket)
{
return mTrader
.OrderDelete(ticket);
}
//
// Cancel Specific Orders ...
int CancelOrders(
XOrder &orders[] // Specified Orders ...
)
{
//
int result = 0;
//
int ordersCount = ArraySize(orders);
if (ordersCount <= 0)
{
return result;
}
//
for (int i = 0; i < ordersCount; i++)
{
//
XOrder iOrder = orders[i];
//
bool isDeleted = mTrader
.OrderDelete(iOrder.ticket);
if (isDeleted && !result)
{
result++;
}
}
//
return result;
}
//
// Extractors ...
//
// Positions ...
//
// Calculate Positions Profit ...
double Profit(
string symbol = NULL, // Trading Symbol
string provider = NULL, // Signal Provider
ENUM_TIMEFRAMES period = NULL, // Trading Period
ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
bool filterByMagic = true // Get Only Self Open Positions
)
{
//
double result = 0;
//
// Retrieve Positions ...
XPosition positions[];
int positionsCount = GetPositions(
positions,
symbol,
provider,
period,
type,
filterByMagic
//
);
if (positionsCount <= 0)
{
return result;
}
//
for (int i = 0; i < positionsCount; i++)
{
result += positions[i].profit;
}
//
return result;
}
//
// Retrieve Specific Position by Index ...
bool GetPosition(
int index, // Specified Index
XPosition &result // Hold's Result
)
{
//
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;
}
//
};
//