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.
@@ -1 +1 @@
1
- var XY_Scale;(()=>{"use strict";var e={d:(r,t)=>{for(var n in t)e.o(t,n)&&!e.o(r,n)&&Object.defineProperty(r,n,{enumerable:!0,get:t[n]})},o:(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},r={};e.r(r),e.d(r,{arrayToTimesteps:()=>p,parseProductionX:()=>u,parseTrainingXY:()=>i});const t=({arrObj:e,repeat:r={},minmaxRange:t=[0,1],groups:i={},prevConfig:u=null,customMinMaxRanges:p=null})=>{const m=[...e],f=m.length,l=m[0],c="object"==typeof p&&null!==p;if(0===f)return{scaledOutput:[],scaledConfig:{}};let g={};const h=u&&o(u);if(h)s(u,{minmaxRange:t,repeat:r,groups:i,firstRow:l}),g={...u};else{const e=Object.keys(l),n=e.map((e=>r.hasOwnProperty(e)?Math.max(r[e],1):1)),a=n.reduce(((e,r)=>e+r),0);g={arrObjLen:f,rangeMin:t[0],rangeMax:t[1],inputTypes:{},min:{},max:{},groupMinMax:{},repeat:r,groups:i,inputKeyNames:e,outputKeyNames:new Array(a),repeatedKeyNames:n};let s=0;for(let e=0;e<g.inputKeyNames.length;e++)for(let r=0;r<g.repeatedKeyNames[e];r++)g.outputKeyNames[s++]=g.inputKeyNames[e];o(g)}n(g.groups);const y=["number","boolean"];for(const e of g.inputKeyNames){const r=typeof l[e],t=a(e,g.groups);if(!y.includes(r))throw new Error(`Invalid input type "${r}" provided for key "${e}". Only accepting `);if(h){if(!g.inputTypes.hasOwnProperty(e))throw new Error(`Error: A new unknown inputType property "${e}" found.`);if(g.inputTypes[e]!==r)throw new Error(`Error: Current inputType of property "${e}" is not the same as in the prevConfig inputType.`)}else g.inputTypes[e]=r,c&&p.hasOwnProperty(e)?t?g.groupMinMax[t]=p[e]:(g.min[e]=p[e].min,g.max[e]=p[e].max):t?g.groupMinMax[t]={min:1/0,max:-1/0}:(g.min[e]=1/0,g.max[e]=-1/0)}for(const e of m)for(const r of g.inputKeyNames){let t=e[r];"boolean"===g.inputTypes[r]&&(e[r]=Number(t));const n=a(r,g.groups);(!1===c||c&&!p.hasOwnProperty(r))&&(n?(g.groupMinMax[n].min=Math.min(g.groupMinMax[n].min,t),g.groupMinMax[n].max=Math.max(g.groupMinMax[n].max,t)):(g.min[r]=Math.min(g.min[r],t),g.max[r]=Math.max(g.max[r],t)))}const w=new Array(f);for(let e=0;e<f;e++){const r=m[e],t=new Array(g.outputKeyNames.length);let n=0;for(let e=0;e<g.inputKeyNames.length;e++){const o=g.inputKeyNames[e],s=r[o],i=a(o,g.groups);let u,p;i?(u=g.groupMinMax[i].min,p=g.groupMinMax[i].max):(u=g.min[o],p=g.max[o]);const m=p!==u?g.rangeMin+(s-u)/(p-u)*(g.rangeMax-g.rangeMin):g.rangeMin,f=g.repeatedKeyNames[e];for(let e=0;e<f;e++)t[n++]=m}w[e]=t}return{scaledOutput:w,scaledConfig:g}},n=e=>{const r=new Set,t=[];for(const[n,a]of Object.entries(e))r.add(n),t.push(n),a.forEach((e=>{r.add(e),t.push(e)}));if(r.size!==t.length)throw new Error("Duplicate value found between properties in validateUniqueProperties function.")},a=(e,r)=>{for(const[t,n]of Object.entries(r))if(n.includes(e))return t;return null},o=e=>{if(!e)return!1;const r=["rangeMin","rangeMax","inputTypes","min","max","groupMinMax","repeat","groups","inputKeyNames","outputKeyNames","repeatedKeyNames"];for(const t of r)if(!e.hasOwnProperty(t))throw new Error(`Missing key "${t}" in config.`);const{rangeMin:t,rangeMax:n,inputTypes:a,min:o,max:s,groupMinMax:i,repeat:u,groups:p,inputKeyNames:m,outputKeyNames:f,repeatedKeyNames:l}=e;if("number"!=typeof t||"number"!=typeof n)throw new Error("rangeMin and rangeMax must be numbers.");if(t>=n)throw new Error("rangeMin must be less than rangeMax.");const c=e=>"object"==typeof e&&null!==e&&!Array.isArray(e);if(!c(a))throw new Error("inputTypes must be an object.");if(!c(o))throw new Error("min must be an object.");if(!c(s))throw new Error("max must be an object.");if(!c(i))throw new Error("groupMinMax must be an object.");if(!c(u))throw new Error("repeat must be an object.");if(!c(p))throw new Error("groups must be an object.");if(!Array.isArray(m))throw new Error("inputKeyNames must be an array.");if(!Array.isArray(f))throw new Error("outputKeyNames must be an array.");if(!Array.isArray(l))throw new Error("repeatedKeyNames must be an array.");return!0},s=(e,{minmaxRange:r,repeat:t,groups:n,firstRow:a})=>{if(e.rangeMin!==r[0]||e.rangeMax!==r[1])throw new Error('"prevConfig.minmaxRange" is not equal "minmaxRange".');if(JSON.stringify(e.inputKeyNames)!==JSON.stringify(Object.keys(a)))throw new Error('"prevConfig.inputKeyNames" structure does not match "Object.keys(firstRow)" structure. The order of keys is important.');if(JSON.stringify(e.repeat)!==JSON.stringify(t))throw new Error('"prevConfig.repeat" structure does not match "repeat" structure. The order of keys is important.');if(JSON.stringify(e.groups)!==JSON.stringify(n))throw new Error('"prevConfig.groups" structure does not match "groups" structure. The order of keys is important.');return!0},i=({arrObj:e,trainingSplit:r=.8,repeat:n,yCallbackFunc:a=e=>e,xCallbackFunc:o=e=>e,validateRows:s=()=>!0,groups:i,shuffle:u=!1,minmaxRange:p,balancing:m="",state:f={},customMinMaxRanges:l})=>{let c=[],g=[];for(let r=0;r<e.length;r++){if(!s({objRow:e,index:r,state:f}))continue;const t=o({objRow:e,index:r,state:f}),n=a({objRow:e,index:r,state:f});null!=t&&null!=n&&(c.push(t),g.push(n))}if(u){const{shuffledX:e,shuffledY:r}=((e,r)=>{if(e.length!==r.length)throw new Error("X and Y arrays must have the same length");const t=Array.from({length:e.length},((e,r)=>r));for(let e=t.length-1;e>0;e--){const r=Math.floor(Math.random()*(e+1));[t[e],t[r]]=[t[r],t[e]]}return{shuffledX:t.map((r=>e[r])),shuffledY:t.map((e=>r[e]))}})(c,g);c=e,g=r}let{scaledOutput:h,scaledConfig:y}=t({arrObj:c,repeat:n,groups:i,minmaxRange:p,customMinMaxRanges:l}),{scaledOutput:w,scaledConfig:M}=t({arrObj:g,repeat:n,groups:i,minmaxRange:p,customMinMaxRanges:l});const d=Math.floor(h.length*r);let b=h.slice(0,d),x=w.slice(0,d),O=h.slice(d),j=w.slice(d);if(m){let e;if("oversample"===m)e=((e,r)=>{const t={},n={};r.forEach(((a,o)=>{t[a]||(t[a]=0,n[a]=[]),t[a]++,n[a].push([e[o],r[o]])}));const a=Math.max(...Object.values(t)),o=[],s=[];return Object.keys(n).forEach((e=>{const r=n[e],t=r.length;for(let e=0;e<a;e++){const n=r[e%t];o.push(n[0]),s.push(n[1])}})),{X:o,Y:s}})(b,x),b=e.X,x=e.Y;else{if("undersample"!==m)throw Error('balancing argument only accepts "false", "oversample" and "undersample". Defaults to "false".');e=((e,r)=>{const t={},n={};r.forEach(((a,o)=>{t[a]||(t[a]=0,n[a]=[]),t[a]++,n[a].push([e[o],r[o]])}));const a=Math.min(...Object.values(t)),o=[],s=[];return Object.keys(n).forEach((e=>{const r=n[e];for(let e=0;e<a;e++){const t=r[e];o.push(t[0]),s.push(t[1])}})),{X:o,Y:s}})(b,x),b=e.X,x=e.Y}}return{trainX:b,trainY:x,testX:O,testY:j,configX:y,configY:M}},u=({arrObj:e,repeat:r,xCallbackFunc:n=e=>e,validateRows:a=()=>!0,groups:o,shuffle:s=!1,minmaxRange:i,state:u={},prevConfig:p,customMinMaxRanges:m})=>{let f=[];for(let r=0;r<e.length;r++){if(!a(e[r]))continue;const t=n({objRow:e,index:r,state:u});null!=t&&!1!==t&&f.push(t)}s&&(f=(e=>{const r=[...e];for(let e=r.length-1;e>0;e--){const t=Math.floor(Math.random()*(e+1));[r[e],r[t]]=[r[t],r[e]]}return r})(f));const{scaledOutput:l,scaledConfig:c}=t({arrObj:f,repeat:r,groups:o,minmaxRange:i,prevConfig:p,customMinMaxRanges:m});return{X:l,configX:c}},p=(e,r)=>{if(0===r)return e;if(r<0)throw new Error("timeSteps must be greater than 0");const t=[];for(let n=0;n<=e.length-r;n++)t.push(e.slice(n,n+r));return t};XY_Scale=r})();
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.2",
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, prevConfig, customMinMaxRanges})
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 = {}, prevConfig = null, customMinMaxRanges = null }) => {
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
- const countRepeatedKeyNames = repeatedKeyNames.reduce((sum, rep) => sum + rep, 0);
33
-
34
- config = {
35
- arrObjLen,
36
- rangeMin: minmaxRange[0],
37
- rangeMax: minmaxRange[1],
38
- inputTypes: {},
39
- min: {},
40
- max: {},
41
- groupMinMax: {},
42
- repeat,
43
- groups,
44
- inputKeyNames,
45
- outputKeyNames: new Array(countRepeatedKeyNames),
46
- repeatedKeyNames,
47
- }
48
-
49
- let keyNamesIdx = 0;
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
- for (let i = 0; i < config.inputKeyNames.length; i++) {
52
- for (let w = 0; w < config.repeatedKeyNames[i]; w++) {
53
- config.outputKeyNames[keyNamesIdx++] = config.inputKeyNames[i];
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 myArray = (await loadFile({fileName: '1d-spy.json', pathName: 'datasets'}))
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
- //callback function used to prepare Y before scaling
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
- //returning null or undefined will exclude current row X and Y from training
39
- if (typeof next === 'undefined') return null;
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: myArray,
56
- trainingSplit: 0.90,
44
+ arrObj: parsedOhlcv,
45
+ trainingSplit: 0.50,
57
46
  yCallbackFunc,
58
47
  xCallbackFunc,
59
- groups: {
60
- ohlc: ['open', 'high', 'low', 'close']
61
- },
62
- shuffle: true,
63
- repeat: {
64
- close: 20
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
- minmaxRange: [-1, 1],
67
- balancing: 'oversample',
68
- customMinMaxRanges: {
69
- close: {min: 0, max: 10000}
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
- //console.log('testX', testX.slice(-2))
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[trainX.length - 1])
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()