diff --git a/index.ts b/index.ts index 3db7fa6..90bae67 100644 --- a/index.ts +++ b/index.ts @@ -1,4 +1,4 @@ -export * from './lib/index.ts'; +export * from './src/index.ts'; export const getAvailableIndicators = () => { const AvailableIndicators: string[] = [] // AvailableIndicators.push('sma'); @@ -7,7 +7,7 @@ export const getAvailableIndicators = () => { // AvailableIndicators.push('wema'); // AvailableIndicators.push('macd'); AvailableIndicators.push('rsi'); - // AvailableIndicators.push('bollingerbands'); + AvailableIndicators.push('bollingerbands'); // AvailableIndicators.push('adx'); // AvailableIndicators.push('atr'); // AvailableIndicators.push('truerange'); @@ -29,7 +29,7 @@ export const getAvailableIndicators = () => { // AvailableIndicators.push('heikinashi'); // AvailableIndicators.push('stochasticrsi'); - // AvailableIndicators.push('mfi'); + AvailableIndicators.push('mfi'); // AvailableIndicators.push('averagegain'); // AvailableIndicators.push('averageloss'); diff --git a/lib/StockData.js b/lib/StockData.js deleted file mode 100644 index 645b7e5..0000000 --- a/lib/StockData.js +++ /dev/null @@ -1,20 +0,0 @@ -export default class StockData { - constructor(open, high, low, close, reversedInput) { - this.open = open; - this.high = high; - this.low = low; - this.close = close; - this.reversedInput = reversedInput; - } -} -export class CandleData {} -export class CandleList { - constructor() { - this.open = []; - this.high = []; - this.low = []; - this.close = []; - this.volume = []; - this.timestamp = []; - } -} diff --git a/lib/Utils/AverageGain.ts b/lib/Utils/AverageGain.ts deleted file mode 100644 index 8cabf41..0000000 --- a/lib/Utils/AverageGain.ts +++ /dev/null @@ -1,68 +0,0 @@ -// deno-lint-ignore-file -import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; -export class AvgGainInput extends IndicatorInput { -} -export class AverageGain extends Indicator { -generator: Generator; -static calculate: (input: { reversedInput: any; values?: any[]|undefined; open?: any[]|undefined; high?: any[]|undefined; low?: any[]|undefined; close?: any[]|undefined; volume?: any[]|undefined; timestamp?: any[]|undefined; }) => any; - constructor(input: { period?: any; values?: any; format?: (v: any) => any; } | any) { - super(input); - let values = input.values; - let period = input.period; - let format = this.format; - this.generator = (function* (period) { - // @ts-ignore - var currentValue = yield; - var counter = 1; - var gainSum = 0; - var avgGain; - var gain; - var lastValue = currentValue; - // @ts-ignore - currentValue = yield; - while (true) { - gain = currentValue - lastValue; - gain = gain > 0 ? gain : 0; - if (gain > 0) { - gainSum = gainSum + gain; - } - if (counter < period) { - counter++; - } - else if (avgGain === undefined) { - avgGain = gainSum / period; - } - else { - avgGain = ((avgGain * (period - 1)) + gain) / period; - } - lastValue = currentValue; - avgGain = (avgGain !== undefined) ? format(avgGain) : undefined; - // @ts-ignore - currentValue = yield avgGain; - } - })(period); - this.generator.next(); - this.result = []; - values.forEach((tick: any) => { - var result = this.generator.next(tick); - if (result.value !== undefined) { - this.result.push(result.value); - } - }); - } - nextValue(price: any) { - return this.generator.next(price).value; - } - ; -} -AverageGain.calculate = averagegain; -export function averagegain(input: { reversedInput: any; values?: any[]; open?: any[]; high?: any[]; low?: any[]; close?: any[]; volume?: any[]; timestamp?: any[]; } | any) { - Indicator.reverseInputs(input); - var result = new AverageGain(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/Utils/AverageLoss.ts b/lib/Utils/AverageLoss.ts deleted file mode 100644 index e9ce0fd..0000000 --- a/lib/Utils/AverageLoss.ts +++ /dev/null @@ -1,68 +0,0 @@ -// deno-lint-ignore-file -import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; -export class AvgLossInput extends IndicatorInput { -} -export class AverageLoss extends Indicator { -generator: Generator; -static calculate: (input: { reversedInput: any; values?: any[]|undefined; open?: any[]|undefined; high?: any[]|undefined; low?: any[]|undefined; close?: any[]|undefined; volume?: any[]|undefined; timestamp?: any[]|undefined; }) => any; - constructor(input: { period?: any; values?: any; format?: (v: any) => any; } | any) { - super(input); - let values = input.values; - let period = input.period; - let format = this.format; - this.generator = (function* (period) { - // @ts-ignore - var currentValue = yield; - var counter = 1; - var lossSum = 0; - var avgLoss; - var loss; - var lastValue = currentValue; - // @ts-ignore - currentValue = yield; - while (true) { - loss = lastValue - currentValue; - loss = loss > 0 ? loss : 0; - if (loss > 0) { - lossSum = lossSum + loss; - } - if (counter < period) { - counter++; - } - else if (avgLoss === undefined) { - avgLoss = lossSum / period; - } - else { - avgLoss = ((avgLoss * (period - 1)) + loss) / period; - } - lastValue = currentValue; - avgLoss = (avgLoss !== undefined) ? format(avgLoss) : undefined; - // @ts-ignore - currentValue = yield avgLoss; - } - })(period); - this.generator.next(); - this.result = []; - values.forEach((tick: any) => { - var result = this.generator.next(tick); - if (result.value !== undefined) { - this.result.push(result.value); - } - }); - } - nextValue(price: any) { - return this.generator.next(price).value; - } - ; -} -AverageLoss.calculate = averageloss; -export function averageloss(input: { reversedInput: any; values?: any[]; open?: any[]; high?: any[]; low?: any[]; close?: any[]; volume?: any[]; timestamp?: any[]; } | any) { - Indicator.reverseInputs(input); - var result = new AverageLoss(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/Utils/CrossDown.ts b/lib/Utils/CrossDown.ts deleted file mode 100644 index 50a17b1..0000000 --- a/lib/Utils/CrossDown.ts +++ /dev/null @@ -1,79 +0,0 @@ -// @ts-nocheck -import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; -export class CrossInput extends IndicatorInput { - constructor(lineA, lineB) { - super(); - this.lineA = lineA; - this.lineB = lineB; - } -} -export class CrossDown extends Indicator { - constructor(input) { - super(input); - this.lineA = input.lineA; - this.lineB = input.lineB; - var currentLineA = []; - var currentLineB = []; - const genFn = (function* () { - var current = yield; - var result = false; - while (true) { - currentLineA.unshift(current.valueA); - currentLineB.unshift(current.valueB); - result = current.valueA < current.valueB; - var pointer = 1; - while (result === true && currentLineA[pointer] <= currentLineB[pointer]) { - if (currentLineA[pointer] < currentLineB[pointer]) { - result = false; - } - else if (currentLineA[pointer] > currentLineB[pointer]) { - result = true; - } - else if (currentLineA[pointer] === currentLineB[pointer]) { - pointer += 1; - } - } - if (result === true) { - currentLineA = [current.valueA]; - currentLineB = [current.valueB]; - } - current = yield result; - } - }); - this.generator = genFn(); - this.generator.next(); - this.result = []; - this.lineA.forEach((value, index) => { - var result = this.generator.next({ - valueA: this.lineA[index], - valueB: this.lineB[index] - }); - if (result.value !== undefined) { - this.result.push(result.value); - } - }); - } - static reverseInputs(input) { - if (input.reversedInput) { - input.lineA ? input.lineA.reverse() : undefined; - input.lineB ? input.lineB.reverse() : undefined; - } - } - nextValue(valueA, valueB) { - return this.generator.next({ - valueA: valueA, - valueB: valueB - }).value; - } - ; -} -CrossDown.calculate = crossDown; -export function crossDown(input) { - Indicator.reverseInputs(input); - var result = new CrossDown(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} diff --git a/lib/Utils/CrossOver.ts b/lib/Utils/CrossOver.ts deleted file mode 100644 index be43dca..0000000 --- a/lib/Utils/CrossOver.ts +++ /dev/null @@ -1,63 +0,0 @@ -// @ts-nocheck -import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; -import { CrossUp } from './CrossUp.ts'; -import { CrossDown } from './CrossDown.ts'; -export class CrossInput extends IndicatorInput { - constructor(lineA, lineB) { - super(); - this.lineA = lineA; - this.lineB = lineB; - } -} -export class CrossOver extends Indicator { - constructor(input) { - super(input); - var crossUp = new CrossUp({ lineA: input.lineA, lineB: input.lineB }); - var crossDown = new CrossDown({ lineA: input.lineA, lineB: input.lineB }); - const genFn = (function* () { - var current = yield; - var result = false; - var first = true; - while (true) { - var nextUp = crossUp.nextValue(current.valueA, current.valueB); - var nextDown = crossDown.nextValue(current.valueA, current.valueB); - result = nextUp || nextDown; - if (first) - result = false; - first = false; - current = yield result; - } - }); - this.generator = genFn(); - this.generator.next(); - var resultA = crossUp.getResult(); - var resultB = crossDown.getResult(); - this.result = resultA.map((a, index) => { - if (index === 0) - return false; - return !!(a || resultB[index]); - }); - } - static reverseInputs(input) { - if (input.reversedInput) { - input.lineA ? input.lineA.reverse() : undefined; - input.lineB ? input.lineB.reverse() : undefined; - } - } - nextValue(valueA, valueB) { - return this.generator.next({ - valueA: valueA, - valueB: valueB - }).value; - } -} -CrossOver.calculate = crossOver; -export function crossOver(input) { - Indicator.reverseInputs(input); - var result = new CrossOver(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} diff --git a/lib/Utils/CrossUp.ts b/lib/Utils/CrossUp.ts deleted file mode 100644 index d449966..0000000 --- a/lib/Utils/CrossUp.ts +++ /dev/null @@ -1,79 +0,0 @@ -// @ts-nocheck -import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; -export class CrossInput extends IndicatorInput { - constructor(lineA, lineB) { - super(); - this.lineA = lineA; - this.lineB = lineB; - } -} -export class CrossUp extends Indicator { - constructor(input) { - super(input); - this.lineA = input.lineA; - this.lineB = input.lineB; - var currentLineA = []; - var currentLineB = []; - const genFn = (function* () { - var current = yield; - var result = false; - while (true) { - currentLineA.unshift(current.valueA); - currentLineB.unshift(current.valueB); - result = current.valueA > current.valueB; - var pointer = 1; - while (result === true && currentLineA[pointer] >= currentLineB[pointer]) { - if (currentLineA[pointer] > currentLineB[pointer]) { - result = false; - } - else if (currentLineA[pointer] < currentLineB[pointer]) { - result = true; - } - else if (currentLineA[pointer] === currentLineB[pointer]) { - pointer += 1; - } - } - if (result === true) { - currentLineA = [current.valueA]; - currentLineB = [current.valueB]; - } - current = yield result; - } - }); - this.generator = genFn(); - this.generator.next(); - this.result = []; - this.lineA.forEach((value, index) => { - var result = this.generator.next({ - valueA: this.lineA[index], - valueB: this.lineB[index] - }); - if (result.value !== undefined) { - this.result.push(result.value); - } - }); - } - static reverseInputs(input) { - if (input.reversedInput) { - input.lineA ? input.lineA.reverse() : undefined; - input.lineB ? input.lineB.reverse() : undefined; - } - } - nextValue(valueA, valueB) { - return this.generator.next({ - valueA: valueA, - valueB: valueB - }).value; - } - ; -} -CrossUp.calculate = crossUp; -export function crossUp(input) { - Indicator.reverseInputs(input); - var result = new CrossUp(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} diff --git a/lib/Utils/FixedSizeLinkedList.ts b/lib/Utils/FixedSizeLinkedList.ts deleted file mode 100644 index 7c923b4..0000000 --- a/lib/Utils/FixedSizeLinkedList.ts +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Created by AAravindan on 5/7/16. - */ -import { LinkedList } from './LinkedList.ts'; -export default class FixedSizeLinkedList extends LinkedList { -size: any; -maintainHigh: undefined; -maintainLow: undefined; -maintainSum: undefined; -totalPushed: number; -periodHigh: number; -periodLow: number; -periodSum: number; -_push: (data: any) => void; -lastShift: any; - constructor(size: any, maintainHigh?: undefined, maintainLow?: undefined, maintainSum?: undefined) { - super(); - this.size = size; - this.maintainHigh = maintainHigh; - this.maintainLow = maintainLow; - this.maintainSum = maintainSum; - this.totalPushed = 0; - this.periodHigh = 0; - this.periodLow = Infinity; - this.periodSum = 0; - if (!size || typeof size !== 'number') { - throw ('Size required and should be a number.'); - } - this._push = this.push; - this.push = function (data) { - this.add(data); - this.totalPushed++; - }; - } - add(data: number) { - if (this.length === this.size) { - this.lastShift = this.shift(); - this._push(data); - //TODO: FInd a better way - if (this.maintainHigh) - if (this.lastShift == this.periodHigh) - this.calculatePeriodHigh(); - if (this.maintainLow) - if (this.lastShift == this.periodLow) - this.calculatePeriodLow(); - if (this.maintainSum) { - this.periodSum = this.periodSum - this.lastShift; - } - } - else { - this._push(data); - } - //TODO: FInd a better way - if (this.maintainHigh) - if (this.periodHigh <= data) - (this.periodHigh = data); - if (this.maintainLow) - if (this.periodLow >= data) - (this.periodLow = data); - if (this.maintainSum) { - this.periodSum = this.periodSum + data; - } - } - *iterator() { - this.resetCursor(); - while (this.next()) { - yield this.current; - } - } - calculatePeriodHigh() { - this.resetCursor(); - if (this.next()) - this.periodHigh = this.current; - while (this.next()) { - if (this.periodHigh <= this.current) { - this.periodHigh = this.current; - } - } - } - calculatePeriodLow() { - this.resetCursor(); - if (this.next()) - this.periodLow = this.current; - while (this.next()) { - if (this.periodLow >= this.current) { - this.periodLow = this.current; - } - } - } -} diff --git a/lib/Utils/Highest.js b/lib/Utils/Highest.js deleted file mode 100644 index 7bf6688..0000000 --- a/lib/Utils/Highest.js +++ /dev/null @@ -1,51 +0,0 @@ -// @ts-nocheck -import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; -import FixedSizedLinkedList from './FixedSizeLinkedList.ts'; -export class HighestInput extends IndicatorInput { -} -export class Highest extends Indicator { - constructor(input) { - super(input); - var values = input.values; - var period = input.period; - this.result = []; - var periodList = new FixedSizedLinkedList(period, true, false, false); - this.generator = (function* () { - var result; - var tick; - var high; - tick = yield; - while (true) { - periodList.push(tick); - if (periodList.totalPushed >= period) { - high = periodList.periodHigh; - } - tick = yield high; - } - })(); - this.generator.next(); - values.forEach((value, index) => { - var result = this.generator.next(value); - if (result.value != undefined) { - this.result.push(result.value); - } - }); - } - ; - nextValue(price) { - var result = this.generator.next(price); - if (result.value != undefined) { - return result.value; - } - } -} -Highest.calculate = highest; -export function highest(input) { - Indicator.reverseInputs(input); - var result = new Highest(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} \ No newline at end of file diff --git a/lib/Utils/LinkedList.ts b/lib/Utils/LinkedList.ts deleted file mode 100644 index d1e2bed..0000000 --- a/lib/Utils/LinkedList.ts +++ /dev/null @@ -1,153 +0,0 @@ -// deno-lint-ignore-file -class Item { -next: any; -prev: any; -data: any; - constructor(data: any, prev: any, next?: any) { - this.next = next; - if (next) - next.prev = this; - this.prev = prev; - if (prev) - prev.next = this; - this.data = data; - } -} -export class LinkedList { -_length: number; -_head: any; -_tail: any; -_current: any; -_next: any; - constructor() { - this._length = 0; - } - get head() { - return this._head && this._head.data; - } - get tail() { - return this._tail && this._tail.data; - } - get current() { - return this._current && this._current.data; - } - get length() { - return this._length; - } - push(data: number) { - this._tail = new Item(data, this._tail); - if (this._length === 0) { - this._head = this._tail; - this._current = this._head; - this._next = this._head; - } - this._length++; - } - pop() { - var tail = this._tail; - if (this._length === 0) { - return; - } - this._length--; - if (this._length === 0) { - this._head = this._tail = this._current = this._next = undefined; - return tail.data; - } - this._tail = tail.prev; - this._tail.next = undefined; - if (this._current === tail) { - this._current = this._tail; - this._next = undefined; - } - return tail.data; - } - shift() { - var head = this._head; - if (this._length === 0) { - return; - } - this._length--; - if (this._length === 0) { - this._head = this._tail = this._current = this._next = undefined; - return head.data; - } - this._head = this._head.next; - if (this._current === head) { - this._current = this._head; - this._next = this._current.next; - } - return head.data; - } - unshift(data: any) { - this._head = new Item(data, undefined, this._head); - if (this._length === 0) { - this._tail = this._head; - this._next = this._head; - } - this._length++; - } - unshiftCurrent() { - var current = this._current; - if (current === this._head || this._length < 2) { - return current && current.data; - } - // remove - if (current === this._tail) { - this._tail = current.prev; - this._tail.next = undefined; - this._current = this._tail; - } - else { - current.next.prev = current.prev; - current.prev.next = current.next; - this._current = current.prev; - } - this._next = this._current.next; - // unshift - current.next = this._head; - current.prev = undefined; - this._head.prev = current; - this._head = current; - return current.data; - } - removeCurrent() { - var current = this._current; - if (this._length === 0) { - return; - } - this._length--; - if (this._length === 0) { - this._head = this._tail = this._current = this._next = undefined; - return current.data; - } - if (current === this._tail) { - this._tail = current.prev; - this._tail.next = undefined; - this._current = this._tail; - } - else if (current === this._head) { - this._head = current.next; - this._head.prev = undefined; - this._current = this._head; - } - else { - current.next.prev = current.prev; - current.prev.next = current.next; - this._current = current.prev; - } - this._next = this._current.next; - return current.data; - } - resetCursor() { - this._current = this._next = this._head; - return this; - } - next() { - var next = this._next; - if (next !== undefined) { - this._next = next.next; - this._current = next; - return next.data; - } - } -} diff --git a/lib/Utils/Lowest.js b/lib/Utils/Lowest.js deleted file mode 100644 index 603927f..0000000 --- a/lib/Utils/Lowest.js +++ /dev/null @@ -1,53 +0,0 @@ -// @ts-nocheck -import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; -import FixedSizedLinkedList from './FixedSizeLinkedList.ts'; -export class LowestInput extends IndicatorInput { -} -export class Lowest extends Indicator { - constructor(input) { - super(input); - var values = input.values; - var period = input.period; - this.result = []; - var periodList = new FixedSizedLinkedList(period, false, true, false); - this.generator = (function* () { - var result; - var tick; - var high; - tick = yield; - while (true) { - periodList.push(tick); - if (periodList.totalPushed >= period) { - high = periodList.periodLow; - } - tick = yield high; - } - })(); - this.generator.next(); - values.forEach((value, index) => { - var result = this.generator.next(value); - if (result.value != undefined) { - this.result.push(result.value); - } - }); - } - ; - nextValue(price) { - var result = this.generator.next(price); - if (result.value != undefined) { - return result.value; - } - } - ; -} -Lowest.calculate = lowest; -export function lowest(input) { - Indicator.reverseInputs(input); - var result = new Lowest(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/Utils/NumberFormatter.ts b/lib/Utils/NumberFormatter.ts deleted file mode 100644 index ce6f180..0000000 --- a/lib/Utils/NumberFormatter.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { getConfig } from '../config.ts'; -export function format(v: number) { - let precision = getConfig('precision'); - if (precision) { - return parseFloat(v.toPrecision(precision)); - } - return v; -} diff --git a/lib/Utils/SD.js b/lib/Utils/SD.js deleted file mode 100644 index 7f5b3b3..0000000 --- a/lib/Utils/SD.js +++ /dev/null @@ -1,64 +0,0 @@ -// @ts-nocheck -import { IndicatorInput, Indicator } from '../indicator/indicator.ts'; -import { SMA } from '../moving_averages/SMA.ts'; -import LinkedList from '../Utils/FixedSizeLinkedList.ts'; -/** - * Created by AAravindan on 5/7/16. - */ -"use strict"; -export class SDInput extends IndicatorInput { -} -; -export class SD extends Indicator { - constructor(input) { - super(input); - var period = input.period; - var priceArray = input.values; - var sma = new SMA({ period: period, values: [], format: (v) => { return v; } }); - this.result = []; - this.generator = (function* () { - var tick; - var mean; - var currentSet = new LinkedList(period); - ; - tick = yield; - var sd; - while (true) { - currentSet.push(tick); - mean = sma.nextValue(tick); - if (mean) { - let sum = 0; - for (let x of currentSet.iterator()) { - sum = sum + (Math.pow((x - mean), 2)); - } - sd = Math.sqrt(sum / (period)); - } - tick = yield sd; - } - })(); - this.generator.next(); - priceArray.forEach((tick) => { - var result = this.generator.next(tick); - if (result.value != undefined) { - this.result.push(this.format(result.value)); - } - }); - } - nextValue(price) { - var nextResult = this.generator.next(price); - if (nextResult.value != undefined) - return this.format(nextResult.value); - } - ; -} -SD.calculate = sd; -export function sd(input) { - Indicator.reverseInputs(input); - var result = new SD(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/Utils/Sum.js b/lib/Utils/Sum.js deleted file mode 100644 index c4872f3..0000000 --- a/lib/Utils/Sum.js +++ /dev/null @@ -1,53 +0,0 @@ -// @ts-nocheck -import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; -import FixedSizedLinkedList from './FixedSizeLinkedList.ts'; -export class SumInput extends IndicatorInput { -} -export class Sum extends Indicator { - constructor(input) { - super(input); - var values = input.values; - var period = input.period; - this.result = []; - var periodList = new FixedSizedLinkedList(period, false, false, true); - this.generator = (function* () { - var result; - var tick; - var high; - tick = yield; - while (true) { - periodList.push(tick); - if (periodList.totalPushed >= period) { - high = periodList.periodSum; - } - tick = yield high; - } - })(); - this.generator.next(); - values.forEach((value, index) => { - var result = this.generator.next(value); - if (result.value != undefined) { - this.result.push(result.value); - } - }); - } - ; - nextValue(price) { - var result = this.generator.next(price); - if (result.value != undefined) { - return result.value; - } - } - ; -} -Sum.calculate = sum; -export function sum(input) { - Indicator.reverseInputs(input); - var result = new Sum(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/candlestick/AbandonedBaby.js b/lib/candlestick/AbandonedBaby.js deleted file mode 100644 index 35fcd98..0000000 --- a/lib/candlestick/AbandonedBaby.js +++ /dev/null @@ -1,38 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -import Doji from './Doji'; -export default class AbandonedBaby extends CandlestickFinder { - constructor() { - super(); - this.name = 'AbandonedBaby'; - this.requiredCount = 3; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let thirddaysOpen = data.open[2]; - let thirddaysClose = data.close[2]; - let thirddaysHigh = data.high[2]; - let thirddaysLow = data.low[2]; - let isFirstBearish = firstdaysClose < firstdaysOpen; - let dojiExists = new Doji().hasPattern({ - "open": [seconddaysOpen], - "close": [seconddaysClose], - "high": [seconddaysHigh], - "low": [seconddaysLow] - }); - let gapExists = ((seconddaysHigh < firstdaysLow) && - (thirddaysLow > seconddaysHigh) && - (thirddaysClose > thirddaysOpen)); - let isThirdBullish = (thirddaysHigh < firstdaysOpen); - return (isFirstBearish && dojiExists && gapExists && isThirdBullish); - } -} -export function abandonedbaby(data) { - return new AbandonedBaby().hasPattern(data); -} diff --git a/lib/candlestick/Bearish.js b/lib/candlestick/Bearish.js deleted file mode 100644 index 22556ed..0000000 --- a/lib/candlestick/Bearish.js +++ /dev/null @@ -1,45 +0,0 @@ -import BearishEngulfingPattern from './BearishEngulfingPattern'; -import BearishHarami from './BearishHarami'; -import BearishHaramiCross from './BearishHaramiCross'; -import EveningDojiStar from './EveningDojiStar'; -import EveningStar from './EveningStar'; -import BearishMarubozu from './BearishMarubozu'; -import ThreeBlackCrows from './ThreeBlackCrows'; -import BearishHammerStick from './BearishHammerStick'; -import BearishInvertedHammerStick from './BearishInvertedHammerStick'; -import HangingMan from './HangingMan'; -import HangingManUnconfirmed from './HangingManUnconfirmed'; -import ShootingStar from './ShootingStar'; -import ShootingStarUnconfirmed from './ShootingStarUnconfirmed'; -import TweezerTop from './TweezerTop'; -import CandlestickFinder from './CandlestickFinder'; -let bearishPatterns = [ - new BearishEngulfingPattern(), - new BearishHarami(), - new BearishHaramiCross(), - new EveningDojiStar(), - new EveningStar(), - new BearishMarubozu(), - new ThreeBlackCrows(), - new BearishHammerStick(), - new BearishInvertedHammerStick(), - new HangingMan(), - new HangingManUnconfirmed(), - new ShootingStar(), - new ShootingStarUnconfirmed(), - new TweezerTop() -]; -export default class BearishPatterns extends CandlestickFinder { - constructor() { - super(); - this.name = 'Bearish Candlesticks'; - } - hasPattern(data) { - return bearishPatterns.reduce(function (state, pattern) { - return state || pattern.hasPattern(data); - }, false); - } -} -export function bearish(data) { - return new BearishPatterns().hasPattern(data); -} diff --git a/lib/candlestick/BearishEngulfingPattern.js b/lib/candlestick/BearishEngulfingPattern.js deleted file mode 100644 index a7a62af..0000000 --- a/lib/candlestick/BearishEngulfingPattern.js +++ /dev/null @@ -1,26 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class BearishEngulfingPattern extends CandlestickFinder { - constructor() { - super(); - this.name = 'BearishEngulfingPattern'; - this.requiredCount = 2; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let isBearishEngulfing = ((firstdaysClose > firstdaysOpen) && - (firstdaysOpen < seconddaysOpen) && - (firstdaysClose < seconddaysOpen) && - (firstdaysOpen > seconddaysClose)); - return (isBearishEngulfing); - } -} -export function bearishengulfingpattern(data) { - return new BearishEngulfingPattern().hasPattern(data); -} diff --git a/lib/candlestick/BearishHarami.js b/lib/candlestick/BearishHarami.js deleted file mode 100644 index dff4158..0000000 --- a/lib/candlestick/BearishHarami.js +++ /dev/null @@ -1,27 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class BearishHarami extends CandlestickFinder { - constructor() { - super(); - this.requiredCount = 2; - this.name = 'BearishHarami'; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let isBearishHaramiPattern = ((firstdaysOpen < seconddaysOpen) && - (firstdaysClose > seconddaysOpen) && - (firstdaysClose > seconddaysClose) && - (firstdaysOpen < seconddaysLow) && - (firstdaysHigh > seconddaysHigh)); - return (isBearishHaramiPattern); - } -} -export function bearishharami(data) { - return new BearishHarami().hasPattern(data); -} diff --git a/lib/candlestick/BearishHaramiCross.js b/lib/candlestick/BearishHaramiCross.js deleted file mode 100644 index bf4a3f5..0000000 --- a/lib/candlestick/BearishHaramiCross.js +++ /dev/null @@ -1,28 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class BearishHaramiCross extends CandlestickFinder { - constructor() { - super(); - this.requiredCount = 2; - this.name = 'BearishHaramiCross'; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let isBearishHaramiCrossPattern = ((firstdaysOpen < seconddaysOpen) && - (firstdaysClose > seconddaysOpen) && - (firstdaysClose > seconddaysClose) && - (firstdaysOpen < seconddaysLow) && - (firstdaysHigh > seconddaysHigh)); - let isSecondDayDoji = this.approximateEqual(seconddaysOpen, seconddaysClose); - return (isBearishHaramiCrossPattern && isSecondDayDoji); - } -} -export function bearishharamicross(data) { - return new BearishHaramiCross().hasPattern(data); -} diff --git a/lib/candlestick/BearishMarubozu.js b/lib/candlestick/BearishMarubozu.js deleted file mode 100644 index 2819f68..0000000 --- a/lib/candlestick/BearishMarubozu.js +++ /dev/null @@ -1,22 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class BearishMarubozu extends CandlestickFinder { - constructor() { - super(); - this.name = 'BearishMarubozu'; - this.requiredCount = 1; - } - logic(data) { - let daysOpen = data.open[0]; - let daysClose = data.close[0]; - let daysHigh = data.high[0]; - let daysLow = data.low[0]; - let isBearishMarbozu = this.approximateEqual(daysOpen, daysHigh) && - this.approximateEqual(daysLow, daysClose) && - daysOpen > daysClose && - daysOpen > daysLow; - return (isBearishMarbozu); - } -} -export function bearishmarubozu(data) { - return new BearishMarubozu().hasPattern(data); -} diff --git a/lib/candlestick/BearishSpinningTop.js b/lib/candlestick/BearishSpinningTop.js deleted file mode 100644 index 43b0544..0000000 --- a/lib/candlestick/BearishSpinningTop.js +++ /dev/null @@ -1,23 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class BearishSpinningTop extends CandlestickFinder { - constructor() { - super(); - this.name = 'BearishSpinningTop'; - this.requiredCount = 1; - } - logic(data) { - let daysOpen = data.open[0]; - let daysClose = data.close[0]; - let daysHigh = data.high[0]; - let daysLow = data.low[0]; - let bodyLength = Math.abs(daysClose - daysOpen); - let upperShadowLength = Math.abs(daysHigh - daysOpen); - let lowerShadowLength = Math.abs(daysHigh - daysLow); - let isBearishSpinningTop = bodyLength < upperShadowLength && - bodyLength < lowerShadowLength; - return isBearishSpinningTop; - } -} -export function bearishspinningtop(data) { - return new BearishSpinningTop().hasPattern(data); -} diff --git a/lib/candlestick/Bullish.js b/lib/candlestick/Bullish.js deleted file mode 100644 index 48bf523..0000000 --- a/lib/candlestick/Bullish.js +++ /dev/null @@ -1,46 +0,0 @@ -import MorningStar from './MorningStar'; -import BullishEngulfingPattern from './BullishEngulfingPattern'; -import BullishHarami from './BullishHarami'; -import BullishHaramiCross from './BullishHaramiCross'; -import MorningDojiStar from './MorningDojiStar'; -import DownsideTasukiGap from './DownsideTasukiGap'; -import BullishMarubozu from './BullishMarubozu'; -import PiercingLine from './PiercingLine'; -import ThreeWhiteSoldiers from './ThreeWhiteSoldiers'; -import BullishHammerStick from './BullishHammerStick'; -import BullishInvertedHammerStick from './BullishInvertedHammerStick'; -import HammerPattern from './HammerPattern'; -import HammerPatternUnconfirmed from './HammerPatternUnconfirmed'; -import CandlestickFinder from './CandlestickFinder'; -import TweezerBottom from './TweezerBottom'; -let bullishPatterns = [ - new BullishEngulfingPattern(), - new DownsideTasukiGap(), - new BullishHarami(), - new BullishHaramiCross(), - new MorningDojiStar(), - new MorningStar(), - new BullishMarubozu(), - new PiercingLine(), - new ThreeWhiteSoldiers(), - new BullishHammerStick(), - new BullishInvertedHammerStick(), - new HammerPattern(), - new HammerPatternUnconfirmed(), - new TweezerBottom() -]; -export default class BullishPatterns extends CandlestickFinder { - constructor() { - super(); - this.name = 'Bullish Candlesticks'; - } - hasPattern(data) { - return bullishPatterns.reduce(function (state, pattern) { - let result = pattern.hasPattern(data); - return state || result; - }, false); - } -} -export function bullish(data) { - return new BullishPatterns().hasPattern(data); -} diff --git a/lib/candlestick/BullishEngulfingPattern.js b/lib/candlestick/BullishEngulfingPattern.js deleted file mode 100644 index a8f95a7..0000000 --- a/lib/candlestick/BullishEngulfingPattern.js +++ /dev/null @@ -1,26 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class BullishEngulfingPattern extends CandlestickFinder { - constructor() { - super(); - this.name = 'BullishEngulfingPattern'; - this.requiredCount = 2; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let isBullishEngulfing = ((firstdaysClose < firstdaysOpen) && - (firstdaysOpen > seconddaysOpen) && - (firstdaysClose > seconddaysOpen) && - (firstdaysOpen < seconddaysClose)); - return (isBullishEngulfing); - } -} -export function bullishengulfingpattern(data) { - return new BullishEngulfingPattern().hasPattern(data); -} diff --git a/lib/candlestick/BullishHarami.js b/lib/candlestick/BullishHarami.js deleted file mode 100644 index 6ce2b4a..0000000 --- a/lib/candlestick/BullishHarami.js +++ /dev/null @@ -1,27 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class BullishHarami extends CandlestickFinder { - constructor() { - super(); - this.requiredCount = 2; - this.name = "BullishHarami"; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let isBullishHaramiPattern = ((firstdaysOpen > seconddaysOpen) && - (firstdaysClose < seconddaysOpen) && - (firstdaysClose < seconddaysClose) && - (firstdaysOpen > seconddaysLow) && - (firstdaysHigh > seconddaysHigh)); - return (isBullishHaramiPattern); - } -} -export function bullishharami(data) { - return new BullishHarami().hasPattern(data); -} diff --git a/lib/candlestick/BullishHaramiCross.js b/lib/candlestick/BullishHaramiCross.js deleted file mode 100644 index d5305cf..0000000 --- a/lib/candlestick/BullishHaramiCross.js +++ /dev/null @@ -1,28 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class BullishHaramiCross extends CandlestickFinder { - constructor() { - super(); - this.requiredCount = 2; - this.name = 'BullishHaramiCross'; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let isBullishHaramiCrossPattern = ((firstdaysOpen > seconddaysOpen) && - (firstdaysClose < seconddaysOpen) && - (firstdaysClose < seconddaysClose) && - (firstdaysOpen > seconddaysLow) && - (firstdaysHigh > seconddaysHigh)); - let isSecondDayDoji = this.approximateEqual(seconddaysOpen, seconddaysClose); - return (isBullishHaramiCrossPattern && isSecondDayDoji); - } -} -export function bullishharamicross(data) { - return new BullishHaramiCross().hasPattern(data); -} diff --git a/lib/candlestick/BullishMarubozu.js b/lib/candlestick/BullishMarubozu.js deleted file mode 100644 index a2ba19c..0000000 --- a/lib/candlestick/BullishMarubozu.js +++ /dev/null @@ -1,22 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class BullishMarubozu extends CandlestickFinder { - constructor() { - super(); - this.name = 'BullishMarubozu'; - this.requiredCount = 1; - } - logic(data) { - let daysOpen = data.open[0]; - let daysClose = data.close[0]; - let daysHigh = data.high[0]; - let daysLow = data.low[0]; - let isBullishMarbozu = this.approximateEqual(daysClose, daysHigh) && - this.approximateEqual(daysLow, daysOpen) && - daysOpen < daysClose && - daysOpen < daysHigh; - return (isBullishMarbozu); - } -} -export function bullishmarubozu(data) { - return new BullishMarubozu().hasPattern(data); -} diff --git a/lib/candlestick/BullishSpinningTop.js b/lib/candlestick/BullishSpinningTop.js deleted file mode 100644 index e60eee4..0000000 --- a/lib/candlestick/BullishSpinningTop.js +++ /dev/null @@ -1,23 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class BullishSpinningTop extends CandlestickFinder { - constructor() { - super(); - this.name = 'BullishSpinningTop'; - this.requiredCount = 1; - } - logic(data) { - let daysOpen = data.open[0]; - let daysClose = data.close[0]; - let daysHigh = data.high[0]; - let daysLow = data.low[0]; - let bodyLength = Math.abs(daysClose - daysOpen); - let upperShadowLength = Math.abs(daysHigh - daysClose); - let lowerShadowLength = Math.abs(daysOpen - daysLow); - let isBullishSpinningTop = bodyLength < upperShadowLength && - bodyLength < lowerShadowLength; - return isBullishSpinningTop; - } -} -export function bullishspinningtop(data) { - return new BullishSpinningTop().hasPattern(data); -} diff --git a/lib/candlestick/CandlestickFinder.js b/lib/candlestick/CandlestickFinder.js deleted file mode 100644 index ee04593..0000000 --- a/lib/candlestick/CandlestickFinder.js +++ /dev/null @@ -1,93 +0,0 @@ -export default class CandlestickFinder { - constructor() { - // if (new.target === Abstract) { - // throw new TypeError("Abstract class"); - // } - } - approximateEqual(a, b) { - let left = parseFloat(Math.abs(a - b).toPrecision(4)) * 1; - let right = parseFloat((a * 0.001).toPrecision(4)) * 1; - return left <= right; - } - logic(data) { - throw "this has to be implemented"; - } - getAllPatternIndex(data) { - if (data.close.length < this.requiredCount) { - console.warn('Data count less than data required for the strategy ', this.name); - return []; - } - if (data.reversedInput) { - data.open.reverse(); - data.high.reverse(); - data.low.reverse(); - data.close.reverse(); - } - let strategyFn = this.logic; - return this._generateDataForCandleStick(data) - .map((current, index) => { - return strategyFn.call(this, current) ? index : undefined; - }).filter((hasIndex) => { - return hasIndex; - }); - } - hasPattern(data) { - if (data.close.length < this.requiredCount) { - console.warn('Data count less than data required for the strategy ', this.name); - return false; - } - if (data.reversedInput) { - data.open.reverse(); - data.high.reverse(); - data.low.reverse(); - data.close.reverse(); - } - let strategyFn = this.logic; - return strategyFn.call(this, this._getLastDataForCandleStick(data)); - } - _getLastDataForCandleStick(data) { - let requiredCount = this.requiredCount; - if (data.close.length === requiredCount) { - return data; - } - else { - let returnVal = { - open: [], - high: [], - low: [], - close: [] - }; - let i = 0; - let index = data.close.length - requiredCount; - while (i < requiredCount) { - returnVal.open.push(data.open[index + i]); - returnVal.high.push(data.high[index + i]); - returnVal.low.push(data.low[index + i]); - returnVal.close.push(data.close[index + i]); - i++; - } - return returnVal; - } - } - _generateDataForCandleStick(data) { - let requiredCount = this.requiredCount; - let generatedData = data.close.map(function (currentData, index) { - let i = 0; - let returnVal = { - open: [], - high: [], - low: [], - close: [] - }; - while (i < requiredCount) { - returnVal.open.push(data.open[index + i]); - returnVal.high.push(data.high[index + i]); - returnVal.low.push(data.low[index + i]); - returnVal.close.push(data.close[index + i]); - i++; - } - return returnVal; - }).filter((val, index) => { return (index <= (data.close.length - requiredCount)); }); - return generatedData; - } -} diff --git a/lib/candlestick/DarkCloudCover.js b/lib/candlestick/DarkCloudCover.js deleted file mode 100644 index 77342ba..0000000 --- a/lib/candlestick/DarkCloudCover.js +++ /dev/null @@ -1,28 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class DarkCloudCover extends CandlestickFinder { - constructor() { - super(); - this.name = 'DarkCloudCover'; - this.requiredCount = 2; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let firstdayMidpoint = ((firstdaysClose + firstdaysOpen) / 2); - let isFirstBullish = firstdaysClose > firstdaysOpen; - let isSecondBearish = seconddaysClose < seconddaysOpen; - let isDarkCloudPattern = ((seconddaysOpen > firstdaysHigh) && - (seconddaysClose < firstdayMidpoint) && - (seconddaysClose > firstdaysOpen)); - return (isFirstBullish && isSecondBearish && isDarkCloudPattern); - } -} -export function darkcloudcover(data) { - return new DarkCloudCover().hasPattern(data); -} diff --git a/lib/candlestick/Doji.js.map b/lib/candlestick/Doji.js.map deleted file mode 100644 index 5b12337..0000000 --- a/lib/candlestick/Doji.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Doji.js","sourceRoot":"","sources":["../../src/candlestick/Doji.ts"],"names":[],"mappings":"AACA,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAEpD,MAAM,CAAC,OAAO,WAAY,SAAQ,iBAAiB;IAC/C;QACI,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;QACnB,IAAI,CAAC,aAAa,GAAI,CAAC,CAAC;IAC5B,CAAC;IACD,KAAK,CAAE,IAAc;QACjB,IAAI,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACtD,CAAC;CACJ;AAED,MAAM,eAAe,IAAc;IACjC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC"} \ No newline at end of file diff --git a/lib/candlestick/DownsideTasukiGap.js b/lib/candlestick/DownsideTasukiGap.js deleted file mode 100644 index 7f61b5c..0000000 --- a/lib/candlestick/DownsideTasukiGap.js +++ /dev/null @@ -1,34 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class DownsideTasukiGap extends CandlestickFinder { - constructor() { - super(); - this.requiredCount = 3; - this.name = 'DownsideTasukiGap'; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let thirddaysOpen = data.open[2]; - let thirddaysClose = data.close[2]; - let thirddaysHigh = data.high[2]; - let thirddaysLow = data.low[2]; - let isFirstBearish = firstdaysClose < firstdaysOpen; - let isSecondBearish = seconddaysClose < seconddaysOpen; - let isThirdBullish = thirddaysClose > thirddaysOpen; - let isFirstGapExists = seconddaysHigh < firstdaysLow; - let isDownsideTasukiGap = ((seconddaysOpen > thirddaysOpen) && - (seconddaysClose < thirddaysOpen) && - (thirddaysClose > seconddaysOpen) && - (thirddaysClose < firstdaysClose)); - return (isFirstBearish && isSecondBearish && isThirdBullish && isFirstGapExists && isDownsideTasukiGap); - } -} -export function downsidetasukigap(data) { - return new DownsideTasukiGap().hasPattern(data); -} diff --git a/lib/candlestick/EveningDojiStar.js b/lib/candlestick/EveningDojiStar.js deleted file mode 100644 index 60565b3..0000000 --- a/lib/candlestick/EveningDojiStar.js +++ /dev/null @@ -1,41 +0,0 @@ -import Doji from './Doji'; -import CandlestickFinder from './CandlestickFinder'; -export default class EveningDojiStar extends CandlestickFinder { - constructor() { - super(); - this.name = 'EveningDojiStar'; - this.requiredCount = 3; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let thirddaysOpen = data.open[2]; - let thirddaysClose = data.close[2]; - let thirddaysHigh = data.high[2]; - let thirddaysLow = data.low[2]; - let firstdaysMidpoint = ((firstdaysOpen + firstdaysClose) / 2); - let isFirstBullish = firstdaysClose > firstdaysOpen; - let dojiExists = new Doji().hasPattern({ - "open": [seconddaysOpen], - "close": [seconddaysClose], - "high": [seconddaysHigh], - "low": [seconddaysLow] - }); - let isThirdBearish = thirddaysOpen > thirddaysClose; - let gapExists = ((seconddaysHigh > firstdaysHigh) && - (seconddaysLow > firstdaysHigh) && - (thirddaysOpen < seconddaysLow) && - (seconddaysClose > thirddaysOpen)); - let doesCloseBelowFirstMidpoint = thirddaysClose < firstdaysMidpoint; - return (isFirstBullish && dojiExists && gapExists && isThirdBearish && doesCloseBelowFirstMidpoint); - } -} -export function eveningdojistar(data) { - return new EveningDojiStar().hasPattern(data); -} diff --git a/lib/candlestick/EveningStar.js b/lib/candlestick/EveningStar.js deleted file mode 100644 index 79fbd7d..0000000 --- a/lib/candlestick/EveningStar.js +++ /dev/null @@ -1,36 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class EveningStar extends CandlestickFinder { - constructor() { - super(); - this.name = 'EveningStar'; - this.requiredCount = 3; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let thirddaysOpen = data.open[2]; - let thirddaysClose = data.close[2]; - let thirddaysHigh = data.high[2]; - let thirddaysLow = data.low[2]; - let firstdaysMidpoint = ((firstdaysOpen + firstdaysClose) / 2); - let isFirstBullish = firstdaysClose > firstdaysOpen; - let isSmallBodyExists = ((firstdaysHigh < seconddaysLow) && - (firstdaysHigh < seconddaysHigh)); - let isThirdBearish = thirddaysOpen > thirddaysClose; - let gapExists = ((seconddaysHigh > firstdaysHigh) && - (seconddaysLow > firstdaysHigh) && - (thirddaysOpen < seconddaysLow) && - (seconddaysClose > thirddaysOpen)); - let doesCloseBelowFirstMidpoint = thirddaysClose < firstdaysMidpoint; - return (isFirstBullish && isSmallBodyExists && gapExists && isThirdBearish && doesCloseBelowFirstMidpoint); - } -} -export function eveningstar(data) { - return new EveningStar().hasPattern(data); -} diff --git a/lib/candlestick/MorningDojiStar.js b/lib/candlestick/MorningDojiStar.js deleted file mode 100644 index a72cdbf..0000000 --- a/lib/candlestick/MorningDojiStar.js +++ /dev/null @@ -1,42 +0,0 @@ -import Doji from './Doji'; -import CandlestickFinder from './CandlestickFinder'; -export default class MorningDojiStar extends CandlestickFinder { - constructor() { - super(); - this.name = 'MorningDojiStar'; - this.requiredCount = 3; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let thirddaysOpen = data.open[2]; - let thirddaysClose = data.close[2]; - let thirddaysHigh = data.high[2]; - let thirddaysLow = data.low[2]; - let firstdaysMidpoint = ((firstdaysOpen + firstdaysClose) / 2); - let isFirstBearish = firstdaysClose < firstdaysOpen; - let dojiExists = new Doji().hasPattern({ - "open": [seconddaysOpen], - "close": [seconddaysClose], - "high": [seconddaysHigh], - "low": [seconddaysLow] - }); - let isThirdBullish = thirddaysOpen < thirddaysClose; - let gapExists = ((seconddaysHigh < firstdaysLow) && - (seconddaysLow < firstdaysLow) && - (thirddaysOpen > seconddaysHigh) && - (seconddaysClose < thirddaysOpen)); - let doesCloseAboveFirstMidpoint = thirddaysClose > firstdaysMidpoint; - return (isFirstBearish && dojiExists && isThirdBullish && gapExists && - doesCloseAboveFirstMidpoint); - } -} -export function morningdojistar(data) { - return new MorningDojiStar().hasPattern(data); -} diff --git a/lib/candlestick/MorningStar.js b/lib/candlestick/MorningStar.js deleted file mode 100644 index bef661b..0000000 --- a/lib/candlestick/MorningStar.js +++ /dev/null @@ -1,36 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class MorningStar extends CandlestickFinder { - constructor() { - super(); - this.name = 'MorningStar'; - this.requiredCount = 3; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let thirddaysOpen = data.open[2]; - let thirddaysClose = data.close[2]; - let thirddaysHigh = data.high[2]; - let thirddaysLow = data.low[2]; - let firstdaysMidpoint = ((firstdaysOpen + firstdaysClose) / 2); - let isFirstBearish = firstdaysClose < firstdaysOpen; - let isSmallBodyExists = ((firstdaysLow > seconddaysLow) && - (firstdaysLow > seconddaysHigh)); - let isThirdBullish = thirddaysOpen < thirddaysClose; - let gapExists = ((seconddaysHigh < firstdaysLow) && - (seconddaysLow < firstdaysLow) && - (thirddaysOpen > seconddaysHigh) && - (seconddaysClose < thirddaysOpen)); - let doesCloseAboveFirstMidpoint = thirddaysClose > firstdaysMidpoint; - return (isFirstBearish && isSmallBodyExists && gapExists && isThirdBullish && doesCloseAboveFirstMidpoint); - } -} -export function morningstar(data) { - return new MorningStar().hasPattern(data); -} diff --git a/lib/candlestick/PiercingLine.js b/lib/candlestick/PiercingLine.js deleted file mode 100644 index 415f8ae..0000000 --- a/lib/candlestick/PiercingLine.js +++ /dev/null @@ -1,28 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class PiercingLine extends CandlestickFinder { - constructor() { - super(); - this.requiredCount = 2; - this.name = 'PiercingLine'; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let firstdaysMidpoint = ((firstdaysOpen + firstdaysClose) / 2); - let isDowntrend = seconddaysLow < firstdaysLow; - let isFirstBearish = firstdaysClose < firstdaysOpen; - let isSecondBullish = seconddaysClose > seconddaysOpen; - let isPiercingLinePattern = ((firstdaysLow > seconddaysOpen) && - (seconddaysClose > firstdaysMidpoint)); - return (isDowntrend && isFirstBearish && isPiercingLinePattern && isSecondBullish); - } -} -export function piercingline(data) { - return new PiercingLine().hasPattern(data); -} diff --git a/lib/candlestick/ThreeBlackCrows.js b/lib/candlestick/ThreeBlackCrows.js deleted file mode 100644 index 3b29a51..0000000 --- a/lib/candlestick/ThreeBlackCrows.js +++ /dev/null @@ -1,35 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class ThreeBlackCrows extends CandlestickFinder { - constructor() { - super(); - this.name = 'ThreeBlackCrows'; - this.requiredCount = 3; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let thirddaysOpen = data.open[2]; - let thirddaysClose = data.close[2]; - let thirddaysHigh = data.high[2]; - let thirddaysLow = data.low[2]; - let isDownTrend = firstdaysLow > seconddaysLow && - seconddaysLow > thirddaysLow; - let isAllBearish = firstdaysOpen > firstdaysClose && - seconddaysOpen > seconddaysClose && - thirddaysOpen > thirddaysClose; - let doesOpenWithinPreviousBody = firstdaysOpen > seconddaysOpen && - seconddaysOpen > firstdaysClose && - seconddaysOpen > thirddaysOpen && - thirddaysOpen > seconddaysClose; - return (isDownTrend && isAllBearish && doesOpenWithinPreviousBody); - } -} -export function threeblackcrows(data) { - return new ThreeBlackCrows().hasPattern(data); -} diff --git a/lib/candlestick/ThreeWhiteSoldiers.js b/lib/candlestick/ThreeWhiteSoldiers.js deleted file mode 100644 index 0dfc4e5..0000000 --- a/lib/candlestick/ThreeWhiteSoldiers.js +++ /dev/null @@ -1,35 +0,0 @@ -import CandlestickFinder from './CandlestickFinder'; -export default class ThreeWhiteSoldiers extends CandlestickFinder { - constructor() { - super(); - this.name = 'ThreeWhiteSoldiers'; - this.requiredCount = 3; - } - logic(data) { - let firstdaysOpen = data.open[0]; - let firstdaysClose = data.close[0]; - let firstdaysHigh = data.high[0]; - let firstdaysLow = data.low[0]; - let seconddaysOpen = data.open[1]; - let seconddaysClose = data.close[1]; - let seconddaysHigh = data.high[1]; - let seconddaysLow = data.low[1]; - let thirddaysOpen = data.open[2]; - let thirddaysClose = data.close[2]; - let thirddaysHigh = data.high[2]; - let thirddaysLow = data.low[2]; - let isUpTrend = seconddaysHigh > firstdaysHigh && - thirddaysHigh > seconddaysHigh; - let isAllBullish = firstdaysOpen < firstdaysClose && - seconddaysOpen < seconddaysClose && - thirddaysOpen < thirddaysClose; - let doesOpenWithinPreviousBody = firstdaysClose > seconddaysOpen && - seconddaysOpen < firstdaysHigh && - seconddaysHigh > thirddaysOpen && - thirddaysOpen < seconddaysClose; - return (isUpTrend && isAllBullish && doesOpenWithinPreviousBody); - } -} -export function threewhitesoldiers(data) { - return new ThreeWhiteSoldiers().hasPattern(data); -} diff --git a/lib/chart_types/HeikinAshi.js b/lib/chart_types/HeikinAshi.js deleted file mode 100644 index b51c74b..0000000 --- a/lib/chart_types/HeikinAshi.js +++ /dev/null @@ -1,101 +0,0 @@ -import { CandleList } from '../StockData'; -/** - * Created by AAravindan on 5/4/16. - */ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -export class HeikinAshiInput extends IndicatorInput { -} -export class HeikinAshi extends Indicator { - constructor(input) { - super(input); - var format = this.format; - this.result = new CandleList(); - let lastOpen = null; - let lastHigh = 0; - let lastLow = Infinity; - let lastClose = 0; - let lastVolume = 0; - let lastTimestamp = 0; - this.generator = (function* () { - let candleData = yield; - let calculated = null; - while (true) { - if (lastOpen === null) { - lastOpen = (candleData.close + candleData.open) / 2; - lastHigh = candleData.high; - lastLow = candleData.low; - lastClose = (candleData.close + candleData.open + candleData.high + candleData.low) / 4; - lastVolume = (candleData.volume || 0); - lastTimestamp = (candleData.timestamp || 0); - calculated = { - open: lastOpen, - high: lastHigh, - low: lastLow, - close: lastClose, - volume: candleData.volume || 0, - timestamp: (candleData.timestamp || 0) - }; - } - else { - let newClose = (candleData.close + candleData.open + candleData.high + candleData.low) / 4; - let newOpen = (lastOpen + lastClose) / 2; - let newHigh = Math.max(newOpen, newClose, candleData.high); - let newLow = Math.min(candleData.low, newOpen, newClose); - calculated = { - close: newClose, - open: newOpen, - high: newHigh, - low: newLow, - volume: (candleData.volume || 0), - timestamp: (candleData.timestamp || 0) - }; - lastClose = newClose; - lastOpen = newOpen; - lastHigh = newHigh; - lastLow = newLow; - } - candleData = yield calculated; - } - })(); - this.generator.next(); - input.low.forEach((tick, index) => { - var result = this.generator.next({ - open: input.open[index], - high: input.high[index], - low: input.low[index], - close: input.close[index], - volume: input.volume ? input.volume[index] : input.volume, - timestamp: input.timestamp ? input.timestamp[index] : input.timestamp - }); - if (result.value) { - this.result.open.push(result.value.open); - this.result.high.push(result.value.high); - this.result.low.push(result.value.low); - this.result.close.push(result.value.close); - this.result.volume.push(result.value.volume); - this.result.timestamp.push(result.value.timestamp); - } - }); - } - nextValue(price) { - var result = this.generator.next(price).value; - return result; - } - ; -} -HeikinAshi.calculate = heikinashi; -export function heikinashi(input) { - Indicator.reverseInputs(input); - var result = new HeikinAshi(input).result; - if (input.reversedInput) { - result.open.reverse(); - result.high.reverse(); - result.low.reverse(); - result.close.reverse(); - result.volume.reverse(); - result.timestamp.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/chart_types/Renko.js b/lib/chart_types/Renko.js deleted file mode 100644 index e17e28c..0000000 --- a/lib/chart_types/Renko.js +++ /dev/null @@ -1,114 +0,0 @@ -import { CandleList } from '../StockData'; -import { atr } from '../directionalmovement/ATR'; -/** - * Created by AAravindan on 5/4/16. - */ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -export class RenkoInput extends IndicatorInput { -} -class Renko extends Indicator { - constructor(input) { - super(input); - var format = this.format; - let useATR = input.useATR; - let brickSize = input.brickSize || 0; - if (useATR) { - let atrResult = atr(Object.assign({}, input)); - brickSize = atrResult[atrResult.length - 1]; - } - this.result = new CandleList(); - ; - if (brickSize === 0) { - console.error('Not enough data to calculate brickSize for renko when using ATR'); - return; - } - let lastOpen = 0; - let lastHigh = 0; - let lastLow = Infinity; - let lastClose = 0; - let lastVolume = 0; - let lastTimestamp = 0; - this.generator = (function* () { - let candleData = yield; - while (true) { - //Calculating first bar - if (lastOpen === 0) { - lastOpen = candleData.close; - lastHigh = candleData.high; - lastLow = candleData.low; - lastClose = candleData.close; - lastVolume = candleData.volume; - lastTimestamp = candleData.timestamp; - candleData = yield; - continue; - } - let absoluteMovementFromClose = Math.abs(candleData.close - lastClose); - let absoluteMovementFromOpen = Math.abs(candleData.close - lastOpen); - if ((absoluteMovementFromClose >= brickSize) && (absoluteMovementFromOpen >= brickSize)) { - let reference = absoluteMovementFromClose > absoluteMovementFromOpen ? lastOpen : lastClose; - let calculated = { - open: reference, - high: lastHigh > candleData.high ? lastHigh : candleData.high, - low: lastLow < candleData.Low ? lastLow : candleData.low, - close: reference > candleData.close ? (reference - brickSize) : (reference + brickSize), - volume: lastVolume + candleData.volume, - timestamp: candleData.timestamp - }; - lastOpen = calculated.open; - lastHigh = calculated.close; - lastLow = calculated.close; - lastClose = calculated.close; - lastVolume = 0; - candleData = yield calculated; - } - else { - lastHigh = lastHigh > candleData.high ? lastHigh : candleData.high; - lastLow = lastLow < candleData.Low ? lastLow : candleData.low; - lastVolume = lastVolume + candleData.volume; - lastTimestamp = candleData.timestamp; - candleData = yield; - } - } - })(); - this.generator.next(); - input.low.forEach((tick, index) => { - var result = this.generator.next({ - open: input.open[index], - high: input.high[index], - low: input.low[index], - close: input.close[index], - volume: input.volume[index], - timestamp: input.timestamp[index] - }); - if (result.value) { - this.result.open.push(result.value.open); - this.result.high.push(result.value.high); - this.result.low.push(result.value.low); - this.result.close.push(result.value.close); - this.result.volume.push(result.value.volume); - this.result.timestamp.push(result.value.timestamp); - } - }); - } - nextValue(price) { - console.error('Cannot calculate next value on Renko, Every value has to be recomputed for every change, use calcualte method'); - return null; - } - ; -} -Renko.calculate = renko; -export function renko(input) { - Indicator.reverseInputs(input); - var result = new Renko(input).result; - if (input.reversedInput) { - result.open.reverse(); - result.high.reverse(); - result.low.reverse(); - result.close.reverse(); - result.volume.reverse(); - result.timestamp.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/chart_types/TypicalPrice.js b/lib/chart_types/TypicalPrice.js deleted file mode 100644 index ba86e0f..0000000 --- a/lib/chart_types/TypicalPrice.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Created by AAravindan on 5/4/16. - */ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -export class TypicalPriceInput extends IndicatorInput { -} -export class TypicalPrice extends Indicator { - constructor(input) { - super(input); - this.result = []; - this.generator = (function* () { - let priceInput = yield; - while (true) { - priceInput = yield (priceInput.high + priceInput.low + priceInput.close) / 3; - } - })(); - this.generator.next(); - input.low.forEach((tick, index) => { - var result = this.generator.next({ - high: input.high[index], - low: input.low[index], - close: input.close[index], - }); - this.result.push(result.value); - }); - } - nextValue(price) { - var result = this.generator.next(price).value; - return result; - } - ; -} -TypicalPrice.calculate = typicalprice; -export function typicalprice(input) { - Indicator.reverseInputs(input); - var result = new TypicalPrice(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/config.ts b/lib/config.ts deleted file mode 100644 index a3c234d..0000000 --- a/lib/config.ts +++ /dev/null @@ -1,7 +0,0 @@ -let config:any = {}; -export function setConfig(key: string, value: string) { - config[key] = value; -} -export function getConfig(key: string) { - return config[key]; -} diff --git a/lib/directionalmovement/ADX.js b/lib/directionalmovement/ADX.js deleted file mode 100644 index a4e8ee8..0000000 --- a/lib/directionalmovement/ADX.js +++ /dev/null @@ -1,105 +0,0 @@ -import { WilderSmoothing } from '../moving_averages/WilderSmoothing'; -import { Indicator, IndicatorInput } from '../indicator/indicator'; -import { MDM } from './MinusDM'; -import { PDM } from './PlusDM'; -import { TrueRange } from './TrueRange'; -import { WEMA } from '../moving_averages/WEMA'; -export class ADXInput extends IndicatorInput { -} -; -export class ADXOutput extends IndicatorInput { -} -; -export class ADX extends Indicator { - constructor(input) { - super(input); - var lows = input.low; - var highs = input.high; - var closes = input.close; - var period = input.period; - var format = this.format; - var plusDM = new PDM({ - high: [], - low: [] - }); - var minusDM = new MDM({ - high: [], - low: [] - }); - var emaPDM = new WilderSmoothing({ period: period, values: [], format: (v) => { return v; } }); - var emaMDM = new WilderSmoothing({ period: period, values: [], format: (v) => { return v; } }); - var emaTR = new WilderSmoothing({ period: period, values: [], format: (v) => { return v; } }); - var emaDX = new WEMA({ period: period, values: [], format: (v) => { return v; } }); - var tr = new TrueRange({ - low: [], - high: [], - close: [], - }); - if (!((lows.length === highs.length) && (highs.length === closes.length))) { - throw ('Inputs(low,high, close) not of equal size'); - } - this.result = []; - ADXOutput; - this.generator = (function* () { - var tick = yield; - var index = 0; - var lastATR, lastAPDM, lastAMDM, lastPDI, lastMDI, lastDX, smoothedDX; - lastATR = 0; - lastAPDM = 0; - lastAMDM = 0; - while (true) { - let calcTr = tr.nextValue(tick); - let calcPDM = plusDM.nextValue(tick); - let calcMDM = minusDM.nextValue(tick); - if (calcTr === undefined) { - tick = yield; - continue; - } - let lastATR = emaTR.nextValue(calcTr); - let lastAPDM = emaPDM.nextValue(calcPDM); - let lastAMDM = emaMDM.nextValue(calcMDM); - if ((lastATR != undefined) && (lastAPDM != undefined) && (lastAMDM != undefined)) { - lastPDI = (lastAPDM) * 100 / lastATR; - lastMDI = (lastAMDM) * 100 / lastATR; - let diDiff = Math.abs(lastPDI - lastMDI); - let diSum = (lastPDI + lastMDI); - lastDX = (diDiff / diSum) * 100; - smoothedDX = emaDX.nextValue(lastDX); - // console.log(tick.high.toFixed(2), tick.low.toFixed(2), tick.close.toFixed(2) , calcTr.toFixed(2), calcPDM.toFixed(2), calcMDM.toFixed(2), lastATR.toFixed(2), lastAPDM.toFixed(2), lastAMDM.toFixed(2), lastPDI.toFixed(2), lastMDI.toFixed(2), diDiff.toFixed(2), diSum.toFixed(2), lastDX.toFixed(2)); - } - tick = yield { adx: smoothedDX, pdi: lastPDI, mdi: lastMDI }; - } - })(); - this.generator.next(); - lows.forEach((tick, index) => { - var result = this.generator.next({ - high: highs[index], - low: lows[index], - close: closes[index] - }); - if (result.value != undefined && result.value.adx != undefined) { - this.result.push({ adx: format(result.value.adx), pdi: format(result.value.pdi), mdi: format(result.value.mdi) }); - } - }); - } - ; - ; - nextValue(price) { - let result = this.generator.next(price).value; - if (result != undefined && result.adx != undefined) { - return { adx: this.format(result.adx), pdi: this.format(result.pdi), mdi: this.format(result.mdi) }; - } - } - ; -} -ADX.calculate = adx; -export function adx(input) { - Indicator.reverseInputs(input); - var result = new ADX(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/directionalmovement/ATR.js b/lib/directionalmovement/ATR.js deleted file mode 100644 index 7a5c9e8..0000000 --- a/lib/directionalmovement/ATR.js +++ /dev/null @@ -1,76 +0,0 @@ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -/** - * Created by AAravindan on 5/8/16. - */ -"use strict"; -import { WEMA } from '../moving_averages/WEMA'; -import { TrueRange } from './TrueRange'; -export class ATRInput extends IndicatorInput { -} -; -export class ATR extends Indicator { - constructor(input) { - super(input); - var lows = input.low; - var highs = input.high; - var closes = input.close; - var period = input.period; - var format = this.format; - if (!((lows.length === highs.length) && (highs.length === closes.length))) { - throw ('Inputs(low,high, close) not of equal size'); - } - var trueRange = new TrueRange({ - low: [], - high: [], - close: [] - }); - var wema = new WEMA({ period: period, values: [], format: (v) => { return v; } }); - this.result = []; - this.generator = (function* () { - var tick = yield; - var avgTrueRange, trange; - ; - while (true) { - trange = trueRange.nextValue({ - low: tick.low, - high: tick.high, - close: tick.close - }); - if (trange === undefined) { - avgTrueRange = undefined; - } - else { - avgTrueRange = wema.nextValue(trange); - } - tick = yield avgTrueRange; - } - })(); - this.generator.next(); - lows.forEach((tick, index) => { - var result = this.generator.next({ - high: highs[index], - low: lows[index], - close: closes[index] - }); - if (result.value !== undefined) { - this.result.push(format(result.value)); - } - }); - } - ; - nextValue(price) { - return this.generator.next(price).value; - } - ; -} -ATR.calculate = atr; -export function atr(input) { - Indicator.reverseInputs(input); - var result = new ATR(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/directionalmovement/MinusDM.js b/lib/directionalmovement/MinusDM.js deleted file mode 100644 index 615daea..0000000 --- a/lib/directionalmovement/MinusDM.js +++ /dev/null @@ -1,58 +0,0 @@ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -/** - * Created by AAravindan on 5/8/16. - */ -"use strict"; -export class MDMInput extends IndicatorInput { -} -; -export class MDM extends Indicator { - constructor(input) { - super(input); - var lows = input.low; - var highs = input.high; - var format = this.format; - if (lows.length != highs.length) { - throw ('Inputs(low,high) not of equal size'); - } - this.result = []; - this.generator = (function* () { - var minusDm; - var current = yield; - var last; - while (true) { - if (last) { - let upMove = (current.high - last.high); - let downMove = (last.low - current.low); - minusDm = format((downMove > upMove && downMove > 0) ? downMove : 0); - } - last = current; - current = yield minusDm; - } - })(); - this.generator.next(); - lows.forEach((tick, index) => { - var result = this.generator.next({ - high: highs[index], - low: lows[index] - }); - if (result.value !== undefined) - this.result.push(result.value); - }); - } - ; - static calculate(input) { - Indicator.reverseInputs(input); - var result = new MDM(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; - } - ; - nextValue(price) { - return this.generator.next(price).value; - } - ; -} diff --git a/lib/directionalmovement/PlusDM.js b/lib/directionalmovement/PlusDM.js deleted file mode 100644 index 3feb36c..0000000 --- a/lib/directionalmovement/PlusDM.js +++ /dev/null @@ -1,57 +0,0 @@ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -/** - * Created by AAravindan on 5/8/16. - */ -export class PDMInput extends IndicatorInput { -} -; -export class PDM extends Indicator { - constructor(input) { - super(input); - var lows = input.low; - var highs = input.high; - var format = this.format; - if (lows.length != highs.length) { - throw ('Inputs(low,high) not of equal size'); - } - this.result = []; - this.generator = (function* () { - var plusDm; - var current = yield; - var last; - while (true) { - if (last) { - let upMove = (current.high - last.high); - let downMove = (last.low - current.low); - plusDm = format((upMove > downMove && upMove > 0) ? upMove : 0); - } - last = current; - current = yield plusDm; - } - })(); - this.generator.next(); - lows.forEach((tick, index) => { - var result = this.generator.next({ - high: highs[index], - low: lows[index] - }); - if (result.value !== undefined) - this.result.push(result.value); - }); - } - ; - static calculate(input) { - Indicator.reverseInputs(input); - var result = new PDM(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; - } - ; - nextValue(price) { - return this.generator.next(price).value; - } - ; -} diff --git a/lib/directionalmovement/TrueRange.js b/lib/directionalmovement/TrueRange.js deleted file mode 100644 index aa96f79..0000000 --- a/lib/directionalmovement/TrueRange.js +++ /dev/null @@ -1,67 +0,0 @@ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -/** - * Created by AAravindan on 5/8/16. - */ -/** - * Created by AAravindan on 5/8/16. - */ -"use strict"; -export class TrueRangeInput extends IndicatorInput { -} -; -export class TrueRange extends Indicator { - constructor(input) { - super(input); - var lows = input.low; - var highs = input.high; - var closes = input.close; - var format = this.format; - if (lows.length != highs.length) { - throw ('Inputs(low,high) not of equal size'); - } - this.result = []; - this.generator = (function* () { - var current = yield; - var previousClose, result; - while (true) { - if (previousClose === undefined) { - previousClose = current.close; - current = yield result; - } - result = Math.max(current.high - current.low, isNaN(Math.abs(current.high - previousClose)) ? 0 : Math.abs(current.high - previousClose), isNaN(Math.abs(current.low - previousClose)) ? 0 : Math.abs(current.low - previousClose)); - previousClose = current.close; - if (result != undefined) { - result = format(result); - } - current = yield result; - } - })(); - this.generator.next(); - lows.forEach((tick, index) => { - var result = this.generator.next({ - high: highs[index], - low: lows[index], - close: closes[index] - }); - if (result.value != undefined) { - this.result.push(result.value); - } - }); - } - ; - nextValue(price) { - return this.generator.next(price).value; - } - ; -} -TrueRange.calculate = truerange; -export function truerange(input) { - Indicator.reverseInputs(input); - var result = new TrueRange(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/ichimoku/IchimokuCloud.js b/lib/ichimoku/IchimokuCloud.js deleted file mode 100644 index 51df6c4..0000000 --- a/lib/ichimoku/IchimokuCloud.js +++ /dev/null @@ -1,96 +0,0 @@ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -import LinkedList from '../Utils/FixedSizeLinkedList'; -export class IchimokuCloudInput extends IndicatorInput { - constructor() { - super(...arguments); - this.conversionPeriod = 9; - this.basePeriod = 26; - this.spanPeriod = 52; - this.displacement = 26; - } -} -export class IchimokuCloudOutput { -} -export class IchimokuCloud extends Indicator { - constructor(input) { - super(input); - this.result = []; - var defaults = { - conversionPeriod: 9, - basePeriod: 26, - spanPeriod: 52, - displacement: 26 - }; - var params = Object.assign({}, defaults, input); - var currentConversionData = new LinkedList(params.conversionPeriod * 2, true, true, false); - var currentBaseData = new LinkedList(params.basePeriod * 2, true, true, false); - var currenSpanData = new LinkedList(params.spanPeriod * 2, true, true, false); - this.generator = (function* () { - let result; - let tick; - let period = Math.max(params.conversionPeriod, params.basePeriod, params.spanPeriod, params.displacement); - let periodCounter = 1; - tick = yield; - while (true) { - // Keep a list of lows/highs for the max period - currentConversionData.push(tick.high); - currentConversionData.push(tick.low); - currentBaseData.push(tick.high); - currentBaseData.push(tick.low); - currenSpanData.push(tick.high); - currenSpanData.push(tick.low); - if (periodCounter < period) { - periodCounter++; - } - else { - // Tenkan-sen (ConversionLine): (9-period high + 9-period low)/2)) - let conversionLine = (currentConversionData.periodHigh + currentConversionData.periodLow) / 2; - // Kijun-sen (Base Line): (26-period high + 26-period low)/2)) - let baseLine = (currentBaseData.periodHigh + currentBaseData.periodLow) / 2; - // Senkou Span A (Leading Span A): (Conversion Line + Base Line)/2)) - let spanA = (conversionLine + baseLine) / 2; - // Senkou Span B (Leading Span B): (52-period high + 52-period low)/2)) - let spanB = (currenSpanData.periodHigh + currenSpanData.periodLow) / 2; - // Senkou Span A / Senkou Span B offset by 26 periods - // if(spanCounter < params.displacement) { - // spanCounter++ - // } else { - // spanA = spanAs.shift() - // spanB = spanBs.shift() - // } - result = { - conversion: conversionLine, - base: baseLine, - spanA: spanA, - spanB: spanB - }; - } - tick = yield result; - } - })(); - this.generator.next(); - input.low.forEach((tick, index) => { - var result = this.generator.next({ - high: input.high[index], - low: input.low[index], - }); - if (result.value) { - this.result.push(result.value); - } - }); - } - nextValue(price) { - return this.generator.next(price).value; - } -} -IchimokuCloud.calculate = ichimokucloud; -export function ichimokucloud(input) { - Indicator.reverseInputs(input); - var result = new IchimokuCloud(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/index.ts b/lib/index.ts deleted file mode 100644 index 478393d..0000000 --- a/lib/index.ts +++ /dev/null @@ -1,90 +0,0 @@ -// import FixedSizeLinkedList from "./Utils/FixedSizeLinkedList"; -// export { CandleData, CandleList } from "./StockData"; -// export { sma, SMA } from './moving_averages/SMA'; -// export { ema, EMA } from './moving_averages/EMA'; -// export { wma, WMA } from './moving_averages/WMA'; -// export { wema, WEMA } from './moving_averages/WEMA'; -// export { macd, MACD } from './moving_averages/MACD'; -export { rsi, RSI } from './oscillators/RSI.ts'; -// export { bollingerbands, BollingerBands } from './volatility/BollingerBands'; -// export { adx, ADX } from './directionalmovement/ADX'; -// export { atr, ATR } from './directionalmovement/ATR'; -// export { truerange, TrueRange } from './directionalmovement/TrueRange'; -// export { roc, ROC } from './momentum/ROC'; -// export { kst, KST } from './momentum/KST'; -// export { psar, PSAR } from './momentum/PSAR'; -// export { stochastic, Stochastic } from './momentum/Stochastic'; -// export { williamsr, WilliamsR } from './momentum/WilliamsR'; -// export { adl, ADL } from './volume/ADL'; -// export { obv, OBV } from './volume/OBV'; -// export { trix, TRIX } from './momentum/TRIX'; -// export { forceindex, ForceIndex } from './volume/ForceIndex'; -// export { cci, CCI } from './oscillators/CCI'; -// export { awesomeoscillator, AwesomeOscillator } from './oscillators/AwesomeOscillator'; -// export { vwap, VWAP } from './volume/VWAP'; -// export { volumeprofile, VolumeProfile } from './volume/VolumeProfile'; -// export { mfi, MFI } from './volume/MFI'; -// export { stochasticrsi, StochasticRSI } from './momentum/StochasticRSI'; -// export { averagegain, AverageGain } from './Utils/AverageGain'; -// export { averageloss, AverageLoss } from './Utils/AverageLoss'; -// export { sd, SD } from './Utils/SD'; -// export { highest, Highest } from './Utils/Highest'; -// export { lowest, Lowest } from './Utils/Lowest'; -// export { sum, Sum } from './Utils/Sum'; -// export { FixedSizeLinkedList }; -// export { renko } from './chart_types/Renko'; -// export { HeikinAshi, heikinashi } from './chart_types/HeikinAshi'; -// export { bullish } from './candlestick/Bullish'; -// export { bearish } from './candlestick/Bearish'; -// export { abandonedbaby } from './candlestick/AbandonedBaby'; -// export { doji } from './candlestick/Doji'; -// export { bearishengulfingpattern } from './candlestick/BearishEngulfingPattern'; -// export { bullishengulfingpattern } from './candlestick/BullishEngulfingPattern'; -// export { darkcloudcover } from './candlestick/DarkCloudCover'; -// export { downsidetasukigap } from './candlestick/DownsideTasukiGap'; -// export { dragonflydoji } from './candlestick/DragonFlyDoji'; -// export { gravestonedoji } from './candlestick/GraveStoneDoji'; -// export { bullishharami } from './candlestick/BullishHarami'; -// export { bearishharami } from './candlestick/BearishHarami'; -// export { bullishharamicross } from './candlestick/BullishHaramiCross'; -// export { bearishharamicross } from './candlestick/BearishHaramiCross'; -// export { eveningdojistar } from './candlestick/EveningDojiStar'; -// export { eveningstar } from './candlestick/EveningStar'; -// export { morningdojistar } from './candlestick/MorningDojiStar'; -// export { morningstar } from './candlestick/MorningStar'; -// export { bullishmarubozu } from './candlestick/BullishMarubozu'; -// export { bearishmarubozu } from './candlestick/BearishMarubozu'; -// export { piercingline } from './candlestick/PiercingLine'; -// export { bullishspinningtop } from './candlestick/BullishSpinningTop'; -// export { bearishspinningtop } from './candlestick/BearishSpinningTop'; -// export { threeblackcrows } from './candlestick/ThreeBlackCrows'; -// export { threewhitesoldiers } from './candlestick/ThreeWhiteSoldiers'; -// export { bullishhammerstick } from './candlestick/BullishHammerStick'; -// export { bearishhammerstick } from './candlestick/BearishHammerStick'; -// export { bullishinvertedhammerstick } from './candlestick/BullishInvertedHammerStick'; -// export { bearishinvertedhammerstick } from './candlestick/BearishInvertedHammerStick'; -// export { hammerpattern } from './candlestick/HammerPattern'; -// export { hammerpatternunconfirmed } from './candlestick/HammerPatternUnconfirmed'; -// export { hangingman } from './candlestick/HangingMan'; -// export { hangingmanunconfirmed } from './candlestick/HangingManUnconfirmed'; -// export { shootingstar } from './candlestick/ShootingStar'; -// export { shootingstarunconfirmed } from './candlestick/ShootingStarUnconfirmed'; -// export { tweezertop } from './candlestick/TweezerTop'; -// export { tweezerbottom } from './candlestick/TweezerBottom'; -// export { fibonacciretracement } from './drawingtools/fibonacci'; - -// export { predictPattern, PatternDetector } from './patterndetection/patterndetection'; -// export { AvailablePatterns } from './patterndetection/patterndetection'; -// export { hasDoubleBottom} from './patterndetection/patterndetection'; -// export { hasDoubleTop } from './patterndetection/patterndetection'; -// export { hasHeadAndShoulder} from './patterndetection/patterndetection'; -// export { hasInverseHeadAndShoulder } from './patterndetection/patterndetection'; -// export { isTrendingUp} from './patterndetection/patterndetection'; -// export { isTrendingDown } from './patterndetection/patterndetection'; - -// export { ichimokucloud, IchimokuCloud } from './ichimoku/IchimokuCloud'; -// export { keltnerchannels, KeltnerChannels, KeltnerChannelsInput, KeltnerChannelsOutput } from './volatility/KeltnerChannels'; -// export { chandelierexit, ChandelierExit, ChandelierExitInput, ChandelierExitOutput } from './volatility/ChandelierExit'; -// export { crossUp, CrossUp } from './Utils/CrossUp'; -// export { crossDown, CrossDown } from './Utils/CrossDown'; -// export { setConfig, getConfig } from './config'; diff --git a/lib/momentum/KST.js b/lib/momentum/KST.js deleted file mode 100644 index 0c85a75..0000000 --- a/lib/momentum/KST.js +++ /dev/null @@ -1,87 +0,0 @@ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -import { SMA } from '../moving_averages/SMA'; -import { ROC } from './ROC'; -export class KSTInput extends IndicatorInput { -} -export class KSTOutput { -} -export class KST extends Indicator { - constructor(input) { - super(input); - let priceArray = input.values; - let rocPer1 = input.ROCPer1; - let rocPer2 = input.ROCPer2; - let rocPer3 = input.ROCPer3; - let rocPer4 = input.ROCPer4; - let smaPer1 = input.SMAROCPer1; - let smaPer2 = input.SMAROCPer2; - let smaPer3 = input.SMAROCPer3; - let smaPer4 = input.SMAROCPer4; - let signalPeriod = input.signalPeriod; - let roc1 = new ROC({ period: rocPer1, values: [] }); - let roc2 = new ROC({ period: rocPer2, values: [] }); - let roc3 = new ROC({ period: rocPer3, values: [] }); - let roc4 = new ROC({ period: rocPer4, values: [] }); - let sma1 = new SMA({ period: smaPer1, values: [], format: (v) => { return v; } }); - let sma2 = new SMA({ period: smaPer2, values: [], format: (v) => { return v; } }); - let sma3 = new SMA({ period: smaPer3, values: [], format: (v) => { return v; } }); - let sma4 = new SMA({ period: smaPer4, values: [], format: (v) => { return v; } }); - let signalSMA = new SMA({ period: signalPeriod, values: [], format: (v) => { return v; } }); - var format = this.format; - this.result = []; - let firstResult = Math.max(rocPer1 + smaPer1, rocPer2 + smaPer2, rocPer3 + smaPer3, rocPer4 + smaPer4); - this.generator = (function* () { - let index = 1; - let tick = yield; - let kst; - let RCMA1, RCMA2, RCMA3, RCMA4, signal, result; - while (true) { - let roc1Result = roc1.nextValue(tick); - let roc2Result = roc2.nextValue(tick); - let roc3Result = roc3.nextValue(tick); - let roc4Result = roc4.nextValue(tick); - RCMA1 = (roc1Result !== undefined) ? sma1.nextValue(roc1Result) : undefined; - RCMA2 = (roc2Result !== undefined) ? sma2.nextValue(roc2Result) : undefined; - RCMA3 = (roc3Result !== undefined) ? sma3.nextValue(roc3Result) : undefined; - RCMA4 = (roc4Result !== undefined) ? sma4.nextValue(roc4Result) : undefined; - if (index < firstResult) { - index++; - } - else { - kst = (RCMA1 * 1) + (RCMA2 * 2) + (RCMA3 * 3) + (RCMA4 * 4); - } - signal = (kst !== undefined) ? signalSMA.nextValue(kst) : undefined; - result = kst !== undefined ? { - kst: format(kst), - signal: signal ? format(signal) : undefined - } : undefined; - tick = yield result; - } - })(); - this.generator.next(); - priceArray.forEach((tick) => { - let result = this.generator.next(tick); - if (result.value != undefined) { - this.result.push(result.value); - } - }); - } - ; - nextValue(price) { - let nextResult = this.generator.next(price); - if (nextResult.value != undefined) - return nextResult.value; - } - ; -} -KST.calculate = kst; -export function kst(input) { - Indicator.reverseInputs(input); - var result = new KST(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/momentum/PSAR.js b/lib/momentum/PSAR.js deleted file mode 100644 index 0a05a41..0000000 --- a/lib/momentum/PSAR.js +++ /dev/null @@ -1,114 +0,0 @@ -import { IndicatorInput, Indicator } from '../indicator/indicator'; -"use strict"; -/* - There seems to be a few interpretations of the rules for this regarding which prices. - I mean the english from which periods are included. The wording does seem to - introduce some discrepancy so maybe that is why. I want to put the author's - own description here to reassess this later. - ---------------------------------------------------------------------------------------- - For the first day of entry the SAR is the previous Significant Point - - If long the SP is the lowest price reached while in the previous short trade - If short the SP is the highest price reached while in the previous long trade - - If long: - Find the difference between the highest price made while in the trade and the SAR for today. - Multiple the difference by the AF and ADD the result to today's SAR to obtain the SAR for tomorrow. - Use 0.02 for the first AF and increase it by 0.02 on every day that a new high for the trade is made. - If a new high is not made continue to use the AF as last increased. Do not increase the AF above .20 - - Never move the SAR for tomorrow ABOVE the previous day's LOW or today's LOW. - If the SAR is calculated to be ABOVE the previous day's LOW or today's LOW then use the lower low between today and the previous day as the new SAR. - Make the next day's calculations based on this SAR. - - If short: - Find the difference between the lowest price made while in the trade and the SAR for today. - Multiple the difference by the AF and SUBTRACT the result to today's SAR to obtain the SAR for tomorrow. - Use 0.02 for the first AF and increase it by 0.02 on every day that a new high for the trade is made. - If a new high is not made continue to use the AF as last increased. Do not increase the AF above .20 - - Never move the SAR for tomorrow BELOW the previous day's HIGH or today's HIGH. - If the SAR is calculated to be BELOW the previous day's HIGH or today's HIGH then use the higher high between today and the previous day as the new SAR. Make the next day's calculations based on this SAR. - ---------------------------------------------------------------------------------------- -*/ -export class PSARInput extends IndicatorInput { -} -; -export class PSAR extends Indicator { - constructor(input) { - super(input); - let highs = input.high || []; - let lows = input.low || []; - var genFn = function* (step, max) { - let curr, extreme, sar, furthest; - let up = true; - let accel = step; - let prev = yield; - while (true) { - if (curr) { - sar = sar + accel * (extreme - sar); - if (up) { - sar = Math.min(sar, furthest.low, prev.low); - if (curr.high > extreme) { - extreme = curr.high; - accel = Math.min(accel + step, max); - } - ; - } - else { - sar = Math.max(sar, furthest.high, prev.high); - if (curr.low < extreme) { - extreme = curr.low; - accel = Math.min(accel + step, max); - } - } - if ((up && curr.low < sar) || (!up && curr.high > sar)) { - accel = step; - sar = extreme; - up = !up; - extreme = !up ? curr.low : curr.high; - } - } - else { - // Randomly setup start values? What is the trend on first tick?? - sar = prev.low; - extreme = prev.high; - } - furthest = prev; - if (curr) - prev = curr; - curr = yield sar; - } - }; - this.result = []; - this.generator = genFn(input.step, input.max); - this.generator.next(); - lows.forEach((tick, index) => { - var result = this.generator.next({ - high: highs[index], - low: lows[index], - }); - if (result.value !== undefined) { - this.result.push(result.value); - } - }); - } - ; - nextValue(input) { - let nextResult = this.generator.next(input); - if (nextResult.value !== undefined) - return nextResult.value; - } - ; -} -PSAR.calculate = psar; -export function psar(input) { - Indicator.reverseInputs(input); - var result = new PSAR(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/momentum/ROC.js b/lib/momentum/ROC.js deleted file mode 100644 index ae7b73a..0000000 --- a/lib/momentum/ROC.js +++ /dev/null @@ -1,55 +0,0 @@ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -import LinkedList from '../Utils/FixedSizeLinkedList'; -export class ROCInput extends IndicatorInput { -} -export class ROC extends Indicator { - constructor(input) { - super(input); - var period = input.period; - var priceArray = input.values; - this.result = []; - this.generator = (function* () { - let index = 1; - var pastPeriods = new LinkedList(period); - ; - var tick = yield; - var roc; - while (true) { - pastPeriods.push(tick); - if (index < period) { - index++; - } - else { - roc = ((tick - pastPeriods.lastShift) / (pastPeriods.lastShift)) * 100; - } - tick = yield roc; - } - })(); - this.generator.next(); - priceArray.forEach((tick) => { - var result = this.generator.next(tick); - if (result.value != undefined && (!isNaN(result.value))) { - this.result.push(this.format(result.value)); - } - }); - } - nextValue(price) { - var nextResult = this.generator.next(price); - if (nextResult.value != undefined && (!isNaN(nextResult.value))) { - return this.format(nextResult.value); - } - } - ; -} -ROC.calculate = roc; -; -export function roc(input) { - Indicator.reverseInputs(input); - var result = new ROC(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/momentum/Stochastic.1.js b/lib/momentum/Stochastic.1.js deleted file mode 100644 index 8e5712c..0000000 --- a/lib/momentum/Stochastic.1.js +++ /dev/null @@ -1,93 +0,0 @@ -import { IndicatorInput, Indicator } from '../indicator/indicator'; -/** - * Created by AAravindan on 5/10/16. - */ -"use strict"; -import LinkedList from '../Utils/FixedSizeLinkedList'; -import { SMA } from '../moving_averages/SMA'; -export class StochasticInput extends IndicatorInput { -} -; -export class StochasticOutput { -} -; -export class Stochastic extends Indicator { - constructor(input) { - super(input); - let lows = input.low; - let highs = input.high; - let closes = input.close; - let period = input.period; - let signalPeriod = input.signalPeriod; - let format = this.format; - if (!((lows.length === highs.length) && (highs.length === closes.length))) { - throw ('Inputs(low,high, close) not of equal size'); - } - this.result = []; - //%K = (Current Close - Lowest Low)/(Highest High - Lowest Low) * 100 - //%D = 3-day SMA of %K - // - //Lowest Low = lowest low for the look-back period - //Highest High = highest high for the look-back period - //%K is multiplied by 100 to move the decimal point two places - this.generator = (function* () { - let index = 1; - let pastHighPeriods = new LinkedList(period, true, false); - let pastLowPeriods = new LinkedList(period, false, true); - let dSma = new SMA({ - period: signalPeriod, - values: [], - format: (v) => { return v; } - }); - let k, d; - var tick = yield; - while (true) { - pastHighPeriods.push(tick.high); - pastLowPeriods.push(tick.low); - if (index < period) { - index++; - tick = yield; - continue; - } - let periodLow = pastLowPeriods.periodLow; - k = (tick.close - periodLow) / (pastHighPeriods.periodHigh - periodLow) * 100; - k = isNaN(k) ? 0 : k; //This happens when the close, high and low are same for the entire period; Bug fix for - d = dSma.nextValue(k); - tick = yield { - k: format(k), - d: (d !== undefined) ? format(d) : undefined - }; - } - })(); - this.generator.next(); - lows.forEach((tick, index) => { - var result = this.generator.next({ - high: highs[index], - low: lows[index], - close: closes[index] - }); - if (result.value !== undefined) { - this.result.push(result.value); - } - }); - } - ; - nextValue(input) { - let nextResult = this.generator.next(input); - if (nextResult.value !== undefined) - return nextResult.value; - } - ; -} -Stochastic.calculate = stochastic; -export function stochastic(input) { - Indicator.reverseInputs(input); - var result = new Stochastic(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; -//# sourceMappingURL=Stochastic.1.js.map \ No newline at end of file diff --git a/lib/momentum/Stochastic.js b/lib/momentum/Stochastic.js deleted file mode 100644 index a2fab54..0000000 --- a/lib/momentum/Stochastic.js +++ /dev/null @@ -1,92 +0,0 @@ -import { IndicatorInput, Indicator } from '../indicator/indicator'; -/** - * Created by AAravindan on 5/10/16. - */ -"use strict"; -import LinkedList from '../Utils/FixedSizeLinkedList'; -import { SMA } from '../moving_averages/SMA'; -export class StochasticInput extends IndicatorInput { -} -; -export class StochasticOutput { -} -; -export class Stochastic extends Indicator { - constructor(input) { - super(input); - let lows = input.low; - let highs = input.high; - let closes = input.close; - let period = input.period; - let signalPeriod = input.signalPeriod; - let format = this.format; - if (!((lows.length === highs.length) && (highs.length === closes.length))) { - throw ('Inputs(low,high, close) not of equal size'); - } - this.result = []; - //%K = (Current Close - Lowest Low)/(Highest High - Lowest Low) * 100 - //%D = 3-day SMA of %K - // - //Lowest Low = lowest low for the look-back period - //Highest High = highest high for the look-back period - //%K is multiplied by 100 to move the decimal point two places - this.generator = (function* () { - let index = 1; - let pastHighPeriods = new LinkedList(period, true, false); - let pastLowPeriods = new LinkedList(period, false, true); - let dSma = new SMA({ - period: signalPeriod, - values: [], - format: (v) => { return v; } - }); - let k, d; - var tick = yield; - while (true) { - pastHighPeriods.push(tick.high); - pastLowPeriods.push(tick.low); - if (index < period) { - index++; - tick = yield; - continue; - } - let periodLow = pastLowPeriods.periodLow; - k = (tick.close - periodLow) / (pastHighPeriods.periodHigh - periodLow) * 100; - k = isNaN(k) ? 0 : k; //This happens when the close, high and low are same for the entire period; Bug fix for - d = dSma.nextValue(k); - tick = yield { - k: format(k), - d: (d !== undefined) ? format(d) : undefined - }; - } - })(); - this.generator.next(); - lows.forEach((tick, index) => { - var result = this.generator.next({ - high: highs[index], - low: lows[index], - close: closes[index] - }); - if (result.value !== undefined) { - this.result.push(result.value); - } - }); - } - ; - nextValue(input) { - let nextResult = this.generator.next(input); - if (nextResult.value !== undefined) - return nextResult.value; - } - ; -} -Stochastic.calculate = stochastic; -export function stochastic(input) { - Indicator.reverseInputs(input); - var result = new Stochastic(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/momentum/StochasticRSI.js b/lib/momentum/StochasticRSI.js deleted file mode 100644 index 8869331..0000000 --- a/lib/momentum/StochasticRSI.js +++ /dev/null @@ -1,80 +0,0 @@ -import { IndicatorInput, Indicator } from '../indicator/indicator'; -/** - * Created by AAravindan on 5/10/16. - */ -"use strict"; -import { SMA } from '../moving_averages/SMA'; -import { RSI } from '../oscillators/RSI'; -import { Stochastic } from '../momentum/Stochastic'; -export class StochasticRsiInput extends IndicatorInput { -} -; -export class StochasticRSIOutput { -} -; -export class StochasticRSI extends Indicator { - constructor(input) { - super(input); - let closes = input.values; - let rsiPeriod = input.rsiPeriod; - let stochasticPeriod = input.stochasticPeriod; - let kPeriod = input.kPeriod; - let dPeriod = input.dPeriod; - let format = this.format; - this.result = []; - this.generator = (function* () { - let index = 1; - let rsi = new RSI({ period: rsiPeriod, values: [] }); - let stochastic = new Stochastic({ period: stochasticPeriod, high: [], low: [], close: [], signalPeriod: kPeriod }); - let dSma = new SMA({ - period: dPeriod, - values: [], - format: (v) => { return v; } - }); - let lastRSI, stochasticRSI, d, result; - var tick = yield; - while (true) { - lastRSI = rsi.nextValue(tick); - if (lastRSI !== undefined) { - var stochasticInput = { high: lastRSI, low: lastRSI, close: lastRSI }; - stochasticRSI = stochastic.nextValue(stochasticInput); - if (stochasticRSI !== undefined && stochasticRSI.d !== undefined) { - d = dSma.nextValue(stochasticRSI.d); - if (d !== undefined) - result = { - stochRSI: stochasticRSI.k, - k: stochasticRSI.d, - d: d - }; - } - } - tick = yield result; - } - })(); - this.generator.next(); - closes.forEach((tick, index) => { - var result = this.generator.next(tick); - if (result.value !== undefined) { - this.result.push(result.value); - } - }); - } - ; - nextValue(input) { - let nextResult = this.generator.next(input); - if (nextResult.value !== undefined) - return nextResult.value; - } - ; -} -StochasticRSI.calculate = stochasticrsi; -export function stochasticrsi(input) { - Indicator.reverseInputs(input); - var result = new StochasticRSI(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/momentum/TRIX.js b/lib/momentum/TRIX.js deleted file mode 100644 index 826bbc5..0000000 --- a/lib/momentum/TRIX.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Created by AAravindan on 5/9/16. - */ -"use strict"; -import { ROC } from './ROC.js'; -import { EMA } from '../moving_averages/EMA.js'; -import { Indicator, IndicatorInput } from '../indicator/indicator'; -export class TRIXInput extends IndicatorInput { -} -; -export class TRIX extends Indicator { - constructor(input) { - super(input); - let priceArray = input.values; - let period = input.period; - let format = this.format; - let ema = new EMA({ period: period, values: [], format: (v) => { return v; } }); - let emaOfema = new EMA({ period: period, values: [], format: (v) => { return v; } }); - let emaOfemaOfema = new EMA({ period: period, values: [], format: (v) => { return v; } }); - let trixROC = new ROC({ period: 1, values: [], format: (v) => { return v; } }); - this.result = []; - this.generator = (function* () { - let tick = yield; - while (true) { - let initialema = ema.nextValue(tick); - let smoothedResult = initialema ? emaOfema.nextValue(initialema) : undefined; - let doubleSmoothedResult = smoothedResult ? emaOfemaOfema.nextValue(smoothedResult) : undefined; - let result = doubleSmoothedResult ? trixROC.nextValue(doubleSmoothedResult) : undefined; - tick = yield result ? format(result) : undefined; - } - })(); - this.generator.next(); - priceArray.forEach((tick) => { - let result = this.generator.next(tick); - if (result.value !== undefined) { - this.result.push(result.value); - } - }); - } - nextValue(price) { - let nextResult = this.generator.next(price); - if (nextResult.value !== undefined) - return nextResult.value; - } - ; -} -TRIX.calculate = trix; -export function trix(input) { - Indicator.reverseInputs(input); - var result = new TRIX(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/momentum/WilliamsR.js b/lib/momentum/WilliamsR.js deleted file mode 100644 index 96806a3..0000000 --- a/lib/momentum/WilliamsR.js +++ /dev/null @@ -1,74 +0,0 @@ -import { IndicatorInput, Indicator } from '../indicator/indicator'; -import LinkedList from '../Utils/FixedSizeLinkedList'; -export class WilliamsRInput extends IndicatorInput { -} -; -export class WilliamsR extends Indicator { - constructor(input) { - super(input); - let lows = input.low; - let highs = input.high; - let closes = input.close; - let period = input.period; - let format = this.format; - if (!((lows.length === highs.length) && (highs.length === closes.length))) { - throw ('Inputs(low,high, close) not of equal size'); - } - this.result = []; - //%R = (Highest High - Close)/(Highest High - Lowest Low) * -100 - //Lowest Low = lowest low for the look-back period - //Highest High = highest high for the look-back period - //%R is multiplied by -100 correct the inversion and move the decimal. - this.generator = (function* () { - let index = 1; - let pastHighPeriods = new LinkedList(period, true, false); - let pastLowPeriods = new LinkedList(period, false, true); - let periodLow; - let periodHigh; - var tick = yield; - let williamsR; - while (true) { - pastHighPeriods.push(tick.high); - pastLowPeriods.push(tick.low); - if (index < period) { - index++; - tick = yield; - continue; - } - periodLow = pastLowPeriods.periodLow; - periodHigh = pastHighPeriods.periodHigh; - williamsR = format((periodHigh - tick.close) / (periodHigh - periodLow) * -100); - tick = yield williamsR; - } - })(); - this.generator.next(); - lows.forEach((low, index) => { - var result = this.generator.next({ - high: highs[index], - low: lows[index], - close: closes[index] - }); - if (result.value !== undefined) { - this.result.push(result.value); - } - }); - } - ; - nextValue(price) { - var nextResult = this.generator.next(price); - if (nextResult.value != undefined) - return this.format(nextResult.value); - } - ; -} -WilliamsR.calculate = williamsr; -export function williamsr(input) { - Indicator.reverseInputs(input); - var result = new WilliamsR(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/moving_averages/EMA.txt b/lib/moving_averages/EMA.txt deleted file mode 100644 index 0564821..0000000 --- a/lib/moving_averages/EMA.txt +++ /dev/null @@ -1,54 +0,0 @@ -import { Indicator } from '../indicator/indicator.ts'; -import { SMA } from './SMA.ts'; -export class EMA extends Indicator { - constructor(input) { - super(input); - var period = input.period; - var priceArray = input.values; - var exponent = (2 / (period + 1)); - var sma; - this.result = []; - sma = new SMA({ period: period, values: [] }); - var genFn = (function* () { - var tick = yield; - var prevEma; - while (true) { - if (prevEma !== undefined && tick !== undefined) { - prevEma = ((tick - prevEma) * exponent) + prevEma; - tick = yield prevEma; - } - else { - tick = yield; - prevEma = sma.nextValue(tick); - if (prevEma) - tick = yield prevEma; - } - } - }); - this.generator = genFn(); - this.generator.next(); - this.generator.next(); - priceArray.forEach((tick) => { - var result = this.generator.next(tick); - if (result.value != undefined) { - this.result.push(this.format(result.value)); - } - }); - } - nextValue(price) { - var result = this.generator.next(price).value; - if (result != undefined) - return this.format(result); - } - ; -} -EMA.calculate = ema; -export function ema(input) { - Indicator.reverseInputs(input); - var result = new EMA(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} diff --git a/lib/moving_averages/MACD.txt b/lib/moving_averages/MACD.txt deleted file mode 100644 index caa54ab..0000000 --- a/lib/moving_averages/MACD.txt +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Created by AAravindan on 5/4/16. - */ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -import { SMA } from './SMA'; -import { EMA } from './EMA'; -export class MACDInput extends IndicatorInput { - constructor(values) { - super(); - this.values = values; - this.SimpleMAOscillator = true; - this.SimpleMASignal = true; - } -} -export class MACDOutput { -} -export class MACD extends Indicator { - constructor(input) { - super(input); - var oscillatorMAtype = input.SimpleMAOscillator ? SMA : EMA; - var signalMAtype = input.SimpleMASignal ? SMA : EMA; - var fastMAProducer = new oscillatorMAtype({ period: input.fastPeriod, values: [], format: (v) => { return v; } }); - var slowMAProducer = new oscillatorMAtype({ period: input.slowPeriod, values: [], format: (v) => { return v; } }); - var signalMAProducer = new signalMAtype({ period: input.signalPeriod, values: [], format: (v) => { return v; } }); - var format = this.format; - this.result = []; - this.generator = (function* () { - var index = 0; - var tick; - var MACD, signal, histogram, fast, slow; - while (true) { - if (index < input.slowPeriod) { - tick = yield; - fast = fastMAProducer.nextValue(tick); - slow = slowMAProducer.nextValue(tick); - index++; - continue; - } - if (fast && slow) { //Just for typescript to be happy - MACD = fast - slow; - signal = signalMAProducer.nextValue(MACD); - } - histogram = MACD - signal; - tick = yield ({ - //fast : fast, - //slow : slow, - MACD: format(MACD), - signal: signal ? format(signal) : undefined, - histogram: isNaN(histogram) ? undefined : format(histogram) - }); - fast = fastMAProducer.nextValue(tick); - slow = slowMAProducer.nextValue(tick); - } - })(); - this.generator.next(); - input.values.forEach((tick) => { - var result = this.generator.next(tick); - if (result.value != undefined) { - this.result.push(result.value); - } - }); - } - nextValue(price) { - var result = this.generator.next(price).value; - return result; - } - ; -} -MACD.calculate = macd; -export function macd(input) { - Indicator.reverseInputs(input); - var result = new MACD(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/moving_averages/SMA.ts b/lib/moving_averages/SMA.ts deleted file mode 100644 index 6de1d40..0000000 --- a/lib/moving_averages/SMA.ts +++ /dev/null @@ -1,78 +0,0 @@ -// deno-lint-ignore-file -//STEP 1. Import Necessary indicator or rather last step -import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; -import { LinkedList } from '../Utils/LinkedList.ts'; -//STEP 2. Create the input for the indicator, mandatory should be in the constructor -export class MAInput extends IndicatorInput { -period: any; -values: any; - constructor(period: any, values: any) { - super(); - this.period = period; - this.values = values; - } -} -//STEP3. Add class based syntax with export -interface Input { period?: any; values?: any; format?: (v: any) => any; } - -export class SMA extends Indicator { -period: any; -price: any; -generator: Generator; -static calculate: (input: { reversedInput: any; values?: any[]|undefined; open?: any[]|undefined; high?: any[]|undefined; low?: any[]|undefined; close?: any[]|undefined; volume?: any[]|undefined; timestamp?: any[]|undefined; }) => any; - - constructor(input: Input| any) { - super(input); - this.period = input.period; - this.price = input.values; - var genFn = (function* (period: number) { - var list = new LinkedList(); - var sum = 0; - var counter = 1; - // @ts-ignore - var current = yield; - var result; - list.push(0); - while (true) { - if (counter < period) { - counter++; - list.push(current); - sum = sum + current; - } - else { - sum = sum - list.shift() + current; - result = ((sum) / period); - list.push(current); - } - // @ts-ignore - current = yield result; - } - }); - this.generator = genFn(this.period); - this.generator.next(); - this.result = []; - this.price.forEach((tick: any) => { - var result = this.generator.next(tick); - if (result.value !== undefined) { - this.result.push(this.format(result.value)); - } - }); - } - nextValue(price: any) { - var result = this.generator.next(price).value; - if (result != undefined) - return this.format(result); - } - ; -} -SMA.calculate = sma; -export function sma(input: { reversedInput: any; values?: any[]; open?: any[]; high?: any[]; low?: any[]; close?: any[]; volume?: any[]; timestamp?: any[]; } | any) { - Indicator.reverseInputs(input); - var result = new SMA(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -}; -//STEP 6. Run the tests diff --git a/lib/moving_averages/WEMA.txt b/lib/moving_averages/WEMA.txt deleted file mode 100644 index 2a5c84e..0000000 --- a/lib/moving_averages/WEMA.txt +++ /dev/null @@ -1,54 +0,0 @@ -import { Indicator } from '../indicator/indicator'; -import { SMA } from './SMA'; -export class WEMA extends Indicator { - constructor(input) { - super(input); - var period = input.period; - var priceArray = input.values; - var exponent = 1 / period; - var sma; - this.result = []; - sma = new SMA({ period: period, values: [] }); - var genFn = (function* () { - var tick = yield; - var prevEma; - while (true) { - if (prevEma !== undefined && tick !== undefined) { - prevEma = ((tick - prevEma) * exponent) + prevEma; - tick = yield prevEma; - } - else { - tick = yield; - prevEma = sma.nextValue(tick); - if (prevEma !== undefined) - tick = yield prevEma; - } - } - }); - this.generator = genFn(); - this.generator.next(); - this.generator.next(); - priceArray.forEach((tick) => { - var result = this.generator.next(tick); - if (result.value != undefined) { - this.result.push(this.format(result.value)); - } - }); - } - nextValue(price) { - var result = this.generator.next(price).value; - if (result != undefined) - return this.format(result); - } - ; -} -WEMA.calculate = wema; -export function wema(input) { - Indicator.reverseInputs(input); - var result = new WEMA(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} diff --git a/lib/moving_averages/WMA.txt b/lib/moving_averages/WMA.txt deleted file mode 100644 index 7be3cec..0000000 --- a/lib/moving_averages/WMA.txt +++ /dev/null @@ -1,55 +0,0 @@ -"use strict"; -import { Indicator } from '../indicator/indicator'; -import { LinkedList } from '../Utils/LinkedList'; -export class WMA extends Indicator { - constructor(input) { - super(input); - var period = input.period; - var priceArray = input.values; - this.result = []; - this.generator = (function* () { - let data = new LinkedList(); - let denominator = period * (period + 1) / 2; - while (true) { - if ((data.length) < period) { - data.push(yield); - } - else { - data.resetCursor(); - let result = 0; - for (let i = 1; i <= period; i++) { - result = result + (data.next() * i / (denominator)); - } - var next = yield result; - data.shift(); - data.push(next); - } - } - })(); - this.generator.next(); - priceArray.forEach((tick, index) => { - var result = this.generator.next(tick); - if (result.value != undefined) { - this.result.push(this.format(result.value)); - } - }); - } - //STEP 5. REMOVE GET RESULT FUNCTION - nextValue(price) { - var result = this.generator.next(price).value; - if (result != undefined) - return this.format(result); - } - ; -} -WMA.calculate = wma; -; -export function wma(input) { - Indicator.reverseInputs(input); - var result = new WMA(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} diff --git a/lib/oscillators/AwesomeOscillator.ts b/lib/oscillators/AwesomeOscillator.ts deleted file mode 100644 index 63d444e..0000000 --- a/lib/oscillators/AwesomeOscillator.ts +++ /dev/null @@ -1,68 +0,0 @@ -// deno-lint-ignore-file -import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; -import { SMA } from '../moving_averages/SMA.ts'; -export class AwesomeOscillatorInput extends IndicatorInput { -} -export class AwesomeOscillator extends Indicator { -generator: Generator; -static calculate: (input: { reversedInput: any; values?: any[]|undefined; open?: any[]|undefined; high?: any[]|undefined; low?: any[]|undefined; close?: any[]|undefined; volume?: any[]|undefined; timestamp?: any[]|undefined; }) => any; - constructor(input: { high?: any; low?: any; fastPeriod?: any; slowPeriod?: any; format?: (v: any) => any; } | any) { - super(input); - var highs = input.high; - var lows = input.low; - var fastPeriod = input.fastPeriod; - var slowPeriod = input.slowPeriod; - var slowSMA = new SMA({ values: [], period: slowPeriod }); - var fastSMA = new SMA({ values: [], period: fastPeriod }); - this.result = []; - this.generator = (function* () { - var result; - var tick; - var medianPrice; - var slowSmaValue; - var fastSmaValue; - // @ts-ignore - tick = yield; - while (true) { - medianPrice = (tick.high + tick.low) / 2; - slowSmaValue = slowSMA.nextValue(medianPrice); - fastSmaValue = fastSMA.nextValue(medianPrice); - if (slowSmaValue !== undefined && fastSmaValue !== undefined) { - result = fastSmaValue - slowSmaValue; - } - // @ts-ignore - tick = yield result; - } - })(); - this.generator.next(); - highs.forEach((tickHigh: any, index: string|number) => { - var tickInput = { - high: tickHigh, - low: lows[index], - }; - var result = this.generator.next(tickInput); - if (result.value != undefined) { - this.result.push(this.format(result.value)); - } - }); - } - ; - nextValue(price: any) { - var result = this.generator.next(price); - if (result.value != undefined) { - return this.format(result.value); - } - } - ; -} -AwesomeOscillator.calculate = awesomeoscillator; -export function awesomeoscillator(input: { reversedInput: any; values?: any[]; open?: any[]; high?: any[]; low?: any[]; close?: any[]; volume?: any[]; timestamp?: any[]; } | any) { - Indicator.reverseInputs(input); - var result = new AwesomeOscillator(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/oscillators/CCI.ts b/lib/oscillators/CCI.ts deleted file mode 100644 index 6fe3b36..0000000 --- a/lib/oscillators/CCI.ts +++ /dev/null @@ -1,78 +0,0 @@ -// deno-lint-ignore-file -import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; -import { SMA } from '../moving_averages/SMA.ts'; -import LinkedList from '../Utils/FixedSizeLinkedList.ts'; -export class CCIInput extends IndicatorInput {}; -export class CCI extends Indicator { -generator: Generator; -static calculate: (input: any) => any; - constructor(input: { low?: any; high?: any; close?: any; period?: any; format?: (v: any) => any; } | any) { - super(input); - var lows = input.low; - var highs = input.high; - var closes = input.close; - var period = input.period; - var format = this.format; - let constant = .015; - var currentTpSet = new LinkedList(period); - ; - var tpSMACalculator = new SMA({ period: period, values: [], format: (v: any) => { return v; } }); - if (!((lows.length === highs.length) && (highs.length === closes.length))) { - throw ('Inputs(low,high, close) not of equal size'); - } - this.result = []; - this.generator = (function* () { - // @ts-ignore - var tick = yield; - while (true) { - let tp = (tick.high + tick.low + tick.close) / 3; - currentTpSet.push(tp); - let smaTp = tpSMACalculator.nextValue(tp); - let meanDeviation = null; - let cci; - let sum = 0; - if (smaTp != undefined) { - //First, subtract the most recent 20-period average of the typical price from each period's typical price. - //Second, take the absolute values of these numbers. - //Third,sum the absolute values. - for (let x of currentTpSet.iterator()) { - sum = sum + (Math.abs(x - smaTp)); - } - //Fourth, divide by the total number of periods (20). - meanDeviation = sum / period; - cci = (tp - smaTp) / (constant * meanDeviation); - } - // @ts-ignore - tick = yield cci; - } - })(); - this.generator.next(); - lows.forEach((_: any, index: string|number) => { - var result = this.generator.next({ - high: highs[index], - low: lows[index], - close: closes[index] - }); - if (result.value != undefined) { - this.result.push(result.value); - } - }); - } - nextValue(price: any) { - let result = this.generator.next(price).value; - if (result != undefined) { - return result; - } - } -} -CCI.calculate = cci; -export function cci(input: { reversedInput: any; values?: any[]; open?: any[]; high?: any[]; low?: any[]; close?: any[]; volume?: any[]; timestamp?: any[]; } | any) { - Indicator.reverseInputs(input); - var result = new CCI(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/oscillators/RSI.ts b/lib/oscillators/RSI.ts deleted file mode 100644 index 0fd24db..0000000 --- a/lib/oscillators/RSI.ts +++ /dev/null @@ -1,70 +0,0 @@ -// deno-lint-ignore-file -/** - * Created by AAravindan on 5/5/16. - */ -import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; -import { AverageGain } from '../Utils/AverageGain.ts'; -import { AverageLoss } from '../Utils/AverageLoss.ts'; -export class RSIInput extends IndicatorInput { -} -export class RSI extends Indicator { -generator: Generator; -static calculate: (input: { reversedInput: any; values?: any[]|undefined; open?: any[]|undefined; high?: any[]|undefined; low?: any[]|undefined; close?: any[]|undefined; volume?: any[]|undefined; timestamp?: any[]|undefined; }) => any; - constructor(input: { period?: any; values?: any; format?: (v: any) => any; } | any) { - super(input); - var period = input.period; - var values = input.values; - var GainProvider = new AverageGain({ period: period, values: [] }); - var LossProvider = new AverageLoss({ period: period, values: [] }); - let count = 1; - this.generator = (function* (period) { - // @ts-ignore - var current = yield; - var lastAvgGain, lastAvgLoss, RS, currentRSI; - while (true) { - lastAvgGain = GainProvider.nextValue(current); - lastAvgLoss = LossProvider.nextValue(current); - if ((lastAvgGain !== undefined) && (lastAvgLoss !== undefined)) { - if (lastAvgLoss === 0) { - currentRSI = 100; - } - else if (lastAvgGain === 0) { - currentRSI = 0; - } - else { - RS = lastAvgGain / lastAvgLoss; - RS = isNaN(RS) ? 0 : RS; - currentRSI = parseFloat((100 - (100 / (1 + RS))).toFixed(2)); - } - } - count++; - // @ts-ignore - current = yield currentRSI; - } - })(period); - this.generator.next(); - this.result = []; - values.forEach((tick: any) => { - var result = this.generator.next(tick); - if (result.value !== undefined) { - this.result.push(result.value); - } - }); - } - ; - nextValue(price: any) { - return this.generator.next(price).value; - } - ; -} -RSI.calculate = rsi; -export function rsi(input: { reversedInput: any; values?: any[]; open?: any[]; high?: any[]; low?: any[]; close?: any[]; volume?: any[]; timestamp?: any[]; } | any) { - Indicator.reverseInputs(input); - var result = new RSI(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/patterndetection/patterndetection.js b/lib/patterndetection/patterndetection.js deleted file mode 100644 index 0de4e69..0000000 --- a/lib/patterndetection/patterndetection.js +++ /dev/null @@ -1,165 +0,0 @@ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -import { Indicator, IndicatorInput } from '../indicator/indicator'; -// import * as tf from '@tensorflow/tfjs'; -var isNodeEnvironment = false; -var model; -var oneHotMap = ['IHS', 'TU', 'DB', 'HS', 'TD', 'DT']; -var tf; -try { - isNodeEnvironment = Object.prototype.toString.call(global.process) === '[object process]'; -} -catch (e) { } -export class PatternDetectorInput extends IndicatorInput { - constructor(values) { - super(); - this.values = values; - } -} -export var AvailablePatterns; -(function (AvailablePatterns) { - AvailablePatterns[AvailablePatterns["IHS"] = 0] = "IHS"; - AvailablePatterns[AvailablePatterns["TU"] = 1] = "TU"; - AvailablePatterns[AvailablePatterns["DB"] = 2] = "DB"; - AvailablePatterns[AvailablePatterns["HS"] = 3] = "HS"; - AvailablePatterns[AvailablePatterns["TD"] = 4] = "TD"; - AvailablePatterns[AvailablePatterns["DT"] = 5] = "DT"; -})(AvailablePatterns || (AvailablePatterns = {})); -function interpolateArray(data, fitCount) { - var linearInterpolate = function (before, after, atPoint) { - return before + (after - before) * atPoint; - }; - var newData = new Array(); - var springFactor = new Number((data.length - 1) / (fitCount - 1)); - newData[0] = data[0]; // for new allocation - for (var i = 1; i < fitCount - 1; i++) { - var tmp = i * springFactor; - var before = new Number(Math.floor(tmp)).toFixed(); - var after = new Number(Math.ceil(tmp)).toFixed(); - var atPoint = tmp - before; - newData[i] = linearInterpolate(data[before], data[after], atPoint); - } - newData[fitCount - 1] = data[data.length - 1]; // for new allocation - return newData; -} -; -function l2Normalize(arr) { - var sum = arr.reduce((cum, value) => { return cum + (value * value); }, 0); - var norm = Math.sqrt(sum); - return arr.map((v) => v / norm); -} -export class PatternDetectorOutput { -} -var modelLoaded = false; -var laodingModel = false; -var loadingPromise; -function loadModel() { - return __awaiter(this, void 0, void 0, function* () { - if (modelLoaded) - return Promise.resolve(true); - if (laodingModel) - return loadingPromise; - laodingModel = true; - loadingPromise = new Promise(function (resolve, reject) { - return __awaiter(this, void 0, void 0, function* () { - if (isNodeEnvironment) { - tf = require('@tensorflow/tfjs'); - console.log('Nodejs Environment detected '); - var tfnode = require('@tensorflow/tfjs-node'); - var modelPath = require('path').resolve(__dirname, '../tf_model/model.json'); - model = yield tf.loadModel(tfnode.io.fileSystem(modelPath)); - } - else { - if (typeof window.tf == "undefined") { - modelLoaded = false; - laodingModel = false; - console.log('Tensorflow js not imported, pattern detection may not work'); - resolve(); - return; - } - tf = window.tf; - console.log('Browser Environment detected ', tf); - console.log('Loading model ....'); - model = yield tf.loadModel('/tf_model/model.json'); - modelLoaded = true; - laodingModel = false; - setTimeout(resolve, 1000); - console.log('Loaded model'); - return; - } - modelLoaded = true; - laodingModel = false; - resolve(); - return; - }); - }); - yield loadingPromise; - return; - }); -} -loadModel(); -export function predictPattern(input) { - return __awaiter(this, void 0, void 0, function* () { - yield loadModel(); - if (input.values.length < 300) { - console.warn('Pattern detector requires atleast 300 data points for a reliable prediction, received just ', input.values.length); - } - Indicator.reverseInputs(input); - var values = input.values; - var output = yield model.predict(tf.tensor2d([l2Normalize(interpolateArray(values, 400))])); - var index = tf.argMax(output, 1).get(0); - Indicator.reverseInputs(input); - return { patternId: index, pattern: oneHotMap[index], probability: output.get(0, 4) * 100 }; - }); -} -export function hasDoubleBottom(input) { - return __awaiter(this, void 0, void 0, function* () { - var result = yield predictPattern(input); - return (result.patternId === AvailablePatterns.DB); - }); -} -export function hasDoubleTop(input) { - return __awaiter(this, void 0, void 0, function* () { - var result = yield predictPattern(input); - return (result.patternId === AvailablePatterns.DT); - }); -} -export function hasHeadAndShoulder(input) { - return __awaiter(this, void 0, void 0, function* () { - var result = yield predictPattern(input); - return (result.patternId === AvailablePatterns.HS); - }); -} -export function hasInverseHeadAndShoulder(input) { - return __awaiter(this, void 0, void 0, function* () { - var result = yield predictPattern(input); - return (result.patternId === AvailablePatterns.IHS); - }); -} -export function isTrendingUp(input) { - return __awaiter(this, void 0, void 0, function* () { - var result = yield predictPattern(input); - return (result.patternId === AvailablePatterns.TU); - }); -} -export function isTrendingDown(input) { - return __awaiter(this, void 0, void 0, function* () { - var result = yield predictPattern(input); - return (result.patternId === AvailablePatterns.TD); - }); -} -export class PatternDetector extends Indicator { -} -PatternDetector.predictPattern = predictPattern; -PatternDetector.hasDoubleBottom = hasDoubleBottom; -PatternDetector.hasDoubleTop = hasDoubleTop; -PatternDetector.hasHeadAndShoulder = hasHeadAndShoulder; -PatternDetector.hasInverseHeadAndShoulder = hasInverseHeadAndShoulder; -PatternDetector.isTrendingUp = isTrendingUp; -PatternDetector.isTrendingDown = isTrendingDown; diff --git a/lib/volatility/BollingerBands.js b/lib/volatility/BollingerBands.js deleted file mode 100644 index 42b930f..0000000 --- a/lib/volatility/BollingerBands.js +++ /dev/null @@ -1,69 +0,0 @@ -"use strict"; -import { Indicator, IndicatorInput } from '../indicator/indicator'; -import { SMA } from '../moving_averages/SMA'; -import { SD } from '../Utils/SD'; -export class BollingerBandsInput extends IndicatorInput { -} -; -export class BollingerBandsOutput extends IndicatorInput { -} -; -export class BollingerBands extends Indicator { - constructor(input) { - super(input); - var period = input.period; - var priceArray = input.values; - var stdDev = input.stdDev; - var format = this.format; - var sma, sd; - this.result = []; - sma = new SMA({ period: period, values: [], format: (v) => { return v; } }); - sd = new SD({ period: period, values: [], format: (v) => { return v; } }); - this.generator = (function* () { - var result; - var tick; - var calcSMA; - var calcsd; - tick = yield; - while (true) { - calcSMA = sma.nextValue(tick); - calcsd = sd.nextValue(tick); - if (calcSMA) { - let middle = format(calcSMA); - let upper = format(calcSMA + (calcsd * stdDev)); - let lower = format(calcSMA - (calcsd * stdDev)); - let pb = format((tick - lower) / (upper - lower)); - result = { - middle: middle, - upper: upper, - lower: lower, - pb: pb - }; - } - tick = yield result; - } - })(); - this.generator.next(); - priceArray.forEach((tick) => { - var result = this.generator.next(tick); - if (result.value != undefined) { - this.result.push(result.value); - } - }); - } - nextValue(price) { - return this.generator.next(price).value; - } - ; -} -BollingerBands.calculate = bollingerbands; -export function bollingerbands(input) { - Indicator.reverseInputs(input); - var result = new BollingerBands(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/volatility/ChandelierExit.js b/lib/volatility/ChandelierExit.js deleted file mode 100644 index 665ba59..0000000 --- a/lib/volatility/ChandelierExit.js +++ /dev/null @@ -1,73 +0,0 @@ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -import { ATR } from '../directionalmovement/ATR'; -import LinkedList from '../Utils/FixedSizeLinkedList'; -export class ChandelierExitInput extends IndicatorInput { - constructor() { - super(...arguments); - this.period = 22; - this.multiplier = 3; - } -} -export class ChandelierExitOutput extends IndicatorInput { -} -; -export class ChandelierExit extends Indicator { - constructor(input) { - super(input); - var highs = input.high; - var lows = input.low; - var closes = input.close; - this.result = []; - var atrProducer = new ATR({ period: input.period, high: [], low: [], close: [], format: (v) => { return v; } }); - var dataCollector = new LinkedList(input.period * 2, true, true, false); - this.generator = (function* () { - var result; - var tick = yield; - var atr; - while (true) { - var { high, low } = tick; - dataCollector.push(high); - dataCollector.push(low); - atr = atrProducer.nextValue(tick); - if ((dataCollector.totalPushed >= (2 * input.period)) && atr != undefined) { - result = { - exitLong: dataCollector.periodHigh - atr * input.multiplier, - exitShort: dataCollector.periodLow + atr * input.multiplier - }; - } - tick = yield result; - } - })(); - this.generator.next(); - highs.forEach((tickHigh, index) => { - var tickInput = { - high: tickHigh, - low: lows[index], - close: closes[index], - }; - var result = this.generator.next(tickInput); - if (result.value != undefined) { - this.result.push(result.value); - } - }); - } - ; - nextValue(price) { - var result = this.generator.next(price); - if (result.value != undefined) { - return result.value; - } - } - ; -} -ChandelierExit.calculate = chandelierexit; -export function chandelierexit(input) { - Indicator.reverseInputs(input); - var result = new ChandelierExit(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/volatility/KeltnerChannels.js b/lib/volatility/KeltnerChannels.js deleted file mode 100644 index cf24e55..0000000 --- a/lib/volatility/KeltnerChannels.js +++ /dev/null @@ -1,76 +0,0 @@ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -import { SMA } from '../moving_averages/SMA'; -import { EMA } from '../moving_averages/EMA'; -import { ATR } from '../directionalmovement/ATR'; -export class KeltnerChannelsInput extends IndicatorInput { - constructor() { - super(...arguments); - this.maPeriod = 20; - this.atrPeriod = 10; - this.useSMA = false; - this.multiplier = 1; - } -} -export class KeltnerChannelsOutput extends IndicatorInput { -} -; -export class KeltnerChannels extends Indicator { - constructor(input) { - super(input); - var maType = input.useSMA ? SMA : EMA; - var maProducer = new maType({ period: input.maPeriod, values: [], format: (v) => { return v; } }); - var atrProducer = new ATR({ period: input.atrPeriod, high: [], low: [], close: [], format: (v) => { return v; } }); - var tick; - this.result = []; - this.generator = (function* () { - var KeltnerChannelsOutput; - var result; - tick = yield; - while (true) { - var { close } = tick; - var ma = maProducer.nextValue(close); - var atr = atrProducer.nextValue(tick); - if (ma != undefined && atr != undefined) { - result = { - middle: ma, - upper: ma + (input.multiplier * (atr)), - lower: ma - (input.multiplier * (atr)) - }; - } - tick = yield result; - } - })(); - this.generator.next(); - var highs = input.high; - highs.forEach((tickHigh, index) => { - var tickInput = { - high: tickHigh, - low: input.low[index], - close: input.close[index], - }; - var result = this.generator.next(tickInput); - if (result.value != undefined) { - this.result.push(result.value); - } - }); - } - ; - nextValue(price) { - var result = this.generator.next(price); - if (result.value != undefined) { - return result.value; - } - } - ; -} -KeltnerChannels.calculate = keltnerchannels; -export function keltnerchannels(input) { - Indicator.reverseInputs(input); - var result = new KeltnerChannels(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/volume/ADL.js b/lib/volume/ADL.js deleted file mode 100644 index c5fc04c..0000000 --- a/lib/volume/ADL.js +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Created by AAravindan on 5/17/16. - */ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -export class ADLInput extends IndicatorInput { -} -export class ADL extends Indicator { - constructor(input) { - super(input); - var highs = input.high; - var lows = input.low; - var closes = input.close; - var volumes = input.volume; - if (!((lows.length === highs.length) && (highs.length === closes.length) && (highs.length === volumes.length))) { - throw ('Inputs(low,high, close, volumes) not of equal size'); - } - this.result = []; - this.generator = (function* () { - var result = 0; - var tick; - tick = yield; - while (true) { - let moneyFlowMultiplier = ((tick.close - tick.low) - (tick.high - tick.close)) / (tick.high - tick.low); - moneyFlowMultiplier = isNaN(moneyFlowMultiplier) ? 1 : moneyFlowMultiplier; - let moneyFlowVolume = moneyFlowMultiplier * tick.volume; - result = result + moneyFlowVolume; - tick = yield Math.round(result); - } - })(); - this.generator.next(); - highs.forEach((tickHigh, index) => { - var tickInput = { - high: tickHigh, - low: lows[index], - close: closes[index], - volume: volumes[index] - }; - var result = this.generator.next(tickInput); - if (result.value != undefined) { - this.result.push(result.value); - } - }); - } - ; - nextValue(price) { - return this.generator.next(price).value; - } - ; -} -ADL.calculate = adl; -export function adl(input) { - Indicator.reverseInputs(input); - var result = new ADL(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/volume/ForceIndex.js b/lib/volume/ForceIndex.js deleted file mode 100644 index abac138..0000000 --- a/lib/volume/ForceIndex.js +++ /dev/null @@ -1,62 +0,0 @@ -import { EMA } from '../moving_averages/EMA'; -import { Indicator, IndicatorInput } from '../indicator/indicator'; -export class ForceIndexInput extends IndicatorInput { - constructor() { - super(...arguments); - this.period = 1; - } -} -; -export class ForceIndex extends Indicator { - constructor(input) { - super(input); - var closes = input.close; - var volumes = input.volume; - var period = input.period || 1; - if (!((volumes.length === closes.length))) { - throw ('Inputs(volume, close) not of equal size'); - } - let emaForceIndex = new EMA({ values: [], period: period }); - this.result = []; - this.generator = (function* () { - var previousTick = yield; - var tick = yield; - let forceIndex; - while (true) { - forceIndex = (tick.close - previousTick.close) * tick.volume; - previousTick = tick; - tick = yield emaForceIndex.nextValue(forceIndex); - } - })(); - this.generator.next(); - volumes.forEach((tick, index) => { - var result = this.generator.next({ - close: closes[index], - volume: volumes[index] - }); - if (result.value != undefined) { - this.result.push(result.value); - } - }); - } - ; - ; - nextValue(price) { - let result = this.generator.next(price).value; - if (result != undefined) { - return result; - } - } - ; -} -ForceIndex.calculate = forceindex; -export function forceindex(input) { - Indicator.reverseInputs(input); - var result = new ForceIndex(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/volume/MFI.js b/lib/volume/MFI.js deleted file mode 100644 index 0a0c87f..0000000 --- a/lib/volume/MFI.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Created by AAravindan on 5/17/16. - */ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -import { TypicalPrice } from '../chart_types/TypicalPrice'; -import FixedSizeLinkedList from '../Utils/FixedSizeLinkedList'; -export class MFIInput extends IndicatorInput { -} -export class MFI extends Indicator { - constructor(input) { - super(input); - var highs = input.high; - var lows = input.low; - var closes = input.close; - var volumes = input.volume; - var period = input.period; - var typicalPrice = new TypicalPrice({ low: [], high: [], close: [] }); - var positiveFlow = new FixedSizeLinkedList(period, false, false, true); - var negativeFlow = new FixedSizeLinkedList(period, false, false, true); - if (!((lows.length === highs.length) && (highs.length === closes.length) && (highs.length === volumes.length))) { - throw ('Inputs(low,high, close, volumes) not of equal size'); - } - this.result = []; - this.generator = (function* () { - var result; - var tick; - var lastClose; - var positiveFlowForPeriod; - var rawMoneyFlow = 0; - var moneyFlowRatio; - var negativeFlowForPeriod; - let typicalPriceValue = null; - let prevousTypicalPrice = null; - tick = yield; - lastClose = tick.close; //Fist value - tick = yield; - while (true) { - var { high, low, close, volume } = tick; - var positionMoney = 0; - var negativeMoney = 0; - typicalPriceValue = typicalPrice.nextValue({ high, low, close }); - rawMoneyFlow = typicalPriceValue * volume; - if ((typicalPriceValue != null) && (prevousTypicalPrice != null)) { - typicalPriceValue > prevousTypicalPrice ? positionMoney = rawMoneyFlow : negativeMoney = rawMoneyFlow; - positiveFlow.push(positionMoney); - negativeFlow.push(negativeMoney); - positiveFlowForPeriod = positiveFlow.periodSum; - negativeFlowForPeriod = negativeFlow.periodSum; - if ((positiveFlow.totalPushed >= period) && (positiveFlow.totalPushed >= period)) { - moneyFlowRatio = positiveFlowForPeriod / negativeFlowForPeriod; - result = 100 - 100 / (1 + moneyFlowRatio); - } - } - prevousTypicalPrice = typicalPriceValue; - tick = yield result; - } - })(); - this.generator.next(); - highs.forEach((tickHigh, index) => { - var tickInput = { - high: tickHigh, - low: lows[index], - close: closes[index], - volume: volumes[index] - }; - var result = this.generator.next(tickInput); - if (result.value != undefined) { - this.result.push(parseFloat(result.value.toFixed(2))); - } - }); - } - ; - nextValue(price) { - var result = this.generator.next(price); - if (result.value != undefined) { - return (parseFloat(result.value.toFixed(2))); - } - } - ; -} -MFI.calculate = mfi; -export function mfi(input) { - Indicator.reverseInputs(input); - var result = new MFI(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/volume/OBV.js b/lib/volume/OBV.js deleted file mode 100644 index ae3ceb2..0000000 --- a/lib/volume/OBV.js +++ /dev/null @@ -1,61 +0,0 @@ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -/** - * Created by AAravindan on 5/17/16. - */ -"use strict"; -export class OBVInput extends IndicatorInput { -} -export class OBV extends Indicator { - constructor(input) { - super(input); - var closes = input.close; - var volumes = input.volume; - this.result = []; - this.generator = (function* () { - var result = 0; - var tick; - var lastClose; - tick = yield; - if (tick.close && (typeof tick.close === 'number')) { - lastClose = tick.close; - tick = yield; - } - while (true) { - if (lastClose < tick.close) { - result = result + tick.volume; - } - else if (tick.close < lastClose) { - result = result - tick.volume; - } - lastClose = tick.close; - tick = yield result; - } - })(); - this.generator.next(); - closes.forEach((close, index) => { - let tickInput = { - close: closes[index], - volume: volumes[index] - }; - let result = this.generator.next(tickInput); - if (result.value != undefined) { - this.result.push(result.value); - } - }); - } - nextValue(price) { - return this.generator.next(price).value; - } - ; -} -OBV.calculate = obv; -export function obv(input) { - Indicator.reverseInputs(input); - var result = new OBV(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/volume/VWAP.js b/lib/volume/VWAP.js deleted file mode 100644 index 18a3c01..0000000 --- a/lib/volume/VWAP.js +++ /dev/null @@ -1,63 +0,0 @@ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -export class VWAPInput extends IndicatorInput { -} -; -export class VWAP extends Indicator { - constructor(input) { - super(input); - var lows = input.low; - var highs = input.high; - var closes = input.close; - var volumes = input.volume; - var format = this.format; - if (!((lows.length === highs.length) && (highs.length === closes.length))) { - throw ('Inputs(low,high, close) not of equal size'); - } - this.result = []; - this.generator = (function* () { - var tick = yield; - let cumulativeTotal = 0; - let cumulativeVolume = 0; - while (true) { - let typicalPrice = (tick.high + tick.low + tick.close) / 3; - let total = tick.volume * typicalPrice; - cumulativeTotal = cumulativeTotal + total; - cumulativeVolume = cumulativeVolume + tick.volume; - tick = yield cumulativeTotal / cumulativeVolume; - ; - } - })(); - this.generator.next(); - lows.forEach((tick, index) => { - var result = this.generator.next({ - high: highs[index], - low: lows[index], - close: closes[index], - volume: volumes[index] - }); - if (result.value != undefined) { - this.result.push(result.value); - } - }); - } - ; - ; - nextValue(price) { - let result = this.generator.next(price).value; - if (result != undefined) { - return result; - } - } - ; -} -VWAP.calculate = vwap; -export function vwap(input) { - Indicator.reverseInputs(input); - var result = new VWAP(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/lib/volume/VolumeProfile.js b/lib/volume/VolumeProfile.js deleted file mode 100644 index 0b90ce0..0000000 --- a/lib/volume/VolumeProfile.js +++ /dev/null @@ -1,70 +0,0 @@ -import { Indicator, IndicatorInput } from '../indicator/indicator'; -export class VolumeProfileInput extends IndicatorInput { -} -export class VolumeProfileOutput { -} -export function priceFallsBetweenBarRange(low, high, low1, high1) { - return (low <= low1 && high >= low1) || (low1 <= low && high1 >= low); -} -export class VolumeProfile extends Indicator { - constructor(input) { - super(input); - var highs = input.high; - var lows = input.low; - var closes = input.close; - var opens = input.open; - var volumes = input.volume; - var bars = input.noOfBars; - if (!((lows.length === highs.length) && (highs.length === closes.length) && (highs.length === volumes.length))) { - throw ('Inputs(low,high, close, volumes) not of equal size'); - } - this.result = []; - var max = Math.max(...highs, ...lows, ...closes, ...opens); - var min = Math.min(...highs, ...lows, ...closes, ...opens); - var barRange = (max - min) / bars; - var lastEnd = min; - for (let i = 0; i < bars; i++) { - let rangeStart = lastEnd; - let rangeEnd = rangeStart + barRange; - lastEnd = rangeEnd; - let bullishVolume = 0; - let bearishVolume = 0; - let totalVolume = 0; - for (let priceBar = 0; priceBar < highs.length; priceBar++) { - let priceBarStart = lows[priceBar]; - let priceBarEnd = highs[priceBar]; - let priceBarOpen = opens[priceBar]; - let priceBarClose = closes[priceBar]; - let priceBarVolume = volumes[priceBar]; - if (priceFallsBetweenBarRange(rangeStart, rangeEnd, priceBarStart, priceBarEnd)) { - totalVolume = totalVolume + priceBarVolume; - if (priceBarOpen > priceBarClose) { - bearishVolume = bearishVolume + priceBarVolume; - } - else { - bullishVolume = bullishVolume + priceBarVolume; - } - } - } - this.result.push({ - rangeStart, rangeEnd, bullishVolume, bearishVolume, totalVolume - }); - } - } - ; - nextValue(price) { - throw ('Next value not supported for volume profile'); - } - ; -} -VolumeProfile.calculate = volumeprofile; -export function volumeprofile(input) { - Indicator.reverseInputs(input); - var result = new VolumeProfile(input).result; - if (input.reversedInput) { - result.reverse(); - } - Indicator.reverseInputs(input); - return result; -} -; diff --git a/src/StockData.ts b/src/StockData.ts new file mode 100644 index 0000000..d063b59 --- /dev/null +++ b/src/StockData.ts @@ -0,0 +1,24 @@ +export default class StockData { + reversedInput?:boolean + constructor(public open:number[], public high:number[], public low:number[], public close:number[], reversedInput:boolean) { + this.reversedInput = reversedInput; + } +} + +export class CandleData { + open?:number; + high?:number; + low?:number; + close?:number + timestamp?:number + volume?:number +} + +export class CandleList { + open?:number[] = []; + high?:number[] = []; + low?:number[] = []; + close?:number[] = [] + volume?: number[] = [] + timestamp? : number[] = [] +} \ No newline at end of file diff --git a/src/Utils/AverageGain.ts b/src/Utils/AverageGain.ts new file mode 100644 index 0000000..4701a78 --- /dev/null +++ b/src/Utils/AverageGain.ts @@ -0,0 +1,72 @@ +// @ts-nocheck +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; + +export class AvgGainInput extends IndicatorInput { + period:number + values:number[] +} + +export class AverageGain extends Indicator { + generator:IterableIterator; + constructor(input:AvgGainInput) { + super(input); + let values = input.values; + let period = input.period; + let format = this.format; + + this.generator = (function* (period){ + var currentValue = yield; + var counter = 1; + var gainSum = 0; + var avgGain; + var gain; + var lastValue = currentValue; + currentValue = yield + while(true){ + gain = currentValue - lastValue; + gain = gain > 0 ? gain : 0; + if(gain > 0){ + gainSum = gainSum + gain; + } + if(counter < period){ + counter++; + } + else if(avgGain === undefined){ + avgGain = gainSum / period; + }else { + avgGain = ((avgGain * (period-1)) + gain)/period; + } + lastValue = currentValue; + avgGain = (avgGain!==undefined) ? format(avgGain) : undefined; + currentValue = yield avgGain; + } + })(period); + + this.generator.next(); + + this.result = []; + + values.forEach((tick:number) => { + var result = this.generator.next(tick); + if(result.value !== undefined){ + this.result.push(result.value); + } + }); + } + + static calculate = averagegain; + + nextValue(price:number):number | undefined { + return this.generator.next(price).value; + }; +} + +export function averagegain(input:AvgGainInput):number[] { + Indicator.reverseInputs(input); + var result = new AverageGain(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; \ No newline at end of file diff --git a/src/Utils/AverageLoss.ts b/src/Utils/AverageLoss.ts new file mode 100644 index 0000000..2b6373b --- /dev/null +++ b/src/Utils/AverageLoss.ts @@ -0,0 +1,71 @@ +// @ts-nocheck +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +export class AvgLossInput extends IndicatorInput { + values:number[] + period:number +} + +export class AverageLoss extends Indicator { + generator:IterableIterator; + constructor(input:AvgLossInput) { + super(input); + let values = input.values; + let period = input.period; + let format = this.format; + + this.generator = (function* (period){ + var currentValue = yield; + var counter = 1; + var lossSum = 0; + var avgLoss; + var loss; + var lastValue = currentValue; + currentValue = yield + while(true){ + loss = lastValue - currentValue; + loss = loss > 0 ? loss : 0; + if(loss > 0){ + lossSum = lossSum + loss; + } + if(counter < period){ + counter++; + } + else if(avgLoss === undefined){ + avgLoss = lossSum / period; + }else { + avgLoss = ((avgLoss * (period-1)) + loss)/period; + } + lastValue = currentValue; + avgLoss = (avgLoss!==undefined) ? format(avgLoss) : undefined; + currentValue = yield avgLoss; + } + })(period); + + this.generator.next(); + + this.result = []; + + values.forEach((tick:number) => { + var result = this.generator.next(tick); + if(result.value !== undefined){ + this.result.push(result.value); + } + }); + } + + static calculate = averageloss; + + nextValue(price:number):number | undefined { + return this.generator.next(price).value; + }; +} + +export function averageloss(input:AvgLossInput):number[] { + Indicator.reverseInputs(input); + var result = new AverageLoss(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; diff --git a/src/Utils/CrossDown.ts b/src/Utils/CrossDown.ts new file mode 100644 index 0000000..e91d0fc --- /dev/null +++ b/src/Utils/CrossDown.ts @@ -0,0 +1,110 @@ +import { Indicator, IndicatorInput } from "../indicator/indicator.ts"; + +export class CrossInput extends IndicatorInput { + constructor(public lineA: number[], public lineB: number[]) { + super(); + } +} + +export class CrossDown extends Indicator { + lineA: number[]; + lineB: number[]; + result: boolean[]; + generator: IterableIterator; + + constructor(input: CrossInput) { + super(input); + + this.lineA = input.lineA; + this.lineB = input.lineB; + // @ts-ignore + var currentLineA = []; + // @ts-ignore + var currentLineB = []; + + const genFn = function* (): IterableIterator { + // @ts-ignore + var current = yield; + var result = false; + + while (true) { + // @ts-ignore + currentLineA.unshift(current.valueA); + // @ts-ignore + currentLineB.unshift(current.valueB); + // @ts-ignore + result = current.valueA < current.valueB; + + var pointer = 1; + while ( + result === true && + // @ts-ignore + currentLineA[pointer] <= currentLineB[pointer] + ) { + // @ts-ignore + if (currentLineA[pointer] < currentLineB[pointer]) { + result = false; + // @ts-ignore + } else if (currentLineA[pointer] > currentLineB[pointer]) { + result = true; + // @ts-ignore + } else if (currentLineA[pointer] === currentLineB[pointer]) { + pointer += 1; + } + } + + if (result === true) { + // @ts-ignore + currentLineA = [current.valueA]; + // @ts-ignore + currentLineB = [current.valueB]; + } + + current = yield result; + } + }; + + this.generator = genFn(); + this.generator.next(); + + this.result = []; + this.lineA.forEach((value, index) => { + // @ts-ignore + var result = this.generator.next({ + valueA: this.lineA[index], + valueB: this.lineB[index] + }); + + if (result.value !== undefined) { + this.result.push(result.value); + } + }); + } + + static calculate = crossDown; + + static reverseInputs(input: CrossInput): void { + if (input.reversedInput) { + input.lineA ? input.lineA.reverse() : undefined; + input.lineB ? input.lineB.reverse() : undefined; + } + } + + nextValue(valueA: number, valueB: number): true | false { + // @ts-ignore + return this.generator.next({ + valueA: valueA, + valueB: valueB + }).value; + } +} + +export function crossDown(input: CrossInput): boolean[] { + Indicator.reverseInputs(input); + var result = new CrossDown(input).result; + if (input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; +} diff --git a/src/Utils/CrossOver.ts b/src/Utils/CrossOver.ts new file mode 100644 index 0000000..0b04784 --- /dev/null +++ b/src/Utils/CrossOver.ts @@ -0,0 +1,79 @@ +import { Indicator, IndicatorInput } from "../indicator/indicator.ts"; +import { CrossUp } from "./CrossUp.ts"; +import { CrossDown } from "./CrossDown.ts"; + +export class CrossInput extends IndicatorInput { + constructor(public lineA: number[], public lineB: number[]) { + super(); + } +} + +export class CrossOver extends Indicator { + generator: IterableIterator; + result: boolean[]; + + constructor(input: CrossInput) { + super(input); + + var crossUp = new CrossUp({ lineA: input.lineA, lineB: input.lineB }); + var crossDown = new CrossDown({ lineA: input.lineA, lineB: input.lineB }); + + const genFn = function* (): IterableIterator { + // @ts-ignore + var current = yield; + var result = false; + var first = true; + while (true) { + // @ts-ignore + var nextUp = crossUp.nextValue(current.valueA, current.valueB); + // @ts-ignore + var nextDown = crossDown.nextValue(current.valueA, current.valueB); + + result = nextUp || nextDown; + + if (first) result = false; + first = false; + current = yield result; + } + }; + + this.generator = genFn(); + this.generator.next(); + + var resultA = crossUp.getResult(); + // @ts-ignore + var resultB = crossDown.getResult(); + // @ts-ignore + this.result = resultA.map((a, index) => { + if (index === 0) return false; + return !!(a || resultB[index]); + }); + } + + static calculate = crossOver; + + static reverseInputs(input: CrossInput): void { + if (input.reversedInput) { + input.lineA ? input.lineA.reverse() : undefined; + input.lineB ? input.lineB.reverse() : undefined; + } + } + + nextValue(valueA: number, valueB: number): true | false { + // @ts-ignore + return this.generator.next({ + valueA: valueA, + valueB: valueB + }).value; + } +} + +export function crossOver(input: CrossInput): boolean[] { + Indicator.reverseInputs(input); + var result = new CrossOver(input).result; + if (input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; +} diff --git a/src/Utils/CrossUp.ts b/src/Utils/CrossUp.ts new file mode 100644 index 0000000..4633a5a --- /dev/null +++ b/src/Utils/CrossUp.ts @@ -0,0 +1,111 @@ +import { Indicator, IndicatorInput } from "../indicator/indicator.ts"; + +export class CrossInput extends IndicatorInput { + constructor(public lineA: number[], public lineB: number[]) { + super(); + } +} + +export class CrossUp extends Indicator { + lineA: number[]; + lineB: number[]; + result: boolean[]; + generator: IterableIterator; + + constructor(input: CrossInput) { + super(input); + + this.lineA = input.lineA; + this.lineB = input.lineB; + // @ts-ignore + var currentLineA = []; + // @ts-ignore + var currentLineB = []; + + const genFn = function* (): IterableIterator { + // @ts-ignore + var current = yield; + var result = false; + + while (true) { + // @ts-ignore + currentLineA.unshift(current.valueA); + // @ts-ignore + currentLineB.unshift(current.valueB); + // @ts-ignore + result = current.valueA > current.valueB; + + var pointer = 1; + // @ts-ignore + while ( + result === true && + // @ts-ignore + currentLineA[pointer] >= currentLineB[pointer] + ) { + // @ts-ignore + if (currentLineA[pointer] > currentLineB[pointer]) { + result = false; + // @ts-ignore + } else if (currentLineA[pointer] < currentLineB[pointer]) { + result = true; + // @ts-ignore + } else if (currentLineA[pointer] === currentLineB[pointer]) { + pointer += 1; + } + } + + if (result === true) { + // @ts-ignore + currentLineA = [current.valueA]; + // @ts-ignore + currentLineB = [current.valueB]; + } + + current = yield result; + } + }; + + this.generator = genFn(); + this.generator.next(); + + this.result = []; + this.lineA.forEach((value, index) => { + // @ts-ignore + var result = this.generator.next({ + valueA: this.lineA[index], + valueB: this.lineB[index] + }); + + if (result.value !== undefined) { + this.result.push(result.value); + } + }); + } + + static calculate = crossUp; + + static reverseInputs(input: CrossInput): void { + if (input.reversedInput) { + input.lineA ? input.lineA.reverse() : undefined; + input.lineB ? input.lineB.reverse() : undefined; + } + } + + nextValue(valueA: number, valueB: number): true | false { + // @ts-ignore + return this.generator.next({ + valueA: valueA, + valueB: valueB + }).value; + } +} + +export function crossUp(input: CrossInput): boolean[] { + Indicator.reverseInputs(input); + var result = new CrossUp(input).result; + if (input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; +} diff --git a/src/Utils/FixedSizeLinkedList.ts b/src/Utils/FixedSizeLinkedList.ts new file mode 100644 index 0000000..ba6352b --- /dev/null +++ b/src/Utils/FixedSizeLinkedList.ts @@ -0,0 +1,80 @@ +/** + * Created by AAravindan on 5/7/16. + */ +import { LinkedList } from "./LinkedList.ts"; + +export default class FixedSizeLinkedList extends LinkedList { + public totalPushed: number = 0; + public periodHigh: number = 0; + public periodLow: number = Infinity; + public periodSum: number = 0; + // @ts-ignore + public lastShift: number; + public _push: (data: number) => void; + constructor( + public size: number, + public maintainHigh?: boolean, + public maintainLow?: boolean, + public maintainSum?: boolean + ) { + super(); + if (!size || typeof size !== "number") { + throw "Size required and should be a number."; + } + this._push = this.push; + this.push = function (data) { + this.add(data); + this.totalPushed++; + }; + } + + add(data: number) { + if (this.length === this.size) { + this.lastShift = this.shift(); + this._push(data); + //TODO: FInd a better way + if (this.maintainHigh) + if (this.lastShift == this.periodHigh) this.calculatePeriodHigh(); + if (this.maintainLow) + if (this.lastShift == this.periodLow) this.calculatePeriodLow(); + if (this.maintainSum) { + this.periodSum = this.periodSum - this.lastShift; + } + } else { + this._push(data); + } + //TODO: FInd a better way + if (this.maintainHigh) if (this.periodHigh <= data) this.periodHigh = data; + if (this.maintainLow) if (this.periodLow >= data) this.periodLow = data; + if (this.maintainSum) { + this.periodSum = this.periodSum + data; + } + } + + *iterator() { + this.resetCursor(); + while (this.next()) { + yield this.current; + } + } + + calculatePeriodHigh() { + this.resetCursor(); + if (this.next()) this.periodHigh = this.current; + while (this.next()) { + if (this.periodHigh <= this.current) { + this.periodHigh = this.current; + } + } + } + + calculatePeriodLow() { + this.resetCursor(); + if (this.next()) this.periodLow = this.current; + while (this.next()) { + if (this.periodLow >= this.current) { + this.periodLow = this.current; + } + } + } +} diff --git a/src/Utils/Highest.ts b/src/Utils/Highest.ts new file mode 100644 index 0000000..f9cd2f2 --- /dev/null +++ b/src/Utils/Highest.ts @@ -0,0 +1,65 @@ + +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import FixedSizedLinkedList from './FixedSizeLinkedList.ts'; +import { CandleData } from '../StockData.ts'; + +export class HighestInput extends IndicatorInput { + values :number[] + period :number +} + +export class Highest extends Indicator { + generator:IterableIterator; + constructor (input:HighestInput) { + super(input); + var values = input.values; + var period = input.period; + + this.result = []; + + var periodList = new FixedSizedLinkedList(period, true, false, false); + + this.generator = (function* (){ + var result; + var tick; + var high; + tick = yield; + while (true) + { + periodList.push(tick); + if(periodList.totalPushed >= period) { + high = periodList.periodHigh; + } + tick = yield high + } + })(); + + this.generator.next(); + + values.forEach((value, index) => { + var result = this.generator.next(value); + if(result.value != undefined) { + this.result.push(result.value); + } + }); + }; + + static calculate = highest; + + nextValue(price:number):number | undefined { + var result = this.generator.next(price); + if(result.value != undefined){ + return result.value; + } + }; +} + +export function highest(input:HighestInput):number[] { + Indicator.reverseInputs(input); + var result = new Highest(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; diff --git a/src/Utils/LinkedList.ts b/src/Utils/LinkedList.ts new file mode 100644 index 0000000..000f525 --- /dev/null +++ b/src/Utils/LinkedList.ts @@ -0,0 +1,161 @@ +class Item { + next:any; + prev:any; + data:any; + constructor (data:any, prev?:any, next?:any) { + this.next = next + if (next) next.prev = this + this.prev = prev + if (prev) prev.next = this + this.data = data + } +} + +export class LinkedList { + private _head:any + private _tail:any + private _next:any + private _length:any=0; + private _current:any + + constructor() { + + } + + get head() { + return this._head && this._head.data + } + + get tail() { + return this._tail && this._tail.data + } + + get current() { + return this._current && this._current.data + } + + get length() { + return this._length + } + + public push (data:any) { + this._tail = new Item(data, this._tail) + if (this._length === 0) { + this._head = this._tail + this._current = this._head + this._next = this._head + } + this._length++ + } + + public pop () { + var tail = this._tail + if (this._length === 0) { + return + } + this._length-- + if (this._length === 0) { + this._head = this._tail = this._current = this._next = undefined + return tail.data + } + this._tail = tail.prev + this._tail.next = undefined + if (this._current === tail) { + this._current = this._tail + this._next = undefined + } + return tail.data + } + + public shift() { + var head = this._head + if (this._length === 0) { + return + } + this._length-- + if (this._length === 0) { + this._head = this._tail = this._current = this._next = undefined + return head.data + } + this._head = this._head.next + if (this._current === head) { + this._current = this._head + this._next = this._current.next + } + return head.data + } + + public unshift(data:any) { + this._head = new Item(data, undefined, this._head) + if (this._length === 0) { + this._tail = this._head + this._next = this._head + } + this._length++ + } + + public unshiftCurrent() { + var current = this._current + if (current === this._head || this._length < 2) { + return current && current.data + } + // remove + if (current === this._tail) { + this._tail = current.prev + this._tail.next = undefined + this._current = this._tail + } else { + current.next.prev = current.prev + current.prev.next = current.next + this._current = current.prev + } + this._next = this._current.next + // unshift + current.next = this._head + current.prev = undefined + this._head.prev = current + this._head = current + return current.data + } + + public removeCurrent() { + var current = this._current + if (this._length === 0) { + return + } + this._length-- + if (this._length === 0) { + this._head = this._tail = this._current = this._next = undefined + return current.data + } + if (current === this._tail) { + this._tail = current.prev + this._tail.next = undefined + this._current = this._tail + } else if (current === this._head) { + this._head = current.next + this._head.prev = undefined + this._current = this._head + } else { + current.next.prev = current.prev + current.prev.next = current.next + this._current = current.prev + } + this._next = this._current.next + return current.data + } + + public resetCursor() { + this._current = this._next = this._head + return this + } + + public next() { + var next = this._next + if (next !== undefined) { + this._next = next.next + this._current = next + return next.data + } + } +} \ No newline at end of file diff --git a/src/Utils/Lowest.ts b/src/Utils/Lowest.ts new file mode 100644 index 0000000..93e9bc6 --- /dev/null +++ b/src/Utils/Lowest.ts @@ -0,0 +1,65 @@ + +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import FixedSizedLinkedList from './FixedSizeLinkedList.ts'; +import { CandleData } from '../StockData.ts'; + +export class LowestInput extends IndicatorInput { + values :number[] + period :number +} + +export class Lowest extends Indicator { + generator:IterableIterator; + constructor (input:LowestInput) { + super(input); + var values = input.values; + var period = input.period; + + this.result = []; + + var periodList = new FixedSizedLinkedList(period, false, true, false); + + this.generator = (function* (){ + var result; + var tick; + var high; + tick = yield; + while (true) + { + periodList.push(tick); + if(periodList.totalPushed >= period) { + high = periodList.periodLow; + } + tick = yield high + } + })(); + + this.generator.next(); + + values.forEach((value, index) => { + var result = this.generator.next(value); + if(result.value != undefined) { + this.result.push(result.value); + } + }); + }; + + static calculate = lowest; + + nextValue(price:number):number | undefined { + var result = this.generator.next(price); + if(result.value != undefined){ + return result.value; + } + }; +} + +export function lowest(input:LowestInput):number[] { + Indicator.reverseInputs(input); + var result = new Lowest(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; diff --git a/src/Utils/NumberFormatter.ts b/src/Utils/NumberFormatter.ts new file mode 100644 index 0000000..db40e68 --- /dev/null +++ b/src/Utils/NumberFormatter.ts @@ -0,0 +1,9 @@ +import { getConfig } from '../config.ts'; +export function format(v:number):number { + // @ts-ignore + let precision:number = getConfig('precision'); + if(precision) { + return parseFloat(v.toPrecision(precision)); + } + return v; +} \ No newline at end of file diff --git a/src/Utils/SD.ts b/src/Utils/SD.ts new file mode 100644 index 0000000..c721f1e --- /dev/null +++ b/src/Utils/SD.ts @@ -0,0 +1,83 @@ +import { IndicatorInput, Indicator } from "../indicator/indicator.ts"; +import { SMA } from "../moving_averages/SMA.ts"; +import LinkedList from "../Utils/FixedSizeLinkedList.ts"; +/** + * Created by AAravindan on 5/7/16. + */ +("use strict"); + +export class SDInput extends IndicatorInput { + // @ts-ignore + period: number; + // @ts-ignore + values: number[]; +} + +export class SD extends Indicator { + generator: IterableIterator; + constructor(input: SDInput) { + super(input); + var period = input.period; + var priceArray = input.values; + + var sma = new SMA({ + period: period, + values: [], + format: (v: number) => { + return v; + } + }); + + this.result = []; + + this.generator = (function* () { + var tick; + var mean; + var currentSet = new LinkedList(period); + // @ts-ignore + tick = yield; + var sd; + while (true) { + currentSet.push(tick); + mean = sma.nextValue(tick); + if (mean) { + let sum = 0; + for (let x of currentSet.iterator()) { + sum = sum + Math.pow(x - mean, 2); + } + sd = Math.sqrt(sum / period); + } + // @ts-ignore + tick = yield sd; + } + })(); + + this.generator.next(); + + priceArray.forEach(tick => { + // @ts-ignore + var result = this.generator.next(tick); + if (result.value != undefined) { + this.result.push(this.format(result.value)); + } + }); + } + + static calculate = sd; + + nextValue(price: number): number | undefined { + // @ts-ignore + var nextResult = this.generator.next(price); + if (nextResult.value != undefined) return this.format(nextResult.value); + } +} + +export function sd(input: SDInput): number[] { + Indicator.reverseInputs(input); + var result = new SD(input).result; + if (input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; +} diff --git a/src/Utils/Sum.ts b/src/Utils/Sum.ts new file mode 100644 index 0000000..32f9253 --- /dev/null +++ b/src/Utils/Sum.ts @@ -0,0 +1,65 @@ + +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import FixedSizedLinkedList from './FixedSizeLinkedList.ts'; +import { CandleData } from '../StockData.ts'; + +export class SumInput extends IndicatorInput { + values :number[] + period :number +} + +export class Sum extends Indicator { + generator:IterableIterator; + constructor (input:SumInput) { + super(input); + var values = input.values; + var period = input.period; + + this.result = []; + + var periodList = new FixedSizedLinkedList(period, false, false, true); + + this.generator = (function* (){ + var result; + var tick; + var high; + tick = yield; + while (true) + { + periodList.push(tick); + if(periodList.totalPushed >= period) { + high = periodList.periodSum; + } + tick = yield high + } + })(); + + this.generator.next(); + + values.forEach((value, index) => { + var result = this.generator.next(value); + if(result.value != undefined) { + this.result.push(result.value); + } + }); + }; + + static calculate = sum; + + nextValue(price:number):number | undefined { + var result = this.generator.next(price); + if(result.value != undefined){ + return result.value; + } + }; +} + +export function sum(input:SumInput):number[] { + Indicator.reverseInputs(input); + var result = new Sum(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; diff --git a/src/candlestick/AbandonedBaby.ts b/src/candlestick/AbandonedBaby.ts new file mode 100644 index 0000000..337b3d4 --- /dev/null +++ b/src/candlestick/AbandonedBaby.ts @@ -0,0 +1,43 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; +import Doji from './Doji.ts'; + + +export default class AbandonedBaby extends CandlestickFinder { + constructor() { + super(); + this.name = 'AbandonedBaby'; + this.requiredCount = 3; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + let thirddaysOpen = data.open[2]; + let thirddaysClose = data.close[2]; + let thirddaysHigh = data.high[2]; + let thirddaysLow = data.low[2]; + + let isFirstBearish = firstdaysClose seconddaysHigh) && + (thirddaysClose > thirddaysOpen)); + let isThirdBullish = (thirddaysHigh< firstdaysOpen); + return (isFirstBearish && dojiExists && gapExists && isThirdBullish ); + } +} + +export function abandonedbaby(data:StockData) { + return new AbandonedBaby().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/Bearish.ts b/src/candlestick/Bearish.ts new file mode 100644 index 0000000..00a4689 --- /dev/null +++ b/src/candlestick/Bearish.ts @@ -0,0 +1,50 @@ +import BearishEngulfingPattern from './BearishEngulfingPattern.ts'; +import BearishHarami from './BearishHarami.ts'; +import BearishHaramiCross from './BearishHaramiCross.ts'; +import EveningDojiStar from './EveningDojiStar.ts'; +import EveningStar from './EveningStar.ts'; +import BearishMarubozu from './BearishMarubozu.ts'; +import ThreeBlackCrows from './ThreeBlackCrows.ts'; +import BearishHammerStick from './BearishHammerStick.ts'; +import BearishInvertedHammerStick from './BearishInvertedHammerStick.ts'; +import HangingMan from './HangingMan.ts'; +import HangingManUnconfirmed from './HangingManUnconfirmed.ts'; +import ShootingStar from './ShootingStar.ts'; +import ShootingStarUnconfirmed from './ShootingStarUnconfirmed.ts'; +import TweezerTop from './TweezerTop.ts'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +let bearishPatterns = [ + new BearishEngulfingPattern(), + new BearishHarami(), + new BearishHaramiCross(), + new EveningDojiStar(), + new EveningStar(), + new BearishMarubozu(), + new ThreeBlackCrows(), + new BearishHammerStick(), + new BearishInvertedHammerStick(), + new HangingMan(), + new HangingManUnconfirmed(), + new ShootingStar(), + new ShootingStarUnconfirmed(), + new TweezerTop() +]; + +export default class BearishPatterns extends CandlestickFinder { + constructor() { + super(); + this.name = 'Bearish Candlesticks'; + } + + hasPattern (data:StockData) { + return bearishPatterns.reduce(function(state, pattern) { + return state || pattern.hasPattern(data); + }, false) + } +} + +export function bearish(data:StockData){ + return new BearishPatterns().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/BearishEngulfingPattern.ts b/src/candlestick/BearishEngulfingPattern.ts new file mode 100644 index 0000000..ee4bca5 --- /dev/null +++ b/src/candlestick/BearishEngulfingPattern.ts @@ -0,0 +1,31 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class BearishEngulfingPattern extends CandlestickFinder { + constructor() { + super(); + this.name = 'BearishEngulfingPattern'; + this.requiredCount = 2; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + + let isBearishEngulfing = ((firstdaysClose > firstdaysOpen) && + (firstdaysOpen < seconddaysOpen) && + (firstdaysClose < seconddaysOpen)&& + (firstdaysOpen > seconddaysClose)); + + return (isBearishEngulfing); + } +} + +export function bearishengulfingpattern(data:StockData) { + return new BearishEngulfingPattern().hasPattern(data); +} \ No newline at end of file diff --git a/lib/candlestick/BearishHammerStick.js b/src/candlestick/BearishHammerStick.ts similarity index 55% rename from lib/candlestick/BearishHammerStick.js rename to src/candlestick/BearishHammerStick.ts index 7c432cd..5034622 100644 --- a/lib/candlestick/BearishHammerStick.js +++ b/src/candlestick/BearishHammerStick.ts @@ -1,21 +1,26 @@ -import CandlestickFinder from './CandlestickFinder'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + export default class BearishHammerStick extends CandlestickFinder { constructor() { super(); this.name = 'BearishHammerStick'; - this.requiredCount = 1; + this.requiredCount = 1; } - logic(data) { - let daysOpen = data.open[0]; + logic (data:StockData) { + let daysOpen = data.open[0]; let daysClose = data.close[0]; - let daysHigh = data.high[0]; - let daysLow = data.low[0]; + let daysHigh = data.high[0]; + let daysLow = data.low[0]; + let isBearishHammer = daysOpen > daysClose; isBearishHammer = isBearishHammer && this.approximateEqual(daysOpen, daysHigh); isBearishHammer = isBearishHammer && (daysOpen - daysClose) <= 2 * (daysClose - daysLow); + return isBearishHammer; } } -export function bearishhammerstick(data) { - return new BearishHammerStick().hasPattern(data); -} + +export function bearishhammerstick(data:StockData) { + return new BearishHammerStick().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/BearishHarami.ts b/src/candlestick/BearishHarami.ts new file mode 100644 index 0000000..43f028d --- /dev/null +++ b/src/candlestick/BearishHarami.ts @@ -0,0 +1,32 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; +export default class BearishHarami extends CandlestickFinder { + constructor() { + super(); + this.requiredCount = 2; + this.name = 'BearishHarami'; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + + let isBearishHaramiPattern = ((firstdaysOpen < seconddaysOpen) && + (firstdaysClose > seconddaysOpen)&& + (firstdaysClose > seconddaysClose)&& + (firstdaysOpen < seconddaysLow)&& + (firstdaysHigh > seconddaysHigh)); + + return (isBearishHaramiPattern); + + } +} + +export function bearishharami(data:StockData) { + return new BearishHarami().hasPattern(data); +} diff --git a/src/candlestick/BearishHaramiCross.ts b/src/candlestick/BearishHaramiCross.ts new file mode 100644 index 0000000..4a669cf --- /dev/null +++ b/src/candlestick/BearishHaramiCross.ts @@ -0,0 +1,35 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class BearishHaramiCross extends CandlestickFinder { + constructor() { + super(); + this.requiredCount = 2; + this.name = 'BearishHaramiCross'; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + + let isBearishHaramiCrossPattern = ((firstdaysOpen < seconddaysOpen) && + (firstdaysClose > seconddaysOpen)&& + (firstdaysClose > seconddaysClose)&& + (firstdaysOpen < seconddaysLow)&& + (firstdaysHigh > seconddaysHigh)); + + let isSecondDayDoji = this.approximateEqual(seconddaysOpen, seconddaysClose); + + return (isBearishHaramiCrossPattern && isSecondDayDoji); + + } +} + +export function bearishharamicross(data:StockData) { + return new BearishHaramiCross().hasPattern(data); +} diff --git a/lib/candlestick/BearishInvertedHammerStick.js b/src/candlestick/BearishInvertedHammerStick.ts similarity index 57% rename from lib/candlestick/BearishInvertedHammerStick.js rename to src/candlestick/BearishInvertedHammerStick.ts index 5791c2c..24688b8 100644 --- a/lib/candlestick/BearishInvertedHammerStick.js +++ b/src/candlestick/BearishInvertedHammerStick.ts @@ -1,21 +1,26 @@ -import CandlestickFinder from './CandlestickFinder'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + export default class BearishInvertedHammerStick extends CandlestickFinder { constructor() { super(); this.name = 'BearishInvertedHammerStick'; - this.requiredCount = 1; + this.requiredCount = 1; } - logic(data) { - let daysOpen = data.open[0]; + logic (data:StockData) { + let daysOpen = data.open[0]; let daysClose = data.close[0]; - let daysHigh = data.high[0]; - let daysLow = data.low[0]; + let daysHigh = data.high[0]; + let daysLow = data.low[0]; + let isBearishInvertedHammer = daysOpen > daysClose; isBearishInvertedHammer = isBearishInvertedHammer && this.approximateEqual(daysClose, daysLow); isBearishInvertedHammer = isBearishInvertedHammer && (daysOpen - daysClose) <= 2 * (daysHigh - daysOpen); + return isBearishInvertedHammer; } } -export function bearishinvertedhammerstick(data) { - return new BearishInvertedHammerStick().hasPattern(data); -} + +export function bearishinvertedhammerstick(data:StockData) { + return new BearishInvertedHammerStick().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/BearishMarubozu.ts b/src/candlestick/BearishMarubozu.ts new file mode 100644 index 0000000..ecc89d4 --- /dev/null +++ b/src/candlestick/BearishMarubozu.ts @@ -0,0 +1,28 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class BearishMarubozu extends CandlestickFinder { + constructor() { + super(); + this.name = 'BearishMarubozu'; + this.requiredCount = 1; + } + logic (data:StockData) { + let daysOpen = data.open[0]; + let daysClose = data.close[0]; + let daysHigh = data.high[0]; + let daysLow = data.low[0]; + + let isBearishMarbozu = this.approximateEqual(daysOpen, daysHigh) && + this.approximateEqual(daysLow, daysClose) && + daysOpen > daysClose && + daysOpen > daysLow; + + return (isBearishMarbozu); + + } +} + +export function bearishmarubozu(data:StockData) { + return new BearishMarubozu().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/BearishSpinningTop.ts b/src/candlestick/BearishSpinningTop.ts new file mode 100644 index 0000000..9148efe --- /dev/null +++ b/src/candlestick/BearishSpinningTop.ts @@ -0,0 +1,28 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class BearishSpinningTop extends CandlestickFinder { + constructor() { + super(); + this.name = 'BearishSpinningTop'; + this.requiredCount = 1; + } + logic (data:StockData) { + let daysOpen = data.open[0]; + let daysClose = data.close[0]; + let daysHigh = data.high[0]; + let daysLow = data.low[0]; + + let bodyLength = Math.abs(daysClose-daysOpen); + let upperShadowLength = Math.abs(daysHigh-daysOpen); + let lowerShadowLength = Math.abs(daysHigh-daysLow); + let isBearishSpinningTop = bodyLength < upperShadowLength && + bodyLength < lowerShadowLength; + + return isBearishSpinningTop; + } +} + +export function bearishspinningtop(data:StockData) { + return new BearishSpinningTop().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/Bullish.ts b/src/candlestick/Bullish.ts new file mode 100644 index 0000000..84b4d18 --- /dev/null +++ b/src/candlestick/Bullish.ts @@ -0,0 +1,51 @@ +import MorningStar from './MorningStar.ts'; +import BullishEngulfingPattern from './BullishEngulfingPattern.ts'; +import BullishHarami from './BullishHarami.ts'; +import BullishHaramiCross from './BullishHaramiCross.ts'; +import MorningDojiStar from './MorningDojiStar.ts'; +import DownsideTasukiGap from './DownsideTasukiGap.ts'; +import BullishMarubozu from './BullishMarubozu.ts'; +import PiercingLine from './PiercingLine.ts'; +import ThreeWhiteSoldiers from './ThreeWhiteSoldiers.ts'; +import BullishHammerStick from './BullishHammerStick.ts'; +import BullishInvertedHammerStick from './BullishInvertedHammerStick.ts'; +import HammerPattern from './HammerPattern.ts'; +import HammerPatternUnconfirmed from './HammerPatternUnconfirmed.ts'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; +import TweezerBottom from './TweezerBottom.ts'; + +let bullishPatterns = [ + new BullishEngulfingPattern(), + new DownsideTasukiGap(), + new BullishHarami(), + new BullishHaramiCross(), + new MorningDojiStar(), + new MorningStar(), + new BullishMarubozu(), + new PiercingLine(), + new ThreeWhiteSoldiers(), + new BullishHammerStick(), + new BullishInvertedHammerStick(), + new HammerPattern(), + new HammerPatternUnconfirmed(), + new TweezerBottom() +]; + +export default class BullishPatterns extends CandlestickFinder { + constructor() { + super(); + this.name = 'Bullish Candlesticks'; + } + + hasPattern (data:StockData) { + return bullishPatterns.reduce(function(state, pattern) { + let result = pattern.hasPattern(data); + return state || result; + }, false) + } +} + +export function bullish(data:StockData) { + return new BullishPatterns().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/BullishEngulfingPattern.ts b/src/candlestick/BullishEngulfingPattern.ts new file mode 100644 index 0000000..9be0e62 --- /dev/null +++ b/src/candlestick/BullishEngulfingPattern.ts @@ -0,0 +1,32 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class BullishEngulfingPattern extends CandlestickFinder { + constructor() { + super(); + this.name = 'BullishEngulfingPattern'; + this.requiredCount = 2; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + + let isBullishEngulfing = ((firstdaysClose < firstdaysOpen) && + (firstdaysOpen > seconddaysOpen) && + (firstdaysClose > seconddaysOpen) && + (firstdaysOpen < seconddaysClose)); + + return (isBullishEngulfing ); + + } +} + +export function bullishengulfingpattern(data:StockData) { + return new BullishEngulfingPattern().hasPattern(data); +} \ No newline at end of file diff --git a/lib/candlestick/BullishHammerStick.js b/src/candlestick/BullishHammerStick.ts similarity index 55% rename from lib/candlestick/BullishHammerStick.js rename to src/candlestick/BullishHammerStick.ts index d13b983..03a90ba 100644 --- a/lib/candlestick/BullishHammerStick.js +++ b/src/candlestick/BullishHammerStick.ts @@ -1,21 +1,26 @@ -import CandlestickFinder from './CandlestickFinder'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + export default class BullishHammerStick extends CandlestickFinder { constructor() { super(); this.name = 'BullishHammerStick'; - this.requiredCount = 1; + this.requiredCount = 1; } - logic(data) { - let daysOpen = data.open[0]; + logic (data:StockData) { + let daysOpen = data.open[0]; let daysClose = data.close[0]; - let daysHigh = data.high[0]; - let daysLow = data.low[0]; + let daysHigh = data.high[0]; + let daysLow = data.low[0]; + let isBullishHammer = daysClose > daysOpen; isBullishHammer = isBullishHammer && this.approximateEqual(daysClose, daysHigh); isBullishHammer = isBullishHammer && (daysClose - daysOpen) <= 2 * (daysOpen - daysLow); + return isBullishHammer; } } -export function bullishhammerstick(data) { - return new BullishHammerStick().hasPattern(data); -} + +export function bullishhammerstick(data:StockData) { + return new BullishHammerStick().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/BullishHarami.ts b/src/candlestick/BullishHarami.ts new file mode 100644 index 0000000..45d5ca7 --- /dev/null +++ b/src/candlestick/BullishHarami.ts @@ -0,0 +1,33 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class BullishHarami extends CandlestickFinder { + constructor() { + super(); + this.requiredCount = 2; + this.name = "BullishHarami"; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + + let isBullishHaramiPattern = ((firstdaysOpen > seconddaysOpen) && + (firstdaysClose < seconddaysOpen)&& + (firstdaysClose < seconddaysClose)&& + (firstdaysOpen > seconddaysLow)&& + (firstdaysHigh > seconddaysHigh)); + + return (isBullishHaramiPattern); + + } +} + +export function bullishharami(data:StockData) { + return new BullishHarami().hasPattern(data); +} diff --git a/src/candlestick/BullishHaramiCross.ts b/src/candlestick/BullishHaramiCross.ts new file mode 100644 index 0000000..3856a54 --- /dev/null +++ b/src/candlestick/BullishHaramiCross.ts @@ -0,0 +1,35 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class BullishHaramiCross extends CandlestickFinder { + constructor() { + super(); + this.requiredCount = 2; + this.name = 'BullishHaramiCross'; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + + let isBullishHaramiCrossPattern = ((firstdaysOpen > seconddaysOpen) && + (firstdaysClose < seconddaysOpen)&& + (firstdaysClose < seconddaysClose)&& + (firstdaysOpen > seconddaysLow)&& + (firstdaysHigh > seconddaysHigh)); + + let isSecondDayDoji = this.approximateEqual(seconddaysOpen, seconddaysClose); + + return (isBullishHaramiCrossPattern && isSecondDayDoji); + + } +} + +export function bullishharamicross(data:StockData) { + return new BullishHaramiCross().hasPattern(data); +} diff --git a/lib/candlestick/BullishInvertedHammerStick.js b/src/candlestick/BullishInvertedHammerStick.ts similarity index 57% rename from lib/candlestick/BullishInvertedHammerStick.js rename to src/candlestick/BullishInvertedHammerStick.ts index 46955e1..0f1b1ba 100644 --- a/lib/candlestick/BullishInvertedHammerStick.js +++ b/src/candlestick/BullishInvertedHammerStick.ts @@ -1,21 +1,26 @@ -import CandlestickFinder from './CandlestickFinder'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + export default class BullishInvertedHammerStick extends CandlestickFinder { constructor() { super(); this.name = 'BullishInvertedHammerStick'; - this.requiredCount = 1; + this.requiredCount = 1; } - logic(data) { - let daysOpen = data.open[0]; + logic (data:StockData) { + let daysOpen = data.open[0]; let daysClose = data.close[0]; - let daysHigh = data.high[0]; - let daysLow = data.low[0]; + let daysHigh = data.high[0]; + let daysLow = data.low[0]; + let isBullishInvertedHammer = daysClose > daysOpen; isBullishInvertedHammer = isBullishInvertedHammer && this.approximateEqual(daysOpen, daysLow); isBullishInvertedHammer = isBullishInvertedHammer && (daysClose - daysOpen) <= 2 * (daysHigh - daysClose); + return isBullishInvertedHammer; } } -export function bullishinvertedhammerstick(data) { - return new BullishInvertedHammerStick().hasPattern(data); -} + +export function bullishinvertedhammerstick(data:StockData) { + return new BullishInvertedHammerStick().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/BullishMarubozu.ts b/src/candlestick/BullishMarubozu.ts new file mode 100644 index 0000000..e597364 --- /dev/null +++ b/src/candlestick/BullishMarubozu.ts @@ -0,0 +1,29 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class BullishMarubozu extends CandlestickFinder { + constructor() { + super(); + this.name = 'BullishMarubozu'; + this.requiredCount = 1; + } + logic (data:StockData) { + let daysOpen = data.open[0]; + let daysClose = data.close[0]; + let daysHigh = data.high[0]; + let daysLow = data.low[0]; + + let isBullishMarbozu = this.approximateEqual(daysClose, daysHigh) && + this.approximateEqual(daysLow, daysOpen) && + daysOpen < daysClose && + daysOpen < daysHigh; + + + return (isBullishMarbozu); + + } +} + +export function bullishmarubozu(data:StockData) { + return new BullishMarubozu().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/BullishSpinningTop.ts b/src/candlestick/BullishSpinningTop.ts new file mode 100644 index 0000000..86ea06b --- /dev/null +++ b/src/candlestick/BullishSpinningTop.ts @@ -0,0 +1,28 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class BullishSpinningTop extends CandlestickFinder { + constructor() { + super(); + this.name = 'BullishSpinningTop'; + this.requiredCount = 1; + } + logic (data:StockData) { + let daysOpen = data.open[0]; + let daysClose = data.close[0]; + let daysHigh = data.high[0]; + let daysLow = data.low[0]; + + let bodyLength = Math.abs(daysClose-daysOpen); + let upperShadowLength = Math.abs(daysHigh-daysClose); + let lowerShadowLength = Math.abs(daysOpen-daysLow); + let isBullishSpinningTop = bodyLength < upperShadowLength && + bodyLength < lowerShadowLength; + + return isBullishSpinningTop; + } +} + +export function bullishspinningtop(data:StockData) { + return new BullishSpinningTop().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/CandlestickFinder.ts b/src/candlestick/CandlestickFinder.ts new file mode 100644 index 0000000..636a7bf --- /dev/null +++ b/src/candlestick/CandlestickFinder.ts @@ -0,0 +1,99 @@ +import StockData from '../StockData.ts'; +export default class CandlestickFinder { + requiredCount:number + name:string + constructor() { + // if (new.target === Abstract) { + // throw new TypeError("Abstract class"); + // } + } + approximateEqual(a:number, b:number):boolean { + let left = parseFloat(Math.abs(a - b).toPrecision(4)) * 1; + let right = parseFloat((a * 0.001).toPrecision(4)) * 1; + return left <= right + } + + logic(data:StockData):boolean { + throw "this has to be implemented"; + } + getAllPatternIndex (data:StockData) { + if(data.close.length < this.requiredCount) { + console.warn('Data count less than data required for the strategy ', this.name); + return []; + } + if(data.reversedInput) { + data.open.reverse(); + data.high.reverse(); + data.low.reverse(); + data.close.reverse(); + } + let strategyFn = this.logic; + return this._generateDataForCandleStick(data) + .map((current, index) => { + return strategyFn.call(this, current) ? index : undefined; + }).filter((hasIndex) => { + return hasIndex; + }); + } + + hasPattern (data:StockData) { + if(data.close.length < this.requiredCount) { + console.warn('Data count less than data required for the strategy ', this.name); + return false; + } + if(data.reversedInput) { + data.open.reverse(); + data.high.reverse(); + data.low.reverse(); + data.close.reverse(); + } + let strategyFn = this.logic; + return strategyFn.call(this, this._getLastDataForCandleStick(data)); + } + + protected _getLastDataForCandleStick(data:StockData) { + let requiredCount = this.requiredCount; + if (data.close.length === requiredCount) { + return data; + } else { + let returnVal = { + open : [], + high : [], + low : [], + close: [] + } as StockData; + let i = 0; + let index = data.close.length - requiredCount; + while (i < requiredCount) { + returnVal.open.push(data.open[index + i]); + returnVal.high.push(data.high[index + i]); + returnVal.low.push(data.low[index + i]); + returnVal.close.push(data.close[index + i]); + i++; + } + return returnVal; + } + } + + protected _generateDataForCandleStick(data:StockData) { + let requiredCount = this.requiredCount; + let generatedData = data.close.map(function(currentData, index) { + let i = 0; + let returnVal = { + open : [], + high : [], + low : [], + close: [] + } as StockData; + while(i < requiredCount) { + returnVal.open.push(data.open[index + i]); + returnVal.high.push(data.high[index + i]); + returnVal.low.push(data.low[index + i]); + returnVal.close.push(data.close[index + i]); + i++; + } + return returnVal; + }).filter((val, index) => { return (index <= (data.close.length - requiredCount)) }); + return generatedData; + } +} \ No newline at end of file diff --git a/src/candlestick/DarkCloudCover.ts b/src/candlestick/DarkCloudCover.ts new file mode 100644 index 0000000..784db49 --- /dev/null +++ b/src/candlestick/DarkCloudCover.ts @@ -0,0 +1,34 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class DarkCloudCover extends CandlestickFinder { + constructor() { + super(); + this.name = 'DarkCloudCover'; + this.requiredCount = 2; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + + let firstdayMidpoint = ((firstdaysClose+firstdaysOpen)/2); + let isFirstBullish = firstdaysClose > firstdaysOpen; + let isSecondBearish = seconddaysClose < seconddaysOpen; + let isDarkCloudPattern = ((seconddaysOpen > firstdaysHigh) && + (seconddaysClose < firstdayMidpoint)&& + (seconddaysClose > firstdaysOpen)); + + return (isFirstBullish && isSecondBearish && isDarkCloudPattern); + + } +} + +export function darkcloudcover(data:StockData) { + return new DarkCloudCover().hasPattern(data); +} \ No newline at end of file diff --git a/lib/candlestick/Doji.js b/src/candlestick/Doji.ts similarity index 71% rename from lib/candlestick/Doji.js rename to src/candlestick/Doji.ts index 2f584c3..5917721 100644 --- a/lib/candlestick/Doji.js +++ b/src/candlestick/Doji.ts @@ -1,11 +1,13 @@ -import CandlestickFinder from './CandlestickFinder'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + export default class Doji extends CandlestickFinder { constructor() { super(); this.name = 'Doji'; - this.requiredCount = 1; + this.requiredCount = 1; } - logic(data) { + logic (data:StockData):boolean { let daysOpen = data.open[0]; let daysClose = data.close[0]; let daysHigh = data.high[0]; @@ -16,6 +18,7 @@ export default class Doji extends CandlestickFinder { return (isOpenEqualsClose && isHighEqualsOpen == isLowEqualsClose); } } -export function doji(data) { - return new Doji().hasPattern(data); -} + +export function doji(data:StockData) { + return new Doji().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/DownsideTasukiGap.ts b/src/candlestick/DownsideTasukiGap.ts new file mode 100644 index 0000000..deefa73 --- /dev/null +++ b/src/candlestick/DownsideTasukiGap.ts @@ -0,0 +1,40 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class DownsideTasukiGap extends CandlestickFinder { + constructor() { + super(); + this.requiredCount = 3; + this.name = 'DownsideTasukiGap'; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1]; + let thirddaysOpen = data.open[2]; + let thirddaysClose = data.close[2]; + let thirddaysHigh = data.high[2]; + let thirddaysLow = data.low[2]; + + let isFirstBearish = firstdaysClose < firstdaysOpen; + let isSecondBearish = seconddaysClose < seconddaysOpen; + let isThirdBullish = thirddaysClose > thirddaysOpen; + let isFirstGapExists = seconddaysHigh < firstdaysLow; + let isDownsideTasukiGap = ((seconddaysOpen > thirddaysOpen) && + (seconddaysClose < thirddaysOpen) && + (thirddaysClose > seconddaysOpen) && + (thirddaysClose < firstdaysClose)); + + return (isFirstBearish && isSecondBearish && isThirdBullish && isFirstGapExists && isDownsideTasukiGap); + + } +} + +export function downsidetasukigap(data:StockData) { + return new DownsideTasukiGap().hasPattern(data); +} \ No newline at end of file diff --git a/lib/candlestick/DragonFlyDoji.js b/src/candlestick/DragonFlyDoji.ts similarity index 58% rename from lib/candlestick/DragonFlyDoji.js rename to src/candlestick/DragonFlyDoji.ts index 67c2ac7..a596ad6 100644 --- a/lib/candlestick/DragonFlyDoji.js +++ b/src/candlestick/DragonFlyDoji.ts @@ -1,14 +1,16 @@ -import CandlestickFinder from './CandlestickFinder'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + export default class DragonFlyDoji extends CandlestickFinder { constructor() { super(); - this.requiredCount = 1; + this.requiredCount = 1; this.name = 'DragonFlyDoji'; } - logic(data) { - let daysOpen = data.open[0]; - let daysClose = data.close[0]; - let daysHigh = data.high[0]; + logic (data:StockData) { + let daysOpen = data.open[0]; + let daysClose = data.close[0]; + let daysHigh = data.high[0]; let daysLow = data.low[0]; let isOpenEqualsClose = this.approximateEqual(daysOpen, daysClose); let isHighEqualsOpen = isOpenEqualsClose && this.approximateEqual(daysOpen, daysHigh); @@ -16,6 +18,7 @@ export default class DragonFlyDoji extends CandlestickFinder { return (isOpenEqualsClose && isHighEqualsOpen && !isLowEqualsClose); } } -export function dragonflydoji(data) { - return new DragonFlyDoji().hasPattern(data); -} + +export function dragonflydoji(data:StockData) { + return new DragonFlyDoji().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/EveningDojiStar.ts b/src/candlestick/EveningDojiStar.ts new file mode 100644 index 0000000..4b1e5fc --- /dev/null +++ b/src/candlestick/EveningDojiStar.ts @@ -0,0 +1,48 @@ +import Doji from './Doji.ts'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + + +export default class EveningDojiStar extends CandlestickFinder { + constructor() { + super(); + this.name = 'EveningDojiStar'; + this.requiredCount = 3; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + let thirddaysOpen = data.open[2]; + let thirddaysClose = data.close[2]; + let thirddaysHigh = data.high[2]; + let thirddaysLow = data.low[2]; + + let firstdaysMidpoint = ((firstdaysOpen+firstdaysClose)/2); + let isFirstBullish = firstdaysClose > firstdaysOpen; + let dojiExists = new Doji().hasPattern({ + "open" : [seconddaysOpen], + "close": [seconddaysClose], + "high" : [seconddaysHigh], + "low" : [seconddaysLow] + }); + let isThirdBearish = thirddaysOpen > thirddaysClose; + + let gapExists = ((seconddaysHigh > firstdaysHigh) && + (seconddaysLow > firstdaysHigh) && + (thirddaysOpen < seconddaysLow) && + (seconddaysClose > thirddaysOpen)); + let doesCloseBelowFirstMidpoint = thirddaysClose < firstdaysMidpoint; + return (isFirstBullish && dojiExists && gapExists && isThirdBearish && doesCloseBelowFirstMidpoint ); + } +} + + +export function eveningdojistar(data:StockData) { + return new EveningDojiStar().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/EveningStar.ts b/src/candlestick/EveningStar.ts new file mode 100644 index 0000000..4fc8031 --- /dev/null +++ b/src/candlestick/EveningStar.ts @@ -0,0 +1,41 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class EveningStar extends CandlestickFinder { + constructor() { + super(); + this.name = 'EveningStar'; + this.requiredCount = 3; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + let thirddaysOpen = data.open[2]; + let thirddaysClose = data.close[2]; + let thirddaysHigh = data.high[2]; + let thirddaysLow = data.low[2]; + + let firstdaysMidpoint = ((firstdaysOpen+firstdaysClose)/2); + let isFirstBullish = firstdaysClose > firstdaysOpen; + let isSmallBodyExists = ((firstdaysHigh < seconddaysLow)&& + (firstdaysHigh < seconddaysHigh)); + let isThirdBearish = thirddaysOpen > thirddaysClose; + + let gapExists = ((seconddaysHigh > firstdaysHigh) && + (seconddaysLow > firstdaysHigh) && + (thirddaysOpen < seconddaysLow) && + (seconddaysClose > thirddaysOpen)); + let doesCloseBelowFirstMidpoint = thirddaysClose < firstdaysMidpoint; + return (isFirstBullish && isSmallBodyExists && gapExists && isThirdBearish && doesCloseBelowFirstMidpoint ); + } +} + +export function eveningstar(data:StockData) { + return new EveningStar().hasPattern(data); +} \ No newline at end of file diff --git a/lib/candlestick/GraveStoneDoji.js b/src/candlestick/GraveStoneDoji.ts similarity index 62% rename from lib/candlestick/GraveStoneDoji.js rename to src/candlestick/GraveStoneDoji.ts index 35c1b3f..b4643a9 100644 --- a/lib/candlestick/GraveStoneDoji.js +++ b/src/candlestick/GraveStoneDoji.ts @@ -1,13 +1,15 @@ -import CandlestickFinder from './CandlestickFinder'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + export default class GraveStoneDoji extends CandlestickFinder { constructor() { super(); - this.requiredCount = 1; + this.requiredCount = 1; this.name = 'GraveStoneDoji'; } - logic(data) { - let daysOpen = data.open[0]; - let daysClose = data.close[0]; + logic (data:StockData) { + let daysOpen = data.open[0]; + let daysClose = data.close[0]; let daysHigh = data.high[0]; let daysLow = data.low[0]; let isOpenEqualsClose = this.approximateEqual(daysOpen, daysClose); @@ -16,6 +18,7 @@ export default class GraveStoneDoji extends CandlestickFinder { return (isOpenEqualsClose && isLowEqualsClose && !isHighEqualsOpen); } } -export function gravestonedoji(data) { - return new GraveStoneDoji().hasPattern(data); -} + +export function gravestonedoji(data:StockData) { + return new GraveStoneDoji().hasPattern(data); +} \ No newline at end of file diff --git a/lib/candlestick/HammerPattern.js b/src/candlestick/HammerPattern.ts similarity index 75% rename from lib/candlestick/HammerPattern.js rename to src/candlestick/HammerPattern.ts index 374b635..ceedeca 100644 --- a/lib/candlestick/HammerPattern.js +++ b/src/candlestick/HammerPattern.ts @@ -1,23 +1,27 @@ -import CandlestickFinder from './CandlestickFinder'; -import { averageloss } from '../Utils/AverageLoss'; -import { averagegain } from '../Utils/AverageGain'; -import { bearishhammerstick } from './BearishHammerStick'; -import { bearishinvertedhammerstick } from './BearishInvertedHammerStick'; -import { bullishhammerstick } from './BullishHammerStick'; -import { bullishinvertedhammerstick } from './BullishInvertedHammerStick'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; +import { averageloss } from '../Utils/AverageLoss.ts'; +import { averagegain } from '../Utils/AverageGain.ts'; +import { bearishhammerstick } from './BearishHammerStick.ts'; +import { bearishinvertedhammerstick } from './BearishInvertedHammerStick.ts'; +import { bullishhammerstick } from './BullishHammerStick.ts'; +import { bullishinvertedhammerstick } from './BullishInvertedHammerStick.ts'; + export default class HammerPattern extends CandlestickFinder { constructor() { super(); this.name = 'HammerPattern'; this.requiredCount = 5; } - logic(data) { + + logic (data:StockData) { let isPattern = this.downwardTrend(data); isPattern = isPattern && this.includesHammer(data); isPattern = isPattern && this.hasConfirmation(data); return isPattern; } - downwardTrend(data, confirm = true) { + + downwardTrend (data:StockData, confirm = true) { let end = confirm ? 3 : 4; // Analyze trends in closing prices of the first three or four candlesticks let gains = averagegain({ values: data.close.slice(0, end), period: end - 1 }); @@ -25,7 +29,8 @@ export default class HammerPattern extends CandlestickFinder { // Downward trend, so more losses than gains return losses > gains; } - includesHammer(data, confirm = true) { + + includesHammer (data:StockData, confirm = true) { let start = confirm ? 3 : 4; let end = confirm ? 4 : undefined; let possibleHammerData = { @@ -34,30 +39,34 @@ export default class HammerPattern extends CandlestickFinder { low: data.low.slice(start, end), high: data.high.slice(start, end), }; + let isPattern = bearishhammerstick(possibleHammerData); isPattern = isPattern || bearishinvertedhammerstick(possibleHammerData); isPattern = isPattern || bullishhammerstick(possibleHammerData); isPattern = isPattern || bullishinvertedhammerstick(possibleHammerData); + return isPattern; } - hasConfirmation(data) { + + hasConfirmation (data:StockData) { let possibleHammer = { open: data.open[3], close: data.close[3], low: data.low[3], high: data.high[3], - }; + } let possibleConfirmation = { open: data.open[4], close: data.close[4], low: data.low[4], high: data.high[4], - }; + } // Confirmation candlestick is bullish let isPattern = possibleConfirmation.open < possibleConfirmation.close; return isPattern && possibleHammer.close < possibleConfirmation.close; } } -export function hammerpattern(data) { - return new HammerPattern().hasPattern(data); + +export function hammerpattern(data:StockData) { + return new HammerPattern().hasPattern(data); } diff --git a/lib/candlestick/HammerPatternUnconfirmed.js b/src/candlestick/HammerPatternUnconfirmed.ts similarity index 56% rename from lib/candlestick/HammerPatternUnconfirmed.js rename to src/candlestick/HammerPatternUnconfirmed.ts index 3deab68..bf66ec2 100644 --- a/lib/candlestick/HammerPatternUnconfirmed.js +++ b/src/candlestick/HammerPatternUnconfirmed.ts @@ -1,15 +1,19 @@ -import HammerPattern from './HammerPattern'; +import StockData from '../StockData.ts'; +import HammerPattern from './HammerPattern.ts'; + export default class HammerPatternUnconfirmed extends HammerPattern { constructor() { super(); this.name = 'HammerPatternUnconfirmed'; } - logic(data) { + + logic (data:StockData) { let isPattern = this.downwardTrend(data, false); isPattern = isPattern && this.includesHammer(data, false); return isPattern; - } + } } -export function hammerpatternunconfirmed(data) { - return new HammerPatternUnconfirmed().hasPattern(data); + +export function hammerpatternunconfirmed(data:StockData) { + return new HammerPatternUnconfirmed().hasPattern(data); } diff --git a/lib/candlestick/HangingMan.js b/src/candlestick/HangingMan.ts similarity index 74% rename from lib/candlestick/HangingMan.js rename to src/candlestick/HangingMan.ts index a02e443..6fd2460 100644 --- a/lib/candlestick/HangingMan.js +++ b/src/candlestick/HangingMan.ts @@ -1,21 +1,25 @@ -import CandlestickFinder from './CandlestickFinder'; -import { averageloss } from '../Utils/AverageLoss'; -import { averagegain } from '../Utils/AverageGain'; -import { bearishhammerstick } from './BearishHammerStick'; -import { bullishhammerstick } from './BullishHammerStick'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; +import { averageloss } from '../Utils/AverageLoss.ts'; +import { averagegain } from '../Utils/AverageGain.ts'; +import { bearishhammerstick } from './BearishHammerStick.ts'; +import { bullishhammerstick } from './BullishHammerStick.ts'; + export default class HangingMan extends CandlestickFinder { constructor() { super(); this.name = 'HangingMan'; this.requiredCount = 5; } - logic(data) { + + logic (data:StockData) { let isPattern = this.upwardTrend(data); isPattern = isPattern && this.includesHammer(data); isPattern = isPattern && this.hasConfirmation(data); return isPattern; } - upwardTrend(data, confirm = true) { + + upwardTrend (data:StockData, confirm = true) { let end = confirm ? 3 : 4; // Analyze trends in closing prices of the first three or four candlesticks let gains = averagegain({ values: data.close.slice(0, end), period: end - 1 }); @@ -23,7 +27,8 @@ export default class HangingMan extends CandlestickFinder { // Upward trend, so more gains than losses return gains > losses; } - includesHammer(data, confirm = true) { + + includesHammer (data:StockData, confirm = true) { let start = confirm ? 3 : 4; let end = confirm ? 4 : undefined; let possibleHammerData = { @@ -32,28 +37,32 @@ export default class HangingMan extends CandlestickFinder { low: data.low.slice(start, end), high: data.high.slice(start, end), }; + let isPattern = bearishhammerstick(possibleHammerData); isPattern = isPattern || bullishhammerstick(possibleHammerData); + return isPattern; } - hasConfirmation(data) { + + hasConfirmation (data:StockData) { let possibleHammer = { open: data.open[3], close: data.close[3], low: data.low[3], high: data.high[3], - }; + } let possibleConfirmation = { open: data.open[4], close: data.close[4], low: data.low[4], high: data.high[4], - }; + } // Confirmation candlestick is bearish let isPattern = possibleConfirmation.open > possibleConfirmation.close; return isPattern && possibleHammer.close > possibleConfirmation.close; } } -export function hangingman(data) { - return new HangingMan().hasPattern(data); + +export function hangingman(data:StockData) { + return new HangingMan().hasPattern(data); } diff --git a/lib/candlestick/HangingManUnconfirmed.js b/src/candlestick/HangingManUnconfirmed.ts similarity index 57% rename from lib/candlestick/HangingManUnconfirmed.js rename to src/candlestick/HangingManUnconfirmed.ts index 6f3c20a..c024254 100644 --- a/lib/candlestick/HangingManUnconfirmed.js +++ b/src/candlestick/HangingManUnconfirmed.ts @@ -1,15 +1,19 @@ -import HangingMan from './HangingMan'; +import StockData from '../StockData.ts'; +import HangingMan from './HangingMan.ts'; + export default class HangingManUnconfirmed extends HangingMan { constructor() { super(); this.name = 'HangingManUnconfirmed'; } - logic(data) { + + logic (data:StockData) { let isPattern = this.upwardTrend(data, false); isPattern = isPattern && this.includesHammer(data, false); return isPattern; } } -export function hangingmanunconfirmed(data) { - return new HangingManUnconfirmed().hasPattern(data); + +export function hangingmanunconfirmed(data:StockData) { + return new HangingManUnconfirmed().hasPattern(data); } diff --git a/src/candlestick/MorningDojiStar.ts b/src/candlestick/MorningDojiStar.ts new file mode 100644 index 0000000..855b001 --- /dev/null +++ b/src/candlestick/MorningDojiStar.ts @@ -0,0 +1,48 @@ +import Doji from './Doji.ts'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class MorningDojiStar extends CandlestickFinder { + constructor() { + super(); + this.name = 'MorningDojiStar'; + this.requiredCount = 3; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + let thirddaysOpen = data.open[2]; + let thirddaysClose = data.close[2]; + let thirddaysHigh = data.high[2]; + let thirddaysLow = data.low[2]; + + let firstdaysMidpoint = ((firstdaysOpen+firstdaysClose)/2); + let isFirstBearish = firstdaysClose < firstdaysOpen; + let dojiExists = new Doji().hasPattern({ + "open" : [seconddaysOpen], + "close": [seconddaysClose], + "high" : [seconddaysHigh], + "low" : [seconddaysLow] + }); + let isThirdBullish = thirddaysOpen < thirddaysClose; + + let gapExists = ((seconddaysHigh < firstdaysLow) && + (seconddaysLow < firstdaysLow) && + (thirddaysOpen > seconddaysHigh) && + (seconddaysClose < thirddaysOpen)); + let doesCloseAboveFirstMidpoint = thirddaysClose > firstdaysMidpoint; + return (isFirstBearish && dojiExists && isThirdBullish && gapExists && + doesCloseAboveFirstMidpoint ); + } +} + + +export function morningdojistar(data:StockData) { + return new MorningDojiStar().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/MorningStar.ts b/src/candlestick/MorningStar.ts new file mode 100644 index 0000000..286f80c --- /dev/null +++ b/src/candlestick/MorningStar.ts @@ -0,0 +1,41 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class MorningStar extends CandlestickFinder { + constructor() { + super(); + this.name = 'MorningStar'; + this.requiredCount = 3; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + let thirddaysOpen = data.open[2]; + let thirddaysClose = data.close[2]; + let thirddaysHigh = data.high[2]; + let thirddaysLow = data.low[2]; + + let firstdaysMidpoint = ((firstdaysOpen+firstdaysClose)/2); + let isFirstBearish = firstdaysClose < firstdaysOpen; + let isSmallBodyExists = ((firstdaysLow > seconddaysLow)&& + (firstdaysLow > seconddaysHigh)); + let isThirdBullish = thirddaysOpen < thirddaysClose; + + let gapExists = ((seconddaysHigh < firstdaysLow) && + (seconddaysLow < firstdaysLow) && + (thirddaysOpen > seconddaysHigh) && + (seconddaysClose < thirddaysOpen)); + let doesCloseAboveFirstMidpoint = thirddaysClose > firstdaysMidpoint; + return (isFirstBearish && isSmallBodyExists && gapExists && isThirdBullish && doesCloseAboveFirstMidpoint ); + } +} + +export function morningstar(data:StockData) { + return new MorningStar().hasPattern(data); +} \ No newline at end of file diff --git a/src/candlestick/PiercingLine.ts b/src/candlestick/PiercingLine.ts new file mode 100644 index 0000000..89c8a40 --- /dev/null +++ b/src/candlestick/PiercingLine.ts @@ -0,0 +1,35 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class PiercingLine extends CandlestickFinder { + constructor() { + super(); + this.requiredCount = 2; + this.name = 'PiercingLine'; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + + let firstdaysMidpoint = ((firstdaysOpen+firstdaysClose)/2); + let isDowntrend = seconddaysLow < firstdaysLow; + let isFirstBearish = firstdaysClose < firstdaysOpen; + let isSecondBullish = seconddaysClose > seconddaysOpen; + + let isPiercingLinePattern = ((firstdaysLow > seconddaysOpen) && + (seconddaysClose > firstdaysMidpoint)); + + return (isDowntrend && isFirstBearish && isPiercingLinePattern && isSecondBullish); + + } +} + +export function piercingline(data:StockData) { + return new PiercingLine().hasPattern(data); +} \ No newline at end of file diff --git a/lib/candlestick/ShootingStar.js b/src/candlestick/ShootingStar.ts similarity index 78% rename from lib/candlestick/ShootingStar.js rename to src/candlestick/ShootingStar.ts index af655e1..f68fe90 100644 --- a/lib/candlestick/ShootingStar.js +++ b/src/candlestick/ShootingStar.ts @@ -1,21 +1,25 @@ -import CandlestickFinder from './CandlestickFinder'; -import { averageloss } from '../Utils/AverageLoss'; -import { averagegain } from '../Utils/AverageGain'; -import { bearishinvertedhammerstick } from './BearishInvertedHammerStick'; -import { bullishinvertedhammerstick } from './BullishInvertedHammerStick'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; +import { averageloss } from '../Utils/AverageLoss.ts'; +import { averagegain } from '../Utils/AverageGain.ts'; +import { bearishinvertedhammerstick } from './BearishInvertedHammerStick.ts'; +import { bullishinvertedhammerstick } from './BullishInvertedHammerStick.ts'; + export default class ShootingStar extends CandlestickFinder { constructor() { super(); this.name = 'ShootingStar'; this.requiredCount = 5; } - logic(data) { + + logic (data:StockData) { let isPattern = this.upwardTrend(data); isPattern = isPattern && this.includesHammer(data); isPattern = isPattern && this.hasConfirmation(data); return isPattern; } - upwardTrend(data, confirm = true) { + + upwardTrend (data:StockData, confirm = true) { let end = confirm ? 3 : 4; // Analyze trends in closing prices of the first three or four candlesticks let gains = averagegain({ values: data.close.slice(0, end), period: end - 1 }); @@ -23,7 +27,8 @@ export default class ShootingStar extends CandlestickFinder { // Upward trend, so more gains than losses return gains > losses; } - includesHammer(data, confirm = true) { + + includesHammer (data:StockData, confirm = true) { let start = confirm ? 3 : 4; let end = confirm ? 4 : undefined; let possibleHammerData = { @@ -32,28 +37,32 @@ export default class ShootingStar extends CandlestickFinder { low: data.low.slice(start, end), high: data.high.slice(start, end), }; + let isPattern = bearishinvertedhammerstick(possibleHammerData); isPattern = isPattern || bullishinvertedhammerstick(possibleHammerData); + return isPattern; } - hasConfirmation(data) { + + hasConfirmation (data:StockData) { let possibleHammer = { open: data.open[3], close: data.close[3], low: data.low[3], high: data.high[3], - }; + } let possibleConfirmation = { open: data.open[4], close: data.close[4], low: data.low[4], high: data.high[4], - }; + } // Confirmation candlestick is bearish let isPattern = possibleConfirmation.open > possibleConfirmation.close; return isPattern && possibleHammer.close > possibleConfirmation.close; } } -export function shootingstar(data) { - return new ShootingStar().hasPattern(data); + +export function shootingstar(data:StockData) { + return new ShootingStar().hasPattern(data); } diff --git a/lib/candlestick/ShootingStarUnconfirmed.js b/src/candlestick/ShootingStarUnconfirmed.ts similarity index 57% rename from lib/candlestick/ShootingStarUnconfirmed.js rename to src/candlestick/ShootingStarUnconfirmed.ts index 5cd246b..ba68f94 100644 --- a/lib/candlestick/ShootingStarUnconfirmed.js +++ b/src/candlestick/ShootingStarUnconfirmed.ts @@ -1,15 +1,19 @@ -import ShootingStar from './ShootingStar'; +import StockData from '../StockData.ts'; +import ShootingStar from './ShootingStar.ts'; + export default class ShootingStarUnconfirmed extends ShootingStar { constructor() { super(); this.name = 'ShootingStarUnconfirmed'; } - logic(data) { + + logic (data:StockData) { let isPattern = this.upwardTrend(data, false); isPattern = isPattern && this.includesHammer(data, false); return isPattern; } } -export function shootingstarunconfirmed(data) { - return new ShootingStarUnconfirmed().hasPattern(data); + +export function shootingstarunconfirmed(data:StockData) { + return new ShootingStarUnconfirmed().hasPattern(data); } diff --git a/src/candlestick/ThreeBlackCrows.ts b/src/candlestick/ThreeBlackCrows.ts new file mode 100644 index 0000000..0fa7616 --- /dev/null +++ b/src/candlestick/ThreeBlackCrows.ts @@ -0,0 +1,41 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class ThreeBlackCrows extends CandlestickFinder { + constructor() { + super(); + this.name = 'ThreeBlackCrows'; + this.requiredCount = 3; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + let thirddaysOpen = data.open[2]; + let thirddaysClose = data.close[2]; + let thirddaysHigh = data.high[2]; + let thirddaysLow = data.low[2]; + + let isDownTrend = firstdaysLow > seconddaysLow && + seconddaysLow > thirddaysLow; + let isAllBearish = firstdaysOpen > firstdaysClose && + seconddaysOpen > seconddaysClose && + thirddaysOpen > thirddaysClose; + + let doesOpenWithinPreviousBody = firstdaysOpen > seconddaysOpen && + seconddaysOpen > firstdaysClose && + seconddaysOpen > thirddaysOpen && + thirddaysOpen > seconddaysClose; + + return (isDownTrend && isAllBearish && doesOpenWithinPreviousBody); + } +} + +export function threeblackcrows(data:StockData) { + return new ThreeBlackCrows().hasPattern(data); +} diff --git a/src/candlestick/ThreeWhiteSoldiers.ts b/src/candlestick/ThreeWhiteSoldiers.ts new file mode 100644 index 0000000..2a83b94 --- /dev/null +++ b/src/candlestick/ThreeWhiteSoldiers.ts @@ -0,0 +1,41 @@ +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; + +export default class ThreeWhiteSoldiers extends CandlestickFinder { + constructor() { + super(); + this.name = 'ThreeWhiteSoldiers'; + this.requiredCount = 3; + } + logic (data:StockData) { + let firstdaysOpen = data.open[0]; + let firstdaysClose = data.close[0]; + let firstdaysHigh = data.high[0]; + let firstdaysLow = data.low[0] + let seconddaysOpen = data.open[1]; + let seconddaysClose = data.close[1]; + let seconddaysHigh = data.high[1]; + let seconddaysLow = data.low[1] + let thirddaysOpen = data.open[2]; + let thirddaysClose = data.close[2]; + let thirddaysHigh = data.high[2]; + let thirddaysLow = data.low[2]; + + let isUpTrend = seconddaysHigh > firstdaysHigh && + thirddaysHigh > seconddaysHigh; + let isAllBullish = firstdaysOpen < firstdaysClose && + seconddaysOpen < seconddaysClose && + thirddaysOpen < thirddaysClose; + + let doesOpenWithinPreviousBody = firstdaysClose > seconddaysOpen && + seconddaysOpen < firstdaysHigh && + seconddaysHigh > thirddaysOpen && + thirddaysOpen < seconddaysClose; + + return (isUpTrend && isAllBullish && doesOpenWithinPreviousBody); + } +} + +export function threewhitesoldiers(data:StockData) { + return new ThreeWhiteSoldiers().hasPattern(data); +} diff --git a/lib/candlestick/TweezerBottom.js b/src/candlestick/TweezerBottom.ts similarity index 61% rename from lib/candlestick/TweezerBottom.js rename to src/candlestick/TweezerBottom.ts index 96451f3..d31e67a 100644 --- a/lib/candlestick/TweezerBottom.js +++ b/src/candlestick/TweezerBottom.ts @@ -1,16 +1,20 @@ -import CandlestickFinder from './CandlestickFinder'; -import { averageloss } from '../Utils/AverageLoss'; -import { averagegain } from '../Utils/AverageGain'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; +import { averageloss } from '../Utils/AverageLoss.ts'; +import { averagegain } from '../Utils/AverageGain.ts'; + export default class TweezerBottom extends CandlestickFinder { constructor() { super(); this.name = 'TweezerBottom'; this.requiredCount = 5; } - logic(data) { + + logic (data:StockData) { return this.downwardTrend(data) && data.low[3] == data.low[4]; } - downwardTrend(data) { + + downwardTrend (data:StockData) { // Analyze trends in closing prices of the first three or four candlesticks let gains = averagegain({ values: data.close.slice(0, 3), period: 2 }); let losses = averageloss({ values: data.close.slice(0, 3), period: 2 }); @@ -18,6 +22,7 @@ export default class TweezerBottom extends CandlestickFinder { return losses > gains; } } -export function tweezerbottom(data) { - return new TweezerBottom().hasPattern(data); + +export function tweezerbottom(data:StockData) { + return new TweezerBottom().hasPattern(data); } diff --git a/lib/candlestick/TweezerTop.js b/src/candlestick/TweezerTop.ts similarity index 61% rename from lib/candlestick/TweezerTop.js rename to src/candlestick/TweezerTop.ts index 068b3b3..80d9332 100644 --- a/lib/candlestick/TweezerTop.js +++ b/src/candlestick/TweezerTop.ts @@ -1,16 +1,20 @@ -import CandlestickFinder from './CandlestickFinder'; -import { averageloss } from '../Utils/AverageLoss'; -import { averagegain } from '../Utils/AverageGain'; +import StockData from '../StockData.ts'; +import CandlestickFinder from './CandlestickFinder.ts'; +import { averageloss } from '../Utils/AverageLoss.ts'; +import { averagegain } from '../Utils/AverageGain.ts'; + export default class TweezerTop extends CandlestickFinder { constructor() { super(); this.name = 'TweezerTop'; this.requiredCount = 5; } - logic(data) { + + logic (data:StockData) { return this.upwardTrend(data) && data.high[3] == data.high[4]; } - upwardTrend(data) { + + upwardTrend (data:StockData) { // Analyze trends in closing prices of the first three or four candlesticks let gains = averagegain({ values: data.close.slice(0, 3), period: 2 }); let losses = averageloss({ values: data.close.slice(0, 3), period: 2 }); @@ -18,6 +22,7 @@ export default class TweezerTop extends CandlestickFinder { return gains > losses; } } -export function tweezertop(data) { - return new TweezerTop().hasPattern(data); + +export function tweezertop(data:StockData) { + return new TweezerTop().hasPattern(data); } diff --git a/src/chart_types/HeikinAshi.ts b/src/chart_types/HeikinAshi.ts new file mode 100644 index 0000000..73a5f12 --- /dev/null +++ b/src/chart_types/HeikinAshi.ts @@ -0,0 +1,114 @@ +import { CandleData, CandleList } from '../StockData.ts'; +/** + * Created by AAravindan on 5/4/16. + */ +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; + +export class HeikinAshiInput extends IndicatorInput { + low? : number[] + open? : number[] + volume? : number[] + high? : number[] + close? : number[] + timestamp? : number[] +} + +export class HeikinAshi extends Indicator{ + result : CandleList; + generator:IterableIterator; + constructor(input:HeikinAshiInput) { + super(input); + var format = this.format; + this.result = new CandleList(); + + let lastOpen:number = null; + let lastHigh = 0; + let lastLow = Infinity; + let lastClose = 0; + let lastVolume = 0; + let lastTimestamp = 0; + + this.generator = (function* (){ + let candleData = yield; + let calculated = null; + while (true) { + if(lastOpen === null) { + lastOpen = (candleData.close + candleData.open) / 2 ; + lastHigh = candleData.high; + lastLow = candleData.low; + lastClose = (candleData.close + candleData.open + candleData.high + candleData.low) / 4; + lastVolume = (candleData.volume || 0); + lastTimestamp = (candleData.timestamp || 0); + calculated = { + open : lastOpen, + high : lastHigh, + low : lastLow, + close : lastClose, + volume : candleData.volume || 0, + timestamp : (candleData.timestamp || 0) + }; + } else { + let newClose = (candleData.close + candleData.open + candleData.high + candleData.low) / 4 + let newOpen = (lastOpen + lastClose)/ 2; + let newHigh = Math.max(newOpen, newClose, candleData.high); + let newLow = Math.min(candleData.low, newOpen, newClose); + calculated = { + close : newClose, + open : newOpen, + high : newHigh, + low : newLow, + volume : (candleData.volume || 0), + timestamp : (candleData.timestamp || 0) + }; + lastClose = newClose; + lastOpen = newOpen; + lastHigh = newHigh; + lastLow = newLow; + } + candleData = yield calculated; + } + })(); + + this.generator.next(); + input.low.forEach((tick, index) => { + var result = this.generator.next({ + open : input.open[index], + high : input.high[index], + low : input.low[index], + close : input.close[index], + volume : input.volume ? input.volume[index] : input.volume, + timestamp : input.timestamp ? input.timestamp[index] : input.timestamp + }); + if(result.value){ + this.result.open.push(result.value.open) + this.result.high.push(result.value.high) + this.result.low.push(result.value.low) + this.result.close.push(result.value.close) + this.result.volume.push(result.value.volume) + this.result.timestamp.push(result.value.timestamp) + } + }); + } + + static calculate=heikinashi; + + nextValue(price:CandleData):CandleData | undefined { + var result = this.generator.next(price).value; + return result; + }; +} + +export function heikinashi(input:HeikinAshiInput):CandleList { + Indicator.reverseInputs(input); + var result = new HeikinAshi(input).result; + if(input.reversedInput) { + result.open.reverse(); + result.high.reverse(); + result.low.reverse(); + result.close.reverse(); + result.volume.reverse(); + result.timestamp.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; \ No newline at end of file diff --git a/src/chart_types/Renko.ts b/src/chart_types/Renko.ts new file mode 100644 index 0000000..5eaeb51 --- /dev/null +++ b/src/chart_types/Renko.ts @@ -0,0 +1,129 @@ +import { CandleData, CandleList } from '../StockData.ts'; +import { atr } from '../directionalmovement/ATR.ts'; + +/** + * Created by AAravindan on 5/4/16. + */ +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; + +export class RenkoInput extends IndicatorInput { + period? :number; + brickSize? : number + useATR?: boolean + low? : number[] + open? : number[] + volume? : number[] + high? : number[] + close? : number[] + timestamp? : number[] +} + +class Renko extends Indicator{ + result : CandleList; + generator:IterableIterator; + constructor(input:RenkoInput) { + super(input); + var format = this.format; + let useATR = input.useATR; + let brickSize = input.brickSize || 0; + if(useATR) { + let atrResult = atr((Object).assign({}, input)); + brickSize = atrResult[atrResult.length - 1]; + } + this.result = new CandleList();; + if(brickSize === 0) { + console.error('Not enough data to calculate brickSize for renko when using ATR'); + return; + } + let lastOpen = 0; + let lastHigh = 0; + let lastLow = Infinity; + let lastClose = 0; + let lastVolume = 0; + let lastTimestamp = 0; + this.generator = (function* (){ + let candleData = yield; + while (true) { + //Calculating first bar + if(lastOpen === 0) { + lastOpen = candleData.close; + lastHigh = candleData.high; + lastLow = candleData.low; + lastClose = candleData.close; + lastVolume = candleData.volume; + lastTimestamp = candleData.timestamp; + candleData = yield; + continue; + } + let absoluteMovementFromClose = Math.abs(candleData.close - lastClose); + let absoluteMovementFromOpen = Math.abs(candleData.close - lastOpen); + + if((absoluteMovementFromClose >= brickSize) && (absoluteMovementFromOpen >= brickSize)) { + let reference = absoluteMovementFromClose > absoluteMovementFromOpen ? lastOpen : lastClose + let calculated = { + open : reference, + high : lastHigh > candleData.high ? lastHigh : candleData.high, + low : lastLow < candleData.Low ? lastLow : candleData.low, + close : reference > candleData.close ? (reference - brickSize) : (reference + brickSize), + volume : lastVolume + candleData.volume, + timestamp : candleData.timestamp + }; + lastOpen = calculated.open; + lastHigh = calculated.close; + lastLow = calculated.close; + lastClose = calculated.close; + lastVolume = 0; + candleData = yield calculated + } else { + lastHigh = lastHigh > candleData.high ? lastHigh : candleData.high; + lastLow = lastLow < candleData.Low ? lastLow : candleData.low; + lastVolume = lastVolume + candleData.volume; + lastTimestamp = candleData.timestamp; + candleData = yield; + } + } + })(); + + this.generator.next(); + input.low.forEach((tick, index) => { + var result = this.generator.next({ + open : input.open[index], + high : input.high[index], + low : input.low[index], + close : input.close[index], + volume : input.volume[index], + timestamp : input.timestamp[index] + }); + if(result.value){ + this.result.open.push(result.value.open) + this.result.high.push(result.value.high) + this.result.low.push(result.value.low) + this.result.close.push(result.value.close) + this.result.volume.push(result.value.volume) + this.result.timestamp.push(result.value.timestamp) + } + }); + } + + static calculate=renko; + + nextValue(price:number):CandleList | undefined { + console.error('Cannot calculate next value on Renko, Every value has to be recomputed for every change, use calcualte method'); + return null; + }; +} + +export function renko(input:RenkoInput):CandleList { + Indicator.reverseInputs(input); + var result = new Renko(input).result; + if(input.reversedInput) { + result.open.reverse(); + result.high.reverse(); + result.low.reverse(); + result.close.reverse(); + result.volume.reverse(); + result.timestamp.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; \ No newline at end of file diff --git a/src/chart_types/TypicalPrice.ts b/src/chart_types/TypicalPrice.ts new file mode 100644 index 0000000..2904c8a --- /dev/null +++ b/src/chart_types/TypicalPrice.ts @@ -0,0 +1,58 @@ +// @ts-nocheck +import { CandleData, CandleList } from "../StockData.ts"; +import { atr } from "../directionalmovement/ATR.ts"; + +/** + * Created by AAravindan on 5/4/16. + */ +import { Indicator, IndicatorInput } from "../indicator/indicator.ts"; + +export class TypicalPriceInput extends IndicatorInput { + low?: number[]; + high?: number[]; + close?: number[]; +} + +export class TypicalPrice extends Indicator { + result: number[] = []; + generator: IterableIterator; + constructor(input: TypicalPriceInput) { + super(input); + this.generator = (function* () { + let priceInput = yield; + while (true) { + priceInput = yield (priceInput.high + + priceInput.low + + priceInput.close) / + 3; + } + })(); + + this.generator.next(); + input.low.forEach((tick, index) => { + var result = this.generator.next({ + high: input.high[index], + low: input.low[index], + close: input.close[index] + }); + this.result.push(result.value); + }); + } + + static calculate = typicalprice; + + nextValue(price: CandleData): number | undefined { + var result = this.generator.next(price).value; + return result; + } +} + +export function typicalprice(input: TypicalPriceInput): number[] { + Indicator.reverseInputs(input); + var result = new TypicalPrice(input).result; + if (input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; +} diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..65a0088 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,10 @@ +let config:Record = { + +}; +export function setConfig(key:string, value:unknown) { + config[key] = value; +} + +export function getConfig(key:string){ + return config[key]; +} diff --git a/src/directionalmovement/ADX.ts b/src/directionalmovement/ADX.ts new file mode 100644 index 0000000..309a8c2 --- /dev/null +++ b/src/directionalmovement/ADX.ts @@ -0,0 +1,125 @@ +import { WilderSmoothing } from '../moving_averages/WilderSmoothing.ts'; +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { MDM } from './MinusDM.ts'; +import { PDM } from './PlusDM.ts'; +import { TrueRange } from './TrueRange.ts'; +import { SMA } from '../moving_averages/SMA.ts'; +import { WEMA } from '../moving_averages/WEMA.ts'; + +export class ADXInput extends IndicatorInput { + high : number[]; + low :number[]; + close : number[]; + period : number; +}; + +export class ADXOutput extends IndicatorInput { + adx : number; + pdi : number; + mdi : number; +}; + + + +export class ADX extends Indicator { + result : ADXOutput[] + generator:IterableIterator;; + constructor(input:ADXInput) { + super(input); + var lows = input.low; + var highs = input.high; + var closes = input.close; + var period = input.period; + var format = this.format; + + var plusDM = new PDM({ + high: [], + low : [] + }); + + var minusDM = new MDM({ + high: [], + low : [] + }); + + var emaPDM = new WilderSmoothing({period: period, values:[], format : (v) => {return v}}); + var emaMDM = new WilderSmoothing({period: period, values:[], format : (v) => {return v}}); + var emaTR = new WilderSmoothing({period: period, values:[], format : (v) => {return v}}); + var emaDX = new WEMA({period: period, values:[], format : (v) => {return v}}); + + var tr = new TrueRange({ + low : [], + high: [], + close: [], + }); + + if(!((lows.length === highs.length) && (highs.length === closes.length) )){ + throw ('Inputs(low,high, close) not of equal size'); + } + + this.result = []; +ADXOutput + this.generator = (function* (){ + var tick = yield; + var index = 0; + var lastATR,lastAPDM,lastAMDM,lastPDI,lastMDI,lastDX,smoothedDX; + lastATR = 0; + lastAPDM = 0; + lastAMDM = 0; + while (true) { + let calcTr = tr.nextValue(tick); + let calcPDM = plusDM.nextValue(tick); + let calcMDM = minusDM.nextValue(tick); + if(calcTr === undefined){ + tick = yield; + continue; + } + let lastATR = emaTR.nextValue(calcTr) + let lastAPDM = emaPDM.nextValue(calcPDM); + let lastAMDM = emaMDM.nextValue(calcMDM); + if((lastATR != undefined) && (lastAPDM != undefined) && (lastAMDM != undefined)) { + lastPDI = (lastAPDM) * 100 / lastATR; + lastMDI = (lastAMDM) * 100 / lastATR; + let diDiff = Math.abs(lastPDI - lastMDI); + let diSum = (lastPDI + lastMDI); + lastDX = (diDiff / diSum) * 100; + smoothedDX = emaDX.nextValue(lastDX) + // console.log(tick.high.toFixed(2), tick.low.toFixed(2), tick.close.toFixed(2) , calcTr.toFixed(2), calcPDM.toFixed(2), calcMDM.toFixed(2), lastATR.toFixed(2), lastAPDM.toFixed(2), lastAMDM.toFixed(2), lastPDI.toFixed(2), lastMDI.toFixed(2), diDiff.toFixed(2), diSum.toFixed(2), lastDX.toFixed(2)); + } + tick = yield { adx : smoothedDX, pdi : lastPDI, mdi : lastMDI }; + } + })(); + + this.generator.next(); + + lows.forEach((tick,index) => { + var result = this.generator.next({ + high : highs[index], + low : lows[index], + close : closes[index] + }); + if(result.value != undefined && result.value.adx != undefined){ + this.result.push({ adx : format(result.value.adx), pdi : format(result.value.pdi), mdi : format(result.value.mdi) }); + } + }); + }; + + static calculate = adx; + + nextValue(price:number):ADXOutput | undefined { + let result = this.generator.next(price).value; + if(result != undefined && result.adx != undefined) { + return { adx : this.format(result.adx), pdi : this.format(result.pdi), mdi : this.format(result.mdi) }; + } + }; +} + +export function adx(input:ADXInput):ADXOutput[] { + Indicator.reverseInputs(input); + var result = new ADX(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; \ No newline at end of file diff --git a/src/directionalmovement/ATR.ts b/src/directionalmovement/ATR.ts new file mode 100644 index 0000000..b42f3b2 --- /dev/null +++ b/src/directionalmovement/ATR.ts @@ -0,0 +1,93 @@ +// @ts-nocheck +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { CandleData } from '../StockData.ts'; +/** + * Created by AAravindan on 5/8/16. + */ +"use strict" +import { WEMA } from '../moving_averages/WEMA.ts'; +import { TrueRange } from './TrueRange.ts'; + +export class ATRInput extends IndicatorInput { + low:number[] + high:number[] + close:number[] + period:number +}; + +export class ATR extends Indicator { + result : number[]; + generator:IterableIterator; + constructor(input:ATRInput) { + super(input); + var lows = input.low; + var highs = input.high; + var closes = input.close; + var period = input.period; + var format = this.format; + + if(!((lows.length === highs.length) && (highs.length === closes.length) )){ + throw ('Inputs(low,high, close) not of equal size'); + } + + var trueRange = new TrueRange({ + low : [], + high: [], + close: [] + }); + + + var wema = new WEMA({period : period, values : [], format : (v) => {return v}}); + + + this.result = []; + + this.generator = (function* (){ + var tick = yield; + var avgTrueRange,trange;; + while (true) { + trange = trueRange.nextValue({ + low : tick.low, + high : tick.high, + close : tick.close + }); + if(trange === undefined){ + avgTrueRange = undefined; + }else { + avgTrueRange = wema.nextValue(trange); + } + tick = yield avgTrueRange + } + })(); + + this.generator.next(); + + lows.forEach((tick,index) => { + var result = this.generator.next({ + high : highs[index], + low : lows[index], + close : closes[index] + }); + if(result.value !== undefined){ + this.result.push(format(result.value)); + } + }); + }; + + static calculate = atr; + + nextValue(price:CandleData):number | undefined { + return this.generator.next(price).value; + }; +} + + +export function atr(input:ATRInput):number[] { + Indicator.reverseInputs(input); + var result = new ATR(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; +}; \ No newline at end of file diff --git a/src/directionalmovement/MinusDM.ts b/src/directionalmovement/MinusDM.ts new file mode 100644 index 0000000..d044af0 --- /dev/null +++ b/src/directionalmovement/MinusDM.ts @@ -0,0 +1,65 @@ +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +/** + * Created by AAravindan on 5/8/16. + */ +"use strict" + +export class MDMInput extends IndicatorInput { + low:number[] + high:number[] +}; + +export class MDM extends Indicator { + result : number[]; + generator:IterableIterator; + constructor(input:MDMInput) { + super(input); + var lows = input.low + var highs = input.high; + var format = this.format; + + if(lows.length != highs.length) { + throw ('Inputs(low,high) not of equal size'); + } + this.result = []; + this.generator = (function* (){ + var minusDm; + var current = yield; + var last; + while (true) { + if(last){ + let upMove = (current.high - last.high) + let downMove = (last.low - current.low) + minusDm = format((downMove > upMove && downMove > 0) ? downMove : 0); + } + last = current; + current = yield minusDm; + } + })(); + + this.generator.next(); + + lows.forEach((tick, index) => { + var result = this.generator.next({ + high : highs[index], + low : lows[index] + }); + if(result.value !== undefined) + this.result.push(result.value); + }); + }; + + static calculate(input:MDMInput):number[] { + Indicator.reverseInputs(input); + var result = new MDM(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; + + nextValue(price:number):number | undefined { + return this.generator.next(price).value; + }; +} \ No newline at end of file diff --git a/src/directionalmovement/PlusDM.ts b/src/directionalmovement/PlusDM.ts new file mode 100644 index 0000000..3d0c7c5 --- /dev/null +++ b/src/directionalmovement/PlusDM.ts @@ -0,0 +1,66 @@ +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +/** + * Created by AAravindan on 5/8/16. + */ +export class PDMInput extends IndicatorInput { + low:number[] + high:number[] +}; + +export class PDM extends Indicator { + result : number[]; + generator:IterableIterator; + constructor(input:PDMInput) { + super(input); + var lows = input.low + var highs = input.high; + var format = this.format; + + if(lows.length != highs.length) { + throw ('Inputs(low,high) not of equal size'); + } + + this.result = []; + + this.generator = (function* (){ + var plusDm; + var current = yield; + var last; + while (true) { + if(last) { + let upMove = (current.high - last.high) + let downMove = (last.low - current.low) + plusDm = format((upMove > downMove && upMove > 0) ? upMove : 0); + } + last = current; + current = yield plusDm; + } + })(); + + this.generator.next(); + + lows.forEach((tick, index) => { + var result = this.generator.next({ + high : highs[index], + low : lows[index] + }); + if(result.value !== undefined) + this.result.push(result.value); + }); + }; + + static calculate(input:PDMInput):number[] { + Indicator.reverseInputs(input); + var result = new PDM(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; + + nextValue(price:number):number | undefined { + return this.generator.next(price).value; + }; + +} \ No newline at end of file diff --git a/src/directionalmovement/TrueRange.ts b/src/directionalmovement/TrueRange.ts new file mode 100644 index 0000000..0090fb9 --- /dev/null +++ b/src/directionalmovement/TrueRange.ts @@ -0,0 +1,87 @@ +// @ts-nocheck +import { CandleData } from "../StockData.ts"; +import { Indicator, IndicatorInput } from "../indicator/indicator.ts"; +/** + * Created by AAravindan on 5/8/16. + */ +/** + * Created by AAravindan on 5/8/16. + */ +("use strict"); +export class TrueRangeInput extends IndicatorInput { + low: number[]; + high: number[]; + close: number[]; +} + +export class TrueRange extends Indicator { + result: number[]; + generator: IterableIterator; + constructor(input: TrueRangeInput) { + super(input); + var lows = input.low; + var highs = input.high; + var closes = input.close; + var format = this.format; + + if (lows.length != highs.length) { + throw "Inputs(low,high) not of equal size"; + } + + this.result = []; + + this.generator = (function* (): IterableIterator { + var current: CandleData = yield; + var previousClose, result; + while (true) { + if (previousClose === undefined) { + previousClose = current.close; + current = yield result; + } + result = Math.max( + current.high - current.low, + isNaN(Math.abs(current.high - previousClose)) + ? 0 + : Math.abs(current.high - previousClose), + isNaN(Math.abs(current.low - previousClose)) + ? 0 + : Math.abs(current.low - previousClose) + ); + previousClose = current.close; + if (result != undefined) { + result = format(result); + } + current = yield result; + } + })(); + + this.generator.next(); + + lows.forEach((tick, index) => { + var result = this.generator.next({ + high: highs[index], + low: lows[index], + close: closes[index] + }); + if (result.value != undefined) { + this.result.push(result.value); + } + }); + } + + static calculate = truerange; + + nextValue(price: CandleData): number | undefined { + return this.generator.next(price).value; + } +} + +export function truerange(input: TrueRangeInput): number[] { + Indicator.reverseInputs(input); + var result = new TrueRange(input).result; + if (input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; +} diff --git a/lib/drawingtools/fibonacci.js b/src/drawingtools/fibonacci.ts similarity index 56% rename from lib/drawingtools/fibonacci.js rename to src/drawingtools/fibonacci.ts index ae3309d..4fbb02a 100644 --- a/lib/drawingtools/fibonacci.js +++ b/src/drawingtools/fibonacci.ts @@ -1,29 +1,28 @@ /** * Calcaultes the fibonacci retracements for given start and end points - * + * * If calculating for up trend start should be low and end should be high and vice versa - * - * returns an array of retracements level containing [0 , 23.6, 38.2, 50, 61.8, 78.6, 100, 127.2, 161.8, 261.8, 423.6] - * + * + * returns an array of retracements level containing [0 , 23.6, 38.2, 50, 61.8, 78.6, 100, 127.2, 161.8, 261.8, 423.6] + * * @export * @param {number} start * @param {number} end * @returns {number[]} */ -export function fibonacciretracement(start, end) { - let levels = [0, 23.6, 38.2, 50, 61.8, 78.6, 100, 127.2, 161.8, 261.8, 423.6]; - let retracements; - if (start < end) { - retracements = levels.map(function (level) { - let calculated = end - Math.abs(start - end) * (level) / 100; +export function fibonacciretracement(start:number, end:number):number[] { + let levels:number[] = [0 , 23.6, 38.2, 50, 61.8, 78.6, 100, 127.2, 161.8, 261.8, 423.6]; + let retracements:number[]; + if(start < end) { + retracements = levels.map(function(level) { + let calculated = end - Math.abs(start - end) * (level)/ 100; return calculated > 0 ? calculated : 0; - }); - } - else { - retracements = levels.map(function (level) { - let calculated = end + Math.abs(start - end) * (level) / 100; + }) + } else { + retracements = levels.map(function(level) { + let calculated = end + Math.abs(start - end) * (level)/ 100; return calculated > 0 ? calculated : 0; - }); + }) } return retracements; -} +} \ No newline at end of file diff --git a/src/ichimoku/IchimokuCloud.ts b/src/ichimoku/IchimokuCloud.ts new file mode 100644 index 0000000..24b9b4d --- /dev/null +++ b/src/ichimoku/IchimokuCloud.ts @@ -0,0 +1,121 @@ +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { CandleData } from '../StockData.ts'; +import LinkedList from '../Utils/FixedSizeLinkedList.ts'; + +export class IchimokuCloudInput extends IndicatorInput { + high : number[] + low : number[] + conversionPeriod:number = 9 + basePeriod:number = 26 + spanPeriod:number = 52 + displacement:number = 26 +} + +export class IchimokuCloudOutput { + conversion : number + base : number + spanA : number + spanB : number +} + +export class IchimokuCloud extends Indicator{ + result : IchimokuCloudOutput[]; + generator:IterableIterator; + constructor(input:IchimokuCloudInput) { + super(input); + this.result = []; + var defaults = { + conversionPeriod : 9, + basePeriod : 26, + spanPeriod : 52, + displacement : 26 + } + + var params = (Object as any).assign({}, defaults, input) + + var currentConversionData = new LinkedList(params.conversionPeriod * 2, true, true, false); + var currentBaseData = new LinkedList(params.basePeriod * 2, true, true, false); + var currenSpanData = new LinkedList(params.spanPeriod * 2, true, true, false); + + this.generator = (function* () { + let result:IchimokuCloudOutput + let tick:CandleData; + + let period = Math.max(params.conversionPeriod,params.basePeriod,params.spanPeriod, params.displacement) + let periodCounter = 1 + + tick = yield + while (true) { + // Keep a list of lows/highs for the max period + currentConversionData.push(tick.high) + currentConversionData.push(tick.low) + currentBaseData.push(tick.high) + currentBaseData.push(tick.low) + currenSpanData.push(tick.high) + currenSpanData.push(tick.low) + + if(periodCounter < period) { + periodCounter++ + } else { + // Tenkan-sen (ConversionLine): (9-period high + 9-period low)/2)) + let conversionLine = (currentConversionData.periodHigh + currentConversionData.periodLow) /2 + + // Kijun-sen (Base Line): (26-period high + 26-period low)/2)) + let baseLine = (currentBaseData.periodHigh + currentBaseData.periodLow) /2 + + // Senkou Span A (Leading Span A): (Conversion Line + Base Line)/2)) + let spanA = (conversionLine + baseLine) /2; + + // Senkou Span B (Leading Span B): (52-period high + 52-period low)/2)) + let spanB = (currenSpanData.periodHigh + currenSpanData.periodLow) /2 + + // Senkou Span A / Senkou Span B offset by 26 periods + // if(spanCounter < params.displacement) { + // spanCounter++ + // } else { + // spanA = spanAs.shift() + // spanB = spanBs.shift() + // } + + result = { + conversion : conversionLine, + base : baseLine, + spanA : spanA, + spanB : spanB + } + } + + tick = yield result + } + })() + + this.generator.next(); + input.low.forEach((tick, index) => { + var result = this.generator.next({ + high : input.high[index], + low : input.low[index], + }); + if(result.value){ + this.result.push(result.value) + } + }); + + } + + static calculate = ichimokucloud + + nextValue(price:CandleData):IchimokuCloudOutput { + return this.generator.next(price).value; + } + +} + +export function ichimokucloud(input:IchimokuCloudInput):IchimokuCloudOutput[] { + Indicator.reverseInputs(input); + var result = new IchimokuCloud(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..70eb870 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,98 @@ +// import FixedSizeLinkedList from "./Utils/FixedSizeLinkedList"; + +// export { CandleData, CandleList } from "./StockData"; +// export { sma, SMA } from './moving_averages/SMA' +// export { ema, EMA } from './moving_averages/EMA'; +// export { wma, WMA } from './moving_averages/WMA'; +// export { wema, WEMA } from './moving_averages/WEMA'; +// export { macd, MACD } from './moving_averages/MACD'; +export { rsi, RSI } from './oscillators/RSI.ts'; +export { bollingerbands, BollingerBands } from './volatility/BollingerBands.ts'; +// export { adx, ADX } from './directionalmovement/ADX'; +// // export { atr, ATR } from './directionalmovement/ATR'; +// export { truerange, TrueRange } from './directionalmovement/TrueRange'; +// export { roc, ROC } from './momentum/ROC'; +// export { kst, KST } from './momentum/KST'; +// export { psar, PSAR } from './momentum/PSAR'; +// export { stochastic, Stochastic } from './momentum/Stochastic'; +// export { williamsr, WilliamsR } from './momentum/WilliamsR'; +// export { adl, ADL } from './volume/ADL'; +// export { obv, OBV } from './volume/OBV'; +// export { trix, TRIX } from './momentum/TRIX'; +// export { forceindex, ForceIndex } from './volume/ForceIndex'; +// export { cci, CCI } from './oscillators/CCI'; +// export { awesomeoscillator, AwesomeOscillator } from './oscillators/AwesomeOscillator'; +// export { vwap, VWAP } from './volume/VWAP'; +// export { volumeprofile, VolumeProfile } from './volume/VolumeProfile'; +export { mfi, MFI } from './volume/MFI.ts'; +// export { stochasticrsi, StochasticRSI } from './momentum/StochasticRSI'; + +// export { averagegain, AverageGain } from './Utils/AverageGain'; +// export { averageloss, AverageLoss } from './Utils/AverageLoss'; +// export { sd, SD } from './Utils/SD'; +// export { highest, Highest } from './Utils/Highest'; +// export { lowest, Lowest } from './Utils/Lowest'; +// export { sum, Sum } from './Utils/Sum'; +// export { FixedSizeLinkedList } + +// export { renko } from './chart_types/Renko'; +// export { HeikinAshi, heikinashi } from './chart_types/HeikinAshi'; + +// // export { bullish } from './candlestick/Bullish'; +// // export { bearish } from './candlestick/Bearish'; +// // export { abandonedbaby } from './candlestick/AbandonedBaby'; +// // export { doji } from './candlestick/Doji'; +// // export { bearishengulfingpattern } from './candlestick/BearishEngulfingPattern'; +// // export { bullishengulfingpattern } from './candlestick/BullishEngulfingPattern'; +// // export { darkcloudcover } from './candlestick/DarkCloudCover'; +// // export { downsidetasukigap } from './candlestick/DownsideTasukiGap'; +// // export { dragonflydoji } from './candlestick/DragonFlyDoji'; +// // export { gravestonedoji } from './candlestick/GraveStoneDoji'; +// // export { bullishharami } from './candlestick/BullishHarami'; +// // export { bearishharami } from './candlestick/BearishHarami'; +// // export { bullishharamicross } from './candlestick/BullishHaramiCross'; +// // export { bearishharamicross } from './candlestick/BearishHaramiCross'; +// export { eveningdojistar } from './candlestick/EveningDojiStar'; +// export { eveningstar } from './candlestick/EveningStar'; +// export { morningdojistar } from './candlestick/MorningDojiStar'; +// export { morningstar } from './candlestick/MorningStar'; +// export { bullishmarubozu } from './candlestick/BullishMarubozu'; +// export { bearishmarubozu } from './candlestick/BearishMarubozu'; +// export { piercingline } from './candlestick/PiercingLine'; +// export { bullishspinningtop } from './candlestick/BullishSpinningTop'; +// export { bearishspinningtop } from './candlestick/BearishSpinningTop'; +// export { threeblackcrows } from './candlestick/ThreeBlackCrows'; +// export { threewhitesoldiers } from './candlestick/ThreeWhiteSoldiers'; + +// export { bullishhammerstick } from './candlestick/BullishHammerStick'; +// export { bearishhammerstick } from './candlestick/BearishHammerStick'; +// export { bullishinvertedhammerstick } from './candlestick/BullishInvertedHammerStick'; +// export { bearishinvertedhammerstick } from './candlestick/BearishInvertedHammerStick'; +// export { hammerpattern } from './candlestick/HammerPattern'; +// export { hammerpatternunconfirmed } from './candlestick/HammerPatternUnconfirmed'; +// export { hangingman } from './candlestick/HangingMan'; +// export { hangingmanunconfirmed } from './candlestick/HangingManUnconfirmed'; +// export { shootingstar } from './candlestick/ShootingStar'; +// export { shootingstarunconfirmed } from './candlestick/ShootingStarUnconfirmed'; +// export { tweezertop } from './candlestick/TweezerTop'; +// export { tweezerbottom } from './candlestick/TweezerBottom'; + +// export { fibonacciretracement} from './drawingtools/fibonacci'; + +// export { predictPattern, PatternDetector } from './patterndetection/patterndetection'; +// export { AvailablePatterns } from './patterndetection/patterndetection'; +// export { hasDoubleBottom} from './patterndetection/patterndetection'; +// export { hasDoubleTop } from './patterndetection/patterndetection'; +// export { hasHeadAndShoulder} from './patterndetection/patterndetection'; +// export { hasInverseHeadAndShoulder } from './patterndetection/patterndetection'; +// export { isTrendingUp} from './patterndetection/patterndetection'; +// export { isTrendingDown } from './patterndetection/patterndetection'; + +// export { ichimokucloud, IchimokuCloud } from './ichimoku/IchimokuCloud'; + +// export { keltnerchannels, KeltnerChannels, KeltnerChannelsInput, KeltnerChannelsOutput } from './volatility/KeltnerChannels'; +// export { chandelierexit, ChandelierExit, ChandelierExitInput, ChandelierExitOutput } from './volatility/ChandelierExit'; +// export { crossUp, CrossUp } from './Utils/CrossUp'; +// export { crossDown, CrossDown } from './Utils/CrossDown'; + +// export { setConfig, getConfig } from './config' diff --git a/lib/indicator/indicator.ts b/src/indicator/indicator.ts similarity index 63% rename from lib/indicator/indicator.ts rename to src/indicator/indicator.ts index c7264e2..f5efedf 100644 --- a/lib/indicator/indicator.ts +++ b/src/indicator/indicator.ts @@ -1,16 +1,28 @@ import { format as nf } from '../Utils/NumberFormatter.ts'; + export class IndicatorInput { + reversedInput?:boolean; + format?:(data:number)=>number } + export class AllInputs { + values?:number[] + open?:number[] + high?:number[] + low?:number[] + close?:number[] + volume?:number[] + timestamp?: number[] } + export class Indicator { - format: any; - result: any; - constructor(input: { format: (v: any) => any; }) { + result:any; + format:(data:number)=>number; + constructor(input:IndicatorInput) { this.format = input.format || nf; } - static reverseInputs(input: { reversedInput: any; values: any[]; open: any[]; high: any[]; low: any[]; close: any[]; volume: any[]; timestamp: any[]; }) { - if (input.reversedInput) { + static reverseInputs(input:any):void { + if(input.reversedInput) { input.values ? input.values.reverse() : undefined; input.open ? input.open.reverse() : undefined; input.high ? input.high.reverse() : undefined; @@ -20,6 +32,7 @@ export class Indicator { input.timestamp ? input.timestamp.reverse() : undefined; } } + getResult() { return this.result; } diff --git a/src/momentum/KST.ts b/src/momentum/KST.ts new file mode 100644 index 0000000..6102d62 --- /dev/null +++ b/src/momentum/KST.ts @@ -0,0 +1,111 @@ +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { SMA } from '../moving_averages/SMA.ts'; +import { ROC } from './ROC.ts'; + +export class KSTInput extends IndicatorInput { + ROCPer1 : number; + ROCPer2 : number; + ROCPer3 : number; + ROCPer4 : number; + SMAROCPer1 : number; + SMAROCPer2 : number; + SMAROCPer3 : number; + SMAROCPer4 : number; + signalPeriod: number; + values : number[]; +} + +export class KSTOutput { + kst:number + signal:number +} + +export class KST extends Indicator { + result : KSTOutput[]; + generator:IterableIterator; + constructor(input:KSTInput) { + super(input); + let priceArray = input.values; + let rocPer1 = input.ROCPer1; + let rocPer2 = input.ROCPer2; + let rocPer3 = input.ROCPer3; + let rocPer4 = input.ROCPer4; + + let smaPer1 = input.SMAROCPer1; + let smaPer2 = input.SMAROCPer2; + let smaPer3 = input.SMAROCPer3; + let smaPer4 = input.SMAROCPer4; + + let signalPeriod= input.signalPeriod; + + let roc1 = new ROC({ period : rocPer1, values: []}); + let roc2 = new ROC({ period : rocPer2, values: []}); + let roc3 = new ROC({ period : rocPer3, values: []}); + let roc4 = new ROC({ period : rocPer4, values: []}); + + let sma1 = new SMA({ period : smaPer1, values: [], format : (v) => {return v}}); + let sma2 = new SMA({ period : smaPer2, values: [], format : (v) => {return v}}); + let sma3 = new SMA({ period : smaPer3, values: [], format : (v) => {return v}}); + let sma4 = new SMA({ period : smaPer4, values: [], format : (v) => {return v}}); + let signalSMA = new SMA({ period : signalPeriod, values: [], format : (v) => {return v}}) + var format = this.format; + this.result = []; + + let firstResult = Math.max(rocPer1 + smaPer1, rocPer2+smaPer2, rocPer3+smaPer3, rocPer4+smaPer4); + this.generator = (function* ():IterableIterator{ + let index = 1; + let tick = yield; + let kst; + let RCMA1,RCMA2,RCMA3,RCMA4,signal,result; + while (true) { + let roc1Result = roc1.nextValue(tick); + let roc2Result = roc2.nextValue(tick); + let roc3Result = roc3.nextValue(tick); + let roc4Result = roc4.nextValue(tick); + RCMA1 = (roc1Result!==undefined) ? sma1.nextValue(roc1Result) : undefined; + RCMA2 = (roc2Result!==undefined) ? sma2.nextValue(roc2Result) : undefined; + RCMA3 = (roc3Result!==undefined) ? sma3.nextValue(roc3Result) : undefined; + RCMA4 = (roc4Result!==undefined) ? sma4.nextValue(roc4Result) : undefined; + if(index < firstResult){ + index++; + }else { + kst = (RCMA1 * 1) + (RCMA2 * 2) + (RCMA3 * 3) + (RCMA4 * 4) + } + signal = (kst!==undefined) ? signalSMA.nextValue(kst) : undefined; + result = kst!==undefined ? { + kst : format(kst), + signal : signal ? format(signal) : undefined + } : undefined; + tick = yield result; + } + })(); + + this.generator.next(); + + priceArray.forEach((tick) => { + let result = this.generator.next(tick); + if(result.value != undefined){ + this.result.push(result.value); + } + }); + }; + + static calculate = kst; + + + nextValue (price:number) { + let nextResult = this.generator.next(price); + if(nextResult.value != undefined) + return nextResult.value; + }; +} + +export function kst(input:KSTInput):KSTOutput[] { + Indicator.reverseInputs(input); + var result = new KST(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; \ No newline at end of file diff --git a/src/momentum/PSAR.ts b/src/momentum/PSAR.ts new file mode 100644 index 0000000..508a463 --- /dev/null +++ b/src/momentum/PSAR.ts @@ -0,0 +1,128 @@ +import { IndicatorInput, Indicator } from '../indicator/indicator.ts'; + +"use strict" + +/* + There seems to be a few interpretations of the rules for this regarding which prices. + I mean the english from which periods are included. The wording does seem to + introduce some discrepancy so maybe that is why. I want to put the author's + own description here to reassess this later. + ---------------------------------------------------------------------------------------- + For the first day of entry the SAR is the previous Significant Point + + If long the SP is the lowest price reached while in the previous short trade + If short the SP is the highest price reached while in the previous long trade + + If long: + Find the difference between the highest price made while in the trade and the SAR for today. + Multiple the difference by the AF and ADD the result to today's SAR to obtain the SAR for tomorrow. + Use 0.02 for the first AF and increase it by 0.02 on every day that a new high for the trade is made. + If a new high is not made continue to use the AF as last increased. Do not increase the AF above .20 + + Never move the SAR for tomorrow ABOVE the previous day's LOW or today's LOW. + If the SAR is calculated to be ABOVE the previous day's LOW or today's LOW then use the lower low between today and the previous day as the new SAR. + Make the next day's calculations based on this SAR. + + If short: + Find the difference between the lowest price made while in the trade and the SAR for today. + Multiple the difference by the AF and SUBTRACT the result to today's SAR to obtain the SAR for tomorrow. + Use 0.02 for the first AF and increase it by 0.02 on every day that a new high for the trade is made. + If a new high is not made continue to use the AF as last increased. Do not increase the AF above .20 + + Never move the SAR for tomorrow BELOW the previous day's HIGH or today's HIGH. + If the SAR is calculated to be BELOW the previous day's HIGH or today's HIGH then use the higher high between today and the previous day as the new SAR. Make the next day's calculations based on this SAR. + ---------------------------------------------------------------------------------------- +*/ +export class PSARInput extends IndicatorInput{ + step:number; + max:number; + high:number[]; + low:number[]; +}; + +export class PSAR extends Indicator { + result:number[]; + generator:IterableIterator; + constructor (input:PSARInput) { + super(input); + + let highs = input.high || []; + let lows = input.low || []; + + var genFn = function* (step:number, max:number):IterableIterator { + let curr, extreme, sar, furthest; + + let up = true; + let accel = step; + let prev = yield; + while(true) { + if (curr) { + sar = sar + accel * (extreme - sar); + + if (up) { + sar = Math.min(sar, furthest.low, prev.low); + + if (curr.high > extreme) { + extreme = curr.high; + accel = Math.min(accel + step, max); + }; + } else { + sar = Math.max(sar, furthest.high, prev.high); + + if (curr.low < extreme) { + extreme = curr.low; + accel = Math.min(accel + step, max); + } + } + + if ((up && curr.low < sar) || (!up && curr.high > sar)) { + accel = step; + sar = extreme; + up = !up; + + extreme = !up ? curr.low : curr.high; + } + } else { + // Randomly setup start values? What is the trend on first tick?? + sar = prev.low; extreme = prev.high; + } + + furthest = prev; + if (curr) prev = curr; + curr = yield sar; + } + }; + + this.result = []; + this.generator = genFn(input.step, input.max); + this.generator.next(); + + lows.forEach((tick, index) => { + var result = this.generator.next({ + high: highs[index], + low: lows[index], + }); + if(result.value !== undefined){ + this.result.push(result.value); + } + }); + }; + + static calculate = psar; + + nextValue (input:PSARInput):number { + let nextResult = this.generator.next(input); + if(nextResult.value !== undefined) + return nextResult.value; + }; +} + +export function psar(input:PSARInput):number[] { + Indicator.reverseInputs(input); + var result = new PSAR(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; diff --git a/src/momentum/ROC.ts b/src/momentum/ROC.ts new file mode 100644 index 0000000..4e8122c --- /dev/null +++ b/src/momentum/ROC.ts @@ -0,0 +1,64 @@ +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import LinkedList from '../Utils/FixedSizeLinkedList.ts'; + + +export class ROCInput extends IndicatorInput { + period : number; + values : number[]; +} + +export class ROC extends Indicator { + result : number[]; + generator:IterableIterator; + constructor(input:ROCInput) { + super(input); + var period = input.period + var priceArray = input.values; + this.result = []; + this.generator = (function* (){ + let index = 1; + var pastPeriods = new LinkedList(period);; + var tick = yield; + var roc; + while (true) { + pastPeriods.push(tick) + if(index < period){ + index++; + }else { + roc = ((tick - pastPeriods.lastShift) / (pastPeriods.lastShift)) * 100 + } + tick = yield roc; + } + })(); + + this.generator.next(); + + priceArray.forEach((tick) => { + var result = this.generator.next(tick); + if(result.value != undefined && (!isNaN(result.value))){ + this.result.push(this.format(result.value)); + } + }); + } + + static calculate = roc; + + nextValue(price:number):number | undefined { + var nextResult = this.generator.next(price); + if(nextResult.value != undefined && (!isNaN(nextResult.value))) { + return this.format(nextResult.value); + } + }; + +}; + + +export function roc(input:ROCInput):number[] { + Indicator.reverseInputs(input); + var result = new ROC(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; \ No newline at end of file diff --git a/src/momentum/Stochastic.ts b/src/momentum/Stochastic.ts new file mode 100644 index 0000000..1d95747 --- /dev/null +++ b/src/momentum/Stochastic.ts @@ -0,0 +1,105 @@ +import { IndicatorInput, Indicator } from '../indicator/indicator.ts'; +/** + * Created by AAravindan on 5/10/16. + */ +"use strict" + +import LinkedList from '../Utils/FixedSizeLinkedList.ts'; +import { SMA } from '../moving_averages/SMA.ts'; + +export class StochasticInput extends IndicatorInput{ + period:number; + low:number[]; + high:number[]; + close:number[]; + signalPeriod:number; +}; + +export class StochasticOutput{ + k:number; + d:number; +}; + +export class Stochastic extends Indicator { + result:StochasticOutput[] + generator:IterableIterator; + constructor (input:StochasticInput) { + super(input); + let lows = input.low; + let highs = input.high; + let closes = input.close; + let period = input.period; + let signalPeriod = input.signalPeriod; + let format = this.format; + if(!((lows.length === highs.length) && (highs.length === closes.length) )){ + throw ('Inputs(low,high, close) not of equal size'); + } + this.result = []; + //%K = (Current Close - Lowest Low)/(Highest High - Lowest Low) * 100 + //%D = 3-day SMA of %K + // + //Lowest Low = lowest low for the look-back period + //Highest High = highest high for the look-back period + //%K is multiplied by 100 to move the decimal point two places + this.generator = (function* (){ + let index = 1; + let pastHighPeriods = new LinkedList(period, true, false); + let pastLowPeriods = new LinkedList(period, false, true); + let dSma = new SMA({ + period : signalPeriod, + values : [], + format : (v) => {return v} + }); + let k,d; + var tick = yield; + while (true) { + pastHighPeriods.push(tick.high); + pastLowPeriods.push(tick.low); + if(index < period){ + index++; + tick = yield; + continue; + } + let periodLow = pastLowPeriods.periodLow; + k = (tick.close - periodLow) / (pastHighPeriods.periodHigh - periodLow) * 100; + k = isNaN(k) ? 0 : k; //This happens when the close, high and low are same for the entire period; Bug fix for + d = dSma.nextValue(k); + tick = yield { + k : format(k), + d : (d !== undefined) ? format(d) : undefined + } + } + })(); + + this.generator.next(); + + lows.forEach((tick, index) => { + var result = this.generator.next({ + high : highs[index], + low : lows[index], + close : closes[index] + }); + if(result.value !== undefined){ + this.result.push(result.value); + } + }); + }; + + static calculate = stochastic + + nextValue (input:StochasticInput):StochasticOutput { + let nextResult = this.generator.next(input); + if(nextResult.value !== undefined) + return nextResult.value; + }; +} + +export function stochastic(input:StochasticInput):StochasticOutput[] { + Indicator.reverseInputs(input); + var result = new Stochastic(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; \ No newline at end of file diff --git a/src/momentum/StochasticRSI.ts b/src/momentum/StochasticRSI.ts new file mode 100644 index 0000000..e199252 --- /dev/null +++ b/src/momentum/StochasticRSI.ts @@ -0,0 +1,95 @@ +import { IndicatorInput, Indicator } from '../indicator/indicator.ts'; +/** + * Created by AAravindan on 5/10/16. + */ +"use strict" + +import LinkedList from '../Utils/FixedSizeLinkedList.ts'; +import { SMA } from '../moving_averages/SMA.ts'; +import { RSI } from '../oscillators/RSI.ts'; +import { Stochastic } from '../momentum/Stochastic.ts'; + +export class StochasticRsiInput extends IndicatorInput{ + values : number[]; + rsiPeriod:number; + stochasticPeriod:number; + kPeriod:number; + dPeriod:number; +}; + +export class StochasticRSIOutput{ + stochRSI : number + k:number; + d:number; +}; + +export class StochasticRSI extends Indicator { + result:StochasticRSIOutput[] + generator:IterableIterator; + constructor (input:StochasticRsiInput) { + super(input); + let closes = input.values; + let rsiPeriod = input.rsiPeriod; + let stochasticPeriod = input.stochasticPeriod; + let kPeriod = input.kPeriod; + let dPeriod = input.dPeriod; + let format = this.format; + this.result = []; + this.generator = (function* (){ + let index = 1; + let rsi = new RSI({ period : rsiPeriod, values : []}); + let stochastic = new Stochastic({ period : stochasticPeriod, high : [], low: [], close: [], signalPeriod : kPeriod}); + let dSma = new SMA({ + period : dPeriod, + values : [], + format : (v) => {return v} + }); + let lastRSI, stochasticRSI, d, result; + var tick = yield; + while (true) { + lastRSI = rsi.nextValue(tick); + if(lastRSI !== undefined) { + var stochasticInput = { high : lastRSI, low : lastRSI, close: lastRSI } as any; + stochasticRSI = stochastic.nextValue(stochasticInput); + if(stochasticRSI !== undefined && stochasticRSI.d !== undefined) { + d = dSma.nextValue(stochasticRSI.d); + if(d !== undefined) + result = { + stochRSI : stochasticRSI.k, + k: stochasticRSI.d, + d : d + } + } + } + tick = yield result; + } + })(); + + this.generator.next(); + + closes.forEach((tick, index) => { + var result = this.generator.next(tick); + if(result.value !== undefined){ + this.result.push(result.value); + } + }); + }; + + static calculate = stochasticrsi + + nextValue (input:StochasticRsiInput):StochasticRSIOutput { + let nextResult = this.generator.next(input); + if(nextResult.value !== undefined) + return nextResult.value; + }; +} + +export function stochasticrsi(input:StochasticRsiInput):StochasticRSIOutput[] { + Indicator.reverseInputs(input); + var result = new StochasticRSI(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; +}; \ No newline at end of file diff --git a/src/momentum/TRIX.ts b/src/momentum/TRIX.ts new file mode 100644 index 0000000..f51365b --- /dev/null +++ b/src/momentum/TRIX.ts @@ -0,0 +1,69 @@ +/** + * Created by AAravindan on 5/9/16. + */ +"use strict" + +import { ROC } from './ROC.ts'; +import { EMA } from '../moving_averages/EMA.ts'; +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; + +export class TRIXInput extends IndicatorInput{ + values:number[]; + period:number; +}; + +export class TRIX extends Indicator { + result : number[]; + generator:IterableIterator; + constructor(input:TRIXInput) { + super(input); + let priceArray = input.values; + let period = input.period; + let format = this.format; + + let ema = new EMA({ period : period, values : [], format : (v) => {return v}}); + let emaOfema = new EMA({ period : period, values : [], format : (v) => {return v}}); + let emaOfemaOfema = new EMA({ period : period, values : [], format : (v) => {return v}}); + let trixROC = new ROC({ period : 1, values : [], format : (v) => {return v}}); + + this.result = []; + + this.generator = (function* ():IterableIterator< number | undefined>{ + let tick = yield; + while (true) { + let initialema = ema.nextValue(tick); + let smoothedResult = initialema ? emaOfema.nextValue(initialema) : undefined; + let doubleSmoothedResult = smoothedResult ? emaOfemaOfema.nextValue(smoothedResult) : undefined; + let result = doubleSmoothedResult ? trixROC.nextValue(doubleSmoothedResult) : undefined; + tick = yield result ? format(result) : undefined; + } + })(); + + this.generator.next(); + + priceArray.forEach((tick) => { + let result = this.generator.next(tick); + if(result.value !== undefined){ + this.result.push(result.value); + } + }); + } + + static calculate=trix; + + nextValue(price:number) { + let nextResult = this.generator.next(price); + if(nextResult.value !== undefined) + return nextResult.value; + }; +} + +export function trix(input:TRIXInput):number[] { + Indicator.reverseInputs(input); + var result = new TRIX(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; +}; diff --git a/src/momentum/WilliamsR.ts b/src/momentum/WilliamsR.ts new file mode 100644 index 0000000..ea3ce7f --- /dev/null +++ b/src/momentum/WilliamsR.ts @@ -0,0 +1,86 @@ +import { IndicatorInput, Indicator } from '../indicator/indicator.ts'; + +import LinkedList from '../Utils/FixedSizeLinkedList.ts'; + +export class WilliamsRInput extends IndicatorInput { + low:number[]; + high:number[]; + close:number[]; + period:number; +}; + +export class WilliamsR extends Indicator { + result : number[]; + generator:IterableIterator; + constructor(input:WilliamsRInput) { + super(input); + let lows = input.low; + let highs = input.high; + let closes = input.close; + let period = input.period; + let format = this.format; + + if(!((lows.length === highs.length) && (highs.length === closes.length) )){ + throw ('Inputs(low,high, close) not of equal size'); + } + this.result = []; + + //%R = (Highest High - Close)/(Highest High - Lowest Low) * -100 + //Lowest Low = lowest low for the look-back period + //Highest High = highest high for the look-back period + //%R is multiplied by -100 correct the inversion and move the decimal. + this.generator = (function* ():IterableIterator{ + let index = 1; + let pastHighPeriods = new LinkedList(period, true, false); + let pastLowPeriods = new LinkedList(period, false, true); + let periodLow; + let periodHigh; + var tick = yield; + let williamsR; + while (true) { + pastHighPeriods.push(tick.high); + pastLowPeriods.push(tick.low); + if(index < period){ + index++; + tick = yield; + continue; + } + periodLow = pastLowPeriods.periodLow; + periodHigh= pastHighPeriods.periodHigh; + williamsR = format((periodHigh - tick.close) / (periodHigh- periodLow) * -100); + tick = yield williamsR; + } + })(); + + this.generator.next(); + + lows.forEach((low, index) => { + var result = this.generator.next({ + high : highs[index], + low : lows[index], + close : closes[index] + }); + if(result.value !== undefined){ + this.result.push(result.value); + } + }); + }; + + static calculate = williamsr; + + nextValue(price:number):number | undefined { + var nextResult = this.generator.next(price); + if(nextResult.value != undefined) + return this.format(nextResult.value); + }; +} + +export function williamsr(input:WilliamsRInput):number[] { + Indicator.reverseInputs(input); + var result = new WilliamsR(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; \ No newline at end of file diff --git a/src/moving_averages/EMA.ts b/src/moving_averages/EMA.ts new file mode 100644 index 0000000..51dc06f --- /dev/null +++ b/src/moving_averages/EMA.ts @@ -0,0 +1,67 @@ +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { MAInput, SMA } from './SMA.ts'; +import { LinkedList } from '../Utils/LinkedList.ts'; + +export class EMA extends Indicator{ + period:number; + price:number[]; + result : number[]; + generator:IterableIterator; + constructor(input:MAInput) { + super(input); + var period = input.period + var priceArray = input.values; + var exponent = (2 / (period + 1)); + var sma:SMA; + + this.result = []; + + sma = new SMA({period : period, values :[]}); + + var genFn = (function* ():IterableIterator{ + var tick = yield; + var prevEma; + while (true) { + if(prevEma !== undefined && tick !== undefined){ + prevEma = ((tick - prevEma) * exponent) + prevEma; + tick = yield prevEma; + }else { + tick = yield; + prevEma = sma.nextValue(tick) + if(prevEma) + tick = yield prevEma; + } + } + }); + + this.generator = genFn(); + + this.generator.next(); + this.generator.next(); + + priceArray.forEach((tick) => { + var result = this.generator.next(tick); + if(result.value != undefined){ + this.result.push(this.format(result.value)); + } + }); + } + + static calculate = ema; + + nextValue(price:number) { + var result = this.generator.next(price).value; + if(result != undefined) + return this.format(result); + }; +} + +export function ema(input:MAInput):number[] { + Indicator.reverseInputs(input); + var result = new EMA(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + } diff --git a/src/moving_averages/MACD.ts b/src/moving_averages/MACD.ts new file mode 100644 index 0000000..8216730 --- /dev/null +++ b/src/moving_averages/MACD.ts @@ -0,0 +1,93 @@ +/** + * Created by AAravindan on 5/4/16. + */ +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { SMA } from './SMA.ts'; +import { EMA } from './EMA.ts'; + +export class MACDInput extends IndicatorInput { + SimpleMAOscillator:boolean = true; + SimpleMASignal:boolean = true; + fastPeriod:number; + slowPeriod:number; + signalPeriod:number; + constructor(public values:number[]) { + super(); + } +} + +export class MACDOutput { + MACD?:number + signal?:number + histogram?:number +} + +export class MACD extends Indicator{ + result : MACDOutput[]; + generator:IterableIterator; + constructor(input:MACDInput) { + super(input); + var oscillatorMAtype = input.SimpleMAOscillator ? SMA : EMA; + var signalMAtype = input.SimpleMASignal ? SMA : EMA; + var fastMAProducer = new oscillatorMAtype({period : input.fastPeriod, values : [], format : (v) => {return v}}); + var slowMAProducer = new oscillatorMAtype({period : input.slowPeriod, values : [], format : (v) => {return v}}); + var signalMAProducer = new signalMAtype({period : input.signalPeriod, values : [], format : (v) => {return v}}); + var format = this.format; + this.result = []; + + this.generator = (function* (){ + var index = 0; + var tick; + var MACD:number|undefined, signal:number|undefined, histogram:number|undefined, fast:number|undefined, slow:number|undefined; + while (true) { + if(index < input.slowPeriod){ + tick = yield; + fast = fastMAProducer.nextValue(tick); + slow = slowMAProducer.nextValue(tick); + index++; + continue; + } + if(fast && slow) { //Just for typescript to be happy + MACD = fast - slow; + signal = signalMAProducer.nextValue(MACD); + } + histogram = MACD - signal; + tick = yield({ + //fast : fast, + //slow : slow, + MACD : format(MACD), + signal : signal ? format(signal) : undefined, + histogram : isNaN(histogram) ? undefined : format(histogram) + }) + fast = fastMAProducer.nextValue(tick); + slow = slowMAProducer.nextValue(tick); + } + })(); + + this.generator.next(); + + input.values.forEach((tick) => { + var result = this.generator.next(tick); + if(result.value != undefined){ + this.result.push(result.value); + } + }); + } + + static calculate=macd; + + nextValue(price:number):MACDOutput | undefined { + var result = this.generator.next(price).value; + return result; + }; +} + +export function macd(input:MACDInput):MACDOutput[] { + Indicator.reverseInputs(input); + var result = new MACD(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; \ No newline at end of file diff --git a/src/moving_averages/SMA.ts b/src/moving_averages/SMA.ts new file mode 100644 index 0000000..cfcfb5f --- /dev/null +++ b/src/moving_averages/SMA.ts @@ -0,0 +1,77 @@ +//STEP 1. Import Necessary indicator or rather last step +import { Indicator, IndicatorInput } from "../indicator/indicator.ts"; +import { LinkedList } from "../Utils/LinkedList.ts"; + +//STEP 2. Create the input for the indicator, mandatory should be in the constructor +export class MAInput extends IndicatorInput { + constructor(public period: number, public values: number[]) { + super(); + } +} + +//STEP3. Add class based syntax with export +export class SMA extends Indicator { + period: number; + price: number[]; + result: number[]; + generator: IterableIterator; + constructor(input: MAInput) { + super(input); + this.period = input.period; + this.price = input.values; + var genFn = function* ( + period: number + ): IterableIterator { + var list = new LinkedList(); + var sum = 0; + var counter = 1; + var current = yield; + var result; + list.push(0); + while (true) { + if (counter < period) { + counter++; + list.push(current); + // @ts-ignore + sum = sum + current; + } else { + // @ts-ignore + sum = sum - list.shift() + current; + result = sum / period; + list.push(current); + } + current = yield result; + } + }; + this.generator = genFn(this.period); + this.generator.next(); + this.result = []; + this.price.forEach(tick => { + // @ts-ignore + var result = this.generator.next(tick); + if (result.value !== undefined) { + this.result.push(this.format(result.value)); + } + }); + } + + static calculate = sma; + + nextValue(price: number): number | undefined { + // @ts-ignore + var result = this.generator.next(price).value; + if (result != undefined) return this.format(result); + } +} + +export function sma(input: MAInput): number[] { + Indicator.reverseInputs(input); + var result = new SMA(input).result; + if (input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; +} + +//STEP 6. Run the tests diff --git a/src/moving_averages/WEMA.ts b/src/moving_averages/WEMA.ts new file mode 100644 index 0000000..d35af62 --- /dev/null +++ b/src/moving_averages/WEMA.ts @@ -0,0 +1,66 @@ +// @ts-nocheck +import { Indicator, IndicatorInput } from "../indicator/indicator.ts"; +import { MAInput, SMA } from "./SMA.ts"; +import { LinkedList } from "../Utils/LinkedList.ts"; + +export class WEMA extends Indicator { + period: number; + price: number[]; + result: number[]; + generator: IterableIterator; + constructor(input: MAInput) { + super(input); + var period = input.period; + var priceArray = input.values; + var exponent = 1 / period; + var sma: SMA; + + this.result = []; + + sma = new SMA({ period: period, values: [] }); + + var genFn = function* (): IterableIterator { + var tick = yield; + var prevEma; + while (true) { + if (prevEma !== undefined && tick !== undefined) { + prevEma = (tick - prevEma) * exponent + prevEma; + tick = yield prevEma; + } else { + tick = yield; + prevEma = sma.nextValue(tick); + if (prevEma !== undefined) tick = yield prevEma; + } + } + }; + + this.generator = genFn(); + + this.generator.next(); + this.generator.next(); + + priceArray.forEach(tick => { + var result = this.generator.next(tick); + if (result.value != undefined) { + this.result.push(this.format(result.value)); + } + }); + } + + static calculate = wema; + + nextValue(price: number): number | undefined { + var result = this.generator.next(price).value; + if (result != undefined) return this.format(result); + } +} + +export function wema(input: MAInput): number[] { + Indicator.reverseInputs(input); + var result = new WEMA(input).result; + if (input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; +} diff --git a/src/moving_averages/WMA.ts b/src/moving_averages/WMA.ts new file mode 100644 index 0000000..076245d --- /dev/null +++ b/src/moving_averages/WMA.ts @@ -0,0 +1,65 @@ +"use strict" +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { MAInput } from './SMA.ts'; +import { LinkedList } from '../Utils/LinkedList.ts'; + +export class WMA extends Indicator{ + period:number; + price:number[]; + result : number[]; + generator:IterableIterator; + constructor (input:MAInput) { + super(input); + var period = input.period; + var priceArray = input.values; + this.result = []; + this.generator = (function* (){ + let data = new LinkedList(); + let denominator = period * (period + 1)/2; + + while (true) { + if((data.length) < period ){ + data.push(yield) + }else { + data.resetCursor(); + let result = 0; + for(let i=1; i<=period; i++){ + result = result + (data.next() * i/(denominator)) + } + var next = yield result; + data.shift(); + data.push(next); + } + } + })(); + + this.generator.next(); + + priceArray.forEach((tick, index) => { + var result = this.generator.next(tick) + if(result.value != undefined){ + this.result.push(this.format(result.value)); + } + }); + } + + static calculate = wma; + + //STEP 5. REMOVE GET RESULT FUNCTION + nextValue(price:number):number | undefined { + var result = this.generator.next(price).value; + if(result != undefined) + return this.format(result); + }; + +}; + +export function wma(input:MAInput):number[] { + Indicator.reverseInputs(input); + var result = new WMA(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + } diff --git a/lib/moving_averages/WilderSmoothing.txt b/src/moving_averages/WilderSmoothing.ts similarity index 54% rename from lib/moving_averages/WilderSmoothing.txt rename to src/moving_averages/WilderSmoothing.ts index bb2321b..4d29769 100644 --- a/lib/moving_averages/WilderSmoothing.txt +++ b/src/moving_averages/WilderSmoothing.ts @@ -1,29 +1,34 @@ -import { Indicator } from '../indicator/indicator'; -import { LinkedList } from '../Utils/LinkedList'; +import { MAInput } from './SMA.ts'; +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { LinkedList } from '../Utils/LinkedList.ts'; + //STEP3. Add class based syntax with export -export class WilderSmoothing extends Indicator { - constructor(input) { +export class WilderSmoothing extends Indicator{ + period:number; + price:number[]; + result : number[]; + generator:IterableIterator; + constructor(input:MAInput) { super(input); - this.period = input.period; + this.period = input.period; this.price = input.values; - var genFn = (function* (period) { + var genFn = (function*(period:number):IterableIterator { var list = new LinkedList(); var sum = 0; var counter = 1; var current = yield; var result = 0; - while (true) { - if (counter < period) { - counter++; + while(true){ + if(counter < period){ + counter ++; sum = sum + current; result = undefined; - } - else if (counter == period) { - counter++; + } else if(counter == period){ + counter ++; sum = sum + current; result = sum; } - else { + else{ result = result - (result / period) + current; } current = yield result; @@ -34,27 +39,29 @@ export class WilderSmoothing extends Indicator { this.result = []; this.price.forEach((tick) => { var result = this.generator.next(tick); - if (result.value != undefined) { + if(result.value != undefined){ this.result.push(this.format(result.value)); } }); } - nextValue(price) { + + static calculate = wildersmoothing; + + nextValue(price:number):number | undefined { var result = this.generator.next(price).value; - if (result != undefined) + if(result != undefined) return this.format(result); - } - ; + }; } -WilderSmoothing.calculate = wildersmoothing; -export function wildersmoothing(input) { + +export function wildersmoothing(input:MAInput):number[] { Indicator.reverseInputs(input); var result = new WilderSmoothing(input).result; - if (input.reversedInput) { + if(input.reversedInput) { result.reverse(); } Indicator.reverseInputs(input); return result; -} -; -//STEP 6. Run the tests +}; + +//STEP 6. Run the tests \ No newline at end of file diff --git a/src/oscillators/AwesomeOscillator.ts b/src/oscillators/AwesomeOscillator.ts new file mode 100644 index 0000000..3d9321f --- /dev/null +++ b/src/oscillators/AwesomeOscillator.ts @@ -0,0 +1,81 @@ +import { Indicator, IndicatorInput } from "../indicator/indicator.ts"; +import { SMA } from "../moving_averages/SMA.ts"; +import { CandleData } from "../StockData.ts"; + +export class AwesomeOscillatorInput extends IndicatorInput { + high: number[] = []; + low: number[] = []; + // @ts-ignore + fastPeriod: number; + // @ts-ignore + slowPeriod: number; +} + +export class AwesomeOscillator extends Indicator { + generator: IterableIterator; + constructor(input: AwesomeOscillatorInput) { + super(input); + var highs = input.high; + var lows = input.low; + var fastPeriod = input.fastPeriod; + var slowPeriod = input.slowPeriod; + + var slowSMA = new SMA({ values: [], period: slowPeriod }); + var fastSMA = new SMA({ values: [], period: fastPeriod }); + + this.result = []; + + this.generator = (function* () { + var result; + var tick; + var medianPrice; + var slowSmaValue; + var fastSmaValue; + // @ts-ignore + tick = yield; + while (true) { + medianPrice = (tick.high + tick.low) / 2; + slowSmaValue = slowSMA.nextValue(medianPrice); + fastSmaValue = fastSMA.nextValue(medianPrice); + if (slowSmaValue !== undefined && fastSmaValue !== undefined) { + result = fastSmaValue - slowSmaValue; + } + // @ts-ignore + tick = yield result; + } + })(); + + this.generator.next(); + + highs.forEach((tickHigh, index) => { + var tickInput:any = { + high: tickHigh, + low: lows[index] + }; + var result = this.generator.next(tickInput); + if (result.value != undefined) { + this.result.push(this.format(result.value)); + } + }); + } + + static calculate = awesomeoscillator; + + nextValue(price: CandleData): number | undefined { + // @ts-ignore + var result = this.generator.next(price); + if (result.value != undefined) { + return this.format(result.value); + } + } +} + +export function awesomeoscillator(input: AwesomeOscillatorInput): number[] { + Indicator.reverseInputs(input); + var result = new AwesomeOscillator(input).result; + if (input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; +} diff --git a/src/oscillators/CCI.ts b/src/oscillators/CCI.ts new file mode 100644 index 0000000..9c700dc --- /dev/null +++ b/src/oscillators/CCI.ts @@ -0,0 +1,102 @@ +import { CandleData } from "../StockData.ts"; +import { Indicator, IndicatorInput } from "../indicator/indicator.ts"; +import { SMA } from "../moving_averages/SMA.ts"; +import LinkedList from "../Utils/FixedSizeLinkedList.ts"; + +export class CCIInput extends IndicatorInput { + high: number[] = []; + low: number[] = []; + close: number[] = []; + period?: number; +} + +export class CCI extends Indicator { + result: number[]; + generator: IterableIterator; + constructor(input: CCIInput) { + super(input); + const lows = input.low; + const highs = input.high; + const closes = input.close; + const period = input.period || 14; + let format = this.format; + const constant = 0.015; + const currentTpSet = new LinkedList(period); + + const tpSMACalculator = new SMA({ + period: period, + values: [], + format: (v: any) => v + }); + + if (!(lows.length === highs.length && highs.length === closes.length)) { + throw "Inputs(low,high, close) not of equal size"; + } + + this.result = []; + + interface _tick { + high: number; + low: number; + close: number; + } + // @ts-ignore + this.generator = (function* () { + let tick:_tick = yield; + while (true) { + const tp = (tick.high + tick.low + tick.close) / 3; + currentTpSet.push(tp); + const smaTp = tpSMACalculator.nextValue(tp); + let meanDeviation = null; + let cci: number; + let sum = 0; + if (smaTp != undefined) { + //First, subtract the most recent 20-period average of the typical price from each period's typical price. + //Second, take the absolute values of these numbers. + //Third,sum the absolute values. + for (const x of currentTpSet.iterator()) { + sum = sum + Math.abs(x - smaTp); + } + //Fourth, divide by the total number of periods (20). + meanDeviation = sum / period; + cci = (tp - smaTp) / (constant * meanDeviation); + } + // @ts-ignore + tick = yield cci; + } + })(); + + this.generator.next(); + + lows.forEach((_tick, index) => { + const result = this.generator.next({ + high: highs[index], + low: lows[index], + close: closes[index] + } as any); + if (result.value != undefined) { + this.result.push(result.value); + } + }); + } + + static calculate = cci; + + nextValue(price: CandleData): number | undefined { + // @ts-ignore + const result = this.generator.next(price).value; + if (result != undefined) { + return result; + } + } +} + +export function cci(input: CCIInput): number[] { + Indicator.reverseInputs(input); + let result = new CCI(input).result; + if (input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; +} diff --git a/src/oscillators/RSI.ts b/src/oscillators/RSI.ts new file mode 100644 index 0000000..8a26567 --- /dev/null +++ b/src/oscillators/RSI.ts @@ -0,0 +1,73 @@ +/** + * Created by AAravindan on 5/5/16. + */ + +import { Indicator, IndicatorInput } from "../indicator/indicator.ts"; +import { AverageGain } from "../Utils/AverageGain.ts"; +import { AverageLoss } from "../Utils/AverageLoss.ts"; + +export class RSIInput extends IndicatorInput { + period!: number; + values!: number[]; +} + +export class RSI extends Indicator { + generator: IterableIterator; + constructor(input: RSIInput) { + super(input); + const period = input.period; + const values = input.values; + const GainProvider = new AverageGain({ period: period, values: [] }); + const LossProvider = new AverageLoss({ period: period, values: [] }); + // let count = 1; + // @ts-ignore + this.generator = (function* (_) { + let current: number = yield; + let lastAvgGain, lastAvgLoss, RS, currentRSI; + while (true) { + lastAvgGain = GainProvider.nextValue(current); + lastAvgLoss = LossProvider.nextValue(current); + if (lastAvgGain !== undefined && lastAvgLoss !== undefined) { + if (lastAvgLoss === 0) { + currentRSI = 100; + } else if (lastAvgGain === 0) { + currentRSI = 0; + } else { + RS = lastAvgGain / lastAvgLoss; + RS = isNaN(RS) ? 0 : RS; + currentRSI = parseFloat((100 - 100 / (1 + RS)).toFixed(2)); + } + } + // count++; + current = yield currentRSI; + } + })(period); + + this.generator.next(); + + this.result = []; + + values.forEach((tick: number) => { + // @ts-ignore + const result = this.generator.next(tick); + if (result.value !== undefined) { + this.result.push(result.value); + } + }); + } + + static calculate = rsi; + + nextValue(price: number): number | undefined { + // @ts-ignore + return this.generator.next(price).value; + } +} + +export function rsi(input: RSIInput): number[] { + Indicator.reverseInputs(input); + const result = new RSI(input).result; + if (input.reversedInput) result.reverse(); + Indicator.reverseInputs(input); + return result; +} diff --git a/src/patterndetection/patterndetection.ts b/src/patterndetection/patterndetection.ts new file mode 100644 index 0000000..53f4b7f --- /dev/null +++ b/src/patterndetection/patterndetection.ts @@ -0,0 +1,161 @@ +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { getConfig } from '../config.ts'; +// import * as tf from '@tensorflow/tfjs'; + +var isNodeEnvironment = false; + +var model; +var oneHotMap = ['IHS', 'TU', 'DB', 'HS', 'TD', 'DT']; + +declare var module; +declare var __dirname; +declare var global; +declare var require; +var tf; + +try { + isNodeEnvironment = Object.prototype.toString.call(global.process) === '[object process]' ; + } catch(e) {} + +export class PatternDetectorInput extends IndicatorInput { + constructor(public values:number[]) { + super(); + } +} + +export enum AvailablePatterns { + 'IHS', + 'TU', + 'DB', + 'HS', + 'TD', + 'DT' +} + +function interpolateArray(data:any, fitCount:any):number[] { + var linearInterpolate = function (before:any, after:any, atPoint:any) { + return before + (after - before) * atPoint; + }; + + var newData = new Array(); + var springFactor:any = new Number((data.length - 1) / (fitCount - 1)); + newData[0] = data[0]; // for new allocation + for ( var i = 1; i < fitCount - 1; i++) { + var tmp = i * springFactor; + var before:any = new Number(Math.floor(tmp)).toFixed(); + var after:any = new Number(Math.ceil(tmp)).toFixed(); + var atPoint = tmp - before; + newData[i] = linearInterpolate(data[before], data[after], atPoint); + } + newData[fitCount - 1] = data[data.length - 1]; // for new allocation + return newData; +}; + +function l2Normalize(arr:any):number[] { + var sum = arr.reduce((cum:any, value:any)=> { return cum + (value * value) }, 0); + var norm = Math.sqrt(sum); + return arr.map((v:any)=>v/norm); +} + +export class PatternDetectorOutput { + patternId: AvailablePatterns + pattern : string + probability : number +} + +var modelLoaded = false; +var laodingModel = false; +var loadingPromise; + +async function loadModel() { + if(modelLoaded) return Promise.resolve(true); + if(laodingModel) return loadingPromise; + laodingModel = true; + loadingPromise = new Promise(async function(resolve, reject) { + if(isNodeEnvironment) { + tf = require('@tensorflow/tfjs') + console.log('Nodejs Environment detected '); + var tfnode = require('@tensorflow/tfjs-node'); + var modelPath = require('path').resolve(__dirname, '../tf_model/model.json'); + model = await tf.loadModel(tfnode.io.fileSystem(modelPath)); + } else { + if(typeof (window as any).tf == "undefined") { + modelLoaded = false; + laodingModel = false; + console.log('Tensorflow js not imported, pattern detection may not work'); + resolve(); + return; + } + tf = (window as any).tf; + console.log('Browser Environment detected ', tf); + console.log('Loading model ....') + model = await tf.loadModel('/tf_model/model.json'); + modelLoaded = true; + laodingModel = false; + setTimeout(resolve, 1000); + console.log('Loaded model'); + return; + } + modelLoaded = true; + laodingModel = false; + resolve(); + return; + }); + await loadingPromise; + return; + } + +loadModel(); + +export async function predictPattern(input:PatternDetectorInput):Promise { + await loadModel() + if(input.values.length < 300) { + console.warn('Pattern detector requires atleast 300 data points for a reliable prediction, received just ', input.values.length) + } + Indicator.reverseInputs(input); + var values = input.values; + var output = await model.predict(tf.tensor2d([l2Normalize(interpolateArray(values, 400))])); + var index = tf.argMax(output, 1).get(0); + Indicator.reverseInputs(input); + return { patternId : index, pattern : oneHotMap[index], probability : output.get(0,4) * 100} +} + +export async function hasDoubleBottom(input:PatternDetectorInput):Promise { + var result = await predictPattern(input) + return (result.patternId === AvailablePatterns.DB) +} + +export async function hasDoubleTop(input:PatternDetectorInput):Promise { + var result = await predictPattern(input) + return (result.patternId === AvailablePatterns.DT) +} + +export async function hasHeadAndShoulder(input:PatternDetectorInput):Promise { + var result = await predictPattern(input) + return (result.patternId === AvailablePatterns.HS) +} + +export async function hasInverseHeadAndShoulder(input:PatternDetectorInput):Promise { + var result = await predictPattern(input) + return (result.patternId === AvailablePatterns.IHS) +} + +export async function isTrendingUp(input:PatternDetectorInput):Promise { + var result = await predictPattern(input) + return (result.patternId === AvailablePatterns.TU) +} + +export async function isTrendingDown(input:PatternDetectorInput):Promise { + var result = await predictPattern(input) + return (result.patternId === AvailablePatterns.TD) +} + +export class PatternDetector extends Indicator { + static predictPattern = predictPattern; + static hasDoubleBottom = hasDoubleBottom; + static hasDoubleTop = hasDoubleTop; + static hasHeadAndShoulder = hasHeadAndShoulder; + static hasInverseHeadAndShoulder = hasInverseHeadAndShoulder; + static isTrendingUp = isTrendingUp; + static isTrendingDown = isTrendingDown; +} \ No newline at end of file diff --git a/src/volatility/BollingerBands.ts b/src/volatility/BollingerBands.ts new file mode 100644 index 0000000..7566252 --- /dev/null +++ b/src/volatility/BollingerBands.ts @@ -0,0 +1,97 @@ +"use strict" +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { SMA } from '../moving_averages/SMA.ts'; +import { SD } from '../Utils/SD.ts'; +export class BollingerBandsInput extends IndicatorInput { + // @ts-ignore + period : number; + // @ts-ignore + stdDev : number; + // @ts-ignore + values : number[]; +}; + +export class BollingerBandsOutput extends IndicatorInput { + // @ts-ignore + middle : number; + // @ts-ignore + upper : number; + // @ts-ignore + lower :number; + // @ts-ignore + pb:number; +}; + +export class BollingerBands extends Indicator { + generator:IterableIterator; + constructor(input:BollingerBandsInput) { + super(input); + var period = input.period + var priceArray = input.values; + var stdDev = input.stdDev; + var format = this.format; + + var sma,sd; + + this.result = []; + + sma = new SMA({period : period, values :[], format : (v) => {return v}}); + sd = new SD({period : period, values : [], format : (v) => {return v}}); + + this.generator = (function* (){ + var result; + var tick; + var calcSMA; + var calcsd; + // @ts-ignore + tick = yield; + while (true) { + calcSMA = sma.nextValue(tick); + calcsd = sd.nextValue(tick); + if(calcSMA){ + let middle = format(calcSMA); + // @ts-ignore + let upper = format(calcSMA + (calcsd * stdDev)); + // @ts-ignore + let lower = format(calcSMA - (calcsd * stdDev)); + let pb:number = format((tick - lower) / (upper - lower)); + result = { + middle : middle, + upper : upper, + lower : lower, + pb : pb + } + } + // @ts-ignore + tick = yield result; + } + })(); + + this.generator.next(); + + priceArray.forEach((tick) => { + // @ts-ignore + var result = this.generator.next(tick); + if(result.value != undefined){ + this.result.push(result.value); + } + }); + } + + static calculate = bollingerbands; + + nextValue(price:number):BollingerBandsOutput | undefined { + // @ts-ignore + return this.generator.next(price).value; + }; +} + +export function bollingerbands(input:BollingerBandsInput):BollingerBandsOutput[] { + Indicator.reverseInputs(input); + var result = new BollingerBands(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; diff --git a/src/volatility/ChandelierExit.ts b/src/volatility/ChandelierExit.ts new file mode 100644 index 0000000..99309ee --- /dev/null +++ b/src/volatility/ChandelierExit.ts @@ -0,0 +1,81 @@ +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { ATR } from '../directionalmovement/ATR.ts'; +import LinkedList from '../Utils/FixedSizeLinkedList.ts'; +export class ChandelierExitInput extends IndicatorInput { + period : number = 22; + multiplier : number = 3; + high : number[]; + low : number[]; + close : number[]; +} + +export class ChandelierExitOutput extends IndicatorInput { + exitLong : number; + exitShort : number; +}; + +export class ChandelierExit extends Indicator { + generator:IterableIterator; + constructor (input:ChandelierExitInput) { + super(input); + var highs = input.high; + var lows = input.low; + var closes = input.close; + + this.result = []; + var atrProducer = new ATR({period : input.period, high : [], low : [], close : [], format : (v) => {return v}}); + var dataCollector = new LinkedList(input.period * 2, true, true, false); + this.generator = (function* (){ + var result; + var tick = yield; + var atr; + while (true) + { + var { high, low } = tick; + dataCollector.push(high); + dataCollector.push(low); + atr = atrProducer.nextValue(tick) + if((dataCollector.totalPushed >= (2 * input.period)) && atr!= undefined) { + result = { + exitLong : dataCollector.periodHigh - atr * input.multiplier, + exitShort : dataCollector.periodLow + atr * input.multiplier + } + } + tick = yield result + } + })(); + + this.generator.next(); + + highs.forEach((tickHigh, index) => { + var tickInput = { + high : tickHigh, + low : lows[index], + close : closes[index], + } + var result = this.generator.next(tickInput); + if(result.value != undefined){ + this.result.push(result.value); + } + }); + }; + + static calculate = chandelierexit; + + nextValue(price:ChandelierExitInput):ChandelierExitOutput | undefined { + var result = this.generator.next(price); + if(result.value != undefined){ + return result.value; + } + }; +} + +export function chandelierexit(input:ChandelierExitInput):number[] { + Indicator.reverseInputs(input); + var result = new ChandelierExit(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; diff --git a/src/volatility/KeltnerChannels.ts b/src/volatility/KeltnerChannels.ts new file mode 100644 index 0000000..ed253ae --- /dev/null +++ b/src/volatility/KeltnerChannels.ts @@ -0,0 +1,89 @@ + +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { SMA } from '../moving_averages/SMA.ts'; +import { EMA } from '../moving_averages/EMA.ts'; +import { ATR } from '../directionalmovement/ATR.ts'; + +export class KeltnerChannelsInput extends IndicatorInput { + maPeriod : number = 20; + atrPeriod: number = 10; + useSMA : boolean = false; + multiplier : number = 1; + high : number[]; + low : number[]; + close : number[]; +} + + +export class KeltnerChannelsOutput extends IndicatorInput { + middle : number; + upper : number; + lower :number; +}; + +export class KeltnerChannels extends Indicator { + result : KeltnerChannelsOutput[] + generator:IterableIterator; + constructor (input:KeltnerChannelsInput) { + super(input); + var maType = input.useSMA ? SMA : EMA; + var maProducer = new maType({period : input.maPeriod, values : [], format : (v) => {return v}}); + var atrProducer = new ATR({period : input.atrPeriod, high : [], low : [], close : [], format : (v) => {return v}}); + var tick; + this.result = []; + this.generator = (function* (){ + var KeltnerChannelsOutput + var result; + tick = yield; + while (true) + { + var { close } = tick; + var ma = maProducer.nextValue(close); + var atr = atrProducer.nextValue(tick) + if(ma!=undefined && atr!=undefined) { + result = { + middle : ma, + upper : ma + (input.multiplier * (atr)), + lower : ma - (input.multiplier * (atr)) + } + } + tick = yield result; + } + })(); + + this.generator.next(); + + var highs = input.high; + + highs.forEach((tickHigh, index) => { + var tickInput = { + high : tickHigh, + low : input.low[index], + close : input.close[index], + } + var result = this.generator.next(tickInput); + if(result.value != undefined){ + this.result.push(result.value); + } + }); + }; + + static calculate = keltnerchannels; + + nextValue(price:KeltnerChannelsInput):KeltnerChannelsOutput | undefined { + var result = this.generator.next(price); + if(result.value != undefined){ + return result.value; + } + }; +} + +export function keltnerchannels(input:KeltnerChannelsInput):KeltnerChannelsOutput[] { + Indicator.reverseInputs(input); + var result = new KeltnerChannels(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; diff --git a/src/volume/ADL.ts b/src/volume/ADL.ts new file mode 100644 index 0000000..7b74363 --- /dev/null +++ b/src/volume/ADL.ts @@ -0,0 +1,74 @@ +/** + * Created by AAravindan on 5/17/16. + */ +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { CandleData } from '../StockData.ts'; + +export class ADLInput extends IndicatorInput { + high:number[] + low:number[] + close:number[] + volume:number[] +} + +export class ADL extends Indicator { + generator:IterableIterator; + constructor (input:ADLInput) { + super(input); + var highs = input.high; + var lows = input.low; + var closes = input.close; + var volumes = input.volume; + + if(!((lows.length === highs.length) && (highs.length === closes.length) && (highs.length === volumes.length) )){ + throw ('Inputs(low,high, close, volumes) not of equal size'); + } + + this.result = []; + + this.generator = (function* (){ + var result = 0; + var tick; + tick = yield; + while (true) + { + let moneyFlowMultiplier = ((tick.close - tick.low) - (tick.high - tick.close)) / (tick.high - tick.low); + moneyFlowMultiplier = isNaN(moneyFlowMultiplier) ? 1 : moneyFlowMultiplier; + let moneyFlowVolume = moneyFlowMultiplier * tick.volume; + result = result + moneyFlowVolume + tick = yield Math.round(result); + } + })(); + + this.generator.next(); + + highs.forEach((tickHigh, index) => { + var tickInput = { + high : tickHigh, + low : lows[index], + close : closes[index], + volume : volumes[index] + } + var result = this.generator.next(tickInput); + if(result.value != undefined){ + this.result.push(result.value); + } + }); + }; + + static calculate = adl; + + nextValue(price:CandleData):number | undefined { + return this.generator.next(price).value; + }; +} + +export function adl(input:ADLInput):number[] { + Indicator.reverseInputs(input); + var result = new ADL(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; \ No newline at end of file diff --git a/src/volume/ForceIndex.ts b/src/volume/ForceIndex.ts new file mode 100644 index 0000000..23fbfc5 --- /dev/null +++ b/src/volume/ForceIndex.ts @@ -0,0 +1,69 @@ +import { EMA } from '../moving_averages/EMA.ts'; +import { CandleData } from '../StockData.ts'; +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; + +export class ForceIndexInput extends IndicatorInput { + close : number[]; + volume : number[]; + period : number = 1; +}; + + +export class ForceIndex extends Indicator { + result : number[] + generator:IterableIterator;; + constructor(input:ForceIndexInput) { + super(input); + var closes = input.close; + var volumes = input.volume; + var period = input.period || 1 + + if(!((volumes.length === closes.length))){ + throw ('Inputs(volume, close) not of equal size'); + } + let emaForceIndex = new EMA({ values : [], period: period }) + this.result = []; + + this.generator = (function* (){ + var previousTick = yield; + var tick = yield; + let forceIndex; + while (true) { + forceIndex = (tick.close - previousTick.close) * tick.volume; + previousTick = tick; + tick = yield emaForceIndex.nextValue(forceIndex); + } + })(); + + this.generator.next(); + + volumes.forEach((tick,index) => { + var result = this.generator.next({ + close : closes[index], + volume : volumes[index] + }); + if(result.value != undefined){ + this.result.push(result.value); + } + }); + }; + + static calculate = forceindex; + + nextValue(price: CandleData):number | undefined { + let result = this.generator.next(price).value; + if(result != undefined) { + return result; + } + }; +} + +export function forceindex(input:ForceIndexInput):number[] { + Indicator.reverseInputs(input); + var result = new ForceIndex(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; \ No newline at end of file diff --git a/src/volume/MFI.ts b/src/volume/MFI.ts new file mode 100644 index 0000000..ae7a8e2 --- /dev/null +++ b/src/volume/MFI.ts @@ -0,0 +1,130 @@ +/** + * Created by AAravindan on 5/17/16. + */ +import { Indicator, IndicatorInput } from "../indicator/indicator.ts"; +import { CandleData } from "../StockData.ts"; +import { TypicalPrice } from "../chart_types/TypicalPrice.ts"; +import FixedSizeLinkedList from "../Utils/FixedSizeLinkedList.ts"; + +export class MFIInput extends IndicatorInput { + // @ts-ignore + high: number[]; + // @ts-ignore + low: number[]; + // @ts-ignore + close: number[]; + // @ts-ignore + volume: number[]; + // @ts-ignore + period: number; +} + +export class MFI extends Indicator { + generator: IterableIterator; + constructor(input: MFIInput) { + super(input); + var highs = input.high; + var lows = input.low; + var closes = input.close; + var volumes = input.volume; + var period = input.period; + + var typicalPrice = new TypicalPrice({ low: [], high: [], close: [] }); + + var positiveFlow = new FixedSizeLinkedList(period, false, false, true); + var negativeFlow = new FixedSizeLinkedList(period, false, false, true); + + if ( + !( + lows.length === highs.length && + highs.length === closes.length && + highs.length === volumes.length + ) + ) { + throw "Inputs(low,high, close, volumes) not of equal size"; + } + + this.result = []; + + this.generator = (function* () { + var result; + var tick; + var lastClose; + var positiveFlowForPeriod; + var rawMoneyFlow = 0; + var moneyFlowRatio; + var negativeFlowForPeriod; + let typicalPriceValue = null; + let prevousTypicalPrice = null; + // @ts-ignore + tick = yield; + lastClose = tick.close; //Fist value + // @ts-ignore + tick = yield; + while (true) { + var { high, low, close, volume } = tick; + var positionMoney = 0; + var negativeMoney = 0; + typicalPriceValue = typicalPrice.nextValue({ high, low, close }); + // @ts-ignore + rawMoneyFlow = typicalPriceValue * volume; + if (typicalPriceValue != null && prevousTypicalPrice != null) { + typicalPriceValue > prevousTypicalPrice + ? (positionMoney = rawMoneyFlow) + : (negativeMoney = rawMoneyFlow); + positiveFlow.push(positionMoney); + negativeFlow.push(negativeMoney); + positiveFlowForPeriod = positiveFlow.periodSum; + negativeFlowForPeriod = negativeFlow.periodSum; + if ( + positiveFlow.totalPushed >= period && + positiveFlow.totalPushed >= period + ) { + moneyFlowRatio = positiveFlowForPeriod / negativeFlowForPeriod; + result = 100 - 100 / (1 + moneyFlowRatio); + } + } + prevousTypicalPrice = typicalPriceValue; + // @ts-ignore + tick = yield result; + } + })(); + + this.generator.next(); + + highs.forEach((tickHigh, index) => { + var tickInput = { + high: tickHigh, + low: lows[index], + close: closes[index], + volume: volumes[index] + }; + // @ts-ignore + var result = this.generator.next(tickInput); + if (result.value != undefined) { + this.result.push(parseFloat(result.value.toFixed(2))); + } + }); + } + + static calculate = mfi; + + nextValue(price: CandleData): number | undefined { + // @ts-ignore + var result = this.generator.next(price); + if (result.value != undefined) { + return parseFloat(result.value.toFixed(2)); + } + } +} + +export function mfi(input: MFIInput): number[] { + Indicator.reverseInputs(input); + // @ts-ignore + var result = new MFI(input).result; + if (input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; +} diff --git a/src/volume/OBV.ts b/src/volume/OBV.ts new file mode 100644 index 0000000..94aad44 --- /dev/null +++ b/src/volume/OBV.ts @@ -0,0 +1,73 @@ +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { CandleData } from '../StockData.ts'; +/** + * Created by AAravindan on 5/17/16. + */ +"use strict" +export class OBVInput extends IndicatorInput { + close:number[]; + volume:number[]; +} + +export class OBV extends Indicator { + generator:IterableIterator; + constructor(input:OBVInput) { + super(input); + var closes = input.close; + var volumes = input.volume; + + this.result = []; + + this.generator = (function* (){ + var result = 0; + var tick; + var lastClose; + tick = yield; + if(tick.close && (typeof tick.close === 'number')){ + lastClose = tick.close; + tick = yield; + } + while (true) + { + if(lastClose < tick.close ){ + result = result + tick.volume; + } + else if(tick.close < lastClose){ + result = result - tick.volume; + } + lastClose = tick.close; + tick = yield result; + } + })(); + + this.generator.next(); + + closes.forEach((close, index) => { + let tickInput = { + close : closes[index], + volume : volumes[index] + } + let result = this.generator.next(tickInput); + if(result.value != undefined){ + this.result.push(result.value); + } + }); + } + + static calculate = obv; + + nextValue(price:CandleData):number | undefined { + return this.generator.next(price).value; + }; + +} + +export function obv(input:OBVInput):number[] { + Indicator.reverseInputs(input); + var result = new OBV(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; \ No newline at end of file diff --git a/src/volume/VWAP.ts b/src/volume/VWAP.ts new file mode 100644 index 0000000..4d14dbc --- /dev/null +++ b/src/volume/VWAP.ts @@ -0,0 +1,76 @@ +// @ts-nocheck +import { CandleData } from '../StockData.ts'; +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; + +export class VWAPInput extends IndicatorInput { + high : number[]; + low :number[]; + close : number[]; + volume : number[] +}; + + +export class VWAP extends Indicator { + result : number[] + generator:IterableIterator;; + constructor(input:VWAPInput) { + super(input); + var lows = input.low; + var highs = input.high; + var closes = input.close; + var volumes = input.volume; + var format = this.format; + + if(!((lows.length === highs.length) && (highs.length === closes.length) )){ + throw ('Inputs(low,high, close) not of equal size'); + } + + this.result = []; + + this.generator = (function* (){ + var tick = yield; + let cumulativeTotal = 0; + let cumulativeVolume = 0; + while (true) { + let typicalPrice = (tick.high + tick.low + tick.close) / 3; + let total = tick.volume * typicalPrice; + cumulativeTotal = cumulativeTotal + total; + cumulativeVolume = cumulativeVolume + tick.volume; + tick = yield cumulativeTotal / cumulativeVolume;; + } + })(); + + this.generator.next(); + + lows.forEach((tick,index) => { + var result = this.generator.next({ + high : highs[index], + low : lows[index], + close : closes[index], + volume : volumes[index] + }); + if(result.value != undefined){ + this.result.push(result.value); + } + }); + }; + + static calculate = vwap; + + nextValue(price: CandleData):number | undefined { + let result = this.generator.next(price).value; + if(result != undefined) { + return result; + } + }; +} + +export function vwap(input:VWAPInput):number[] { + Indicator.reverseInputs(input); + var result = new VWAP(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; \ No newline at end of file diff --git a/src/volume/VolumeProfile.ts b/src/volume/VolumeProfile.ts new file mode 100644 index 0000000..a9cc3f2 --- /dev/null +++ b/src/volume/VolumeProfile.ts @@ -0,0 +1,89 @@ + +import { Indicator, IndicatorInput } from '../indicator/indicator.ts'; +import { CandleData } from '../StockData.ts'; + +export class VolumeProfileInput extends IndicatorInput { + high:number[] + open:number[] + low:number[] + close:number[] + volume:number[] + noOfBars :number +} + +export class VolumeProfileOutput { + rangeStart : number + rangeEnd : number + bullishVolume : number + bearishVolume : number +} + +export function priceFallsBetweenBarRange(low, high, low1, high1) { + return (low <= low1 && high >=low1) || (low1 <= low && high1 >=low) +} + +export class VolumeProfile extends Indicator { + generator:IterableIterator; + constructor (input:VolumeProfileInput) { + super(input); + var highs = input.high; + var lows = input.low; + var closes = input.close; + var opens = input.open; + var volumes = input.volume; + var bars = input.noOfBars; + + if(!((lows.length === highs.length) && (highs.length === closes.length) && (highs.length === volumes.length) )){ + throw ('Inputs(low,high, close, volumes) not of equal size'); + } + + this.result = []; + + var max = Math.max(...highs, ...lows, ...closes, ...opens); + var min = Math.min(...highs, ...lows, ...closes, ...opens); + var barRange = (max - min) / bars; + var lastEnd = min; + for(let i=0; i < bars; i++) { + let rangeStart = lastEnd; + let rangeEnd = rangeStart + barRange; + lastEnd = rangeEnd; + let bullishVolume = 0; + let bearishVolume = 0; + let totalVolume = 0; + for(let priceBar=0; priceBar < highs.length; priceBar++) { + let priceBarStart = lows[priceBar]; + let priceBarEnd = highs[priceBar]; + let priceBarOpen = opens[priceBar]; + let priceBarClose = closes[priceBar]; + let priceBarVolume = volumes[priceBar]; + if(priceFallsBetweenBarRange(rangeStart, rangeEnd, priceBarStart, priceBarEnd)) { + totalVolume = totalVolume + priceBarVolume; + if(priceBarOpen > priceBarClose) { + bearishVolume = bearishVolume + priceBarVolume; + } else { + bullishVolume = bullishVolume + priceBarVolume; + } + } + } + this.result.push({ + rangeStart, rangeEnd, bullishVolume, bearishVolume, totalVolume + }) + } + }; + + static calculate = volumeprofile; + + nextValue(price:CandleData):number | undefined { + throw('Next value not supported for volume profile') + }; +} + +export function volumeprofile(input:VolumeProfileInput):number[] { + Indicator.reverseInputs(input); + var result = new VolumeProfile(input).result; + if(input.reversedInput) { + result.reverse(); + } + Indicator.reverseInputs(input); + return result; + }; diff --git a/test/Utils/AverageGain.js b/test/Utils/AverageGain.js new file mode 100644 index 0000000..2d2e42b --- /dev/null +++ b/test/Utils/AverageGain.js @@ -0,0 +1,23 @@ +/** +* Created by AAravindan on 5/5/16. +*/ +var AverageGain = require('../../lib/Utils/AverageGain').AverageGain; +var assert = require("assert"); +var data = require('../data'); + +var input = { + period : 14, + values : [44.3389,44.0902,44.1497,43.6124,44.3278,44.8264,45.0955,45.4245,45.8433,46.0826,45.8931,46.0328,45.6140,46.2820,46.2820,46.0028,46.0328,46.4116,46.2222,45.6439, 46.2122,46.2521,45.7137,46.4515,45.7835,45.3548,44.0288,44.1783,44.2181,44.5672,43.4205,42.6628,43.1314] + //values : [44.34,44.09,44.15,43.61,44.33,44.83,45.10,45.42,45.84,46.08,45.89,46.03,45.61,46.28,46.28,46.00,46.03,46.41,46.22,45.64,46.21,46.25,45.71,46.45] +} + +var expectedResults = [.24,.22,.21,.22,0.20,.19,.22,.20,.19,.23,.21,.20,.18,.18,.17,.18,.17,.16,.18] + +describe('Average Gain', function() { + "use strict"; + it('Should calculate average gain', function(){ + assert.deepEqual(AverageGain.calculate(input).map(a=>parseFloat(a.toFixed(2))), expectedResults); + }) +}); + + diff --git a/test/Utils/AverageLoss.js b/test/Utils/AverageLoss.js new file mode 100644 index 0000000..025754f --- /dev/null +++ b/test/Utils/AverageLoss.js @@ -0,0 +1,20 @@ +/** +* Created by AAravindan on 5/5/16. +*/ +var AverageLoss = require('../../lib/Utils/AverageLoss').AverageLoss; +var assert = require("assert"); +var data = require('../data'); + +var input = { + period : 6, + values : [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] +} + +describe('Average Loss', function() { + "use strict"; + it('Should calculate average loss', function(){ + assert.deepEqual(AverageLoss.calculate(input), [0,0,0,0,0,0,0,0,0,0]); + }) +}); + + diff --git a/test/Utils/CrossDown.js b/test/Utils/CrossDown.js new file mode 100644 index 0000000..fcb533d --- /dev/null +++ b/test/Utils/CrossDown.js @@ -0,0 +1,36 @@ +/** + * Created by cwouter on 2/3/2020. + */ +var CrossDown = require('../../lib/Utils/CrossDown').CrossDown; +var assert = require('assert'); + +var input = { + lineA: [7, 6, 5, 4, 3, 8, 3, 5, 3, 8, 5, 5, 3, 8, 5, 5, 8, 3], + lineB: [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], +}; + +var expectedResults = [false, false, false, true, false, false, true, false, false, false, false, false, true, false, false, false, false, true]; + +describe('Cross Down', function() { + 'use strict'; + it('should calculate negative line cross over using the calculate method', function() { + assert.deepEqual(CrossDown.calculate(input), expectedResults); + }); + + it('should calculate negative line cross over by using getResult', function() { + var crossDown = new CrossDown(input); + assert.deepEqual(crossDown.getResult(), expectedResults, 'Wrong Results while calculating next bar'); + }); + + it('should calculate negative line cross over by using nextValue', function() { + var crossDown = new CrossDown({lineA: [], lineB: []}); + var results = []; + input.lineA.forEach((value, index) => { + var result = crossDown.nextValue(input.lineA[index], input.lineB[index]); + results.push(result) + }); + assert.deepEqual(results, expectedResults, 'Wrong Results while getting results'); + }) +}); + + diff --git a/test/Utils/CrossOver.js b/test/Utils/CrossOver.js new file mode 100644 index 0000000..15b8260 --- /dev/null +++ b/test/Utils/CrossOver.js @@ -0,0 +1,36 @@ +/** + * Created by cwouter on 2/3/2020. + */ +var CrossOver = require('../../lib/Utils/CrossOver').CrossOver; +var assert = require('assert'); + +var input = { + lineA: [3, 4, 5, 6, 7, 2, 7, 5, 7, 2, 5, 5, 7, 2, 5, 5, 2, 7], + lineB: [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], +}; + +var expectedResults = [false, false, false, true, false, true, true, false, false, true, false, false, true, true, false, false, false, true]; + +describe('Cross Over', function() { + 'use strict'; + it('should calculate positive and negative line cross over using the calculate method', function() { + assert.deepEqual(CrossOver.calculate(input), expectedResults); + }); + + it('should calculate positive and negative line cross over by using getResult', function() { + var crossOver = new CrossOver(input); + assert.deepEqual(crossOver.getResult(), expectedResults, 'Wrong Results while calculating next bar'); + }); + + it('should calculate positive and negative line cross over by using nextValue', function() { + var crossOver = new CrossOver({lineA: [], lineB: []}); + var results = []; + input.lineA.forEach((value, index) => { + var result = crossOver.nextValue(input.lineA[index], input.lineB[index]); + results.push(result) + }); + assert.deepEqual(results, expectedResults, 'Wrong Results while getting results'); + }) +}); + + diff --git a/test/Utils/CrossUp.js b/test/Utils/CrossUp.js new file mode 100644 index 0000000..8a43390 --- /dev/null +++ b/test/Utils/CrossUp.js @@ -0,0 +1,36 @@ +/** + * Created by cwouter on 1/3/2020. + */ +var CrossUp = require('../../lib/Utils/CrossUp').CrossUp; +var assert = require('assert'); + +var input = { + lineA: [3, 4, 5, 6, 7, 2, 7, 5, 7, 2, 5, 5, 7, 2, 5, 5, 2, 7], + lineB: [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5], +}; + +var expectedResults = [false, false, false, true, false, false, true, false, false, false, false, false, true, false, false, false, false, true]; + +describe('Cross Up', function() { + 'use strict'; + it('should calculate positive line cross over using the calculate method', function() { + assert.deepEqual(CrossUp.calculate(input), expectedResults); + }); + + it('should calculate positive line cross over by using getResult', function() { + var crossUp = new CrossUp(input); + assert.deepEqual(crossUp.getResult(), expectedResults, 'Wrong Results while calculating next bar'); + }); + + it('should calculate positive line cross over by using nextValue', function() { + var crossUp = new CrossUp({lineA: [], lineB: []}); + var results = []; + input.lineA.forEach((value, index) => { + var result = crossUp.nextValue(input.lineA[index], input.lineB[index]); + results.push(result) + }); + assert.deepEqual(results, expectedResults, 'Wrong Results while getting results'); + }) +}); + + diff --git a/test/Utils/FixedSizeLinkedList.js b/test/Utils/FixedSizeLinkedList.js new file mode 100644 index 0000000..8c04179 --- /dev/null +++ b/test/Utils/FixedSizeLinkedList.js @@ -0,0 +1,106 @@ +/** + * Created by AAravindan on 5/7/16. + */ +"use strict"; +var FixedSizeLinkedList = require("../../lib/Utils/FixedSizeLinkedList").default; +var assert = require('assert'); + +var linkedList; +var size = 10; + +describe('Fixed Size Linked List', function() { + beforeEach(function() { + linkedList = new FixedSizeLinkedList(size, true, true, true); + }); + + it('Should maintain only the fixed size', function() { + for(let i=1; i<20; i++) { + linkedList.push(i.toString()) + } + assert.equal(linkedList.length, size); + }); + + it('Should not popup if there is not enough', function(){ + for(let i=1; i<=10; i++) { + linkedList.push(i.toString()) + } + assert.equal(linkedList.length, size); + assert.equal(linkedList.head, '1'); + assert.equal(linkedList.tail, '10'); + linkedList.push('11'); + assert.equal(linkedList.lastShift, '1'); + assert.equal(linkedList.head, '2'); + assert.equal(linkedList.tail, '11'); + linkedList.push('12'); + assert.equal(linkedList.lastShift, '2'); + assert.equal(linkedList.head, '3'); + assert.equal(linkedList.tail, '12'); + }) + + it('Should popup out the first excess to the lastShift', function(){ + for(let i=1; i<=11; i++) { + linkedList.push(i.toString()) + } + assert.equal(linkedList.length, size); + assert.equal(linkedList.lastShift, '1'); + linkedList.push('12'); + assert.equal(linkedList.lastShift, '2'); + assert.equal(linkedList.head, '3'); + assert.equal(linkedList.tail, '12'); + assert.equal(linkedList.length, size); + }); + + it('Should contain an iterator function', function(){ + for(let i=1; i<=11; i++) { + linkedList.push(i.toString()) + } + assert(linkedList.iterator, 'Iterator not found'); + var results = []; + for(let values of linkedList.iterator()){ + results.push(values); + } + assert.deepEqual(['2','3','4','5','6','7','8','9','10','11'], results); + }) + + it('Should maintain period high before shift', function(){ + for(let i=1; i<=10; i++) { + linkedList.push(i) + } + assert.equal(linkedList.periodHigh, 10) + }); + + it('Should maintain period high after shift', function(){ + for(let i=1; i<=13; i++) { + linkedList.push(i) + } + assert.equal(linkedList.periodHigh, 13) + }) + + it('Should maintain period low before shift', function(){ + for(let i=1; i<=10; i++) { + linkedList.push(i) + } + assert.equal(linkedList.periodLow, 1) + }); + + it('Should maintain period low after shift', function(){ + for(let i=1; i<=14; i++) { + linkedList.push(i) + } + assert.equal(linkedList.periodLow, 5) + }) + + it('Should maintain sum if requested', function(){ + for(let i=1; i<=10; i++) { + linkedList.push(i) + } + assert.equal(linkedList.periodSum, (10 * 11)/2 ) + }) + + it('Should maintain sum if requested', function(){ + for(let i=1; i<=14; i++) { + linkedList.push(i) + } + assert.equal(linkedList.periodSum, 95) + }) +}); \ No newline at end of file diff --git a/test/Utils/Highest.js b/test/Utils/Highest.js new file mode 100644 index 0000000..5b71c59 --- /dev/null +++ b/test/Utils/Highest.js @@ -0,0 +1,18 @@ + +"use strict"; +let assert = require('assert'); +let Highest = require('../../lib/Utils/Highest').Highest; + +let input = { + values : [10,20,30,40,30,20,10,20,16,29,15], + period : 3 +} + +let expectResult = [30, 40, 40, 40, 30, 20, 20, 29, 29] + +describe('Highest', function() { + it('should calculate Highest using the calculate method', function() { + var result = Highest.calculate(input); + assert.deepEqual(result, expectResult, 'Wrong Results'); + }); +}) diff --git a/test/Utils/Lowest.js b/test/Utils/Lowest.js new file mode 100644 index 0000000..a1593a8 --- /dev/null +++ b/test/Utils/Lowest.js @@ -0,0 +1,18 @@ + +"use strict"; +let assert = require('assert'); +let Lowest = require('../../lib/Utils/Lowest').Lowest; + +let input = { + values : [10,20,30,40,30,20,10,20,16,29,15], + period : 3 +} + +let expectResult = [ 10, 20, 30, 20, 10, 10, 10, 16, 15 ] + +describe('Lowest', function() { + it('should calculate Lowest using the calculate method', function() { + var result = Lowest.calculate(input); + assert.deepEqual(result, expectResult, 'Wrong Results'); + }); +}) diff --git a/test/Utils/SD.js b/test/Utils/SD.js new file mode 100644 index 0000000..9e5a9f3 --- /dev/null +++ b/test/Utils/SD.js @@ -0,0 +1,53 @@ +/** + * Created by AAravindan on 5/7/16. + */ +"use strict" +const assert = require('assert'); +const SD = require('../../lib/Utils/SD').SD + +//var data = [4,2,5,8,6]; +var data = [11,12,13,14,15,16,18, 19, 22, 23, 23]; +var period = 5; +//var expectResult = [2.24] +var expectResult = [ + 1.4142135623730951, + 1.4142135623730951, + 1.7204650534085253, + 1.854723699099141, + 2.449489742783178, + 2.576819745345025, + 2.0976176963403033 +] +describe('Standard Deviation', function() { + "use strict"; + it('should calculate SD using the calculate method', function() { + assert.deepEqual(SD.calculate({period : period, values : data}), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate EMA by using getResult', function() { + var sd = new SD({period : period, values : data}); + assert.deepEqual(sd.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to get EMA for the next bar using nextValue', function() { + var sd = new SD({period : period, values : []}); + var results = []; + data.forEach(price => { + var result = sd.nextValue(price); + if(result) + results.push(result) + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }) + + it('should be able to calculate ROC for reversed input by using calculate method', function() { + let myInput = Object.assign({}, { + period : period, + values : data + }); + myInput.reversedInput = true; + myInput.values.reverse(); + assert.deepEqual(SD.calculate(myInput), expectResult.slice().reverse(), 'Wrong Results while calculating next bar'); + }); + +}) \ No newline at end of file diff --git a/test/Utils/Sum.js b/test/Utils/Sum.js new file mode 100644 index 0000000..dfc6c97 --- /dev/null +++ b/test/Utils/Sum.js @@ -0,0 +1,18 @@ + +"use strict"; +let assert = require('assert'); +let Sum = require('../../lib/Utils/Sum').Sum; + +let input = { + values : [10,20,30,40,30,20,10,20,16,29,15], + period : 3 +} + +let expectResult = [ 60, 90, 100, 90, 60, 50, 46, 65, 60 ] + +describe('Sum', function() { + it('should calculate Sum using the calculate method', function() { + var result = Sum.calculate(input); + assert.deepEqual(result, expectResult, 'Wrong Results'); + }); +}) diff --git a/test/candlestick/AbandonedBaby.js b/test/candlestick/AbandonedBaby.js new file mode 100644 index 0000000..f5e7aa9 --- /dev/null +++ b/test/candlestick/AbandonedBaby.js @@ -0,0 +1,26 @@ +var AbandonedBaby = require('../../lib/candlestick/AbandonedBaby').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [31.10,26.18,27.47], + high: [31.80,26.91,30.94], + close: [28.10,26.18,30.62], + low: [27.50,25.40,27.03] +} + +describe('AbandonedBaby : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/abandonedbaby.png',imageBuffer); + }); + it('Check whether the supplied data has AbandonedBaby pattern', function() { + var abandonedBaby = new AbandonedBaby (); + var result = abandonedBaby.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for AbandonedBaby'); + }); +}) + + + diff --git a/test/candlestick/Bearish.js b/test/candlestick/Bearish.js new file mode 100644 index 0000000..9ea4c2f --- /dev/null +++ b/test/candlestick/Bearish.js @@ -0,0 +1,30 @@ +var Bearish = require('../../lib/candlestick/Bearish.js').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); +var twoDayBearishInput = { + open: [21.44,27.89], + high: [25.10,30.87], + close: [23.25,15.36], + low: [20.82,14.93], +} + +var oneDayBearishInput = { + open: [21.44], + high: [25.10], + close: [23.25], + low: [20.82], +} + +describe('BearishPattern : ', function() { + before(function() { + var imageBuffer = drawCandleStick(twoDayBearishInput); + fs.writeFileSync(__dirname+'/images/bearish.png',imageBuffer); + }); + it('Check whether the supplied data has Bearish pattern', function() { + var bearishPattern = new Bearish (); + var result = bearishPattern.hasPattern(twoDayBearishInput); + assert.deepEqual(result, true, 'Invalid result for BearishPattern'); + }); +}) + diff --git a/test/candlestick/BearishEngulfingPattern.js b/test/candlestick/BearishEngulfingPattern.js new file mode 100644 index 0000000..ff4ad18 --- /dev/null +++ b/test/candlestick/BearishEngulfingPattern.js @@ -0,0 +1,36 @@ +var BearishEngulfingPattern = require('../../lib/candlestick/BearishEngulfingPattern').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); +var twoDayBearishInput = { + open: [21.44,27.89], + high: [25.10,30.87], + close: [23.25,15.36], + low: [20.82,14.93], +} + +var oneDayBearishInput = { + open: [21.44], + high: [25.10], + close: [23.25], + low: [20.82], +} + +describe('BearishEngulfingPattern : ', function() { + before(function() { + var imageBuffer = drawCandleStick(twoDayBearishInput); + fs.writeFileSync(__dirname+'/images/bearishEngulfing.png',imageBuffer); + }); + it('Check whether the supplied data has BearishEngulfingPattern pattern', function() { + var bearishEngulfingPattern = new BearishEngulfingPattern (); + var result = bearishEngulfingPattern.hasPattern(twoDayBearishInput); + assert.deepEqual(result, true, 'Invalid result for BearishEngulfingPattern'); + }); + + it('Should return false if less data is provided', function() { + var bearishEngulfingPattern = new BearishEngulfingPattern (); + var result = bearishEngulfingPattern.hasPattern(oneDayBearishInput); + assert.deepEqual(result, false, 'Invalid result for BearishEngulfingPattern'); + }); +}) + diff --git a/test/candlestick/BearishHammerStick.js b/test/candlestick/BearishHammerStick.js new file mode 100644 index 0000000..018b3eb --- /dev/null +++ b/test/candlestick/BearishHammerStick.js @@ -0,0 +1,23 @@ +var BearishHammer = require('../../lib/candlestick/BearishHammerStick').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var basicHammer = { + open: [30.10], + high: [30.10], + close: [26.13], + low: [10.06], +} + +describe('Bearish Hammer (Single Stick) : ', function() { + before(function() { + var imageBuffer = drawCandleStick(basicHammer); + fs.writeFileSync(__dirname+'/images/BearishHammerStick.png',imageBuffer); + }); + it('Check whether the supplied data has Bearish Hammer (Single Stick) pattern', function() { + var bearishHammer = new BearishHammer(); + var result = bearishHammer.hasPattern(basicHammer); + assert.deepEqual(result, true, 'Invalid result for Bearish Hammer (Single Stick)'); + }); +}) diff --git a/test/candlestick/BearishHarami.js b/test/candlestick/BearishHarami.js new file mode 100644 index 0000000..baa6366 --- /dev/null +++ b/test/candlestick/BearishHarami.js @@ -0,0 +1,25 @@ +var BearishHarami = require('../../lib/candlestick/BearishHarami').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [20.12, 22,13], + high: [23.82,22.76], + close: [23.50,21.70], + low: [19.88,21.31], +} + +describe('BearishHarami : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/BearishHarami.png',imageBuffer); + }); + it('Check whether the supplied data has BearishHarami pattern', function() { + var bearishHarami = new BearishHarami (); + var result = bearishHarami.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for BearishHarami') + + }); +}) + diff --git a/test/candlestick/BearishHaramiCross.js b/test/candlestick/BearishHaramiCross.js new file mode 100644 index 0000000..234987b --- /dev/null +++ b/test/candlestick/BearishHaramiCross.js @@ -0,0 +1,26 @@ +var BearishHaramiCross = require('../../lib/candlestick/BearishHaramiCross').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [20.12, 22.13], + high: [23.82,22.76], + close: [23.50,22.13], + low: [19.88,21.31], + +} + +describe('BearishHaramiCross: ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/BearishHaramiCross.png',imageBuffer); + }); + it('Check whether the supplied data has BearishHaramiCross pattern', function() { + var bearishHaramiCross = new BearishHaramiCross (); + var result = bearishHaramiCross.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for BearishHaramiCross') + + }); +}) + diff --git a/test/candlestick/BearishInvertedHammerStick.js b/test/candlestick/BearishInvertedHammerStick.js new file mode 100644 index 0000000..233a771 --- /dev/null +++ b/test/candlestick/BearishInvertedHammerStick.js @@ -0,0 +1,23 @@ +var BearishInvertedHammer = require('../../lib/candlestick/BearishInvertedHammerStick').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var basicHammer = { + open: [30.10], + high: [52.06], + close: [26.13], + low: [26.13], +} + +describe('Bearish Inverted Hammer (Single Stick) : ', function() { + before(function() { + var imageBuffer = drawCandleStick(basicHammer); + fs.writeFileSync(__dirname+'/images/BearishInvertedHammerStick.png',imageBuffer); + }); + it('Check whether the supplied data has Bearish Inverted Hammer (Single Stick) pattern', function() { + var bearishInvertedHammer = new BearishInvertedHammer(); + var result = bearishInvertedHammer.hasPattern(basicHammer); + assert.deepEqual(result, true, 'Invalid result for Bearish Inverted (Single Stick) Hammer'); + }); +}) diff --git a/test/candlestick/BearishMarubozu.js b/test/candlestick/BearishMarubozu.js new file mode 100644 index 0000000..a96fe4d --- /dev/null +++ b/test/candlestick/BearishMarubozu.js @@ -0,0 +1,23 @@ +var BearishMarubozu = require('../../lib/candlestick/BearishMarubozu').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + close: [30.50], + open: [31.23], + high: [31.23], + low: [30.50], +} + +describe('BearishMarubozu : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/BearishMarubozu.png',imageBuffer); + }); + it('Check whether the supplied data has BearishMarubozu pattern', function() { + var bearishMarubozu = new BearishMarubozu(); + var result = bearishMarubozu.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for BearishMarubozu'); + }); +}) diff --git a/test/candlestick/BearishSpinningTop.js b/test/candlestick/BearishSpinningTop.js new file mode 100644 index 0000000..e2a2fb7 --- /dev/null +++ b/test/candlestick/BearishSpinningTop.js @@ -0,0 +1,26 @@ +var BearishSpinningTop = require('../../lib/candlestick/BearishSpinningTop').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [20.62], + high: [20.75], + close: [20.50], + low: [20.34], + +} + +describe('BearishSpinningTop : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/BearishSpinningTop.png',imageBuffer); + }); + it('Check whether the supplied data has BearishSpinningTop pattern', function() { + var bearishSpinningTop = new BearishSpinningTop (); + var result = bearishSpinningTop.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for BearishSpinningTop') + + }); +}) + diff --git a/test/candlestick/Bullish.js b/test/candlestick/Bullish.js new file mode 100644 index 0000000..14bba88 --- /dev/null +++ b/test/candlestick/Bullish.js @@ -0,0 +1,35 @@ +var Bullish = require('../../lib/candlestick/Bullish.js').default; +var bullish = require('../../lib/candlestick/Bullish.js').bullish; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [21.12,21.48,21.80],//21.80 + close: [21.65,22.20,22.65],//22.65 + high: [21.83,22.40,22.80],//22.80 + low: [20.85,21.36,21.66],//21.66 +} + +describe('BullishPattern : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/Bullish.png',imageBuffer); + }); + it('Check whether the supplied data has Bullish pattern', function() { + var BullishPattern = new Bullish (); + var result = BullishPattern.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for BullishPattern'); + }); + it('Check whether the supplied data has Bullish pattern if reversed and using static', function() { + var BullishPattern = new Bullish (); + input.open.reverse() + input.high.reverse() + input.low.reverse() + input.close.reverse() + input.reversedInput = true; + var result = bullish(input); + assert.deepEqual(result, true, 'Invalid result for BullishPattern'); + }); +}) + diff --git a/test/candlestick/BullishEngulfingPattern.js b/test/candlestick/BullishEngulfingPattern.js new file mode 100644 index 0000000..b122363 --- /dev/null +++ b/test/candlestick/BullishEngulfingPattern.js @@ -0,0 +1,25 @@ +var BullishEngulfingPattern = require('../../lib/candlestick/BullishEngulfingPattern').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [23.25,15.36], + high: [25.10,30.87], + close: [21.44,27.89], + low: [20.82,14.93], +} + +describe('BullishEngulfingPattern : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/bullishEngulfingPattern.png',imageBuffer); + }); + it('Check whether the supplied data has BullishEngulfingPattern pattern', function() { + var bullishEngulfingPattern = new BullishEngulfingPattern (); + var result = bullishEngulfingPattern.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for BullishEngulfingPattern'); + + }); +}) + diff --git a/test/candlestick/BullishHammerStick.js b/test/candlestick/BullishHammerStick.js new file mode 100644 index 0000000..b4bc869 --- /dev/null +++ b/test/candlestick/BullishHammerStick.js @@ -0,0 +1,23 @@ +var BullishHammer = require('../../lib/candlestick/BullishHammerStick').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var basicHammer = { + open: [26.13], + high: [30.10], + close: [30.10], + low: [10.06], +} + +describe('Bullish Hammer (Single Stick): ', function() { + before(function() { + var imageBuffer = drawCandleStick(basicHammer); + fs.writeFileSync(__dirname+'/images/BullishHammerStick.png',imageBuffer); + }); + it('Check whether the supplied data has Bullish Hammer (Single Stick) pattern', function() { + var bullishHammer = new BullishHammer(); + var result = bullishHammer.hasPattern(basicHammer); + assert.deepEqual(result, true, 'Invalid result for Bullish Hammer (Single Stick)'); + }); +}) diff --git a/test/candlestick/BullishHarami.js b/test/candlestick/BullishHarami.js new file mode 100644 index 0000000..01773b0 --- /dev/null +++ b/test/candlestick/BullishHarami.js @@ -0,0 +1,25 @@ +var BullishHarami = require('../../lib/candlestick/BullishHarami').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [25.13, 23.45], + high: [25.80,24.59], + close: [22.14,24.1], + low: [21.7,23.07], +} + +describe('BullishHarami : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/BullishHarami.png',imageBuffer); + }); + it('Check whether the supplied data has BullishHarami pattern', function() { + var bullishHarami = new BullishHarami (); + var result = bullishHarami.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for BullishHarami') + + }); +}) + diff --git a/test/candlestick/BullishHaramiCross.js b/test/candlestick/BullishHaramiCross.js new file mode 100644 index 0000000..5eb5120 --- /dev/null +++ b/test/candlestick/BullishHaramiCross.js @@ -0,0 +1,25 @@ +var BullishHaramiCross = require('../../lib/candlestick/BullishHaramiCross').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [25.13, 23.45], + high: [25.80,24.59], + close: [22.14,23.45], + low: [21.7,23.07], +} + +describe('BullishHaramiCross: ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/BullishHaramiCross.png',imageBuffer); + }); + it('Check whether the supplied data has BullishHaramiCross pattern', function() { + var bullishHaramiCross = new BullishHaramiCross (); + var result = bullishHaramiCross.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for BullishHaramiCross') + + }); +}) + diff --git a/test/candlestick/BullishInvertedHammerStick.js b/test/candlestick/BullishInvertedHammerStick.js new file mode 100644 index 0000000..440ed4b --- /dev/null +++ b/test/candlestick/BullishInvertedHammerStick.js @@ -0,0 +1,23 @@ +var BullishInvertedHammer = require('../../lib/candlestick/BullishInvertedHammerStick').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var basicHammer = { + open: [26.13], + high: [52.06], + close: [30.10], + low: [26.13], +} + +describe('Bullish Inverted Hammer (Single Stick) : ', function() { + before(function() { + var imageBuffer = drawCandleStick(basicHammer); + fs.writeFileSync(__dirname+'/images/BullishInvertedHammerStick.png',imageBuffer); + }); + it('Check whether the supplied data has Bullish Inverted Hammer (Single Stick) pattern', function() { + var bullishInvertedHammer = new BullishInvertedHammer(); + var result = bullishInvertedHammer.hasPattern(basicHammer); + assert.deepEqual(result, true, 'Invalid result for Bullish Inverted Hammer (Single Stick)'); + }); +}) diff --git a/test/candlestick/BullishMarubozu.js b/test/candlestick/BullishMarubozu.js new file mode 100644 index 0000000..03b7138 --- /dev/null +++ b/test/candlestick/BullishMarubozu.js @@ -0,0 +1,23 @@ +var BullishMarubozu = require('../../lib/candlestick/BullishMarubozu').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + close: [31.23], + open: [30.50], + high: [31.23], + low: [30.50], +} + +describe('BullishMarubozu : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/BullishMarubozu.png',imageBuffer); + }); + it('Check whether the supplied data has BullishMarubozu pattern', function() { + var bullishMarubozu = new BullishMarubozu(); + var result = bullishMarubozu.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for BullishMarubozu'); + }); +}) diff --git a/test/candlestick/BullishSpinningTop.js b/test/candlestick/BullishSpinningTop.js new file mode 100644 index 0000000..7cc59eb --- /dev/null +++ b/test/candlestick/BullishSpinningTop.js @@ -0,0 +1,26 @@ +var BullishSpinningTop = require('../../lib/candlestick/BullishSpinningTop').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [20.50], + high: [20.87], + close: [20.62], + low: [20.23], + +} + +describe('BullishSpinningTop : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/BullishSpinningTop.png',imageBuffer); + }); + it('Check whether the supplied data has BullishSpinningTop pattern', function() { + var bullishSpinningTop = new BullishSpinningTop (); + var result = bullishSpinningTop.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for BullishSpinningTop') + + }); +}) + diff --git a/test/candlestick/CandlestickFinder.js b/test/candlestick/CandlestickFinder.js new file mode 100644 index 0000000..5bcf6a5 --- /dev/null +++ b/test/candlestick/CandlestickFinder.js @@ -0,0 +1,265 @@ +var CandlestickFinder = require('../../lib/candlestick/CandlestickFinder').default; +var assert = require('assert'); + + +var input = { + open: [30.10,30.18,30.15,29.15,28.35,29.19,28.83,28.13,28.17,28.35,28.34], + high: [30.20,30.28,30.45,29.35,29.35,29.29,28.83,28.73,28.67,28.85,28.64], + low: [29.41,29.32,29.96,28.74,28.56,28.41,28.08,27.43,27.66,27.83,27.40], + close:[29.87,30.24,30.10,28.90,28.92,28.48,28.56,27.56,28.47,28.28,27.49], +} + +var expectResult = [ + { + "open": [ + 30.1, + 30.18, + 30.15 + ], + "high": [ + 30.2, + 30.28, + 30.45 + ], + "low": [ + 29.41, + 29.32, + 29.96 + ], + "close": [ + 29.87, + 30.24, + 30.1 + ] + }, + { + "open": [ + 30.18, + 30.15, + 29.15 + ], + "high": [ + 30.28, + 30.45, + 29.35 + ], + "low": [ + 29.32, + 29.96, + 28.74 + ], + "close": [ + 30.24, + 30.1, + 28.9 + ] + }, + { + "open": [ + 30.15, + 29.15, + 28.35 + ], + "high": [ + 30.45, + 29.35, + 29.35 + ], + "low": [ + 29.96, + 28.74, + 28.56 + ], + "close": [ + 30.1, + 28.9, + 28.92 + ] + }, + { + "open": [ + 29.15, + 28.35, + 29.19 + ], + "high": [ + 29.35, + 29.35, + 29.29 + ], + "low": [ + 28.74, + 28.56, + 28.41 + ], + "close": [ + 28.9, + 28.92, + 28.48 + ] + }, + { + "open": [ + 28.35, + 29.19, + 28.83 + ], + "high": [ + 29.35, + 29.29, + 28.83 + ], + "low": [ + 28.56, + 28.41, + 28.08 + ], + "close": [ + 28.92, + 28.48, + 28.56 + ] + }, + { + "open": [ + 29.19, + 28.83, + 28.13 + ], + "high": [ + 29.29, + 28.83, + 28.73 + ], + "low": [ + 28.41, + 28.08, + 27.43 + ], + "close": [ + 28.48, + 28.56, + 27.56 + ] + }, + { + "open": [ + 28.83, + 28.13, + 28.17 + ], + "high": [ + 28.83, + 28.73, + 28.67 + ], + "low": [ + 28.08, + 27.43, + 27.66 + ], + "close": [ + 28.56, + 27.56, + 28.47 + ] + }, + { + "open": [ + 28.13, + 28.17, + 28.35 + ], + "high": [ + 28.73, + 28.67, + 28.85 + ], + "low": [ + 27.43, + 27.66, + 27.83 + ], + "close": [ + 27.56, + 28.47, + 28.28 + ] + }, + { + "open": [ + 28.17, + 28.35, + 28.34 + ], + "high": [ + 28.67, + 28.85, + 28.64 + ], + "low": [ + 27.66, + 27.83, + 27.4 + ], + "close": [ + 28.47, + 28.28, + 27.49 + ] + } +] + +let singleLastData = { + "open": [ + 28.17, + 28.35, + 28.34 + ], + "high": [ + 28.67, + 28.85, + 28.64 + ], + "low": [ + 27.66, + 27.83, + 27.4 + ], + "close": [ + 28.47, + 28.28, + 27.49 + ] + } + +describe('Common candlestick utilities : ', function() { + it('Generate candlestick should generate subset of data based on supplied data', function() { + var results = CandlestickFinder.prototype._generateDataForCandleStick.call({ requiredCount : 3 }, input); + assert.deepEqual(results.length, input.close.length - (3 -1), 'Wrong subset length of data while generating data for candlestick'); + assert.deepEqual(results, expectResult, 'Wrong subset of data while generating data for candlestick'); + }) + + it('Generate candlestick should generate subset of data based on supplied data', function() { + let results = CandlestickFinder.prototype._getLastDataForCandleStick.call({ requiredCount : 3 }, input); + assert.deepEqual(results, expectResult[expectResult.length - 1], 'Wrong Results while getting last data for candlestick'); + }) + + it('Generate candlestick should generate subset of data based on supplied data', function() { + let results = CandlestickFinder.prototype._getLastDataForCandleStick.call({ requiredCount : 3 }, singleLastData); + assert.deepEqual(results, expectResult[expectResult.length - 1], 'Wrong Results while getting single last data for candlestick'); + }) + + it('Approximate Equal return true when value is less than 0.1 percent of difference', function() { + var results = CandlestickFinder.prototype.approximateEqual.call(null ,1 , 1.001); + assert.deepEqual(results, true, 'Approximate equal returns fals when true'); + var results = CandlestickFinder.prototype.approximateEqual.call(null ,10 , 10.01); + assert.deepEqual(results, true, 'Approximate equal returns fals when true'); + var results = CandlestickFinder.prototype.approximateEqual.call(null ,100 , 100.1); + assert.deepEqual(results, true, 'Approximate equal returns fals when true'); + var results = CandlestickFinder.prototype.approximateEqual.call(null ,1000 , 1001); + assert.deepEqual(results, true, 'Approximate equal returns fals when true'); + var results = CandlestickFinder.prototype.approximateEqual.call(null ,10000 , 10010); + assert.deepEqual(results, true, 'Approximate equal returns fals when true'); + }) +}) diff --git a/test/candlestick/DarkCloudCover.js b/test/candlestick/DarkCloudCover.js new file mode 100644 index 0000000..db3cb8c --- /dev/null +++ b/test/candlestick/DarkCloudCover.js @@ -0,0 +1,25 @@ +var DarkCloudCover = require('../../lib/candlestick/DarkCloudCover').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [30.10,39.45], + high: [37.40,41.45], + close: [35.36,32.50], + low: [28.30,31.25], + +} + +describe('DarkCloudCover: ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/darkCloudCover.png',imageBuffer); + }); + it('Check whether the supplied data has DarkCloudCover pattern', function() { + var darkCloudCover = new DarkCloudCover (); + var result = darkCloudCover.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for DarkCloudCover'); + }); +}) + diff --git a/test/candlestick/Doji.js b/test/candlestick/Doji.js new file mode 100644 index 0000000..626f450 --- /dev/null +++ b/test/candlestick/Doji.js @@ -0,0 +1,37 @@ +var Doji = require('../../lib/candlestick/Doji').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [30.10], + high: [32.10], + close: [30.13], + low: [28.10], + +} + +var inputDot = { + open: [30.10], + high: [30.11], + close: [30.10], + low: [30.09], + +} + +describe('Doji : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/doji.png',imageBuffer); + }); + it('Check whether the supplied data has Doji pattern', function() { + var doji = new Doji(); + var result = doji.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for Doji'); + }); + it('Check whether the supplied data has Doji pattern', function() { + var doji = new Doji(); + var result = doji.hasPattern(inputDot); + assert.deepEqual(result, true, 'Invalid result for a single point Doji'); + }); +}) diff --git a/test/candlestick/DownsideTasukiGap.js b/test/candlestick/DownsideTasukiGap.js new file mode 100644 index 0000000..6d6892f --- /dev/null +++ b/test/candlestick/DownsideTasukiGap.js @@ -0,0 +1,25 @@ +var DownsideTasukiGap = require('../../lib/candlestick/DownsideTasukiGap').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [45.00, 33.45, 30.20], + high: [46.20,34.70,36.63], + close:[41.20,29.31,36.28], + low: [38.56,28,29.80], + +} + +describe('DownsideTasukiGap : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/downsideTasukiGap.png',imageBuffer); + }); + it('Check whether the supplied data has DownsideTasukiGap pattern', function() { + var downsideTasukiGap = new DownsideTasukiGap (); + var result = downsideTasukiGap.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for DownsideTasukiGap'); + }); +}) + diff --git a/test/candlestick/DragonFlyDoji.js b/test/candlestick/DragonFlyDoji.js new file mode 100644 index 0000000..3a85378 --- /dev/null +++ b/test/candlestick/DragonFlyDoji.js @@ -0,0 +1,37 @@ +var DragonFlyDoji = require('../../lib/candlestick/DragonFlyDoji').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [30.10], + high: [30.10], + close: [30.13], + low: [28.10], + +} + +var inputDot = { + open: [30.10], + high: [30.11], + close: [30.10], + low: [30.09], + +} + +describe('DragonFlyDoji : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/dragonFlyDoji.png',imageBuffer); + }); + it('Check whether the supplied data has DragonFlyDoji pattern', function() { + var dragonFlyDoji = new DragonFlyDoji(); + var result = dragonFlyDoji.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for DragonFlyDoji'); + }); + it('Check whether the supplied data has DragonFlyDoji pattern', function() { + var dragonFlyDoji = new DragonFlyDoji(); + var result = dragonFlyDoji.hasPattern(inputDot); + assert.deepEqual(result, false, 'Invalid result for a single point Doji'); + }); +}) diff --git a/test/candlestick/EveningDojiStar.js b/test/candlestick/EveningDojiStar.js new file mode 100644 index 0000000..a1ee58e --- /dev/null +++ b/test/candlestick/EveningDojiStar.js @@ -0,0 +1,26 @@ +var EveningDojiStar = require('../../lib/candlestick/EveningDojiStar').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [18.35,22.20,21.60], + high: [21.60,22.40,22.05], + close: [21.30,22.22,19.45], + low: [18.13,21.87,19.30] +} + +describe('EveningDojiStar : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/EveningDojiStar.png',imageBuffer); + }); + it('Check whether the supplied data has EveningDojiStar pattern', function() { + var eveningDojiStar = new EveningDojiStar (); + var result = eveningDojiStar.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for EveningDojiStar'); + }); +}) + + + diff --git a/test/candlestick/EveningStar.js b/test/candlestick/EveningStar.js new file mode 100644 index 0000000..bb25bc7 --- /dev/null +++ b/test/candlestick/EveningStar.js @@ -0,0 +1,26 @@ +var EveningStar = require('../../lib/candlestick/EveningStar').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [18.35,22.20,21.60], + high: [21.60,22.70,22.05], + close: [21.30,22.52,19.45], + low: [18.13,21.87,19.30] +} + +describe('EveningStar : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/EveningStar.png',imageBuffer); + }); + it('Check whether the supplied data has EveningStar pattern', function() { + var eveningStar = new EveningStar (); + var result = eveningStar.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for EveningStar'); + }); +}) + + + diff --git a/test/candlestick/GraveStoneDoji.js b/test/candlestick/GraveStoneDoji.js new file mode 100644 index 0000000..cdf285f --- /dev/null +++ b/test/candlestick/GraveStoneDoji.js @@ -0,0 +1,37 @@ +var GraveStoneDoji = require('../../lib/candlestick/GraveStoneDoji').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [30.10], + high: [36.13], + close: [30.13], + low: [30.12], + +} + +var inputDot = { + open: [30.10], + high: [30.11], + close: [30.10], + low: [30.09], + +} + +describe('GraveStoneDoji : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/graveStoneDoji.png',imageBuffer); + }); + it('Check whether the supplied data has GraveStoneDoji pattern', function() { + var graveStoneDoji = new GraveStoneDoji(); + var result = graveStoneDoji.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for GraveStoneDoji'); + }); + it('Check whether the supplied data has GraveStoneDoji pattern', function() { + var graveStoneDoji = new GraveStoneDoji(); + var result = graveStoneDoji.hasPattern(inputDot); + assert.deepEqual(result, false, 'Invalid result for a single point Doji'); + }); +}) diff --git a/test/candlestick/HammerPattern.js b/test/candlestick/HammerPattern.js new file mode 100644 index 0000000..2cd41a3 --- /dev/null +++ b/test/candlestick/HammerPattern.js @@ -0,0 +1,59 @@ +var HammerPattern = require('../../lib/candlestick/HammerPattern').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var hammerData = [ + { + name: 'Bearish', + data: { + open: [40.90, 36.00, 33.10, 30.10, 26.13], + high: [41.80, 37.60, 35.90, 30.10, 33.60], + close: [36.00, 33.10, 29.50, 26.13, 31.00], + low: [28.00, 27.70, 26.90, 10.06, 25.90], + }, + }, + { + name: 'Bearish Inverted', + data: { + open: [40.90, 36.00, 33.10, 29.10, 26.13], + high: [41.80, 37.60, 35.90, 36.10, 33.60], + close: [36.00, 33.10, 29.50, 26.13, 31.00], + low: [28.00, 27.70, 26.90, 26.13, 25.90], + }, + }, + { + name: 'Bullish', + data: { + open: [40.90, 36.00, 33.10, 26.13, 30.10], + high: [41.80, 37.60, 35.90, 30.10, 33.60], + close: [36.00, 33.10, 29.50, 30.10, 32.30], + low: [28.00, 27.70, 26.90, 10.06, 25.90], + }, + }, + { + name: 'Bullish Inverted', + data: { + open: [40.90, 36.00, 33.10, 26.13, 29.10], + high: [41.80, 37.60, 35.90, 36.10, 33.60], + close: [36.00, 33.10, 29.50, 29.10, 31.00], + low: [28.00, 27.70, 26.90, 26.13, 25.90], + }, + }, +]; + +describe('Hammer Pattern : ', function() { + before(function() { + hammerData.forEach((patternSet) => { + var imageBuffer = drawCandleStick(patternSet.data); + fs.writeFileSync(`${__dirname}/images/${patternSet.name.replace(' ', '')}HammerPattern.png`,imageBuffer); + }); + }); + hammerData.forEach((patternSet) => { + it(`Check whether the supplied data has Hammer Pattern: ${patternSet.name}`, function() { + var Hammer = new HammerPattern(); + var result = Hammer.hasPattern(patternSet.data); + assert.deepEqual(result, true, `Invalid result for Hammer Pattern: ${patternSet.name}`); + }); + }); +}) diff --git a/test/candlestick/HammerPatternUnconfirmed.js b/test/candlestick/HammerPatternUnconfirmed.js new file mode 100644 index 0000000..f00e65f --- /dev/null +++ b/test/candlestick/HammerPatternUnconfirmed.js @@ -0,0 +1,59 @@ +var HammerPattern = require('../../lib/candlestick/HammerPatternUnconfirmed').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var hammerData = [ + { + name: 'Bearish', + data: { + open: [44.00, 40.90, 36.00, 33.10, 30.10], + high: [45.00, 41.80, 37.60, 35.90, 30.10], + close: [42.00, 36.00, 33.10, 29.50, 26.13], + low: [38.00, 28.00, 27.70, 26.90, 10.06], + }, + }, + { + name: 'Bearish Inverted', + data: { + open: [44.00, 40.90, 36.00, 33.10, 29.10], + high: [45.00, 41.80, 37.60, 35.90, 36.10], + close: [42.00, 36.00, 33.10, 29.50, 26.13], + low: [38.00, 28.00, 27.70, 26.90, 26.13], + }, + }, + { + name: 'Bullish', + data: { + open: [44.00, 40.90, 36.00, 33.10, 26.13], + high: [45.00, 41.80, 37.60, 35.90, 30.10], + close: [42.00, 36.00, 33.10, 29.50, 30.10], + low: [38.00, 28.00, 27.70, 26.90, 10.06], + }, + }, + { + name: 'Bullish Inverted', + data: { + open: [44.00, 40.90, 36.00, 33.10, 26.13], + high: [45.00, 41.80, 37.60, 35.90, 36.10], + close: [42.00, 36.00, 33.10, 29.50, 29.10], + low: [38.00, 28.00, 27.70, 26.90, 26.13], + }, + }, +]; + +describe('Hammer Pattern (Unconfirmed) : ', function() { + before(function() { + hammerData.forEach((patternSet) => { + var imageBuffer = drawCandleStick(patternSet.data); + fs.writeFileSync(`${__dirname}/images/${patternSet.name.replace(' ', '')}HammerPatternUnconfirmed.png`,imageBuffer); + }); + }); + hammerData.forEach((patternSet) => { + it(`Check whether the supplied data has Hammer Pattern (Unconfirmed): ${patternSet.name}`, function() { + var Hammer = new HammerPattern(); + var result = Hammer.hasPattern(patternSet.data); + assert.deepEqual(result, true, `Invalid result for Hammer Pattern (Unconfirmed): ${patternSet.name}`); + }); + }); +}) diff --git a/test/candlestick/HangingMan.js b/test/candlestick/HangingMan.js new file mode 100644 index 0000000..356cdd9 --- /dev/null +++ b/test/candlestick/HangingMan.js @@ -0,0 +1,41 @@ +var HangingMan = require('../../lib/candlestick/HangingMan').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var hangingManData = [ + { + name: 'Bearish', + data: { + open: [29.50, 33.10, 36.00, 42.80, 40.90], + high: [35.90, 37.60, 41.80, 42.80, 43.10], + close: [33.10, 36.00, 40.90, 40.90, 38.05], + low: [26.90, 27.70, 28.00, 33.10, 37.50], + }, + }, + { + name: 'Bullish', + data: { + open: [29.50, 33.10, 36.00, 40.90, 40.90], + high: [35.90, 37.60, 41.80, 42.80, 43.10], + close: [33.10, 36.00, 40.90, 42.80, 38.05], + low: [26.90, 27.70, 28.00, 33.10, 37.50], + }, + }, +]; + +describe('Hanging Man : ', function() { + before(function() { + hangingManData.forEach((patternSet) => { + var imageBuffer = drawCandleStick(patternSet.data); + fs.writeFileSync(`${__dirname}/images/${patternSet.name.replace(' ', '')}HangingMan.png`,imageBuffer); + }); + }); + hangingManData.forEach((patternSet) => { + it(`Check whether the supplied data has Hanging Man: ${patternSet.name}`, function() { + var hangingMan = new HangingMan(); + var result = hangingMan.hasPattern(patternSet.data); + assert.deepEqual(result, true, `Invalid result for Hanging Man: ${patternSet.name}`); + }); + }); +}) diff --git a/test/candlestick/HangingManUnconfirmed.js b/test/candlestick/HangingManUnconfirmed.js new file mode 100644 index 0000000..ffe62ff --- /dev/null +++ b/test/candlestick/HangingManUnconfirmed.js @@ -0,0 +1,41 @@ +var HangingMan = require('../../lib/candlestick/HangingManUnconfirmed').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var hangingManData = [ + { + name: 'Bearish', + data: { + open: [28.90, 29.50, 33.10, 36.00, 42.80], + high: [36.10, 35.90, 37.60, 41.80, 42.80], + close: [29.50, 33.10, 36.00, 40.90, 40.90], + low: [27.00, 26.90, 27.70, 28.00, 33.10], + }, + }, + { + name: 'Bullish', + data: { + open: [28.90, 29.50, 33.10, 36.00, 40.90], + high: [36.10, 35.90, 37.60, 41.80, 42.80], + close: [29.50, 33.10, 36.00, 40.90, 42.80], + low: [27.00, 26.90, 27.70, 28.00, 33.10], + }, + }, +]; + +describe('Hanging Man (Unconfirmed) : ', function() { + before(function() { + hangingManData.forEach((patternSet) => { + var imageBuffer = drawCandleStick(patternSet.data); + fs.writeFileSync(`${__dirname}/images/${patternSet.name.replace(' ', '')}HangingManUnconfirmed.png`,imageBuffer); + }); + }); + hangingManData.forEach((patternSet) => { + it(`Check whether the supplied data has Hanging Man (Unconfirmed): ${patternSet.name}`, function() { + var hangingMan = new HangingMan(); + var result = hangingMan.hasPattern(patternSet.data); + assert.deepEqual(result, true, `Invalid result for Hanging Man (Unconfirmed): ${patternSet.name}`); + }); + }); +}) diff --git a/test/candlestick/MorningDojiStar.js b/test/candlestick/MorningDojiStar.js new file mode 100644 index 0000000..b4828a3 --- /dev/null +++ b/test/candlestick/MorningDojiStar.js @@ -0,0 +1,26 @@ +var MorningDojiStar = require('../../lib/candlestick/MorningDojiStar').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [22.20,20.30,20.70], + high: [22.50,20.45,21.82], + close: [20.80,20.30,21.58], + low: [20.65,20.10,20.40] +} + +describe('MorningDojiStar : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/MorningDojiStar.png',imageBuffer); + }); + it('Check whether the supplied data has MorningDojiStar pattern', function() { + var morningDojiStar = new MorningDojiStar (); + var result = morningDojiStar.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for MorningDojiStar'); + }); +}) + + + diff --git a/test/candlestick/MorningStar.js b/test/candlestick/MorningStar.js new file mode 100644 index 0000000..e3e3d78 --- /dev/null +++ b/test/candlestick/MorningStar.js @@ -0,0 +1,33 @@ +var MorningStar = require('../../lib/candlestick/MorningStar').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +// var input = { +// open: [22.20,19.80,20.70], +// high: [22.50,20.45,21.82], +// close: [20.80,20.30,21.58], +// low: [20.65,19.60,20.40] +// } + +var input = { + open: [22.20,20.30,20.70], + high: [22.50,20.45,21.82], + close: [20.80,19.80,21.58], + low: [20.65,19.60,20.40] +} + +describe('MorningStar : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/MorningStar.png',imageBuffer); + }); + it('Check whether the supplied data has MorningStar pattern', function() { + var morningStar = new MorningStar (); + var result = morningStar.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for MorningStar'); + }); +}) + + + diff --git a/test/candlestick/PiercingLine.js b/test/candlestick/PiercingLine.js new file mode 100644 index 0000000..6288231 --- /dev/null +++ b/test/candlestick/PiercingLine.js @@ -0,0 +1,26 @@ +var PiercingLine = require('../../lib/candlestick/PiercingLine').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [42.70, 41.33], + high: [42.82,42.50], + close: [41.60,42.34], + low: [41.45,41.15], + +} + +describe('PiercingLine : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/PiercingLine.png',imageBuffer); + }); + it('Check whether the supplied data has PiercingLine pattern', function() { + var piercingLine = new PiercingLine (); + var result = piercingLine.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for PiercingLine') + + }); +}) + diff --git a/test/candlestick/ShootingStar.js b/test/candlestick/ShootingStar.js new file mode 100644 index 0000000..0229455 --- /dev/null +++ b/test/candlestick/ShootingStar.js @@ -0,0 +1,41 @@ +var ShootingStar = require('../../lib/candlestick/ShootingStar').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var shootingStarData = [ + { + name: 'Bearish', + data: { + open: [29.50, 33.10, 36.00, 42.80, 40.90], + high: [35.90, 37.60, 41.80, 48.80, 43.10], + close: [33.10, 36.00, 40.90, 40.90, 38.05], + low: [26.90, 27.70, 28.00, 40.90, 37.50], + }, + }, + { + name: 'Bullish', + data: { + open: [29.50, 33.10, 36.00, 40.90, 40.90], + high: [35.90, 37.60, 41.80, 48.80, 43.10], + close: [33.10, 36.00, 40.90, 42.80, 38.05], + low: [26.90, 27.70, 28.00, 40.90, 37.50], + }, + }, +]; + +describe('Shooting Star : ', function() { + before(function() { + shootingStarData.forEach((patternSet) => { + var imageBuffer = drawCandleStick(patternSet.data); + fs.writeFileSync(`${__dirname}/images/${patternSet.name.replace(' ', '')}ShootingStar.png`,imageBuffer); + }); + }); + shootingStarData.forEach((patternSet) => { + it(`Check whether the supplied data has Shooting Star: ${patternSet.name}`, function() { + var hangingMan = new ShootingStar(); + var result = hangingMan.hasPattern(patternSet.data); + assert.deepEqual(result, true, `Invalid result for Shooting Star: ${patternSet.name}`); + }); + }); +}) diff --git a/test/candlestick/ShootingStarUnconfirmed.js b/test/candlestick/ShootingStarUnconfirmed.js new file mode 100644 index 0000000..c3e1b68 --- /dev/null +++ b/test/candlestick/ShootingStarUnconfirmed.js @@ -0,0 +1,41 @@ +var ShootingStar = require('../../lib/candlestick/ShootingStarUnconfirmed').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var shootingStarData = [ + { + name: 'Bearish', + data: { + open: [28.90, 29.50, 33.10, 36.00, 42.80], + high: [36.10, 35.90, 37.60, 41.80, 48.80], + close: [29.50, 33.10, 36.00, 40.90, 40.90], + low: [27.00, 26.90, 27.70, 28.00, 40.90], + }, + }, + { + name: 'Bullish', + data: { + open: [28.90, 29.50, 33.10, 36.00, 40.90], + high: [36.10, 35.90, 37.60, 41.80, 48.80], + close: [29.50, 33.10, 36.00, 40.90, 42.80], + low: [27.00, 26.90, 27.70, 28.00, 40.90], + }, + }, +]; + +describe('Shooting Star (Unconfirmed) : ', function() { + before(function() { + shootingStarData.forEach((patternSet) => { + var imageBuffer = drawCandleStick(patternSet.data); + fs.writeFileSync(`${__dirname}/images/${patternSet.name.replace(' ', '')}ShootingStarUnconfirmed.png`,imageBuffer); + }); + }); + shootingStarData.forEach((patternSet) => { + it(`Check whether the supplied data has Shooting Star (Unconfirmed): ${patternSet.name}`, function() { + var hangingMan = new ShootingStar(); + var result = hangingMan.hasPattern(patternSet.data); + assert.deepEqual(result, true, `Invalid result for Shooting Star (Unconfirmed): ${patternSet.name}`); + }); + }); +}) diff --git a/test/candlestick/ThreeBlackCrows.js b/test/candlestick/ThreeBlackCrows.js new file mode 100644 index 0000000..b435834 --- /dev/null +++ b/test/candlestick/ThreeBlackCrows.js @@ -0,0 +1,26 @@ +var ThreeBlackCrows = require('../../lib/candlestick/ThreeBlackCrows').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [21.65,21.48,21.25], + high: [21.82,21.57,21.35], + close: [21.32,21.10,20.70], + low: [21.25,20.97,20.60] +} + +describe('ThreeBlackCrows : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/ThreeBlackCrows.png',imageBuffer); + }); + it('Check whether the supplied data has ThreeBlackCrows pattern', function() { + var threeBlackCrows = new ThreeBlackCrows (); + var result = threeBlackCrows.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for ThreeBlackCrows'); + }); +}) + + + diff --git a/test/candlestick/ThreeWhiteSoldiers.js b/test/candlestick/ThreeWhiteSoldiers.js new file mode 100644 index 0000000..e730852 --- /dev/null +++ b/test/candlestick/ThreeWhiteSoldiers.js @@ -0,0 +1,26 @@ +var ThreeWhiteSoldiers = require('../../lib/candlestick/ThreeWhiteSoldiers').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var input = { + open: [21.12,21.48,21.80], + close: [21.65,22.20,22.65], + high: [21.83,22.40,22.80], + low: [20.85,21.36,21.66] +} + +describe('ThreeWhiteSoldiers : ', function() { + before(function() { + var imageBuffer = drawCandleStick(input); + fs.writeFileSync(__dirname+'/images/ThreeWhiteSoldiers.png',imageBuffer); + }); + it('Check whether the supplied data has ThreeWhiteSoldiers pattern', function() { + var threeWhiteSoldiers = new ThreeWhiteSoldiers (); + var result = threeWhiteSoldiers.hasPattern(input); + assert.deepEqual(result, true, 'Invalid result for ThreeWhiteSoldiers'); + }); +}) + + + diff --git a/test/candlestick/TweezerBottom.js b/test/candlestick/TweezerBottom.js new file mode 100644 index 0000000..8a3cbdd --- /dev/null +++ b/test/candlestick/TweezerBottom.js @@ -0,0 +1,23 @@ +var TweezerBottom = require('../../lib/candlestick/TweezerBottom').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var testData = { + open: [40.90, 36.00, 33.10, 30.10, 26.13], + high: [41.80, 37.60, 35.90, 31.60, 33.60], + close: [36.00, 33.10, 29.50, 26.13, 31.00], + low: [28.00, 27.70, 26.90, 25.90, 25.90], +}; + +describe('Tweezer Bottom : ', function() { + before(function() { + var imageBuffer = drawCandleStick(testData); + fs.writeFileSync(`${__dirname}/images/TweezerBottom.png`,imageBuffer); + }); + it(`Check whether the supplied data has Tweezer Bottom`, function() { + var tweezerBottom = new TweezerBottom(); + var result = tweezerBottom.hasPattern(testData); + assert.deepEqual(result, true, `Invalid result for Tweezer Bottom`); + }); +}) diff --git a/test/candlestick/TweezerTop.js b/test/candlestick/TweezerTop.js new file mode 100644 index 0000000..2662e82 --- /dev/null +++ b/test/candlestick/TweezerTop.js @@ -0,0 +1,23 @@ +var TweezerTop = require('../../lib/candlestick/TweezerTop').default; +var assert = require('assert'); +var drawCandleStick = require('draw-candlestick'); +var fs = require('fs'); + +var testData = { + open: [29.50, 33.10, 36.00, 40.90, 42.80], + high: [35.90, 37.60, 41.80, 43.10, 43.10], + close: [33.10, 36.00, 40.90, 42.80, 38.05], + low: [26.90, 27.70, 28.00, 39.10, 37.50], +}; + +describe('Tweezer Top : ', function() { + before(function() { + var imageBuffer = drawCandleStick(testData); + fs.writeFileSync(`${__dirname}/images/TweezerTop.png`,imageBuffer); + }); + it(`Check whether the supplied data has Tweezer Top`, function() { + var tweezerTop = new TweezerTop(); + var result = tweezerTop.hasPattern(testData); + assert.deepEqual(result, true, `Invalid result for Tweezer Top`); + }); +}) diff --git a/test/candlestick/images/BearishHammerPattern.png b/test/candlestick/images/BearishHammerPattern.png new file mode 100644 index 0000000..e07a81c Binary files /dev/null and b/test/candlestick/images/BearishHammerPattern.png differ diff --git a/test/candlestick/images/BearishHammerPatternUnconfirmed.png b/test/candlestick/images/BearishHammerPatternUnconfirmed.png new file mode 100644 index 0000000..47fabfc Binary files /dev/null and b/test/candlestick/images/BearishHammerPatternUnconfirmed.png differ diff --git a/test/candlestick/images/BearishHammerStick.png b/test/candlestick/images/BearishHammerStick.png new file mode 100644 index 0000000..7f467fb Binary files /dev/null and b/test/candlestick/images/BearishHammerStick.png differ diff --git a/test/candlestick/images/BearishHangingMan.png b/test/candlestick/images/BearishHangingMan.png new file mode 100644 index 0000000..6e51ee4 Binary files /dev/null and b/test/candlestick/images/BearishHangingMan.png differ diff --git a/test/candlestick/images/BearishHangingManUnconfirmed.png b/test/candlestick/images/BearishHangingManUnconfirmed.png new file mode 100644 index 0000000..213bfea Binary files /dev/null and b/test/candlestick/images/BearishHangingManUnconfirmed.png differ diff --git a/test/candlestick/images/BearishHarami.png b/test/candlestick/images/BearishHarami.png new file mode 100644 index 0000000..6a5a020 Binary files /dev/null and b/test/candlestick/images/BearishHarami.png differ diff --git a/test/candlestick/images/BearishHaramiCross.png b/test/candlestick/images/BearishHaramiCross.png new file mode 100644 index 0000000..5d7cee6 Binary files /dev/null and b/test/candlestick/images/BearishHaramiCross.png differ diff --git a/test/candlestick/images/BearishInvertedHammerPattern.png b/test/candlestick/images/BearishInvertedHammerPattern.png new file mode 100644 index 0000000..80ea178 Binary files /dev/null and b/test/candlestick/images/BearishInvertedHammerPattern.png differ diff --git a/test/candlestick/images/BearishInvertedHammerPatternUnconfirmed.png b/test/candlestick/images/BearishInvertedHammerPatternUnconfirmed.png new file mode 100644 index 0000000..0a163d4 Binary files /dev/null and b/test/candlestick/images/BearishInvertedHammerPatternUnconfirmed.png differ diff --git a/test/candlestick/images/BearishInvertedHammerStick.png b/test/candlestick/images/BearishInvertedHammerStick.png new file mode 100644 index 0000000..28ba9fb Binary files /dev/null and b/test/candlestick/images/BearishInvertedHammerStick.png differ diff --git a/test/candlestick/images/BearishMarubozu.png b/test/candlestick/images/BearishMarubozu.png new file mode 100644 index 0000000..4f18471 Binary files /dev/null and b/test/candlestick/images/BearishMarubozu.png differ diff --git a/test/candlestick/images/BearishShootingStar.png b/test/candlestick/images/BearishShootingStar.png new file mode 100644 index 0000000..744bd32 Binary files /dev/null and b/test/candlestick/images/BearishShootingStar.png differ diff --git a/test/candlestick/images/BearishShootingStarUnconfirmed.png b/test/candlestick/images/BearishShootingStarUnconfirmed.png new file mode 100644 index 0000000..916087a Binary files /dev/null and b/test/candlestick/images/BearishShootingStarUnconfirmed.png differ diff --git a/test/candlestick/images/BearishSpinningTop.png b/test/candlestick/images/BearishSpinningTop.png new file mode 100644 index 0000000..8cc38dd Binary files /dev/null and b/test/candlestick/images/BearishSpinningTop.png differ diff --git a/test/candlestick/images/Bullish.png b/test/candlestick/images/Bullish.png new file mode 100644 index 0000000..2ea7f52 Binary files /dev/null and b/test/candlestick/images/Bullish.png differ diff --git a/test/candlestick/images/BullishHammerPattern.png b/test/candlestick/images/BullishHammerPattern.png new file mode 100644 index 0000000..fabbae4 Binary files /dev/null and b/test/candlestick/images/BullishHammerPattern.png differ diff --git a/test/candlestick/images/BullishHammerPatternUnconfirmed.png b/test/candlestick/images/BullishHammerPatternUnconfirmed.png new file mode 100644 index 0000000..27711de Binary files /dev/null and b/test/candlestick/images/BullishHammerPatternUnconfirmed.png differ diff --git a/test/candlestick/images/BullishHammerStick.png b/test/candlestick/images/BullishHammerStick.png new file mode 100644 index 0000000..62416e9 Binary files /dev/null and b/test/candlestick/images/BullishHammerStick.png differ diff --git a/test/candlestick/images/BullishHangingMan.png b/test/candlestick/images/BullishHangingMan.png new file mode 100644 index 0000000..be2b6eb Binary files /dev/null and b/test/candlestick/images/BullishHangingMan.png differ diff --git a/test/candlestick/images/BullishHangingManUnconfirmed.png b/test/candlestick/images/BullishHangingManUnconfirmed.png new file mode 100644 index 0000000..d3429a0 Binary files /dev/null and b/test/candlestick/images/BullishHangingManUnconfirmed.png differ diff --git a/test/candlestick/images/BullishHarami.png b/test/candlestick/images/BullishHarami.png new file mode 100644 index 0000000..cb1f9e0 Binary files /dev/null and b/test/candlestick/images/BullishHarami.png differ diff --git a/test/candlestick/images/BullishHaramiCross.png b/test/candlestick/images/BullishHaramiCross.png new file mode 100644 index 0000000..30f56ea Binary files /dev/null and b/test/candlestick/images/BullishHaramiCross.png differ diff --git a/test/candlestick/images/BullishInvertedHammerPattern.png b/test/candlestick/images/BullishInvertedHammerPattern.png new file mode 100644 index 0000000..7b09cc1 Binary files /dev/null and b/test/candlestick/images/BullishInvertedHammerPattern.png differ diff --git a/test/candlestick/images/BullishInvertedHammerPatternUnconfirmed.png b/test/candlestick/images/BullishInvertedHammerPatternUnconfirmed.png new file mode 100644 index 0000000..cd1355c Binary files /dev/null and b/test/candlestick/images/BullishInvertedHammerPatternUnconfirmed.png differ diff --git a/test/candlestick/images/BullishInvertedHammerStick.png b/test/candlestick/images/BullishInvertedHammerStick.png new file mode 100644 index 0000000..7e980d6 Binary files /dev/null and b/test/candlestick/images/BullishInvertedHammerStick.png differ diff --git a/test/candlestick/images/BullishMarubozu.png b/test/candlestick/images/BullishMarubozu.png new file mode 100644 index 0000000..fdccb07 Binary files /dev/null and b/test/candlestick/images/BullishMarubozu.png differ diff --git a/test/candlestick/images/BullishShootingStar.png b/test/candlestick/images/BullishShootingStar.png new file mode 100644 index 0000000..ec32267 Binary files /dev/null and b/test/candlestick/images/BullishShootingStar.png differ diff --git a/test/candlestick/images/BullishShootingStarUnconfirmed.png b/test/candlestick/images/BullishShootingStarUnconfirmed.png new file mode 100644 index 0000000..95c6912 Binary files /dev/null and b/test/candlestick/images/BullishShootingStarUnconfirmed.png differ diff --git a/test/candlestick/images/BullishSpinningTop.png b/test/candlestick/images/BullishSpinningTop.png new file mode 100644 index 0000000..3904d97 Binary files /dev/null and b/test/candlestick/images/BullishSpinningTop.png differ diff --git a/test/candlestick/images/EveningDojiStar.png b/test/candlestick/images/EveningDojiStar.png new file mode 100644 index 0000000..c72024e Binary files /dev/null and b/test/candlestick/images/EveningDojiStar.png differ diff --git a/test/candlestick/images/EveningStar.png b/test/candlestick/images/EveningStar.png new file mode 100644 index 0000000..9c87993 Binary files /dev/null and b/test/candlestick/images/EveningStar.png differ diff --git a/test/candlestick/images/MorningDojiStar.png b/test/candlestick/images/MorningDojiStar.png new file mode 100644 index 0000000..70d6202 Binary files /dev/null and b/test/candlestick/images/MorningDojiStar.png differ diff --git a/test/candlestick/images/MorningStar.png b/test/candlestick/images/MorningStar.png new file mode 100644 index 0000000..bf5cd7b Binary files /dev/null and b/test/candlestick/images/MorningStar.png differ diff --git a/test/candlestick/images/PiercingLine.png b/test/candlestick/images/PiercingLine.png new file mode 100644 index 0000000..288ba77 Binary files /dev/null and b/test/candlestick/images/PiercingLine.png differ diff --git a/test/candlestick/images/ThreeBlackCrows.png b/test/candlestick/images/ThreeBlackCrows.png new file mode 100644 index 0000000..20c75ec Binary files /dev/null and b/test/candlestick/images/ThreeBlackCrows.png differ diff --git a/test/candlestick/images/ThreeWhiteSoldiers.png b/test/candlestick/images/ThreeWhiteSoldiers.png new file mode 100644 index 0000000..2ea7f52 Binary files /dev/null and b/test/candlestick/images/ThreeWhiteSoldiers.png differ diff --git a/test/candlestick/images/TweezerBottom.png b/test/candlestick/images/TweezerBottom.png new file mode 100644 index 0000000..b110e65 Binary files /dev/null and b/test/candlestick/images/TweezerBottom.png differ diff --git a/test/candlestick/images/TweezerTop.png b/test/candlestick/images/TweezerTop.png new file mode 100644 index 0000000..31b7b8e Binary files /dev/null and b/test/candlestick/images/TweezerTop.png differ diff --git a/test/candlestick/images/abandonedbaby.png b/test/candlestick/images/abandonedbaby.png new file mode 100644 index 0000000..124435f Binary files /dev/null and b/test/candlestick/images/abandonedbaby.png differ diff --git a/test/candlestick/images/bearish.png b/test/candlestick/images/bearish.png new file mode 100644 index 0000000..500558a Binary files /dev/null and b/test/candlestick/images/bearish.png differ diff --git a/test/candlestick/images/bearishEngulfing.png b/test/candlestick/images/bearishEngulfing.png new file mode 100644 index 0000000..500558a Binary files /dev/null and b/test/candlestick/images/bearishEngulfing.png differ diff --git a/test/candlestick/images/bullishEngulfingPattern.png b/test/candlestick/images/bullishEngulfingPattern.png new file mode 100644 index 0000000..7ad3903 Binary files /dev/null and b/test/candlestick/images/bullishEngulfingPattern.png differ diff --git a/test/candlestick/images/darkCloudCover.png b/test/candlestick/images/darkCloudCover.png new file mode 100644 index 0000000..7e6f0b6 Binary files /dev/null and b/test/candlestick/images/darkCloudCover.png differ diff --git a/test/candlestick/images/doji.png b/test/candlestick/images/doji.png new file mode 100644 index 0000000..d149409 Binary files /dev/null and b/test/candlestick/images/doji.png differ diff --git a/test/candlestick/images/downsideTasukiGap.png b/test/candlestick/images/downsideTasukiGap.png new file mode 100644 index 0000000..08112cc Binary files /dev/null and b/test/candlestick/images/downsideTasukiGap.png differ diff --git a/test/candlestick/images/dragonFlyDoji.png b/test/candlestick/images/dragonFlyDoji.png new file mode 100644 index 0000000..c7d45d1 Binary files /dev/null and b/test/candlestick/images/dragonFlyDoji.png differ diff --git a/test/candlestick/images/graveStoneDoji.png b/test/candlestick/images/graveStoneDoji.png new file mode 100644 index 0000000..dd907ad Binary files /dev/null and b/test/candlestick/images/graveStoneDoji.png differ diff --git a/test/chart_types/HeikinAshi.js b/test/chart_types/HeikinAshi.js new file mode 100644 index 0000000..4d2afdd --- /dev/null +++ b/test/chart_types/HeikinAshi.js @@ -0,0 +1,104 @@ +/** + * Created by AAravindan on 5/4/16. + */ +var HeikinAshi = require('../../lib/chart_types/HeikinAshi').HeikinAshi; +var assert = require('assert'); + +var data = { + open : [58.67,57.46,56.37,55.98,54.79,52.21,51.31,51.82,51.58], + high : [58.82,57.72,56.88,56.09,55.03,53.12,53.08,50.04,53.69], + low : [57.03,56.21,55.35,54.17,52.32,50.59,49.93,50.80,51.34], + close: [57.73,56.27,56.81,54.17,53.83,50.59,53.03,50.86,53.10] +} + +let expectedOutput = { + "close": [ + 58.0625, + 56.915, + 56.3525, + 55.102500000000006, + 53.9925, + 51.627500000000005, + 51.837500000000006, + 50.879999999999995, + 52.4275, + ], + "high": [ + 58.82, + 58.13125, + 57.523125, + 56.9378125, + 56.02015625, + 55.006328124999996, + 53.316914062500004, + 52.577207031250005, + 53.69, + ], + "low": [ + 57.03, + 56.21, + 55.35, + 54.17, + 52.32, + 50.59, + 49.93, + 50.8, + 51.34, + ], + "open": [ + 58.2, + 58.13125, + 57.523125, + 56.9378125, + 56.02015625, + 55.006328124999996, + 53.316914062500004, + 52.577207031250005, + 51.728603515625, + ], + "timestamp": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "volume": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ] + } + +describe('HeikinAshi (Calculate Heikin ashi bars)', function() { + let input = {}; + beforeEach(function(){ + input = JSON.parse(JSON.stringify(data)); + }); + + it('should calculate HeikinAshi using the calculate method', function() { + assert.deepEqual(HeikinAshi.calculate(input), expectedOutput,'Wrong Results'); + }); + + it('should be able to get HeikinAshi for the next bar using nextValue', function() { + input.values = []; + var heikinAshi = new HeikinAshi(input); + var result = heikinAshi.nextValue({ + open : 53.42, + high: 53.90, + low : 52.88, + close : 53.57 + }); + assert.deepEqual(result, { timestamp : 0, volume: 0, open : 52.0780517578125, high : 53.90, low : 52.0780517578125, close : 53.4425}, 'Wrong Results'); + }); +}); \ No newline at end of file diff --git a/test/chart_types/Renko.js b/test/chart_types/Renko.js new file mode 100644 index 0000000..ee1c4a6 --- /dev/null +++ b/test/chart_types/Renko.js @@ -0,0 +1,34 @@ +/** + * Created by AAravindan on 5/3/16. + */ +// var renko = require('../../lib/chart_types/Renko').renko; +var renko = require('../../dist/index').renko; +var assert = require('assert'); +var data = { "close": [0.08099359, 0.08249997, 0.08375944, 0.08265, 0.08257601, 0.08185048, 0.081875, 0.0814, 0.08206313, 0.08237554, 0.08228, 0.08252799, 0.08200003, 0.0818, 0.08190021, 0.0818, 0.08178776, 0.0815, 0.08170006, 0.08194, 0.08176177, 0.08200007, 0.08220989, 0.0819413, 0.081744, 0.08181004, 0.08235, 0.082, 0.08266197, 0.08190536, 0.08205642, 0.08250018, 0.0825, 0.08291223, 0.08235927, 0.0825, 0.08231915, 0.08245641, 0.08230011, 0.08269996, 0.0825, 0.08284069, 0.08281361, 0.0828, 0.08294917, 0.08345978, 0.08349999, 0.08335549, 0.08320776, 0.08374852, 0.08391653, 0.082852, 0.08309751, 0.0833, 0.08344999, 0.0829, 0.08284161, 0.08327523, 0.08284161, 0.08307577, 0.08314206, 0.08299998, 0.08210986, 0.08218993, 0.08279002, 0.0829998, 0.0826761, 0.08277, 0.08258964, 0.0827215, 0.08245774, 0.08232142, 0.08219, 0.08242144, 0.08242123, 0.08242123, 0.08200918, 0.08210004, 0.082, 0.082, 0.08200001, 0.08222, 0.08196199, 0.08233845, 0.082127, 0.0821, 0.08208251, 0.08203, 0.08193041, 0.08199735, 0.08222853, 0.08236529, 0.08200539, 0.08196334, 0.082, 0.0817783, 0.08200292, 0.081536, 0.08199997, 0.08199999, 0.08175, 0.08151002, 0.08128116, 0.0813, 0.08141209, 0.0812, 0.0814, 0.08135, 0.08128817, 0.08118002, 0.08101, 0.08116935, 0.0806, 0.08081181, 0.0815698, 0.08100001, 0.08070881, 0.0794198, 0.07972, 0.08000022, 0.0801, 0.07989775, 0.08104112, 0.07980354, 0.07932534, 0.079789, 0.08025398, 0.07991237, 0.08017894, 0.08038985, 0.0804123, 0.0803, 0.08057547, 0.0805, 0.08039999, 0.08, 0.08040041, 0.08030036, 0.08, 0.08, 0.08013476, 0.08, 0.0803, 0.078, 0.0776843, 0.0784, 0.07821, 0.07839989, 0.07900003, 0.0795, 0.07974198, 0.08013458, 0.0798771, 0.07994005, 0.08, 0.07949749, 0.07928094, 0.0792202, 0.07929997, 0.07993136, 0.07945756, 0.07848047, 0.07881003, 0.07900109, 0.07935699, 0.079379, 0.08000145, 0.08019319, 0.079823, 0.07982159, 0.08021171, 0.08006, 0.08056517, 0.080555, 0.081, 0.08058912, 0.0805, 0.08048139, 0.08048139, 0.08055969, 0.07988281, 0.07990026, 0.08055998, 0.08014437, 0.08019], "high": [0.081, 0.08296429, 0.0838, 0.08385043, 0.08292382, 0.08273745, 0.08234938, 0.08234919, 0.08222222, 0.08266665, 0.0828, 0.08269998, 0.08252799, 0.08233005, 0.08241875, 0.08241876, 0.08200338, 0.0819952, 0.08241491, 0.08209999, 0.08214998, 0.08219999, 0.08229999, 0.08237996, 0.08212189, 0.08226764, 0.08241583, 0.0829974, 0.08266197, 0.083, 0.08298966, 0.08322042, 0.0829998, 0.08291227, 0.08291223, 0.0825113, 0.0825113, 0.08269788, 0.082599, 0.0827, 0.08271355, 0.08287713, 0.08299, 0.08309507, 0.0830951, 0.08345978, 0.0836, 0.08349999, 0.08338998, 0.08375, 0.08399999, 0.08397489, 0.08329939, 0.08344999, 0.08344999, 0.08349904, 0.08329, 0.08329, 0.08334372, 0.08328635, 0.0834, 0.08349903, 0.08323875, 0.08234994, 0.083, 0.083, 0.08309999, 0.08305835, 0.083045, 0.08291006, 0.08291117, 0.08283997, 0.08232144, 0.08242144, 0.08284995, 0.08268749, 0.08254855, 0.08225665, 0.08225665, 0.0826, 0.08234786, 0.0824, 0.0823, 0.0824, 0.0824, 0.08249648, 0.08249525, 0.08244227, 0.08244227, 0.0823585, 0.08235854, 0.082496, 0.08293048, 0.08239992, 0.08209999, 0.08235129, 0.08200847, 0.082089, 0.08200099, 0.082, 0.08199999, 0.0819999, 0.08199999, 0.0813, 0.0815441, 0.08199999, 0.08190104, 0.0818, 0.0818, 0.08156967, 0.0815698, 0.08116937, 0.08116936, 0.08114994, 0.0815698, 0.08171907, 0.08127766, 0.08079999, 0.08030975, 0.081, 0.08050683, 0.08014988, 0.08104112, 0.0812, 0.0800004, 0.07999048, 0.08083898, 0.08069997, 0.081, 0.081, 0.081, 0.08079805, 0.08079802, 0.08068147, 0.08075599, 0.08049999, 0.0806, 0.08082917, 0.08038036, 0.08031036, 0.08041036, 0.08036901, 0.08036898, 0.0803, 0.0792, 0.07876002, 0.07868, 0.0785, 0.07929999, 0.08089988, 0.08048457, 0.08061, 0.0803943, 0.0808, 0.08035053, 0.08011004, 0.0796, 0.079588, 0.079588, 0.0802575, 0.07999998, 0.07999999, 0.07961228, 0.07950043, 0.0793576, 0.0797, 0.08000145, 0.08084602, 0.08019319, 0.08019313, 0.08027959, 0.08056564, 0.08056565, 0.08059989, 0.081, 0.08103424, 0.08102475, 0.08061445, 0.08075333, 0.08068677, 0.08055969, 0.08023, 0.08055998, 0.0808, 0.08041597], "low": [0.08042753, 0.0809558, 0.08197926, 0.08259678, 0.082, 0.08185043, 0.0816, 0.08072626, 0.08122092, 0.0819635, 0.082, 0.08180001, 0.08183151, 0.0818, 0.08179999, 0.08164137, 0.08123003, 0.0806638, 0.08150001, 0.0813, 0.08140004, 0.08167999, 0.0813001, 0.08177783, 0.08154048, 0.08168938, 0.08181, 0.08190007, 0.08181602, 0.0818, 0.08182, 0.08198673, 0.08205642, 0.08220135, 0.08182, 0.08190324, 0.08224815, 0.08228444, 0.0823, 0.08230011, 0.0823001, 0.0823, 0.08237336, 0.08240102, 0.082401, 0.08294917, 0.08298349, 0.08267687, 0.08280057, 0.0831, 0.08298054, 0.08274618, 0.082822, 0.08271595, 0.08329, 0.08277205, 0.08277768, 0.08284161, 0.08284161, 0.08284161, 0.08303864, 0.08245815, 0.08210986, 0.08165223, 0.08205001, 0.0826005, 0.0824, 0.082501, 0.08258964, 0.08220003, 0.08220004, 0.0823214, 0.0820155, 0.0820155, 0.0822429, 0.0824212, 0.08200916, 0.08195001, 0.08162001, 0.08164815, 0.08181299, 0.0818889, 0.08166, 0.08170911, 0.08170009, 0.08193028, 0.08193028, 0.08193028, 0.08193028, 0.08193028, 0.08194701, 0.082148, 0.08193028, 0.08170028, 0.08161954, 0.0817, 0.0817, 0.0815, 0.08134738, 0.08151014, 0.08163, 0.08134739, 0.0812, 0.08059, 0.0808422, 0.08118817, 0.0812, 0.08130002, 0.081, 0.0809, 0.08101, 0.08101, 0.08058, 0.0806, 0.08062104, 0.08086527, 0.08060102, 0.07924775, 0.079, 0.07961797, 0.0797, 0.0795, 0.0797, 0.07959775, 0.07922221, 0.07912001, 0.07952003, 0.07978568, 0.07986311, 0.080015, 0.08018, 0.0803, 0.08008949, 0.08014726, 0.08014726, 0.0798, 0.08, 0.08030036, 0.08, 0.08, 0.07980002, 0.07969999, 0.07959025, 0.07799999, 0.07741907, 0.0774, 0.07777018, 0.078, 0.07809809, 0.07900003, 0.07906656, 0.07959638, 0.07917127, 0.07984076, 0.07944975, 0.0786, 0.07870004, 0.07870021, 0.0789, 0.07902151, 0.07913, 0.0782, 0.07837983, 0.0788, 0.07870068, 0.07900001, 0.079, 0.07979509, 0.07940102, 0.07940101, 0.07981508, 0.07991018, 0.08, 0.08000006, 0.08040006, 0.08040004, 0.08040004, 0.0802766, 0.080386, 0.08, 0.07988281, 0.07973161, 0.07990035, 0.0800159, 0.08014442], "open": [0.081, 0.081, 0.08249997, 0.08375944, 0.08265, 0.08257601, 0.08229943, 0.081875, 0.08140001, 0.08206313, 0.08226129, 0.08228, 0.08252799, 0.08200003, 0.08180081, 0.08190021, 0.0818, 0.08178776, 0.08150001, 0.08170005, 0.08194, 0.08186098, 0.08200007, 0.08220989, 0.0819, 0.081744, 0.08226765, 0.08235, 0.082, 0.08259585, 0.08190536, 0.08205642, 0.08250018, 0.0825, 0.08291223, 0.08235927, 0.0825, 0.08231915, 0.08256041, 0.08230011, 0.08269996, 0.08230012, 0.08284069, 0.08286492, 0.0829, 0.08294917, 0.0834, 0.08349999, 0.08338996, 0.08320776, 0.08374852, 0.08397474, 0.08320716, 0.08309751, 0.0833, 0.08344999, 0.0831292, 0.08284161, 0.08327523, 0.08284161, 0.08307577, 0.08314206, 0.08299998, 0.08210986, 0.08205001, 0.08260051, 0.0828, 0.08300221, 0.08277, 0.08258964, 0.0827215, 0.08283997, 0.08232142, 0.08219, 0.08242144, 0.08242123, 0.08242122, 0.08224426, 0.08195001, 0.082, 0.082, 0.08234783, 0.0823, 0.08196199, 0.0823385, 0.08197909, 0.08200001, 0.08208251, 0.08203, 0.08193041, 0.08194701, 0.08222853, 0.08244999, 0.08200539, 0.08196334, 0.082, 0.08177438, 0.08200292, 0.081536, 0.08199997, 0.08199999, 0.0816515, 0.08151002, 0.08128498, 0.0813, 0.08141209, 0.0812, 0.0814, 0.081355, 0.08128817, 0.08118002, 0.08101, 0.08116935, 0.08065, 0.08081181, 0.0815698, 0.08100001, 0.08070881, 0.0794198, 0.07972, 0.08039994, 0.0801, 0.07989775, 0.08104122, 0.07980354, 0.07932536, 0.07978651, 0.07991124, 0.07991237, 0.08017894, 0.0802521, 0.0804123, 0.08079802, 0.08053094, 0.0805, 0.08039999, 0.08, 0.0805, 0.08030036, 0.08, 0.08, 0.08013476, 0.08015909, 0.0802, 0.0777, 0.07770003, 0.07840001, 0.07845479, 0.07839988, 0.07900003, 0.0795, 0.07974198, 0.08013458, 0.0798771, 0.08028894, 0.08, 0.07949749, 0.07928094, 0.07928, 0.07929997, 0.07985749, 0.07945756, 0.07848047, 0.07881003, 0.07901427, 0.07935699, 0.07937899, 0.08000145, 0.08, 0.079823, 0.07982159, 0.08021171, 0.08006899, 0.08056517, 0.080555, 0.081, 0.08040009, 0.0805, 0.08048139, 0.08048139, 0.08055969, 0.07988281, 0.08023, 0.08055998, 0.08025], "timestamp": [1500759000000, 1500760800000, 1500762600000, 1500764400000, 1500766200000, 1500768000000, 1500769800000, 1500771600000, 1500773400000, 1500775200000, 1500777000000, 1500778800000, 1500780600000, 1500782400000, 1500784200000, 1500786000000, 1500787800000, 1500789600000, 1500791400000, 1500793200000, 1500795000000, 1500796800000, 1500798600000, 1500800400000, 1500802200000, 1500804000000, 1500805800000, 1500807600000, 1500809400000, 1500811200000, 1500813000000, 1500814800000, 1500816600000, 1500818400000, 1500820200000, 1500822000000, 1500823800000, 1500825600000, 1500827400000, 1500829200000, 1500831000000, 1500832800000, 1500834600000, 1500836400000, 1500838200000, 1500840000000, 1500841800000, 1500843600000, 1500845400000, 1500847200000, 1500849000000, 1500850800000, 1500852600000, 1500854400000, 1500856200000, 1500858000000, 1500859800000, 1500861600000, 1500863400000, 1500865200000, 1500867000000, 1500868800000, 1500870600000, 1500872400000, 1500874200000, 1500876000000, 1500877800000, 1500879600000, 1500881400000, 1500883200000, 1500885000000, 1500886800000, 1500888600000, 1500890400000, 1500892200000, 1500894000000, 1500895800000, 1500897600000, 1500899400000, 1500901200000, 1500903000000, 1500904800000, 1500906600000, 1500908400000, 1500910200000, 1500912000000, 1500913800000, 1500915600000, 1500917400000, 1500919200000, 1500921000000, 1500922800000, 1500924600000, 1500926400000, 1500928200000, 1500930000000, 1500931800000, 1500933600000, 1500935400000, 1500937200000, 1500939000000, 1500940800000, 1500942600000, 1500944400000, 1500946200000, 1500948000000, 1500949800000, 1500951600000, 1500953400000, 1500955200000, 1500957000000, 1500958800000, 1500960600000, 1500962400000, 1500964200000, 1500966000000, 1500967800000, 1500969600000, 1500971400000, 1500973200000, 1500975000000, 1500976800000, 1500978600000, 1500980400000, 1500982200000, 1500984000000, 1500985800000, 1500987600000, 1500989400000, 1500991200000, 1500993000000, 1500994800000, 1500996600000, 1500998400000, 1501000200000, 1501002000000, 1501003800000, 1501005600000, 1501007400000, 1501009200000, 1501011000000, 1501012800000, 1501014600000, 1501016400000, 1501018200000, 1501020000000, 1501021800000, 1501023600000, 1501025400000, 1501027200000, 1501029000000, 1501030800000, 1501032600000, 1501034400000, 1501036200000, 1501038000000, 1501039800000, 1501041600000, 1501043400000, 1501045200000, 1501047000000, 1501048800000, 1501050600000, 1501052400000, 1501054200000, 1501056000000, 1501057800000, 1501059600000, 1501061400000, 1501063200000, 1501065000000, 1501066800000, 1501068600000, 1501070400000, 1501072200000, 1501074000000, 1501075800000, 1501077600000, 1501079400000, 1501081200000, 1501083000000, 1501084800000, 1501086600000, 1501088400000, 1501090200000], "volume": [309, 170, 221, 677, 251, 491, 750, 632, 1035, 844, 540, 351, 254, 239, 364, 125, 946, 620, 600, 669, 365, 395, 662, 1197, 1260, 696, 380, 540, 434, 1048, 324, 409, 389, 366, 700, 1280, 556, 1007, 778, 1830, 2475, 2146, 447, 498, 465, 1507, 723, 882, 328, 397, 520, 457, 655, 737, 636, 733, 1358, 1040, 1633, 880, 1465, 2016, 1602, 355, 227, 819, 1182, 1556, 931, 746, 886, 236, 465, 431, 370, 581, 594, 264, 252, 919, 481, 1194, 532, 331, 218, 679, 1536, 519, 224, 668, 573, 802, 1155, 407, 596, 585, 444, 904, 1108, 831, 1076, 863, 731, 457, 403, 880, 598, 448, 478, 674, 398, 666, 448, 355, 273, 346, 680, 642, 672, 947, 751, 1249, 1121, 1818, 321, 313, 742, 423, 105, 437, 711, 744, 273, 570, 736, 448, 611, 663, 857, 856, 314, 290, 300, 256, 292, 453, 262, 707, 583, 455, 627, 283, 442, 935, 1366, 1375, 552, 1009, 750, 602, 438, 762, 1174, 887, 885, 1361, 1588, 1383, 1759, 529, 725, 652, 500, 786, 489, 732, 737, 1535, 1712, 1435, 1554, 1154, 1864, 3187, 400], "ticker": "BITTREX:BTC-ETH" } + +var expectedOutput = []; +var period = 14; + +var result = renko(Object.assign({}, data, {brickSize : 0.001, useATR : false })); +// console.log(result); +// console.log(data.volume.length) +// console.log(data.timestamp.length) +// console.log(result.volume.length) +// console.log(result.timestamp.length) + +// var result = renko(Object.assign({}, data, {period : 14 , useATR : true })); +// console.log(result); + + +// describe('Renko (Exponential Moving Average)', function () { +// it('should calculate renko using the fixed bar', function () { +// let result = renko(Object.assign({}, data, {brickSize : 0.1, useATR : false })); +// console.log(result) +// assert.deepEqual(result, expectedOutput, 'Wrong Results'); +// }); + +// it('should be able to get Renko using atr', function () { +// var result = renko(Object.assign({}, data, {period : 14 , useATR : true })); +// assert.deepEqual(result, expectedOutput, 'Wrong Results while getting results'); +// }); +// }) diff --git a/test/chart_types/TypicalPrice.js b/test/chart_types/TypicalPrice.js new file mode 100644 index 0000000..7f1f173 --- /dev/null +++ b/test/chart_types/TypicalPrice.js @@ -0,0 +1,31 @@ +var TypicalPrice = require('../../lib/chart_types/TypicalPrice').TypicalPrice; +var assert = require('assert'); + +var data = { + high : [1, 4, 1, 2, 1], + low : [2, 5, 2, 2, 1], + close: [3, 6, 4, 2, 1] +} + +var expectedOutput = [(1 + 2 + 3)/ 3, (4 + 5 +6)/ 3, (1 + 2 + 4)/ 3, 2, 1]; + +describe('TypicalPrice ', function() { + let input = {}; + beforeEach(function(){ + input = JSON.parse(JSON.stringify(data)); + }); + it('should calculate TypicalPrice using the calculate method', function() { + assert.deepEqual(TypicalPrice.calculate(input), expectedOutput,'Wrong Results'); + }); + + it('should be able to get TypicalPrice for the next bar using nextValue', function() { + input.values = []; + var typicalPrice = new TypicalPrice(input); + var result = typicalPrice.nextValue({ + high: 4, + low : 4, + close : 4 + }); + assert.deepEqual(result, 4, 'Wrong Results'); + }); + }); \ No newline at end of file diff --git a/test/data.js b/test/data.js new file mode 100644 index 0000000..7b89e09 --- /dev/null +++ b/test/data.js @@ -0,0 +1,28 @@ +/** + * Created by AAravindan on 5/6/16. + */ +module.exports = { + close : [127.75,129.02,132.75,145.40,148.98,137.52,147.38,139.05,137.23,149.30,162.45,178.95,200.35,221.90,243.23,243.52,286.42,280.27], + open : [], + high : [], + low : [], + volume: [] +}; + +//search for candle. +//Have a debug point in s.prototype.renderer, save this to temp +//s.prototype.renderer = function() { +// var e, t; +// return this._invalidated && (this._updateImpl(), +//temp1.m_data.m_bars._items.forEach(function(data){ +// console.log(data.value[4]); +//}); + +//var close = [127.75,129.024995,132.75,145.399995,148.975005,137.524995,147.375,139.050005,137.225005,149.300005,162.449995,178.949995,200.350005,221.899995,243.225005,243.524995,286.42499,280.274995,277.350005,269.024995,263.225005,214.899995,214.824995,244.449995,237.824995,258.95001,269.29999,258.524995,266.850005,266.149995,278.125,273.024995,264.024995,267.5,321.274995,351.67499,359.70001,396.774995,381.25,416.350005,444.649995,356.95001,354.42499,397.67499,498.975005,477.475005,529.925,489.399995,558.675,585.875,613,622.225,635.075,683.225,676.25,685.15,780.525,879.4,850.275,946.75,977,1149.025,1391.9,1425.82495,1441.35,1239.44995,1231.725,1132.9,1307.25,1201.75,1047.57495,1103.75,1068.1,974.675,687.725,567.225,616.375,661.8,633.025,762.375,903.125,1135.94995,1011.7,977.7,1002.55,1100.82495,965.575,1063.5,1090.55,1046.2,978.95001,1074.25,1033.6,1045.6,1089.85,1009.65,919.20001,987.25,1096.25,985.59998,1058.7,919.29999,964.25,1049.1,983.75,951.84998,898.5,827.95001,782.59998,808.34998,877.54999,778.25,692.95001,817.09998,820.75,750.59998,745.09998,705.59998,737.84998,743.59998,766.40002,837.20001,805.5,793.70001,839.54999,886.65002,813.84998,772.90002,786.09998,805,860.45001,875.54999,849.5,821.5,915,852,894,830.54999,799.95001,930.75,935.79999,1064.7,1015.4,1006.45,998.70001,945.70001,1000.55,991.59998,891.15002,915.25,865.15002,826,862.34998,877,1000.1,1001.85,855.90002,862.29999,947.70001,965.84998,1014.6,1035.4,966.65002,1045.2,982.70001,969.15] +//var open = [118.875,130,128.050005,132.899995,144.949995,150.399995,141,147.5,138,137.225005,149.5,163.024995,179.850005,199.125,220.5,243.475005,245.050005,290,280.274995,284.5,269.5,263.5,217.5,219.699995,244,237.824995,259.04999,265.95001,261.5,269.70001,266.95001,278.75,278.5,264.75,269,321.274995,351.5,359.5,392.5,381.25,416.5,446.5,357.29999,354,400.5,498.975005,482.5,530.825,487,557.5,585.875,610.5,622.55,635.075,684,687.5,677.5,787.5,886.4,852,932.5,980.45,1155,1410,1444.1,1445,1245,1199.9,1149.5,1341,1214,1055,1085.025,1060,980,719.5,562.65,620,645,614.35,761.5,925.5,1165,1014.95,984.45,1012.4,1099.94995,960.025,1075,1091.1,1037.5,985,1078,1026,1043.3,1084,1016,925.04999,992,1121,985,1065,925.09998,973.70001,1049.05,983.90002,952,907,838,796.70001,791.20001,870.5,799,696.79999,818.90002,811.79999,749.34998,747.90002,707.34998,735.5,740.25,768,840,795,795,844,891.34998,816,781,787.40002,803.70001,862,875.15002,853.20001,826.29999,910.09998,853,897.45001,826.20001,800.09998,936,940,1074.75,1017.2,995,1004.35,943.59998,1004,989,886.29999,916.04999,869,824.90002,866.20001,880,1001,1001,853.90002,873.95001,949,964,1009.8,1039.95,964.70001,1039.1,979]; +//var high = [130,135.475005,134.699995,147.899995,153.199995,155,151.875,150.600005,151,151,168.875,180.925005,212.350005,255,248.949995,255,291.399995,302.95001,310,300,325,316.524995,229.125,245.25,277.5,259.875,300,293.5,273.95001,275,298.899995,300.399995,288.45001,277.875,337.5,355.95001,399.5,420,404.95001,432.92499,450.42499,468,362.17499,404,525,592.2,532,550,569.95,598.4,620,657.975,652.5,714,722.2,691.9,813.3,898.9,900,974.25,982.9,1207.5,1427.225,1464,1494,1649,1321,1214.19995,1359,1354.5,1222,1169.225,1188,1112.5,981.5,752.6,703.5,695,707.05,791.7,920,1267.5,1189.5,1091.5,1062.5,1119.94995,1141.65,1112.94995,1120,1149.9,1056.35,1111,1149.7,1093.4,1093.95,1094.45,1031,1048.5,1110,1124.9,1075,1091.4,1009.4,1055,1065.9,986.79999,967,907,838.90002,859,904,905,844.59998,827.90002,864.70001,830,762.90002,751.40002,742.95001,746.09998,824.90002,881.59998,862,814.90002,849.79999,955,901,869.40002,826.59998,855,873,927.90002,882.84998,901,918,926.54999,909,898.25,829.79999,939.79999,988.75,1145.25,1133,1043.3,1023,1041.3,1003.7,1017.35,991.95001,934.5,943.79999,909.65002,944.29999,915.40002,1014.1,1067.85,1013.85,899.5,974.79999,992.5,1019.7,1089.75,1041.8,1056,1069.9,994.95001]; +//var low = [117.15,126,109.025,125.875,141.100005,131.649995,138.274995,137.050005,132.375,128.574995,145.625,161.625,171.800005,196,211.25,223.100005,217.5,263.625,269.5,252.625,261.100005,190.850005,204.574995,200,224.25,234.300005,256.125,243.5,236.050005,245,264.32501,269.54999,256,257.79999,264.274995,304.649995,341.54999,359.5,367.5,367.5,413.5,290.04999,341.75,351.04999,400.5,420,403.5,470.75,476,536.55,569.25,610.05,588.175,632.05,652.55,624.1,652.625,765.625,823.55,838.05,841.6,960.625,1098.85,1288.5,1325.775,1060,1117.5,1060,1121.75,1195.125,978.125,960,1026.4,882.5,465,516.05,512.5,532.5,600.675,557.65,748.675,918,950,858.55,933.8,961.65,961,901.125,990,1018.1,959.15002,979.40002,1012,976,995.09998,1007.25,915,921,990,958.29999,978.75,902,885.09998,964,970,898.34998,828.09998,823.15002,712,747.54999,761.75,751,689,687.15002,765.34998,718,723.70001,673.04999,673.40002,706.65002,730.54999,760.04999,791.70001,761.09998,789.75,836.25,805.75,764.40002,764.15002,779.20001,775.29999,839,763.90002,819.09998,817.70001,835.25,836.75,822.20001,793.09998,797.15002,929.09998,925,1000,958.5,971,917.29999,911.54999,963.40002,859.70001,831.20001,836.59998,796.45001,813.09998,857.20001,873.65002,983.15002,818,825.09998,858.59998,909.95001,912.84998,976.70001,888.09998,964.65002,978.84998,966.35]; +//var volume = [60577000,89946800,167726600,289632000,232001400,178826400,148041200,133674800,172278600,131019400,245337000,270529200,236635600,324700800,258226400,204663600,313141400,298956800,301877000,289352800,215000000,338703400,288238200,287246200,251527200,202257600,197788600,273117000,378550800,214897800,172596200,242043200,178335600,135060400,326057600,202756000,337055800,236351000,243632800,184568400,227348200,410727200,191313200,194453800,202785000,358355800,300754000,196140200,147580000,144146800,115584600,119844290,131124856,100744910,93303976,108602702,88590942,112116658,90924192,117166512,134674676,103462726,227825212,152571460,109291876,199306604,109503370,128866994,118720168,100431954,199759140,186116870,124538052,193998022,277441434,290597678,292519966,241434156,164067324,224474706,178741462,179762056,194312750,195674316,135370362,164162302,130697792,143219318,77702945,146977465,74213058,95920152,86174598,108726718,92688742,77875004,106608183,110026964,109893736,103305959,85489588,102274186,123323053,98712204,64389147,71838568,85205752,84498501,88527072,125318259,73157398,79948301,96632041,109799923,102524419,96127528,61342022,75818408,66779815,49594677,71144648,75589654,67836136,54729545,48871224,82616300,54713547,60416573,68778328,71207346,77937589,76066558,72892884,69181328,62361526,45902759,48918932,64800287,41935622,96008108,59145995,94972282,74429949,72967161,50982514,69539618,63070521,48742867,67355504,77991514,61540933,70141354,88056659,66204721,102324474,71775890,75737345,72243540,57735526,54829775,66341940,119101275,81868834,71067102,79851932,37032250] +//var timestamp = [1028173500,1030938300,1033443900,1036122300,1038800700,1041392700,1044243900,1046663100,1049168700,1051760700,1054525500,1057031100,1059709500,1062387900,1064979900,1067831100,1070250300,1072928700,1075693500,1078112700,1080791100,1083555900,1086061500,1088653500,1091418300,1094010300,1096602300,1099280700,1101872700,1104723900,1107229500,1109648700,1112327100,1115005500,1117597500,1120189500,1122867900,1125546300,1128311100,1130816700,1133408700,1136173500,1138765500,1141184700,1144035900,1146455100,1149133500,1151898300,1154403900,1157082300,1159760700,1162352700,1164944700,1167623100,1170301500,1172720700,1175485500,1177991100,1180669500,1183347900,1185939900,1188791100,1191210300,1193888700,1196653500,1199159100,1201837500,1204515900,1207021500,1209613500,1212378300,1214883900,1217562300,1220240700,1222832700,1225683900,1228103100,1230781500,1233546300,1235965500,1238557500,1241149500,1243827900,1246419900,1249271100,1251776700,1254368700,1257133500,1259639100,1262317500,1264995900,1267415100,1270093500,1272858300,1275363900,1277955900,1280720700,1283312700,1285904700,1288583100,1291175100,1294026300,1296531900,1298951100,1301629500,1304307900,1306899900,1309491900,1312170300,1314848700,1317613500,1320119100,1322711100,1325475900,1328067900,1330573500,1333338300,1335843900,1338522300,1341200700,1343792700,1346643900,1349063100,1351741500,1354506300,1357011900,1359690300,1362109500,1364787900,1367379900,1370231100,1372650300,1375328700,1378093500,1380599100,1383277500,1385955900,1388547900,1391399100,1393818300,1396323900,1398915900,1401680700,1404186300,1406864700,1409543100,1412135100,1414986300,1417405500,1420083900,1422848700,1425267900,1427859900,1430451900,1433130300,1435722300,1438573500,1441079100,1443671100,1446435900,1448941500,1451619900,1454298300,1456803900,1459482300,1462160700]; + +var TRIX = [294.0550,299.1142,303.7449,307.7237,312.2738,318.1194,323.9484,330.4611,337.2436,345.5079,356.4119] \ No newline at end of file diff --git a/test/directionalmovement/ADX.js b/test/directionalmovement/ADX.js new file mode 100644 index 0000000..5defe00 --- /dev/null +++ b/test/directionalmovement/ADX.js @@ -0,0 +1,159 @@ +/** + * Created by AAravindan on 5/8/16. + */ +/** + * Created by AAravindan on 5/3/16. + */ +var ADX = require('../../lib/directionalmovement/ADX').ADX; +var assert = require('assert'); +var data = require('../data') + +var period = 14; + +// var input = { +// close: [], +// high: [], +// low: [], +// period : period +// } + + +// var expectResult = [33.58,32.15,29.93,28.36,26.90,25.78,23.95,22.78,22.07,21.53,20.80,19.59,18.72,18.75,18.84,18.55,17.73,17.95,18.23,17.35] + +var input = { + close: [29.87,30.24,30.10,28.90,28.92,28.48,28.56,27.56,28.47,28.28,27.49,27.23,26.35,26.33,27.03,26.22,26.01,25.46,27.03,27.45,28.36,28.43,27.95,29.01,29.38,29.36,28.91,30.61,30.05,30.19,31.12,30.54,29.78,30.04,30.49,31.47,32.05,31.97,31.13,31.66,32.64,32.59,32.19,32.10,32.93,33.00,31.94], + high: [30.20,30.28,30.45,29.35,29.35,29.29,28.83,28.73,28.67,28.85,28.64,27.68,27.21,26.87,27.41,26.94,26.52,26.52,27.09,27.69,28.45,28.53,28.67,29.01,29.87,29.80,29.75,30.65,30.60,30.76,31.17,30.89,30.04,30.66,30.60,31.97,32.10,32.03,31.63,31.85,32.71,32.76,32.58,32.13,33.12,33.19,32.52], + low: [29.41,29.32,29.96,28.74,28.56,28.41,28.08,27.43,27.66,27.83,27.40,27.09,26.18,26.13,26.63,26.13,25.43,25.35,25.88,26.96,27.14,28.01,27.88,27.99,28.76,29.14,28.71,28.93,30.03,29.39,30.14,30.43,29.35,29.99,29.52,30.94,31.54,31.36,30.92,31.20,32.13,32.23,31.97,31.56,32.21,32.63,31.76], + period : period +} + + +var expectResult = [ + { + "adx": 33.70788849599704, + "mdi": 18.116192555042613, + "pdi": 23.718186893672044 + }, + { + "adx": 32.256674079436806, + "mdi": 17.360948613749574, + "pdi": 22.72940203198124 + }, + { + "adx": 30.018345619444343, + "mdi": 20.17542094822075, + "pdi": 20.550126792049156 + }, + { + "adx": 28.439012595686158, + "mdi": 18.72204272969499, + "pdi": 21.937250896300913 + }, + { + "adx": 26.97248907362499, + "mdi": 17.797126339975456, + "pdi": 20.85349506942225 + }, + { + "adx": 25.847323911213152, + "mdi": 23.928903398549593, + "pdi": 19.10088505720813 + }, + { + "adx": 24.017906430971514, + "mdi": 22.427790620529375, + "pdi": 22.32241320401099 + }, + { + "adx": 22.85086147775649, + "mdi": 24.042575888819258, + "pdi": 20.613325486941086 + }, + { + "adx": 22.129747886876853, + "mdi": 21.601760160516733, + "pdi": 27.9181603183101 + }, + { + "adx": 21.57869470891934, + "mdi": 20.64114106178586, + "pdi": 27.59427841755249 + }, + { + "adx": 20.841568435721417, + "mdi": 20.912545072268006, + "pdi": 26.21905777631746 + }, + { + "adx": 19.61908179692733, + "mdi": 22.497586315867906, + "pdi": 24.239357953501997 + }, + { + "adx": 18.72577768656732, + "mdi": 21.30940824159246, + "pdi": 24.572938960734678 + }, + { + "adx": 18.751962418956367, + "mdi": 19.677287360163756, + "pdi": 28.964072883200057 + }, + { + "adx": 18.82256916633815, + "mdi": 18.89078124365505, + "pdi": 28.183449457987933 + }, + { + "adx": 18.52094194845808, + "mdi": 19.99555317354794, + "pdi": 26.832324023553472 + }, + { + "adx": 17.684613039701503, + "mdi": 22.24279473593016, + "pdi": 25.494843646184236 + }, + { + "adx": 17.90758568664478, + "mdi": 20.464233142299246, + "pdi": 31.217188685051074 + }, + { + "adx": 18.17858351323038, + "mdi": 19.540416237637213, + "pdi": 30.372238610706088 + }, + { + "adx": 17.28769386037729, + "mdi": 24.460090756499206, + "pdi": 27.42046167925381 + } +] + + +describe('ADX (Average Directional Index)', function() { + it('should calculate ADX using the calculate method', function() { + assert.deepEqual(ADX.calculate(input), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate ADX by using getResult', function() { + var adx = new ADX(input); + assert.deepEqual(adx.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to get ADX for the next bar using nextValue', function() { + var adx = new ADX({period : period, high : [], low:[], close:[]}); + var results = []; + input.close.forEach(function(close,index) { + var result = adx.nextValue({ + close: input.close[index], + high: input.high[index], + low: input.low[index] + }); + if(result) + results.push(result) + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }) +}) \ No newline at end of file diff --git a/test/directionalmovement/ATR.js b/test/directionalmovement/ATR.js new file mode 100644 index 0000000..bc81fb6 --- /dev/null +++ b/test/directionalmovement/ATR.js @@ -0,0 +1,104 @@ +/** + * Created by AAravindan on 5/8/16. + */ +var assert = require('assert'); +var ATR = require('../../lib/directionalmovement/ATR').ATR; + +var period = 14 + +var input = { + high : [48.70,48.72,48.90,48.87,48.82,49.05,49.20,49.35,49.92,50.19,50.12,49.66,49.88,50.19,50.36,50.57,50.65,50.43,49.63,50.33,50.29,50.17,49.32,48.50,48.32,46.80,47.80,48.39,48.66,48.79], + low : [47.79,48.14,48.39,48.37,48.24,48.64,48.94,48.86,49.50,49.87,49.20,48.90,49.43,49.73,49.26,50.09,50.30,49.21,48.98,49.61,49.20,49.43,48.08,47.64,41.55,44.28,47.31,47.20,47.90,47.73], + close : [48.16,48.61,48.75,48.63,48.74,49.03,49.07,49.32,49.91,50.13,49.53,49.50,49.75,50.03,50.31,50.52,50.41,49.34,49.37,50.23,49.24,49.93,48.43,48.18,46.57,45.41,47.77,47.72,48.62,47.85], + period : period +} + + +var expectResult = [ + 0.5678571428571431, + 0.5615816326530612, + 0.5464686588921284, + 0.5945780403998334, + 0.5985367517998457, + 0.6243555552427139, + 0.6576158727253769, + 0.6770718818164214, + 0.7608524616866771, + 0.7679344287090573, + 1.196653398086982, + 1.291178155366483, + 1.3696654299831628, + 1.3568321849843652, + 1.3270584574854818, + 1.307982853379376, + ] + + +var expectResultNextBar = [ + 0.5679, + 0.5616, + 0.5465, + 0.5946, + 0.5985, + 0.6244, + 0.6576, + 0.6771, + 0.7609, + 0.7679, + 1.197, + 1.291, + 1.37, + 1.357, + 1.327, + 1.308, +]; + + +var expected = [ + 0.5678571428571431, + 0.5615816326530612, + 0.5464686588921284, + 0.5945780403998334, + 0.5985367517998457, + 0.6243555552427139, + 0.6576158727253769, + 0.6770718818164214, + 0.7608524616866771, + 0.7679344287090573, + 1.196653398086982, + 1.291178155366483, + 1.3696654299831628, + 1.3568321849843652, + 1.3270584574854818, + 1.307982853379376, + ]; + +describe('ATR (Average True Range)', function() { + it('should calculate ATR using the calculate method', function() { + assert.deepEqual(ATR.calculate(input), expectResult, 'Wrong Results'); + }); + + it('should be able to caÃŽlculate ATR by using getResult', function() { + var atr = new ATR(input); + assert.deepEqual(atr.getResult(), expected, 'Wrong Results while calculating next bar'); + }); + + + + it('should be able to get ATR for the next bar using nextValue', function() { + var atr = new ATR({period : period, high : [], low:[], close:[]}); + var results = []; + input.close.forEach(function(close,index) { + var result = atr.nextValue({ + close: input.close[index], + high: input.high[index], + low: input.low[index] + }); + if(result !== undefined){ + results.push(parseFloat(result.toPrecision(4))); + } + }); + assert.deepEqual(results, expectResultNextBar, 'Wrong Results while getting results'); + }) + +}) \ No newline at end of file diff --git a/test/directionalmovement/MinusDM.js b/test/directionalmovement/MinusDM.js new file mode 100644 index 0000000..3f0dfdf --- /dev/null +++ b/test/directionalmovement/MinusDM.js @@ -0,0 +1,84 @@ +/** + * Created by AAravindan on 5/8/16. + */ +var MinusDM = require('../../lib/directionalmovement/MinusDM').MDM; +var assert = require('assert'); +var data = require('../data') + +var period = 14; + +var input = { + close: [29.87,30.24,30.10,28.90,28.92,28.48,28.56,27.56,28.47,28.28,27.49,27.23,26.35,26.33,27.03,26.22,26.01,25.46,27.03,27.45,28.36,28.43,27.95,29.01,29.38,29.36,28.91,30.61,30.05,30.19,31.12,30.54,29.78,30.04,30.49,31.47,32.05,31.97], + high: [30.20,30.28,30.45,29.35,29.35,29.29,28.83,28.73,28.67,28.85,28.64,27.68,27.21,26.87,27.41,26.94,26.52,26.52,27.09,27.69,28.45,28.53,28.67,29.01,29.87,29.80,29.75,30.65,30.60,30.76,31.17,30.89,30.04,30.66,30.60,31.97,32.10,32.03], + low: [29.41,29.32,29.96,28.74,28.56,28.41,28.08,27.43,27.66,27.83,27.40,27.09,26.18,26.13,26.63,26.13,25.43,25.35,25.88,26.96,27.14,28.01,27.88,27.99,28.76,29.14,28.71,28.93,30.03,29.39,30.14,30.43,29.35,29.99,29.52,30.94,31.54,31.36], + period : period +} + + +var expectResult = [ + 0.08999999999999986, + 0, + 1.2200000000000024, + 0.17999999999999972, + 0.14999999999999858, + 0.33000000000000185, + 0.6499999999999986, + 0, + 0, + 0.4299999999999997, + 0.3099999999999987, + 0.9100000000000001, + 0.05000000000000071, + 0, + 0.5, + 0.6999999999999993, + 0.0799999999999983, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0.4299999999999997, + 0, + 0, + 0.6400000000000006, + 0, + 0, + 1.0799999999999983, + 0, + 0.46999999999999886, + 0, + 0, + 0.17999999999999972, + ] + + + describe('MinusDM', function() { + it('should calculate MinusDM using the calculate method', function() { + assert.deepEqual(MinusDM.calculate(input), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate MinusDM by using getResult', function() { + var minusDm = new MinusDM(input); + assert.deepEqual(minusDm.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to get MinusDM for the next bar using nextValue', function() { + var minusDm = new MinusDM({period : period, high : [], low:[], close:[]}); + var results = []; + input.close.forEach(function(close,index) { + var result = minusDm.nextValue({ + close: input.close[index], + high: input.high[index], + low: input.low[index] + }); + if(result!==undefined) + results.push(result) + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }) + + }) \ No newline at end of file diff --git a/test/directionalmovement/PlusDM.js b/test/directionalmovement/PlusDM.js new file mode 100644 index 0000000..8578d4b --- /dev/null +++ b/test/directionalmovement/PlusDM.js @@ -0,0 +1,83 @@ +/** + * Created by AAravindan on 5/8/16. + */ +var PlusDM = require('../../lib/directionalmovement/PlusDM').PDM; +var assert = require('assert'); +var data = require('../data') + +var period = 14; + +var input = { + close: [29.87,30.24,30.10,28.90,28.92,28.48,28.56,27.56,28.47,28.28,27.49,27.23,26.35,26.33,27.03,26.22,26.01,25.46,27.03,27.45,28.36,28.43,27.95,29.01,29.38,29.36,28.91,30.61,30.05,30.19,31.12,30.54,29.78,30.04,30.49,31.47,32.05,31.97], + high: [30.20,30.28,30.45,29.35,29.35,29.29,28.83,28.73,28.67,28.85,28.64,27.68,27.21,26.87,27.41,26.94,26.52,26.52,27.09,27.69,28.45,28.53,28.67,29.01,29.87,29.80,29.75,30.65,30.60,30.76,31.17,30.89,30.04,30.66,30.60,31.97,32.10,32.03], + low: [29.41,29.32,29.96,28.74,28.56,28.41,28.08,27.43,27.66,27.83,27.40,27.09,26.18,26.13,26.63,26.13,25.43,25.35,25.88,26.96,27.14,28.01,27.88,27.99,28.76,29.14,28.71,28.93,30.03,29.39,30.14,30.43,29.35,29.99,29.52,30.94,31.54,31.36], + period : period +} + +var expectResult = [ + 0, + 0.16999999999999815, + 0, + 0, + 0, + 0, + 0, + 0, + 0.17999999999999972, + 0, + 0, + 0, + 0, + 0.5399999999999991, + 0, + 0, + 0, + 0.5700000000000003, + 0.6000000000000014, + 0.759999999999998, + 0.08000000000000185, + 0.14000000000000057, + 0.33999999999999986, + 0.8599999999999994, + 0, + 0, + 0.8999999999999986, + 0, + 0, + 0.41000000000000014, + 0, + 0, + 0.620000000000001, + 0, + 1.3699999999999974, + 0.13000000000000256, + 0, + ] + + +describe('PlusDM', function() { + it('should calculate PlusDM using the calculate method', function() { + assert.deepEqual(PlusDM.calculate(input), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate PlusDM by using getResult', function() { + var plusDm = new PlusDM(input); + assert.deepEqual(plusDm.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to get PlusDM for the next bar using nextValue', function() { + var plusDm = new PlusDM({period : period, high : [], low:[], close:[]}); + var results = []; + input.close.forEach(function(close,index) { + var result = plusDm.nextValue({ + close: input.close[index], + high: input.high[index], + low: input.low[index] + }); + if(result!==undefined) + results.push(result) + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }) + +}) \ No newline at end of file diff --git a/test/ichimoku/IchimokuCloud.js b/test/ichimoku/IchimokuCloud.js new file mode 100644 index 0000000..6742b31 --- /dev/null +++ b/test/ichimoku/IchimokuCloud.js @@ -0,0 +1,740 @@ +/** + * Created by AAravindan on 5/4/16. + */ +var IchimokuCloud = require('../../lib/ichimoku/IchimokuCloud').IchimokuCloud +var assert = require('assert') +var data = require('../data') + +var ichimokuInput = { + high : [130,135.475005,134.699995,147.899995,153.199995,155,151.875,150.600005,151,151,168.875,180.925005,212.350005,255,248.949995,255,291.399995,302.95001,310,300,325,316.524995,229.125,245.25,277.5,259.875,300,293.5,273.95001,275,298.899995,300.399995,288.45001,277.875,337.5,355.95001,399.5,420,404.95001,432.92499,450.42499,468,362.17499,404,525,592.2,532,550,569.95,598.4,620,657.975,652.5,714,722.2,691.9,813.3,898.9,900,974.25,982.9,1207.5,1427.225,1464,1494,1649,1321,1214.19995,1359,1354.5,1222,1169.225,1188,1112.5,981.5,752.6,703.5,695,707.05,791.7,920,1267.5,1189.5,1091.5,1062.5,1119.94995,1141.65,1112.94995,1120,1149.9,1056.35,1111,1149.7,1093.4,1093.95,1094.45,1031,1048.5,1110,1124.9,1075,1091.4,1009.4,1055,1065.9,986.79999,967,907,838.90002,859,904,905,844.59998,827.90002,864.70001,830,762.90002,751.40002,742.95001,746.09998,824.90002,881.59998,862,814.90002,849.79999,955,901,869.40002,826.59998,855,873,927.90002,882.84998,901,918,926.54999,909,898.25,829.79999,939.79999,988.75,1145.25,1133,1043.3,1023,1041.3,1003.7,1017.35,991.95001,934.5,943.79999,909.65002,944.29999,915.40002,1014.1,1067.85,1013.85,899.5,974.79999,992.5,1019.7,1089.75,1041.8,1056,1069.9,994.95001], + low : [117.15,126,109.025,125.875,141.100005,131.649995,138.274995,137.050005,132.375,128.574995,145.625,161.625,171.800005,196,211.25,223.100005,217.5,263.625,269.5,252.625,261.100005,190.850005,204.574995,200,224.25,234.300005,256.125,243.5,236.050005,245,264.32501,269.54999,256,257.79999,264.274995,304.649995,341.54999,359.5,367.5,367.5,413.5,290.04999,341.75,351.04999,400.5,420,403.5,470.75,476,536.55,569.25,610.05,588.175,632.05,652.55,624.1,652.625,765.625,823.55,838.05,841.6,960.625,1098.85,1288.5,1325.775,1060,1117.5,1060,1121.75,1195.125,978.125,960,1026.4,882.5,465,516.05,512.5,532.5,600.675,557.65,748.675,918,950,858.55,933.8,961.65,961,901.125,990,1018.1,959.15002,979.40002,1012,976,995.09998,1007.25,915,921,990,958.29999,978.75,902,885.09998,964,970,898.34998,828.09998,823.15002,712,747.54999,761.75,751,689,687.15002,765.34998,718,723.70001,673.04999,673.40002,706.65002,730.54999,760.04999,791.70001,761.09998,789.75,836.25,805.75,764.40002,764.15002,779.20001,775.29999,839,763.90002,819.09998,817.70001,835.25,836.75,822.20001,793.09998,797.15002,929.09998,925,1000,958.5,971,917.29999,911.54999,963.40002,859.70001,831.20001,836.59998,796.45001,813.09998,857.20001,873.65002,983.15002,818,825.09998,858.59998,909.95001,912.84998,976.70001,888.09998,964.65002,978.84998,966.35], + conversionPeriod: 9, + basePeriod: 26, + spanPeriod: 52, + displacement: 26 +} + +var expectedOutput = [ + { + "conversion": 504.512495, + "base": 447.0125025, + "spanA": 475.76249874999996, + "spanB": 383.5 + }, + { + "conversion": 529.2375, + "base": 447.0125025, + "spanA": 488.12500124999997, + "spanB": 383.5 + }, + { + "conversion": 558.75, + "base": 475.0250025, + "spanA": 516.88750125, + "spanB": 411.5125 + }, + { + "conversion": 562.85, + "base": 483.6, + "spanA": 523.225, + "spanB": 424.0375 + }, + { + "conversion": 596.475, + "base": 489.1, + "spanA": 542.7875, + "spanB": 425.3874975 + }, + { + "conversion": 644.65, + "base": 534.65, + "spanA": 589.65, + "spanB": 470.93749749999995 + }, + { + "conversion": 717.7249999999999, + "base": 577.45, + "spanA": 647.5875, + "spanB": 513.7374975 + }, + { + "conversion": 734.625, + "base": 578.899995, + "spanA": 656.7624975, + "spanB": 514.2874975 + }, + { + "conversion": 781.2125, + "base": 619.2624975, + "spanA": 700.23749875, + "spanB": 551.4124975 + }, + { + "conversion": 785.5374999999999, + "base": 636.474995, + "spanA": 711.0062475, + "spanB": 555.7374975 + }, + { + "conversion": 915.8, + "base": 748.774995, + "spanA": 832.2874975, + "spanB": 676.5625 + }, + { + "conversion": 1025.6625, + "base": 858.637495, + "spanA": 942.1499974999999, + "spanB": 794.425 + }, + { + "conversion": 1044.05, + "base": 877.024995, + "spanA": 960.5374975, + "spanB": 817.9000025 + }, + { + "conversion": 1073.3125, + "base": 892.024995, + "spanA": 982.6687475, + "spanB": 842.4250025 + }, + { + "conversion": 1207.3125, + "base": 969.524995, + "spanA": 1088.4187474999999, + "spanB": 919.9250025 + }, + { + "conversion": 1236.275, + "base": 969.524995, + "spanA": 1102.8999975000002, + "spanB": 919.9250025 + }, + { + "conversion": 1243.525, + "base": 995.375, + "spanA": 1119.45, + "spanB": 919.9250025 + }, + { + "conversion": 1245.3, + "base": 1000.024995, + "spanA": 1122.6624975, + "spanB": 919.9250025 + }, + { + "conversion": 1304.8125, + "base": 1024.75, + "spanA": 1164.78125, + "spanB": 919.9250025 + }, + { + "conversion": 1313.5625, + "base": 1026.25, + "spanA": 1169.90625, + "spanB": 919.9250025 + }, + { + "conversion": 1304.5, + "base": 1026.25, + "spanA": 1165.375, + "spanB": 919.9250025 + }, + { + "conversion": 1304.5, + "base": 1059.875, + "spanA": 1182.1875, + "spanB": 919.9250025 + }, + { + "conversion": 1265.75, + "base": 1062.5, + "spanA": 1164.125, + "spanB": 924.5 + }, + { + "conversion": 912, + "base": 1057, + "spanA": 984.5, + "spanB": 924.5 + }, + { + "conversion": 912, + "base": 1057, + "spanA": 984.5, + "spanB": 936.625 + }, + { + "conversion": 912, + "base": 1057, + "spanA": 984.5, + "spanB": 941.6500025 + }, + { + "conversion": 909.75, + "base": 1057, + "spanA": 983.375, + "spanB": 942.5250025 + }, + { + "conversion": 843.5, + "base": 1057, + "spanA": 950.25, + "spanB": 942.5250025 + }, + { + "conversion": 826.5, + "base": 1057, + "spanA": 941.75, + "spanB": 942.5250025 + }, + { + "conversion": 826.5, + "base": 1057, + "spanA": 941.75, + "spanB": 947 + }, + { + "conversion": 866.25, + "base": 1057, + "spanA": 961.625, + "spanB": 952.5 + }, + { + "conversion": 866.25, + "base": 1057, + "spanA": 961.625, + "spanB": 952.5 + }, + { + "conversion": 890, + "base": 1057, + "spanA": 973.5, + "spanB": 952.5 + }, + { + "conversion": 890, + "base": 1057, + "spanA": 973.5, + "spanB": 953.399995 + }, + { + "conversion": 900, + "base": 1057, + "spanA": 978.5, + "spanB": 956.6374975 + }, + { + "conversion": 912.575, + "base": 1057, + "spanA": 984.7875, + "spanB": 969.524995 + }, + { + "conversion": 912.575, + "base": 1057, + "spanA": 984.7875, + "spanB": 969.524995 + }, + { + "conversion": 1008.0875, + "base": 1057, + "spanA": 1032.54375, + "spanB": 969.524995 + }, + { + "conversion": 1063.025, + "base": 1057, + "spanA": 1060.0125, + "spanB": 969.524995 + }, + { + "conversion": 1024.025, + "base": 1057, + "spanA": 1040.5125, + "spanB": 969.524995 + }, + { + "conversion": 1004.225, + "base": 912, + "spanA": 958.1125, + "spanB": 969.524995 + }, + { + "conversion": 1025.5125, + "base": 912, + "spanA": 968.75625, + "spanB": 969.524995 + }, + { + "conversion": 1025.5125, + "base": 912, + "spanA": 968.75625, + "spanB": 995.375 + }, + { + "conversion": 1025.5125, + "base": 909.75, + "spanA": 967.63125, + "spanB": 1000.024995 + }, + { + "conversion": 1025.5125, + "base": 866.25, + "spanA": 945.88125, + "spanB": 1024.75 + }, + { + "conversion": 1032.45, + "base": 866.25, + "spanA": 949.35, + "spanB": 1026.25 + }, + { + "conversion": 1032.45, + "base": 866.25, + "spanA": 949.35, + "spanB": 1026.25 + }, + { + "conversion": 1032.35, + "base": 866.25, + "spanA": 949.3, + "spanB": 1057 + }, + { + "conversion": 1032.35, + "base": 866.25, + "spanA": 949.3, + "spanB": 1057 + }, + { + "conversion": 1032.35, + "base": 890, + "spanA": 961.175, + "spanB": 1057 + }, + { + "conversion": 1013.45, + "base": 890, + "spanA": 951.725, + "spanB": 1057 + }, + { + "conversion": 1004.99999, + "base": 900, + "spanA": 952.499995, + "spanB": 1057 + }, + { + "conversion": 1004.99999, + "base": 912.575, + "spanA": 958.787495, + "spanB": 1057 + }, + { + "conversion": 1004.99999, + "base": 912.575, + "spanA": 958.787495, + "spanB": 1057 + }, + { + "conversion": 1004.99999, + "base": 1008.0875, + "spanA": 1006.543745, + "spanB": 1057 + }, + { + "conversion": 976.49999, + "base": 1047.79999, + "spanA": 1012.14999, + "spanB": 1057 + }, + { + "conversion": 974.0250100000001, + "base": 1006.32501, + "spanA": 990.17501, + "spanB": 1057 + }, + { + "conversion": 901.7, + "base": 930.95, + "spanA": 916.325, + "spanB": 1057 + }, + { + "conversion": 901.7, + "base": 930.95, + "spanA": 916.325, + "spanB": 1057 + }, + { + "conversion": 888.95, + "base": 930.95, + "spanA": 909.95, + "spanB": 1057 + }, + { + "conversion": 888.95, + "base": 930.95, + "spanA": 909.95, + "spanB": 1057 + }, + { + "conversion": 877.45, + "base": 919.45, + "spanA": 898.45, + "spanB": 1057 + }, + { + "conversion": 836.975005, + "base": 918.5250100000001, + "spanA": 877.7500075, + "spanB": 1057 + }, + { + "conversion": 827.07501, + "base": 918.5250100000001, + "spanA": 872.80001, + "spanB": 1057 + }, + { + "conversion": 797.07501, + "base": 918.42501, + "spanA": 857.75001, + "spanB": 1057 + }, + { + "conversion": 796.07501, + "base": 918.42501, + "spanA": 857.25001, + "spanB": 1057 + }, + { + "conversion": 789.024995, + "base": 911.374995, + "spanA": 850.199995, + "spanB": 912 + }, + { + "conversion": 789.024995, + "base": 898.974995, + "spanA": 843.999995, + "spanB": 912 + }, + { + "conversion": 789.024995, + "base": 898.974995, + "spanA": 843.999995, + "spanB": 912 + }, + { + "conversion": 768.875, + "base": 898.974995, + "spanA": 833.9249975, + "spanB": 909.75 + }, + { + "conversion": 777.324985, + "base": 898.974995, + "spanA": 838.14999, + "spanB": 866.25 + }, + { + "conversion": 777.324985, + "base": 898.974995, + "spanA": 838.14999, + "spanB": 866.25 + }, + { + "conversion": 777.324985, + "base": 898.974995, + "spanA": 838.14999, + "spanB": 866.25 + }, + { + "conversion": 777.324985, + "base": 898.974995, + "spanA": 838.14999, + "spanB": 866.25 + }, + { + "conversion": 814.024995, + "base": 882.224995, + "spanA": 848.124995, + "spanB": 866.25 + }, + { + "conversion": 814.20001, + "base": 882.224995, + "spanA": 848.2125025, + "spanB": 890 + }, + { + "conversion": 830.82501, + "base": 869.474995, + "spanA": 850.1500025, + "spanB": 890 + }, + { + "conversion": 842.774995, + "base": 869.474995, + "spanA": 856.124995, + "spanB": 900 + }, + { + "conversion": 857.524995, + "base": 869.474995, + "spanA": 863.499995, + "spanB": 912.575 + }, + { + "conversion": 858.04999, + "base": 829.92499, + "spanA": 843.98749, + "spanB": 912.575 + }, + { + "conversion": 858.04999, + "base": 820.024995, + "spanA": 839.0374925, + "spanB": 970.274995 + }, + { + "conversion": 859.45001, + "base": 814.024995, + "spanA": 836.7375025, + "spanB": 970.274995 + }, + { + "conversion": 859.45001, + "base": 814.024995, + "spanA": 836.7375025, + "spanB": 931.274995 + }, + { + "conversion": 845.90002, + "base": 814.024995, + "spanA": 829.9625075, + "spanB": 911.474995 + }, + { + "conversion": 845.90002, + "base": 814.024995, + "spanA": 829.9625075, + "spanB": 911.474995 + }, + { + "conversion": 845.90002, + "base": 814.024995, + "spanA": 829.9625075, + "spanB": 911.474995 + }, + { + "conversion": 845.90002, + "base": 814.024995, + "spanA": 829.9625075, + "spanB": 911.474995 + }, + { + "conversion": 845.90002, + "base": 814.024995, + "spanA": 829.9625075, + "spanB": 911.474995 + }, + { + "conversion": 851.850005, + "base": 814.024995, + "spanA": 832.9375, + "spanB": 911.474995 + }, + { + "conversion": 876.32501, + "base": 830.899995, + "spanA": 853.6125025, + "spanB": 911.474995 + }, + { + "conversion": 969.17499, + "base": 909.149995, + "spanA": 939.1624925, + "spanB": 911.374995 + }, + { + "conversion": 969.17499, + "base": 909.149995, + "spanA": 939.1624925, + "spanB": 911.374995 + }, + { + "conversion": 969.17499, + "base": 909.32501, + "spanA": 939.25, + "spanB": 911.374995 + }, + { + "conversion": 969.17499, + "base": 925.95001, + "spanA": 947.5625, + "spanB": 909.149995 + }, + { + "conversion": 969.17499, + "base": 937.899995, + "spanA": 953.5374925, + "spanB": 909.149995 + }, + { + "conversion": 969.17499, + "base": 952.649995, + "spanA": 960.9124925, + "spanB": 909.149995 + }, + { + "conversion": 971.20001, + "base": 953.17499, + "spanA": 962.1875, + "spanB": 909.149995 + }, + { + "conversion": 1002.475005, + "base": 953.17499, + "spanA": 977.8249975, + "spanB": 909.149995 + }, + { + "conversion": 988.225005, + "base": 954.57501, + "spanA": 971.4000075, + "spanB": 909.149995 + }, + { + "conversion": 982.100005, + "base": 954.57501, + "spanA": 968.3375075, + "spanB": 909.149995 + }, + { + "conversion": 919.875005, + "base": 954.57501, + "spanA": 937.2250075, + "spanB": 909.149995 + }, + { + "conversion": 918.875005, + "base": 954.57501, + "spanA": 936.7250075, + "spanB": 909.149995 + }, + { + "conversion": 918.875005, + "base": 954.57501, + "spanA": 936.7250075, + "spanB": 909.149995 + }, + { + "conversion": 906.900005, + "base": 954.57501, + "spanA": 930.7375075, + "spanB": 909.149995 + }, + { + "conversion": 932.150005, + "base": 954.57501, + "spanA": 943.3625075, + "spanB": 909.149995 + }, + { + "conversion": 932.150005, + "base": 954.57501, + "spanA": 943.3625075, + "spanB": 909.149995 + }, + { + "conversion": 932.150005, + "base": 954.57501, + "spanA": 943.3625075, + "spanB": 909.149995 + }, + { + "conversion": 932.150005, + "base": 969.17499, + "spanA": 950.6624975, + "spanB": 909.149995 + }, + { + "conversion": 932.150005, + "base": 969.17499, + "spanA": 950.6624975, + "spanB": 909.149995 + }, + { + "conversion": 940.4749899999999, + "base": 969.17499, + "spanA": 954.82499, + "spanB": 909.149995 + }, + { + "conversion": 953.875, + "base": 969.17499, + "spanA": 961.524995, + "spanB": 909.149995 + }, + { + "conversion": 953.875, + "base": 969.17499, + "spanA": 961.524995, + "spanB": 909.149995 + }, + { + "conversion": 953.875, + "base": 969.17499, + "spanA": 961.524995, + "spanB": 909.149995 + }, + { + "conversion": 953.875, + "base": 970.850005, + "spanA": 962.3625025, + "spanB": 909.149995 + }, + { + "conversion": 957.42499, + "base": 970.850005, + "spanA": 964.1374975, + "spanB": 909.149995 + } +] + +var input; + +describe('Ichimoku cloud', function () { + beforeEach(function () { + input = JSON.parse(JSON.stringify(ichimokuInput)) + }) + + it('should calculate IchimokuCloud using the calculate method', function () { + var result = IchimokuCloud.calculate(input) + // require('fs').writeFileSync('ichimoku', JSON.stringify(result, null, 2)) + assert.deepEqual(result, expectedOutput, 'Wrong Results') + }) + + it('should be able to get ichimoku cloud from the get results', function () { + var ichimoku = new IchimokuCloud(input) + assert.deepEqual(ichimoku.getResult(), expectedOutput, 'Wrong Results') + }) + + it('should be able to get IchimokuCloud for the next bar using nextValue', function () { + input.high = [] + input.low = [] + var ichimoku = new IchimokuCloud(input) + var results = [] + ichimokuInput.high.forEach((high, index) => { + var result = ichimoku.nextValue({ high: high, low: ichimokuInput.low[index]}) + if (result) + results.push(result) + }) + assert.deepEqual(results, expectedOutput, 'Wrong Results') + }) +}) diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..4a52320 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1 @@ +--recursive diff --git a/test/momentum/KST.js b/test/momentum/KST.js new file mode 100644 index 0000000..0cd1a72 --- /dev/null +++ b/test/momentum/KST.js @@ -0,0 +1,78 @@ +/** + * Created by AAravindan on 5/7/16. + */ +const assert = require('assert'); +const KST = require('../../lib/momentum/KST').KST + +let close = [1344.78,1357.98,1355.69,1325.51,1335.02,1313.72,1319.99,1331.85,1329.04,1362.16,1365.51,1374.02,1367.58,1354.68,1352.46,1341.47,1341.45,1334.76,1356.78,1353.64,1363.67,1372.78,1376.51,1362.66,1350.52,1338.31,1337.89,1360.02,1385.97,1385.30,1379.32,1375.32,1365.00,1390.99,1394.23,1401.35,1402.22,1402.80,1405.87,1404.11,1403.93,1405.53,1415.51,1418.16,1418.13,1413.17,1413.49,1402.08,1411.13,1410.44]; +let input = { + values : close, + ROCPer1 : 10, + ROCPer2 : 15, + ROCPer3 : 20, + ROCPer4 : 30, + SMAROCPer1 : 10, + SMAROCPer2 : 10, + SMAROCPer3 : 10, + SMAROCPer4 : 15, + signalPeriod: 3 +}; + + let expectResult = [ + { + "kst": 36.597637686000944, + "signal": undefined + }, + { + "kst": 37.22847898422272, + "signal": undefined + }, + { + "kst": 38.38191122855075, + "signal": 37.40267596625814, + }, + { + "kst": 38.78388892587009, + "signal": 38.131426379547854, + }, + { + "kst": 37.54314785478353, + "signal": 38.23631600306812, + }, + { + "kst": 36.253502032156405, + "signal": 37.52684627093667, + }, + ]; + +describe('Know Sure Thing', function() { + "use strict"; + it('should be able to get KST for the next bar using nextValue', function() { + let rocInput = Object.assign({}, input); + rocInput.values = []; + let roc = new KST(rocInput); + let results = []; + input.values.forEach(price => { + let result = roc.nextValue(price); + if(result) + results.push(result) + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }); + + it('should calculate KST using the calculate method', function() { + assert.deepEqual(KST.calculate(input), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate KST by using getResult', function() { + let roc = new KST(input); + assert.deepEqual(roc.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to calculate KST for reversed input by using calculate method', function() { + let myInput = Object.assign({}, input); + myInput.reversedInput = true; + myInput.values.reverse(); + assert.deepEqual(KST.calculate(myInput), expectResult.slice().reverse(), 'Wrong Results while calculating next bar'); + }); +}) \ No newline at end of file diff --git a/test/momentum/PSAR.js b/test/momentum/PSAR.js new file mode 100644 index 0000000..d0bfc8f --- /dev/null +++ b/test/momentum/PSAR.js @@ -0,0 +1,65 @@ +"use strict" + +const assert = require('assert'); +const PSAR = require('../../lib/momentum/PSAR').PSAR; + +let high = [82.15,81.89,83.03,83.30,83.85,83.90,83.33,84.30,84.84,85.00,75.90,76.58,76.98,78.00,70.87]; +let low = [81.29,80.64,81.31,82.65,83.07,83.11,82.49,82.30,84.15,84.11,74.03,75.39,75.76,77.17,70.01]; + +let expectResult = [ + 81.29, + 82.15, + 80.64, + 80.64, + 80.7464, + 80.932616, + 81.17000672, + 81.3884061824, + 81.67956556416, + 82.0588176964608, + 85, + 85, + 84.7806, + 84.565588, + 84.35487624000001 +]; + +let step = 0.02; +let max = 0.2; + +let input = { high, low, step, max }; + +describe('Parabolic Stop and Reverse', function() { + + it('should be able to calculate PSAR by using getResult', function() { + let psar = new PSAR(input); + assert.deepEqual(psar.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to calculate PSAR for reversed input by using getResult', function() { + let psar = new PSAR(Object.assign({}, input, { reversedInput: true })); + assert.deepEqual(psar.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to get PSAR for the next bar using nextValue', function() { + let psar = new PSAR({ step, max }); + let results = []; + low.forEach((v, index) => { + let result = psar.nextValue({ low: low[index], high: high[index] }); + if(result) results.push(result) + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }) + + it('should calculate PSAR using the calculate method', function() { + assert.deepEqual(PSAR.calculate(input), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate PSAR for reversed input by using calculate method', function() { + let myInput = Object.assign({}, input); + myInput.reversedInput = true; + myInput.high.reverse(); + myInput.low.reverse(); + assert.deepEqual(PSAR.calculate(myInput), expectResult.slice().reverse(), 'Wrong Results while calculating next bar'); + }); +}); diff --git a/test/momentum/ROC.js b/test/momentum/ROC.js new file mode 100644 index 0000000..8fcd09a --- /dev/null +++ b/test/momentum/ROC.js @@ -0,0 +1,57 @@ +/** + * Created by AAravindan on 5/7/16. + */ +"use strict" +const assert = require('assert'); +const ROC = require('../../lib/momentum/ROC').ROC + +//let data = [4,2,5,8,6]; +let data = [11045.27,11167.32,11008.61,11151.83,10926.77,10868.12,10520.32,10380.43,10785.14,10748.26,10896.91,10782.95,10620.16,10625.83,10510.95,10444.37,10068.01,10193.39,10066.57,10043.75]; +let period = 12; +let expectResult = [ + -3.848796815288359, + -4.848880483410521, + -4.520643387312293, + -6.343891540670896, + -7.859230129306283, + -6.2083414610806775, + -4.313081731354179, + -3.243410918430164, + ] +describe('Rate of change', function() { + "use strict"; + it('should be able to calculate ROC by using getResult', function() { + let roc = new ROC({period : period, values : data}); + assert.deepEqual(roc.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to calculate ROC for reversed input by using getResult', function() { + let roc = new ROC({period : period, values : data, reversedInput : true}); + assert.deepEqual(roc.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to get ROC for the next bar using nextValue', function() { + let roc = new ROC({period : period, values : []}); + let results = []; + data.forEach(price => { + let result = roc.nextValue(price); + if(result) + results.push(result) + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }) + + it('should calculate ROC using the calculate method', function() { + assert.deepEqual(ROC.calculate({period : period, values : data}), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate ROC for reversed input by using calculate method', function() { + let myInput = Object.assign({}, { + period : period, + values : data + }); + myInput.reversedInput = true; + myInput.values.reverse(); + assert.deepEqual(ROC.calculate(myInput), expectResult.slice().reverse(), 'Wrong Results while calculating next bar'); + }); +}) diff --git a/test/momentum/Stochastic.js b/test/momentum/Stochastic.js new file mode 100644 index 0000000..573748c --- /dev/null +++ b/test/momentum/Stochastic.js @@ -0,0 +1,133 @@ +/** + * Created by AAravindan on 5/7/16. + */ +"use strict" +const assert = require('assert'); +const Stochastic = require('../../lib/momentum/Stochastic').Stochastic + +//let data = [4,2,5,8,6]; +let high = [127.009,127.616,126.591,127.347,128.173,128.432,127.367,126.422,126.900,126.850,125.646,125.716,127.158,127.715,127.686,128.223,128.273,128.093,128.273,127.735,128.770,129.287,130.063,129.118,129.287,128.472,128.093,128.651,129.138,128.641]; +let low = [125.357,126.163,124.930,126.094,126.820,126.482,126.034,124.830,126.392,125.716,124.562,124.572,125.069,126.860,126.631,126.800,126.711,126.800,126.134,125.925,126.989,127.815,128.472,128.064,127.606,127.596,126.999,126.900,127.487,127.397]; +let close = [125.357,126.163,124.930,126.094,126.820,126.482,126.034,124.830,126.392,125.716,124.562,124.572,125.069,127.288,127.178,128.014,127.109,127.725,127.059,127.327,128.710,127.875,128.581,128.601,127.934,128.113,127.596,127.596,128.690,128.273]; +let period = 14; +let signalPeriod = 3; +let expectResult = +[ + { + "d": undefined, + "k": 70.43927648578827, + }, + { + "d": undefined, + "k": 67.59689922480636, + }, + { + "d": 75.74504737295463, + "k": 89.19896640826927, + }, + { + "d": 74.2032730404826, + "k": 65.81395348837218, + }, + { + "d": 78.91472868217079, + "k": 81.73126614987092, + }, + { + "d": 70.68906115417757, + "k": 64.52196382428956, + }, + { + "d": 73.58714959436897, + "k": 74.50821880894642, + }, + { + "d": 79.20144237330932, + "k": 98.57414448669196, + }, + { + "d": 81.06625513734681, + "k": 70.11640211640204, + }, + { + "d": 80.58333011353209, + "k": 73.05944373750224, + }, + { + "d": 72.19961995045314, + "k": 73.42301399745516, + }, + { + "d": 69.23664028547631, + "k": 61.22746312147157, + }, + { + "d": 65.20120696381794, + "k": 60.95314377252715, + }, + { + "d": 54.187477954516474, + "k": 40.38182696955075, + }, + { + "d": 47.238932570542865, + "k": 40.38182696955075, + }, + { + "d": 49.19445787014681, + "k": 66.81971967133897, + }, + { + "d": 54.647978089254224, + "k": 56.74238762687298, + } + ] + +let input = { + high: high, + low: low, + close: close, + period: period, + signalPeriod: signalPeriod +}; + +describe('Stochastic', function() { + "use strict"; + it('should calculate Stochastic using the calculate method', function() { + assert.deepEqual(Stochastic.calculate(input), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate Stochastic by using getResult', function() { + let stochastic = new Stochastic(input); + assert.deepEqual(stochastic.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + + it('should be able to get Stochastic for the next bar using nextValue', function() { + let myInput = Object.assign({}, input); + myInput.high = []; + myInput.low = []; + myInput.close = []; + let stochastic = new Stochastic(myInput); + let results = []; + input.high.forEach((price,index) => { + let result = stochastic.nextValue({ + high : input.high[index], + low : input.low[index], + close: input.close[index] + }); + if(result) + results.push(result) + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }) + + it('should be able to calculate Stochastic for reversed input by using calculate method', function() { + let myInput = Object.assign({}, input); + myInput.reversedInput = true; + myInput.high.reverse(); + myInput.low.reverse(); + myInput.close.reverse(); + assert.deepEqual(Stochastic.calculate(myInput), expectResult.slice().reverse(), 'Wrong Results while calculating next bar'); + }); +}) \ No newline at end of file diff --git a/test/momentum/StochasticRSI.js b/test/momentum/StochasticRSI.js new file mode 100644 index 0000000..0a94fd8 --- /dev/null +++ b/test/momentum/StochasticRSI.js @@ -0,0 +1,41 @@ +// "use strict" +// var StochasticRSI = require('../../lib/momentum/StochasticRSI').StochasticRSI; +// var AverageGain = require('../../lib/Utils/AverageGain').AverageGain; +// var AverageLoss = require('../../lib/Utils/AverageLoss').AverageLoss; +// var assert = require("assert"); +// var data = require('../data'); + +// var inputStochasticRSI = { +// values : [44.34,44.09,44.15,43.61,44.33,44.83,45.10,45.42,45.84,46.08,45.89,46.03,45.61,46.28,46.28,46.00,46.03,46.41,46.22,45.64,46.21,46.25,45.71,46.45,45.78,45.35,44.03,44.18,44.22,44.57,43.42,42.66,43.13], +// rsiPeriod : 14, +// stochasticPeriod : 14, +// kPeriod : 3, +// dPeriod : 3, +// }; +// var expectedResult = [ +// 70.46, +// 66.25, +// 66.48, +// 69.35, +// 66.29, +// 57.92, +// 62.88, +// 63.21, +// 56.01, +// 62.34, +// 54.67, +// 50.39, +// 40.02, +// 41.49, +// 41.90, +// 45.50, +// 37.32, +// 33.09, +// 37.79 +// ]; + +// describe('StochasticRSI', function () { +// it('should calculate StochasticRSI using the calculate method', function () { +// assert.deepEqual(StochasticRSI.calculate(inputStochasticRSI), expectedResult, 'Wrong Results'); +// }); +// }) \ No newline at end of file diff --git a/test/momentum/TRIX.js b/test/momentum/TRIX.js new file mode 100644 index 0000000..dc8c968 --- /dev/null +++ b/test/momentum/TRIX.js @@ -0,0 +1,61 @@ +/** + * Created by AAravindan on 5/7/16. + */ +"use strict"; +const assert = require('assert'); +const TRIX = require('../../lib/momentum/TRIX').TRIX +var close = [127.75,129.024995,132.75,145.399995,148.975005,137.524995,147.375,139.050005,137.225005,149.300005,162.449995,178.949995,200.350005,221.899995,243.225005,243.524995,286.42499,280.274995,277.350005,269.024995,263.225005,214.899995,214.824995,244.449995,237.824995,258.95001,269.29999,258.524995,266.850005,266.149995,278.125,273.024995,264.024995,267.5,321.274995,351.67499,359.70001,396.774995,381.25,416.350005,444.649995,356.95001,354.42499,397.67499,498.975005,477.475005,529.925,489.399995,558.675,585.875,613,622.225,635.075,683.225,676.25,685.15,780.525,879.4,850.275,946.75,977,1149.025,1391.9]; + +let input = { + values : close, + period : 18 +}; + + +//let expectResult = [294.0550,299.1142,303.7449,307.7237,312.2738,318.1194,323.9484,330.4611,337.2436,345.5079,356.4119] +let expectResult = [ + 3.495053924801398, + 3.5278549202327794, + 3.5522629525879506, + 3.5643471650933187, + 3.593521705866772, + 3.6569506396348666, + 3.7185329071104127, + 3.7967569024995673, + 3.879744447459609, + 4.001718026984088, + 4.198949431354705, +] + +describe('TRIX', function() { + "use strict"; + it('should calculate TRIX using the calculate method', function() { + assert.deepEqual(TRIX.calculate(input), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate TRIX by using getResult', function() { + let trix = new TRIX(input); + assert.deepEqual(trix.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to get TRIX for the next bar using nextValue', function() { + let trixInput = Object.assign({}, input); + trixInput.values = []; + let trix = new TRIX(trixInput); + let results = []; + input.values.forEach(price => { + let result = trix.nextValue(price); + if(result) + results.push(result) + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }); + + it('should be able to calculate TRIX for reversed input by using calculate method', function() { + let myInput = Object.assign({}, input); + myInput.reversedInput = true; + myInput.values.reverse(); + assert.deepEqual(TRIX.calculate(myInput), expectResult.slice().reverse(), 'Wrong Results while calculating next bar'); + }); + +}) \ No newline at end of file diff --git a/test/momentum/WilliamsR.js b/test/momentum/WilliamsR.js new file mode 100644 index 0000000..ad558f8 --- /dev/null +++ b/test/momentum/WilliamsR.js @@ -0,0 +1,78 @@ +/** + * Created by AAravindan on 5/7/16. + */ +"use strict" +const assert = require('assert'); +const WilliamsR = require('../../lib/momentum/WilliamsR').WilliamsR + +//let data = [4,2,5,8,6]; +let high = [127.0090,127.6159,126.5911,127.3472,128.1730,128.4317,127.3671,126.4220,126.8995,126.8498,125.6460,125.7156,127.1582,127.7154,127.6855,128.2228,128.2725,128.0934,128.2725,127.7353,128.7700,129.2873,130.0633,129.1182,129.2873,128.4715,128.0934,128.6506,129.1381,128.6406]; +let low = [125.3574,126.1633,124.9296,126.0937,126.8199,126.4817,126.0340,124.8301,126.3921,125.7156,124.5615,124.5715,125.0689,126.8597,126.6309,126.8001,126.7105,126.8001,126.1335,125.9245,126.9891,127.8148,128.4715,128.0641,127.6059,127.5960,126.9990,126.8995,127.4865,127.3970]; +let close = [125.3574,126.1633,124.9296,126.0937,126.8199,126.4817,126.0340,124.8301,126.3921,125.7156,124.5615,124.5715,125.0689,127.2876,127.1781,128.0138,127.1085,127.7253,127.0587,127.3273,128.7103,127.8745,128.5809,128.6008,127.9342,128.1133,127.5960,127.5960,128.6904,128.2725]; +let period = 14; +let expectResult=[ + -29.561779752984485, + -32.391090899695165, + -10.797891581830443, + -34.189447573768696, + -18.252286703529535, + -35.476202780218095, + -25.470223659391287, + -1.4185576808844131, + -29.89546743408507, + -26.943909266058082, + -26.582209458722687, + -38.76870971266242, + -39.04372897645341, + -59.61389774813938, + -59.61389774813938, + -33.171450662027304, + -43.26858026481079, +] +let input = { + high: high, + low: low, + close: close, + period: period, +}; + +describe('WilliamsR', function() { + "use strict"; + it('should calculate WilliamsR using the calculate method', function() { + assert.deepEqual(WilliamsR.calculate(input), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate williamsR by using getResult', function() { + let williamsR = new WilliamsR(input); + assert.deepEqual(williamsR.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + + it('should be able to get williamsR for the next bar using nextValue', function() { + let myInput = Object.assign({}, input); + myInput.high = []; + myInput.low = []; + myInput.close = []; + let williamsR = new WilliamsR(myInput); + let results = []; + input.high.forEach((price,index) => { + let result = williamsR.nextValue({ + high : input.high[index], + low : input.low[index], + close: input.close[index] + }); + if(result) + results.push(result) + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }) + + it('should be able to calculate williamsR for reversed input by using calculate method', function() { + let myInput = Object.assign({}, input); + myInput.reversedInput = true; + myInput.high.reverse(); + myInput.low.reverse(); + myInput.close.reverse(); + assert.deepEqual(WilliamsR.calculate(myInput), expectResult.slice().reverse(), 'Wrong Results while calculating next bar'); + }); +}); \ No newline at end of file diff --git a/test/moving_averages/EMA.js b/test/moving_averages/EMA.js new file mode 100644 index 0000000..20f6de2 --- /dev/null +++ b/test/moving_averages/EMA.js @@ -0,0 +1,44 @@ +/** + * Created by AAravindan on 5/3/16. + */ +var EMA = require('../../lib/moving_averages/EMA').EMA; +var assert = require('assert'); +var data = require('../data'); + +var prices = data.close; +var period = 9; +var expectedOutput = [ + 138.3422222222222, + 140.53377777777777, + 144.91702222222222, + 151.72361777777778, + 161.4488942222222, + 173.53911537777776, + 187.4772923022222, + 198.68583384177776, + 216.23266707342222, + 229.04013365873777, +] + + +describe('EMA (Exponential Moving Average)', function() { + it('should calculate EMA using the calculate method', function() { + assert.deepEqual(EMA.calculate({period : period, values : prices}), expectedOutput, 'Wrong Results'); + }); + + it('should be able to get EMA from the get results', function() { + var emaProducer = new EMA({period : period, values : prices}); + assert.deepEqual(emaProducer.getResult(), expectedOutput, 'Wrong Results while getting results'); + }); + + it('should be able to get EMA for the next bar using nextValue', function() { + var emaProducer = new EMA({period : period, values : []}); + var results = []; + prices.forEach(price => { + var result = emaProducer.nextValue(price); + if(result) + results.push(result) + }); + assert.deepEqual(results, expectedOutput, 'Wrong Results while getting results'); + }) +}) diff --git a/test/moving_averages/MACD.js b/test/moving_averages/MACD.js new file mode 100644 index 0000000..9a552da --- /dev/null +++ b/test/moving_averages/MACD.js @@ -0,0 +1,104 @@ +/** + * Created by AAravindan on 5/4/16. + */ +var MACD = require('../../lib/moving_averages/MACD').MACD; +var assert = require('assert'); +var data = require('../data'); + +var macdInput = { + values : data.close, + fastPeriod : 5, + slowPeriod : 8, + signalPeriod : 3 , + SimpleMAOscillator: false, + SimpleMASignal : false +} + +var expectedOutput = [ + { + "MACD": 1.5206018518518647, + "histogram": undefined, + "signal": undefined + }, + { + "MACD": 0.8747067901234686, + "histogram": undefined, + "signal": undefined + }, + { + "MACD": 1.8161162551440384, + "histogram": 0.41230795610424775, + "signal": 1.4038082990397907, + }, + { + "MACD": 3.630838477366268, + "histogram": 1.113515089163239, + "signal": 2.517323388203029, + }, + { + "MACD": 6.1361878905654805, + "histogram": 1.8094322511812262, + "signal": 4.326755639384254, + }, + { + "MACD": 9.35850329810836, + "histogram": 2.515873829362053, + "signal": 6.842629468746307, + }, + { + "MACD": 12.730555487344787, + "histogram": 2.9439630092992406, + "signal": 9.786592478045547, + }, + { + "MACD": 15.906022882701109, + "histogram": 3.05971520232778, + "signal": 12.846307680373329, + }, + { + "MACD": 16.40655983713023, + "histogram": 1.7801260783784514, + "signal": 14.62643375875178, + }, + { + "MACD": 20.217463455194945, + "histogram": 2.7955148482215826, + "signal": 17.421948606973363, + }, + { + "MACD": 20.012564334547392, + "histogram": 1.2953078637870163, + "signal": 18.717256470760375, + }, + ]; + +var input; + +describe('MACD (Moving Average Convergence Divergence)', function() { + + beforeEach(function(){ + input = JSON.parse(JSON.stringify(macdInput)); + }); + + it('should calculate MACD using the calculate method', function() { + assert.deepEqual(MACD.calculate(input), expectedOutput,'Wrong Results'); + }); + + it('should be able to get EMA from the get results', function() { + var macd = new MACD(input); + assert.deepEqual(macd.getResult(), expectedOutput,'Wrong Results'); + }); + + it('should be able to get MACD for the next bar using nextValue', function() { + input.values = []; + var macd = new MACD(input); + var results = []; + macdInput.values.forEach(price => { + var result = macd.nextValue(price); + if(result) + results.push(result) + }); + assert.deepEqual(results, expectedOutput, 'Wrong Results'); + + }); +}); \ No newline at end of file diff --git a/test/moving_averages/SMA.js b/test/moving_averages/SMA.js new file mode 100644 index 0000000..2d32f2c --- /dev/null +++ b/test/moving_averages/SMA.js @@ -0,0 +1,55 @@ +/** + * Created by AAravindan on 5/3/16. + */ +var SMA = require('../../lib/moving_averages/SMA').SMA; +var assert = require('assert'); +var data = require('../data') + +var prices = data.close; + +var period = 10; + +var expectResult = [ + 139.438, + 142.908, + 147.901, + 154.661, + 162.31099999999998, + 171.736, + 182.33599999999998, + 196.24, + 210.362, +] + + +describe('SMA (Simple Moving Average)', function() { + it('should calculate SMA using the calculate method', function() { + assert.deepEqual(SMA.calculate({period : period, values : prices}), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate EMA by using getResult', function() { + var smaProducer = new SMA({period : period, values : prices}); + assert.deepEqual(smaProducer.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to get EMA for the next bar using nextValue', function() { + var smaProducer = new SMA({period : period, values : []}); + var results = []; + prices.forEach(price => { + var result = smaProducer.nextValue(price); + if(result) + results.push(result) + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }) + + it('should be able to get SMA for low values(issue 1)', function() { + let expectedResult = [ 0.002, 0.00275, 0.0025, 0.003, 0.003, 0.0025 ]; + assert.deepEqual(SMA.calculate({period : 4, values : [0.001, 0.003, 0.001, 0.003, 0.004, 0.002, 0.003, 0.003, 0.002]}), expectedResult, 'Wrong Results'); + }) + + it('Passing format function should format the results appropriately', function() { + let expectedResult = [ 0.002, 0.003, 0.003, 0.003, 0.003, 0.003 ]; + assert.deepEqual(SMA.calculate({period : 4, values : [0.001, 0.003, 0.001, 0.003, 0.004, 0.002, 0.003, 0.003, 0.002], format : (val) => { return val.toPrecision(1) }}), expectedResult, 'Wrong Results'); + }) +}) \ No newline at end of file diff --git a/test/moving_averages/WEMA.js b/test/moving_averages/WEMA.js new file mode 100644 index 0000000..30f2058 --- /dev/null +++ b/test/moving_averages/WEMA.js @@ -0,0 +1,68 @@ +// /** +// * Created by AAravindan on 5/3/16. +// */ +// var WEMA = require('../../lib/moving_averages/WEMA').WEMA; +// var assert = require('assert'); +// var data = require('../data'); + +// var prices = [ +// 0.959399999999999, +// 0.4847, +// 1.3553, +// 0.7911, +// 0.880499999999998, +// 0.7516, +// 1.3057, +// 1.1078, +// 1.0187, +// 1.2364, +// 0.583400000000001, +// 1.0484, +// 0.731900000000003, +// 1.0781, +// 0.900100000000002, +// 1.0882, +// 1.1671, +// 1.6322, +// 0.722000000000001 +// ]; +// var expectedResult = [ +// 13.33, +// 13.28, +// 13.42, +// 13.63, +// 14.29, +// 13.99 +// ]; +// var period = 9; + +// describe('WEMA (Weighted Moving Average)', function() { +// it('should calculate WEMA using the calculate method', function() { +// assert.deepEqual(WEMA.calculate({ +// period : period, +// values : prices +// }), expectedResult, 'Wrong Results'); +// }); + +// it('should be able to get WEMA for the next bar', function() { +// var WEMA = new WEMA({ +// period : period, +// values : prices +// }); +// assert.deepEqual(WEMA.getResult(), expectedResult, 'Wrong Results while getting results'); +// }) + +// it('should be able to get WEMA for the next bar using nextValue', function() { +// var WEMA = new WEMA({ +// period : period, +// values : [] +// }); +// var results = []; +// prices.forEach(price => { +// var result = WEMA.nextValue(price); +// if(result) +// results.push(result) +// }); +// assert.deepEqual(results, expectedResult, 'Wrong Results while getting results'); +// }) +// }) diff --git a/test/moving_averages/WMA.js b/test/moving_averages/WMA.js new file mode 100644 index 0000000..0b06f54 --- /dev/null +++ b/test/moving_averages/WMA.js @@ -0,0 +1,52 @@ +/** + * Created by AAravindan on 5/3/16. + */ +var WMA = require('../../lib/moving_averages/WMA').WMA; +var assert = require('assert'); +var data = require('../data'); + +var prices = data.close; +var expectedResult = [ + 140.32866666666666, + 142.52022222222223, + 146.86288888888888, + 153.76266666666666, + 163.91577777777778, + 177.15777777777777, + 193.04533333333333, + 206.64177777777778, + 226.68177777777777, + 242.2168888888889, +]; +var period = 9; + +describe('WMA (Weighted Moving Average)', function() { + it('should calculate WMA using the calculate method', function() { + assert.deepEqual(WMA.calculate({ + period : period, + values : prices + }), expectedResult, 'Wrong Results'); + }); + + it('should be able to get WMA for the next bar', function() { + var wma = new WMA({ + period : period, + values : prices + }); + assert.deepEqual(wma.getResult(), expectedResult, 'Wrong Results while getting results'); + }) + + it('should be able to get WMA for the next bar using nextValue', function() { + var wma = new WMA({ + period : period, + values : [] + }); + var results = []; + prices.forEach(price => { + var result = wma.nextValue(price); + if(result) + results.push(result) + }); + assert.deepEqual(results, expectedResult, 'Wrong Results while getting results'); + }) +}) diff --git a/test/moving_averages/WilderSmoothing.js b/test/moving_averages/WilderSmoothing.js new file mode 100644 index 0000000..2c596e1 --- /dev/null +++ b/test/moving_averages/WilderSmoothing.js @@ -0,0 +1,68 @@ +/** + * Created by AAravindan on 5/3/16. + */ +var WilderSmoothing = require('../../lib/moving_averages/WilderSmoothing').WilderSmoothing; +var assert = require('assert'); +var data = require('../data'); + +var prices = [ +0.959399999999999, +0.4847, +1.3553, +0.7911, +0.880499999999998, +0.7516, +1.3057, +1.1078, +1.0187, +1.2364, +0.583400000000001, +1.0484, +0.731900000000003, +1.0781, +0.900100000000002, +1.0882, +1.1671, +1.6322, +0.722000000000001 +]; +var expectedResult = [ + 13.333000000000002, + 13.280742857142862, + 13.420318367346944, + 13.628824198250733, + 14.287536755518538, + 13.988998415838644, +]; +var period = 14; + +describe('WilderSmoothing (Wilder smoothing)', function() { + it('should calculate WilderSmoothing using the calculate method', function() { + assert.deepEqual(WilderSmoothing.calculate({ + period : period, + values : prices + }), expectedResult, 'Wrong Results'); + }); + + it('should be able to get WilderSmoothing for the next bar', function() { + var wilderSmoothing = new WilderSmoothing({ + period : period, + values : prices + }); + assert.deepEqual(wilderSmoothing.getResult(), expectedResult, 'Wrong Results while getting results'); + }) + + it('should be able to get WilderSmoothing for the next bar using nextValue', function() { + var wilderSmoothing = new WilderSmoothing({ + period : period, + values : [] + }); + var results = []; + prices.forEach(price => { + var result = wilderSmoothing.nextValue(price); + if(result) + results.push(result) + }); + assert.deepEqual(results, expectedResult, 'Wrong Results while getting results'); + }) +}) diff --git a/test/node.js b/test/node.js new file mode 100644 index 0000000..af06326 --- /dev/null +++ b/test/node.js @@ -0,0 +1,35 @@ +/** + * Created by AAravindan on 5/3/16. + */ +var sma = require('../dist/index.js').sma; +var cci = require('../dist/index.js').cci; +var AvailableIndicators = require('../dist/index.js').getAvailableIndicators; +var assert = require('assert'); +var data = require('./data') + +var prices = data.close; + +var period = 10; + +var expectResult = [ + 139.438, + 142.908, + 147.901, + 154.661, + 162.31099999999998, + 171.736, + 182.33599999999998, + 196.24, + 210.362, +] + + +describe('Test in node after build process', function() { + it('should calculate sma', function() { + assert.deepEqual(sma({period : period, values : prices}), expectResult, 'Wrong Results'); + }); + it('Available Indicators should be availabel in global object', function() { + console.log(AvailableIndicators()) + assert.notDeepStrictEqual(AvailableIndicators(), undefined, 'Available indicators not available in global object'); + }); +}) \ No newline at end of file diff --git a/test/oscillators/AwesomeOscillator.js b/test/oscillators/AwesomeOscillator.js new file mode 100644 index 0000000..a49b766 --- /dev/null +++ b/test/oscillators/AwesomeOscillator.js @@ -0,0 +1,56 @@ + +"use strict"; +let assert = require('assert'); +let AwesomeOscillator = require('../../lib/oscillators/AwesomeOscillator').AwesomeOscillator; + +let input = { + high : [24.63,24.69,24.99,25.36,25.19,25.17,25.01,24.96,25.08,25.25,25.21,25.37,25.61,25.58,25.46,25.33,25.09,25.03,24.91,24.89,25.13,24.63,24.69,24.99,25.36,25.19,25.17,25.01,24.96,25.08,25.25,25.21,25.37,25.61,25.58,25.46,25.33,25.09,25.03,24.91,24.89,25.13], + low : [24.63,24.69,24.99,25.36,25.19,25.17,25.01,24.96,25.08,25.25,25.21,25.37,25.61,25.58,25.46,25.33,25.09,25.03,24.91,24.89,25.13,24.63,24.69,24.99,25.36,25.19,25.17,25.01,24.96,25.08,25.25,25.21,25.37,25.61,25.58,25.46,25.33,25.09,25.03,24.91,24.89,25.13], + fastPeriod : 5, + slowPeriod : 34, + format : (a)=>parseFloat(a.toFixed(2)) +} + +let expectResult = [0.17, + 0.24, + 0.26, + 0.28, + 0.23, + 0.12, + -0.01, + -0.12, + -0.16] + +describe('AwesomeOscillator', function() { + it('should calculate AwesomeOscillator using the calculate method', function() { + assert.deepEqual(AwesomeOscillator.calculate(input), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate AwesomeOscillator by using getResult', function() { + let awesomeoscillator = new AwesomeOscillator(input); + assert.deepEqual(awesomeoscillator.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to get AwesomeOscillator for the next bar using nextValue', function() { + let awesomeoscillator = new AwesomeOscillator({ high : [], low:[], close:[], volume : [], fastPeriod: 5, slowPeriod : 34}); + let results = []; + input.high.forEach(function(close,index) { + let result = awesomeoscillator.nextValue({ + high: input.high[index], + low: input.low[index], + }); + if(result !== undefined){ + results.push(parseFloat(result.toFixed(2))); + } + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }) + + it('should be able to calculate AwesomeOscillator for reversed input by using calculate method', function() { + let myInput = Object.assign({}, input); + myInput.reversedInput = true; + myInput.high.reverse(); + myInput.low.reverse(); + assert.deepEqual(AwesomeOscillator.calculate(myInput), expectResult.slice().reverse(), 'Wrong Results while calculating next bar'); + }); +}) diff --git a/test/oscillators/CCI.js b/test/oscillators/CCI.js new file mode 100644 index 0000000..87604b1 --- /dev/null +++ b/test/oscillators/CCI.js @@ -0,0 +1,72 @@ +"use strict" +var CCI = require('../../lib/oscillators/CCI').CCI; +var assert = require("assert"); +var data = require('../data'); + +var inputCCI = { + open : [ + 23.94,23.85,23.94,23.73,23.60,23.46,23.53,23.73,24.09,23.95,23.92,24.04,23.83,24.05,24.89,24.95,24.91,25.24,25.13,25.26,24.74,24.36,24.49,24.70,24.65,24.48,24.46,24.62,23.81,23.91 + ], + high : [ + 24.20,24.07,24.04,23.87,23.67,23.59,23.80,23.80,24.30,24.15,24.05,24.06,23.88,25.14,25.20,25.07,25.22,25.37,25.36,25.26,24.82,24.44,24.65,24.84,24.75,24.51,24.68,24.67,23.84,24.30, + ], + low : [ + 23.85,23.72,23.64,23.37,23.46,23.18,23.40,23.57,24.05,23.77,23.60,23.84,23.64,23.94,24.74,24.77,24.90,24.93,24.96,24.93,24.21,24.21,24.43,24.44,24.20,24.25,24.21,24.15,23.63,23.76, + ], + close : [ + 23.89,23.95,23.67,23.78,23.50,23.32,23.75,23.79,24.14,23.81,23.78,23.86,23.70,24.96,24.88,24.96,25.18,25.07,25.27,25.00,24.46,24.28,24.62,24.58,24.53,24.35,24.34,24.23,23.76,24.20, + ], + period : 20 +}; +var expectedResult = [ + 102.19852632840085, + 30.770139381053642, + 6.498977012877848, + 33.16030534351142, + 34.93862134088762, + 13.992326788535587, + -10.730541358353888, + -11.528187825109272, + -29.31511455515407, + -129.55641482382595, + -73.17724561559666, +]; + + + + +describe('CCI (Commodity Channel Index', function () { + it('should calculate CCI using the calculate method', function () { + assert.deepEqual(CCI.calculate(inputCCI), expectedResult, 'Wrong Results'); + }); + + it('should be able to get CCI for the next bar', function () { + var cci = new CCI(inputCCI); + assert.deepEqual(cci.getResult(), expectedResult, 'Wrong Results while getting results'); + }) + + it('should be able to get CCI for the next bar using nextValue', function () { + var cci = new CCI({ + open : [], + high : [], + low : [], + close : [], + period : 20 + }); + + var results = []; + + inputCCI.close.forEach((price,index) => { + var result = cci.nextValue({ + open : inputCCI.open[index], + high : inputCCI.high[index], + low : inputCCI.low[index], + close : inputCCI.close[index] + }); + if (result != undefined) { + results.push(result) + } + }); + assert.deepEqual(results, expectedResult, 'Wrong Results while getting results'); + }) +}) \ No newline at end of file diff --git a/test/oscillators/RSI.js b/test/oscillators/RSI.js new file mode 100644 index 0000000..232461e --- /dev/null +++ b/test/oscillators/RSI.js @@ -0,0 +1,80 @@ +"use strict" +var RSI = require('../../lib/oscillators/RSI').RSI; +var AverageGain = require('../../lib/Utils/AverageGain').AverageGain; +var AverageLoss = require('../../lib/Utils/AverageLoss').AverageLoss; +var assert = require("assert"); +var data = require('../data'); + +var inputRSI = { + values : [44.34,44.09,44.15,43.61,44.33,44.83,45.10,45.42,45.84,46.08,45.89,46.03,45.61,46.28,46.28,46.00,46.03,46.41,46.22,45.64,46.21,46.25,45.71,46.45,45.78,45.35,44.03,44.18,44.22,44.57,43.42,42.66,43.13], + period : 14 +}; +var expectedResult = [ + 70.46, + 66.25, + 66.48, + 69.35, + 66.29, + 57.92, + 62.88, + 63.21, + 56.01, + 62.34, + 54.67, + 50.39, + 40.02, + 41.49, + 41.90, + 45.50, + 37.32, + 33.09, + 37.79 +]; + +//have issue with this input +var noGainsInput = { + values : [ 294435, 294435, 294435, 294500, 294500, 294500, 294520, 294539, 294539, 294600, 294600, 294600, 294600, 294600, 294700, 294600, 294600, 294600, 294600, 294600, 294700 ], + period : 14 + }; + var noGainsExpectedResult = [ + 100, 71.1, 71.1, 71.1, 71.1, 71.1, 79.63 + ]; + + +describe('RSI (Relative Strength Index)', function () { + it('should calculate RSI when there is no gains or losses', function () { + let result = RSI.calculate(noGainsInput); + assert.deepEqual(result, noGainsExpectedResult, 'Wrong Results'); + }); + + it('should calculate RSI using the calculate method', function () { + assert.deepEqual(RSI.calculate(inputRSI), expectedResult, 'Wrong Results'); + }); + + it('should be able to get RSI for the next bar', function () { + var rsi = new RSI(inputRSI); + assert.deepEqual(rsi.getResult(), expectedResult, 'Wrong Results while getting results'); + }) + + it('should be able to get RSI for the next bar using nextValue', function () { + var rsi = new RSI({ + values : [], + period : 14 + }); + var results = []; + inputRSI.values.forEach(price => { + var result = rsi.nextValue(price); + if (result!==undefined) { + results.push(result) + } + }); + assert.deepEqual(results, expectedResult, 'Wrong Results while getting results'); + }) + + it('should be able to calculate ROC for reversed input by using calculate method', function() { + let myInput = Object.assign({}, inputRSI); + myInput.reversedInput = true; + myInput.values.reverse(); + assert.deepEqual(RSI.calculate(myInput), expectedResult.slice().reverse(), 'Wrong Results while calculating next bar'); + }); +}) \ No newline at end of file diff --git a/test/volatility/BollingerBands.js b/test/volatility/BollingerBands.js new file mode 100644 index 0000000..aef78c4 --- /dev/null +++ b/test/volatility/BollingerBands.js @@ -0,0 +1,104 @@ +/** + * Created by AAravindan on 5/3/16. + */ +"use strict" +var BB = require('../../lib/volatility/BollingerBands').BollingerBands; +var assert = require('assert'); +var data = require('../data') + +var prices = data.close; + +var period = 10; + +var stdDev = 2; +var expectResult = [ + { + "lower": 124.13430685095913, + "middle": 139.438, + "pb": 0.8222098059584437, + "upper": 154.74169314904083, + }, + { + "lower": 124.38189560646923, + "middle": 142.908, + "pb": 1.0274179499610292, + "upper": 161.43410439353073, + }, + { + "lower": 121.7102375826896, + "middle": 147.901, + "pb": 1.092747158430917, + "upper": 174.0917624173104, + }, + { + "lower": 115.78036785493323, + "middle": 154.661, + "pb": 1.0875547474322258, + "upper": 193.54163214506679, + }, + { + "lower": 107.06844071822883, + "middle": 162.31099999999998, + "pb": 1.0393396031496236, + "upper": 217.55355928177113, + }, + { + "lower": 99.31718023607398, + "middle": 171.736, + "pb": 0.9936147829601419, + "upper": 244.154819763926, + }, + { + "lower": 102.41148150911386, + "middle": 182.33599999999998, + "pb": 0.882761142358192, + "upper": 262.2605184908861, + }, + { + "lower": 98.98123874940623, + "middle": 196.24, + "pb": 0.9636086191127045, + "upper": 293.4987612505938, + }, + { + "lower": 109.47750652354941, + "middle": 210.362, + "pb": 0.8464754472713815, + "upper": 311.2464934764506, + } +] + + +describe('BB (Bollinger Bands)', function () { + it('should calculate BB using the calculate method', function () { + assert.deepEqual(BB.calculate({ period: period, values: prices, stdDev: stdDev }), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate BB by using getResult', function () { + var bb = new BB({ period: period, values: prices, stdDev: stdDev }); + assert.deepEqual(bb.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to get BB for the next bar using nextValue', function () { + var bb = new BB({ period: period, values: [], stdDev: stdDev }); + var results = []; + prices.forEach(price => { + var result = bb.nextValue(price); + if (result) + results.push(result) + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }) + + it('should be able to calculate BB for reversed input by using calculate method', function () { + let myInput = Object.assign({}, { + period: period, + values: prices, + stdDev: stdDev + }); + myInput.reversedInput = true; + myInput.values.reverse(); + assert.deepEqual(BB.calculate(myInput), expectResult.slice().reverse(), 'Wrong Results while calculating next bar'); + }); + +}) diff --git a/test/volatility/ChandelierExit.js b/test/volatility/ChandelierExit.js new file mode 100644 index 0000000..d51c3cd --- /dev/null +++ b/test/volatility/ChandelierExit.js @@ -0,0 +1,52 @@ + +// "use strict"; +// let assert = require('assert'); +// let ChandelierExit = require('../../lib/volume/ChandelierExit').ChandelierExit; + +// let input = { +// high : [24.63,24.69,24.99,25.36,25.19,25.17,25.01,24.96,25.08,25.25,25.21,25.37,25.61,25.58,25.46,25.33,25.09,25.03,24.91,24.89,25.13], +// low : [24.63,24.69,24.99,25.36,25.19,25.17,25.01,24.96,25.08,25.25,25.21,25.37,25.61,25.58,25.46,25.33,25.09,25.03,24.91,24.89,25.13], +// close : [24.63,24.69,24.99,25.36,25.19,25.17,25.01,24.96,25.08,25.25,25.21,25.37,25.61,25.58,25.46,25.33,25.09,25.03,24.91,24.89,25.13], +// volume : [18730,12272,24691,18358,22964,15919,16067,16568,16019,9774,22573,12987,10907,5799,7395,5818,7165,5673,5625,5023,7457], +// period : 14 +// } + +// let expectResult = [49.46,45.11,36.27,28.40,31.53,33.87,41.30] + +// describe('ChandelierExit', function() { +// it('should calculate ChandelierExit using the calculate method', function() { +// assert.deepEqual(ChandelierExit.calculate(input), expectResult, 'Wrong Results'); +// }); + +// it('should be able to calculate ChandelierExit by using getResult', function() { +// let chandelierexit = new ChandelierExit(input); +// assert.deepEqual(chandelierexit.getResult(), expectResult, 'Wrong Results while calculating next bar'); +// }); + +// it('should be able to get ChandelierExit for the next bar using nextValue', function() { +// let chandelierexit = new ChandelierExit({ high : [], low:[], close:[], volume : [], period:14}); +// let results = []; +// input.close.forEach(function(close,index) { +// let result = chandelierexit.nextValue({ +// close: input.close[index], +// high: input.high[index], +// low: input.low[index], +// volume: input.volume[index] +// }); +// if(result !== undefined){ +// results.push(parseFloat(result.toFixed(2))); +// } +// }); +// assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); +// }) + +// it('should be able to calculate ChandelierExit for reversed input by using calculate method', function() { +// let myInput = Object.assign({}, input); +// myInput.reversedInput = true; +// myInput.high.reverse(); +// myInput.low.reverse(); +// myInput.close.reverse(); +// myInput.volume.reverse(); +// assert.deepEqual(ChandelierExit.calculate(myInput), expectResult.slice().reverse(), 'Wrong Results while calculating next bar'); +// }); +// }) diff --git a/test/volatility/KeltnerChannels.js b/test/volatility/KeltnerChannels.js new file mode 100644 index 0000000..14a54d4 --- /dev/null +++ b/test/volatility/KeltnerChannels.js @@ -0,0 +1,772 @@ + +"use strict"; +let assert = require('assert'); +let KeltnerChannels = require('../../lib/volatility/KeltnerChannels').KeltnerChannels; + +var input = { + useSMA : false, + maPeriod : 20, + atrPeriod : 10, + multiplier : 1, + "high": [ + 227.82, + 228.89, + 228.04, + 228.33, + 227.94, + 229.44, + 229.59, + 229.07, + 229.06, + 227.88, + 228.26, + 228.14, + 227.44, + 228.2, + 228.1, + 228.24, + 228.45, + 228.2, + 228.43, + 227.47, + 227.59, + 227.09, + 226.3, + 225.78, + 226.21, + 226.45, + 226.45, + 226, + 226.94, + 226.85, + 226.33, + 225.97, + 226.45, + 226.7, + 226.25, + 226.04, + 225.28, + 225.03, + 223.99, + 222.99, + 223.25, + 223.09, + 222.91, + 223.02, + 223.01, + 223.21, + 223.8, + 223.9, + 223.77, + 223.08, + 222.89, + 223.9, + 224.39, + 224.36, + 223.67, + 223.46, + 223.06, + 223.89, + 223.61, + 217.19, + 203.33, + 203.51, + 202.86, + 202.84, + 200.38, + 197.64, + 199.12, + 198.47, + 198.14, + 198.02, + 197.5, + 199.5, + 201.1, + 199.83, + 199.86, + 200.66, + 199.9, + 199.64, + 198.82, + 198.32, + 197.4, + 197.45, + 190.72, + 192.93, + 193, + 193.98, + 194.6, + 193.9, + 196.76, + 198.5, + 196.25, + 196, + 194.94, + 195.07, + 195.71, + 195.71, + 195.5, + 198, + 196.84, + 196.25, + 194.49, + 195.5, + 195.6 + ], + "low": [ + 226.96, + 227.42, + 227.07, + 227.3, + 227.29, + 227.37, + 228.69, + 228.24, + 227.7, + 227.57, + 227.5, + 226.5, + 226.8, + 227, + 227.67, + 228.09, + 227.7, + 226.95, + 226.82, + 226.77, + 226.92, + 225.59, + 225.12, + 225.06, + 225.06, + 226, + 225.63, + 225.6, + 225.99, + 226.2, + 225.5, + 225.28, + 225.9, + 225.83, + 225.81, + 224.37, + 224.93, + 223.55, + 222.99, + 221.59, + 221.52, + 222.32, + 222.22, + 222.6, + 222.52, + 222.69, + 222.95, + 223.36, + 223.01, + 221.73, + 221.89, + 222.79, + 223.38, + 222.79, + 223.29, + 222.93, + 222.73, + 222.66, + 216.41, + 195.1, + 197.51, + 200.07, + 201, + 198.52, + 192.5, + 194.43, + 197.19, + 196.64, + 196.94, + 194.62, + 195.59, + 193.49, + 198.87, + 197.43, + 197.79, + 198.76, + 197.94, + 197.87, + 194.44, + 196.92, + 195.55, + 184.81, + 187.15, + 189.92, + 191.63, + 190.77, + 192.61, + 192.62, + 192.65, + 195.2, + 195, + 193.3, + 193.49, + 193.3, + 193.98, + 194.5, + 194.54, + 194.26, + 195.86, + 193.52, + 193.6, + 193.89, + 195.02 + ], + "close": [ + 227.49, + 227.62, + 227.3, + 227.93, + 227.37, + 229.39, + 228.9, + 229.06, + 227.71, + 227.68, + 228.14, + 226.96, + 227.31, + 227.92, + 228.09, + 228.24, + 227.71, + 228.2, + 227.46, + 227.18, + 227.06, + 226.12, + 225.78, + 225.07, + 226.02, + 226.44, + 225.74, + 225.99, + 226.61, + 226.33, + 225.97, + 225.91, + 226.36, + 226.15, + 225.91, + 225.08, + 225.03, + 223.95, + 222.99, + 222.19, + 223.09, + 222.8, + 222.68, + 222.78, + 222.95, + 222.96, + 223.8, + 223.77, + 223.06, + 221.94, + 222.8, + 223.59, + 224.35, + 223.37, + 223.35, + 222.99, + 222.76, + 223.61, + 217.19, + 202.24, + 200.26, + 201.22, + 201.65, + 200.38, + 196.83, + 197.28, + 198.47, + 197.55, + 198.02, + 195.58, + 197.06, + 198.87, + 199.33, + 197.9, + 198.76, + 199.79, + 197.96, + 198.76, + 197.9, + 197.08, + 196.34, + 188.32, + 189.93, + 191.64, + 192.44, + 193.55, + 193.68, + 192.82, + 195.35, + 195.89, + 195.82, + 193.48, + 194.75, + 194.35, + 195.17, + 195.45, + 194.64, + 196.3, + 196.01, + 193.95, + 193.89, + 195.36, + 195.16 + ] +} + + +let expectResult = [ + { + "middle": 227.88299999999998, + "upper": 228.85501832015498, + "lower": 226.91098167984498 + }, + { + "middle": 227.80461904761904, + "upper": 228.74643553575854, + "lower": 226.86280255947955 + }, + { + "middle": 227.644179138322, + "upper": 228.64181397764753, + "lower": 226.64654429899645 + }, + { + "middle": 227.4666382680056, + "upper": 228.48250962339858, + "lower": 226.4507669126126 + }, + { + "middle": 227.23838700438603, + "upper": 228.22467122423973, + "lower": 226.25210278453233 + }, + { + "middle": 227.12235014682545, + "upper": 228.12500594469378, + "lower": 226.11969434895713 + }, + { + "middle": 227.05736441855638, + "upper": 228.00475463663787, + "lower": 226.10997420047488 + }, + { + "middle": 226.93190114059863, + "upper": 227.86655233687196, + "lower": 225.9972499443253 + }, + { + "middle": 226.84219627006541, + "upper": 227.72338234671142, + "lower": 225.9610101934194 + }, + { + "middle": 226.820082339583, + "upper": 227.7081498085644, + "lower": 225.9320148706016 + }, + { + "middle": 226.7734078310513, + "upper": 227.63766855313455, + "lower": 225.90914710896803 + }, + { + "middle": 226.6968927995226, + "upper": 227.55772744939753, + "lower": 225.83605814964767 + }, + { + "middle": 226.6219506281395, + "upper": 227.46570181302695, + "lower": 225.77819944325205 + }, + { + "middle": 226.59700294926907, + "upper": 227.41137901566776, + "lower": 225.78262688287037 + }, + { + "middle": 226.55443123981487, + "upper": 227.3743696995737, + "lower": 225.73449278005606 + }, + { + "middle": 226.493056836023, + "upper": 227.27500144980593, + "lower": 225.71111222224005 + }, + { + "middle": 226.358479994497, + "upper": 227.22923014690164, + "lower": 225.48772984209236 + }, + { + "middle": 226.23195809025918, + "upper": 227.05063322742336, + "lower": 225.413282953095 + }, + { + "middle": 226.01462874832973, + "upper": 226.89943637177748, + "lower": 225.12982112488197 + }, + { + "middle": 225.72656886753643, + "upper": 226.6228957286394, + "lower": 224.83024200643345 + }, + { + "middle": 225.38975278491392, + "upper": 226.33644695990662, + "lower": 224.44305860992122 + }, + { + "middle": 225.1707287101602, + "upper": 226.19575346765362, + "lower": 224.1457039526668 + }, + { + "middle": 224.94494502347828, + "upper": 225.94446730522236, + "lower": 223.9454227417342 + }, + { + "middle": 224.72923597362322, + "upper": 225.69780602719288, + "lower": 223.76066592005355 + }, + { + "middle": 224.54359445232578, + "upper": 225.4573075005385, + "lower": 223.62988140411306 + }, + { + "middle": 224.39182355210428, + "upper": 225.26316529549572, + "lower": 223.52048180871284 + }, + { + "middle": 224.25545940428484, + "upper": 225.0916669733371, + "lower": 223.41925183523256 + }, + { + "middle": 224.21208231816246, + "upper": 225.04966913030952, + "lower": 223.3744955060154 + }, + { + "middle": 224.1699792402422, + "upper": 224.97780737117458, + "lower": 223.36215110930985 + }, + { + "middle": 224.06426693164772, + "upper": 224.86731224948684, + "lower": 223.2612216138086 + }, + { + "middle": 223.86195579530033, + "upper": 224.71969658135555, + "lower": 223.00421500924512 + }, + { + "middle": 223.76081714812887, + "upper": 224.63278385557857, + "lower": 222.88885044067916 + }, + { + "middle": 223.74454884830706, + "upper": 224.6403188850118, + "lower": 222.84877881160233 + }, + { + "middle": 223.802210862754, + "upper": 224.70940389578826, + "lower": 222.89501782971976 + }, + { + "middle": 223.7610479234441, + "upper": 224.73452165317494, + "lower": 222.78757419371325 + }, + { + "middle": 223.7219005021637, + "upper": 224.63602685892144, + "lower": 222.80777414540594 + }, + { + "middle": 223.65219569243382, + "upper": 224.5279094135158, + "lower": 222.77648197135184 + }, + { + "middle": 223.5672246741068, + "upper": 224.38836702308058, + "lower": 222.74608232513302 + }, + { + "middle": 223.57129851466806, + "upper": 224.43332662874445, + "lower": 222.70927040059166 + }, + { + "middle": 222.9635557989854, + "upper": 224.45938110165415, + "lower": 221.46773049631665 + }, + { + "middle": 220.98988381812964, + "upper": 224.54512659053154, + "lower": 217.43464104572774 + }, + { + "middle": 219.01560916878395, + "upper": 222.79732766394565, + "lower": 215.23389067362226 + }, + { + "middle": 217.32078924794737, + "upper": 221.0683358935929, + "lower": 213.57324260230186 + }, + { + "middle": 215.82833312909526, + "upper": 219.38712511017624, + "lower": 212.26954114801427 + }, + { + "middle": 214.35706330727666, + "upper": 217.99197609024955, + "lower": 210.72215052430377 + }, + { + "middle": 212.68781918277412, + "upper": 216.7472406874497, + "lower": 208.62839767809854 + }, + { + "middle": 211.22040783203371, + "upper": 215.19488718624174, + "lower": 207.2459284778257 + }, + { + "middle": 210.00608327660194, + "upper": 213.77611469538917, + "lower": 206.23605185781472 + }, + { + "middle": 208.81978963121128, + "upper": 212.3958179081198, + "lower": 205.24376135430276 + }, + { + "middle": 207.7912382377626, + "upper": 211.12966368698025, + "lower": 204.45281278854495 + }, + { + "middle": 206.62826316749948, + "upper": 209.97284607179537, + "lower": 203.2836802632036 + }, + { + "middle": 205.71700000869, + "upper": 208.9191246225563, + "lower": 202.51487539482372 + }, + { + "middle": 205.06490476976714, + "upper": 208.5478169222468, + "lower": 201.58199261728748 + }, + { + "middle": 204.51872336312266, + "upper": 207.87634430035436, + "lower": 201.16110242589096 + }, + { + "middle": 203.88836875711098, + "upper": 207.1502276006195, + "lower": 200.62650991360246 + }, + { + "middle": 203.39995268500516, + "upper": 206.54262564416285, + "lower": 200.25727972584747 + }, + { + "middle": 203.05614766738563, + "upper": 206.07455333062754, + "lower": 200.03774200414372 + }, + { + "middle": 202.57080027049176, + "upper": 205.48336536740948, + "lower": 199.65823517357404 + }, + { + "middle": 202.2078669113973, + "upper": 205.00617549862326, + "lower": 199.40955832417134 + }, + { + "middle": 201.7975938722166, + "upper": 204.75407160071995, + "lower": 198.84111614371324 + }, + { + "middle": 201.34829921771978, + "upper": 204.1491291733728, + "lower": 198.54746926206676 + }, + { + "middle": 200.8713183398417, + "upper": 203.57706529992942, + "lower": 198.165571379754 + }, + { + "middle": 199.67595468842822, + "upper": 203.37512695250717, + "lower": 195.97678242434927 + }, + { + "middle": 198.74776852762554, + "upper": 202.4340235652966, + "lower": 195.06151348995448 + }, + { + "middle": 198.0708381916612, + "upper": 201.68946772556515, + "lower": 194.45220865775727 + }, + { + "middle": 197.53456788769347, + "upper": 200.92833446820703, + "lower": 194.1408013071799 + }, + { + "middle": 197.15508523172267, + "upper": 200.53047515418487, + "lower": 193.77969530926046 + }, + { + "middle": 196.82412473346338, + "upper": 200.06097566367936, + "lower": 193.5872738032474 + }, + { + "middle": 196.44277952075257, + "upper": 199.48394535794694, + "lower": 193.4016136835582 + }, + { + "middle": 196.3387052806809, + "upper": 199.48675453415584, + "lower": 193.19065602720596 + }, + { + "middle": 196.29597144442556, + "upper": 199.459215772553, + "lower": 193.13272711629813 + }, + { + "middle": 196.25064083067073, + "upper": 199.22256072598543, + "lower": 193.27872093535603 + }, + { + "middle": 195.98677027536877, + "upper": 198.931498181152, + "lower": 193.04204236958554 + }, + { + "middle": 195.86898263009556, + "upper": 198.66523774530046, + "lower": 193.07272751489066 + }, + { + "middle": 195.7243176177055, + "upper": 198.4179472213899, + "lower": 193.0306880140211 + }, + { + "middle": 195.67152546363832, + "upper": 198.2687921069543, + "lower": 193.07425882032234 + }, + { + "middle": 195.65042780043467, + "upper": 198.10896777941906, + "lower": 193.19188782145028 + }, + { + "middle": 195.55419658134565, + "upper": 197.8628825624316, + "lower": 193.2455106002597 + }, + { + "middle": 195.62522547836036, + "upper": 198.0770428613377, + "lower": 193.17340809538302 + }, + { + "middle": 195.66187067089746, + "upper": 197.96650631557708, + "lower": 193.35723502621784 + }, + { + "middle": 195.4988353689072, + "upper": 197.84600744911887, + "lower": 193.15166328869554 + }, + { + "middle": 195.3456129528208, + "upper": 197.54706782501128, + "lower": 193.14415808063032 + }, + { + "middle": 195.34698314779024, + "upper": 197.48929253276168, + "lower": 193.2046737628188 + }, + { + "middle": 195.32917522895306, + "upper": 197.31525367542736, + "lower": 193.34309678247877 + } +] + +describe('KeltnerChannels', function() { + it('should calculate KeltnerChannels using the calculate method', function() { + var out = KeltnerChannels.calculate(input); + require('fs').writeFileSync('keltner', JSON.stringify(out, null, 2)) + assert.deepEqual(out, expectResult, 'Wrong Results'); + }); + + it('should be able to calculate KeltnerChannels by using getResult', function() { + let keltnerchannels = new KeltnerChannels(input); + assert.deepEqual(keltnerchannels.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to calculate KeltnerChannels for reversed input by using calculate method', function() { + let myInput = Object.assign({}, input); + myInput.reversedInput = true; + myInput.high.reverse(); + myInput.low.reverse(); + myInput.close.reverse(); + assert.deepEqual(KeltnerChannels.calculate(myInput), expectResult.slice().reverse(), 'Wrong Results while calculating next bar'); + }); +}) diff --git a/test/volume/ADL.js b/test/volume/ADL.js new file mode 100644 index 0000000..35400e1 --- /dev/null +++ b/test/volume/ADL.js @@ -0,0 +1,57 @@ +/** + * Created by AAravindan on 5/17/16. + */ +/** + * Created by AAravindan on 5/8/16. + */ +"use strict"; +let assert = require('assert'); +let ADL = require('../../lib/volume/ADL').ADL; + +let input = { + high : [62.3400,62.0500,62.2700,60.7900,59.9300,61.7500,60.0000,59.0000,59.0700,59.2200,58.7500,58.6500,58.4700,58.2500,58.3500,59.8600,59.5299,62.1000,62.1600,62.6700,62.3800,63.7300,63.8500,66.1500,65.3400,66.4800,65.2300,63.4000,63.1800,62.7000], + low : [61.3700,60.6900,60.1000,58.6100,58.7120,59.8600,57.9700,58.0200,57.4800,58.3000,57.8276,57.8600,57.9100,57.8333,57.5300,58.5800,58.3000,58.5300,59.8000,60.9300,60.1500,62.2618,63.0000,63.5800,64.0700,65.2000,63.2100,61.8800,61.1100,61.2500], + close : [62.1500,60.8100,60.4500,59.1800,59.2400,60.2000,58.4800,58.2400,58.6900,58.6500,58.4700,58.0200,58.1700,58.0700,58.1300,58.9400,59.1000,61.9200,61.3700,61.6800,62.0900,62.8900,63.5300,64.0100,64.7700,65.2200,63.2800,62.4000,61.5500,62.6900], + volume : [7849.025,11692.075,10575.307,13059.128,20733.508,29630.096,17705.294,7259.203,10474.629,5203.714,3422.865,3962.15,4095.905,3766.006,4239.335,8039.979,6956.717,18171.552,22225.894,14613.509,12319.763,15007.69,8879.667,22693.812,10191.814,10074.152,9411.62,10391.69,8926.512,7459.575] +} + +let expectResult = [4774,-4855,-12019,-18249,-21006,-39976,-48785,-52785,-47317,-48561,-47216,-49574,-49866,-49354,-47389,-50907,-48813,-32474,-25128,-27144,-18028,-20193,-18000,-33099,-32056,-41816,-50575,-53856,-58988,-51631] + +describe('ADL (Accumulation Distribution line)', function() { + it('should calculate ADL using the calculate method', function() { + assert.deepEqual(ADL.calculate(input), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate ADL by using getResult', function() { + let adl = new ADL(input); + assert.deepEqual(adl.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to get ADL for the next bar using nextValue', function() { + let adl = new ADL({ high : [], low:[], close:[], volume : []}); + let results = []; + input.close.forEach(function(close,index) { + let result = adl.nextValue({ + close: input.close[index], + high: input.high[index], + low: input.low[index], + volume: input.volume[index] + }); + if(result !== undefined){ + results.push(parseFloat(result.toFixed(2))); + } + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }) + + it('should be able to calculate ADL for reversed input by using calculate method', function() { + let myInput = Object.assign({}, input); + myInput.reversedInput = true; + myInput.high.reverse(); + myInput.low.reverse(); + myInput.close.reverse(); + myInput.volume.reverse(); + assert.deepEqual(ADL.calculate(myInput), expectResult.slice().reverse(), 'Wrong Results while calculating next bar'); + }); + +}) \ No newline at end of file diff --git a/test/volume/ForceIndex.js b/test/volume/ForceIndex.js new file mode 100644 index 0000000..39a4a83 --- /dev/null +++ b/test/volume/ForceIndex.js @@ -0,0 +1,63 @@ +"use strict" +var ForceIndex = require('../../lib/volume/ForceIndex').ForceIndex; +var assert = require("assert"); + +var inputForceIndex = { + open : [ + + ], + high : [ + ], + low : [ + ], + close : [ + 14.33,14.23,13.98,13.96,13.93,13.84,13.99,14.31,14.51,14.46,14.61,14.48,14.53,14.56,14.25,14.42 + ], + volume : [ + 0,45579,66285,51761,69341,41631,73499,55427,61082,33325,39191,51128,46505,44562,48815,33411 + ], + period : 1 +}; +var expectedResult = [ + -4558,-16571,-1035,-2080,-3747,11025,17737,12216,-1666,5879,-6647,2325,1337,-15133,5680 +]; + + + + +describe('ForceIndex (Force Index', function () { + it('should calculate ForceIndex using the calculate method', function () { + assert.deepEqual(ForceIndex.calculate(inputForceIndex).map(Math.round), expectedResult, 'Wrong Results'); + }); + + it('should be able to get ForceIndex for the next bar', function () { + var forceIndex = new ForceIndex(inputForceIndex); + assert.deepEqual(forceIndex.getResult().map(Math.round), expectedResult, 'Wrong Results while getting results'); + }) + + it('should be able to get ForceIndex for the next bar using nextValue', function () { + var forceIndex = new ForceIndex({ + open : [], + high : [], + low : [], + close : [], + volume : [] + }); + + var results = []; + + inputForceIndex.close.forEach((price,index) => { + var result = forceIndex.nextValue({ + open : inputForceIndex.open[index], + high : inputForceIndex.high[index], + low : inputForceIndex.low[index], + close : inputForceIndex.close[index], + volume : inputForceIndex.volume[index] + }); + if (result != undefined) { + results.push(result) + } + }); + assert.deepEqual(results.map(Math.round), expectedResult, 'Wrong Results while getting results'); + }) +}) \ No newline at end of file diff --git a/test/volume/MFI.js b/test/volume/MFI.js new file mode 100644 index 0000000..7d8b77c --- /dev/null +++ b/test/volume/MFI.js @@ -0,0 +1,57 @@ +/** + * Created by AAravindan on 5/17/16. + */ +/** + * Created by AAravindan on 5/8/16. + */ +"use strict"; +let assert = require('assert'); +let MFI = require('../../lib/volume/MFI').MFI; + +let input = { + high : [24.61,24.69,24.99,25.36,25.19,25.17,25.00,24.97,25.08,25.26,25.21,25.37,25.61,25.58,25.46,25.33,25.09,25.03,24.91,24.89,25.13], + low : [24.64,24.69,24.99,25.36,25.19,25.17,25.01,24.96,25.08,25.25,25.21,25.37,25.61,25.58,25.46,25.33,25.09,25.03,24.91,24.89,25.13], + close : [24.63,24.69,24.99,25.36,25.19,25.17,25.02,24.95,25.08,25.24,25.21,25.37,25.61,25.58,25.46,25.33,25.09,25.03,24.91,24.89,25.13], +volume : [18730,12272,24691,18358,22964,15919,16067,16568,16019,9774,22573,12987,10907,5799,7395,5818,7165,5673,5625,5023,7457], + period : 14 +} + +let expectResult = [45.11,36.27,28.40,31.53,33.87,41.30] + +describe('MFI (Accumulation Distribution line)', function() { + it('should calculate MFI using the calculate method', function() { + assert.deepEqual(MFI.calculate(input), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate MFI by using getResult', function() { + let mfi = new MFI(input); + assert.deepEqual(mfi.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to get MFI for the next bar using nextValue', function() { + let mfi = new MFI({ high : [], low:[], close:[], volume : [], period:14}); + let results = []; + input.close.forEach(function(close,index) { + let result = mfi.nextValue({ + close: input.close[index], + high: input.high[index], + low: input.low[index], + volume: input.volume[index] + }); + if(result !== undefined){ + results.push(parseFloat(result.toFixed(2))); + } + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }) + + it('should be able to calculate MFI for reversed input by using calculate method', function() { + let myInput = Object.assign({}, input); + myInput.reversedInput = true; + myInput.high.reverse(); + myInput.low.reverse(); + myInput.close.reverse(); + myInput.volume.reverse(); + assert.deepEqual(MFI.calculate(myInput), expectResult.slice().reverse(), 'Wrong Results while calculating next bar'); + }); +}) \ No newline at end of file diff --git a/test/volume/OBV.js b/test/volume/OBV.js new file mode 100644 index 0000000..1a0a723 --- /dev/null +++ b/test/volume/OBV.js @@ -0,0 +1,51 @@ +/** + * Created by AAravindan on 5/17/16. + */ +/** + * Created by AAravindan on 5/8/16. + */ +"use strict"; +let assert = require('assert'); +let OBV = require('../../lib/volume/OBV').OBV; + +let input = { + close : [53.26,53.30,53.32,53.72,54.19,53.92,54.65,54.60,54.21,54.53,53.79,53.66,53.56,53.57,53.94,53.27], + volume : [88888,8200,8100,8300,8900,9200,13300,10300,9900,10100,11300,12600,10700,11500,23800,14600] +} + +let expectResult = [8200,16300,24600,33500,24300,37600,27300,17400,27500,16200,3600,-7100,4400,28200,13600] + +describe('OBV (Accumulation Distribution line)', function() { + it('should calculate OBV using the calculate method', function() { + assert.deepEqual(OBV.calculate(input), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate OBV by using getResult', function() { + let obv = new OBV(input); + assert.deepEqual(obv.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + + it('should be able to get OBV for the next bar using nextValue', function() { + let obv = new OBV({volume:[], close:[]}); + let results = []; + input.close.forEach(function(close,index) { + let result = obv.nextValue({ + close: input.close[index], + volume: input.volume[index] + }); + if(result !== undefined){ + results.push(parseFloat(result.toFixed(2))); + } + }); + assert.deepEqual(results, expectResult, 'Wrong Results while getting results'); + }) + + it('should be able to calculate OBV for reversed input by using calculate method', function() { + let myInput = Object.assign({}, input); + myInput.reversedInput = true; + myInput.close.reverse(); + myInput.volume.reverse(); + assert.deepEqual(OBV.calculate(myInput), expectResult.slice().reverse(), 'Wrong Results while calculating next bar'); + }); + +}) \ No newline at end of file diff --git a/test/volume/VWAP.js b/test/volume/VWAP.js new file mode 100644 index 0000000..cc0eba3 --- /dev/null +++ b/test/volume/VWAP.js @@ -0,0 +1,64 @@ +"use strict" +var VWAP = require('../../lib/volume/VWAP').VWAP; +var assert = require("assert"); + +var inputVWAP = { + open : [ + + ], + high : [ + 127.36,127.31,127.21,127.15,127.08,127.19,127.09,127.08,127.18,127.16,127.31,127.35,127.34,127.29,127.36 + ], + low : [ + 126.99,127.10,127.11,126.93,126.98,126.99,126.82,126.95,127.05,127.05,127.08,127.20,127.25,127.17,127.25 + ], + close : [ + 127.28,127.11,127.15,127.04,126.98,127.07,126.93,127.05,127.11,127.15,127.30,127.28,127.28,127.29,127.25 + ], + volume : [ + 89329,16137,23945,20679,27252,20915,17372,17600,13896,6700,13848,9925,5540,10803,19400 + ], +}; +var expectedResult = [ + 127.21,127.20,127.20,127.17,127.15,127.14,127.13,127.12,127.12,127.12,127.12,127.13,127.13,127.14,127.15 +]; + + + + +describe('VWAP (Commodity Channel Index', function () { + it('should calculate VWAP using the calculate method', function () { + assert.deepEqual(VWAP.calculate(inputVWAP).map((val)=>val.toFixed(2)), expectedResult, 'Wrong Results'); + }); + + it('should be able to get VWAP for the next bar', function () { + var vwap = new VWAP(inputVWAP); + assert.deepEqual(vwap.getResult().map((val)=>val.toFixed(2)), expectedResult, 'Wrong Results while getting results'); + }) + + it('should be able to get VWAP for the next bar using nextValue', function () { + var vwap = new VWAP({ + open : [], + high : [], + low : [], + close : [], + volume : [] + }); + + var results = []; + + inputVWAP.close.forEach((price,index) => { + var result = vwap.nextValue({ + open : inputVWAP.open[index], + high : inputVWAP.high[index], + low : inputVWAP.low[index], + close : inputVWAP.close[index], + volume : inputVWAP.volume[index] + }); + if (result != undefined) { + results.push(result) + } + }); + assert.deepEqual(results.map((val)=>val.toFixed(2)), expectedResult, 'Wrong Results while getting results'); + }) +}) \ No newline at end of file diff --git a/test/volume/VolumeProfile.js b/test/volume/VolumeProfile.js new file mode 100644 index 0000000..d0e8045 --- /dev/null +++ b/test/volume/VolumeProfile.js @@ -0,0 +1,145 @@ + +"use strict"; +let assert = require('assert'); +let VolumeProfile = require('../../lib/volume/VolumeProfile').VolumeProfile; +let priceFallsBetweenBarRange = require('../../lib/volume/VolumeProfile').priceFallsBetweenBarRange; + +let input = { + high : [24.63,24.69,24.99,25.36,25.19,25.17,25.01,24.96,25.08,25.25,25.21,25.37,25.61,25.58,25.46,25.33,25.09,25.03,24.91,24.89,25.13], + open : [24.63,24.69,24.99,25.36,25.19,25.17,25.01,24.96,25.08,25.25,25.21,25.37,25.61,25.58,25.46,25.33,25.09,25.03,24.91,24.89,25.13], + low : [24.63,23.69,24.99,25.36,25.19,25.17,25.01,24.96,25.08,25.25,25.21,25.37,25.61,25.58,25.46,25.33,25.09,25.03,24.91,24.89,25.13], + close : [24.63,23.69,24.99,25.36,25.19,25.17,25.01,24.96,25.08,25.25,25.21,25.37,25.61,25.58,25.46,25.33,25.09,25.03,24.91,24.89,25.13], + volume : [18730,12272,24691,18358,22964,15919,16067,16568,16019,9774,22573,12987,10907,5799,7395,5818,7165,5673,5625,5023,7457], + noOfBars : 14 +} + +let expectResult = +[ + { + "bearishVolume": 12272, + "bullishVolume": 0, + "rangeEnd": 23.82714285714286, + "rangeStart": 23.69, + "totalVolume": 12272 + }, + { + "bearishVolume": 12272, + "bullishVolume": 0, + "rangeEnd": 23.964285714285715, + "rangeStart": 23.82714285714286, + "totalVolume": 12272 + }, + { + "bearishVolume": 12272, + "bullishVolume": 0, + "rangeEnd": 24.10142857142857, + "rangeStart": 23.964285714285715, + "totalVolume": 12272 + }, + { + "bearishVolume": 12272, + "bullishVolume": 0, + "rangeEnd": 24.238571428571426, + "rangeStart": 24.10142857142857, + "totalVolume": 12272 + }, + { + "bearishVolume": 12272, + "bullishVolume": 0, + "rangeEnd": 24.37571428571428, + "rangeStart": 24.238571428571426, + "totalVolume": 12272 + }, + { + "bearishVolume": 12272, + "bullishVolume": 0, + "rangeEnd": 24.512857142857136, + "rangeStart": 24.37571428571428, + "totalVolume": 12272 + }, + { + "bearishVolume": 12272, + "bullishVolume": 18730, + "rangeEnd": 24.64999999999999, + "rangeStart": 24.512857142857136, + "totalVolume": 31002 + }, + { + "bearishVolume": 12272, + "bullishVolume": 0, + "rangeEnd": 24.787142857142847, + "rangeStart": 24.64999999999999, + "totalVolume": 12272 + }, + { + "bearishVolume": 0, + "bullishVolume": 10648, + "rangeEnd": 24.924285714285702, + "rangeStart": 24.787142857142847, + "totalVolume": 10648 + }, + { + "bearishVolume": 0, + "bullishVolume": 62999, + "rangeEnd": 25.061428571428557, + "rangeStart": 24.924285714285702, + "totalVolume": 62999 + }, + { + "bearishVolume": 0, + "bullishVolume": 69524, + "rangeEnd": 25.198571428571412, + "rangeStart": 25.061428571428557, + "totalVolume": 69524 + }, + { + "bearishVolume": 0, + "bullishVolume": 38165, + "rangeEnd": 25.335714285714268, + "rangeStart": 25.198571428571412, + "totalVolume": 38165 + }, + { + "bearishVolume": 0, + "bullishVolume": 38740, + "rangeEnd": 25.472857142857123, + "rangeStart": 25.335714285714268, + "totalVolume": 38740 + }, + { + "bearishVolume": 0, + "bullishVolume": 5799, + "rangeEnd": 25.609999999999978, + "rangeStart": 25.472857142857123, + "totalVolume": 5799 + } +] + +describe('VolumeProfile', function() { + + it('Price falls between range should work for different range:', ()=> { + assert.deepEqual(priceFallsBetweenBarRange(1,1,1,1), true); + + assert.deepEqual(priceFallsBetweenBarRange(1,1,2,2), false); + assert.deepEqual(priceFallsBetweenBarRange(1,2,2,3), true); + assert.deepEqual(priceFallsBetweenBarRange(1,2,1,2), true); + assert.deepEqual(priceFallsBetweenBarRange(1,2,3,4), false); + assert.deepEqual(priceFallsBetweenBarRange(2,3,1,4), true); + + assert.deepEqual(priceFallsBetweenBarRange(2,2,1,1), false); + assert.deepEqual(priceFallsBetweenBarRange(2,3,1,2), true); + assert.deepEqual(priceFallsBetweenBarRange(1,2,1,2), true); + assert.deepEqual(priceFallsBetweenBarRange(3,4,1,2), false); + assert.deepEqual(priceFallsBetweenBarRange(1,4,2,3), true); + }) + + it('should calculate VolumeProfile using the calculate method', function() { + assert.deepEqual(VolumeProfile.calculate(input), expectResult, 'Wrong Results'); + }); + + it('should be able to calculate VolumeProfile by using getResult', function() { + let volumeprofile = new VolumeProfile(input); + assert.deepEqual(volumeprofile.getResult(), expectResult, 'Wrong Results while calculating next bar'); + }); + +})