It is important to note that trading in financial markets involves inherent risks, including the potential loss of capital. The results presented within this package may not be suitable for all investors, and past performance is not indicative of future results.
Users are solely responsible for evaluating the suitability of any trading strategy for their own individual circumstances and risk tolerance. The author of this software shall not be held liable for any losses, damages, or other consequences arising from the use or application of the results presented herein in real trading environments.
Before engaging in any trading activity, users are strongly advised to consult with a qualified financial advisor and to thoroughly understand the risks involved. By using this software, users acknowledge and accept that they do so at their own risk, and the author shall not be held responsible for any outcomes resulting from its use.
traderlab is a package for fast trading strategies design, backtesting and quantitative analysis with practical applications. It allows the creation of strategies by the composition of a YAML file where data, indicators, rules and money management, among other information required for the backtesting engine, are specified. Simple commands run the YAML file, show the results and plot them.
This current version has a series of limitations since it has been developed for a specific need and might not be suitable for other uses. Here are the most important ones:
-
Only Binance and Yahoo data download is implemented so far. Other sources and datasets are technically possible as long as they contain OHLCV data and this is provided in the expected format.
-
Strategy rules are evaluated at the close of the last bar.
-
The user can only indicate an out-of-sample period by defining its start date. Then the period will cover from the specified date until last available time in the dataset.
-
Each model execution overwrites the results from previous runs. The reason behind this is that a large number of worthless backtests and metrics are usually generated, taking up a lot of space and making it very difficult to explore later. In the future, the user will be able to store the results in a more permanent database, allowing more advanced features.
traderlab is not a package for typical financial analysis and there is not a intention to substitute them. Instead, traderlab is focused on trading markets by means of technical indicators and pursuing a practical approach.
The package does not find for the user profitable strategies neither. The user must provide the indicators and trading rules and he/she is the responsible of evaluating whether or not the strategy is suitable for him/her.
In the current version, traderlab does not execute real trading in any case. As its name suggests, it is intended to be a trading strategies laboratory to test and analyze them.
While other packages in R, Python and other languages require from a moderate to an intensive coding to specify and test a trading strategy, traderlab makes this task easy for the user.
A YAML file (basically a text file) is almost everything a user has to worry about. This file contains what we call a model.
A model is nothing but indicators and rules with variables involved, and additional information. Part of this information specifies the range of values that the variables can take, a preliminary target metric, an out-of-sample period, etc.
The target metric does not exclude other important metrics. All of them will be available after the backtesting, but the target is useful as it informs to the user about the performance without waiting to the end as long as the previewer is set to true.
Install the current development version of traderlab as follows:
install.packages("devtools")
devtools::install_github("jmartindelasierra/traderlab")
In the DESCRIPTION file you can see that traderlab has many dependencies. If your installation fails and you get lost in the output messages, then be patient and install the dependencies one by one so you can focus on the failing dependency.
Binance data can be downloaded with the get_binance_ohlcv_data()
function.
This function downloads all the available files in the Binance
repository and consolidates them in a single dataset. Finally, it is
saved in ./datasets
from your working folder.
The following example downloads the 4-hour history for Bitcoin/USDT:
get_binance_ohlcv_data(symbol = "BTCUSDT", timeframe = "4h")
After the download, the file btcusdt_4h.rds
will be available in
./datasets
.
You can also download other data for different assets with the
get_yahoo_ohlcv_data()
function.
The data from this source will be limited to daily OHLCV. In addition, you may get data with missing values, which are replaced (not very accurately) with the previous non-missing value.
The following examples download data for SP500, Tesla, and EUR/USD:
get_yahoo_ohlcv_data(symbol = "SPY")
get_yahoo_ohlcv_data(symbol = "TSLA")
get_yahoo_ohlcv_data(symbol = "EUR=X")
The datasets are then saved as spy_1d.rds
, tsla_1d.rds
and
eur=x_1d.rds
in ./datasets
.
Despite not providing functions for downloading data from different sources than Binance and Yahoo, there is not technical inconvenient to use other sources.
As you have probably guessed, traderlab works with open
, high
, low
and close
prices, and volume
data. Two additional features are also
required: open_time
and close_time
. So, if you want to use other
datasets, ensure you can provide to traderlab a data.frame
in .rds
format with the following columns:
column | class |
---|---|
open_time | POSIXct |
open | numeric |
high | numeric |
low | numeric |
close | numeric |
volume | numeric |
close_time | POSIXct |
Note: do not use any of the previous column names for naming your indicators or variables to avoid collisions and unexpected behavior.
Remember that a model is a YAML file containing the indicators setup, strategy rules, money management and other information for the backtest engine. This concept allows the user create and test strategies avoiding to code them as required in many other solutions.
We made an effort to design a YAML structure as simple as possible resulting in a set of lists of key-values as we will show soon.
The following sections (lists) must exist in a model:
description:
indicators:
rules:
management:
periods:
target:
steps:
Let’s look at each one in detail.
Here is where we annotate descriptive information about the model including the data source path:
description:
name:
symbol:
timeframe:
data:
data
is the only mandatory key and a path to the data source is
expected here (e.g. ./datasets/btcusdt_4h.rds). However, we recommend to
add values to the other keys as this helps to keep organized your work
by giving the model a name
and specifying the symbol
and
timeframe
.
Note: strings do not need to be quoted as formats are automatically detected. This applies to the entire YAML file.
This section must have the following structure:
indicators:
name1:
indicator:
parameter1:
parameter2:
name2:
indicator:
parameter1:
...
Here, indicators
announces the indicators section start. Each
indicator must be identified by an unique name
of your choice that
will be used for referencing later. The indicator
key is used to call
a listed indicator (see the accepted indicators list) and the following
parameter
keys are used to setup such indicator.
The rules section is declared as follows:
rules:
entry_signal:
exit_signal:
stop_loss_price:
entry_signal
and exit_signal
must contain the logical expressions
that indicate whether a trade entry or exit takes place (e.g. close >
ema). These two signals are necessary to run a model.
stop_loss_price
must contain a expression which evaluation returns a
stop loss price (e.g. entry_price - 100). Stop loss is not mandatory.
Here is how you tell the engine about managing the capital:
management:
position:
start_capital:
interest_type:
reinvest:
leverage:
fee_type:
fee:
position
can be long or short.
The reinvest
must be provided in a percentage between 0 and 1. If
interest_type
is simple, the reinvest
is recomputed as
reinvest * start_capital
, reinvesting a fixed capital per trade. If
interest_type
is compound, each trade will have available the
percentage portion of the current capital.
fee_type
concerns to a fixed amount or a percentage per trade. If it
is configured as per_trade_fixed
, fee
will be provided in dollars.
If on the other hand is configured as per_trade_pct
, fee
must be
provided in a percentage between 0 and 1.
This section allows us cropping the dataset by defining a start
and/or
an end
date. In addition, oos_start
is used to indicate the
out-of-sample start period. We can only indicate the start as
automatically the last bar in the dataset becomes the out-of-sample end:
periods:
start:
end:
oos_start:
The target is intended for preliminary evaluation of the model. This is
especially useful if preview
is set to TRUE in the run model call. You
must specify a metric
to evaluate, set the operation
type whether
you look to maximize or minimize the metric, and the scope
where it is
evaluated (in-sample, out-of-sample or full-sample):
target:
metric:
operation:
scope:
Some keys allow the use of variables given in the format
$variable_name
. Their values are evaluated according to the specified
in this section:
steps:
variable_1:
variable_2:
variable_n:
Once the YAML file is completed and saved, running the backtests is as simple as writing a single line:
library(traderlab)
run <- run_model("my_model.yaml", preview = TRUE)
If you save your model in a subfolder, ensure you specify the path (e.g. “./models/my_model.yaml”).
There are different functions we can use to analyze and extract insights from the resulting data. We find more suitable to briefly present these functions in the following example.
Here we want to illustrate the analysis capabilities that this package offers. In the next example we will see how to implement a basic moving average crossover strategy as well as the steps to run the model and analyzing its results.
The following YAML represents a simple two-EMA crossover strategy for Bitcoin at 4-hour bars data:
version: 1
description:
name: Basic EMA crossover
symbol: BTCUSDT
timeframe: 4h
data: ./datasets/btcusdt_4h.rds
indicators:
ema1:
indicator: EMA
source: close
periods: $ema1_periods
ema2:
indicator: EMA
source: close
periods: $ema2_periods
rules:
entry_signal: ema1 > ema2
exit_signal: ema1 < ema2
stop_loss_price: entry_price - 0.1 * entry_price
management:
position: long
start_capital: 10000
interest_type: simple
reinvest: 1
leverage: 1
fee_type: per_trade_pct
fee: 0.002
periods:
start: 2018-01-01
end:
oos_start: 2023-01-01
target:
metric: return_dd
operation: max
scope: is
steps:
ema1_periods: [40, 50, 5]
ema2_periods: [90, 100, 5]
Note: if you work with a Unix based system, you might require a newline at the end of your document to avoid an error.
Let’s comment the setup:
The version
is not mandatory so far. We are just annotating this for
future developments.
The description
section indicates that this model is using a 4-hour
Bitcoin dataset.
Two indicators
are setup. They are two EMAs evaluated at the close for
each bar. The periods are not fixed but referenced to two custom
variables: $ema1_periods
and $ema2_periods
.
The rules
meet a typical two-EMA crossover strategy. The
entry_signal
will be TRUE when ema1
crosses above ema2
. The
exit_signal
will be TRUE when ema1
crosses below ema2
. A stop loss
has also been setup such that the trades will close when the price is a
10% below the entry price. All three rules refer at the bar close.
Concerning the management
setup, when the entry signal happens, a long
position is open. The start_capital
is $10000 and because of the
simple interest_type
, a fixed amount of capital (as maximum) of 100%
* $10000 = $10000 will be available per trade. There is no leverage
(1x) and finally, the fee_type
is configured as a % per trade which is
fixed to 0.2% in the fee
key.
The object periods
specifies that the dataset starts on 2018-01-01 and
the out-of-sample period does on 2023-01-01. The out-of-sample period
ends at the last bar of the dataset always.
The section target
indicates the metric
goal to monitor in the
backtesting process. We want to evaluate the max
(maximum) return_dd
(return / drawdown) on is
(in-sample period). This target does not
exclude the calculation of other metrics.
In steps
, we assign values to the two custom variables defined before.
We want ema1_periods
to go from 40 to 50 in increments of 5 (i.e. 40,
45, 50). ema2_periods
will go from 90 to 100 in increments of 5
(i.e. 90, 95, 100). Note that here, the ‘$’ symbol does not appear
anymore.
One of the things we would like to know before running the model is the number of steps it will take as well as the variable values in each step. In the context of this package, a step can be understand as a single backtest, so our model will run 9 backtests:
get_model_steps("./models/basic_ema_xover.yml")
$n_steps
[1] 9
$steps
$ema1_periods $ema2_periods
1 40 90
2 45 90
3 50 90
4 40 95
5 45 95
6 50 95
7 40 100
8 45 100
9 50 100
We might also want to see how the indicators look like for some specific step. Plotting the indicators is very useful to verify that they are configured as expected and our strategy setup makes sense. This feature still needs some improvements, but for the moment we can do like this:
library(ggplot2)
plot_trades(model = "./models/basic_ema_xover.yml",
step = 1,
from = "2024-01-01",
to = "2024-03-31",
show_trades = FALSE) +
geom_line(aes(x = close_time, y = ema1), color = "steelblue3") +
geom_line(aes(x = close_time, y = ema2), color = "orange3")
Or perhaps we are more interested in how the strategy signals occur. In this case, our code do not need the traces associated to the indicators:
plot_trades(model = "./models/basic_ema_xover.yml",
step = 1,
from = "2023-01-01",
to = "2024-03-31",
show_trades = TRUE)
Of course, we can combine signals and indicators in the same plot by
switching back show_trades
to TRUE and keeping the lines.
We also have the choice of getting a dataframe of this pre-process with
the help of our set_indicators()
function:
si <- set_indicators("./models/basic_ema_xover.yml", step = 1)
set_indicators()
returns the OHLCV data from the model decorated with
the indicator outputs, entry/exit signals, stop loss level, etc.,
before fees. This is especially helpful for a first exploratory
analysis of our trading idea and for plotting other features in the
dataset:
ggplot(si |> dplyr::filter(open_time >= "2024-01-01",
close_time <= "2024-03-31"),
aes(x = close_time)) +
geom_line(aes(y = volume)) +
theme_bw() +
labs(x = NULL, y = "Volume")
In this section, we perform an actual backtesting with the run_model()
function:
run <- run_model("./models/basic_ema_xover.yml", preview = TRUE)
By previewing the results, we will see a plot like in the image below. The upper half is the area for the current step and the lower one applies for the best step found so far (according to the target defined in the model).
Two plots are shown by row. The left one is the balance (the cumulative results + the capital) and the right one is the percentage return per year. Giving these two plots we can have a good idea about the performance before a deeper analysis.
Note: an issue is known for the previewing feature. If the plot pane (in RStudio) has not enough area to fit the plots, a series of warnings related to graphics state are expected. This does not have an effect on the execution nor the stored data.
With the previous variable assignment, run
will keep a structure with
some data that we can reuse later:
- data: string with the path to the dataset.
- model: string with the path to the YAML file.
- target: dataframe with the target function and scope.
- step: the step from set of steps that the target is met.
- variables: dataframe with the variable values at such step.
- metrics: dataframe comparing the metrics per scope at such step.
We also have the choice of making no variable assignment by simply
running run_model("./models/basic_ema_xover.yml")
, but our
recommendation is to make a variable assignment to keep reference
values.
After running the model, we can obtain very general information in terms of timings and signals for a first contact with the results:
The following code computes the timings from the stored balances:
balances() |>
timings()
The output shows information about the total time covered by the dataset and the time per scope. In the example, the total duration of the dataset is about 6 years and 10 months. The in-sample period has a duration of 5 years and represents the 73% of the data. The out-of-sample period has a duration of 1 year and 10 months, and represents the 27% of the data:
$total_time
start end duration
1 2018-01-01 04:59:59 2024-11-01 00:59:59 6y 10m -1d 20H 0M 0S
$scope_time
scope start end duration bars p
1 is 2018-01-01 04:59:59 2023-01-01 00:59:59 5y 0m -1d 20H 0M 0S 10940 0.7312834
2 oos 2023-01-01 04:59:59 2024-11-01 00:59:59 1y 10m -1d 20H 0M 0S 4020 0.2687166
The next commands shows information about the signals. The signals()
function requires the number of step this time. The variable assignment
from the run_model()
is useful here. Since we are interested in the
best step from the backtesting execution, we will get it by simply
calling run$step
:
balances() |>
signals(run$step)
Specifically, this step has 69 total entries and 68 exits (the last trade is not closed in the chosen period). We also obtain the number of entries/exits per scope, as well as its proportion (72% for in-sample, 28% for out-of-sample). Other information such exposure (52% in this example) and average trade duration (2.65 weeks) are available:
$total_entries
[1] 69
$total_exits
[1] 68
$scope
scope entries exits p_entries p_exits
1 is 49 49 0.7101449 0.7205882
2 oos 20 19 0.2898551 0.2794118
$exposure
bars trading p_trading
1 14960 7846 0.5244652
$avg_bars
[1] 111.2565
$avg_duration
[1] "1602092.90322581s (~2.65 weeks)"
If we move to a more performance related results, the most immediate
action is to read the data kept in the run
variable. By simply typing
run
, an interesting summary is returned. Now we are interested in the
metrics report and the values of the variables at this step:
$variables
variable value
1 ema1_periods 45
2 ema2_periods 95
$metrics
metric is oos full
1 trades 49.00000000 19.00000000 68.00000000
2 monthly_trades 0.81673985 0.86384747 0.82932267
3 pct_return 3.02195212 0.23015123 3.94760935
4 avg_pct_return 0.06964976 0.04871880 0.06380140
5 avg_pct_winner 0.37635671 0.21289373 0.32186905
6 avg_pct_loser -0.05303302 -0.04704990 -0.05150542
7 win_rate 0.28571429 0.36842105 0.30882353
8 winners_losers_ratio 0.40000000 0.58333333 0.44680851
9 avg_bars 109.02040816 122.52631579 112.79411765
10 exposure 0.49277879 0.61069652 0.52446524
11 expectancy 616.72492249 487.18801481 580.53078652
12 profit_factor 2.74150157 2.63949541 2.71645975
13 max_consec_winners 3.00000000 2.00000000 3.00000000
14 max_consec_losers 7.00000000 3.00000000 7.00000000
15 reward_risk_ratio 3.53155523 2.14893726 3.09163133
16 return_exposure_ratio 6.13247193 0.37686678 7.52692274
17 cagr 0.32098662 0.11964531 0.26364654
18 annual_return 0.32095304 0.11961388 0.26362683
19 annual_volatility 0.75166502 0.13103208 0.64639444
20 pct_dd -0.42532318 -0.09665300 -0.42532318
21 return_dd 7.02247107 2.15106026 9.17353132
22 cagr_dd 0.75468876 1.23788517 0.61987343
23 sharpe_ratio 0.57773545 0.72563484 0.51021844
24 r_squared 0.85038789 0.65671016 0.88255150
25 k_ratio 0.33771018 0.31329641 0.33029779
26 sqn 1.41034537 1.25302417 1.72405303
27 var -0.09991205 -0.02361677 -0.05851872
28 cvar -0.11305337 -0.02679445 -0.10368965
There are other functions that allow exploring the balances, variables and metrics obtained from the model execution. If we are not satisfied with the results for the target metrics, we can explore other metrics.
In the following example we filter the dataset returned by metrics()
.
We are interested in the in-sample period and only the steps that meet a
return / drawdown greater than 5. We reshape the result and sort in
descending mode by Sharpe ratio. If we printed at this point, we would
see that the best step is the number 6, so the next function filters by
6 and finally we print the scope comparison:
metrics() |>
filter_is() |>
filter_metrics(return_dd > 5) |>
pivot_metrics() |>
sort_metrics(-sharpe_ratio) |>
filter_step(6) |>
compare_scopes() |>
print(n = 30)
If this resulted in better metrics for us, we could get the variable configuration as follows:
variables() |>
filter_step(6)
There is much more to explore with the functions provided by the package, but going deeper in this aspect is not the purpose of this example nor document.
Printing metrics is essential as it is the way of analyzing our strategy with numbers, but making visuals of the performance is also necessary.
The first plot one can expect is the balance as shown in the previewing.
The plot_balance()
function offers this and adds drawdown subplots:
plot_balance(step = run$step)
The out-of-sample period is shadowed and two drawdown subplots are shown. We are usually interested in percentage terms, but in this plot we have available the drawdown in monetary terms as well. This double representation of loss helps us to understand the risk of the strategy and allows us keeping focus on the money we would lose.
Once known the balance curve and drawdown, we might be curious about the
individual returns, their distribution and how they accumulate in
monthly and annual terms. This information is given by the
plot_returns()
function:
plot_returns(step = run$step)
An strategy performance must also consider timing aspects as the
duration of the trades. This is just what plot_durations()
gives us:
plot_durations(step = run$step)
The durations are given in number of bars so we must multiply by the timeframe if we want them in terms of time.
Making profitable strategies with low drawdowns is our target, but at
the end of the day, our strategies must also beat certain benchmarks.
One simple benchmark is the buy and hold strategy and what we must
question is if our trading strategy is better than that, meaning that
our effort to design and analyze strategies makes sense. The
plot_benchmark()
function shows us a comparison:
plot_benchmark(step = run$step)
The two strategies are compared in the same plot as they are represented in percentage terms. Additional information is useful here like the portion of time that the trading strategy is above the buy and hold strategy, the correlation of both series and the correlation in annual return terms.
An interesting and important trading performance study is the analysis
of the excursions per trade so we can basically see what loss assume
each winning and losing trade. We can access this information with the
plot_excursions()
function, which returns a compound plot with
different information about the % price excursions from the entry price:
plot_excursions(step = run$step)
traderlab offers a robustness test based on Monte Carlo simulations with
the plot_monte_carlo()
function. This function allows different
configurations that helps us to determine if our strategy is robust
enough:
plot_monte_carlo(step = run$step, scope = "oos", samples = 1000)
For a series of relevant performance metrics, the values for in-sample and out-of-sample are compared. The metrics distribution as a result of the Monte Carlo simulations and its average are also shown. In addition, in the console are printed the simulated values for different confidence intervals.
Giving a training about this test and plot is out of the scope of this document.
This is the list of available indicators and their required parameters:
- Output: high - low
Example:
indicators:
ar:
indicator: AR
- Parameter:
- periods
- Outputs:
- up
- dn
- osc: up - dn
- Range:
- up: [0, 100]
- dn: [0, 100]
- osc: [-100, 100]
Example:
indicators:
aroon:
indicator: Aroon
periods: 6
- Parameter:
- periods
- Outputs:
- dip: positive direction index
- din: negative direction index
- dx: direction index
- adx: average direction index (trend strength)
- Range: [0, 100]
Example:
indicators:
adx:
indicator: ADX
periods: 20
- Parameter:
- periods: number of bars to average.
- Output: mean(TR)
Example:
indicators:
atr:
indicator: ATR
periods: 10
- Parameters:
- periods
- sd
- Outputs:
- upr = SMA + SD * 2
- avg = SMA
- lwr = SMA - SD * 2
- pct = (close - lwr) / (upr - lwr)
Example:
indicators:
bol:
indicator: Bollinger
periods: 10
sd: 2
- Parameter:
- expression: custom logic
Example:
indicators:
hl_avg:
indicator: custom_indicator
expression: (high + low) / 2
- Parameters:
- source: open_time or close_time
Example:
indicators:
mday:
indicator: day_of_month
source: close_time
- Parameters:
- source: open_time or close_time
Example:
indicators:
wday:
indicator: day_of_week
source: close_time
- Parameter:
- source: open_time or close_time
- Output: days in current month - current day
Example:
indicators:
dtme:
indicator: days_to_month_end
source: close_time
- Parameter:
- source: open, high or low
- Output: current day open, high or low
Example:
indicators:
daily_high:
indicator: daily_value
source: high
- Parameter:
- periods
- Outputs:
- upr
- avg
- lwr
Example:
indicators:
dch:
indicator: Donchian
periods: 10
- Parameter:
- source
- periods
Example:
indicators:
dir:
indicator: down_in_row
source: close
periods: 3
- Parameters:
- source: open, high, low or close
- periods: number of bars to average
Example:
indicators:
ema:
indicator: EMA
source: close
periods: 20
This is not an indicator, but it is formulated and applied on the OHLCV data in the same way
- Parameters:
- source
- pct_dev: deviation from the source value in %
Example:
indicators:
noisy_close:
indicator: noise
source: close
pct_dev: 0.005
- Parameters:
- source: open, high, low or close
- periods
Example:
indicators:
highest:
indicator: highest_value
source: close
periods: 15
- Parameters:
- source: open_time or close_time
Example:
indicators:
hour:
indicator: hour
source: close_time
- Parameter:
- source: open_time or close_time
Example:
indicators:
htde:
indicator: hours_to_day_end
source: close_time
- Parameters:
- source: open, high, low or close
- periods: number of bars to average
Example:
indicators:
hma:
indicator: HMA
source: close
periods: 20
- Output: 100 * (close - low) / (high - low)
- Range: [0, 100]
Example:
indicators:
ibs:
indicator: IBS
- Parameters:
- periods
- atr_periods
- Outputs:
- upr
- avg
- lwr
Example:
indicators:
klt:
indicator: Keltner
periods: 20
atr_periods: 2
- Parameters:
- source
- periods
Example:
indicators:
close1:
indicator: lag_indicator
source: close
periods: 1
- Parameters:
- source: open, high, low or close
- periods
- Outputs:
- fit = OLS fitting
- lwr = lower limit from 95% prediction interval
- upr = upper limit from 95% prediction interval
- slope
Example:
indicators:
reg:
indicator: linear_regression
source: close
periods: 20
- Parameters:
- source: open, high, low or close
- periods
Example:
indicators:
lowest:
indicator: lowest_value
source: close
periods: 15
- Parameter:
- source: open_time or close_time
Example:
indicators:
month:
indicator: month
source: close_time
- Parameters:
- fast_periods
- slow_periods
- signal_periods
- Outputs:
- macd
- signal
- Range: [-n, n]
Example:
indicators:
macd:
indicator: MACD
fast_periods: 5
slow_periods: 10
signal_periods: 10
Example:
indicators:
nbars:
indicator: n_bars
- Parameters:
- fast_periods
- slow_periods
- signal_periods
- Outputs:
- ppo: 100 * (fast EMA - slow EMA) / slow EMA
- hist: PPO - signal EMA
- Range: [-n, n]
Example:
indicators:
ppo:
indicator: PPO
source: close
fast_periods: 5
slow_periods: 10
signal_periods: 10
- Outputs:
- p = (high + low + close) / 3
- r1 = (p * 2) - low
- r2 = p + (high - low)
- s1 = (p * 2) - high
- s2 = p - (high - low)
Example:
indicators:
pvts:
indicator: pivot_points
- Parameters:
- source: open, high, low or close
- start: start hour 0-23
- end: end hour 0-23
Example:
indicators:
rhv:
indicator: range_highest_value
source: close
start: 0
end: 14
- Parameters:
- source: open, high, low or close
- start: start hour 0-23
- end: end hour 0-23
Example:
indicators:
rlv:
indicator: range_lowest_value
source: close
start: 0
end: 14
- Parameters:
- source: open, high, low or close
- periods
- Output: 100 - 100 / (1 + (average wins / average losses))
- Range: [0, 100]
Example:
indicators:
rsi:
indicator: RSI
source: close
periods: 20
- Parameters:
- source: open, high, low or close
- periods
Example:
indicators:
sma:
indicator: SMA
source: close
periods: 20
- Parameters:
- source: if not provided, source = HLC
- fast_k_periods
- fast_d_periods
- slow_d_periods
- Outputs:
- fastK
- fastD
- slowD
- Range: [0, 1]
Example:
indicators:
stoch:
indicator: stochastic
fast_k_periods: 13
fast_d_periods: 6
slow_d_periods: 2
- Output: max(high - low, high - close[-1], close[-1] - low)
Example:
indicators:
tr:
indicator: TR
- Parameter:
- source
- periods
Example:
indicators:
uir:
indicator: up_in_row
source: close
periods: 3
- Parameters:
- price_source: open, high, low or close
- volume_source: volume
- periods: number of bars to average
Example:
indicators:
vwap:
indicator: VWAP
price_source: close
volume_source: volume
periods: 20
- Parameter:
- periods
- Output: ((highest high - close) / (highest high - lowest low)) * (- 100)
- Range: [-100, 0]
Example:
indicators:
wpr:
indicator: WPR
periods: 20
- Name: trades
- Output: number of closed trades
- Name: pct_return
- Output: (final balance - initial balance) / initial balance
- Range: [0, 1]
- Name: avg_pct_winner
- Output: mean(positive return %)
- Name: avg_pct_loser
- Output: mean(negative return %)
- Name: cagr
- Output: (final balance / initial balance)^(1 / years) - 1
- Name: win_rate
- Output: winning trades / total trades
- Range: [0, 1]
- Name: profit_factor
- Output: sum(positive returns) / |sum(negative returns)|
- Name: wl_ratio
- Output: number of positive returns / number of negative returns
- Name: rr_ratio
- Output: mean(r_multiples), where r_multiples = (target price - entry price) / (entry price - stop loss price)
Stop loss is required to compute this metric.
- Name: avg_bars
- Output: mean(trade durations)
- Name: exposure
- Output: number of trading bars / total bars
- Name: expectancy
- Output: win % * mean(positive returns) - loss % * |mean(negative returns)|
- Name: max_consec_wins
- Output: number of consecutive trades with positive return
- Name: max_consec_losses
- Output: number of consecutive trades with negative return
- Name: exp_a_ret
- Output: mean(annual returns)
- Name: std_a_ret
- Output: sd(annual returns)
- Name: annual_return
- Output: (total percent return + 1) ^ (12/months) - 1
- Name: annual_volatility
- Output: sd(monthly percent return) * sqrt(12)
- Name: risk_adj_return
- Output: total % return / exposure
- Name: pct_dd
- Output: max(drawdown)
- Range: [-1, 0]
- Name: return_dd
- Output: (final balance - initial balance) / max(drawdown)
- Name: cagr_dd
- Output: CAGR / max(drawdown)
- Name: sharpe_ratio
- Output: (mean(annual returns) - risk-free rate) / sd(annual returns)
- Name: r_squared
- Output: R² statistic from linear regression
- Range: [-1, 1]
- Name: k_ratio
- Output: slope of regression line / (standard error of slope * total bars)
- Name: sqn
- Output: (mean(r multiples) / sd(r multiples)) * sqrt(if(number of trades > 100, number of trades, 100), where r_multiples = (target price - entry price) / (entry price - stop loss price)
Stop loss is required to compute this metric.
- Name: var
- Output: 5th percentile of monthly returns
- Name: cvar
- Output: mean under 5th percentile of monthly returns