UNPKG

@c9up/technical-indicators-napi

Version:

A Rust-based indicator and Charts library compiled to napi.

181 lines (159 loc) 6.42 kB
import { test } from '@japa/runner' import pkg from '../../index.js' const { volatilityEngine, volatilityBucket } = pkg import { generateTestData } from './lib.mjs' /** Valid regime values as defined by the Rust implementation. */ const VALID_REGIMES = new Set([-1, 0, 1, 2]) test.group('Volatility Engine', (group) => { let marketData group.setup(() => { marketData = generateTestData(200) }) // volatilityEngine test('atr array has same length as input', ({ assert }) => { const result = volatilityEngine(marketData) assert.lengthOf(result.atr, marketData.length) }) test('volatility array has same length as input', ({ assert }) => { const result = volatilityEngine(marketData) assert.lengthOf(result.volatility, marketData.length) }) test('regimes array has same length as input', ({ assert }) => { const result = volatilityEngine(marketData) assert.lengthOf(result.regimes, marketData.length) }) test('atrMultipliers array has same length as input', ({ assert }) => { const result = volatilityEngine(marketData) assert.lengthOf(result.atrMultipliers, marketData.length) }) test('stopDistances array has same length as input', ({ assert }) => { const result = volatilityEngine(marketData) assert.lengthOf(result.stopDistances, marketData.length) }) test('lowThresholds array has same length as input', ({ assert }) => { const result = volatilityEngine(marketData) assert.lengthOf(result.lowThresholds, marketData.length) }) test('highThresholds array has same length as input', ({ assert }) => { const result = volatilityEngine(marketData) assert.lengthOf(result.highThresholds, marketData.length) }) test('regimes values are in the set -1, 0, 1, 2', ({ assert }) => { const result = volatilityEngine(marketData) for (let i = 0; i < result.regimes.length; i++) { const regime = result.regimes[i] assert.isTrue( VALID_REGIMES.has(regime), `regimes[${i}] value ${regime} is not a valid regime` ) } }) test('stopDistances equal atr times atrMultiplier where neither is NaN', ({ assert }) => { const result = volatilityEngine(marketData) for (let i = 0; i < marketData.length; i++) { const atr = result.atr[i] const mult = result.atrMultipliers[i] const stop = result.stopDistances[i] if (!isNaN(atr) && !isNaN(mult) && !isNaN(stop)) { assert.approximately( stop, atr * mult, 1e-9, `stopDistances[${i}] should equal atr[${i}] * atrMultipliers[${i}]` ) } } }) test('result contains all expected output keys', ({ assert }) => { const result = volatilityEngine(marketData) assert.properties(result, [ 'atr', 'volatility', 'regimes', 'atrMultipliers', 'stopDistances', 'lowThresholds', 'highThresholds', ]) }) test('accepts optional parameters without throwing', ({ assert }) => { const result = volatilityEngine(marketData, 14, 20, 50, 30, 25, 75, 1.0, 1.5, 2.0) assert.lengthOf(result.atr, marketData.length) }) // volatilityBucket test('volatilityBucket returns a regime field', ({ assert }) => { const engineResult = volatilityEngine(marketData) const warmup = 30 const volHistory = engineResult.volatility.slice(0, warmup).filter((v) => !isNaN(v)) if (volHistory.length === 0) { assert.isTrue(true, 'skipped: insufficient warm-up data') return } const idx = warmup const result = volatilityBucket( engineResult.atr[idx], engineResult.volatility[idx], volHistory ) assert.property(result, 'regime') }) test('volatilityBucket regime is a valid value', ({ assert }) => { const engineResult = volatilityEngine(marketData) const warmup = 30 const volHistory = engineResult.volatility.slice(0, warmup).filter((v) => !isNaN(v)) if (volHistory.length === 0) { assert.isTrue(true, 'skipped: insufficient warm-up data') return } const idx = warmup const result = volatilityBucket( engineResult.atr[idx], engineResult.volatility[idx], volHistory ) // regime may be numeric or string; verify it is not undefined assert.isDefined(result.regime) }) test('volatilityBucket returns atr, volatility, stopDistance, lowThreshold, highThreshold', ({ assert }) => { const engineResult = volatilityEngine(marketData) const warmup = 30 const volHistory = engineResult.volatility.slice(0, warmup).filter((v) => !isNaN(v)) if (volHistory.length === 0) { assert.isTrue(true, 'skipped: insufficient warm-up data') return } const idx = warmup const result = volatilityBucket( engineResult.atr[idx], engineResult.volatility[idx], volHistory ) assert.properties(result, [ 'regime', 'atrMultiplier', 'atr', 'volatility', 'stopDistance', 'lowThreshold', 'highThreshold', ]) }) test('volatilityBucket stopDistance equals atr times atrMultiplier', ({ assert }) => { const engineResult = volatilityEngine(marketData) const warmup = 30 const volHistory = engineResult.volatility.slice(0, warmup).filter((v) => !isNaN(v)) if (volHistory.length === 0) { assert.isTrue(true, 'skipped: insufficient warm-up data') return } const idx = warmup const result = volatilityBucket( engineResult.atr[idx], engineResult.volatility[idx], volHistory ) if (!isNaN(result.atr) && !isNaN(result.atrMultiplier) && !isNaN(result.stopDistance)) { assert.approximately(result.stopDistance, result.atr * result.atrMultiplier, 1e-9) } }) })