xy-scale 1.3.2 → 1.3.4

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:()=>u,parseProductionX:()=>s,parseTrainingXY:()=>o});const n=({arrObj:e,repeat:t={},minmaxRange:n=[0,1],groups:o={},customMinMaxRanges:s=null,excludes:u=new Set})=>{const i=[...e],l=i.length,c=i[0],p="object"==typeof s&&null!==s;if(0===l)return{scaledOutput:[],scaledConfig:{}};const m=Object.keys(c),f=m.map((e=>t.hasOwnProperty(e)?Math.max(t[e],1):1)),g=f.reduce(((e,t)=>e+t),0),h={arrObjLen:l,rangeMin:n[0],rangeMax:n[1],inputTypes:{},min:{},max:{},groupMinMax:{},repeat:t,groups:o,inputKeyNames:m,outputKeyNames:new Array(g),repeatedKeyNames:f};let d=0;for(let e=0;e<h.inputKeyNames.length;e++)for(let t=0;t<h.repeatedKeyNames[e];t++)h.outputKeyNames[d++]=h.inputKeyNames[e];a(h.groups);const x=["number","boolean"];for(const e of h.inputKeyNames){if(u.has(e)){h.inputTypes[e]="excluded";continue}const t=typeof c[e],n=r(e,h.groups);if(!x.includes(t))throw new Error(`Invalid input type "${t}" provided for key "${e}". Only accepting `);h.inputTypes[e]=t,p&&s.hasOwnProperty(e)?n?h.groupMinMax[n]=s[e]:(h.min[e]=s[e].min,h.max[e]=s[e].max):n?h.groupMinMax[n]={min:1/0,max:-1/0}:(h.min[e]=1/0,h.max[e]=-1/0)}for(const e of i)for(const t of h.inputKeyNames){if("excluded"===h.inputTypes[t])continue;let n=e[t];"boolean"===h.inputTypes[t]&&(e[t]=Number(n));const a=r(t,h.groups);(!1===p||p&&!s.hasOwnProperty(t))&&(a?(h.groupMinMax[a].min=Math.min(h.groupMinMax[a].min,n),h.groupMinMax[a].max=Math.max(h.groupMinMax[a].max,n)):(h.min[t]=Math.min(h.min[t],n),h.max[t]=Math.max(h.max[t],n)))}const M=new Array(l);for(let e=0;e<l;e++){const t=i[e],n=new Array(h.outputKeyNames.length);let a=0;for(let e=0;e<h.inputKeyNames.length;e++){const o=h.inputKeyNames[e],s=t[o];if("excluded"===h.inputTypes[o]){n[a++]=s;continue}const u=r(o,h.groups);let i,l;u?(i=h.groupMinMax[u].min,l=h.groupMinMax[u].max):(i=h.min[o],l=h.max[o]);const c=l!==i?h.rangeMin+(s-i)/(l-i)*(h.rangeMax-h.rangeMin):h.rangeMin,p=h.repeatedKeyNames[e];for(let e=0;e<p;e++)n[a++]=c}M[e]=n}return{scaledOutput:M,scaledConfig:h}},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:u={},shuffle:i=!1,minmaxRange:l=[],balancing:c="",state:p={},customMinMaxRanges:m={},excludes:f=[]})=>{let g=[],h=[];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&&(g.push(n),h.push(a))}if(i){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]))}})(g,h);g=e,h=t}const d=new Set(f);let{scaledOutput:x,scaledConfig:M}=n({arrObj:g,repeat:a,groups:u,minmaxRange:l,customMinMaxRanges:m,excludes:d}),{scaledOutput:y,scaledConfig:b}=n({arrObj:h,repeat:a,groups:u,minmaxRange:l,customMinMaxRanges:m,excludes:d});const O=Math.floor(x.length*t);let w=x.slice(0,O),j=y.slice(0,O),R=x.slice(O),v=y.slice(O);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}})(w,j),w=e.X,j=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}})(w,j),w=e.X,j=e.Y}}return{trainX:w,trainY:j,testX:R,testY:v,configX:M,configY:b}},s=({arrObj:e=[],repeat:t={},xCallbackFunc:a=e=>e,validateRows:r=()=>!0,groups:o={},shuffle:s=!1,minmaxRange:u=[],state:i={},customMinMaxRanges:l,excludes:c=[]})=>{let p=[];for(let t=0;t<e.length;t++){if(!r(e[t]))continue;const n=a({objRow:e,index:t,state:i});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:u,customMinMaxRanges:l,excludes:new Set(c)});return{X:m,configX:f}},u=(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.4",
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
@@ -3,18 +3,19 @@ import { arrayShuffle, xyArrayShuffle } from "./utilities.js";
3
3
  import { oversampleXY, undersampleXY } from "./balancing.js";
4
4
 
5
5
  export const parseTrainingXY = ({
6
- arrObj,
6
+ arrObj = [],
7
7
  trainingSplit = 0.8,
8
- repeat,
8
+ repeat = {},
9
9
  yCallbackFunc = row => row,
10
10
  xCallbackFunc = row => row,
11
11
  validateRows = () => true,
12
- groups,
12
+ groups = {},
13
13
  shuffle = false,
14
- minmaxRange,
14
+ minmaxRange = [],
15
15
  balancing = '',
16
16
  state = {},
17
- customMinMaxRanges
17
+ customMinMaxRanges = {},
18
+ excludes = []
18
19
  }) => {
19
20
  let X = [];
20
21
  let Y = [];
@@ -40,15 +41,17 @@ export const parseTrainingXY = ({
40
41
  Y = shuffledY
41
42
  }
42
43
 
44
+ const excludesSet = new Set(excludes)
45
+
43
46
  let {
44
47
  scaledOutput: scaledX,
45
48
  scaledConfig: configX
46
- } = scaleArrayObj({arrObj: X, repeat, groups, minmaxRange, customMinMaxRanges})
49
+ } = scaleArrayObj({arrObj: X, repeat, groups, minmaxRange, customMinMaxRanges, excludes: excludesSet})
47
50
 
48
51
  let {
49
52
  scaledOutput: scaledY,
50
53
  scaledConfig: configY,
51
- } = scaleArrayObj({arrObj: Y, repeat, groups, minmaxRange, customMinMaxRanges})
54
+ } = scaleArrayObj({arrObj: Y, repeat, groups, minmaxRange, customMinMaxRanges, excludes: excludesSet})
52
55
 
53
56
 
54
57
 
@@ -98,16 +101,16 @@ export const parseTrainingXY = ({
98
101
 
99
102
 
100
103
  export const parseProductionX = ({
101
- arrObj,
102
- repeat,
104
+ arrObj = [],
105
+ repeat = {},
103
106
  xCallbackFunc = row => row,
104
107
  validateRows = () => true,
105
- groups,
108
+ groups = {},
106
109
  shuffle = false,
107
- minmaxRange,
110
+ minmaxRange = [],
108
111
  state = {},
109
- prevConfig,
110
- customMinMaxRanges
112
+ customMinMaxRanges,
113
+ excludes = []
111
114
  }) => {
112
115
  let X = [];
113
116
 
@@ -127,11 +130,12 @@ export const parseProductionX = ({
127
130
  X = arrayShuffle(X)
128
131
  }
129
132
 
133
+
130
134
  // Scale X
131
135
  const {
132
136
  scaledOutput: scaledX,
133
137
  scaledConfig: configX
134
- } = scaleArrayObj({arrObj: X, repeat, groups, minmaxRange, prevConfig, customMinMaxRanges})
138
+ } = scaleArrayObj({arrObj: X, repeat, groups, minmaxRange, customMinMaxRanges, excludes: new Set(excludes)})
135
139
 
136
140
 
137
141
  // 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, excludes = new Set() }) => {
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);
@@ -64,6 +49,12 @@ export const scaleArrayObj = ({ arrObj, repeat = {}, minmaxRange = [0, 1], group
64
49
 
65
50
  for (const key of config.inputKeyNames) {
66
51
 
52
+ if(excludes.has(key))
53
+ {
54
+ config.inputTypes[key] = 'excluded'
55
+ continue
56
+ }
57
+
67
58
  const firstType = typeof firstRow[key]
68
59
  const thisGroup = findGroup(key, config.groups);
69
60
 
@@ -73,22 +64,6 @@ export const scaleArrayObj = ({ arrObj, repeat = {}, minmaxRange = [0, 1], group
73
64
  throw new Error(`Invalid input type "${firstType}" provided for key "${key}". Only accepting `)
74
65
  }
75
66
 
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
67
  config.inputTypes[key] = firstType;
93
68
 
94
69
  if(validCustomMinMaxRanges && customMinMaxRanges.hasOwnProperty(key))
@@ -116,6 +91,12 @@ export const scaleArrayObj = ({ arrObj, repeat = {}, minmaxRange = [0, 1], group
116
91
 
117
92
  for (const obj of arrObjClone) {
118
93
  for (const key of config.inputKeyNames) {
94
+
95
+ if (config.inputTypes[key] === 'excluded')
96
+ {
97
+ continue;
98
+ }
99
+
119
100
  let value = obj[key];
120
101
 
121
102
  if (config.inputTypes[key] === 'boolean') {
@@ -139,24 +120,32 @@ export const scaleArrayObj = ({ arrObj, repeat = {}, minmaxRange = [0, 1], group
139
120
  }
140
121
 
141
122
  const scaledOutput = new Array(arrObjLen);
123
+
124
+
142
125
  for (let i = 0; i < arrObjLen; i++) {
143
126
  const obj = arrObjClone[i];
144
127
  const scaledRow = new Array(config.outputKeyNames.length);
145
128
  let idx = 0;
146
129
 
147
130
  for (let j = 0; j < config.inputKeyNames.length; j++) {
148
- const key = config.inputKeyNames[j];
149
- const value = obj[key];
131
+ const key = config.inputKeyNames[j]
132
+ const value = obj[key]
133
+
134
+ if (config.inputTypes[key] === 'excluded')
135
+ {
136
+ scaledRow[idx++] = value
137
+ continue
138
+ }
150
139
 
151
140
  const thisGroup = findGroup(key, config.groups);
152
- let minValue, maxValue;
141
+ let minValue, maxValue
153
142
 
154
143
  if (thisGroup) {
155
- minValue = config.groupMinMax[thisGroup].min;
156
- maxValue = config.groupMinMax[thisGroup].max;
144
+ minValue = config.groupMinMax[thisGroup].min
145
+ maxValue = config.groupMinMax[thisGroup].max
157
146
  } else {
158
- minValue = config.min[key];
159
- maxValue = config.max[key];
147
+ minValue = config.min[key]
148
+ maxValue = config.max[key]
160
149
  }
161
150
 
162
151
  const scaledValue =
@@ -164,14 +153,14 @@ export const scaleArrayObj = ({ arrObj, repeat = {}, minmaxRange = [0, 1], group
164
153
  ? config.rangeMin + ((value - minValue) / (maxValue - minValue)) * (config.rangeMax - config.rangeMin)
165
154
  : config.rangeMin;
166
155
 
167
- const rep = config.repeatedKeyNames[j];
156
+ const rep = config.repeatedKeyNames[j]
168
157
 
169
158
  for (let w = 0; w < rep; w++) {
170
- scaledRow[idx++] = scaledValue;
159
+ scaledRow[idx++] = scaledValue
171
160
  }
172
161
 
173
162
  }
174
- scaledOutput[i] = scaledRow;
163
+ scaledOutput[i] = scaledRow
175
164
  }
176
165
 
177
166
 
@@ -179,8 +168,8 @@ export const scaleArrayObj = ({ arrObj, repeat = {}, minmaxRange = [0, 1], group
179
168
  return {
180
169
  scaledOutput,
181
170
  scaledConfig: config
182
- };
183
- };
171
+ }
172
+ }
184
173
 
185
174
 
186
175
  const validateUniqueProperties = obj => {
@@ -294,27 +283,4 @@ const validateConfig = config => {
294
283
  }
295
284
 
296
285
  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
286
  }
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,77 @@ 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,
60
+ excludes: ['high']
71
61
  });
72
62
 
63
+ //console.log(configX.outputKeyNames)
64
+ //console.log(configX)
65
+
66
+ console.log('trainX', trainX[0])
67
+
68
+ tensorflowExample({
69
+ trainX,
70
+ trainY,
71
+ testX,
72
+ testY,
73
+ configX,
74
+ keyNamesX,
75
+ })
76
+
77
+ classifiersExample({
78
+ trainX,
79
+ trainY,
80
+ testX,
81
+ testY,
82
+ configX,
83
+ keyNamesX,
84
+ })
85
+
86
+
87
+ }
88
+
89
+ const classifiersExample = ({
90
+ trainX,
91
+ trainY,
92
+ testX,
93
+ testY,
94
+ configX,
95
+ keyNamesX,
96
+ }) => {
97
+ const model = new KNN(trainX, trainY)
73
98
 
74
- //console.log('testX', testX.slice(-2))
99
+ const predictions = model.predict(testX)
100
+ const compare = ConfusionMatrix.fromLabels(testY.flat(), predictions.flat())
75
101
 
102
+ //console.log(testY.flat(), predictions.flat())
103
+
104
+ console.log(compare.getAccuracy())
105
+ }
106
+
107
+ const tensorflowExample = ({
108
+ trainX,
109
+ trainY,
110
+ testX,
111
+ testY,
112
+ configX,
113
+ keyNamesX,
114
+ }) => {
76
115
 
77
116
  const timeSteps = 10
78
117
  const colsX = trainX[0].length
@@ -80,16 +119,71 @@ const test = async () => {
80
119
  const timeSteppedTrainX = arrayToTimesteps(trainX, timeSteps)
81
120
  const trimedTrainY = trainY.slice(timeSteps-1)
82
121
 
83
-
84
- //console.log([trainX.length, timeSteps, timeSteppedTrainX[0][0].length])
85
-
86
122
  const inputX = tf.tensor3d(timeSteppedTrainX, [timeSteppedTrainX.length, timeSteps, colsX])
87
123
  const targetY = tf.tensor2d(trimedTrainY, [trimedTrainY.length, colsY])
88
124
 
89
- console.log('trainX', trainX[trainX.length - 1])
125
+ //console.log('trainX', trainX)
90
126
  //console.log('configX', configX)
91
127
  //console.log('inputX', inputX)
92
- //console.log('inputX', targetY)
128
+ //console.log('inputX', targetY)
129
+
130
+ }
131
+
132
+ //callback function used to prepare X before scaling
133
+ const xCallbackFunc = ({ objRow, index }) => {
134
+ const curr = objRow[index]
135
+ const prev = objRow[index - 1]
136
+
137
+ //returning null or undefined will exclude current row X and Y from training
138
+ if(typeof prev === 'undefined') return null
139
+
140
+ //console.log(((curr.sma_300 - curr.low) / curr.low) * 100)
141
+
142
+ const output = {
143
+ high: curr.high,
144
+ ema50IsUp: curr.ema_50 > prev.ema_50,
145
+ ema50GtSma200: curr.ema_50 > curr.sma_200,
146
+ ema50GtSma300: curr.ema_50 > curr.sma_300,
147
+ sma200IsUp: curr.sma_200 > prev.sma_200,
148
+ sma200GtSma300: curr.sma_200 > prev.sma_300,
149
+ sma_300IsUp: curr.sma_300 > prev.sma_300
150
+ }
151
+
152
+ for(const [key, value] of Object.entries(curr))
153
+ {
154
+ if(key.includes('minmax'))
155
+ {
156
+ output[key] = value
157
+ }
158
+ }
159
+
160
+ return output
161
+ }
162
+
163
+ //callback function used to prepare Y before scaling
164
+ const yCallbackFunc = ({ objRow, index }) => {
165
+ const curr = objRow[index]
166
+ const next = new Array(60).fill(0).map((_, i) => objRow[index + 1 + i])
167
+
168
+ //returning null or undefined will exclude current row X and Y from training
169
+ if (next.some(o => typeof o === 'undefined')) return null;
170
+
171
+ const priceTp = curr.sma_300 * 1.3
172
+ const entryPrice = curr.sma_300 * 0.96
173
+ const tp = next.some(o => o.close > entryPrice && (o.high > priceTp))
174
+ const sl = next.some(o => (o.low - entryPrice)/entryPrice < -0.10 && o.low < entryPrice)
175
+
176
+ const highestHigh = Math.max(...next.map(o => o.high))
177
+ const lowestLow = Math.min(...next.slice(0, 5).map(o => o.low))
178
+
179
+
180
+ if(lowestLow > entryPrice) return null
181
+
182
+ //console.log([curr.date, (lowestLow - entryPrice)/entryPrice])
183
+
184
+ return {
185
+ result: Number(tp === true && sl === false)
186
+ }
93
187
  }
94
188
 
95
189
  test()