ohlcv-indicators 1.1.9 → 1.2.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/index.js +6 -0
- package/package.json +2 -3
- package/src/studies/candles.js +165 -0
package/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import {bollingerBands} from './src/moving-averages/bollingerBands.js'
|
|
|
5
5
|
import { rsi } from './src/oscillators/rsi.js'
|
|
6
6
|
import { VolumeProfile } from './src/studies/volumeProfile.js'
|
|
7
7
|
import {findCrosses} from './src/utilities.js'
|
|
8
|
+
import { candles } from './src/studies/candles.js'
|
|
8
9
|
|
|
9
10
|
export default class OHLCV_INDICATORS {
|
|
10
11
|
constructor() {
|
|
@@ -137,4 +138,9 @@ export default class OHLCV_INDICATORS {
|
|
|
137
138
|
rsi(this, period, movingAverage, movingAveragePeriod)
|
|
138
139
|
return this
|
|
139
140
|
}
|
|
141
|
+
candles()
|
|
142
|
+
{
|
|
143
|
+
candles(this)
|
|
144
|
+
return this
|
|
145
|
+
}
|
|
140
146
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ohlcv-indicators",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A JavaScript library featuring optimized trading indicators for OHLCV arrays [{open, high, low, close, value}]. This library is optimized for backtesting and trading bot development.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
"author": "https://jaimelias.com/",
|
|
12
12
|
"license": "ISC",
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@debut/indicators": "^1.3.20"
|
|
15
|
-
"bignumber.js": "^9.1.2"
|
|
14
|
+
"@debut/indicators": "^1.3.20"
|
|
16
15
|
},
|
|
17
16
|
"devDependencies": {
|
|
18
17
|
"dotenv": "^16.4.5",
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
export const candles = main => {
|
|
2
|
+
|
|
3
|
+
const {ohlcv} = main
|
|
4
|
+
|
|
5
|
+
const cols = getCandlestickPattern(ohlcv)
|
|
6
|
+
|
|
7
|
+
for(let k in cols)
|
|
8
|
+
{
|
|
9
|
+
main.addColumn(`candle_${k}`.toLowerCase(), cols[k])
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export const getCandlestickPattern = ohlcv => {
|
|
16
|
+
const { open, high, low, close } = ohlcv;
|
|
17
|
+
const length = open.length;
|
|
18
|
+
|
|
19
|
+
// Ensure input arrays are valid and of equal length
|
|
20
|
+
if (![open, high, low, close].every(o => Array.isArray(o) && o.length > 1 && o.length === length)) {
|
|
21
|
+
return { pattern: [], name: [], score: [] };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Helper function to check if two numbers are close
|
|
25
|
+
const isCloseTo = (a, b, epsilon = 0.0001) => Math.abs(a - b) < epsilon;
|
|
26
|
+
|
|
27
|
+
// Conditions for bullish and bearish patterns
|
|
28
|
+
const conditions = {
|
|
29
|
+
bullish: [
|
|
30
|
+
{
|
|
31
|
+
name: 'Marubozu',
|
|
32
|
+
condition: (prev, curr) => isCloseTo(curr.open, curr.low) && isCloseTo(curr.close, curr.high),
|
|
33
|
+
score: 3
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'Engulfing',
|
|
37
|
+
condition: (prev, curr) => curr.open < prev.close && curr.close > prev.open && curr.low < prev.low && curr.high > prev.high,
|
|
38
|
+
score: 2
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'Kicker',
|
|
42
|
+
condition: (prev, curr) => curr.open < prev.open && curr.close > curr.open && prev.close < prev.open,
|
|
43
|
+
score: 2
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'Morning Star',
|
|
47
|
+
condition: (prev, curr) => curr.close > prev.close && curr.open < prev.open && prev.close < prev.open,
|
|
48
|
+
score: 2
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'Piercing Line',
|
|
52
|
+
condition: (prev, curr) => curr.close > prev.close && curr.open < prev.open && curr.close > ((prev.open + prev.close) / 2),
|
|
53
|
+
score: 1
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'Belt Hold',
|
|
57
|
+
condition: (prev, curr) => curr.open > prev.open && isCloseTo(curr.close, curr.high) && isCloseTo(prev.close, prev.low),
|
|
58
|
+
score: 1
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'Harami',
|
|
62
|
+
condition: (prev, curr) => curr.open > prev.close && curr.close < prev.open && curr.open < prev.open && curr.close > prev.close,
|
|
63
|
+
score: 1
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'Doji',
|
|
67
|
+
condition: (prev, curr) => isCloseTo(curr.close, curr.open) && (curr.high - curr.low) > Math.abs(curr.close - curr.open),
|
|
68
|
+
score: 0
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
bearish: [
|
|
72
|
+
{
|
|
73
|
+
name: 'Marubozu',
|
|
74
|
+
condition: (prev, curr) => isCloseTo(curr.open, curr.high) && isCloseTo(curr.close, curr.low),
|
|
75
|
+
score: -3
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'Engulfing',
|
|
79
|
+
condition: (prev, curr) => curr.open > prev.close && curr.close < prev.open && curr.high > prev.high && curr.low < prev.low,
|
|
80
|
+
score: -2
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'Kicker',
|
|
84
|
+
condition: (prev, curr) => curr.open > prev.open && curr.close < curr.open && prev.close > prev.open,
|
|
85
|
+
score: -2
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: 'Evening Star',
|
|
89
|
+
condition: (prev, curr) => curr.close < prev.close && curr.open > prev.open && prev.close < prev.open,
|
|
90
|
+
score: -2
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'Dark Cloud Cover',
|
|
94
|
+
condition: (prev, curr) => curr.close < prev.close && curr.open > prev.open && curr.close < ((prev.open + prev.close) / 2),
|
|
95
|
+
score: -1
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'Belt Hold',
|
|
99
|
+
condition: (prev, curr) => curr.open < prev.open && isCloseTo(curr.close, curr.low) && isCloseTo(prev.close, prev.high),
|
|
100
|
+
score: -1
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'Harami',
|
|
104
|
+
condition: (prev, curr) => curr.open < prev.close && curr.close > prev.open && curr.open > prev.open && curr.close < prev.close,
|
|
105
|
+
score: -1
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'Doji',
|
|
109
|
+
condition: (prev, curr) => isCloseTo(curr.close, curr.open) && (curr.high - curr.low) > Math.abs(curr.close - curr.open),
|
|
110
|
+
score: 0
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Helper function to check priority of patterns
|
|
116
|
+
const priorityCheck = (prev, curr, patterns) => {
|
|
117
|
+
for (const { name, condition, score } of patterns) {
|
|
118
|
+
if (condition(prev, curr)) {
|
|
119
|
+
return { name, score };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Arrays to store pattern results
|
|
126
|
+
const patternArray = [];
|
|
127
|
+
const nameArray = [];
|
|
128
|
+
const scoreArray = [];
|
|
129
|
+
|
|
130
|
+
// Loop through each data point starting from the second one
|
|
131
|
+
for (let i = 1; i < length; i++) {
|
|
132
|
+
const prev = {
|
|
133
|
+
open: open[i - 1],
|
|
134
|
+
high: high[i - 1],
|
|
135
|
+
low: low[i - 1],
|
|
136
|
+
close: close[i - 1]
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const curr = {
|
|
140
|
+
open: open[i],
|
|
141
|
+
high: high[i],
|
|
142
|
+
low: low[i],
|
|
143
|
+
close: close[i]
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const result = priorityCheck(prev, curr, conditions.bullish) || priorityCheck(prev, curr, conditions.bearish);
|
|
147
|
+
|
|
148
|
+
if (result) {
|
|
149
|
+
patternArray.push(result.score > 0 ? 'Bullish' : 'Bearish');
|
|
150
|
+
nameArray.push(result.name);
|
|
151
|
+
scoreArray.push(result.score);
|
|
152
|
+
} else {
|
|
153
|
+
patternArray.push('');
|
|
154
|
+
nameArray.push('');
|
|
155
|
+
scoreArray.push(0);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
pattern: patternArray,
|
|
161
|
+
name: nameArray,
|
|
162
|
+
score: scoreArray
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
|