267 lines
6.7 KiB
Plaintext
267 lines
6.7 KiB
Plaintext
//
|
|
// Copyright 2018-2021, Artur Zas
|
|
// GNU General Public License v3.0 -> https://github.com/9nix6/Median-and-Turbo-Renko-indicator-bundle/blob/master/LICENSE
|
|
// https://www.az-invest.eu
|
|
// https://www.mql5.com/en/users/arturz
|
|
//
|
|
|
|
#include <AZ-INVEST/SDK/TradeFunctions.mqh>
|
|
|
|
class CTradeManagerState
|
|
{
|
|
public:
|
|
|
|
void Clear() { DoneBreakEven = false; TrailStarted = false; DonePartialClose = false; };
|
|
|
|
// Break Even
|
|
bool DoneBreakEven;
|
|
|
|
// Trailing Stop
|
|
bool TrailStarted;
|
|
|
|
// Partial Close
|
|
bool DonePartialClose;
|
|
};
|
|
|
|
struct CTradeManagerParameters
|
|
{
|
|
// Break Even
|
|
int BEPoints;
|
|
|
|
// Trailing Stop
|
|
int TrailByPoints;
|
|
int TrailStartPoints;
|
|
|
|
// Partial Close
|
|
int PartialCloseAtProfitPoints;
|
|
int PartialClosePercentage;
|
|
};
|
|
|
|
class CTradeManager
|
|
{
|
|
private:
|
|
|
|
bool initialized;
|
|
CMarketOrder *orderHandler;
|
|
CTradeManagerParameters inputs;
|
|
|
|
ENUM_ORDER_TYPE __type;
|
|
double __open;
|
|
double __lots;
|
|
string __symbol;
|
|
double __tp;
|
|
double __sl;
|
|
double __bid;
|
|
double __ask;
|
|
|
|
public:
|
|
|
|
CTradeManager();
|
|
CTradeManager(CTradeManagerParameters ¶ms, CMarketOrder *orderHalder);
|
|
~CTradeManager();
|
|
|
|
bool IsInitialized() { return this.initialized; };
|
|
bool Initialize(CTradeManagerParameters ¶ms, CMarketOrder *orderHalder);
|
|
bool Manage(ulong ticket, CTradeManagerState &_state);
|
|
string ToString();
|
|
|
|
private:
|
|
|
|
bool GetTradeInfo(ulong ticket);
|
|
bool BreakEven(ulong ticket);
|
|
bool OkToTrailTheStop(ulong ticket);
|
|
bool TrailTheStop(ulong ticket);
|
|
bool PartialClose(ulong ticket, double lots);
|
|
double GetPartialCloseLotSize();
|
|
bool IsDistanceFromOpenReached(double distancePriceDiff);
|
|
ENUM_ORDER_TYPE GetType(ulong ticket);
|
|
};
|
|
|
|
|
|
CTradeManager::CTradeManager(void)
|
|
{
|
|
this.orderHandler = NULL;
|
|
this.initialized = false;
|
|
}
|
|
|
|
CTradeManager::CTradeManager(CTradeManagerParameters ¶ms,CMarketOrder *_orderHandler)
|
|
{
|
|
this.orderHandler = NULL;
|
|
this.initialized = false;
|
|
Initialize(params, _orderHandler);
|
|
}
|
|
|
|
CTradeManager::~CTradeManager(void)
|
|
{
|
|
|
|
}
|
|
|
|
bool CTradeManager::GetTradeInfo(ulong ticket)
|
|
{
|
|
__type = GetType(ticket);
|
|
__open = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
if(__open == 0)
|
|
return false;
|
|
|
|
__lots = PositionGetDouble(POSITION_VOLUME);
|
|
__symbol = PositionGetString(POSITION_SYMBOL);
|
|
__tp = PositionGetDouble(POSITION_TP);
|
|
__sl = PositionGetDouble(POSITION_SL);
|
|
__bid = SymbolInfoDouble(__symbol,SYMBOL_BID);
|
|
__ask = SymbolInfoDouble(__symbol,SYMBOL_ASK);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTradeManager::Initialize(CTradeManagerParameters ¶ms, CMarketOrder *_orderHandler)
|
|
{
|
|
// Dependency injection
|
|
this.orderHandler = _orderHandler;
|
|
if(this.orderHandler == NULL)
|
|
{
|
|
Print(__FUNCTION__," failed on orderHandler == NULL");
|
|
return false;
|
|
}
|
|
|
|
//
|
|
this.inputs = params;
|
|
|
|
// normalize inputs
|
|
this.inputs.PartialClosePercentage = MathMin(MathAbs(this.inputs.PartialClosePercentage), 100);
|
|
|
|
//
|
|
this.initialized = true;
|
|
return initialized;
|
|
}
|
|
|
|
bool CTradeManager::Manage(ulong ticket, CTradeManagerState &_state)
|
|
{
|
|
if(!GetTradeInfo(ticket))
|
|
return false; // trade info not available
|
|
|
|
if(!_state.DonePartialClose)
|
|
{
|
|
_state.DonePartialClose = PartialClose(ticket, GetPartialCloseLotSize());
|
|
}
|
|
|
|
if(!_state.DoneBreakEven)
|
|
{
|
|
if(BreakEven(ticket))
|
|
_state.DoneBreakEven = true;
|
|
}
|
|
|
|
if(!_state.TrailStarted)
|
|
{
|
|
_state.TrailStarted = OkToTrailTheStop(ticket);
|
|
}
|
|
|
|
if(_state.TrailStarted)
|
|
{
|
|
TrailTheStop(ticket);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTradeManager::PartialClose(ulong ticket, double lots)
|
|
{
|
|
if(this.inputs.PartialCloseAtProfitPoints == 0 || lots == 0)
|
|
return false; // nothing to do
|
|
|
|
double _partialCloseDistance = SymbolInfoDouble(__symbol,SYMBOL_POINT) * this.inputs.PartialCloseAtProfitPoints;
|
|
if(!IsDistanceFromOpenReached(_partialCloseDistance))
|
|
return false;
|
|
|
|
return orderHandler.ClosePartial(ticket, lots);
|
|
}
|
|
|
|
bool CTradeManager::BreakEven(ulong ticket)
|
|
{
|
|
if(this.inputs.BEPoints == 0)
|
|
return false; // nothing to do
|
|
|
|
double _beDistance = SymbolInfoDouble(__symbol,SYMBOL_POINT) * this.inputs.BEPoints;
|
|
if(!IsDistanceFromOpenReached(_beDistance))
|
|
return false;
|
|
|
|
return orderHandler.Modify(ticket,__open,__tp);
|
|
}
|
|
|
|
bool CTradeManager::OkToTrailTheStop(ulong ticket)
|
|
{
|
|
if(this.inputs.TrailByPoints == 0)
|
|
return false; // nothing to do
|
|
|
|
double _startDistance = SymbolInfoDouble(__symbol,SYMBOL_POINT) * this.inputs.TrailStartPoints;
|
|
if(!IsDistanceFromOpenReached(_startDistance))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CTradeManager::TrailTheStop(ulong ticket)
|
|
{
|
|
if(this.inputs.TrailByPoints == 0)
|
|
return false; // nothing to do
|
|
|
|
double _trailDistance = SymbolInfoDouble(__symbol,SYMBOL_POINT) * this.inputs.TrailByPoints;
|
|
double _sl = __sl;
|
|
bool okToModify = false;
|
|
|
|
if(__type == ORDER_TYPE_BUY)
|
|
{
|
|
_sl = (__bid - _trailDistance);
|
|
if(_sl > __sl)
|
|
okToModify = true;
|
|
}
|
|
else if(__type == ORDER_TYPE_SELL)
|
|
{
|
|
_sl = (__ask + _trailDistance);
|
|
if(_sl < __sl)
|
|
okToModify = true;
|
|
}
|
|
|
|
if(okToModify)
|
|
return orderHandler.Modify(ticket,_sl,__tp);
|
|
|
|
return false;
|
|
}
|
|
|
|
ENUM_ORDER_TYPE CTradeManager::GetType(ulong ticket)
|
|
{
|
|
ENUM_POSITION_TYPE _pType;
|
|
orderHandler.GetPositionType(ticket,_pType);
|
|
|
|
return orderHandler.TradeBias((ENUM_ORDER_TYPE)_pType);
|
|
}
|
|
|
|
double CTradeManager::GetPartialCloseLotSize()
|
|
{
|
|
double lotsToClose = (__lots * inputs.PartialClosePercentage) / 100;
|
|
return NormalizeLots(__symbol, lotsToClose);
|
|
}
|
|
|
|
bool CTradeManager::IsDistanceFromOpenReached(double distancePriceDiff)
|
|
{
|
|
if(__type == ORDER_TYPE_BUY)
|
|
{
|
|
if((__bid - distancePriceDiff) >= __open)
|
|
return true;
|
|
}
|
|
else if(__type == ORDER_TYPE_SELL)
|
|
{
|
|
if((__ask + distancePriceDiff) <= __open)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
string CTradeManager::ToString()
|
|
{
|
|
string _be = (inputs.BEPoints > 0) ? "[BE ON] " : "[BE off] ";
|
|
string _trail = (inputs.TrailByPoints > 0) ? "[Trail ON] ": "[Trail off] ";
|
|
string _partial = (inputs.PartialCloseAtProfitPoints > 0) ? "[Partial "+(string)inputs.PartialClosePercentage+"%] ": "[Partial off] ";
|
|
|
|
return _be+_trail+_partial;
|
|
} |