ohlcv-indicators 1.3.9 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ohlcv-indicators.min.js +1 -1
- package/index.js +75 -91
- package/package.json +1 -1
- package/src/moving-averages/bollingerBands.js +25 -12
- package/src/moving-averages/ema.js +14 -5
- package/src/moving-averages/macd.js +9 -7
- package/src/moving-averages/relativeVolume.js +2 -2
- package/src/moving-averages/sma.js +13 -5
- package/src/oscillators/rsi.js +10 -7
- package/src/studies/candles.js +253 -156
- package/src/studies/findCrosses.js +27 -16
- package/src/studies/volumeProfile.js +4 -4
- package/src/studies/chart.js +0 -466
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
|
|
2
|
-
import {BollingerBands, Big} from 'trading-signals';
|
|
2
|
+
import {BollingerBands, FasterBollingerBands, Big} from 'trading-signals';
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
export const bollingerBands = (
|
|
5
|
+
export const bollingerBands = (main, size, times) => {
|
|
6
|
+
|
|
7
|
+
const {verticalOhlcv, precision} = main
|
|
8
|
+
const {close} = verticalOhlcv
|
|
6
9
|
|
|
7
10
|
const sliceData = close.slice(-(size*3))
|
|
8
|
-
const col = getBollingerBands(sliceData, size, times)
|
|
11
|
+
const col = getBollingerBands(sliceData, size, times, precision)
|
|
9
12
|
return col
|
|
10
13
|
}
|
|
11
14
|
|
|
12
|
-
export const getBollingerBands = (data, size = 20, times = 2) => {
|
|
15
|
+
export const getBollingerBands = (data, size = 20, times = 2, precision) => {
|
|
13
16
|
|
|
14
17
|
const dataLength = data.length
|
|
15
18
|
const output = {bollinger_bands_upper: [], bollinger_bands_middle: [], bollinger_bands_lower: []}
|
|
16
|
-
const instance = new BollingerBands(size, times)
|
|
19
|
+
const instance = (precision) ? new BollingerBands(size, times) : new FasterBollingerBands(size, times)
|
|
17
20
|
|
|
18
21
|
for(let x = 0; x < dataLength; x++) {
|
|
19
22
|
let obj = {}
|
|
@@ -35,25 +38,35 @@ export const getBollingerBands = (data, size = 20, times = 2) => {
|
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
const {bollinger_bands_upper, bollinger_bands_middle, bollinger_bands_lower} = output
|
|
38
|
-
const bollinger_bands_range = bollingerBandsRange(data, {bollinger_bands_upper, bollinger_bands_lower})
|
|
41
|
+
const bollinger_bands_range = bollingerBandsRange(data, {bollinger_bands_upper, bollinger_bands_lower}, precision)
|
|
39
42
|
|
|
40
43
|
return {bollinger_bands_upper, bollinger_bands_middle, bollinger_bands_lower, bollinger_bands_range}
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
|
|
44
47
|
|
|
45
|
-
const bollingerBandsRange = (data, bollingerBands) => {
|
|
48
|
+
const bollingerBandsRange = (data, bollingerBands, precision) => {
|
|
46
49
|
const { bollinger_bands_upper, bollinger_bands_lower } = bollingerBands;
|
|
50
|
+
|
|
47
51
|
const output = new Array(data.length);
|
|
48
52
|
|
|
49
53
|
for (let i = 0; i < data.length; i++) {
|
|
50
54
|
if (bollinger_bands_upper[i] != null && bollinger_bands_lower[i] != null && data[i] != null) {
|
|
51
|
-
const upperBig = new Big(bollinger_bands_upper[i]);
|
|
52
|
-
const lowerBig = new Big(bollinger_bands_lower[i]);
|
|
53
|
-
const dataBig = new Big(data[i]);
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
if(precision)
|
|
57
|
+
{
|
|
58
|
+
const upperBig = new Big(bollinger_bands_upper[i])
|
|
59
|
+
const lowerBig = new Big(bollinger_bands_lower[i])
|
|
60
|
+
const dataBig = new Big(data[i])
|
|
61
|
+
const range = upperBig.minus(lowerBig);
|
|
62
|
+
output[i] = dataBig.minus(lowerBig).div(range).times(100)
|
|
63
|
+
}
|
|
64
|
+
else
|
|
65
|
+
{
|
|
66
|
+
const range = bollinger_bands_upper[i] - bollinger_bands_lower[i]
|
|
67
|
+
output[i] = ((data[i] - bollinger_bands_lower[i]) / range) * 100
|
|
68
|
+
}
|
|
69
|
+
|
|
57
70
|
} else {
|
|
58
71
|
output[i] = null;
|
|
59
72
|
}
|
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
|
|
2
|
-
import {EMA} from 'trading-signals'
|
|
2
|
+
import {EMA, FasterEMA} from 'trading-signals'
|
|
3
3
|
|
|
4
|
-
export const ema = (
|
|
4
|
+
export const ema = (main, size) => {
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const {verticalOhlcv, precision} = main
|
|
7
|
+
const {close} = verticalOhlcv
|
|
8
|
+
|
|
9
|
+
const col = getEMA(close, size, precision)
|
|
7
10
|
|
|
8
11
|
return {
|
|
9
12
|
[`ema_${size}`]: col
|
|
10
13
|
}
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
export const getEMA = (data, size) => {
|
|
16
|
+
export const getEMA = (data, size, precision) => {
|
|
14
17
|
|
|
18
|
+
|
|
19
|
+
if(typeof precision === 'undefined' || typeof data === 'undefined' || typeof size === 'undefined')
|
|
20
|
+
{
|
|
21
|
+
throw Error('undefined param in getEMA')
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
const output = []
|
|
16
|
-
const instance = new EMA(size)
|
|
25
|
+
const instance = (precision) ? new EMA(size) : new FasterEMA(size)
|
|
17
26
|
const dataLength = data.length
|
|
18
27
|
|
|
19
28
|
for(let x = 0; x < dataLength; x++)
|
|
@@ -1,28 +1,30 @@
|
|
|
1
1
|
import { findCrosses } from "../studies/findCrosses.js";
|
|
2
|
-
import {EMA, MACD} from 'trading-signals';
|
|
2
|
+
import {EMA, MACD, FasterEMA, FasterMACD} from 'trading-signals';
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
export const macd = (
|
|
5
|
+
export const macd = (main, fastLine, slowLine, signalLine) => {
|
|
6
6
|
|
|
7
|
+
const {verticalOhlcv, precision} = main
|
|
8
|
+
const {close} = verticalOhlcv
|
|
7
9
|
const maxSize = Math.max(fastLine, slowLine, signalLine)
|
|
8
10
|
const sliceData = close.slice(-(maxSize*3))
|
|
9
|
-
const col = getMACD(sliceData, fastLine, slowLine, signalLine)
|
|
11
|
+
const col = getMACD(sliceData, fastLine, slowLine, signalLine, precision)
|
|
10
12
|
|
|
11
13
|
return col
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
export const getMACD = (data, fastLine = 12, slowLine = 26, signalLine = 9) => {
|
|
16
|
+
export const getMACD = (data, fastLine = 12, slowLine = 26, signalLine = 9, precision) => {
|
|
15
17
|
|
|
16
18
|
const diff = []
|
|
17
19
|
const dea = []
|
|
18
20
|
const histogram = []
|
|
19
21
|
|
|
20
|
-
const instance = new MACD({
|
|
22
|
+
const instance = (precision) ? new MACD({
|
|
21
23
|
indicator: EMA,
|
|
22
24
|
shortInterval: fastLine,
|
|
23
25
|
longInterval: slowLine,
|
|
24
26
|
signalInterval: signalLine
|
|
25
|
-
})
|
|
27
|
+
}) : new FasterMACD(new FasterEMA(fastLine), new FasterEMA(slowLine), new FasterEMA(signalLine))
|
|
26
28
|
|
|
27
29
|
const dataLength = data.length
|
|
28
30
|
|
|
@@ -45,7 +47,7 @@ export const getMACD = (data, fastLine = 12, slowLine = 26, signalLine = 9) => {
|
|
|
45
47
|
histogram.push(obj.histogram)
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
const x = findCrosses(diff, dea)
|
|
50
|
+
const x = findCrosses(diff, dea, precision)
|
|
49
51
|
|
|
50
52
|
return {
|
|
51
53
|
macd_diff: diff,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getSMA } from './sma.js'
|
|
2
2
|
|
|
3
3
|
export const RelativeVolume = (main, size) => {
|
|
4
|
-
const {
|
|
5
|
-
const data =
|
|
4
|
+
const {verticalOhlcv} = main
|
|
5
|
+
const data = verticalOhlcv['volume']
|
|
6
6
|
const relVolume = getRelativeVolume(main.BigNumber, data, size)
|
|
7
7
|
main.addColumn(`relative_volume_${size}`, relVolume)
|
|
8
8
|
}
|
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
|
|
2
|
-
import {SMA} from 'trading-signals';
|
|
2
|
+
import {SMA, FasterSMA} from 'trading-signals';
|
|
3
3
|
|
|
4
|
-
export const sma = (
|
|
4
|
+
export const sma = (main, size) => {
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const {verticalOhlcv, precision} = main
|
|
7
|
+
const {close} = verticalOhlcv
|
|
8
|
+
|
|
9
|
+
const col = getSMA(close, size, precision)
|
|
7
10
|
|
|
8
11
|
return {
|
|
9
12
|
[`sma_${size}`]: col
|
|
10
13
|
}
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
export const getSMA = (data, size) => {
|
|
16
|
+
export const getSMA = (data, size, precision) => {
|
|
14
17
|
|
|
18
|
+
if(typeof precision === 'undefined' || typeof data === 'undefined' || typeof size === 'undefined')
|
|
19
|
+
{
|
|
20
|
+
throw Error('undefined param in getSMA')
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
const output = []
|
|
16
|
-
const instance = new SMA(size)
|
|
24
|
+
const instance = (precision) ? new SMA(size) : new FasterSMA(size)
|
|
17
25
|
const dataLength = data.length
|
|
18
26
|
|
|
19
27
|
for(let x = 0; x < dataLength; x++)
|
package/src/oscillators/rsi.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { getSMA } from "../moving-averages/sma.js";
|
|
2
2
|
import { getEMA } from "../moving-averages/ema.js";
|
|
3
3
|
import { findCrosses } from "../studies/findCrosses.js";
|
|
4
|
-
import {RSI} from 'trading-signals';
|
|
4
|
+
import {RSI, FasterRSI} from 'trading-signals';
|
|
5
5
|
|
|
6
6
|
const ma = {getSMA, getEMA}
|
|
7
7
|
|
|
8
|
-
export const rsi = (
|
|
8
|
+
export const rsi = (main, period, movingAverage, movingAveragePeriod) => {
|
|
9
|
+
|
|
10
|
+
const {verticalOhlcv, precision} = main
|
|
11
|
+
const {close} = verticalOhlcv
|
|
9
12
|
|
|
10
13
|
const sliceData = close.slice(-(period*3))
|
|
11
14
|
|
|
@@ -14,12 +17,12 @@ export const rsi = (close, period, movingAverage, movingAveragePeriod) => {
|
|
|
14
17
|
movingAveragePeriod = period
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
const col = getRSI(sliceData, period, movingAverage, movingAveragePeriod)
|
|
20
|
+
const col = getRSI(sliceData, period, movingAverage, movingAveragePeriod, precision)
|
|
18
21
|
|
|
19
22
|
return col
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
export const getRSI = (data, period = 14, movingAverage = 'SMA', movingAveragePeriod = 14) => {
|
|
25
|
+
export const getRSI = (data, period = 14, movingAverage = 'SMA', movingAveragePeriod = 14, precision) => {
|
|
23
26
|
|
|
24
27
|
|
|
25
28
|
if (data.length < period) {
|
|
@@ -29,7 +32,7 @@ export const getRSI = (data, period = 14, movingAverage = 'SMA', movingAveragePe
|
|
|
29
32
|
let rsi = []
|
|
30
33
|
period = parseInt(period)
|
|
31
34
|
movingAveragePeriod = parseInt(movingAveragePeriod)
|
|
32
|
-
const instance = new RSI(period)
|
|
35
|
+
const instance = (precision) ? new RSI(period) : new FasterRSI(period)
|
|
33
36
|
const dataLength = data.length
|
|
34
37
|
|
|
35
38
|
for(let x = 0; x < dataLength; x++)
|
|
@@ -57,9 +60,9 @@ export const getRSI = (data, period = 14, movingAverage = 'SMA', movingAveragePe
|
|
|
57
60
|
{
|
|
58
61
|
if(ma.hasOwnProperty(`get${movingAverage}`))
|
|
59
62
|
{
|
|
60
|
-
const rsi_smoothed = ma[`get${movingAverage}`](rsi, movingAveragePeriod)
|
|
63
|
+
const rsi_smoothed = ma[`get${movingAverage}`](rsi, movingAveragePeriod, precision)
|
|
61
64
|
output[`rsi_${movingAverage}_${movingAveragePeriod}`] = rsi_smoothed
|
|
62
|
-
output[`rsi_${period}_x_rsi_${movingAverage}_${movingAveragePeriod}`] = findCrosses(rsi, rsi_smoothed)
|
|
65
|
+
output[`rsi_${period}_x_rsi_${movingAverage}_${movingAveragePeriod}`] = findCrosses(rsi, rsi_smoothed, precision)
|
|
63
66
|
}
|
|
64
67
|
}
|
|
65
68
|
|
package/src/studies/candles.js
CHANGED
|
@@ -1,171 +1,268 @@
|
|
|
1
|
-
export const candles =
|
|
1
|
+
export const candles = main => {
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const {inputOhlcv} = main
|
|
4
|
+
const cols = getCandlestickPattern(inputOhlcv);
|
|
4
5
|
return cols;
|
|
5
6
|
}
|
|
6
7
|
|
|
7
|
-
export const getCandlestickPattern =
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
export const getCandlestickPattern = inputOhlcv => {
|
|
9
|
+
const size = 2; //remove slice for backtesting
|
|
10
|
+
inputOhlcv = inputOhlcv.slice(-size);
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
12
|
+
const candle_pattern = [];
|
|
13
|
+
const candle_name = [];
|
|
14
|
+
const candle_score = [];
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
{
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
condition: (prev2, prev, curr) => prev2.close > prev2.open && prev.close < prev.open && curr.close > prev.open,
|
|
43
|
-
score: 3
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: 'Piercing Line',
|
|
47
|
-
condition: (prev, curr) => curr.close > prev.close && curr.open < prev.open && curr.close > ((prev.open + prev.close) / 2),
|
|
48
|
-
score: 1
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
name: 'Belt Hold',
|
|
52
|
-
condition: (prev, curr) => curr.open > prev.open && isCloseTo(curr.close, curr.high) && isCloseTo(prev.close, prev.low),
|
|
53
|
-
score: 1
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
name: 'Harami',
|
|
57
|
-
condition: (prev, curr) => curr.open > prev.close && curr.close < prev.open && curr.open < prev.open && curr.close > prev.close,
|
|
58
|
-
score: 1
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
name: 'Doji',
|
|
62
|
-
condition: (prev, curr) => isCloseTo(curr.close, curr.open) && (curr.high - curr.low) > Math.abs(curr.close - curr.open),
|
|
63
|
-
score: 0
|
|
64
|
-
}
|
|
65
|
-
],
|
|
66
|
-
bearish: [
|
|
67
|
-
{
|
|
68
|
-
name: 'Marubozu',
|
|
69
|
-
condition: (prev, curr) => isCloseTo(curr.open, curr.high) && isCloseTo(curr.close, curr.low),
|
|
70
|
-
score: -2
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
name: 'Engulfing',
|
|
74
|
-
condition: (prev, curr) => curr.open > prev.close && curr.close < prev.open && curr.high > prev.high && curr.low < prev.low,
|
|
75
|
-
score: -2
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
name: 'Kicker',
|
|
79
|
-
condition: (prev, curr) => curr.open > prev.open && curr.close < curr.open && prev.close > prev.open,
|
|
80
|
-
score: -2
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
name: 'Evening Star',
|
|
84
|
-
condition: (prev2, prev, curr) => prev2.close < prev2.open && prev.close > prev.open && curr.close < prev.open,
|
|
85
|
-
score: -3
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
name: 'Dark Cloud Cover',
|
|
89
|
-
condition: (prev, curr) => curr.close < prev.close && curr.open > prev.open && curr.close < ((prev.open + prev.close) / 2),
|
|
90
|
-
score: -1
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
name: 'Belt Hold',
|
|
94
|
-
condition: (prev, curr) => curr.open < prev.open && isCloseTo(curr.close, curr.low) && isCloseTo(prev.close, prev.high),
|
|
95
|
-
score: -1
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
name: 'Harami',
|
|
99
|
-
condition: (prev, curr) => curr.open < prev.close && curr.close > prev.open && curr.open > prev.open && curr.close < prev.close,
|
|
100
|
-
score: -1
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
name: 'Doji',
|
|
104
|
-
condition: (prev, curr) => isCloseTo(curr.close, curr.open) && (curr.high - curr.low) > Math.abs(curr.close - curr.open),
|
|
105
|
-
score: 0
|
|
106
|
-
}
|
|
107
|
-
]
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
// Helper function to check priority of patterns
|
|
111
|
-
const priorityCheck = (prev, curr, patterns, prev2 = null) => {
|
|
112
|
-
for (const { name, condition, score } of patterns) {
|
|
113
|
-
if (prev2) {
|
|
114
|
-
if (condition(prev2, prev, curr)) {
|
|
115
|
-
return { name, score };
|
|
16
|
+
for (let x = 1; x < size; x++) {
|
|
17
|
+
const curr = inputOhlcv[x];
|
|
18
|
+
const prev = inputOhlcv[x - 1];
|
|
19
|
+
|
|
20
|
+
let pattern = 'Neutral';
|
|
21
|
+
let name = 'None';
|
|
22
|
+
let score = 0;
|
|
23
|
+
|
|
24
|
+
for (const patternObj of candlePatterns) {
|
|
25
|
+
const { isSingleCandle, candle_name: patternName, candle_score: patternScore } = patternObj;
|
|
26
|
+
const isMatch = isSingleCandle
|
|
27
|
+
? candleFunctions[patternName](curr)
|
|
28
|
+
: candleFunctions[patternName](prev, curr);
|
|
29
|
+
|
|
30
|
+
if (isMatch) {
|
|
31
|
+
if (score === 0) {
|
|
32
|
+
pattern = patternScore === 1 ? 'Bullish' : 'Bearish';
|
|
33
|
+
name = patternName;
|
|
34
|
+
score = patternScore;
|
|
35
|
+
} else if (score !== patternScore) {
|
|
36
|
+
pattern = 'Neutral';
|
|
37
|
+
name = 'None';
|
|
38
|
+
score = 0;
|
|
39
|
+
break;
|
|
40
|
+
} else {
|
|
41
|
+
name += `,${patternName}`;
|
|
116
42
|
}
|
|
117
|
-
} else if (condition(prev, curr)) {
|
|
118
|
-
return { name, score };
|
|
119
43
|
}
|
|
120
44
|
}
|
|
121
|
-
return null;
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
// Arrays to store pattern results
|
|
125
|
-
const patternArray = ['Neutral'];
|
|
126
|
-
const nameArray = ['No Pattern'];
|
|
127
|
-
const scoreArray = [0];
|
|
128
|
-
|
|
129
|
-
// Loop through each data point starting from the second one
|
|
130
|
-
for (let i = 2; i < length; i++) {
|
|
131
|
-
const prev = {
|
|
132
|
-
open: open[i - 1],
|
|
133
|
-
high: high[i - 1],
|
|
134
|
-
low: low[i - 1],
|
|
135
|
-
close: close[i - 1]
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const curr = {
|
|
139
|
-
open: open[i],
|
|
140
|
-
high: high[i],
|
|
141
|
-
low: low[i],
|
|
142
|
-
close: close[i]
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
const prev2 = {
|
|
146
|
-
open: open[i - 2],
|
|
147
|
-
high: high[i - 2],
|
|
148
|
-
low: low[i - 2],
|
|
149
|
-
close: close[i - 2]
|
|
150
|
-
}
|
|
151
45
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (result) {
|
|
156
|
-
patternArray.push(result.score > 0 ? 'Bullish' : 'Bearish');
|
|
157
|
-
nameArray.push(result.name);
|
|
158
|
-
scoreArray.push(result.score);
|
|
159
|
-
} else {
|
|
160
|
-
patternArray.push('Neutral');
|
|
161
|
-
nameArray.push('No Pattern');
|
|
162
|
-
scoreArray.push(0);
|
|
163
|
-
}
|
|
46
|
+
candle_pattern.push(pattern);
|
|
47
|
+
candle_name.push(name);
|
|
48
|
+
candle_score.push(score);
|
|
164
49
|
}
|
|
165
50
|
|
|
166
51
|
return {
|
|
167
|
-
candle_pattern
|
|
168
|
-
candle_name
|
|
169
|
-
candle_score
|
|
52
|
+
candle_pattern,
|
|
53
|
+
candle_name,
|
|
54
|
+
candle_score
|
|
170
55
|
}
|
|
171
56
|
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
const candlePatterns = [
|
|
62
|
+
{
|
|
63
|
+
candle_name: "isHammer",
|
|
64
|
+
candle_score: 1,
|
|
65
|
+
isSingleCandle: true
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
candle_name: "isInvertedHammer",
|
|
69
|
+
candle_score: 1,
|
|
70
|
+
isSingleCandle: true
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
candle_name: "isBullishHammer",
|
|
74
|
+
candle_score: 1,
|
|
75
|
+
isSingleCandle: true
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
candle_name: "isBearishHammer",
|
|
79
|
+
candle_score: -1,
|
|
80
|
+
isSingleCandle: true
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
candle_name: "isBullishInvertedHammer",
|
|
84
|
+
candle_score: 1,
|
|
85
|
+
isSingleCandle: true
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
candle_name: "isBearishInvertedHammer",
|
|
89
|
+
candle_score: -1,
|
|
90
|
+
isSingleCandle: true
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
candle_name: "isHangingMan",
|
|
94
|
+
candle_score: -1,
|
|
95
|
+
isSingleCandle: false
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
candle_name: "isShootingStar",
|
|
99
|
+
candle_score: -1,
|
|
100
|
+
isSingleCandle: false
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
candle_name: "isBullishEngulfing",
|
|
104
|
+
candle_score: 1,
|
|
105
|
+
isSingleCandle: false
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
candle_name: "isBearishEngulfing",
|
|
109
|
+
candle_score: -1,
|
|
110
|
+
isSingleCandle: false
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
candle_name: "isBullishHarami",
|
|
114
|
+
candle_score: 1,
|
|
115
|
+
isSingleCandle: false
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
candle_name: "isBearishHarami",
|
|
119
|
+
candle_score: -1
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
candle_name: "isBullishKicker",
|
|
123
|
+
candle_score: 1,
|
|
124
|
+
isSingleCandle: false
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
candle_name: "isBearishKicker",
|
|
128
|
+
candle_score: -1,
|
|
129
|
+
isSingleCandle: false
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
const bodyLen = candlestick => Math.abs(candlestick.open - candlestick.close);
|
|
135
|
+
|
|
136
|
+
const wickLen = candlestick => candlestick.high - Math.max(candlestick.open, candlestick.close);
|
|
137
|
+
|
|
138
|
+
const tailLen = candlestick => Math.min(candlestick.open, candlestick.close) - candlestick.low;
|
|
139
|
+
|
|
140
|
+
const bodyEnds = candlestick => candlestick.open <= candlestick.close ?
|
|
141
|
+
{ bottom: candlestick.open, top: candlestick.close } :
|
|
142
|
+
{ bottom: candlestick.close, top: candlestick.open };
|
|
143
|
+
|
|
144
|
+
const isBullish = candlestick => candlestick.open < candlestick.close;
|
|
145
|
+
|
|
146
|
+
const isBearish = candlestick => candlestick.open > candlestick.close;
|
|
147
|
+
|
|
148
|
+
const isEngulfed = (previous, current) =>
|
|
149
|
+
bodyEnds(previous).top <= bodyEnds(current).top &&
|
|
150
|
+
bodyEnds(previous).bottom >= bodyEnds(current).bottom;
|
|
151
|
+
|
|
152
|
+
const hasGapUp = (previous, current) =>
|
|
153
|
+
bodyEnds(previous).top < bodyEnds(current).bottom;
|
|
154
|
+
|
|
155
|
+
const hasGapDown = (previous, current) =>
|
|
156
|
+
bodyEnds(previous).bottom > bodyEnds(current).top;
|
|
157
|
+
|
|
158
|
+
const isHammer = candlestick =>
|
|
159
|
+
tailLen(candlestick) > (bodyLen(candlestick) * 2) &&
|
|
160
|
+
wickLen(candlestick) < bodyLen(candlestick);
|
|
161
|
+
|
|
162
|
+
const isInvertedHammer = candlestick =>
|
|
163
|
+
wickLen(candlestick) > (bodyLen(candlestick) * 2) &&
|
|
164
|
+
tailLen(candlestick) < bodyLen(candlestick);
|
|
165
|
+
|
|
166
|
+
const isBullishHammer = candlestick =>
|
|
167
|
+
isBullish(candlestick) && isHammer(candlestick);
|
|
168
|
+
|
|
169
|
+
const isBearishHammer = candlestick =>
|
|
170
|
+
isBearish(candlestick) && isHammer(candlestick);
|
|
171
|
+
|
|
172
|
+
const isBullishInvertedHammer = candlestick =>
|
|
173
|
+
isBullish(candlestick) && isInvertedHammer(candlestick);
|
|
174
|
+
|
|
175
|
+
const isBearishInvertedHammer = candlestick =>
|
|
176
|
+
isBearish(candlestick) && isInvertedHammer(candlestick);
|
|
177
|
+
|
|
178
|
+
const isHangingMan = (previous, current) =>
|
|
179
|
+
isBullish(previous) && isBearishHammer(current) && hasGapUp(previous, current);
|
|
180
|
+
|
|
181
|
+
const isShootingStar = (previous, current) =>
|
|
182
|
+
isBullish(previous) && isBearishInvertedHammer(current) && hasGapUp(previous, current);
|
|
183
|
+
|
|
184
|
+
const isBullishEngulfing = (previous, current) =>
|
|
185
|
+
isBearish(previous) && isBullish(current) && isEngulfed(previous, current);
|
|
186
|
+
|
|
187
|
+
const isBearishEngulfing = (previous, current) =>
|
|
188
|
+
isBullish(previous) && isBearish(current) && isEngulfed(previous, current);
|
|
189
|
+
|
|
190
|
+
const isBullishHarami = (previous, current) =>
|
|
191
|
+
isBearish(previous) && isBullish(current) && isEngulfed(current, previous);
|
|
192
|
+
|
|
193
|
+
const isBearishHarami = (previous, current) =>
|
|
194
|
+
isBullish(previous) && isBearish(current) && isEngulfed(current, previous);
|
|
195
|
+
|
|
196
|
+
const isBullishKicker = (previous, current) =>
|
|
197
|
+
isBearish(previous) && isBullish(current) && hasGapUp(previous, current) &&
|
|
198
|
+
!(isHammer(current) || isInvertedHammer(current));
|
|
199
|
+
|
|
200
|
+
const isBearishKicker = (previous, current) =>
|
|
201
|
+
isBullish(previous) && isBearish(current) && hasGapDown(previous, current) &&
|
|
202
|
+
!(isHammer(current) || isInvertedHammer(current));
|
|
203
|
+
|
|
204
|
+
const findPattern = (dataArray, callback) => {
|
|
205
|
+
const paramCount = callback.length;
|
|
206
|
+
const upperBound = dataArray.length - paramCount;
|
|
207
|
+
const results = [];
|
|
208
|
+
|
|
209
|
+
for (let i = 0; i <= upperBound; i++) {
|
|
210
|
+
const values = [];
|
|
211
|
+
|
|
212
|
+
for (let j = 0; j < paramCount; j++) {
|
|
213
|
+
values.push(dataArray[i + j]);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (callback(...values)) {
|
|
217
|
+
results.push(i);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return results;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const hammer = dataArray => findPattern(dataArray, isHammer);
|
|
225
|
+
|
|
226
|
+
const invertedHammer = dataArray => findPattern(dataArray, isInvertedHammer);
|
|
227
|
+
|
|
228
|
+
const bullishHammer = dataArray => findPattern(dataArray, isBullishHammer);
|
|
229
|
+
|
|
230
|
+
const bearishHammer = dataArray => findPattern(dataArray, isBearishHammer);
|
|
231
|
+
|
|
232
|
+
const bullishInvertedHammer = dataArray => findPattern(dataArray, isBullishInvertedHammer);
|
|
233
|
+
|
|
234
|
+
const bearishInvertedHammer = dataArray => findPattern(dataArray, isBearishInvertedHammer);
|
|
235
|
+
|
|
236
|
+
const hangingMan = dataArray => findPattern(dataArray, isHangingMan);
|
|
237
|
+
|
|
238
|
+
const shootingStar = dataArray => findPattern(dataArray, isShootingStar);
|
|
239
|
+
|
|
240
|
+
const bullishEngulfing = dataArray => findPattern(dataArray, isBullishEngulfing);
|
|
241
|
+
|
|
242
|
+
const bearishEngulfing = dataArray => findPattern(dataArray, isBearishEngulfing);
|
|
243
|
+
|
|
244
|
+
const bullishHarami = dataArray => findPattern(dataArray, isBullishHarami);
|
|
245
|
+
|
|
246
|
+
const bearishHarami = dataArray => findPattern(dataArray, isBearishHarami);
|
|
247
|
+
|
|
248
|
+
const bullishKicker = dataArray => findPattern(dataArray, isBullishKicker);
|
|
249
|
+
|
|
250
|
+
const bearishKicker = dataArray => findPattern(dataArray, isBearishKicker)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
const candleFunctions = {
|
|
254
|
+
isHammer,
|
|
255
|
+
isInvertedHammer,
|
|
256
|
+
isBullishHammer,
|
|
257
|
+
isBearishHammer,
|
|
258
|
+
isBullishInvertedHammer,
|
|
259
|
+
isBearishInvertedHammer,
|
|
260
|
+
isHangingMan,
|
|
261
|
+
isShootingStar,
|
|
262
|
+
isBullishEngulfing,
|
|
263
|
+
isBearishEngulfing,
|
|
264
|
+
isBullishHarami,
|
|
265
|
+
isBearishHarami,
|
|
266
|
+
isBullishKicker,
|
|
267
|
+
isBearishKicker
|
|
268
|
+
}
|