Skip to content

Commit

Permalink
EA/Trade/Account: Improves trade risk management
Browse files Browse the repository at this point in the history
EA: Adds GetStrategyViaProp()/GetStrategyViaProp2()

EA: Fixes auto-lot logic after refactor

Strategy: StgParams: Adds timeframe and type

Trade: Adds logic to load the existing strategy orders
  • Loading branch information
kenorb committed Aug 30, 2021
1 parent 40309ec commit 00326b2
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 62 deletions.
44 changes: 38 additions & 6 deletions Account.mqh
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,18 @@ class Account {
return (float)Account::AccountFreeMargin();
}

/**
* Returns free margin value of the current account in percentage.
*
* @return
* Account's free margin in percentage.
*/
double GetMarginFreeInPct() {
double _margin_free = GetMarginFree();
double _margin_avail = GetMarginAvail();
return _margin_avail > 0 ? 100 / _margin_avail * _margin_free : 0;
}

/**
* Returns the current account number.
*/
Expand Down Expand Up @@ -574,16 +586,28 @@ class Account {
// @todo
return false;
*/
case ACCOUNT_COND_MARGIN_FREE_IN_PC:
// Arguments:
// arg[0] - Specify value in percentage.
// arg[1] - Math comparison operators (@see: ENUM_MATH_CONDITION).
return Math::Compare(GetMarginFreeInPct(), _args[0].ToValue<double>(),
(ENUM_MATH_CONDITION)_args[1].ToValue<int>());
case ACCOUNT_COND_MARGIN_USED_10PC:
return AccountMargin() >= AccountEquity() / 100 * 10;
return GetMarginUsedInPct() >= 10;
case ACCOUNT_COND_MARGIN_USED_20PC:
return AccountMargin() >= AccountEquity() / 100 * 20;
return GetMarginUsedInPct() >= 20;
case ACCOUNT_COND_MARGIN_USED_50PC:
return AccountMargin() >= AccountEquity() / 100 * 50;
return GetMarginUsedInPct() >= 50;
case ACCOUNT_COND_MARGIN_USED_80PC:
return AccountMargin() >= AccountEquity() / 100 * 80;
return GetMarginUsedInPct() >= 80;
case ACCOUNT_COND_MARGIN_USED_99PC:
return AccountMargin() >= AccountEquity() / 100 * 99;
return GetMarginUsedInPct() >= 99;
case ACCOUNT_COND_MARGIN_USED_IN_PC:
// Arguments:
// arg[0] - Specify value in percentage.
// arg[1] - Math comparison operators (@see: ENUM_MATH_CONDITION).
return Math::Compare(GetMarginUsedInPct(), _args[0].ToValue<double>(),
(ENUM_MATH_CONDITION)_args[1].ToValue<int>());
default:
// logger.Error(StringFormat("Invalid account condition: %s!", EnumToString(_cond), __FUNCTION_LINE__));
#ifdef __debug__
Expand All @@ -592,14 +616,22 @@ class Account {
return false;
}
}
bool CheckCondition(ENUM_ACCOUNT_CONDITION _cond) {
ARRAY(DataParamEntry, _args);
return Account::CheckCondition(_cond, _args);
}
bool CheckCondition(ENUM_ACCOUNT_CONDITION _cond, long _arg1) {
ARRAY(DataParamEntry, _args);
DataParamEntry _param1 = _arg1;
ArrayPushObject(_args, _param1);
return Account::CheckCondition(_cond, _args);
}
bool CheckCondition(ENUM_ACCOUNT_CONDITION _cond) {
bool CheckCondition(ENUM_ACCOUNT_CONDITION _cond, long _arg1, long _arg2) {
ARRAY(DataParamEntry, _args);
DataParamEntry _param1 = _arg1;
DataParamEntry _param2 = _arg2;
ArrayPushObject(_args, _param1);
ArrayPushObject(_args, _param2);
return Account::CheckCondition(_cond, _args);
}

Expand Down
16 changes: 9 additions & 7 deletions Condition.enum.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,16 @@ enum ENUM_ACCOUNT_CONDITION {
ACCOUNT_COND_EQUITY_IN_LOSS, // Equity in loss
ACCOUNT_COND_EQUITY_IN_PROFIT, // Equity in profit
/* @todo
ACCOUNT_COND_MARGIN_CALL_10PC, // Margin Call (10% margin left)
ACCOUNT_COND_MARGIN_CALL_20PC, // Margin Call (20% margin left)
ACCOUNT_COND_MARGIN_CALL_10PC, // Margin call (10% margin left)
ACCOUNT_COND_MARGIN_CALL_20PC, // Margin call (20% margin left)
*/
ACCOUNT_COND_MARGIN_USED_10PC, // Margin Used in 10%
ACCOUNT_COND_MARGIN_USED_20PC, // Margin Used in 20%
ACCOUNT_COND_MARGIN_USED_50PC, // Margin Used in 50%
ACCOUNT_COND_MARGIN_USED_80PC, // Margin Used in 80%
ACCOUNT_COND_MARGIN_USED_99PC, // Margin Used in 99%
ACCOUNT_COND_MARGIN_FREE_IN_PC, // Margin used in % (args)
ACCOUNT_COND_MARGIN_USED_10PC, // Margin used in 10%
ACCOUNT_COND_MARGIN_USED_20PC, // Margin used in 20%
ACCOUNT_COND_MARGIN_USED_50PC, // Margin used in 50%
ACCOUNT_COND_MARGIN_USED_80PC, // Margin used in 80%
ACCOUNT_COND_MARGIN_USED_99PC, // Margin used in 99%
ACCOUNT_COND_MARGIN_USED_IN_PC, // Margin used in % (args)
FINAL_ACCOUNT_CONDITION_ENTRY
};

Expand Down
77 changes: 65 additions & 12 deletions EA.mqh
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class EA {
logger.Link(_trade.GetLogger());
// trade.GetByKey(_Symbol).GetLogger().Error("Test");
// logger.Flush();
// Loads existing trades by magic number.
}

/**
Expand Down Expand Up @@ -135,7 +136,7 @@ class EA {
void Set(ENUM_STRATEGY_PARAM _param, T _value) {
for (DictStructIterator<long, Ref<Strategy>> iter = strats.Begin(); iter.IsValid(); ++iter) {
Strategy *_strat = iter.Value().Ptr();
_strat.Set<T> Set(_param, _value);
_strat.Set<T>(_param, _value);
}
}

Expand Down Expand Up @@ -212,8 +213,8 @@ class EA {
if (_sig_f == 0 || GetSignalOpenFiltered(_signal, _sig_f) >= 0.5f) {
_strat.Set(TRADE_PARAM_ORDER_COMMENT, _strat.GetOrderOpenComment("B:"));
// Buy order open.
TradeRequest(ORDER_TYPE_BUY, _Symbol, _strat);
if (eparams.CheckSignalFilter(STRUCT_ENUM(EAParams, EA_PARAM_SIGNAL_FILTER_FIRST))) {
_result &= TradeRequest(ORDER_TYPE_BUY, _Symbol, _strat);
if (_result && eparams.CheckSignalFilter(STRUCT_ENUM(EAParams, EA_PARAM_SIGNAL_FILTER_FIRST))) {
_signal.AddSignals(STRAT_SIGNAL_PROCESSED);
break;
}
Expand All @@ -225,8 +226,8 @@ class EA {
if (_sig_f == 0 || GetSignalOpenFiltered(_signal, _sig_f) <= -0.5f) {
_strat.Set(TRADE_PARAM_ORDER_COMMENT, _strat.GetOrderOpenComment("S:"));
// Sell order open.
TradeRequest(ORDER_TYPE_SELL, _Symbol, _strat);
if (eparams.CheckSignalFilter(STRUCT_ENUM(EAParams, EA_PARAM_SIGNAL_FILTER_FIRST))) {
_result &= TradeRequest(ORDER_TYPE_SELL, _Symbol, _strat);
if (_result && eparams.CheckSignalFilter(STRUCT_ENUM(EAParams, EA_PARAM_SIGNAL_FILTER_FIRST))) {
_signal.AddSignals(STRAT_SIGNAL_PROCESSED);
break;
}
Expand All @@ -250,7 +251,7 @@ class EA {
if (_last_error > 0) {
logger.Warning(StringFormat("Processing signals failed! Code: %d", _last_error), __FUNCTION_LINE__);
}
return _last_error == 0;
return _result && _last_error == 0;
}

/**
Expand Down Expand Up @@ -659,11 +660,13 @@ class EA {
* Returns true if the strategy has been initialized correctly, otherwise false.
*/
template <typename SClass>
bool StrategyAdd(ENUM_TIMEFRAMES _tf, long _magic_no = 0) {
bool StrategyAdd(ENUM_TIMEFRAMES _tf, long _magic_no = 0, int _type = 0) {
bool _result = true;
_magic_no = _magic_no > 0 ? _magic_no : rand();
Ref<Strategy> _strat = ((SClass *)NULL).Init(_tf);
_strat.Ptr().Set<long>(STRAT_PARAM_ID, _magic_no);
_strat.Ptr().Set<ENUM_TIMEFRAMES>(STRAT_PARAM_TF, _tf);
_strat.Ptr().Set<int>(STRAT_PARAM_TYPE, _type);
if (!strats.KeyExists(_magic_no)) {
_result &= strats.Set(_magic_no, _strat);
} else {
Expand Down Expand Up @@ -691,16 +694,24 @@ class EA {
* Returns true if all strategies has been initialized correctly, otherwise false.
*/
template <typename SClass>
bool StrategyAdd(unsigned int _tfs, long _init_magic = 0) {
bool StrategyAdd(unsigned int _tfs, long _init_magic = 0, int _type = 0) {
bool _result = true;
for (int _tfi = 0; _tfi < sizeof(int) * 8; ++_tfi) {
if ((_tfs & (1 << _tfi)) != 0) {
_result &= StrategyAdd<SClass>(ChartTf::IndexToTf((ENUM_TIMEFRAMES_INDEX)_tfi), _init_magic + _tfi);
_result &= StrategyAdd<SClass>(ChartTf::IndexToTf((ENUM_TIMEFRAMES_INDEX)_tfi), _init_magic + _tfi, _type);
}
}
return _result;
}

/**
* Loads existing trades for the given strategy.
*/
bool StrategyLoadTrades(Strategy *_strat) {
Trade *_trade = trade.GetByKey(_Symbol);
return _trade.OrdersLoadByMagic(_strat.Get<long>(STRAT_PARAM_ID));
}

/* Update methods */

/**
Expand Down Expand Up @@ -735,11 +746,14 @@ class EA {
* Updates strategy lot size.
*/
bool UpdateLotSize() {
bool _result = false;
if (eparams.CheckFlag(EA_PARAM_FLAG_LOTSIZE_AUTO)) {
// Auto calculate lot size for each strategy.
return ExecuteAction(EA_ACTION_STRATS_EXE_ACTION, STRAT_ACTION_TRADE_EXE, TRADE_ACTION_CALC_LOT_SIZE);
// Auto calculate lot size for all strategies.
Trade *_trade = trade.GetByKey(_Symbol);
_result &= _trade.ExecuteAction(TRADE_ACTION_CALC_LOT_SIZE);
Set(STRAT_PARAM_LS, _trade.Get<float>(TRADE_PARAM_LOT_SIZE));
}
return false;
return _result;
}

/* Conditions and actions */
Expand Down Expand Up @@ -852,6 +866,43 @@ class EA {

/* Getters */

/**
* Gets strategy based on the property value.
*
* @return
* Returns first found strategy instance on success.
* Otherwise, it returns NULL.
*/
template <typename T>
Strategy *GetStrategyViaProp(ENUM_STRATEGY_PARAM _prop, T _value, ENUM_MATH_CONDITION _op = MATH_COND_EQ) {
for (DictStructIterator<long, Ref<Strategy>> iter = strats.Begin(); iter.IsValid(); ++iter) {
Strategy *_strat = iter.Value().Ptr();
if (Math::Compare(_strat.Get<T>(_prop), _value, _op)) {
return _strat;
}
}
return NULL;
}

/**
* Gets strategy based on the two property values.
*
* @return
* Returns first found strategy instance on success.
* Otherwise, it returns NULL.
*/
template <typename T1, typename T2>
Strategy *GetStrategyViaProp2(ENUM_STRATEGY_PARAM _prop1, T1 _value1, ENUM_STRATEGY_PARAM _prop2, T2 _value2,
ENUM_MATH_CONDITION _op = MATH_COND_EQ) {
for (DictStructIterator<long, Ref<Strategy>> iter = strats.Begin(); iter.IsValid(); ++iter) {
Strategy *_strat = iter.Value().Ptr();
if (Math::Compare(_strat.Get<T1>(_prop1), _value1, _op) && Math::Compare(_strat.Get<T2>(_prop2), _value2, _op)) {
return _strat;
}
}
return NULL;
}

/**
* Returns pointer to Terminal object.
*/
Expand Down Expand Up @@ -955,6 +1006,8 @@ class EA {
_strat.Set<float>(TRADE_PARAM_RISK_MARGIN, _margin_risk);
// Link a logger instance.
logger.Link(_strat.GetLogger());
// Load existing strategy trades.
StrategyLoadTrades(_strat);
}

/* Printer methods */
Expand Down
4 changes: 2 additions & 2 deletions Math.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ class Math {
/**
* Checks condition for 2 values based on the given comparison operator.
*/
template <typename V1, typename V2>
static bool Compare(V1 _v1, V2 _v2, ENUM_MATH_CONDITION _op = MATH_COND_EQ) {
template <typename T1, typename T2>
static bool Compare(T1 _v1, T2 _v2, ENUM_MATH_CONDITION _op = MATH_COND_EQ) {
switch (_op) {
case MATH_COND_EQ:
return _v1 == _v2;
Expand Down
2 changes: 2 additions & 0 deletions Strategy.enum.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ enum ENUM_STRATEGY_PARAM {
STRAT_PARAM_SOFT, // Signal open filter time
STRAT_PARAM_SOL, // Signal open level
STRAT_PARAM_SOM, // Signal open method
STRAT_PARAM_TF, // Timeframe
STRAT_PARAM_TFM, // Tick filter method
STRAT_PARAM_TYPE, // Type
STRAT_PARAM_WEIGHT, // Weight
FINAL_ENUM_STRATEGY_PARAM
};
Expand Down
6 changes: 3 additions & 3 deletions Strategy.mqh
Original file line number Diff line number Diff line change
Expand Up @@ -553,8 +553,6 @@ class Strategy : public Object {

/* Setters */

/* Getters */

/**
* Sets a strategy parameter value.
*/
Expand Down Expand Up @@ -888,15 +886,17 @@ class Strategy : public Object {
_sargs[i] = _args[i + 1];
}
_result = trade.ExecuteAction((ENUM_TRADE_ACTION)_args[0].integer_value, _sargs);
/* @fixme
if (_result) {
Order _order = trade.GetOrderLast();
Order *_order = trade.GetOrderLast();
switch ((ENUM_TRADE_ACTION)_args[0].integer_value) {
case TRADE_ACTION_ORDER_OPEN:
// @fixme: Operation on the structure copy.
OnOrderOpen(_order.GetParams());
break;
}
}
*/
}
return _result;
case STRAT_ACTION_UNSUSPEND:
Expand Down
16 changes: 16 additions & 0 deletions Strategy.struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,11 @@ struct StgParams {
float max_spread; // Maximum spread to trade (in pips).
int tp_max; // Hard limit on maximum take profit (in pips).
int sl_max; // Hard limit on maximum stop loss (in pips).
int type; // Strategy type (@see: ENUM_STRATEGY).
long id; // Unique identifier of the strategy.
datetime refresh_time; // Order refresh frequency (in sec).
short shift; // Shift (relative to the current bar, 0 - default)
ChartTf tf; // Main timeframe where strategy operates on.
DictStruct<int, Ref<Indicator>> indicators_managed; // Indicators list keyed by id.
Dict<int, Indicator *> indicators_unmanaged; // Indicators list keyed by id.
// Constructor.
Expand Down Expand Up @@ -106,6 +108,7 @@ struct StgParams {
max_spread(0.0),
tp_max(0),
sl_max(0),
type(0),
refresh_time(0) {}
StgParams(int _som, int _sofm, float _sol, int _sob, int _scm, int _scf, float _scl, int _psm, float _psl, int _tfm,
float _ms, short _s = 0)
Expand Down Expand Up @@ -136,6 +139,7 @@ struct StgParams {
max_spread(0.0),
tp_max(0),
sl_max(0),
type(0),
refresh_time(0) {}
StgParams(StgParams &_stg_params) {
DeleteObjects();
Expand Down Expand Up @@ -190,8 +194,12 @@ struct StgParams {
return (T)price_profit_method;
case STRAT_PARAM_PSM:
return (T)price_stop_method;
case STRAT_PARAM_TF:
return (T)tf.GetTf();
case STRAT_PARAM_TFM:
return (T)tick_filter_method;
case STRAT_PARAM_TYPE:
return (T)type;
case STRAT_PARAM_WEIGHT:
return (T)weight;
}
Expand Down Expand Up @@ -279,9 +287,17 @@ struct StgParams {
case STRAT_PARAM_PSM: // Price stop method
price_stop_method = (int)_value;
return;
case STRAT_PARAM_TF:
// Main timeframe where strategy operates on.
tf = (ENUM_TIMEFRAMES)_value;
return;
case STRAT_PARAM_TFM: // Tick filter method
tick_filter_method = (int)_value;
return;
case STRAT_PARAM_TYPE:
// Strategy type.
type = (int)_value;
return;
case STRAT_PARAM_WEIGHT: // Weight
weight = (float)_value;
return;
Expand Down
Loading

0 comments on commit 00326b2

Please sign in to comment.