
Trendyways is a lightweight JavaScript library that computes financial technical analysis indicators on stock time series data.
Provide an array of price values or OHLC objects, and it returns arrays of indicator values ready for visualization.
You can use the library to build interactive stock charts, backtesting tools, or trading dashboards directly in the browser.
Features:
- Computes simple, exponential, and weighted moving averages on numeric or OHLCV-formatted data.
- Generates Bollinger bands with a configurable window size and standard deviation multiplier.
- Calculates RSI, MACD, momentum, and rate of change for momentum and trend analysis.
- Produces On-Balance Volume and Price and Volume Trend values from separate price and volume arrays.
- Calculates the Money Flow Index from OHLCV candlestick records.
- Computes Average True Range for period-by-period volatility measurement.
- Generates the Average Directional Index for trend strength scoring across a 14-period window.
- Produces stochastic oscillator values with separate K and D smoothing period settings.
- 4 pivot point systems: Floor, Tom DeMark’s, Woodies, and Camarilla.
- Calculates Fibonacci retracement levels for uptrend and downtrend series across six standard ratios.
- Statistical utilities for min, max, mean, standard deviation, MSE, RMSE, and MAE.
- Works as a browser script or as a Node.js CommonJS module.
Use Cases:
- A stock chart dashboard needs MACD and RSI calculations fed directly from a REST API response to support real-time indicator overlays.
- A backtesting script processes full historical CSV datasets through Bollinger band and pivot point functions entirely in the browser, skipping a backend computation service.
- A technical analysis learning tool draws Fibonacci retracement levels across selectable date ranges in both uptrend and downtrend modes.
- A Node.js preprocessing script runs RSI, ATR, and EMA across raw market data before storing results in a database for faster dashboard queries.
How To Use It:
Install and Import Trendyways
Install the package from npm for Node.js, bundlers, or server-side calculations.
npm install trendyways
Load the module in CommonJS code.
const tw = require('trendyways');
For browser usage, include the minified file before your own script.
<script src="trendyways.min.js"></script> <script src="app.js"></script>
The browser build exposes the library as tw.
console.log(tw.max([101, 104, 99, 108]));
Add the Target Data
Most indicator methods expect either numeric arrays or candle objects. Candle objects usually use short OHLCV keys.
const candles = [
{ o: 100, h: 104, l: 98, c: 102, v: 18000 },
{ o: 102, h: 106, l: 101, c: 105, v: 21000 },
{ o: 105, h: 107, l: 103, c: 104, v: 19000 },
{ o: 104, h: 109, l: 104, c: 108, v: 25000 },
{ o: 108, h: 111, l: 106, c: 110, v: 27000 }
];
The default price field for several object-based methods is c, which represents the close price.
Basic Usage
This browser example calculates a moving average and prints the output into a page.
<pre id="indicator-output"></pre>
<script src="trendyways.min.js"></script>
<script>
const candles = [
{ o: 100, h: 104, l: 98, c: 102, v: 18000 },
{ o: 102, h: 106, l: 101, c: 105, v: 21000 },
{ o: 105, h: 107, l: 103, c: 104, v: 19000 },
{ o: 104, h: 109, l: 104, c: 108, v: 25000 },
{ o: 108, h: 111, l: 106, c: 110, v: 27000 }
];
const result = tw.ma(candles.slice(), 3, ['c'], 'sma3');
document.getElementById('indicator-output').textContent =
JSON.stringify(result, null, 2);
</script>
The setup uses a local browser file. The script creates a candle array, passes a copied array into the moving average method, and stores the calculated values under sma3. The first two candles have no 3-period result. The method appends results to the last matching candle objects.
Configuration Options
Trendyways does not expose a global options object. Each calculation uses function arguments.
data(Array): Numeric series or candle object series.period(number): Rolling window size for moving averages, RSI, momentum, ROC, ATR, and related indicators.targetAttr(string or Array): Object field used as the input value. Common values includec,h,l, andv.outputAttr(string): Field name added to returned candle objects for several object-based calculations.weights(Array): Weight list for weighted moving average calculations.multiplier(number): Bollinger Band width multiplier.trend(string): Fibonacci retracement direction. UseUPTRENDorDOWNTREND.smoothPeriod(number): Signal smoothing period for stochastic calculations.
API Methods
const prices = [101.2, 102.8, 100.5, 106.1, 108.4];
const volumes = [12000, 15000, 11000, 21000, 24000];
const candles = [
{ o: 100, h: 104, l: 98, c: 102, v: 18000 },
{ o: 102, h: 106, l: 101, c: 105, v: 21000 },
{ o: 105, h: 107, l: 103, c: 104, v: 19000 },
{ o: 104, h: 109, l: 104, c: 108, v: 25000 },
{ o: 108, h: 111, l: 106, c: 110, v: 27000 }
];
// Return the largest value in a numeric series.
tw.max(prices);
// Return the smallest value in a numeric series.
tw.min(prices);
// Return the arithmetic mean of a numeric series or object field.
tw.mean(prices);
tw.mean(candles, 'c');
// Return standard deviation for a numeric series or object field.
tw.sd(prices);
tw.sd(candles, 'c');
// Return mean squared error between two numeric series.
tw.mse([100, 104, 108], [101, 103, 109]);
// Return root mean squared error between two numeric series.
tw.rmse([100, 104, 108], [101, 103, 109]);
// Return mean absolute error between two numeric series.
tw.mae([100, 104, 108], [101, 103, 109]);
// Append a simple moving average to candle objects.
tw.ma(candles.slice(), 3, ['c'], 'sma3');
// Append an exponential moving average to candle objects.
tw.ema(candles.slice(), 3, 'c', 'ema3');
// Append a weighted moving average to candle objects.
tw.wma(candles.slice(), [1, 2, 3], ['c']);
// Return Bollinger Band values with moving average, upper band, and lower band.
tw.bollinger(candles.slice(), 3, 2, ['c']);
// Return On-Balance Volume from close prices and volume values.
tw.obv(prices, volumes);
// Return Price and Volume Trend from close prices and volume values.
tw.vpt(prices, volumes);
// Append Money Flow Index values to candle objects.
tw.mfi(candles.slice());
// Append MACD line, signal, and histogram values to candle objects.
tw.macd(candles.slice(), ['c']);
// Append n-period momentum values to candle objects.
tw.momentum(candles.slice(), 3);
// Append n-period rate of change values to candle objects.
tw.roc(candles.slice(), 3);
// Append n-period RSI values to candle objects.
tw.rsi(candles.slice(), 14);
// Append Average True Range values to candle objects.
tw.atr(candles.slice(), 14);
// Append Average Directional Index values to candle objects.
tw.adx(candles.slice());
// Return stochastic oscillator values from high, low, and close arrays.
tw.stochastic([104, 106, 107], [98, 101, 103], [102, 105, 104], 14, 3);
// Append floor pivot levels to candle objects.
tw.floorPivots(candles.slice());
// Append Tom DeMark projected high and low values to candle objects.
tw.tomDemarksPoints(candles.slice());
// Append Woodie pivot levels to candle objects.
tw.woodiesPoints(candles.slice());
// Append Camarilla support and resistance levels to candle objects.
tw.camarillaPoints(candles.slice());
// Return Fibonacci retracement levels for each candle.
tw.fibonacciRetrs(candles.slice(), 'UPTREND');
// Return element-by-element differences between two vectors.
tw.diffVectors([105, 108, 111], [100, 107, 109]);
// Square every value in a numeric vector.
tw.powVector([2, 4, 6]);
// Sum a numeric vector or object field.
tw.sumVector(prices);
tw.sumVector(candles, 'c');
// Average a numeric vector or object field.
tw.avgVector(prices);
tw.avgVector(candles, 'c');
// Return absolute values for a numeric vector.
tw.absVector([-3, 5, -8]);
// Divide one numeric vector by another.
tw.divVector([10, 20, 30], [2, 4, 5]);
// Combine two vectors through a custom callback.
tw.combineVectors([1, 2, 3], [4, 5, 6], function (a, b) {
return a + b;
});
// Resolve a value from an object by trying one or more field names.
tw.resolveParam({ close: 104, c: 103 }, ['c', 'close']);
// Return a fallback value when the first value is undefined.
tw.valueIfUndef(undefined, 14);
// Check if a value is undefined.
tw.isUndef(undefined);
// Append calculated values to the trailing objects in a series.
tw.reverseAppend(candles.slice(), [{ custom: 1 }], 'custom');
// Extract a field from an object series and replace undefined values with 0.
tw.flat(candles, 'c');
// Fill a missing field on every object in a series.
tw.fill(candles, 'signal', 0);
// Run a rolling window operation over a numeric or object series.
tw.windowOp(prices, 3, function (windowValues) {
return tw.mean(windowValues);
});
Advanced Examples
Example 1: Calculate MA and Bollinger Bands for a Chart Series
This example prepares a 5-period moving average and Bollinger Band values from candle data.
const candles = [
{ o: 100, h: 105, l: 99, c: 103, v: 14000 },
{ o: 103, h: 108, l: 102, c: 106, v: 21000 },
{ o: 106, h: 109, l: 104, c: 105, v: 18000 },
{ o: 105, h: 111, l: 105, c: 110, v: 26000 },
{ o: 110, h: 113, l: 108, c: 112, v: 24000 },
{ o: 112, h: 114, l: 109, c: 111, v: 20000 },
{ o: 111, h: 116, l: 110, c: 115, v: 28000 }
];
const candlesWithSma = tw.ma(candles.slice(), 5, ['c'], 'sma5');
const bands = tw.bollinger(candles.slice(), 5, 2, ['c']);
console.log(candlesWithSma);
console.log(bands);
The moving average method appends sma5 to the final candles in the rolling window range. The Bollinger method returns separate objects with ma, ub, and lb values.
Example 2: Build a Momentum Panel with RSI, ROC, and MACD
A trading dashboard can calculate several momentum indicators from the same candle list. Use enough candles for each rolling period, especially RSI and MACD.
const apiCandles = getHistoricalCandlesFromYourApi();
const rsiData = tw.rsi(apiCandles.slice(), 14);
const rocData = tw.roc(apiCandles.slice(), 12);
const macdData = tw.macd(apiCandles.slice(), ['c']);
const latest = macdData[macdData.length - 1];
console.log({
rsi: rsiData[rsiData.length - 1].rsi,
roc: rocData[rocData.length - 1].roc,
macdLine: latest.macd.line,
signalLine: latest.macd.signal,
histogram: latest.macd.hist
});
tw.rsi() returns [-1] when the series contains fewer records than the requested period plus one. Keep that guard in your UI before you render an indicator value.
Example 3: Render Pivot Levels for the Latest Candle
Support and resistance tables need compact values from the latest OHLC record. Trendyways appends pivot objects to the source candle records.
const dailyCandles = [
{ o: 92, h: 98, l: 90, c: 96, v: 34000 },
{ o: 96, h: 101, l: 95, c: 99, v: 41000 },
{ o: 99, h: 104, l: 97, c: 102, v: 39000 }
];
const floorData = tw.floorPivots(dailyCandles.slice());
const woodieData = tw.woodiesPoints(dailyCandles.slice());
const camarillaData = tw.camarillaPoints(dailyCandles.slice());
console.log(floorData[floorData.length - 1].floor);
console.log(woodieData[woodieData.length - 1].wood);
console.log(camarillaData[camarillaData.length - 1].cam);
Each pivot method stores its result in a named property. Use those nested values to fill table cells or marker lines on a chart.
Implementation Tips
- Use
.slice()before object-based calculations if your original candle array must stay unchanged. - Give object-based moving averages a custom output field such as
sma20orema50. - Pass
targetAttrtotw.ema()when the input contains objects. - Use at least 15 candle records for a 14-period RSI.
- Use a longer historical series for MACD output since it depends on 12-period, 26-period, and 9-period EMA calculations.
- Keep weighted moving average weights the same length as the intended window.
- The weighted moving average implementation divides the weighted total by window length.
- Load
trendyways.min.jsbefore any browser code that referencestw. - Validate API candle keys before calculation. Most object-based examples use
o,h,l,c, andv.
Alternatives:
- Intuitive Charting Library for Financial Data – DXCharts Lite
- Lightweight Financial Chart JavaScript Library – lightweight-charts v5
- Financial Data Visualization Library for JavaScript – VisionCharts
- GPU-Powered WebGL Canvas Chart Library for JavaScript – WebGL Chart
FAQs:
Q: Why does RSI return [-1]?
A: The input series is too short for the selected period. Add more candle records or reduce the period.
Q: Does Trendyways render charts?
A: No. Trendyways calculates indicator values only. Use a charting library to render candlesticks, overlays, and indicator panels.
Q: Why did my candle objects gain new fields after calculation?
A: Several object-based methods append calculated values to candle objects. Pass a copied array when your application needs to preserve the original objects.
Q: How many records do I need before MACD produces valid output?
A: MACD uses a 26-period slow EMA, a 12-period fast EMA, and a 9-period signal EMA. Records at indices 0 through 24 carry { line: 0, signal: 0, hist: 0 } as placeholders. Feed at least 35 bars to see non-zero results, and aim for 60 or more for the signal line to stabilize.
Q: Can I run indicator calculations on data fetched asynchronously from a market data API?
A: Yes. All trendyways functions run synchronously on the array you pass in. Fetch your data, resolve the promise, build the OHLCV array, then call the indicator functions. The library requires no callback interface or special async integration.







