3190 lines
72 KiB
Plaintext
3190 lines
72 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Class Library
|
|
// --------------------------------------
|
|
// Name: XClass
|
|
// Description: provides all classes for use ...
|
|
//
|
|
//
|
|
// 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
|
|
|
|
//
|
|
#include <Trade\Trade.mqh>
|
|
#include <Trade\OrderInfo.mqh>
|
|
#include <Trade\AccountInfo.mqh>
|
|
#include <Trade\PositionInfo.mqh>
|
|
#include "x-saherelm.draw.lib.mq5"
|
|
#include "x-saherelm.models.lib.mq5"
|
|
#include "x-saherelm.common.lib.mq5"
|
|
#include <Trade\HistoryOrderInfo.mqh>
|
|
|
|
//
|
|
// START Model Definitions ...
|
|
//
|
|
|
|
//
|
|
string X_SIGNAL_TP = "TP";
|
|
string X_SIGNAL_SL = "SL";
|
|
string X_SIGNAL_TYPE = "TY";
|
|
string X_SIGNAL_TICKET = "T";
|
|
string X_SIGNAL_RISKFREE_STEP = "RFS";
|
|
string X_SIGNAL_RISKFREE_RATE = "RFR";
|
|
|
|
//
|
|
struct XTraderHandlerResult
|
|
{
|
|
//
|
|
// List Of Closed On SL Signals ...
|
|
XSignal sl[];
|
|
|
|
//
|
|
// List Of Closed On TP Signals ...
|
|
XSignal tp[];
|
|
|
|
//
|
|
// List Of Risk Free Signals ...
|
|
XSignal rf[];
|
|
};
|
|
|
|
//
|
|
// END Model Definitions ...
|
|
//
|
|
|
|
//
|
|
// START Overrides ...
|
|
//
|
|
class XSCTrade : public CTrade
|
|
{
|
|
public:
|
|
//
|
|
// Modify Specific Position, by Specific Comment ...
|
|
bool PositionModify(
|
|
const string symbol, // Symbol
|
|
const double sl, // Stop Loss
|
|
const double tp, // Take Profit
|
|
const string comment = "" // Comment For Modify
|
|
)
|
|
{
|
|
//
|
|
// check stopped
|
|
if (IsStopped(__FUNCTION__))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check position existence
|
|
if (!SelectPosition(symbol))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// clean
|
|
ClearStructures();
|
|
|
|
//
|
|
// setting request
|
|
m_request.action = TRADE_ACTION_SLTP;
|
|
m_request.symbol = symbol;
|
|
m_request.magic = m_magic;
|
|
m_request.sl = sl;
|
|
m_request.tp = tp;
|
|
m_request.position = PositionGetInteger(POSITION_TICKET);
|
|
|
|
//
|
|
// Specified Comment ...
|
|
if (StringLen(comment) > 0)
|
|
{
|
|
m_request.comment = comment;
|
|
}
|
|
|
|
//
|
|
// action and return the result
|
|
return (OrderSend(m_request, m_result));
|
|
}
|
|
bool PositionModify(
|
|
const ulong ticket, // Position Ticket
|
|
const double sl, // Stop Loss
|
|
const double tp, // Take Profit
|
|
const string comment = "" // Comment For Modify
|
|
)
|
|
{
|
|
//
|
|
// check stopped
|
|
if (IsStopped(__FUNCTION__))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check position existence
|
|
if (!PositionSelectByTicket(ticket))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// clean
|
|
ClearStructures();
|
|
|
|
//
|
|
// setting request
|
|
m_request.action = TRADE_ACTION_SLTP;
|
|
m_request.position = ticket;
|
|
m_request.symbol = PositionGetString(POSITION_SYMBOL);
|
|
m_request.magic = m_magic;
|
|
m_request.sl = sl;
|
|
m_request.tp = tp;
|
|
|
|
//
|
|
// Specified Comment ...
|
|
if (StringLen(comment) > 0)
|
|
{
|
|
m_request.comment = comment;
|
|
}
|
|
|
|
//
|
|
// action and return the result
|
|
return (OrderSend(m_request, m_result));
|
|
}
|
|
|
|
//
|
|
// Close Specific Position, by Specific Comment ...
|
|
bool PositionClose(
|
|
const string symbol, // Symbol
|
|
const ulong deviation, // Deviation
|
|
const string comment = "" // Comment For Close
|
|
)
|
|
{
|
|
//
|
|
bool partial_close = false;
|
|
int retry_count = 10;
|
|
uint retcode = TRADE_RETCODE_REJECT;
|
|
|
|
//
|
|
// check stopped
|
|
if (IsStopped(__FUNCTION__))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// clean
|
|
ClearStructures();
|
|
|
|
//
|
|
// check filling
|
|
if (!FillingCheck(symbol))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
do
|
|
{
|
|
//
|
|
// check
|
|
if (SelectPosition(symbol))
|
|
{
|
|
//
|
|
if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
{
|
|
//
|
|
// prepare request for close BUY position
|
|
m_request.type = ORDER_TYPE_SELL;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// prepare request for close SELL position
|
|
m_request.type = ORDER_TYPE_BUY;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// position not found
|
|
m_result.retcode = retcode;
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// setting request
|
|
m_request.action = TRADE_ACTION_DEAL;
|
|
m_request.symbol = symbol;
|
|
m_request.volume = PositionGetDouble(POSITION_VOLUME);
|
|
m_request.magic = m_magic;
|
|
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
|
|
m_request.position = PositionGetInteger(POSITION_TICKET);
|
|
|
|
//
|
|
// Specify Comment ...
|
|
m_request.comment = comment;
|
|
|
|
//
|
|
// check volume
|
|
double max_volume = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
|
if (m_request.volume > max_volume)
|
|
{
|
|
//
|
|
m_request.volume = max_volume;
|
|
partial_close = true;
|
|
}
|
|
else
|
|
{
|
|
partial_close = false;
|
|
}
|
|
|
|
//
|
|
// hedging? just send order
|
|
if (IsHedging())
|
|
{
|
|
return (OrderSend(m_request, m_result));
|
|
}
|
|
|
|
//
|
|
// order send
|
|
if (!OrderSend(m_request, m_result))
|
|
{
|
|
//
|
|
if (--retry_count != 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (retcode == TRADE_RETCODE_DONE_PARTIAL)
|
|
{
|
|
m_result.retcode = retcode;
|
|
}
|
|
|
|
//
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
//--- WARNING. If position volume exceeds the maximum volume allowed for deal,
|
|
//--- and when the asynchronous trade mode is on, for safety reasons, position is closed not completely,
|
|
//--- but partially. It is decreased by the maximum volume allowed for deal.
|
|
if (m_async_mode)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
retcode = TRADE_RETCODE_DONE_PARTIAL;
|
|
if (partial_close)
|
|
{
|
|
Sleep(1000);
|
|
}
|
|
} while (partial_close);
|
|
|
|
//
|
|
// succeed
|
|
return (true);
|
|
}
|
|
bool PositionClose(
|
|
const ulong ticket, // Position Ticket
|
|
const ulong deviation, // Deviation
|
|
const string comment = "" // Comment For Close
|
|
)
|
|
{
|
|
//
|
|
// check stopped
|
|
if (IsStopped(__FUNCTION__))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check position existence
|
|
if (!PositionSelectByTicket(ticket))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
string symbol = PositionGetString(POSITION_SYMBOL);
|
|
|
|
//
|
|
// clean
|
|
ClearStructures();
|
|
|
|
//
|
|
// check filling
|
|
if (!FillingCheck(symbol))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check
|
|
if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
{
|
|
//
|
|
// prepare request for close BUY position
|
|
m_request.type = ORDER_TYPE_SELL;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// prepare request for close SELL position
|
|
m_request.type = ORDER_TYPE_BUY;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
|
}
|
|
|
|
//
|
|
// setting request
|
|
m_request.action = TRADE_ACTION_DEAL;
|
|
m_request.position = ticket;
|
|
m_request.symbol = symbol;
|
|
m_request.volume = PositionGetDouble(POSITION_VOLUME);
|
|
m_request.magic = m_magic;
|
|
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
|
|
|
|
//
|
|
// Specify Comment ...
|
|
m_request.comment = comment;
|
|
|
|
//
|
|
// close position
|
|
return (OrderSend(m_request, m_result));
|
|
}
|
|
|
|
//
|
|
// Partial Close Specific Position, by Specific Comment ...
|
|
bool PositionClosePartial(
|
|
const string symbol, // Symbol
|
|
const double volume, // Partial Closing Volume
|
|
const ulong deviation, // Deviation
|
|
const string comment = "" // Comment For Close
|
|
)
|
|
{
|
|
//
|
|
uint retcode = TRADE_RETCODE_REJECT;
|
|
|
|
//
|
|
// check stopped
|
|
if (IsStopped(__FUNCTION__))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// for hedging mode only
|
|
if (!IsHedging())
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// clean
|
|
ClearStructures();
|
|
|
|
//
|
|
// check filling
|
|
if (!FillingCheck(symbol))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check
|
|
if (SelectPosition(symbol))
|
|
{
|
|
//
|
|
if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
{
|
|
//
|
|
// prepare request for close BUY position
|
|
m_request.type = ORDER_TYPE_SELL;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// prepare request for close SELL position
|
|
m_request.type = ORDER_TYPE_BUY;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// position not found
|
|
m_result.retcode = retcode;
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check volume
|
|
double position_volume = PositionGetDouble(POSITION_VOLUME);
|
|
if (position_volume > volume)
|
|
{
|
|
position_volume = volume;
|
|
}
|
|
|
|
//
|
|
// setting request
|
|
m_request.action = TRADE_ACTION_DEAL;
|
|
m_request.symbol = symbol;
|
|
m_request.volume = position_volume;
|
|
m_request.magic = m_magic;
|
|
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
|
|
m_request.position = PositionGetInteger(POSITION_TICKET);
|
|
|
|
//
|
|
// Specifieng Comment ...
|
|
m_request.comment = comment;
|
|
|
|
//
|
|
// hedging? just send order
|
|
return (OrderSend(m_request, m_result));
|
|
}
|
|
bool PositionClosePartial(
|
|
const ulong ticket, // Position Ticket
|
|
const double volume, // Partial Closing Volume
|
|
const ulong deviation, // Deviation
|
|
const string comment = "" // Comment For Close
|
|
)
|
|
{
|
|
//
|
|
// check stopped
|
|
if (IsStopped(__FUNCTION__))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// for hedging mode only
|
|
if (!IsHedging())
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check position existence
|
|
if (!PositionSelectByTicket(ticket))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
string symbol = PositionGetString(POSITION_SYMBOL);
|
|
|
|
//
|
|
// clean
|
|
ClearStructures();
|
|
|
|
//
|
|
// check filling
|
|
if (!FillingCheck(symbol))
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
// check
|
|
if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
{
|
|
//
|
|
// prepare request for close BUY position
|
|
m_request.type = ORDER_TYPE_SELL;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// prepare request for close SELL position
|
|
m_request.type = ORDER_TYPE_BUY;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
|
}
|
|
|
|
//
|
|
// check volume
|
|
double position_volume = PositionGetDouble(POSITION_VOLUME);
|
|
if (position_volume > volume)
|
|
{
|
|
position_volume = volume;
|
|
}
|
|
|
|
//
|
|
// setting request
|
|
m_request.action = TRADE_ACTION_DEAL;
|
|
m_request.position = ticket;
|
|
m_request.symbol = symbol;
|
|
m_request.volume = position_volume;
|
|
m_request.magic = m_magic;
|
|
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
|
|
|
|
//
|
|
// Specifieng Comment ...
|
|
m_request.comment = comment;
|
|
|
|
//
|
|
// close position
|
|
return (OrderSend(m_request, m_result));
|
|
}
|
|
};
|
|
//
|
|
// END Overrides ...
|
|
//
|
|
|
|
//
|
|
// XSaherElm Account Info Class ...
|
|
class XCAccountInfo
|
|
{
|
|
//
|
|
// Public properties ...
|
|
public:
|
|
//
|
|
// Constructro ...
|
|
void XCAccountInfo()
|
|
{
|
|
//
|
|
mAccountInfo = new CAccountInfo();
|
|
|
|
//
|
|
mInitialBalance = mAccountInfo.Balance();
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
void ~XCAccountInfo()
|
|
{
|
|
//
|
|
mInitialBalance = 0;
|
|
}
|
|
|
|
//
|
|
// User Account ...
|
|
long GetUserAccount()
|
|
{
|
|
return mAccountInfo.Login();
|
|
}
|
|
|
|
//
|
|
// Account Leverage ...
|
|
long GetLeverage()
|
|
{
|
|
return mAccountInfo.Leverage();
|
|
}
|
|
|
|
//
|
|
// Get Trade Expert State ...
|
|
bool CanExpertTrade()
|
|
{
|
|
return mAccountInfo.TradeExpert();
|
|
}
|
|
|
|
//
|
|
// User Account Trade Mopde (ENUM_ACCOUNT_TRADE_MODE) ...
|
|
ENUM_ACCOUNT_TRADE_MODE GetTradeMode()
|
|
{
|
|
return mAccountInfo.TradeMode();
|
|
}
|
|
|
|
//
|
|
// Get Account Balance ...
|
|
double GetBalance()
|
|
{
|
|
return mAccountInfo.Balance();
|
|
}
|
|
|
|
//
|
|
// Get Initial Deposit Balance ...
|
|
double GetInitialBalance()
|
|
{
|
|
return mInitialBalance;
|
|
}
|
|
|
|
//
|
|
// Get the amount of give Credit ...
|
|
double GetCredit()
|
|
{
|
|
return mAccountInfo.Credit();
|
|
}
|
|
|
|
//
|
|
// Get the amount of current Profit on account ...
|
|
double GetProfit()
|
|
{
|
|
return mAccountInfo.Profit();
|
|
}
|
|
|
|
//
|
|
// Get the amount of current Equity on account ...
|
|
double GetEquity()
|
|
{
|
|
return mAccountInfo.Equity();
|
|
}
|
|
|
|
//
|
|
// Get the amount of reserved Margin ...
|
|
double GetMargin()
|
|
{
|
|
return mAccountInfo.Margin();
|
|
}
|
|
|
|
//
|
|
// Get the amount of free Margin ...
|
|
double GetFreeMargin()
|
|
{
|
|
return mAccountInfo.FreeMargin();
|
|
}
|
|
|
|
//
|
|
// Get the Level of Margin ...
|
|
double GetMarginLevel()
|
|
{
|
|
return mAccountInfo.MarginLevel();
|
|
}
|
|
|
|
//
|
|
// Get the Level Of Margin for a Deposit ...
|
|
double GetMarginCall()
|
|
{
|
|
return mAccountInfo.MarginCall();
|
|
}
|
|
|
|
//
|
|
// Get the Level of Margin for Stop out ...
|
|
double GetMarginStopOut()
|
|
{
|
|
return mAccountInfo.MarginStopOut();
|
|
}
|
|
|
|
//
|
|
// Get the Client Name ...
|
|
string GetName()
|
|
{
|
|
return mAccountInfo.Name();
|
|
}
|
|
|
|
//
|
|
// Get the Trade Server Name ...
|
|
string GetServerName()
|
|
{
|
|
return mAccountInfo.Server();
|
|
}
|
|
|
|
//
|
|
// Get deposit Currency Name ...
|
|
string GetCurrency()
|
|
{
|
|
return mAccountInfo.Currency();
|
|
}
|
|
|
|
//
|
|
// Get the Company Name that serves an Account ...
|
|
string GetCompany()
|
|
{
|
|
return mAccountInfo.Company();
|
|
}
|
|
|
|
//
|
|
// Calculate Profits for the current account based on passed parameters ...
|
|
double CalculateTradeProfit(
|
|
const string symbol, // trading symbol
|
|
ENUM_ORDER_TYPE type, // order type
|
|
double volume, // volume
|
|
double entry, // open price
|
|
double exit // close price
|
|
)
|
|
{
|
|
//
|
|
double result = mAccountInfo.OrderProfitCheck(
|
|
symbol,
|
|
type,
|
|
volume,
|
|
entry,
|
|
exit);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate amount of margin which required for trade operation ...
|
|
double CalculateMarging(
|
|
const string symbol, // trading symbol
|
|
ENUM_ORDER_TYPE type, // order type
|
|
double volume, // volume
|
|
double entry // open price
|
|
)
|
|
{
|
|
//
|
|
double result = mAccountInfo.MarginCheck(
|
|
symbol,
|
|
type,
|
|
volume,
|
|
entry);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate amount of free margin left after trade operation ...
|
|
double CalculateFreeMarging(
|
|
const string symbol, // trading symbol
|
|
ENUM_ORDER_TYPE type, // order type
|
|
double volume, // volume
|
|
double entry // open price
|
|
)
|
|
{
|
|
//
|
|
double result = mAccountInfo.FreeMarginCheck(
|
|
symbol,
|
|
type,
|
|
volume,
|
|
entry);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate the Maximum possible volume of trade operation ...
|
|
double CalculateMaxVolume(
|
|
const string symbol, // trading symbol
|
|
ENUM_ORDER_TYPE type, // order type
|
|
double entry, // open price
|
|
double percent = 100 // percent of available margin
|
|
)
|
|
{
|
|
//
|
|
double result = mAccountInfo.MaxLotCheck(
|
|
symbol,
|
|
type,
|
|
entry,
|
|
percent);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Protected properties ...
|
|
protected:
|
|
//
|
|
// Private properties ...
|
|
private:
|
|
//
|
|
// Initial Account Balance ...
|
|
double mInitialBalance;
|
|
|
|
//
|
|
// Account Info ...
|
|
CAccountInfo mAccountInfo;
|
|
};
|
|
|
|
//
|
|
// START Global Definitions: Variables, Properties and etc ...
|
|
//
|
|
//
|
|
// XSaherElm Trade Class ...
|
|
class XCTrade
|
|
{
|
|
//
|
|
// all public features ...
|
|
public:
|
|
//
|
|
// Constructor ...
|
|
void XCTrade(
|
|
string tag, // Specify a Tag for Trader instance
|
|
string symbol, // Specify Trader Symbol
|
|
int slippage, // Specify Slippage
|
|
ulong magicNumber, // Specify Magic Number
|
|
int maxAllowedTrades, // Specify Max Allowed Trades
|
|
double minBallanceForOpenTrades, // Specify Minimum Balance for Open Trades
|
|
double minAllowedFreeMarginForOpenTrades // Specify Minimum Free Marging for Open Trades
|
|
)
|
|
{
|
|
//
|
|
mTag = tag;
|
|
mSymbol = symbol;
|
|
mSlippage = slippage;
|
|
mMagicNumber = magicNumber;
|
|
mMaxAllowedTrades = maxAllowedTrades;
|
|
mMinBallanceForOpenTrades = minBallanceForOpenTrades;
|
|
mMinAllowedFreeMarginForOpenTrades = minAllowedFreeMarginForOpenTrades;
|
|
|
|
//
|
|
mTrader = new XSCTrade();
|
|
|
|
//
|
|
mTrader.SetAsyncMode(false);
|
|
mTrader.SetDeviationInPoints(mSlippage);
|
|
mTrader.SetExpertMagicNumber(mMagicNumber);
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
void ~XCTrade()
|
|
{
|
|
}
|
|
|
|
//
|
|
// START Count ...
|
|
//
|
|
|
|
//
|
|
// Count Open Positions ...
|
|
int Count()
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
int total = PositionsTotal();
|
|
for (int i = 0; i < total; i++)
|
|
{
|
|
//
|
|
if (!mPositionInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Magic() != mMagicNumber)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Symbol() != mSymbol)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
result++;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Count Longs ...
|
|
int CountLongs()
|
|
{
|
|
int result = CountByType(X_SIGNAL_LONG);
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Count Shorts ...
|
|
int CountShorts()
|
|
{
|
|
int result = CountByType(X_SIGNAL_SHORT);
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Count Specific Type Of Positions ...
|
|
int CountByType(ENUM_POSITION_TYPE type)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
int total = PositionsTotal();
|
|
for (int i = 0; i < total; i++)
|
|
{
|
|
//
|
|
if (!mPositionInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Symbol() != mSymbol)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.PositionType() != type)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
result++;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Count By Type ...
|
|
int CountByType(ENUM_X_SIGNAL_TYPE type)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ENUM_POSITION_TYPE mType = POSITION_TYPE_BUY;
|
|
if (type == X_SIGNAL_LONG)
|
|
{
|
|
mType = POSITION_TYPE_BUY;
|
|
}
|
|
else if (type == X_SIGNAL_SHORT)
|
|
{
|
|
mType = POSITION_TYPE_SELL;
|
|
}
|
|
|
|
//
|
|
int result = CountByType(mType);
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Count ...
|
|
//
|
|
|
|
//
|
|
// START Retrieve ...
|
|
//
|
|
|
|
//
|
|
// Retrieve Specified Position by it's Ticket ...
|
|
XSignal GetSignal(
|
|
ulong ticket // Specified Ticket
|
|
)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
result = PositionToSignal(ticket);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve All Positions ...
|
|
void GetSignals(
|
|
XSignal &result[] // Holds Result ...
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
int totalPositions = PositionsTotal();
|
|
for (int i = 0; i < totalPositions; i++)
|
|
{
|
|
//
|
|
if (!mPositionInfo.SelectByIndex(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Magic() != mMagicNumber)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (mPositionInfo.Symbol() != mSymbol)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Position to Signal ...
|
|
XSignal signal = PositionToSignal(i);
|
|
Add(
|
|
signal,
|
|
result);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Retrieve Type Specific Signals ...
|
|
void GetSignals(
|
|
XSignal &result[], // Holds Result ...
|
|
ENUM_X_SIGNAL_TYPE type // Specified Sginal Type ...
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(signals);
|
|
|
|
//
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Loop ...
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
if (type == X_SIGNAL_UNKNOWN)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
else if (iSignal.type == type)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get All Trades Which Candle Passed after Open ...
|
|
void GetOldSignals(
|
|
XSignal &result[], // Holds Result
|
|
int life, // Max Candle Passed after Trades Open
|
|
ENUM_TIMEFRAMES period = NULL, // Calculate Candles based on time frame
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
// Normalize Period ...
|
|
if (period == NULL)
|
|
{
|
|
period = _Period;
|
|
}
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(
|
|
signals,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Current Time Frame Candle Index ...
|
|
int currentBarIndex = 0;
|
|
|
|
//
|
|
// Loop through Positions ...
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
// Retrieve Trade Open Bar Index based on Current Period ...
|
|
int tradeOpenBarIndex = iBarShift(
|
|
mSymbol,
|
|
period,
|
|
iSignal.time);
|
|
|
|
//
|
|
// Calculate Trade Life ...
|
|
int diff = MathAbs(currentBarIndex - tradeOpenBarIndex);
|
|
|
|
//
|
|
bool isPassed = diff >= life;
|
|
if (isPassed)
|
|
{
|
|
//
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get In Profit Signals ...
|
|
void GetInProfitSignals(
|
|
XSignal &result[], // Holds Result
|
|
double minProfit = 0, // Minimum Profit To Close Trades
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(
|
|
signals,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Loop ...
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
bool isPassed = minProfit <= 0 && iSignal.profit > 0
|
|
? true
|
|
: iSignal.profit >= minProfit;
|
|
if (isPassed)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get In Drawdown Signals ...
|
|
void GetInDrawdownSignals(
|
|
XSignal &result[], // Holds Result
|
|
double maxDrawDown = 0, // Minimum Profit To Close Trades
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(
|
|
signals,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Loop ...
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
bool isPassed = maxDrawDown <= 0 && iSignal.profit < 0
|
|
? true
|
|
: iSignal.profit >= (-1 * maxDrawDown);
|
|
if (isPassed)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// END Retrieve ...
|
|
//
|
|
|
|
//
|
|
// START Signal Execution Actions ...
|
|
//
|
|
|
|
//
|
|
// Execute a Signal in Market ...
|
|
ENUM_EXECUTION_SIGNAL_RESULT ExecuteSignal(
|
|
XSignal &signal, // Specified Signal
|
|
bool ignoreTPSL = false, // Ignore Signal TP and SL for Virtal Handling
|
|
bool ignoreNumberOfTraes = true, // Ignore Number of Trades
|
|
bool checkAccountConditions = true // Check Account Balance, Equity and Free Margin for Signal Execution
|
|
)
|
|
{
|
|
//
|
|
ENUM_EXECUTION_SIGNAL_RESULT result = X_TRADER_UNKNOWN_ERROR;
|
|
|
|
//
|
|
// Validate Signal ...
|
|
bool isValid = IsValidSignal(signal);
|
|
if (!isValid)
|
|
{
|
|
//
|
|
result = X_TRADER_INVALID_SIGNAL_ERROR;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Account Equity ...
|
|
bool isEquityPassed = !checkAccountConditions
|
|
? true
|
|
: IsEquityReadyForTrade();
|
|
if (!isEquityPassed)
|
|
{
|
|
//
|
|
result = X_TRADER_NOT_ENOUGH_EQUITY_ERROR;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Account Balance ...
|
|
bool isBalancePassed = !checkAccountConditions
|
|
? true
|
|
: IsBalanceReadyForTrade();
|
|
if (!isBalancePassed)
|
|
{
|
|
//
|
|
result = X_TRADER_NOT_ENOUGH_BALANCE_ERROR;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Account FreeMargin ...
|
|
bool isFreeMarginPassed = !checkAccountConditions
|
|
? true
|
|
: IsFreeMarginReadyForTrade();
|
|
if (!isFreeMarginPassed)
|
|
{
|
|
//
|
|
result = X_TRADER_NOT_ENOUGH_MARIGIN_ERROR;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Number of Open Trades ...
|
|
int openTradesCount = Count();
|
|
bool isTradeCountPassed = ignoreNumberOfTraes
|
|
? true
|
|
: openTradesCount < mMaxAllowedTrades;
|
|
if (!isTradeCountPassed)
|
|
{
|
|
//
|
|
result = X_TRADER_MAX_TRADES_REACHED_ERROR;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool isExecuted =
|
|
signal.type == X_SIGNAL_LONG
|
|
? Buy(
|
|
signal.volume,
|
|
signal.entry,
|
|
ignoreTPSL
|
|
? 0
|
|
: signal.sl,
|
|
ignoreTPSL
|
|
? 0
|
|
: signal.tp,
|
|
signal.comment)
|
|
: signal.type == X_SIGNAL_SHORT
|
|
? Sell(
|
|
signal.volume,
|
|
signal.entry,
|
|
ignoreTPSL
|
|
? 0
|
|
: signal.sl,
|
|
ignoreTPSL
|
|
? 0
|
|
: signal.tp,
|
|
signal.comment)
|
|
: false;
|
|
if (isExecuted)
|
|
{
|
|
//
|
|
ulong ticket = PositionGetTicket(PositionsTotal() - 1);
|
|
|
|
//
|
|
signal.id = ticket;
|
|
signal.ticket = ticket;
|
|
|
|
//
|
|
AddOrUpdateSignalInfo(signal);
|
|
|
|
//
|
|
result = X_TRADER_SUCCEED_EXECUTION;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// This Function, Handling Following Works on Open Positions:
|
|
// - Close On SL if Reached ...
|
|
// - Close On TP if Reached ...
|
|
// - Make Risk Free Trades ...
|
|
void HandleSignals(
|
|
XTraderHandlerResult &result, // Holds Result
|
|
bool ignoreTPSL = true, // Ignore Signal TP and SL for Virtal Handling
|
|
bool ignoreRiskFree = false // Ignore Risk Free Trdaes
|
|
)
|
|
{
|
|
//
|
|
// Cleanup Result ...
|
|
CleanBuffer(result.sl);
|
|
CleanBuffer(result.tp);
|
|
CleanBuffer(result.rf);
|
|
|
|
//
|
|
// Reading Open Positions ...
|
|
XSignal signals[];
|
|
GetSignals(signals);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
double deviation = mSlippage * GetPoints(mSymbol);
|
|
|
|
//
|
|
// Loop ...
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
double entry = GetEntry(iSignal.symbol, iSignal.type);
|
|
double exit = GetExit(iSignal.symbol, iSignal.type);
|
|
double appliedTPPrice =
|
|
iSignal.tp > 0
|
|
? iSignal.type == X_SIGNAL_LONG
|
|
? iSignal.tp - deviation
|
|
: iSignal.tp + deviation
|
|
: 0;
|
|
double appliedSLPrice =
|
|
iSignal.sl > 0
|
|
? iSignal.type == X_SIGNAL_LONG
|
|
? iSignal.sl + deviation
|
|
: iSignal.sl - deviation
|
|
: 0;
|
|
|
|
//
|
|
// Handle SL ...
|
|
if (
|
|
//
|
|
!ignoreTPSL
|
|
//
|
|
&&
|
|
//
|
|
iSignal.sl > 0
|
|
//
|
|
&&
|
|
//
|
|
exit <= appliedSLPrice
|
|
//
|
|
)
|
|
{
|
|
//
|
|
string comment = GenerateSignalSLComment(iSignal);
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iSignal.ticket,
|
|
comment);
|
|
if (isClosed)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result.sl);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handle TP ...
|
|
if (
|
|
//
|
|
!ignoreTPSL
|
|
//
|
|
&&
|
|
//
|
|
iSignal.tp > 0
|
|
//
|
|
&&
|
|
//
|
|
exit >= appliedTPPrice
|
|
//
|
|
)
|
|
{
|
|
//
|
|
string comment = GenerateSignalTPComment(iSignal);
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iSignal.ticket,
|
|
comment);
|
|
if (isClosed)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result.tp);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handle Risk Free ...
|
|
if (
|
|
//
|
|
!ignoreRiskFree
|
|
//
|
|
&&
|
|
//
|
|
iSignal.riskFreeStep > 0
|
|
//
|
|
&&
|
|
//
|
|
iSignal.riskFreeRate > 0
|
|
//
|
|
)
|
|
{
|
|
//
|
|
// Base Price is ENTRY, then Last Level, in each level SL is Last Level ...
|
|
int level = 1;
|
|
bool isLong = iSignal.type == X_SIGNAL_LONG;
|
|
double stepPrice = PipsToPrice(iSignal.riskFreeStep);
|
|
double basePrice =
|
|
isLong
|
|
? iSignal.sl > iSignal.entry ? iSignal.sl : iSignal.entry
|
|
: iSignal.sl < iSignal.entry ? iSignal.sl
|
|
: iSignal.entry;
|
|
double priceLevel = (exit - basePrice) / stepPrice;
|
|
|
|
//
|
|
if (priceLevel > 1)
|
|
{
|
|
//
|
|
while (priceLevel > level && priceLevel > level + 1)
|
|
{
|
|
level++;
|
|
}
|
|
|
|
//
|
|
double baseRiskFreeLevelExitPrice = (level * stepPrice);
|
|
double riskFreeLevelExitPrice =
|
|
isLong
|
|
? (basePrice + baseRiskFreeLevelExitPrice) - deviation
|
|
: (basePrice - baseRiskFreeLevelExitPrice) + deviation;
|
|
|
|
//
|
|
bool canRiskFree =
|
|
isLong
|
|
? exit >= riskFreeLevelExitPrice
|
|
: exit <= riskFreeLevelExitPrice;
|
|
|
|
//
|
|
if (canRiskFree)
|
|
{
|
|
//
|
|
double volume = iSignal.volume;
|
|
double riskFreeRate = iSignal.riskFreeRate;
|
|
double riskFreeVolume = riskFreeRate * volume;
|
|
riskFreeVolume = NormalizeVolume(riskFreeVolume);
|
|
|
|
//
|
|
double spread = GetSpread(iSignal.symbol);
|
|
double slDistance = spread;
|
|
double sl =
|
|
isLong
|
|
? riskFreeLevelExitPrice - slDistance
|
|
: riskFreeLevelExitPrice + slDistance;
|
|
|
|
//
|
|
double tp = iSignal.tp += stepPrice;
|
|
|
|
//
|
|
string comment = GenerateSignalRFComment(iSignal, (int)level);
|
|
|
|
//
|
|
ResetLastError();
|
|
|
|
//
|
|
bool isClosedPartial = ClosePartial(
|
|
iSignal.ticket,
|
|
riskFreeVolume,
|
|
comment);
|
|
|
|
//
|
|
if (isClosedPartial)
|
|
{
|
|
//
|
|
bool isModified = Modify(
|
|
iSignal.ticket,
|
|
sl,
|
|
tp);
|
|
|
|
//
|
|
if (isModified)
|
|
{
|
|
//
|
|
Add(
|
|
iSignal,
|
|
result.rf);
|
|
|
|
//
|
|
AddOrUpdateSignalInfo(iSignal);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// //
|
|
// // Check TP Step Price ...
|
|
// double stepPrice = PipsToPrice(iSignal.riskFreeStep);
|
|
// double level = MathAbs(exit - iSignal.entry) / stepPrice;
|
|
// double roundedLevel = MathRound(level);
|
|
// if (level > 1 && (roundedLevel - level) < 0)
|
|
// {
|
|
// //
|
|
// level = roundedLevel;
|
|
|
|
// //
|
|
// double basePrice = iSignal.sl > 0 ? iSignal.sl : iSignal.entry;
|
|
// double step = (level * stepPrice);
|
|
// step =
|
|
// iSignal.type == X_SIGNAL_LONG
|
|
// ? (basePrice + step) - deviation
|
|
// : (basePrice - step) + deviation;
|
|
|
|
// //
|
|
// bool canRiskFree =
|
|
// iSignal.type == X_SIGNAL_LONG
|
|
// ? exit >= step
|
|
// : exit <= step;
|
|
|
|
// //
|
|
// if (canRiskFree)
|
|
// {
|
|
// }
|
|
// }
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close All Trades ...
|
|
void CloseSignals(
|
|
XSignal &result[], // Holds Result
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(
|
|
signals,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
string comment = GenerateSignalForceCloseComment(iSignal);
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iSignal.ticket,
|
|
comment);
|
|
if (isClosed)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close Pyramid Signals ...
|
|
void CloseInPyramidSignals(
|
|
XSignal &result[], // Holds Result
|
|
double pyramid = 0, // Close Pyramid Profit
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetSignals(
|
|
signals,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
double profit = 0;
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
profit += iSignal.profit;
|
|
}
|
|
|
|
//
|
|
// Normalize Pyramid ...
|
|
if (pyramid < 0)
|
|
{
|
|
pyramid = 0;
|
|
}
|
|
|
|
//
|
|
if (profit > pyramid)
|
|
{
|
|
CloseSignals(result);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close All Trades Which Candle Passed after Open ...
|
|
void CloseOldSignals(
|
|
XSignal &result[], // Holds Result
|
|
int life, // Max Candle Passed after Trades Open
|
|
ENUM_TIMEFRAMES period = NULL, // Calculate Candles based on time frame
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetOldSignals(
|
|
signals,
|
|
life,
|
|
period,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
string comment = GenerateSignalAgeComment(iSignal);
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iSignal.ticket,
|
|
comment);
|
|
if (isClosed)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close In Profit Signals ...
|
|
void CloseInProfitSignals(
|
|
XSignal &result[], // Holds Result
|
|
double minProfit = 0, // Minimum Profit To Close Trades
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetInProfitSignals(
|
|
signals,
|
|
minProfit,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
string comment = GenerateSignalProfitComment(iSignal);
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iSignal.ticket,
|
|
comment);
|
|
if (isClosed)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close In Drawdown Signals ...
|
|
void CloseInDrawdownSignals(
|
|
XSignal &result[], // Holds Result
|
|
double maxDrawDown = 0, // Minimum Profit To Close Trades
|
|
ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions
|
|
)
|
|
{
|
|
//
|
|
CleanBuffer(result);
|
|
|
|
//
|
|
XSignal signals[];
|
|
GetInDrawdownSignals(
|
|
signals,
|
|
maxDrawDown,
|
|
type);
|
|
int signalsCount = ArraySize(signals);
|
|
if (signalsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < signalsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = signals[i];
|
|
|
|
//
|
|
string comment = GenerateSignalProfitComment(iSignal);
|
|
|
|
//
|
|
bool isClosed = Close(
|
|
iSignal.ticket,
|
|
comment);
|
|
if (isClosed)
|
|
{
|
|
Add(
|
|
iSignal,
|
|
result);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// END Signal Execution Actions ...
|
|
//
|
|
|
|
//
|
|
// START Actions ...
|
|
//
|
|
|
|
//
|
|
// Force Open a Buy/Long Position ...
|
|
bool Buy(
|
|
double volume, // position volume
|
|
double price, // execution price
|
|
double sl = 0, // stop loss price
|
|
double tp = 0, // take profit price
|
|
const string comment = "" // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = mTrader.Buy(
|
|
volume,
|
|
mSymbol,
|
|
price,
|
|
sl,
|
|
tp,
|
|
comment);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Force Open a Sell/Short Position ...
|
|
bool Sell(
|
|
double volume, // position volume
|
|
double price, // execution price
|
|
double sl = 0, // stop loss price
|
|
double tp = 0, // take profit price
|
|
const string comment = "" // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = mTrader.Sell(
|
|
volume,
|
|
mSymbol,
|
|
price,
|
|
sl,
|
|
tp,
|
|
comment);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Modify Position ...
|
|
bool Modify(
|
|
const ulong ticket, // position ticket
|
|
double sl = 0, // stop loss price
|
|
double tp = 0, // take profit
|
|
const string comment = "" // comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (tp <= 0 && sl <= 0)
|
|
{
|
|
result = false;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = mTrader.PositionModify(
|
|
ticket,
|
|
sl,
|
|
tp,
|
|
comment);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close Position By Ticket ...
|
|
bool Close(
|
|
ulong ticket, // Position Ticket
|
|
string comment = "" // Close Position By Specific Comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Try to Select Position ...
|
|
if (!mPositionInfo.SelectByTicket(ticket))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close Position By Specific Comment ...
|
|
result = mTrader.PositionClose(
|
|
ticket,
|
|
mSlippage,
|
|
comment);
|
|
if (result)
|
|
{
|
|
RemoveSignal(ticket);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Close Partial By Ticket ...
|
|
bool ClosePartial(
|
|
ulong ticket, // Position Ticket
|
|
double volume, // Volume Size for Partial Closing
|
|
string comment = "" // Close Position By Specific Comment
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (!mPositionInfo.SelectByTicket(ticket))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = mTrader.PositionClosePartial(
|
|
ticket,
|
|
volume,
|
|
mSlippage,
|
|
comment);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Actions ...
|
|
//
|
|
|
|
//
|
|
// START Account Related ...
|
|
//
|
|
|
|
//
|
|
// Check Account Has Minimum Balance For Trade ...
|
|
bool IsBalanceReadyForTrade()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = mAccountInfo.GetBalance() >= mMinBallanceForOpenTrades;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Free Margin Has Enough For Trade ...
|
|
bool IsFreeMarginReadyForTrade()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
double freeMargin = mAccountInfo.GetFreeMargin();
|
|
|
|
//
|
|
result = freeMargin >= mMinAllowedFreeMarginForOpenTrades;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Equity Has Enough For Trade ...
|
|
bool IsEquityReadyForTrade()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
double equity = mAccountInfo.GetEquity();
|
|
|
|
//
|
|
result = equity >= mMinAllowedFreeMarginForOpenTrades;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Account Related ...
|
|
//
|
|
|
|
//
|
|
// START Signal Related ...
|
|
//
|
|
|
|
//
|
|
// Validate Signal ...
|
|
bool IsValidSignal(
|
|
XSignal &signal // Specified Signal for Execution
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Signal Type ...
|
|
result = signal.type != X_SIGNAL_UNKNOWN;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Symbol ...
|
|
result = StringLen(signal.symbol) == 0 ? true : signal.symbol == mSymbol;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check magic number ...
|
|
result = signal.magicNumber <= 0 ? true : signal.magicNumber == mMagicNumber;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Entry ...
|
|
result = signal.entry > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check SL ...
|
|
result = signal.sl == 0 ? true : signal.type == X_SIGNAL_LONG ? signal.sl < signal.entry
|
|
: signal.sl > signal.entry;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check TP ...
|
|
result = signal.tp == 0 ? true : signal.type == X_SIGNAL_LONG ? signal.tp > signal.entry
|
|
: signal.tp < signal.entry;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Volume ...
|
|
result = signal.volume > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Time ...
|
|
result = signal.time > 0;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get Signal InDirection (Reversal) Type ...
|
|
ENUM_X_SIGNAL_TYPE GetSignalIndirectionType(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
ENUM_X_SIGNAL_TYPE result = X_SIGNAL_UNKNOWN;
|
|
|
|
//
|
|
switch (signal.type)
|
|
{
|
|
//
|
|
case X_SIGNAL_LONG:
|
|
result = X_SIGNAL_SHORT;
|
|
break;
|
|
|
|
//
|
|
case X_SIGNAL_SHORT:
|
|
result = X_SIGNAL_LONG;
|
|
break;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Calculate SignalAge ...
|
|
int GetSignalAge(
|
|
datetime time // Signal Execution Time
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (time <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int sIndex = iBarShift(
|
|
mSymbol,
|
|
_Period,
|
|
time);
|
|
|
|
//
|
|
datetime cTime = iTime(
|
|
mSymbol,
|
|
_Period,
|
|
0);
|
|
int cIndex = iBarShift(
|
|
mSymbol,
|
|
_Period,
|
|
cTime);
|
|
|
|
//
|
|
result = MathAbs(cIndex - sIndex);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
int GetSignalAge(
|
|
datetime time, // Signal Execution Time
|
|
ENUM_TIMEFRAMES period // Specify Time Frame
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (time <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int sIndex = iBarShift(
|
|
mSymbol,
|
|
period,
|
|
time);
|
|
|
|
//
|
|
datetime cTime = iTime(
|
|
mSymbol,
|
|
period,
|
|
0);
|
|
int cIndex = iBarShift(
|
|
mSymbol,
|
|
_Period,
|
|
cTime);
|
|
|
|
//
|
|
result = MathAbs(cIndex - sIndex);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
int GetSignalAge(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (
|
|
signal.time <= 0 ||
|
|
!IsValid(signal, mMagicNumber))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = GetSignalAge(signal.time);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
int GetSignalAge(
|
|
XSignal &signal, // Specify Signal
|
|
ENUM_TIMEFRAMES period // Specify Time Frame
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (
|
|
signal.time <= 0 ||
|
|
!IsValid(signal, mMagicNumber))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = GetSignalAge(
|
|
signal.time,
|
|
period);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Prepare a Signall ...
|
|
XSignal GenerateSignal(
|
|
ENUM_X_SIGNAL_TYPE type, // Signal Type
|
|
double tp = 0, // Take Profit
|
|
double sl = 0, // Stop Loss
|
|
double volume = 0, // Volume
|
|
double riskFreeStep = 0, // Risk Free Step Price
|
|
double riskFreeRate = 0 // Risk Free Rate
|
|
)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (type == X_SIGNAL_UNKNOWN)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Signal Type ...
|
|
bool isLong = type == X_SIGNAL_LONG;
|
|
|
|
//
|
|
// Retrieve entry Price ...
|
|
double entry = GetEntry(mSymbol, type);
|
|
double spread = GetSpread(mSymbol);
|
|
|
|
//
|
|
result.tp = tp;
|
|
result.sl = sl;
|
|
result.type = type;
|
|
result.entry = entry;
|
|
result.symbol = mSymbol;
|
|
result.magicNumber = mMagicNumber;
|
|
result.riskFreeStep = riskFreeStep;
|
|
result.riskFreeRate = riskFreeRate;
|
|
result.time = iTime(mSymbol, _Period, 0);
|
|
result.volume = NormalizeVolume(volume, mSymbol);
|
|
|
|
//
|
|
// Since we Use signal instance to Generate Comment, this must be at the end ...
|
|
result.comment = GenerateSignalComment(result);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Comments for Specific Signal ...
|
|
string GenerateSignalComment(
|
|
ENUM_X_SIGNAL_TYPE type, // Specify Kind Signal Type
|
|
double volume // Signal Volume
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
string typeStr = GetSignalType(type);
|
|
if (
|
|
volume <= 0 ||
|
|
StringLen(typeStr) == 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
volume = NormalizeVolume(volume, mSymbol);
|
|
|
|
//
|
|
result = mTag + "_" + typeStr;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
string GenerateSignalComment(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = GenerateSignalComment(
|
|
signal.type,
|
|
signal.volume
|
|
//
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Signal RF TP Comment ...
|
|
string GenerateSignalRFComment(
|
|
XSignal &signal, // Specified Signal
|
|
int level // Which Step of RFS
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Signal and it's Ticket ...
|
|
if (!IsValidSignal(signal) || signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double exit = GetExit(signal.symbol, signal.type);
|
|
|
|
//
|
|
result =
|
|
//
|
|
X_SIGNAL_RISKFREE_STEP + "(" + (string)signal.ticket + ")_" + (string)level + ": " + string(exit);
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Signal TP Comment ...
|
|
string GenerateSignalTPComment(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Signal and it's Ticket ...
|
|
if (!IsValidSignal(signal) || signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double exit = GetExit(signal.symbol, signal.type);
|
|
|
|
//
|
|
result =
|
|
//
|
|
X_SIGNAL_TP + "(" + (string)signal.ticket + "): " + string(exit);
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Signal SL Comment ...
|
|
string GenerateSignalSLComment(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Signal and it's Ticket ...
|
|
if (!IsValidSignal(signal) || signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
double exit = GetExit(signal.symbol, signal.type);
|
|
|
|
//
|
|
result =
|
|
//
|
|
X_SIGNAL_SL + "(" + (string)signal.ticket + "): " + string(exit);
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Signal Profit Comment ...
|
|
string GenerateSignalProfitComment(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Signal and it's Ticket ...
|
|
if (!IsValidSignal(signal) || signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result =
|
|
//
|
|
"Profit(" + (string)signal.ticket + "): " + string(signal.profit);
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Signal Force Close Comment ...
|
|
string GenerateSignalForceCloseComment(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Signal and it's Ticket ...
|
|
if (!IsValidSignal(signal) || signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result =
|
|
//
|
|
"FC(" + (string)signal.ticket + "): " + string(signal.profit);
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Signal Age Comment ...
|
|
string GenerateSignalAgeComment(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Signal and it's Ticket ...
|
|
if (!IsValidSignal(signal) || signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int age = GetSignalAge(signal);
|
|
|
|
//
|
|
result =
|
|
//
|
|
"(" + (string)signal.ticket + ") A: " + (string)age + ", P:" + string(signal.profit);
|
|
//
|
|
;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate a Signal Object name for Draw Events On Chart ...
|
|
string GenerateSignalObjectName(
|
|
ulong ticket // Specify Ticket
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// TICKET ...
|
|
// it is very importants since they hold position info ...
|
|
result += "T(" + (string)ticket + ")";
|
|
|
|
//
|
|
return result;
|
|
}
|
|
string GenerateSignalObjectName(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (!IsValid(signal, mMagicNumber))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = GenerateSignalObjectName(signal.ticket);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Genrate a Signal Event Object Name ...
|
|
string GenerateSignalEventObjectName(
|
|
ulong ticket // Specify Ticket
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
result = GenerateSignalObjectName(ticket) + "_Event";
|
|
|
|
//
|
|
return result;
|
|
}
|
|
string GenerateSignalEventObjectName(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
result = GenerateSignalEventObjectName(signal.ticket);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Convert a Signal to String Representation ...
|
|
string ToString(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
if (!IsValid(signal, mMagicNumber) || signal.ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// TICKET ...
|
|
// it is very importants since they hold position info ...
|
|
result += X_SIGNAL_TICKET + "(" + (string)signal.ticket + ")";
|
|
|
|
//
|
|
// TYPE ...
|
|
string typeStr = GetSignalType(signal.type);
|
|
result += "_" + X_SIGNAL_TYPE + "(" + typeStr + ")";
|
|
|
|
//
|
|
// TP ...
|
|
result += "_" + X_SIGNAL_TP + "(" + (string)signal.tp + ")";
|
|
|
|
//
|
|
// SL ...
|
|
result += "_" + X_SIGNAL_SL + "(" + (string)signal.sl + ")";
|
|
|
|
//
|
|
// RFS ...
|
|
result += "_" + X_SIGNAL_RISKFREE_STEP + "(" + (string)signal.riskFreeStep + ")";
|
|
|
|
//
|
|
// RFR ...
|
|
result += "_" + X_SIGNAL_RISKFREE_RATE + "(" + (string)signal.riskFreeRate + ")";
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Convert an string to signal ...
|
|
XSignal FromString(
|
|
string represent // String Represenation of a Signal
|
|
)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (StringLen(represent) == 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// TICKET ...
|
|
string ticketStr = ExtractString(
|
|
represent,
|
|
X_SIGNAL_TICKET + "(",
|
|
")");
|
|
if (StringLen(ticketStr) > 0)
|
|
{
|
|
result.ticket = (ulong)ticketStr;
|
|
}
|
|
|
|
//
|
|
// TYPE ...
|
|
string typeStr = ExtractString(
|
|
represent,
|
|
"_" + X_SIGNAL_TYPE + "(",
|
|
")");
|
|
if (StringLen(typeStr) > 0)
|
|
{
|
|
//
|
|
ENUM_X_SIGNAL_TYPE type = GetSignalType(typeStr);
|
|
result.type = type;
|
|
}
|
|
|
|
//
|
|
// TP ...
|
|
string tpStr = ExtractString(
|
|
represent,
|
|
"_" + X_SIGNAL_TP + "(",
|
|
")");
|
|
if (StringLen(tpStr) > 0)
|
|
{
|
|
//
|
|
double tp = (double)tpStr;
|
|
result.tp = tp;
|
|
}
|
|
|
|
//
|
|
// SL ...
|
|
string slStr = ExtractString(
|
|
represent,
|
|
"_" + X_SIGNAL_SL + "(",
|
|
")");
|
|
if (StringLen(slStr) > 0)
|
|
{
|
|
//
|
|
double sl = (double)slStr;
|
|
result.sl = sl;
|
|
}
|
|
|
|
//
|
|
// RFS ...
|
|
string rfsStr = ExtractString(
|
|
represent,
|
|
"_" + X_SIGNAL_RISKFREE_STEP + "(",
|
|
")");
|
|
if (StringLen(rfsStr) > 0)
|
|
{
|
|
//
|
|
double rfs = (double)rfsStr;
|
|
result.riskFreeStep = rfs;
|
|
}
|
|
|
|
//
|
|
// RFR ...
|
|
string rfrStr = ExtractString(
|
|
represent,
|
|
"_" + X_SIGNAL_RISKFREE_RATE + "(",
|
|
")");
|
|
if (StringLen(rfrStr) > 0)
|
|
{
|
|
//
|
|
double rfr = (double)rfrStr;
|
|
result.riskFreeRate = rfr;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
void FromString(
|
|
XSignal &result, // Holds Result
|
|
string represent // String Represenation of a Signal
|
|
)
|
|
{
|
|
//
|
|
// Validate Arg ...
|
|
if (StringLen(represent) == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
XSignal fs = FromString(represent);
|
|
|
|
//
|
|
// Fillisg Result by Retrieved Values ...
|
|
result.tp = fs.tp;
|
|
result.sl = fs.sl;
|
|
result.type = fs.type;
|
|
result.ticket = fs.ticket;
|
|
result.riskFreeStep = fs.riskFreeStep;
|
|
result.riskFreeRate = fs.riskFreeRate;
|
|
}
|
|
|
|
//
|
|
// END Signal Related ...
|
|
//
|
|
|
|
//
|
|
// all protected features ...
|
|
protected:
|
|
//
|
|
// all private features ...
|
|
private:
|
|
//
|
|
// Specified Unique Tag ...
|
|
string mTag;
|
|
|
|
//
|
|
// which Symbol ...
|
|
string mSymbol;
|
|
|
|
//
|
|
// using deviation, it must be in Point ...
|
|
int mSlippage;
|
|
|
|
//
|
|
// using magic number ...
|
|
ulong mMagicNumber;
|
|
|
|
//
|
|
// Specify Max Allowed Trades
|
|
int mMaxAllowedTrades;
|
|
|
|
//
|
|
// Minimum Free Marging for Open Trades
|
|
double mMinAllowedFreeMarginForOpenTrades;
|
|
|
|
//
|
|
// Minimum Balance for Open Trades
|
|
double mMinBallanceForOpenTrades;
|
|
|
|
//
|
|
// using CTrade instance ...
|
|
XSCTrade mTrader;
|
|
|
|
//
|
|
// An Instance Of XCAccount Info for Handling Account Related Actions ...
|
|
XCAccountInfo mAccountInfo;
|
|
|
|
//
|
|
// using PositionInfo instance ...
|
|
CPositionInfo mPositionInfo;
|
|
|
|
//
|
|
// START Signal Private ...
|
|
//
|
|
|
|
//
|
|
// Add Or Update Signal ...
|
|
void AddOrUpdateSignalInfo(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string name = GenerateSignalEventObjectName(signal);
|
|
|
|
//
|
|
bool isExists = IsDrawExists(name);
|
|
if (isExists)
|
|
{
|
|
RemoveSignal(signal.ticket);
|
|
}
|
|
|
|
//
|
|
DrawSignalEvent(signal);
|
|
}
|
|
|
|
//
|
|
// Retrieve Signal String From Chart Event ...
|
|
string RetrieveSignalContent(
|
|
ulong ticket, // Specify Position Ticket
|
|
int chartId = -1, // Specify Chart ID
|
|
int subWindow = -1 // Specify SubWindow ID
|
|
)
|
|
{
|
|
//
|
|
string result = "";
|
|
|
|
//
|
|
// Normalize Chart and SubWindow ID ...
|
|
if (chartId == -1)
|
|
{
|
|
chartId = 0;
|
|
}
|
|
if (subWindow == -1)
|
|
{
|
|
subWindow = 0;
|
|
}
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Get Signal Event Name ...
|
|
string sObjName = GenerateSignalEventObjectName(ticket);
|
|
if (StringLen(sObjName) == 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Signal Event Exists ...
|
|
bool isExists = IsDrawExists(sObjName);
|
|
if (!isExists)
|
|
{
|
|
return result;
|
|
}
|
|
//
|
|
// Loop Through Objects ...
|
|
int count = ObjectsTotal(
|
|
chartId,
|
|
subWindow);
|
|
for (int i = count - 1; i >= 0; i--)
|
|
{
|
|
//
|
|
// Retrieve Object Name ...
|
|
string objectName = ObjectName(chartId, i, subWindow);
|
|
|
|
//
|
|
// Find Signal Ticket Position in Object Name ...
|
|
int stPos = StringFind(
|
|
objectName,
|
|
sObjName);
|
|
if (stPos < 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Retrieve Object Type and Check it Event ...
|
|
int oType = (int)ObjectGetInteger(chartId, objectName, OBJPROP_TYPE);
|
|
if (oType != OBJ_EVENT)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Retrieve Object Text as String Representation OF Signal ...
|
|
result = ObjectGetString(chartId, objectName, OBJPROP_TEXT);
|
|
break;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
XSignal RetrieveSignal(
|
|
ulong ticket // Specify Position Ticket
|
|
)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
if (ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string signalContent = RetrieveSignalContent(ticket);
|
|
result = FromString(
|
|
signalContent);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
void RetrieveSignal(
|
|
XSignal &signal // Specified Signal
|
|
)
|
|
{
|
|
//
|
|
string signalContent = RetrieveSignalContent(signal.ticket);
|
|
|
|
//
|
|
FromString(
|
|
signal,
|
|
signalContent);
|
|
}
|
|
|
|
//
|
|
// Draw a Signal on Screen ...
|
|
void DrawSignalEvent(
|
|
XSignal &signal, // Specify Draw which Signal on Screen
|
|
color longColor = clrAqua, // Long Signal Object Color
|
|
color shortColor = clrFuchsia, // Short Signal Object Color
|
|
int chartId = -1, // Specify Chart ID
|
|
int subWindow = -1 // Specify SubWindow ID
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
signal.ticket <= 0 ||
|
|
!IsValid(signal, mMagicNumber) ||
|
|
signal.type == X_SIGNAL_UNKNOWN)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Normalize Chart and SubWindow ID ...
|
|
if (chartId == -1)
|
|
{
|
|
chartId = 0;
|
|
}
|
|
if (subWindow == -1)
|
|
{
|
|
subWindow = 0;
|
|
}
|
|
|
|
//
|
|
bool isLong = signal.type == X_SIGNAL_LONG;
|
|
|
|
//
|
|
// Simply Draw an Arrow on Chart for Representing Signal Info ...
|
|
string name = GenerateSignalEventObjectName(signal);
|
|
string content = ToString(signal);
|
|
if (StringLen(content) == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
color signalColor = isLong ? longColor : shortColor;
|
|
|
|
//
|
|
// Create an Event On Chart ...
|
|
bool isCreated = EventCreate(
|
|
chartId,
|
|
name,
|
|
subWindow,
|
|
content,
|
|
signal.time,
|
|
signalColor);
|
|
if (isCreated)
|
|
{
|
|
//
|
|
DrawSignalShape(
|
|
signal,
|
|
longColor,
|
|
shortColor);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove Signal Object from Chart ...
|
|
bool RemoveSignal(
|
|
ulong ticket, // Specify Position Ticket
|
|
int chartId = -1 // Specify Chart ID
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Arg ...
|
|
if (ticket <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Normalize Chart Id ...
|
|
if (chartId <= 0)
|
|
{
|
|
chartId = 0;
|
|
}
|
|
|
|
//
|
|
// Generate Signal Object Name ...
|
|
string signalObjectName = GenerateSignalEventObjectName(ticket);
|
|
|
|
//
|
|
// Remove Object from Chart ...
|
|
result = RemoveDraw(signalObjectName);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
bool RemoveSignal(
|
|
XSignal &signal, // Specify Signal
|
|
int chartId = -1 // Specify Chart ID
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = RemoveSignal(
|
|
signal.ticket,
|
|
chartId);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Converts a Position to Signal ...
|
|
|
|
//
|
|
// Convert Position to Signal by Index ...
|
|
XSignal PositionToSignal(int index)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
if (!mPositionInfo.SelectByIndex(index))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ulong ticket = mPositionInfo.Ticket();
|
|
result = PositionToSignal(ticket);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Position To Signal by Ticket ...
|
|
XSignal PositionToSignal(ulong ticket)
|
|
{
|
|
//
|
|
XSignal result = {};
|
|
|
|
//
|
|
if (!mPositionInfo.SelectByTicket(ticket))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string signalContent = RetrieveSignalContent(ticket);
|
|
if (StringLen(signalContent) > 0)
|
|
{
|
|
//
|
|
// Fill Additional Contents ...
|
|
FromString(
|
|
result,
|
|
signalContent);
|
|
}
|
|
|
|
//
|
|
// Ticket ...
|
|
result.ticket = mPositionInfo.Ticket();
|
|
|
|
//
|
|
// ID ...
|
|
result.id = mPositionInfo.Identifier();
|
|
|
|
//
|
|
// Symbol ...
|
|
result.symbol = mPositionInfo.Symbol();
|
|
|
|
//
|
|
// Type ...
|
|
ENUM_POSITION_TYPE pType = (ENUM_POSITION_TYPE)mPositionInfo.PositionType();
|
|
ENUM_X_SIGNAL_TYPE type = pType == POSITION_TYPE_BUY ? X_SIGNAL_LONG : X_SIGNAL_SHORT;
|
|
result.type = type;
|
|
|
|
//
|
|
// MagicNumber ...
|
|
result.magicNumber = mPositionInfo.Magic();
|
|
|
|
//
|
|
// Entry/Open Price ...
|
|
result.entry = mPositionInfo.PriceOpen();
|
|
|
|
//
|
|
// Take Profit ...
|
|
result.tp = result.tp <= 0 && mPositionInfo.TakeProfit() > 0
|
|
? mPositionInfo.TakeProfit()
|
|
: result.tp;
|
|
|
|
//
|
|
// Stop Loss ...
|
|
result.sl = result.sl <= 0 && mPositionInfo.StopLoss() > 0
|
|
? mPositionInfo.StopLoss()
|
|
: result.sl;
|
|
|
|
//
|
|
// Profit ...
|
|
result.profit = mPositionInfo.Profit();
|
|
|
|
//
|
|
// Time ...
|
|
result.time = mPositionInfo.Time();
|
|
|
|
//
|
|
// Volume ...
|
|
result.volume = mPositionInfo.Volume();
|
|
|
|
//
|
|
// Comment ...
|
|
result.comment = mPositionInfo.Comment();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// END Signal Private ...
|
|
//
|
|
};
|
|
//
|
|
// END Global Definitions: Variables, Properties and etc ...
|
|
//
|