xy-scale 1.3.2 → 1.3.3
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/datasets/btc-1d.json +1 -0
- package/dist/xy-scale.min.js +1 -1
- package/package.json +4 -1
- package/src/datasets.js +1 -2
- package/src/scale.js +28 -82
- package/test/test.js +145 -55
- package/datasets/1d-spy.json +0 -1
package/dist/xy-scale.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var XY_Scale;(()=>{"use strict";var e={d:(
|
|
1
|
+
var XY_Scale;(()=>{"use strict";var e={d:(t,n)=>{for(var a in n)e.o(n,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:n[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{arrayToTimesteps:()=>i,parseProductionX:()=>s,parseTrainingXY:()=>o});const n=({arrObj:e,repeat:t={},minmaxRange:n=[0,1],groups:o={},customMinMaxRanges:s=null})=>{const i=[...e],u=i.length,l=i[0],c="object"==typeof s&&null!==s;if(0===u)return{scaledOutput:[],scaledConfig:{}};const p=Object.keys(l),m=p.map((e=>t.hasOwnProperty(e)?Math.max(t[e],1):1)),f=m.reduce(((e,t)=>e+t),0),g={arrObjLen:u,rangeMin:n[0],rangeMax:n[1],inputTypes:{},min:{},max:{},groupMinMax:{},repeat:t,groups:o,inputKeyNames:p,outputKeyNames:new Array(f),repeatedKeyNames:m};let h=0;for(let e=0;e<g.inputKeyNames.length;e++)for(let t=0;t<g.repeatedKeyNames[e];t++)g.outputKeyNames[h++]=g.inputKeyNames[e];a(g.groups);const d=["number","boolean"];for(const e of g.inputKeyNames){const t=typeof l[e],n=r(e,g.groups);if(!d.includes(t))throw new Error(`Invalid input type "${t}" provided for key "${e}". Only accepting `);g.inputTypes[e]=t,c&&s.hasOwnProperty(e)?n?g.groupMinMax[n]=s[e]:(g.min[e]=s[e].min,g.max[e]=s[e].max):n?g.groupMinMax[n]={min:1/0,max:-1/0}:(g.min[e]=1/0,g.max[e]=-1/0)}for(const e of i)for(const t of g.inputKeyNames){let n=e[t];"boolean"===g.inputTypes[t]&&(e[t]=Number(n));const a=r(t,g.groups);(!1===c||c&&!s.hasOwnProperty(t))&&(a?(g.groupMinMax[a].min=Math.min(g.groupMinMax[a].min,n),g.groupMinMax[a].max=Math.max(g.groupMinMax[a].max,n)):(g.min[t]=Math.min(g.min[t],n),g.max[t]=Math.max(g.max[t],n)))}const M=new Array(u);for(let e=0;e<u;e++){const t=i[e],n=new Array(g.outputKeyNames.length);let a=0;for(let e=0;e<g.inputKeyNames.length;e++){const o=g.inputKeyNames[e],s=t[o],i=r(o,g.groups);let u,l;i?(u=g.groupMinMax[i].min,l=g.groupMinMax[i].max):(u=g.min[o],l=g.max[o]);const c=l!==u?g.rangeMin+(s-u)/(l-u)*(g.rangeMax-g.rangeMin):g.rangeMin,p=g.repeatedKeyNames[e];for(let e=0;e<p;e++)n[a++]=c}M[e]=n}return{scaledOutput:M,scaledConfig:g}},a=e=>{const t=new Set,n=[];for(const[a,r]of Object.entries(e))t.add(a),n.push(a),r.forEach((e=>{t.add(e),n.push(e)}));if(t.size!==n.length)throw new Error("Duplicate value found between properties in validateUniqueProperties function.")},r=(e,t)=>{for(const[n,a]of Object.entries(t))if(a.includes(e))return n;return null},o=({arrObj:e,trainingSplit:t=.8,repeat:a,yCallbackFunc:r=e=>e,xCallbackFunc:o=e=>e,validateRows:s=()=>!0,groups:i,shuffle:u=!1,minmaxRange:l,balancing:c="",state:p={},customMinMaxRanges:m})=>{let f=[],g=[];for(let t=0;t<e.length;t++){if(!s({objRow:e,index:t,state:p}))continue;const n=o({objRow:e,index:t,state:p}),a=r({objRow:e,index:t,state:p});null!=n&&null!=a&&(f.push(n),g.push(a))}if(u){const{shuffledX:e,shuffledY:t}=((e,t)=>{if(e.length!==t.length)throw new Error("X and Y arrays must have the same length");const n=Array.from({length:e.length},((e,t)=>t));for(let e=n.length-1;e>0;e--){const t=Math.floor(Math.random()*(e+1));[n[e],n[t]]=[n[t],n[e]]}return{shuffledX:n.map((t=>e[t])),shuffledY:n.map((e=>t[e]))}})(f,g);f=e,g=t}let{scaledOutput:h,scaledConfig:d}=n({arrObj:f,repeat:a,groups:i,minmaxRange:l,customMinMaxRanges:m}),{scaledOutput:M,scaledConfig:x}=n({arrObj:g,repeat:a,groups:i,minmaxRange:l,customMinMaxRanges:m});const y=Math.floor(h.length*t);let b=h.slice(0,y),O=M.slice(0,y),w=h.slice(y),j=M.slice(y);if(c){let e;if("oversample"===c)e=((e,t)=>{const n={},a={};t.forEach(((r,o)=>{n[r]||(n[r]=0,a[r]=[]),n[r]++,a[r].push([e[o],t[o]])}));const r=Math.max(...Object.values(n)),o=[],s=[];return Object.keys(a).forEach((e=>{const t=a[e],n=t.length;for(let e=0;e<r;e++){const a=t[e%n];o.push(a[0]),s.push(a[1])}})),{X:o,Y:s}})(b,O),b=e.X,O=e.Y;else{if("undersample"!==c)throw Error('balancing argument only accepts "false", "oversample" and "undersample". Defaults to "false".');e=((e,t)=>{const n={},a={};t.forEach(((r,o)=>{n[r]||(n[r]=0,a[r]=[]),n[r]++,a[r].push([e[o],t[o]])}));const r=Math.min(...Object.values(n)),o=[],s=[];return Object.keys(a).forEach((e=>{const t=a[e];for(let e=0;e<r;e++){const n=t[e];o.push(n[0]),s.push(n[1])}})),{X:o,Y:s}})(b,O),b=e.X,O=e.Y}}return{trainX:b,trainY:O,testX:w,testY:j,configX:d,configY:x}},s=({arrObj:e,repeat:t,xCallbackFunc:a=e=>e,validateRows:r=()=>!0,groups:o,shuffle:s=!1,minmaxRange:i,state:u={},prevConfig:l,customMinMaxRanges:c})=>{let p=[];for(let t=0;t<e.length;t++){if(!r(e[t]))continue;const n=a({objRow:e,index:t,state:u});null!=n&&!1!==n&&p.push(n)}s&&(p=(e=>{const t=[...e];for(let e=t.length-1;e>0;e--){const n=Math.floor(Math.random()*(e+1));[t[e],t[n]]=[t[n],t[e]]}return t})(p));const{scaledOutput:m,scaledConfig:f}=n({arrObj:p,repeat:t,groups:o,minmaxRange:i,prevConfig:l,customMinMaxRanges:c});return{X:m,configX:f}},i=(e,t)=>{if(0===t)return e;if(t<0)throw new Error("timeSteps must be greater than 0");const n=[];for(let a=0;a<=e.length-t;a++)n.push(e.slice(a,a+t));return n};XY_Scale=t})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xy-scale",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"main": "./index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
"description": "This repository contains a JavaScript module designed to facilitate the preprocessing of datasets for machine learning applications. The primary functionality of the module is to scale feature data using normalization or standardization methods, and to parse training and production datasets into appropriate formats for training models.",
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"@tensorflow/tfjs-node": "^4.22.0",
|
|
15
|
+
"ml-confusion-matrix": "^2.0.0",
|
|
16
|
+
"ml-knn": "^3.0.0",
|
|
17
|
+
"ohlcv-indicators": "^3.4.1",
|
|
15
18
|
"webpack": "^5.96.1",
|
|
16
19
|
"webpack-cli": "^5.1.4"
|
|
17
20
|
},
|
package/src/datasets.js
CHANGED
|
@@ -106,7 +106,6 @@ export const parseProductionX = ({
|
|
|
106
106
|
shuffle = false,
|
|
107
107
|
minmaxRange,
|
|
108
108
|
state = {},
|
|
109
|
-
prevConfig,
|
|
110
109
|
customMinMaxRanges
|
|
111
110
|
}) => {
|
|
112
111
|
let X = [];
|
|
@@ -131,7 +130,7 @@ export const parseProductionX = ({
|
|
|
131
130
|
const {
|
|
132
131
|
scaledOutput: scaledX,
|
|
133
132
|
scaledConfig: configX
|
|
134
|
-
} = scaleArrayObj({arrObj: X, repeat, groups, minmaxRange,
|
|
133
|
+
} = scaleArrayObj({arrObj: X, repeat, groups, minmaxRange, customMinMaxRanges})
|
|
135
134
|
|
|
136
135
|
|
|
137
136
|
// Split into training and testing sets
|
package/src/scale.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const scaleArrayObj = ({ arrObj, repeat = {}, minmaxRange = [0, 1], groups = {},
|
|
1
|
+
export const scaleArrayObj = ({ arrObj, repeat = {}, minmaxRange = [0, 1], groups = {}, customMinMaxRanges = null }) => {
|
|
2
2
|
|
|
3
3
|
const arrObjClone = [...arrObj]
|
|
4
4
|
const arrObjLen = arrObjClone.length;
|
|
@@ -11,51 +11,36 @@ export const scaleArrayObj = ({ arrObj, repeat = {}, minmaxRange = [0, 1], group
|
|
|
11
11
|
scaledConfig: {}
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
|
-
|
|
15
|
-
let config = {}
|
|
16
|
-
|
|
17
|
-
const isValidPrevConfig = prevConfig && validateConfig(prevConfig)
|
|
18
|
-
|
|
19
|
-
if(isValidPrevConfig)
|
|
20
|
-
{
|
|
21
|
-
validateCurrPrevConfig(prevConfig, {minmaxRange, repeat, groups, firstRow})
|
|
22
|
-
config = {...prevConfig}
|
|
23
|
-
}
|
|
24
|
-
else
|
|
25
|
-
{
|
|
26
|
-
const inputKeyNames = Object.keys(firstRow);
|
|
27
|
-
|
|
28
|
-
const repeatedKeyNames = inputKeyNames.map(key => {
|
|
29
|
-
return repeat.hasOwnProperty(key) ? Math.max(repeat[key], 1) : 1;
|
|
30
|
-
});
|
|
31
14
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
15
|
+
const inputKeyNames = Object.keys(firstRow);
|
|
16
|
+
|
|
17
|
+
const repeatedKeyNames = inputKeyNames.map(key => {
|
|
18
|
+
return repeat.hasOwnProperty(key) ? Math.max(repeat[key], 1) : 1;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const countRepeatedKeyNames = repeatedKeyNames.reduce((sum, rep) => sum + rep, 0);
|
|
22
|
+
|
|
23
|
+
const config = {
|
|
24
|
+
arrObjLen,
|
|
25
|
+
rangeMin: minmaxRange[0],
|
|
26
|
+
rangeMax: minmaxRange[1],
|
|
27
|
+
inputTypes: {},
|
|
28
|
+
min: {},
|
|
29
|
+
max: {},
|
|
30
|
+
groupMinMax: {},
|
|
31
|
+
repeat,
|
|
32
|
+
groups,
|
|
33
|
+
inputKeyNames,
|
|
34
|
+
outputKeyNames: new Array(countRepeatedKeyNames),
|
|
35
|
+
repeatedKeyNames,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let keyNamesIdx = 0;
|
|
50
39
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
40
|
+
for (let i = 0; i < config.inputKeyNames.length; i++) {
|
|
41
|
+
for (let w = 0; w < config.repeatedKeyNames[i]; w++) {
|
|
42
|
+
config.outputKeyNames[keyNamesIdx++] = config.inputKeyNames[i];
|
|
55
43
|
}
|
|
56
|
-
|
|
57
|
-
validateConfig(config)
|
|
58
|
-
|
|
59
44
|
}
|
|
60
45
|
|
|
61
46
|
validateUniqueProperties(config.groups);
|
|
@@ -73,22 +58,6 @@ export const scaleArrayObj = ({ arrObj, repeat = {}, minmaxRange = [0, 1], group
|
|
|
73
58
|
throw new Error(`Invalid input type "${firstType}" provided for key "${key}". Only accepting `)
|
|
74
59
|
}
|
|
75
60
|
|
|
76
|
-
if(isValidPrevConfig)
|
|
77
|
-
{
|
|
78
|
-
if(!config.inputTypes.hasOwnProperty(key))
|
|
79
|
-
{
|
|
80
|
-
// If prevConfig is set, no new inputTypes can be introduced
|
|
81
|
-
throw new Error(`Error: A new unknown inputType property "${key}" found.`)
|
|
82
|
-
}
|
|
83
|
-
if(config.inputTypes[key] !== firstType)
|
|
84
|
-
{
|
|
85
|
-
//is prevConfig is set the types of "typeof firstRow[key]" and config.inputTypes[key] must be the same
|
|
86
|
-
throw new Error(`Error: Current inputType of property "${key}" is not the same as in the prevConfig inputType.`)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
61
|
config.inputTypes[key] = firstType;
|
|
93
62
|
|
|
94
63
|
if(validCustomMinMaxRanges && customMinMaxRanges.hasOwnProperty(key))
|
|
@@ -294,27 +263,4 @@ const validateConfig = config => {
|
|
|
294
263
|
}
|
|
295
264
|
|
|
296
265
|
return true;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const validateCurrPrevConfig = (prevConfig, {minmaxRange, repeat, groups, firstRow}) => {
|
|
300
|
-
|
|
301
|
-
if(prevConfig.rangeMin !== minmaxRange[0] || prevConfig.rangeMax !== minmaxRange[1])
|
|
302
|
-
{
|
|
303
|
-
throw new Error(`"prevConfig.minmaxRange" is not equal "minmaxRange".`);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
//it is important o keep the same key order
|
|
307
|
-
if (JSON.stringify(prevConfig.inputKeyNames) !== JSON.stringify(Object.keys(firstRow))) {
|
|
308
|
-
throw new Error(`"prevConfig.inputKeyNames" structure does not match "Object.keys(firstRow)" structure. The order of keys is important.`);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
if (JSON.stringify(prevConfig.repeat) !== JSON.stringify(repeat)) {
|
|
312
|
-
throw new Error(`"prevConfig.repeat" structure does not match "repeat" structure. The order of keys is important.`);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
if (JSON.stringify(prevConfig.groups) !== JSON.stringify(groups)) {
|
|
316
|
-
throw new Error(`"prevConfig.groups" structure does not match "groups" structure. The order of keys is important.`);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return true
|
|
320
266
|
}
|
package/test/test.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import OHLCV_INDICATORS from 'ohlcv-indicators'
|
|
2
|
+
import KNN from 'ml-knn'
|
|
3
|
+
import { ConfusionMatrix } from 'ml-confusion-matrix';
|
|
4
|
+
|
|
1
5
|
import { parseTrainingXY } from "../src/datasets.js"
|
|
2
6
|
import {arrayToTimesteps} from '../src/timeSteps.js'
|
|
3
7
|
import { loadFile } from "./fs.js"
|
|
@@ -5,45 +9,30 @@ import * as tf from '@tensorflow/tfjs-node'
|
|
|
5
9
|
|
|
6
10
|
const test = async () => {
|
|
7
11
|
|
|
8
|
-
const
|
|
9
|
-
.map(({open, high, low, close}) => ({open, high, low, close})) //file in /datasets/1d-spy.json
|
|
10
|
-
|
|
11
|
-
//callback function used to prepare X before scaling
|
|
12
|
-
const xCallbackFunc = ({ objRow, index }) => {
|
|
13
|
-
const curr = objRow[index]
|
|
14
|
-
const prev = objRow[index - 1]
|
|
15
|
-
|
|
16
|
-
//returning null or undefined will exclude current row X and Y from training
|
|
17
|
-
if(typeof prev === 'undefined') return null
|
|
18
|
-
|
|
19
|
-
const { open, high, low, close } = curr
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
open,
|
|
23
|
-
high,
|
|
24
|
-
low,
|
|
25
|
-
close,
|
|
26
|
-
change: open - prev.close,
|
|
27
|
-
top: high - Math.max(open, close),
|
|
28
|
-
bottom: low - Math.min(open, close),
|
|
29
|
-
body: open-close,
|
|
30
|
-
}
|
|
31
|
-
}
|
|
12
|
+
const ohlcv = (await loadFile({fileName: 'btc-1d.json', pathName: 'datasets'}))
|
|
32
13
|
|
|
33
|
-
|
|
34
|
-
const yCallbackFunc = ({ objRow, index }) => {
|
|
35
|
-
const curr = objRow[index];
|
|
36
|
-
const next = objRow[index + 1];
|
|
14
|
+
const indicators = new OHLCV_INDICATORS({input: ohlcv, ticker: 'BTC', precision: false})
|
|
37
15
|
|
|
38
|
-
|
|
39
|
-
|
|
16
|
+
indicators
|
|
17
|
+
.rsi(40)
|
|
18
|
+
.bollingerBands(20, 2)
|
|
19
|
+
.ema(50)
|
|
20
|
+
.sma(200)
|
|
21
|
+
.sma(300)
|
|
22
|
+
.scaler(200, ['open', 'high', 'low', 'close'], {group: true})
|
|
23
|
+
.crossPairs([
|
|
24
|
+
{fast: 'rsi_40', slow: 30},
|
|
25
|
+
{fast: 'price', slow: 'sma_200'},
|
|
26
|
+
{fast: 'price', slow: 'sma_300'},
|
|
27
|
+
{fast: 'price', slow: 'bollinger_bands_upper'}
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
const parsedOhlcv = indicators.getData()
|
|
31
|
+
|
|
32
|
+
const {scaledGroups} = indicators
|
|
33
|
+
|
|
34
|
+
console.log(scaledGroups)
|
|
40
35
|
|
|
41
|
-
return {
|
|
42
|
-
label_1: next.open > curr.close,
|
|
43
|
-
label_2: next.close > curr.close,
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
|
|
47
36
|
const {
|
|
48
37
|
trainX,
|
|
49
38
|
trainY,
|
|
@@ -52,27 +41,74 @@ const test = async () => {
|
|
|
52
41
|
configX,
|
|
53
42
|
keyNamesX,
|
|
54
43
|
} = parseTrainingXY({
|
|
55
|
-
arrObj:
|
|
56
|
-
trainingSplit: 0.
|
|
44
|
+
arrObj: parsedOhlcv,
|
|
45
|
+
trainingSplit: 0.50,
|
|
57
46
|
yCallbackFunc,
|
|
58
47
|
xCallbackFunc,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
48
|
+
validateRows: ({objRow, index}) => {
|
|
49
|
+
const curr = objRow[index]
|
|
50
|
+
const prev = objRow[index - 1]
|
|
51
|
+
|
|
52
|
+
if(typeof prev === 'undefined') return false
|
|
53
|
+
|
|
54
|
+
return curr.ema_50 > curr.sma_300 && curr.sma_200 > curr.sma_300 && (curr.price_x_sma_300 === -1 || curr.price_x_sma_300 === 1)
|
|
65
55
|
},
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
56
|
+
shuffle: false,
|
|
57
|
+
minmaxRange: [0, 1],
|
|
58
|
+
balancing: null,
|
|
59
|
+
groups: scaledGroups
|
|
71
60
|
});
|
|
72
61
|
|
|
62
|
+
//console.log(configX.outputKeyNames)
|
|
63
|
+
console.log(configX)
|
|
64
|
+
|
|
65
|
+
tensorflowExample({
|
|
66
|
+
trainX,
|
|
67
|
+
trainY,
|
|
68
|
+
testX,
|
|
69
|
+
testY,
|
|
70
|
+
configX,
|
|
71
|
+
keyNamesX,
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
classifiersExample({
|
|
75
|
+
trainX,
|
|
76
|
+
trainY,
|
|
77
|
+
testX,
|
|
78
|
+
testY,
|
|
79
|
+
configX,
|
|
80
|
+
keyNamesX,
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
}
|
|
73
85
|
|
|
74
|
-
|
|
86
|
+
const classifiersExample = ({
|
|
87
|
+
trainX,
|
|
88
|
+
trainY,
|
|
89
|
+
testX,
|
|
90
|
+
testY,
|
|
91
|
+
configX,
|
|
92
|
+
keyNamesX,
|
|
93
|
+
}) => {
|
|
94
|
+
const model = new KNN(trainX, trainY)
|
|
75
95
|
|
|
96
|
+
const predictions = model.predict(testX)
|
|
97
|
+
const compare = ConfusionMatrix.fromLabels(testY.flat(), predictions.flat())
|
|
98
|
+
|
|
99
|
+
//console.log(testY.flat(), predictions.flat())
|
|
100
|
+
|
|
101
|
+
console.log(compare.getAccuracy())
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const tensorflowExample = ({
|
|
105
|
+
trainX,
|
|
106
|
+
trainY,
|
|
107
|
+
testX,
|
|
108
|
+
testY,
|
|
109
|
+
configX,
|
|
110
|
+
keyNamesX,
|
|
111
|
+
}) => {
|
|
76
112
|
|
|
77
113
|
const timeSteps = 10
|
|
78
114
|
const colsX = trainX[0].length
|
|
@@ -80,16 +116,70 @@ const test = async () => {
|
|
|
80
116
|
const timeSteppedTrainX = arrayToTimesteps(trainX, timeSteps)
|
|
81
117
|
const trimedTrainY = trainY.slice(timeSteps-1)
|
|
82
118
|
|
|
83
|
-
|
|
84
|
-
//console.log([trainX.length, timeSteps, timeSteppedTrainX[0][0].length])
|
|
85
|
-
|
|
86
119
|
const inputX = tf.tensor3d(timeSteppedTrainX, [timeSteppedTrainX.length, timeSteps, colsX])
|
|
87
120
|
const targetY = tf.tensor2d(trimedTrainY, [trimedTrainY.length, colsY])
|
|
88
121
|
|
|
89
|
-
console.log('trainX', trainX
|
|
122
|
+
//console.log('trainX', trainX)
|
|
90
123
|
//console.log('configX', configX)
|
|
91
124
|
//console.log('inputX', inputX)
|
|
92
|
-
//console.log('inputX', targetY)
|
|
125
|
+
//console.log('inputX', targetY)
|
|
126
|
+
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
//callback function used to prepare X before scaling
|
|
130
|
+
const xCallbackFunc = ({ objRow, index }) => {
|
|
131
|
+
const curr = objRow[index]
|
|
132
|
+
const prev = objRow[index - 1]
|
|
133
|
+
|
|
134
|
+
//returning null or undefined will exclude current row X and Y from training
|
|
135
|
+
if(typeof prev === 'undefined') return null
|
|
136
|
+
|
|
137
|
+
//console.log(((curr.sma_300 - curr.low) / curr.low) * 100)
|
|
138
|
+
|
|
139
|
+
const output = {
|
|
140
|
+
ema50IsUp: curr.ema_50 > prev.ema_50,
|
|
141
|
+
ema50GtSma200: curr.ema_50 > curr.sma_200,
|
|
142
|
+
ema50GtSma300: curr.ema_50 > curr.sma_300,
|
|
143
|
+
sma200IsUp: curr.sma_200 > prev.sma_200,
|
|
144
|
+
sma200GtSma300: curr.sma_200 > prev.sma_300,
|
|
145
|
+
sma_300IsUp: curr.sma_300 > prev.sma_300
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for(const [key, value] of Object.entries(curr))
|
|
149
|
+
{
|
|
150
|
+
if(key.includes('minmax'))
|
|
151
|
+
{
|
|
152
|
+
output[key] = value
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return output
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
//callback function used to prepare Y before scaling
|
|
160
|
+
const yCallbackFunc = ({ objRow, index }) => {
|
|
161
|
+
const curr = objRow[index]
|
|
162
|
+
const next = new Array(60).fill(0).map((_, i) => objRow[index + 1 + i])
|
|
163
|
+
|
|
164
|
+
//returning null or undefined will exclude current row X and Y from training
|
|
165
|
+
if (next.some(o => typeof o === 'undefined')) return null;
|
|
166
|
+
|
|
167
|
+
const priceTp = curr.sma_300 * 1.3
|
|
168
|
+
const entryPrice = curr.sma_300 * 0.96
|
|
169
|
+
const tp = next.some(o => o.close > entryPrice && (o.high > priceTp))
|
|
170
|
+
const sl = next.some(o => (o.low - entryPrice)/entryPrice < -0.10 && o.low < entryPrice)
|
|
171
|
+
|
|
172
|
+
const highestHigh = Math.max(...next.map(o => o.high))
|
|
173
|
+
const lowestLow = Math.min(...next.slice(0, 5).map(o => o.low))
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
if(lowestLow > entryPrice) return null
|
|
177
|
+
|
|
178
|
+
//console.log([curr.date, (lowestLow - entryPrice)/entryPrice])
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
result: Number(tp === true && sl === false)
|
|
182
|
+
}
|
|
93
183
|
}
|
|
94
184
|
|
|
95
185
|
test()
|