Commit b683e788 authored by denis's avatar denis

implemented ATR based stop loss. changed config file option to SL_ATR_FACTOR...

implemented ATR based stop loss. changed config file option to SL_ATR_FACTOR and SL_ATR_PERIOD, as they now apply to both chandelier and ATR based SL. Updated documentation accordingly
parent 22c8fad4
Pipeline #126834139 passed with stage
in 1 minute and 19 seconds
This diff is collapsed.
......@@ -95,17 +95,19 @@ PORTFOLIO_RETURNS_UPDATE = 25 # update mean returns every X periods
# Stop Loss settings
###############################################################################
SL_TYPE = percentage # percentage (percentage stop, default),
# chandelier (Chandelier Stop)
SL_PERCENTAGE_RISK = 0.02 # percent for SL_TYPE= percentage, default 0.02
SL_CHANDELIER_ATR_FACTOR = 3 # SL_TYPE=chandelier: Place SL x-times the ATR
# away (default 3)
SL_CHANDELIER_ATR_PERIOD = 26 # SL_TYPE=chandelier: Period for Averaging the
# True Range (default 26)
SL_ADJUST = trailing # fixed (use initial SL only),
# trailing (adjust SL in trend direction only)
# chandelier, atr (ATR based)
SL_PERCENTAGE_RISK = 0.05 # the SL percentage for SL_TYPE = percentage,
# default 0.02
SL_ATR_FACTOR = 3 # SL_TYPE=chandelier/atr: Place the SL x-times
# the ATR away (default 3)
SL_ATR_PERIOD = 26 # SL_TYPE=chandelier/atr: Period for Averaging
# the True Range (default 26)
SL_ADJUST = trailing # fixed (use initial SL only), trailing (adjust
# SL in trend direction only)
# updown (adjust SL in both directions),
# default: trailing
SL_SAVE_DB = false # save all SL records in mysql database? (true/false)
SL_SAVE_DB = true # save all SL records in mysql database?
# (true/false)
###############################################################################
# Risk management settings
......@@ -123,8 +125,8 @@ RISK_FREE_RATE = 0.02 # rate of returns for 0 risk investment
###############################################################################
CHART_PLOT = true # plot the chart, true/false
CHART_PLOT_WEAK = false # plot weak signals; default false
CHART_PLOT_NEUTRAL = false # plot neutral signals; default false
CHART_PLOT_STRONG = true # plot strong signals; default true
CHART_PLOT_NEUTRAL = false # plot neutral signals; default false
CHART_PLOT_STRONG = true # plot strong signals; default true
CHART_COLORSCHEME = contrast # light/ contrast
CHART_DAYS_TO_PLOT = 120 # default: 120
......
......@@ -511,7 +511,9 @@ This option determines the type of stop loss the system uses.
Valid options are:
* `percentage`: use a fixed percentage of the buy price to determine SL (default)
* `chandelier`: use a stop loss based on volatility (Average True Range)
to determine SL
to determine SL, for details see:
see http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:chandelier_exit
* `atr`: simple ATR based stop
Note that in market conditions with high momentum a new chandelier sl could be over
current price (for long, under current price for short). In this cases the percentage
stop will be used as fallback, so while configuring chandelier stop it is important
......@@ -527,24 +529,25 @@ based on the buy price (default is 0.02).
_Syntax:_
`SL_PERCENTAGE_RISK = 0.02`
**SL_CHANDELIER_ATR_FACTOR**
When using `SL_Type = chandelier` this option specifies the factor that the ATR will be
multiplied with (in other words place the SL x-times the ATR away from current price).
Default is `3`.
**SL_ATR_FACTOR**
When using `SL_Type = chandelier` or `SL_TYPE = atr` this option specifies the factor
that the ATR will be multiplied with (in other words place the SL x-times the ATR away
from current price). Default is `3`.
_Syntax:_
`SL_CHANDELIER_ATR_FACTOR = 3`
`SL_ATR_FACTOR = 3`
**SL_CHANDELIER_ATR_PERIOD**
When using `SL_type = chandelier` this option specifies the _Average_-part of ATR (in other
words the period for averaging the true range). Based on this option the ATR will be calculated
and stored in the db table `indicators_daily`. You can choose your period freely, using the
same period as the mid term period for Ichimoku should give a good starting point.
**SL_ATR_PERIOD**
When using `SL_type = chandelier` or `SL_TYPE = atr` this option specifies the
_Average_-part of ATR (in other words the period for averaging the true range). Based
on this option the ATR will be calculated vand stored in the db table `indicators_daily`.
You can choose your period freely, using the same period as the mid term period for
Ichimoku should give a good starting point.
NOTE: SIGNAL_DAYS_TO_EXECUTE must be at least SL_CHANDELIER_ATR_PERIOD days shorter than
INDI_DAYS_TO_UPDATE, because the first ATR calculation needs SL_CHANDELIER_ATR_PERIOD days.
If earlier ATR values are queried, mysql will return 0 for the ATR so when using chandelier
stop the calculation of the stop might be wrong!
_Syntax:_
`SL_CHANDELIER_ATR_PERIOD = 26`
`SL_ATR_PERIOD = 26`
**SL_ADJUST**
This option specifies how the the stop loss will be adjusted from day to day/ bar to bar.
......@@ -731,17 +734,19 @@ PORTFOLIO_RETURNS_UPDATE = 25 # update mean returns every X periods
# Stop Loss settings
###############################################################################
SL_TYPE = percentage # percentage (percentage stop, default),
# chandelier (Chandelier Stop)
SL_PERCENTAGE_RISK = 0.02 # percent for SL_TYPE= percentage, default 0.02
SL_CHANDELIER_ATR_FACTOR = 3 # SL_TYPE=chandelier: Place SL x-times the ATR
# away (default 3)
SL_CHANDELIER_ATR_PERIOD = 26 # SL_TYPE=chandelier: Period for Averaging the
# True Range (default 26)
SL_ADJUST = trailing # fixed (use initial SL only),
# trailing (adjust SL in trend direction only)
# chandelier, atr (ATR based)
SL_PERCENTAGE_RISK = 0.05 # the SL percentage for SL_TYPE = percentage,
# default 0.02
SL_ATR_FACTOR = 3 # SL_TYPE=chandelier/atr: Place the SL x-times
# the ATR away (default 3)
SL_ATR_PERIOD = 26 # SL_TYPE=chandelier/atr: Period for Averaging
# the True Range (default 26)
SL_ADJUST = trailing # fixed (use initial SL only), trailing (adjust
# SL in trend direction only)
# updown (adjust SL in both directions),
# default: trailing
SL_SAVE_DB = false # save all SL records in mysql database? (true/false)
SL_SAVE_DB = true # save all SL records in mysql database?
# (true/false)
###############################################################################
# Risk management settings
......
......@@ -561,8 +561,8 @@ int update_indicators(class_market* the_market)
the_market->copyIndicatorQuotes(the_market, "close", "ATR");
// calculate indicators
highest_high(parms.SL_CHANDELIER_ATR_PERIOD, the_market->Ind[highPos], the_market->Ind[hh_atrPos]);
lowest_low(parms.SL_CHANDELIER_ATR_PERIOD, the_market->Ind[lowPos], the_market->Ind[ll_atrPos]);
highest_high(parms.SL_ATR_PERIOD, the_market->Ind[highPos], the_market->Ind[hh_atrPos]);
lowest_low(parms.SL_ATR_PERIOD, the_market->Ind[lowPos], the_market->Ind[ll_atrPos]);
calc_true_range(the_market->Ind[highPos],the_market->Ind[lowPos], the_market->Ind[closePos], the_market->Ind[trPos]);
calc_average_true_range(parms.INDI_ADX_PERIOD, the_market->Ind[trPos], the_market->Ind[atrPos]);
......
......@@ -142,8 +142,8 @@ void init_parameters (parameter *parms)
// Stop Loss settings
parms->SL_TYPE = bstrcpy(slstr); // SL type: percentage stop (default), chandelier (Chandelier Stop)
parms->SL_PERCENTAGE_RISK = 0.02; // the SL percentage for SL_TYPE = percentage, default 0.02
parms->SL_CHANDELIER_ATR_FACTOR = 3; // SL_TYPE=chandelier: Place the SL x-times the ATR away (default 3)
parms->SL_CHANDELIER_ATR_PERIOD = 26; // SL_TYPE=chandelier: Period for Averaging the True Range (default 26)
parms->SL_ATR_FACTOR = 3; // SL_TYPE=chandelier: Place the SL x-times the ATR away (default 3)
parms->SL_ATR_PERIOD = 26; // SL_TYPE=chandelier: Period for Averaging the True Range (default 26)
parms->SL_ADJUST = bstrcpy(sladjuststr); // fixed (use initial SL only), trailing (adjust SL in trend direction only), updown (adjust SL in both directions), default: trailing
parms->SL_SAVE_DB = false; // save all SL records in mysql database? (true/false)
......@@ -701,20 +701,20 @@ int parse_config(bstring filename, parameter *parms)
if(check_parameter(parms, name, parms->SL_ADJUST))
warnings++;
}
else if (biseqcstr (name, "SL_CHANDELIER_ATR_FACTOR"))
else if (biseqcstr (name, "SL_ATR_FACTOR"))
{
bstring tmp;
tmp=bmidstr(line, i+1, blength (line));
btrimws(tmp);
parms->SL_CHANDELIER_ATR_FACTOR = atof(bdatae(tmp,"???"));
parms->SL_ATR_FACTOR = atof(bdatae(tmp,"???"));
bdestroy(tmp);
}
else if (biseqcstr (name, "SL_CHANDELIER_ATR_PERIOD"))
else if (biseqcstr (name, "SL_ATR_PERIOD"))
{
bstring tmp;
tmp=bmidstr(line, i+1, blength (line));
btrimws(tmp);
parms->SL_CHANDELIER_ATR_PERIOD = atoi(bdatae(tmp,"???"));
parms->SL_ATR_PERIOD = atoi(bdatae(tmp,"???"));
bdestroy(tmp);
}
else if (biseqcstr(name, "SL_SAVE_DB"))
......@@ -1365,11 +1365,12 @@ int check_parameter(parameter *parms, bstring parameter, bstring value)
else if(biseqcstr(parameter, "SL_TYPE"))
{
if (biseqcstr(value, "percentage") ||
biseqcstr(value, "chandelier"))
biseqcstr(value, "chandelier") ||
biseqcstr(value, "atr"))
return 0;
else
{
log_warn("\"%s\" not valid for parameter %s. currently implemented: percentage, chandelier. Using default percentage",
log_warn("\"%s\" not valid for parameter %s. currently implemented: percentage, chandelier, atr. Using default percentage",
bdata(value),
bdata(parameter));
bassign(parms->SL_TYPE, bfromcstr("percentage"));
......
......@@ -114,8 +114,8 @@ struct parameters
// Stop Loss settings
bstring SL_TYPE; /**< percentage (percentage stop), chandelier (Chandelier Stop) */
float SL_PERCENTAGE_RISK; /**< the SL percentage for SL_TYPE = percentage, default 0.02 */
float SL_CHANDELIER_ATR_FACTOR; /**< Place the SL x-times the ATR away (default 3) */
unsigned int SL_CHANDELIER_ATR_PERIOD; /**< Period for Averaging the True Range (default 26) */
float SL_ATR_FACTOR; /**< Place the SL x-times the ATR away (default 3) */
unsigned int SL_ATR_PERIOD; /**< Period for Averaging the True Range (default 26) */
bstring SL_ADJUST; /**< fixed (use initial SL only), trailing (adjust SL in trend direction only), updown (adjust SL in both directions), default: trailing */
bool SL_SAVE_DB; /**< save all SL records in mysql database? (true/false) */
......
......@@ -58,21 +58,22 @@ float new_stop_loss(class_market* the_market, unsigned int quote_idx, signal_typ
{
float hh_ll = 0;
float atr = 0;
int atr_idx = quote_idx - parms.SL_CHANDELIER_ATR_PERIOD; //TODO: make a sanity check for proper configuration
int atr_idx = quote_idx - parms.SL_ATR_PERIOD; //TODO: make a sanity check for proper configuration
check(atr_idx >= 0, "not enough quotes (%i) for market %s (vs. %i SL_ATR_PERIOD), check data/%s", error_nr_quotes, the_market->Ind[the_market->IndPos.close]->NR_ELEMENTS, bdata(*the_market->symbol), parms.SL_ATR_PERIOD, bdata(*the_market->csvfile));
atr = the_market->Ind[the_market->getIndicatorPos(the_market, "ATR")]->QUOTEVEC[atr_idx];
switch(the_type)
{
case longsignal:
hh_ll = the_market->Ind[the_market->getIndicatorPos(the_market, "HH_atr_period")]->QUOTEVEC[atr_idx];
new_sl = chandelier_stop(hh_ll, atr, parms.SL_CHANDELIER_ATR_FACTOR, the_type);
new_sl = chandelier_stop(hh_ll, atr, parms.SL_ATR_FACTOR, the_type);
if(new_sl > the_quote)
new_sl = percentage_stop(parms.SL_PERCENTAGE_RISK, the_quote, the_type); // fallback
break;
case shortsignal:
hh_ll = the_market->Ind[the_market->getIndicatorPos(the_market, "LL_atr_period")]->QUOTEVEC[atr_idx];
new_sl = chandelier_stop(hh_ll, atr, parms.SL_CHANDELIER_ATR_FACTOR, the_type);
new_sl = chandelier_stop(hh_ll, atr, parms.SL_ATR_FACTOR, the_type);
if(new_sl < the_quote)
new_sl = percentage_stop(parms.SL_PERCENTAGE_RISK, the_quote, the_type); // fallback
break;
......@@ -81,10 +82,21 @@ float new_stop_loss(class_market* the_market, unsigned int quote_idx, signal_typ
new_sl = 0;
break;
}
// new_sl = chandelier_stop_2(hh_ll, atr, parms.SL_CHANDELIER_ATR_FACTOR, the_type);
}
else if(biseqcstr(parms.SL_TYPE, "atr"))
{
float atr = 0;
int atr_idx = quote_idx - parms.SL_ATR_PERIOD; //TODO: make a sanity check for proper configuration
check(atr_idx >= 0, "not enough quotes (%i) for market %s (vs. %i SL_ATR_PERIOD), check data/%s", error_nr_quotes, the_market->Ind[the_market->IndPos.close]->NR_ELEMENTS, bdata(*the_market->symbol), parms.SL_ATR_PERIOD, bdata(*the_market->csvfile));
new_sl = atr_stop(the_quote, atr, parms.SL_ATR_FACTOR, the_type);
}
return new_sl;
error_nr_quotes:
the_market->destroy(the_market);
exit(EXIT_FAILURE);
}
......@@ -160,6 +172,44 @@ float chandelier_stop(float hh_ll, float atr, float atr_factor, signal_type type
return stoploss;
}
///////////////////////////////////////////////////////////////////////////////
/**
* @brief implementation of a simple ATR based stop
*
* This function implements a simple volatility-based SL. It utilizes the ATR
* to calculate a stop loss.
*
* @param price float with latest quote
* @param atr float with average true range
* @param atr_factor float with weight factor of ATR
* @param type signal_type (SL will be above price for down-, and below price
* for uptrends)
* @return float with the new stop loss
*/
///////////////////////////////////////////////////////////////////////////////
float atr_stop(float price, float atr, float atr_factor, signal_type type)
{
float stoploss = 0;
switch(type)
{
case longsignal:
stoploss = price - atr * atr_factor;
break;
case shortsignal:
stoploss = price + atr * atr_factor;
break;
case undefined:
default:
stoploss = 0;
break;
}
return stoploss;
}
///////////////////////////////////////////////////////////////////////////////
/**
* @brief checks if a given SL is needed
......
......@@ -38,6 +38,7 @@ float new_stop_loss(class_market* the_market, unsigned int quote_idx,
float percentage_stop(float percentage_at_risk, float price,
signal_type type);
float chandelier_stop(float hh_ll, float atr, float atr_factor, signal_type type);
float atr_stop(float price, float atr, float atr_factor, signal_type type);
bool new_sl_needed(float newstop, float oldstop, signal_type type);
#endif // STOPLOSS_H
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment