How to determine the best period for a ti-engine function
This guide shows how to programmatically determine the best period for your indicator using the JavaScript package ti-engine.
The rating model is overly simplified and should be refined to suit your needs before usage.
π― Goalβ
- Determine the best period for the RSI using an in-memory dataset
π¦ Requirementsβ
Install ti-engine:
# npm
npm install ti-engine
# yarn
yarn add ti-engine
# pnpm
pnpm add ti-engine
Initialize (recommended, required in browsers):
import init from "ti-engine";
await init(); // In Node it's a no-op; in browsers it loads WASM
π» Step-by-Stepβ
1. Use an in-memory datasetβ
For this guide, weβll use a constant array of prices:
const data = [
6037.59, 5970.84, 5906.94, 5881.63, 5868.55, 5942.47, 5975.38, 5909.03,
5918.25, 5827.04, 5836.22, 5842.91, 5949.91, 5937.34, 5996.66, 6049.24,
6086.37, 6118.71, 6101.24, 6012.28, 6067.70, 6039.31, 6071.17, 6040.53,
5994.57, 6037.88, 6061.48, 6083.57, 6025.99, 6066.44, 6068.50, 6051.97,
6115.07, 6114.63, 6129.58, 6144.15, 6117.52, 6013.13, 5983.25, 5955.25,
5956.06, 5861.57, 5954.50, 5849.72, 5778.15, 5842.63, 5738.52, 5770.20,
5614.56, 5572.07, 5599.30, 5521.52, 5638.94
];
2. Calculate the RSI for multiple periodsβ
The common/default RSI period is 14. Weβll iterate from 2 to 15 to see if a different period performs better (excluding 1 to avoid degenerate windows).
import init, { momentumIndicators, ConstantModelType } from "ti-engine";
await init();
const model = ConstantModelType.SmoothedMovingAverage; // default flavor for RSI
// Example bulk call for a given period:
// const rsiSeries = momentumIndicators.bulk.relativeStrengthIndex(data, model, period);
3. Rate each different RSI to find the bestβ
The logic is overly simple for the purpose of the guide.
- If RSI > 70 (overbought) and the next price < current price, the period gets +1
- If RSI < 30 (oversold) and the next price > current price, the period gets +1
We normalize the score by the number of "attempts" (how many times we evaluated a signal).
Alignment note for bulk outputs:
- momentumIndicators.bulk.relativeStrengthIndex(prices, model, period) returns an array of length prices.length - period + 1
- rsiSeries[0] corresponds to the window prices[0..period-1] and βanchorsβ at prices[period-1]
- For price index i (starting at period-1), the RSI index is rsiIdx = i - (period - 1)
import { momentumIndicators, ConstantModelType } from "ti-engine";
/**
* Returns { period: number, rating: number }
*/
function chooseBestRsiPeriod(prices, minP = 2, maxP = 15) {
const model = ConstantModelType.SmoothedMovingAverage;
let bestRating = -1;
let bestPeriod = minP;
for (let p = minP; p <= maxP; p++) {
const rsi = momentumIndicators.bulk.relativeStrengthIndex(prices, model, p);
let currentRating = 0;
let attempts = 0;
// rsi[0] anchors at prices[p - 1]
for (let i = p - 1; i < prices.length - 1; i++) {
const rsiVal = rsi[i - (p - 1)];
// Overbought: expect price to fall next step
if (rsiVal > 70) {
attempts += 1;
if (prices[i + 1] < prices[i]) currentRating += 1;
}
// Oversold: expect price to rise next step
if (rsiVal < 30) {
attempts += 1;
if (prices[i + 1] > prices[i]) currentRating += 1;
}
}
const averageRating = attempts > 0 ? currentRating / attempts : 0;
if (averageRating > bestRating) {
bestRating = averageRating;
bestPeriod = p;
}
}
return { period: bestPeriod, rating: bestRating };
}
4. Full exampleβ
import init, { momentumIndicators, ConstantModelType } from "ti-engine";
await init();
const data = [
6037.59, 5970.84, 5906.94, 5881.63, 5868.55, 5942.47, 5975.38, 5909.03,
5918.25, 5827.04, 5836.22, 5842.91, 5949.91, 5937.34, 5996.66, 6049.24,
6086.37, 6118.71, 6101.24, 6012.28, 6067.70, 6039.31, 6071.17, 6040.53,
5994.57, 6037.88, 6061.48, 6083.57, 6025.99, 6066.44, 6068.50, 6051.97,
6115.07, 6114.63, 6129.58, 6144.15, 6117.52, 6013.13, 5983.25, 5955.25,
5956.06, 5861.57, 5954.50, 5849.72, 5778.15, 5842.63, 5738.52, 5770.20,
5614.56, 5572.07, 5599.30, 5521.52, 5638.94
];
function chooseBestRsiPeriod(prices, minP = 2, maxP = 15) {
const model = ConstantModelType.SmoothedMovingAverage;
let bestRating = -1;
let bestPeriod = minP;
for (let p = minP; p <= maxP; p++) {
const rsi = momentumIndicators.bulk.relativeStrengthIndex(prices, model, p);
let currentRating = 0;
let attempts = 0;
for (let i = p - 1; i < prices.length - 1; i++) {
const rsiVal = rsi[i - (p - 1)];
if (rsiVal > 70) {
attempts += 1;
if (prices[i + 1] < prices[i]) currentRating += 1;
}
if (rsiVal < 30) {
attempts += 1;
if (prices[i + 1] > prices[i]) currentRating += 1;
}
}
const averageRating = attempts > 0 ? currentRating / attempts : 0;
if (averageRating > bestRating) {
bestRating = averageRating;
bestPeriod = p;
}
}
return { period: bestPeriod, rating: bestRating };
}
const result = chooseBestRsiPeriod(data, 2, 15);
console.log(`Best period for RSI is ${result.period} with a rating of ${result.rating}`);
Notes:
- Valid ConstantModelType values include:
- ConstantModelType.SimpleMovingAverage
- ConstantModelType.SmoothedMovingAverage
- ConstantModelType.ExponentialMovingAverage
- ConstantModelType.SimpleMovingMedian
- ConstantModelType.SimpleMovingMode
π§ͺ Example Outputβ
Best period for RSI is 7 with a rating of 0.56
(Output will vary with data and scoring rules.)
β Next Stepsβ
- Programmatically choose a constant model type (see: choose-constant-model-type.md)
- Combine period selection and constant model type selection
- Introduce the notion of punishment to the rating system (e.g., penalize false signals/whipsaws)