xy-scale 1.3.5 → 1.3.7
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/xy-scale.min.js +1 -1
- package/package.json +2 -2
- package/src/correlation.js +66 -0
- package/src/datasets.js +38 -18
- package/src/validators.js +30 -0
- package/test/test.js +5 -2
package/dist/xy-scale.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var XY_Scale;(()=>{"use strict";var e={d:(t,n)=>{for(var
|
|
1
|
+
var XY_Scale;(()=>{"use strict";var e={d:(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},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:()=>l,parseProductionX:()=>u,parseTrainingXY:()=>i});const n=({arrObj:e,repeat:t={},minmaxRange:n,groups:o={},customMinMaxRanges:s=null,excludes:i=new Set})=>{const u=[...e],l=u.length,c=u[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),d={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 h=0;for(let e=0;e<d.inputKeyNames.length;e++)for(let t=0;t<d.repeatedKeyNames[e];t++)d.outputKeyNames[h++]=d.inputKeyNames[e];r(d.groups);const x=["number","boolean"];for(const e of d.inputKeyNames){if(i.has(e)){d.inputTypes[e]="excluded";continue}const t=typeof c[e],n=a(e,d.groups);if(!x.includes(t))throw new Error(`Invalid input type "${t}" provided for key "${e}". Only accepting `);d.inputTypes[e]=t,p&&s.hasOwnProperty(e)?n?d.groupMinMax[n]=s[e]:(d.min[e]=s[e].min,d.max[e]=s[e].max):n?d.groupMinMax[n]={min:1/0,max:-1/0}:(d.min[e]=1/0,d.max[e]=-1/0)}for(const e of u)for(const t of d.inputKeyNames){if("excluded"===d.inputTypes[t])continue;let n=e[t];"boolean"===d.inputTypes[t]&&(e[t]=Number(n));const r=a(t,d.groups);(!1===p||p&&!s.hasOwnProperty(t))&&(r?(d.groupMinMax[r].min=Math.min(d.groupMinMax[r].min,n),d.groupMinMax[r].max=Math.max(d.groupMinMax[r].max,n)):(d.min[t]=Math.min(d.min[t],n),d.max[t]=Math.max(d.max[t],n)))}const y=new Array(l);for(let e=0;e<l;e++){const t=u[e],n=new Array(d.outputKeyNames.length);let r=0;for(let e=0;e<d.inputKeyNames.length;e++){const o=d.inputKeyNames[e],s=t[o];if("excluded"===d.inputTypes[o]){n[r++]=s;continue}const i=a(o,d.groups);let u,l;i?(u=d.groupMinMax[i].min,l=d.groupMinMax[i].max):(u=d.min[o],l=d.max[o]);const c=l!==u?d.rangeMin+(s-u)/(l-u)*(d.rangeMax-d.rangeMin):d.rangeMin,p=d.repeatedKeyNames[e];for(let e=0;e<p;e++)n[r++]=c}y[e]=n}return{scaledOutput:y,scaledConfig:d}},r=e=>{const t=new Set,n=[];for(const[r,a]of Object.entries(e))t.add(r),n.push(r),a.forEach((e=>{t.add(e),n.push(e)}));if(t.size!==n.length)throw new Error("Duplicate value found between properties in validateUniqueProperties function.")},a=(e,t)=>{for(const[n,r]of Object.entries(t))if(r.includes(e))return n;return null},o=(e,{min:t=-1/0,max:n=1/0},r)=>{if(!Array.isArray(e))throw new Error(`Invalid property. "${r}" expected an array.`);if(e.length<t)throw new Error(`Invalid property value. Array "${r}" expected at least ${n} items.`);if(e.length>n)throw new Error(`Invalid property value. Array "${r}" expected at max ${n} items.`);return!0},s=e=>(Object.entries(e).forEach(((e,t)=>{if(null==t||Number.isNaN(t))throw new Error(`Invalid value at index ${e}: value is ${t}. Expected a defined, non-null, numeric value.`)})),!0),i=({arrObj:e=[],trainingSplit:t=.8,repeat:r={},yCallbackFunc:a=e=>e,xCallbackFunc:i=e=>e,validateRows:u=()=>!0,groups:l={},shuffle:c=!1,minmaxRange:p=[0,1],balancing:m="",state:f={},customMinMaxRanges:g={},excludes:d=[]})=>{let h=[],x=[];o(e,{min:5},"parseTrainingXY"),s(e[0]);for(let t=0;t<e.length;t++){if(!u({objRow:e,index:t,state:f}))continue;const n=i({objRow:e,index:t,state:f}),r=a({objRow:e,index:t,state:f});null!=n&&null!=r&&(h.push(n),x.push(r))}if(c){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]))}})(h,x);h=e,x=t}const y=new Set(d);let{scaledOutput:M,scaledConfig:b}=n({arrObj:h,repeat:r,groups:l,minmaxRange:p,customMinMaxRanges:g,excludes:y}),{scaledOutput:w,scaledConfig:O}=n({arrObj:x,repeat:r,groups:l,minmaxRange:p,customMinMaxRanges:g,excludes:y});const v=Math.floor(M.length*t);let j=M.slice(0,v),R=w.slice(0,v),X=M.slice(v),N=w.slice(v);if(m){let e;if("oversample"===m)e=((e,t)=>{const n={},r={};t.forEach(((a,o)=>{n[a]||(n[a]=0,r[a]=[]),n[a]++,r[a].push([e[o],t[o]])}));const a=Math.max(...Object.values(n)),o=[],s=[];return Object.keys(r).forEach((e=>{const t=r[e],n=t.length;for(let e=0;e<a;e++){const r=t[e%n];o.push(r[0]),s.push(r[1])}})),{X:o,Y:s}})(j,R),j=e.X,R=e.Y;else{if("undersample"!==m)throw Error('balancing argument only accepts "false", "oversample" and "undersample". Defaults to "false".');e=((e,t)=>{const n={},r={};t.forEach(((a,o)=>{n[a]||(n[a]=0,r[a]=[]),n[a]++,r[a].push([e[o],t[o]])}));const a=Math.min(...Object.values(n)),o=[],s=[];return Object.keys(r).forEach((e=>{const t=r[e];for(let e=0;e<a;e++){const n=t[e];o.push(n[0]),s.push(n[1])}})),{X:o,Y:s}})(j,R),j=e.X,R=e.Y}}return{trainX:j,trainY:R,testX:X,testY:N,configX:b,configY:O}},u=({arrObj:e=[],repeat:t={},xCallbackFunc:r=e=>e,validateRows:a=()=>!0,groups:i={},shuffle:u=!1,minmaxRange:l=[0,1],state:c={},customMinMaxRanges:p,excludes:m=[]})=>{let f=[];o(e,{min:5},"parseProductionX"),s(e[0]);for(let t=0;t<e.length;t++){if(!a(e[t]))continue;const n=r({objRow:e,index:t,state:c});null!=n&&!1!==n&&f.push(n)}u&&(f=(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})(f));const{scaledOutput:g,scaledConfig:d}=n({arrObj:f,repeat:t,groups:i,minmaxRange:l,customMinMaxRanges:p,excludes:new Set(m)});return{X:g,configX:d}},l=(e,t)=>{if(0===t)return e;if(t<0)throw new Error("timeSteps must be greater than 0");const n=[];for(let r=0;r<=e.length-t;r++)n.push(e.slice(r,r+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.7",
|
|
4
4
|
"main": "./index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"@tensorflow/tfjs-node": "^4.22.0",
|
|
15
15
|
"ml-confusion-matrix": "^2.0.0",
|
|
16
16
|
"ml-knn": "^3.0.0",
|
|
17
|
-
"ohlcv-indicators": "^3.4.
|
|
17
|
+
"ohlcv-indicators": "^3.4.7",
|
|
18
18
|
"webpack": "^5.96.1",
|
|
19
19
|
"webpack-cli": "^5.1.4"
|
|
20
20
|
},
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { sum } from "@tensorflow/tfjs-node";
|
|
2
|
+
|
|
3
|
+
export const analizeCorrelation = ({arrObj, corrSplit, corrExcludes, corrThreshold}) => {
|
|
4
|
+
const n = Math.floor(arrObj.length * corrSplit);
|
|
5
|
+
if (n < 2) return [];
|
|
6
|
+
|
|
7
|
+
const keys = Object.keys(arrObj[0]).filter(k => !corrExcludes.has(k));
|
|
8
|
+
const m = keys.length;
|
|
9
|
+
|
|
10
|
+
console.log({m})
|
|
11
|
+
|
|
12
|
+
// running sums, sums of squares, and cross‐sums
|
|
13
|
+
const sums = new Float64Array(m); // by default Float64Array are filled with 0
|
|
14
|
+
const sumsq = new Float64Array(m); // by default Float64Array are filled with 0
|
|
15
|
+
const sumxy = Array.from({ length: m }, () => new Float64Array(m));
|
|
16
|
+
|
|
17
|
+
// 1 pass: accumulate sums, sumsq, and sumxy[i][j] (i<j)
|
|
18
|
+
for (let r = 0; r < n; r++) {
|
|
19
|
+
const row = arrObj[r];
|
|
20
|
+
for (let i = 0; i < m; i++) {
|
|
21
|
+
|
|
22
|
+
const xi = row[keys[i]];
|
|
23
|
+
|
|
24
|
+
//errror here, the code neve reaches this part
|
|
25
|
+
|
|
26
|
+
sums[i] += xi;
|
|
27
|
+
sumsq[i] += xi * xi;
|
|
28
|
+
for (let j = i + 1; j < m; j++) {
|
|
29
|
+
sumxy[i][j] += xi * row[keys[j]];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// precompute denominators: n*Σx² − (Σx)²
|
|
35
|
+
const denom = new Float64Array(m);
|
|
36
|
+
for (let i = 0; i < m; i++) {
|
|
37
|
+
denom[i] = n * sumsq[i] - sums[i] * sums[i];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// build result pairs
|
|
41
|
+
// collect pairs below/above your threshold
|
|
42
|
+
let lowCorrelationPairs = `Low Correlation Pairs (< ${corrThreshold}):`;
|
|
43
|
+
let highCorrelationPairs = `High Correlation Pairs (>= ${corrThreshold}):`;
|
|
44
|
+
|
|
45
|
+
for (let i = 0; i < m; i++) {
|
|
46
|
+
for (let j = i + 1; j < m; j++) {
|
|
47
|
+
const cov = n * sumxy[i][j] - sums[i] * sums[j];
|
|
48
|
+
const corr = cov / Math.sqrt(denom[i] * denom[j]);
|
|
49
|
+
const pair = `\n${keys[i]} - ${keys[j]}: ${corr}`
|
|
50
|
+
|
|
51
|
+
if(corr < corrThreshold)
|
|
52
|
+
{
|
|
53
|
+
lowCorrelationPairs += pair
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
highCorrelationPairs += pair
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
console.log(lowCorrelationPairs)
|
|
62
|
+
console.log('---')
|
|
63
|
+
console.log(highCorrelationPairs)
|
|
64
|
+
|
|
65
|
+
return true;
|
|
66
|
+
};
|
package/src/datasets.js
CHANGED
|
@@ -1,25 +1,38 @@
|
|
|
1
1
|
import { scaleArrayObj } from "./scale.js";
|
|
2
2
|
import { arrayShuffle, xyArrayShuffle } from "./utilities.js";
|
|
3
3
|
import { oversampleXY, undersampleXY } from "./balancing.js";
|
|
4
|
+
import { validateFirstRow, validateArray } from "./validators.js";
|
|
5
|
+
import { analizeCorrelation } from "./correlation.js";
|
|
6
|
+
import { correlation } from "ohlcv-indicators/src/studies/correlation.js";
|
|
7
|
+
|
|
8
|
+
//ADD A PARAM max correlation that will measure the correlation between variables if defined
|
|
4
9
|
|
|
5
10
|
export const parseTrainingXY = ({
|
|
6
|
-
arrObj = [],
|
|
7
|
-
trainingSplit = 0.8,
|
|
8
|
-
repeat = {},
|
|
9
|
-
yCallbackFunc = row => row,
|
|
10
|
-
xCallbackFunc = row => row,
|
|
11
|
-
validateRows = () => true
|
|
12
|
-
groups = {}
|
|
13
|
-
shuffle = false
|
|
11
|
+
arrObj = [], //array of objects
|
|
12
|
+
trainingSplit = 0.8, //numberic float between 0.01 and 0.99
|
|
13
|
+
repeat = {}, //accepted key pair object with number as values
|
|
14
|
+
yCallbackFunc = row => row, //accepted callback functions
|
|
15
|
+
xCallbackFunc = row => row, //accepted callback functions
|
|
16
|
+
validateRows = () => true,//accepted callback functions
|
|
17
|
+
groups = {},//accepted object of arrays
|
|
18
|
+
shuffle = false,//only booleans
|
|
14
19
|
minmaxRange = [0, 1],
|
|
15
|
-
balancing = '',
|
|
16
|
-
state = {},
|
|
20
|
+
balancing = '',//accepted null, "oversample" or "undersample"
|
|
21
|
+
state = {}, //accepted object or classes
|
|
17
22
|
customMinMaxRanges = {},
|
|
18
|
-
excludes = []
|
|
23
|
+
excludes = [],//each item must be a string
|
|
24
|
+
correlation = {}
|
|
19
25
|
}) => {
|
|
20
26
|
let X = [];
|
|
21
27
|
let Y = [];
|
|
22
28
|
|
|
29
|
+
validateArray(arrObj, {min: 5}, 'parseTrainingXY')
|
|
30
|
+
validateFirstRow(arrObj[0])
|
|
31
|
+
|
|
32
|
+
const {corrSplit = 0.2, corrExcludes = [], corrThreshold = 0.95} = correlation
|
|
33
|
+
|
|
34
|
+
analizeCorrelation({arrObj, corrSplit, corrExcludes: new Set(corrExcludes), corrThreshold})
|
|
35
|
+
|
|
23
36
|
//if parsedX and parsedY is undefined or null the current row will be excluded from training or production
|
|
24
37
|
for (let x = 0; x < arrObj.length; x++) {
|
|
25
38
|
|
|
@@ -48,21 +61,25 @@ export const parseTrainingXY = ({
|
|
|
48
61
|
scaledConfig: configX
|
|
49
62
|
} = scaleArrayObj({arrObj: X, repeat, groups, minmaxRange, customMinMaxRanges, excludes: excludesSet})
|
|
50
63
|
|
|
51
|
-
let {
|
|
52
|
-
scaledOutput: scaledY,
|
|
53
|
-
scaledConfig: configY,
|
|
54
|
-
} = scaleArrayObj({arrObj: Y, repeat, groups, minmaxRange, customMinMaxRanges, excludes: excludesSet})
|
|
55
|
-
|
|
56
64
|
|
|
65
|
+
const yLen = Y.length
|
|
66
|
+
const flatY = new Array(yLen)
|
|
67
|
+
const configY = {
|
|
68
|
+
keyNames: Object.keys(Y[0])
|
|
69
|
+
}
|
|
57
70
|
|
|
71
|
+
for(let idx = 0; idx < yLen; idx++)
|
|
72
|
+
{
|
|
73
|
+
flatY[idx] = Object.values(Y[idx])
|
|
74
|
+
}
|
|
58
75
|
|
|
59
76
|
|
|
60
77
|
const splitIndex = Math.floor(scaledX.length * trainingSplit)
|
|
61
78
|
|
|
62
79
|
let trainX = scaledX.slice(0, splitIndex)
|
|
63
|
-
let trainY =
|
|
80
|
+
let trainY = flatY.slice(0, splitIndex)
|
|
64
81
|
let testX = scaledX.slice(splitIndex)
|
|
65
|
-
let testY =
|
|
82
|
+
let testY = flatY.slice(splitIndex)
|
|
66
83
|
|
|
67
84
|
|
|
68
85
|
if(balancing)
|
|
@@ -114,6 +131,9 @@ export const parseProductionX = ({
|
|
|
114
131
|
}) => {
|
|
115
132
|
let X = [];
|
|
116
133
|
|
|
134
|
+
validateArray(arrObj, {min: 5}, 'parseProductionX')
|
|
135
|
+
validateFirstRow(arrObj[0])
|
|
136
|
+
|
|
117
137
|
for (let x = 0; x < arrObj.length; x++) {
|
|
118
138
|
|
|
119
139
|
if(!validateRows(arrObj[x])) continue
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const validateArray = (arr, {min = -Infinity, max = Infinity}, paramName) => {
|
|
2
|
+
|
|
3
|
+
if(!Array.isArray(arr))
|
|
4
|
+
{
|
|
5
|
+
throw new Error(`Invalid property. "${paramName}" expected an array.`)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if(arr.length < min)
|
|
9
|
+
{
|
|
10
|
+
throw new Error(`Invalid property value. Array "${paramName}" expected at least ${max} items.`)
|
|
11
|
+
}
|
|
12
|
+
if(arr.length > max)
|
|
13
|
+
{
|
|
14
|
+
throw new Error(`Invalid property value. Array "${paramName}" expected at max ${max} items.`)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return true
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const validateFirstRow = row => {
|
|
21
|
+
|
|
22
|
+
for(const [k, v] of Object.entries(row))
|
|
23
|
+
{
|
|
24
|
+
if (typeof v !== 'number' || Number.isNaN(v)) {
|
|
25
|
+
throw new Error(`Invalid value at index 0 property "${k}": value is "${v}". Expected a numeric value.`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return true
|
|
30
|
+
}
|
package/test/test.js
CHANGED
|
@@ -27,7 +27,9 @@ const test = async () => {
|
|
|
27
27
|
{fast: 'price', slow: 'bollinger_bands_upper'}
|
|
28
28
|
])
|
|
29
29
|
|
|
30
|
-
const parsedOhlcv = indicators.getData()
|
|
30
|
+
const parsedOhlcv = indicators.getData({dateFormat: 'milliseconds'})
|
|
31
|
+
|
|
32
|
+
console.log(parsedOhlcv.slice(-1))
|
|
31
33
|
|
|
32
34
|
const {scaledGroups} = indicators
|
|
33
35
|
|
|
@@ -57,7 +59,8 @@ const test = async () => {
|
|
|
57
59
|
minmaxRange: [0, 1],
|
|
58
60
|
balancing: null,
|
|
59
61
|
groups: scaledGroups,
|
|
60
|
-
excludes: ['high']
|
|
62
|
+
excludes: ['high'],
|
|
63
|
+
correlation: {corrExcludes: ['price_x_sma_300', 'price_x_sma_200']}
|
|
61
64
|
});
|
|
62
65
|
|
|
63
66
|
//console.log(configX.outputKeyNames)
|