ohlcv-indicators 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ohlcv-indicators.min.js +1 -0
- package/index.js +147 -0
- package/package.json +18 -0
- package/src/moving-averages/bollingerBands.js +53 -0
- package/src/moving-averages/ema.js +55 -0
- package/src/moving-averages/ichimokuCloud.js +63 -0
- package/src/moving-averages/macd.js +38 -0
- package/src/moving-averages/relativeVolume.js +17 -0
- package/src/moving-averages/sma.js +34 -0
- package/src/oscillators/mfi.js +62 -0
- package/src/oscillators/rsi.js +74 -0
- package/src/utilities.js +47 -0
- package/test/fetchLiveData.js +60 -0
- package/test/test.js +51 -0
- package/webpack.config.js +13 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var OHLCV_INDICATORS;(()=>{"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,{default:()=>S});const n=(e,t,n)=>{const r=e(2/(n+1)),i=t.length,s=[];if(r.isGreaterThan(1))return Array(i);if(r.isEqualTo(1))return[...t];let o=0,l=0;for(;l<i;l++){const e=t[l];if("object"==typeof e){s[l]=e,o=e,l++;break}}if(Array.isArray(r))for(;l<i;l++){const e=t[l];"object"==typeof e&&"object"==typeof r[l]?o=s[l]=r[l].multipliedBy(e).plus(r[l].minus(1).multipliedBy(o)):s[l]=s[l-1]}else{const n=e(1).minus(r);for(;l<i;l++){const e=t[l];o="object"==typeof e?r.multipliedBy(e).plus(n.multipliedBy(o)):s[l-1],s[l]=o}}return s},r=(e,t,n)=>{let r=[],i=e(0),s=[];for(let e=0;e<t.length;e++)s.push(t[e]),i=i.plus(t[e]),s.length>n&&(i=i.minus(s.shift())),e>=n-1?r.push(i.dividedBy(n)):r.push(i.dividedBy(s.length));return r},i=(e,t,n)=>{let r=new Array(t.length).fill(0),i=0;const s=new e(t[0]),o=new e(n[0]);s.isGreaterThan(o)?(i=1,r[0]=1):s.isLessThan(o)?(i=-1,r[0]=-1):(i=0,r[0]=0);for(let s=1;s<t.length;s++){const o=new e(t[s]),l=new e(n[s]),u=new e(t[s-1]),c=new e(n[s-1]);o.isGreaterThan(l)&&u.isLessThanOrEqualTo(c)?(i=1,r[s]=1):o.isLessThan(l)&&u.isGreaterThanOrEqualTo(c)?(i=-1,r[s]=-1):r[s]=1===i?r[s-1]+1:-1===i?r[s-1]-1:0}return r.map((t=>e(t)))},s=(e,t,n)=>{const i=new e(0),s=t.length,o=r(e,t,n),l=new Array(s).fill(i);for(let e=n-1;e<s;e++){let r=i;for(let i=e-n+1;i<=e;i++)r=r.plus(t[i].minus(o[e]).pow(2));l[e]=r.dividedBy(n).sqrt()}return l},o=(e,t,n,r)=>t.slice(r).map(((i,s)=>{const o=t.slice(s,s+r),l=n.slice(s,s+r);return e.maximum(...o).plus(e.minimum(...l)).dividedBy(2)})),l=(e,t,n,r)=>{const i=e.slice(n).map(((e,n)=>n<t.length?e.plus(t[n]).dividedBy(2):null)).filter((e=>null!==e));return i.slice(0,i.length-r+1)},u=(e,t,n,r,i)=>{let s=t.slice(r).map(((i,s)=>{const o=t.slice(s,s+r),l=n.slice(s,s+r);return e.maximum(...o).plus(e.minimum(...l)).dividedBy(2)}));return s.slice(0,s.length-i+1)},c=(e,t)=>e.slice(0,e.length-t);var f=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,a=Math.ceil,h=Math.floor,p="[BigNumber Error] ",g=p+"Number primitive has more than 15 significant digits: ",d=1e14,m=14,w=9007199254740991,v=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],b=1e7,y=1e9;function N(e){var t=0|e;return e>0||e===t?t:t-1}function O(e){for(var t,n,r=1,i=e.length,s=e[0]+"";r<i;){for(t=e[r++]+"",n=m-t.length;n--;t="0"+t);s+=t}for(i=s.length;48===s.charCodeAt(--i););return s.slice(0,i+1||1)}function B(e,t){var n,r,i=e.c,s=t.c,o=e.s,l=t.s,u=e.e,c=t.e;if(!o||!l)return null;if(n=i&&!i[0],r=s&&!s[0],n||r)return n?r?0:-l:o;if(o!=l)return o;if(n=o<0,r=u==c,!i||!s)return r?0:!i^n?1:-1;if(!r)return u>c^n?1:-1;for(l=(u=i.length)<(c=s.length)?u:c,o=0;o<l;o++)if(i[o]!=s[o])return i[o]>s[o]^n?1:-1;return u==c?0:u>c^n?1:-1}function E(e,t,n,r){if(e<t||e>n||e!==h(e))throw Error(p+(r||"Argument")+("number"==typeof e?e<t||e>n?" out of range: ":" not an integer: ":" not a primitive number: ")+String(e))}function A(e){var t=e.c.length-1;return N(e.e/m)==t&&e.c[t]%2!=0}function _(e,t){return(e.length>1?e.charAt(0)+"."+e.slice(1):e)+(t<0?"e":"e+")+t}function C(e,t,n){var r,i;if(t<0){for(i=n+".";++t;i+=n);e=i+e}else if(++t>(r=e.length)){for(i=n,t-=r;--t;i+=n);e+=i}else t<r&&(e=e.slice(0,t)+"."+e.slice(t));return e}const T=function e(t){var n,r,i,s,o,l,u,c,T,S,D=V.prototype={constructor:V,toString:null,valueOf:null},I=new V(1),P=20,M=4,L=-7,R=21,x=-1e7,$=1e7,U=!1,G=1,j=0,k={prefix:"",groupSize:3,secondaryGroupSize:0,groupSeparator:",",decimalSeparator:".",fractionGroupSize:0,fractionGroupSeparator:" ",suffix:""},q="0123456789abcdefghijklmnopqrstuvwxyz",F=!0;function V(e,t){var n,s,o,l,u,c,a,p,d=this;if(!(d instanceof V))return new V(e,t);if(null==t){if(e&&!0===e._isBigNumber)return d.s=e.s,void(!e.c||e.e>$?d.c=d.e=null:e.e<x?d.c=[d.e=0]:(d.e=e.e,d.c=e.c.slice()));if((c="number"==typeof e)&&0*e==0){if(d.s=1/e<0?(e=-e,-1):1,e===~~e){for(l=0,u=e;u>=10;u/=10,l++);return void(l>$?d.c=d.e=null:(d.e=l,d.c=[e]))}p=String(e)}else{if(!f.test(p=String(e)))return i(d,p,c);d.s=45==p.charCodeAt(0)?(p=p.slice(1),-1):1}(l=p.indexOf("."))>-1&&(p=p.replace(".","")),(u=p.search(/e/i))>0?(l<0&&(l=u),l+=+p.slice(u+1),p=p.substring(0,u)):l<0&&(l=p.length)}else{if(E(t,2,q.length,"Base"),10==t&&F)return X(d=new V(e),P+d.e+1,M);if(p=String(e),c="number"==typeof e){if(0*e!=0)return i(d,p,c,t);if(d.s=1/e<0?(p=p.slice(1),-1):1,V.DEBUG&&p.replace(/^0\.0*|\./,"").length>15)throw Error(g+e)}else d.s=45===p.charCodeAt(0)?(p=p.slice(1),-1):1;for(n=q.slice(0,t),l=u=0,a=p.length;u<a;u++)if(n.indexOf(s=p.charAt(u))<0){if("."==s){if(u>l){l=a;continue}}else if(!o&&(p==p.toUpperCase()&&(p=p.toLowerCase())||p==p.toLowerCase()&&(p=p.toUpperCase()))){o=!0,u=-1,l=0;continue}return i(d,String(e),c,t)}c=!1,(l=(p=r(p,t,10,d.s)).indexOf("."))>-1?p=p.replace(".",""):l=p.length}for(u=0;48===p.charCodeAt(u);u++);for(a=p.length;48===p.charCodeAt(--a););if(p=p.slice(u,++a)){if(a-=u,c&&V.DEBUG&&a>15&&(e>w||e!==h(e)))throw Error(g+d.s*e);if((l=l-u-1)>$)d.c=d.e=null;else if(l<x)d.c=[d.e=0];else{if(d.e=l,d.c=[],u=(l+1)%m,l<0&&(u+=m),u<a){for(u&&d.c.push(+p.slice(0,u)),a-=m;u<a;)d.c.push(+p.slice(u,u+=m));u=m-(p=p.slice(u)).length}else u-=a;for(;u--;p+="0");d.c.push(+p)}}else d.c=[d.e=0]}function H(e,t,n,r){var i,s,o,l,u;if(null==n?n=M:E(n,0,8),!e.c)return e.toString();if(i=e.c[0],o=e.e,null==t)u=O(e.c),u=1==r||2==r&&(o<=L||o>=R)?_(u,o):C(u,o,"0");else if(s=(e=X(new V(e),t,n)).e,l=(u=O(e.c)).length,1==r||2==r&&(t<=s||s<=L)){for(;l<t;u+="0",l++);u=_(u,s)}else if(t-=o,u=C(u,s,"0"),s+1>l){if(--t>0)for(u+=".";t--;u+="0");}else if((t+=s-l)>0)for(s+1==l&&(u+=".");t--;u+="0");return e.s<0&&i?"-"+u:u}function z(e,t){for(var n,r,i=1,s=new V(e[0]);i<e.length;i++)(!(r=new V(e[i])).s||(n=B(s,r))===t||0===n&&s.s===t)&&(s=r);return s}function W(e,t,n){for(var r=1,i=t.length;!t[--i];t.pop());for(i=t[0];i>=10;i/=10,r++);return(n=r+n*m-1)>$?e.c=e.e=null:n<x?e.c=[e.e=0]:(e.e=n,e.c=t),e}function X(e,t,n,r){var i,s,o,l,u,c,f,p=e.c,g=v;if(p){e:{for(i=1,l=p[0];l>=10;l/=10,i++);if((s=t-i)<0)s+=m,o=t,u=p[c=0],f=h(u/g[i-o-1]%10);else if((c=a((s+1)/m))>=p.length){if(!r)break e;for(;p.length<=c;p.push(0));u=f=0,i=1,o=(s%=m)-m+1}else{for(u=l=p[c],i=1;l>=10;l/=10,i++);f=(o=(s%=m)-m+i)<0?0:h(u/g[i-o-1]%10)}if(r=r||t<0||null!=p[c+1]||(o<0?u:u%g[i-o-1]),r=n<4?(f||r)&&(0==n||n==(e.s<0?3:2)):f>5||5==f&&(4==n||r||6==n&&(s>0?o>0?u/g[i-o]:0:p[c-1])%10&1||n==(e.s<0?8:7)),t<1||!p[0])return p.length=0,r?(t-=e.e+1,p[0]=g[(m-t%m)%m],e.e=-t||0):p[0]=e.e=0,e;if(0==s?(p.length=c,l=1,c--):(p.length=c+1,l=g[m-s],p[c]=o>0?h(u/g[i-o]%g[o])*l:0),r)for(;;){if(0==c){for(s=1,o=p[0];o>=10;o/=10,s++);for(o=p[0]+=l,l=1;o>=10;o/=10,l++);s!=l&&(e.e++,p[0]==d&&(p[0]=1));break}if(p[c]+=l,p[c]!=d)break;p[c--]=0,l=1}for(s=p.length;0===p[--s];p.pop());}e.e>$?e.c=e.e=null:e.e<x&&(e.c=[e.e=0])}return e}function Y(e){var t,n=e.e;return null===n?e.toString():(t=O(e.c),t=n<=L||n>=R?_(t,n):C(t,n,"0"),e.s<0?"-"+t:t)}return V.clone=e,V.ROUND_UP=0,V.ROUND_DOWN=1,V.ROUND_CEIL=2,V.ROUND_FLOOR=3,V.ROUND_HALF_UP=4,V.ROUND_HALF_DOWN=5,V.ROUND_HALF_EVEN=6,V.ROUND_HALF_CEIL=7,V.ROUND_HALF_FLOOR=8,V.EUCLID=9,V.config=V.set=function(e){var t,n;if(null!=e){if("object"!=typeof e)throw Error(p+"Object expected: "+e);if(e.hasOwnProperty(t="DECIMAL_PLACES")&&(E(n=e[t],0,y,t),P=n),e.hasOwnProperty(t="ROUNDING_MODE")&&(E(n=e[t],0,8,t),M=n),e.hasOwnProperty(t="EXPONENTIAL_AT")&&((n=e[t])&&n.pop?(E(n[0],-y,0,t),E(n[1],0,y,t),L=n[0],R=n[1]):(E(n,-y,y,t),L=-(R=n<0?-n:n))),e.hasOwnProperty(t="RANGE"))if((n=e[t])&&n.pop)E(n[0],-y,-1,t),E(n[1],1,y,t),x=n[0],$=n[1];else{if(E(n,-y,y,t),!n)throw Error(p+t+" cannot be zero: "+n);x=-($=n<0?-n:n)}if(e.hasOwnProperty(t="CRYPTO")){if((n=e[t])!==!!n)throw Error(p+t+" not true or false: "+n);if(n){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw U=!n,Error(p+"crypto unavailable");U=n}else U=n}if(e.hasOwnProperty(t="MODULO_MODE")&&(E(n=e[t],0,9,t),G=n),e.hasOwnProperty(t="POW_PRECISION")&&(E(n=e[t],0,y,t),j=n),e.hasOwnProperty(t="FORMAT")){if("object"!=typeof(n=e[t]))throw Error(p+t+" not an object: "+n);k=n}if(e.hasOwnProperty(t="ALPHABET")){if("string"!=typeof(n=e[t])||/^.?$|[+\-.\s]|(.).*\1/.test(n))throw Error(p+t+" invalid: "+n);F="0123456789"==n.slice(0,10),q=n}}return{DECIMAL_PLACES:P,ROUNDING_MODE:M,EXPONENTIAL_AT:[L,R],RANGE:[x,$],CRYPTO:U,MODULO_MODE:G,POW_PRECISION:j,FORMAT:k,ALPHABET:q}},V.isBigNumber=function(e){if(!e||!0!==e._isBigNumber)return!1;if(!V.DEBUG)return!0;var t,n,r=e.c,i=e.e,s=e.s;e:if("[object Array]"=={}.toString.call(r)){if((1===s||-1===s)&&i>=-y&&i<=y&&i===h(i)){if(0===r[0]){if(0===i&&1===r.length)return!0;break e}if((t=(i+1)%m)<1&&(t+=m),String(r[0]).length==t){for(t=0;t<r.length;t++)if((n=r[t])<0||n>=d||n!==h(n))break e;if(0!==n)return!0}}}else if(null===r&&null===i&&(null===s||1===s||-1===s))return!0;throw Error(p+"Invalid BigNumber: "+e)},V.maximum=V.max=function(){return z(arguments,-1)},V.minimum=V.min=function(){return z(arguments,1)},V.random=(s=9007199254740992,o=Math.random()*s&2097151?function(){return h(Math.random()*s)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(e){var t,n,r,i,s,l=0,u=[],c=new V(I);if(null==e?e=P:E(e,0,y),i=a(e/m),U)if(crypto.getRandomValues){for(t=crypto.getRandomValues(new Uint32Array(i*=2));l<i;)(s=131072*t[l]+(t[l+1]>>>11))>=9e15?(n=crypto.getRandomValues(new Uint32Array(2)),t[l]=n[0],t[l+1]=n[1]):(u.push(s%1e14),l+=2);l=i/2}else{if(!crypto.randomBytes)throw U=!1,Error(p+"crypto unavailable");for(t=crypto.randomBytes(i*=7);l<i;)(s=281474976710656*(31&t[l])+1099511627776*t[l+1]+4294967296*t[l+2]+16777216*t[l+3]+(t[l+4]<<16)+(t[l+5]<<8)+t[l+6])>=9e15?crypto.randomBytes(7).copy(t,l):(u.push(s%1e14),l+=7);l=i/7}if(!U)for(;l<i;)(s=o())<9e15&&(u[l++]=s%1e14);for(i=u[--l],e%=m,i&&e&&(s=v[m-e],u[l]=h(i/s)*s);0===u[l];u.pop(),l--);if(l<0)u=[r=0];else{for(r=-1;0===u[0];u.splice(0,1),r-=m);for(l=1,s=u[0];s>=10;s/=10,l++);l<m&&(r-=m-l)}return c.e=r,c.c=u,c}),V.sum=function(){for(var e=1,t=arguments,n=new V(t[0]);e<t.length;)n=n.plus(t[e++]);return n},r=function(){var e="0123456789";function t(e,t,n,r){for(var i,s,o=[0],l=0,u=e.length;l<u;){for(s=o.length;s--;o[s]*=t);for(o[0]+=r.indexOf(e.charAt(l++)),i=0;i<o.length;i++)o[i]>n-1&&(null==o[i+1]&&(o[i+1]=0),o[i+1]+=o[i]/n|0,o[i]%=n)}return o.reverse()}return function(r,i,s,o,l){var u,c,f,a,h,p,g,d,m=r.indexOf("."),w=P,v=M;for(m>=0&&(a=j,j=0,r=r.replace(".",""),p=(d=new V(i)).pow(r.length-m),j=a,d.c=t(C(O(p.c),p.e,"0"),10,s,e),d.e=d.c.length),f=a=(g=t(r,i,s,l?(u=q,e):(u=e,q))).length;0==g[--a];g.pop());if(!g[0])return u.charAt(0);if(m<0?--f:(p.c=g,p.e=f,p.s=o,g=(p=n(p,d,w,v,s)).c,h=p.r,f=p.e),m=g[c=f+w+1],a=s/2,h=h||c<0||null!=g[c+1],h=v<4?(null!=m||h)&&(0==v||v==(p.s<0?3:2)):m>a||m==a&&(4==v||h||6==v&&1&g[c-1]||v==(p.s<0?8:7)),c<1||!g[0])r=h?C(u.charAt(1),-w,u.charAt(0)):u.charAt(0);else{if(g.length=c,h)for(--s;++g[--c]>s;)g[c]=0,c||(++f,g=[1].concat(g));for(a=g.length;!g[--a];);for(m=0,r="";m<=a;r+=u.charAt(g[m++]));r=C(r,f,u.charAt(0))}return r}}(),n=function(){function e(e,t,n){var r,i,s,o,l=0,u=e.length,c=t%b,f=t/b|0;for(e=e.slice();u--;)l=((i=c*(s=e[u]%b)+(r=f*s+(o=e[u]/b|0)*c)%b*b+l)/n|0)+(r/b|0)+f*o,e[u]=i%n;return l&&(e=[l].concat(e)),e}function t(e,t,n,r){var i,s;if(n!=r)s=n>r?1:-1;else for(i=s=0;i<n;i++)if(e[i]!=t[i]){s=e[i]>t[i]?1:-1;break}return s}function n(e,t,n,r){for(var i=0;n--;)e[n]-=i,i=e[n]<t[n]?1:0,e[n]=i*r+e[n]-t[n];for(;!e[0]&&e.length>1;e.splice(0,1));}return function(r,i,s,o,l){var u,c,f,a,p,g,w,v,b,y,O,B,E,A,_,C,T,S=r.s==i.s?1:-1,D=r.c,I=i.c;if(!(D&&D[0]&&I&&I[0]))return new V(r.s&&i.s&&(D?!I||D[0]!=I[0]:I)?D&&0==D[0]||!I?0*S:S/0:NaN);for(b=(v=new V(S)).c=[],S=s+(c=r.e-i.e)+1,l||(l=d,c=N(r.e/m)-N(i.e/m),S=S/m|0),f=0;I[f]==(D[f]||0);f++);if(I[f]>(D[f]||0)&&c--,S<0)b.push(1),a=!0;else{for(A=D.length,C=I.length,f=0,S+=2,(p=h(l/(I[0]+1)))>1&&(I=e(I,p,l),D=e(D,p,l),C=I.length,A=D.length),E=C,O=(y=D.slice(0,C)).length;O<C;y[O++]=0);T=I.slice(),T=[0].concat(T),_=I[0],I[1]>=l/2&&_++;do{if(p=0,(u=t(I,y,C,O))<0){if(B=y[0],C!=O&&(B=B*l+(y[1]||0)),(p=h(B/_))>1)for(p>=l&&(p=l-1),w=(g=e(I,p,l)).length,O=y.length;1==t(g,y,w,O);)p--,n(g,C<w?T:I,w,l),w=g.length,u=1;else 0==p&&(u=p=1),w=(g=I.slice()).length;if(w<O&&(g=[0].concat(g)),n(y,g,O,l),O=y.length,-1==u)for(;t(I,y,C,O)<1;)p++,n(y,C<O?T:I,O,l),O=y.length}else 0===u&&(p++,y=[0]);b[f++]=p,y[0]?y[O++]=D[E]||0:(y=[D[E]],O=1)}while((E++<A||null!=y[0])&&S--);a=null!=y[0],b[0]||b.splice(0,1)}if(l==d){for(f=1,S=b[0];S>=10;S/=10,f++);X(v,s+(v.e=f+c*m-1)+1,o,a)}else v.e=c,v.r=+a;return v}}(),l=/^(-?)0([xbo])(?=\w[\w.]*$)/i,u=/^([^.]+)\.$/,c=/^\.([^.]+)$/,T=/^-?(Infinity|NaN)$/,S=/^\s*\+(?=[\w.])|^\s+|\s+$/g,i=function(e,t,n,r){var i,s=n?t:t.replace(S,"");if(T.test(s))e.s=isNaN(s)?null:s<0?-1:1;else{if(!n&&(s=s.replace(l,(function(e,t,n){return i="x"==(n=n.toLowerCase())?16:"b"==n?2:8,r&&r!=i?e:t})),r&&(i=r,s=s.replace(u,"$1").replace(c,"0.$1")),t!=s))return new V(s,i);if(V.DEBUG)throw Error(p+"Not a"+(r?" base "+r:"")+" number: "+t);e.s=null}e.c=e.e=null},D.absoluteValue=D.abs=function(){var e=new V(this);return e.s<0&&(e.s=1),e},D.comparedTo=function(e,t){return B(this,new V(e,t))},D.decimalPlaces=D.dp=function(e,t){var n,r,i,s=this;if(null!=e)return E(e,0,y),null==t?t=M:E(t,0,8),X(new V(s),e+s.e+1,t);if(!(n=s.c))return null;if(r=((i=n.length-1)-N(this.e/m))*m,i=n[i])for(;i%10==0;i/=10,r--);return r<0&&(r=0),r},D.dividedBy=D.div=function(e,t){return n(this,new V(e,t),P,M)},D.dividedToIntegerBy=D.idiv=function(e,t){return n(this,new V(e,t),0,1)},D.exponentiatedBy=D.pow=function(e,t){var n,r,i,s,o,l,u,c,f=this;if((e=new V(e)).c&&!e.isInteger())throw Error(p+"Exponent not an integer: "+Y(e));if(null!=t&&(t=new V(t)),o=e.e>14,!f.c||!f.c[0]||1==f.c[0]&&!f.e&&1==f.c.length||!e.c||!e.c[0])return c=new V(Math.pow(+Y(f),o?e.s*(2-A(e)):+Y(e))),t?c.mod(t):c;if(l=e.s<0,t){if(t.c?!t.c[0]:!t.s)return new V(NaN);(r=!l&&f.isInteger()&&t.isInteger())&&(f=f.mod(t))}else{if(e.e>9&&(f.e>0||f.e<-1||(0==f.e?f.c[0]>1||o&&f.c[1]>=24e7:f.c[0]<8e13||o&&f.c[0]<=9999975e7)))return s=f.s<0&&A(e)?-0:0,f.e>-1&&(s=1/s),new V(l?1/s:s);j&&(s=a(j/m+2))}for(o?(n=new V(.5),l&&(e.s=1),u=A(e)):u=(i=Math.abs(+Y(e)))%2,c=new V(I);;){if(u){if(!(c=c.times(f)).c)break;s?c.c.length>s&&(c.c.length=s):r&&(c=c.mod(t))}if(i){if(0===(i=h(i/2)))break;u=i%2}else if(X(e=e.times(n),e.e+1,1),e.e>14)u=A(e);else{if(0==(i=+Y(e)))break;u=i%2}f=f.times(f),s?f.c&&f.c.length>s&&(f.c.length=s):r&&(f=f.mod(t))}return r?c:(l&&(c=I.div(c)),t?c.mod(t):s?X(c,j,M,void 0):c)},D.integerValue=function(e){var t=new V(this);return null==e?e=M:E(e,0,8),X(t,t.e+1,e)},D.isEqualTo=D.eq=function(e,t){return 0===B(this,new V(e,t))},D.isFinite=function(){return!!this.c},D.isGreaterThan=D.gt=function(e,t){return B(this,new V(e,t))>0},D.isGreaterThanOrEqualTo=D.gte=function(e,t){return 1===(t=B(this,new V(e,t)))||0===t},D.isInteger=function(){return!!this.c&&N(this.e/m)>this.c.length-2},D.isLessThan=D.lt=function(e,t){return B(this,new V(e,t))<0},D.isLessThanOrEqualTo=D.lte=function(e,t){return-1===(t=B(this,new V(e,t)))||0===t},D.isNaN=function(){return!this.s},D.isNegative=function(){return this.s<0},D.isPositive=function(){return this.s>0},D.isZero=function(){return!!this.c&&0==this.c[0]},D.minus=function(e,t){var n,r,i,s,o=this,l=o.s;if(t=(e=new V(e,t)).s,!l||!t)return new V(NaN);if(l!=t)return e.s=-t,o.plus(e);var u=o.e/m,c=e.e/m,f=o.c,a=e.c;if(!u||!c){if(!f||!a)return f?(e.s=-t,e):new V(a?o:NaN);if(!f[0]||!a[0])return a[0]?(e.s=-t,e):new V(f[0]?o:3==M?-0:0)}if(u=N(u),c=N(c),f=f.slice(),l=u-c){for((s=l<0)?(l=-l,i=f):(c=u,i=a),i.reverse(),t=l;t--;i.push(0));i.reverse()}else for(r=(s=(l=f.length)<(t=a.length))?l:t,l=t=0;t<r;t++)if(f[t]!=a[t]){s=f[t]<a[t];break}if(s&&(i=f,f=a,a=i,e.s=-e.s),(t=(r=a.length)-(n=f.length))>0)for(;t--;f[n++]=0);for(t=d-1;r>l;){if(f[--r]<a[r]){for(n=r;n&&!f[--n];f[n]=t);--f[n],f[r]+=d}f[r]-=a[r]}for(;0==f[0];f.splice(0,1),--c);return f[0]?W(e,f,c):(e.s=3==M?-1:1,e.c=[e.e=0],e)},D.modulo=D.mod=function(e,t){var r,i,s=this;return e=new V(e,t),!s.c||!e.s||e.c&&!e.c[0]?new V(NaN):!e.c||s.c&&!s.c[0]?new V(s):(9==G?(i=e.s,e.s=1,r=n(s,e,0,3),e.s=i,r.s*=i):r=n(s,e,0,G),(e=s.minus(r.times(e))).c[0]||1!=G||(e.s=s.s),e)},D.multipliedBy=D.times=function(e,t){var n,r,i,s,o,l,u,c,f,a,h,p,g,w,v,y=this,O=y.c,B=(e=new V(e,t)).c;if(!(O&&B&&O[0]&&B[0]))return!y.s||!e.s||O&&!O[0]&&!B||B&&!B[0]&&!O?e.c=e.e=e.s=null:(e.s*=y.s,O&&B?(e.c=[0],e.e=0):e.c=e.e=null),e;for(r=N(y.e/m)+N(e.e/m),e.s*=y.s,(u=O.length)<(a=B.length)&&(g=O,O=B,B=g,i=u,u=a,a=i),i=u+a,g=[];i--;g.push(0));for(w=d,v=b,i=a;--i>=0;){for(n=0,h=B[i]%v,p=B[i]/v|0,s=i+(o=u);s>i;)n=((c=h*(c=O[--o]%v)+(l=p*c+(f=O[o]/v|0)*h)%v*v+g[s]+n)/w|0)+(l/v|0)+p*f,g[s--]=c%w;g[s]=n}return n?++r:g.splice(0,1),W(e,g,r)},D.negated=function(){var e=new V(this);return e.s=-e.s||null,e},D.plus=function(e,t){var n,r=this,i=r.s;if(t=(e=new V(e,t)).s,!i||!t)return new V(NaN);if(i!=t)return e.s=-t,r.minus(e);var s=r.e/m,o=e.e/m,l=r.c,u=e.c;if(!s||!o){if(!l||!u)return new V(i/0);if(!l[0]||!u[0])return u[0]?e:new V(l[0]?r:0*i)}if(s=N(s),o=N(o),l=l.slice(),i=s-o){for(i>0?(o=s,n=u):(i=-i,n=l),n.reverse();i--;n.push(0));n.reverse()}for((i=l.length)-(t=u.length)<0&&(n=u,u=l,l=n,t=i),i=0;t;)i=(l[--t]=l[t]+u[t]+i)/d|0,l[t]=d===l[t]?0:l[t]%d;return i&&(l=[i].concat(l),++o),W(e,l,o)},D.precision=D.sd=function(e,t){var n,r,i,s=this;if(null!=e&&e!==!!e)return E(e,1,y),null==t?t=M:E(t,0,8),X(new V(s),e,t);if(!(n=s.c))return null;if(r=(i=n.length-1)*m+1,i=n[i]){for(;i%10==0;i/=10,r--);for(i=n[0];i>=10;i/=10,r++);}return e&&s.e+1>r&&(r=s.e+1),r},D.shiftedBy=function(e){return E(e,-9007199254740991,w),this.times("1e"+e)},D.squareRoot=D.sqrt=function(){var e,t,r,i,s,o=this,l=o.c,u=o.s,c=o.e,f=P+4,a=new V("0.5");if(1!==u||!l||!l[0])return new V(!u||u<0&&(!l||l[0])?NaN:l?o:1/0);if(0==(u=Math.sqrt(+Y(o)))||u==1/0?(((t=O(l)).length+c)%2==0&&(t+="0"),u=Math.sqrt(+t),c=N((c+1)/2)-(c<0||c%2),r=new V(t=u==1/0?"5e"+c:(t=u.toExponential()).slice(0,t.indexOf("e")+1)+c)):r=new V(u+""),r.c[0])for((u=(c=r.e)+f)<3&&(u=0);;)if(s=r,r=a.times(s.plus(n(o,s,f,1))),O(s.c).slice(0,u)===(t=O(r.c)).slice(0,u)){if(r.e<c&&--u,"9999"!=(t=t.slice(u-3,u+1))&&(i||"4999"!=t)){+t&&(+t.slice(1)||"5"!=t.charAt(0))||(X(r,r.e+P+2,1),e=!r.times(r).eq(o));break}if(!i&&(X(s,s.e+P+2,0),s.times(s).eq(o))){r=s;break}f+=4,u+=4,i=1}return X(r,r.e+P+1,M,e)},D.toExponential=function(e,t){return null!=e&&(E(e,0,y),e++),H(this,e,t,1)},D.toFixed=function(e,t){return null!=e&&(E(e,0,y),e=e+this.e+1),H(this,e,t)},D.toFormat=function(e,t,n){var r,i=this;if(null==n)null!=e&&t&&"object"==typeof t?(n=t,t=null):e&&"object"==typeof e?(n=e,e=t=null):n=k;else if("object"!=typeof n)throw Error(p+"Argument not an object: "+n);if(r=i.toFixed(e,t),i.c){var s,o=r.split("."),l=+n.groupSize,u=+n.secondaryGroupSize,c=n.groupSeparator||"",f=o[0],a=o[1],h=i.s<0,g=h?f.slice(1):f,d=g.length;if(u&&(s=l,l=u,u=s,d-=s),l>0&&d>0){for(s=d%l||l,f=g.substr(0,s);s<d;s+=l)f+=c+g.substr(s,l);u>0&&(f+=c+g.slice(s)),h&&(f="-"+f)}r=a?f+(n.decimalSeparator||"")+((u=+n.fractionGroupSize)?a.replace(new RegExp("\\d{"+u+"}\\B","g"),"$&"+(n.fractionGroupSeparator||"")):a):f}return(n.prefix||"")+r+(n.suffix||"")},D.toFraction=function(e){var t,r,i,s,o,l,u,c,f,a,h,g,d=this,w=d.c;if(null!=e&&(!(u=new V(e)).isInteger()&&(u.c||1!==u.s)||u.lt(I)))throw Error(p+"Argument "+(u.isInteger()?"out of range: ":"not an integer: ")+Y(u));if(!w)return new V(d);for(t=new V(I),f=r=new V(I),i=c=new V(I),g=O(w),o=t.e=g.length-d.e-1,t.c[0]=v[(l=o%m)<0?m+l:l],e=!e||u.comparedTo(t)>0?o>0?t:f:u,l=$,$=1/0,u=new V(g),c.c[0]=0;a=n(u,t,0,1),1!=(s=r.plus(a.times(i))).comparedTo(e);)r=i,i=s,f=c.plus(a.times(s=f)),c=s,t=u.minus(a.times(s=t)),u=s;return s=n(e.minus(r),i,0,1),c=c.plus(s.times(f)),r=r.plus(s.times(i)),c.s=f.s=d.s,h=n(f,i,o*=2,M).minus(d).abs().comparedTo(n(c,r,o,M).minus(d).abs())<1?[f,i]:[c,r],$=l,h},D.toNumber=function(){return+Y(this)},D.toPrecision=function(e,t){return null!=e&&E(e,1,y),H(this,e,t,2)},D.toString=function(e){var t,n=this,i=n.s,s=n.e;return null===s?i?(t="Infinity",i<0&&(t="-"+t)):t="NaN":(null==e?t=s<=L||s>=R?_(O(n.c),s):C(O(n.c),s,"0"):10===e&&F?t=C(O((n=X(new V(n),P+s+1,M)).c),n.e,"0"):(E(e,2,q.length,"Base"),t=r(C(O(n.c),s,"0"),10,e,i,!0)),i<0&&n.c[0]&&(t="-"+t)),t},D.valueOf=D.toJSON=function(){return Y(this)},D._isBigNumber=!0,D[Symbol.toStringTag]="BigNumber",D[Symbol.for("nodejs.util.inspect.custom")]=D.valueOf,null!=t&&V.set(t),V}();class S{constructor(){this.addColumn=this.addColumn.bind(this),this.crossPairs=this.crossPairs.bind(this),this.BigNumber=T,this.EMA=this.EMA.bind(this),this.MA=this.SMA.bind(this),this.MACD=this.MACD.bind(this),this.BollingerBands=this.BollingerBands.bind(this),this.IchimokuCloud=this.IchimokuCloud.bind(this),this.RSI=this.RSI.bind(this),this.MFI=this.MFI.bind(this)}init(e){const{BigNumber:t}=this,n=e.map((({open:e,high:n,low:r,close:i,volume:s,timestamp:o})=>({open:t(e),high:t(n),low:t(r),close:t(i),volume:t(s),timestamp:o})));return this.ohlcv=n.reduce(((e,{open:t,high:n,low:r,close:i,volume:s,timestamp:o})=>(e.open.push(t),e.high.push(n),e.low.push(r),e.close.push(i),e.volume.push(s),e.timestamp.push(o),e)),{open:[],high:[],low:[],close:[],volume:[],timestamp:[]}),this}getData(){return this.ohlcv}addColumn(e,t){const n=this.ohlcv.open.length;if(t.length>n)throw new Error("Invalid column data: The length of the new column exceeds the length of the OHLCV data");for(;t.length<n;)t.unshift(NaN);this.ohlcv[e]=t.map((e=>isNaN(e)?e:e.toNumber()))}crossPairs(e){const t=this.getData();e.forEach((e=>{const{fast:n,slow:r}=e;if(n&&r)if([n,r].forEach((e=>{if(!t.hasOwnProperty(e)){const t=e.split("_");if(t.length>0){const e=t[0].toUpperCase(),n=t.slice(1);"function"==typeof this[e]&&n.length>0&&this[e](...n)}}})),t.hasOwnProperty(n)&&t.hasOwnProperty(r)){const e=i(this.BigNumber,t[n],t[r]);this.addColumn(`${n}_x_${r}`,e)}else console.error(`Missing ohlcv properties for ${n} or ${r}`)}))}EMA(e){return((e,t)=>{const r=e.getData().close,i=n(e.BigNumber,r,t);e.addColumn(`ema_${t}`,i)})(this,e),this}SMA(e){return((e,t)=>{const n=e.getData().close,i=r(e.BigNumber,n,t);e.addColumn(`sma_${t}`,i)})(this,e),this}MACD(e,t,r){return((e,t=12,r=26,s=9)=>{const o=e.getData().close,l=((e,t,r=12,s=26,o=9)=>{const l=n(e,t,r),u=n(e,t,s),c=l.map(((e,t)=>e.minus(u[t]))),f=n(e,c,o),a=c.map(((e,t)=>e.minus(f[t]))),h=i(e,c,f);return{diff:c,dea:f,histogram:a,crosses:h}})(e.BigNumber,o,t,r,s);for(let t in l)e.addColumn(`macd_${t}`,l[t])})(this,e,t,r),this}BollingerBands(e,t,n){return((e,t,n)=>{const i=e.getData().close,o=((e,t,n=20,i=2)=>{i=new e(i);const o=r(e,t,n),l=s(e,t,n).map((e=>e.times(i)));return{upper:o.map(((e,t)=>e.plus(l[t]))),mid:o,lower:o.map(((e,t)=>e.minus(l[t])))}})(e.BigNumber,i,t,n),{upper:l,mid:u,lower:c}=o;e.addColumn("bollinger_bands_upper",l),e.addColumn("bollinger_bands_mid",u),e.addColumn("bollinger_bands_lower",c)})(this,e,t),this}IchimokuCloud(e,t,n){return((e,t=9,n=26,r=52)=>{const i=t,s=n,f=s-i,a=r,{high:h,low:p,close:g}=e.getData(),d=o(e.BigNumber,h,p,i),m=o(e.BigNumber,h,p,s),w={conversion_line:d,base_line:m,leading_span_a:l(d,m,f,s),leading_span_b:u(e.BigNumber,h,p,a,s),lagging_span:c(g,s)};for(let t in w)console.log(`${t} [length ${w[t].length}] [lastValue ${w[t][w[t].length-1]}]`),e.addColumn(`ichimoku_${t}`,w[t])})(this,e,t,n),this}RSI(e,t){return((e,t=14,r=14)=>{const s=e.getData().close,o=((e,t,r=14,s=14)=>{if(t.length<r)return[];const o=new e(0),l=new e(100),u=new e(1);let c=[],f=o,a=o;for(let n=1;n<=r;n++){const r=new e(t[n]),i=new e(t[n-1]),s=r.minus(i);s.isLessThan(o)?a=a.plus(s.abs()):f=f.plus(s)}let h=f.dividedBy(r),p=a.dividedBy(r),g=p.isEqualTo(o)?o:h.dividedBy(p);c.push(l.minus(l.dividedBy(u.plus(g))));for(let n=r+1;n<t.length;n++){const i=new e(t[n]),s=new e(t[n-1]),d=i.minus(s);d.isLessThan(o)?(a=d.abs(),f=o):(f=d,a=o),h=h.times(r-1).plus(f).dividedBy(r),p=p.times(r-1).plus(a).dividedBy(r),g=p.isEqualTo(o)?o:h.dividedBy(p),c.push(l.minus(l.dividedBy(u.plus(g))))}const d=n(e,c,s);return{rsi:c,rsi_smoothed:d,rsi_crosses:i(e,c,d)}})(e.BigNumber,s,t,r),l=`${t}_${r}`;for(let t in o)e.addColumn(`${t}_${l}`,o[t])})(this,e,t),this}MFI(e){return((e,t=14)=>{const n=e.getData(),r=((e,t,n=14)=>{const{high:r,low:i,close:s,volume:o}=t,l=e(0),u=e(100),c=e(1),f=[],a=[],h=[],p=[],g=[],d=[];for(let e=0;e<s.length;e++){const t=r[e].plus(i[e]).plus(s[e]).dividedBy(3),n=t.times(o[e]);f.push(t),a.push(n)}for(let e=1;e<f.length;e++)f[e].isGreaterThan(f[e-1])?(h.push(a[e]),p.push(l)):f[e].isLessThan(f[e-1])?(h.push(l),p.push(a[e])):(h.push(l),p.push(l));for(let e=n;e<=h.length;e++){const t=h.slice(e-n,e),r=p.slice(e-n,e),i=t.reduce(((e,t)=>e.plus(t)),l),s=r.reduce(((e,t)=>e.plus(t)),l),o=i.dividedBy(s);g.push(o)}for(let e=0;e<g.length;e++){const t=u.minus(u.dividedBy(c.plus(g[e])));d.push(t)}return d})(e.BigNumber,n,t);e.addColumn(`mfi_${t}`,r)})(this,e),this}}})(),OHLCV_INDICATORS=t})();
|
package/index.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js'
|
|
2
|
+
import {EMA} from './src/moving-averages/ema.js'
|
|
3
|
+
import {SMA} from './src/moving-averages/sma.js'
|
|
4
|
+
import {MACD} from './src/moving-averages/macd.js'
|
|
5
|
+
import {BollingerBands} from './src/moving-averages/bollingerBands.js'
|
|
6
|
+
import { IchimokuCloud } from './src/moving-averages/ichimokuCloud.js'
|
|
7
|
+
import { RSI} from './src/oscillators/rsi.js'
|
|
8
|
+
import { MFI} from './src/oscillators/mfi.js'
|
|
9
|
+
import { RelativeVolume} from './src/moving-averages/relativeVolume.js'
|
|
10
|
+
import { findCrosses} from './src/utilities.js'
|
|
11
|
+
|
|
12
|
+
export default class OHLCV_INDICATORS {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.BigNumber = BigNumber
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
init(ohlcv) {
|
|
18
|
+
const { BigNumber } = this
|
|
19
|
+
|
|
20
|
+
const ohlcvObj = ohlcv.map(({ open, high, low, close, volume, timestamp }) => ({
|
|
21
|
+
open: BigNumber(open),
|
|
22
|
+
high: BigNumber(high),
|
|
23
|
+
low: BigNumber(low),
|
|
24
|
+
close: BigNumber(close),
|
|
25
|
+
volume: BigNumber(volume),
|
|
26
|
+
timestamp
|
|
27
|
+
}))
|
|
28
|
+
|
|
29
|
+
this.ohlcv = ohlcvObj.reduce((acc, { open, high, low, close, volume, timestamp }) => {
|
|
30
|
+
acc.open.push(open)
|
|
31
|
+
acc.high.push(high)
|
|
32
|
+
acc.low.push(low)
|
|
33
|
+
acc.close.push(close)
|
|
34
|
+
acc.volume.push(volume)
|
|
35
|
+
acc.timestamp.push(timestamp)
|
|
36
|
+
return acc
|
|
37
|
+
}, { open: [], high: [], low: [], close: [], volume: [], timestamp: [] })
|
|
38
|
+
|
|
39
|
+
return this
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getHeaders(){
|
|
43
|
+
return Object.keys(this.getData())
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getData() {
|
|
47
|
+
return this.ohlcv
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
addColumn(key, arr) {
|
|
51
|
+
const ohlcvLength = this.ohlcv.open.length
|
|
52
|
+
|
|
53
|
+
if (arr.length > ohlcvLength) {
|
|
54
|
+
throw new Error(`Invalid column data: The length of the new column exceeds the length of the OHLCV data`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//fulls arr with NaN until it has the same lenght as ohlcvLength
|
|
58
|
+
|
|
59
|
+
while (arr.length < ohlcvLength) {
|
|
60
|
+
arr.unshift(NaN)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.ohlcv[key] = arr.map(o => isNaN(o) ? o : o.toNumber())
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
crossPairs(arr)
|
|
67
|
+
{
|
|
68
|
+
|
|
69
|
+
//crossing single column pairs
|
|
70
|
+
const ohlcv = this.getData()
|
|
71
|
+
|
|
72
|
+
//each parameter [{fast, slow}, {fast, slow}]
|
|
73
|
+
|
|
74
|
+
arr.forEach(o => {
|
|
75
|
+
const {fast, slow} = o
|
|
76
|
+
|
|
77
|
+
//validates both are destructured correctly
|
|
78
|
+
if(!fast || !slow)
|
|
79
|
+
{
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
//if the column does not exist, will try to create a new one
|
|
84
|
+
//this code can only add single column output indicators such as SMA and EMA
|
|
85
|
+
[fast, slow].forEach(v => {
|
|
86
|
+
if (!ohlcv.hasOwnProperty(v)) {
|
|
87
|
+
const arr = v.split('_')
|
|
88
|
+
if (arr.length > 0) {
|
|
89
|
+
const funcName = arr[0].toUpperCase()
|
|
90
|
+
const params = arr.slice(1)
|
|
91
|
+
|
|
92
|
+
if (typeof this[funcName] === 'function' && params.length > 0) {
|
|
93
|
+
this[funcName](...params)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
//finds the cross and adds is to the columns
|
|
100
|
+
if (ohlcv.hasOwnProperty(fast) && ohlcv.hasOwnProperty(slow)) {
|
|
101
|
+
const cross = findCrosses(this.BigNumber, ohlcv[fast], ohlcv[slow])
|
|
102
|
+
this.addColumn(`${fast}_x_${slow}`, cross)
|
|
103
|
+
} else {
|
|
104
|
+
console.error(`Missing ohlcv properties for ${fast} or ${slow}`)
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
EMA(size) {
|
|
111
|
+
EMA(this, size)
|
|
112
|
+
return this
|
|
113
|
+
}
|
|
114
|
+
SMA(size) {
|
|
115
|
+
SMA(this, size)
|
|
116
|
+
return this
|
|
117
|
+
}
|
|
118
|
+
MACD(fastLine, slowLine, signalLine) {
|
|
119
|
+
MACD(this, fastLine, slowLine, signalLine)
|
|
120
|
+
return this
|
|
121
|
+
}
|
|
122
|
+
BollingerBands(data, size, times)
|
|
123
|
+
{
|
|
124
|
+
BollingerBands(this, data, size, times)
|
|
125
|
+
return this
|
|
126
|
+
}
|
|
127
|
+
IchimokuCloud(tenkan, kijun, senkou)
|
|
128
|
+
{
|
|
129
|
+
IchimokuCloud(this, tenkan, kijun, senkou)
|
|
130
|
+
return this
|
|
131
|
+
}
|
|
132
|
+
RSI(period, smoothingPeriod)
|
|
133
|
+
{
|
|
134
|
+
RSI(this, period, smoothingPeriod)
|
|
135
|
+
return this
|
|
136
|
+
}
|
|
137
|
+
MFI(period)
|
|
138
|
+
{
|
|
139
|
+
MFI(this, period)
|
|
140
|
+
return this
|
|
141
|
+
}
|
|
142
|
+
RelativeVolume(size)
|
|
143
|
+
{
|
|
144
|
+
RelativeVolume(this, size)
|
|
145
|
+
return this
|
|
146
|
+
}
|
|
147
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ohlcv-indicators",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A JavaScript library featuring optimized trading indicators for OHLCV arrays [{open, high, low, close, value}], utilizing BigNumber precision for enhanced accuracy.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "node test/test.js",
|
|
9
|
+
"build": "webpack"
|
|
10
|
+
},
|
|
11
|
+
"author": "https://jaimelias.com/",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"bignumber.js": "^9.1.2",
|
|
15
|
+
"webpack": "^5.92.1",
|
|
16
|
+
"webpack-cli": "^5.1.4"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { getSMA } from "./sma.js";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export const BollingerBands = (main, size, times) => {
|
|
5
|
+
const ohlcv = main.getData()
|
|
6
|
+
const suffix = `${size}_${times}`
|
|
7
|
+
const data = ohlcv['close'];
|
|
8
|
+
const bb = getBollingerBands(main.BigNumber, data, size, times)
|
|
9
|
+
const {upper, mid, lower} = bb
|
|
10
|
+
main.addColumn(`bollinger_bands_upper`, upper)
|
|
11
|
+
main.addColumn(`bollinger_bands_mid`, mid)
|
|
12
|
+
main.addColumn(`bollinger_bands_lower`, lower)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export const getBollingerBands = (BigNumber, data, size = 20, times = 2) => {
|
|
18
|
+
// Ensure times is a BigNumber
|
|
19
|
+
times = new BigNumber(times)
|
|
20
|
+
|
|
21
|
+
const avg = getSMA(BigNumber, data, size)
|
|
22
|
+
const sd = deviation(BigNumber, data, size)
|
|
23
|
+
|
|
24
|
+
// Ensure timesSd is correctly calculated
|
|
25
|
+
const timesSd = sd.map(o => o.times(times))
|
|
26
|
+
|
|
27
|
+
// Correct the mapping of upper and lower bands
|
|
28
|
+
let upper = avg.map((o, i) => o.plus(timesSd[i]))
|
|
29
|
+
let mid = avg
|
|
30
|
+
let lower = avg.map((o, i) => o.minus(timesSd[i]))
|
|
31
|
+
|
|
32
|
+
return { upper, mid, lower }
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const deviation = (BigNumber, data, size) => {
|
|
36
|
+
const zero = new BigNumber(0)
|
|
37
|
+
const length = data.length
|
|
38
|
+
const avg = getSMA(BigNumber, data, size)
|
|
39
|
+
const ret = new Array(length).fill(zero) // Initialize ret array with zeros
|
|
40
|
+
|
|
41
|
+
for (let i = size - 1; i < length; i++) {
|
|
42
|
+
let sum = zero
|
|
43
|
+
|
|
44
|
+
for (let j = i - size + 1; j <= i; j++) {
|
|
45
|
+
sum = sum.plus(data[j].minus(avg[i]).pow(2))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
ret[i] = sum.dividedBy(size).sqrt()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return ret
|
|
52
|
+
};
|
|
53
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
|
|
2
|
+
export const EMA = (main, size) => {
|
|
3
|
+
const ohlcv = main.getData()
|
|
4
|
+
const data = ohlcv['close']
|
|
5
|
+
const ema = getEMA(main.BigNumber, data, size)
|
|
6
|
+
main.addColumn(`ema_${size}`, ema)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const getEMA = (BigNumber, data, size) => {
|
|
10
|
+
const alpha = BigNumber(2 / (size + 1))
|
|
11
|
+
const length = data.length
|
|
12
|
+
const ret = []
|
|
13
|
+
|
|
14
|
+
if (alpha.isGreaterThan(1)) {
|
|
15
|
+
return Array(length)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (alpha.isEqualTo(1)) {
|
|
19
|
+
return [...data]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let s = 0
|
|
23
|
+
|
|
24
|
+
// Handles head
|
|
25
|
+
let i = 0;
|
|
26
|
+
for (; i < length; i++) {
|
|
27
|
+
const datum = data[i]
|
|
28
|
+
if (typeof datum === 'object') {
|
|
29
|
+
ret[i] = datum
|
|
30
|
+
s = datum
|
|
31
|
+
i++
|
|
32
|
+
break
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (Array.isArray(alpha)) {
|
|
37
|
+
for (; i < length; i++) {
|
|
38
|
+
const datum = data[i]
|
|
39
|
+
if (typeof datum === 'object' && typeof alpha[i] === 'object') {
|
|
40
|
+
s = ret[i] = alpha[i].multipliedBy(datum).plus(alpha[i].minus(1).multipliedBy(s))
|
|
41
|
+
} else {
|
|
42
|
+
ret[i] = ret[i - 1]
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
const o = BigNumber(1).minus(alpha);
|
|
47
|
+
for (; i < length; i++) {
|
|
48
|
+
const datum = data[i]
|
|
49
|
+
s = typeof datum === 'object' ? alpha.multipliedBy(datum).plus(o.multipliedBy(s)) : ret[i - 1]
|
|
50
|
+
ret[i] = s
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return ret
|
|
55
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export const IchimokuCloud = (main, tenkan, kijun, senkou) => {
|
|
2
|
+
|
|
3
|
+
const ohlcv = main.getData()
|
|
4
|
+
const ichi = getIchimokuCloud(main.BigNumber, ohlcv, tenkan, kijun, senkou)
|
|
5
|
+
|
|
6
|
+
for(let k in ichi)
|
|
7
|
+
{
|
|
8
|
+
console.log(`${k} [length ${ichi[k].length}] [lastValue ${ichi[k][ichi[k].length -1]}]`)
|
|
9
|
+
main.addColumn(`ichimoku_${k}`, ichi[k])
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
export const getIchimokuCloud = (BigNumber, data, tenkan = 9, kijun = 26, senkou = 52) => {
|
|
15
|
+
const TENKAN_SEN_PERIOD = tenkan
|
|
16
|
+
const KIJUN_SEN_PERIOD = kijun
|
|
17
|
+
const SENKOU_SPAN_A_PERIOD = KIJUN_SEN_PERIOD-TENKAN_SEN_PERIOD
|
|
18
|
+
const SENKOU_SPAN_B_PERIOD = senkou
|
|
19
|
+
const { high, low, close } = data
|
|
20
|
+
const tenkanSen = calculateAverage(BigNumber, high, low, TENKAN_SEN_PERIOD)
|
|
21
|
+
const kijunSen = calculateAverage(BigNumber, high, low, KIJUN_SEN_PERIOD)
|
|
22
|
+
const senkouSpanA = calculateSenkouSpanA(tenkanSen, kijunSen, SENKOU_SPAN_A_PERIOD, KIJUN_SEN_PERIOD)
|
|
23
|
+
const senkouSpanB = calculateSenkouSpanB(BigNumber, high, low, SENKOU_SPAN_B_PERIOD, KIJUN_SEN_PERIOD)
|
|
24
|
+
const chikouSpan = calculateChikouSpan(close, KIJUN_SEN_PERIOD)
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
conversion_line: tenkanSen,
|
|
28
|
+
base_line: kijunSen,
|
|
29
|
+
leading_span_a: senkouSpanA,
|
|
30
|
+
leading_span_b: senkouSpanB,
|
|
31
|
+
lagging_span: chikouSpan
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const calculateAverage = (BigNumber, high, low, period) => {
|
|
36
|
+
return high.slice(period).map((_, i) => {
|
|
37
|
+
const highSlice = high.slice(i, i + period)
|
|
38
|
+
const lowSlice = low.slice(i, i + period)
|
|
39
|
+
return BigNumber.maximum(...highSlice).plus(BigNumber.minimum(...lowSlice)).dividedBy(2)
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const calculateSenkouSpanA = (tenkanSen, kijunSen, period, slice) => {
|
|
44
|
+
const spanA = tenkanSen.slice(period).map((value, i) =>
|
|
45
|
+
i < kijunSen.length ? value.plus(kijunSen[i]).dividedBy(2) : null
|
|
46
|
+
).filter(val => val !== null)
|
|
47
|
+
|
|
48
|
+
return spanA.slice(0, (spanA.length - slice)+1)
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const calculateSenkouSpanB = (BigNumber, high, low, period, slice) => {
|
|
52
|
+
let spanB = high.slice(period).map((_, i) => { // Adjusted slicing
|
|
53
|
+
const highSlice = high.slice(i, i + period)
|
|
54
|
+
const lowSlice = low.slice(i, i + period)
|
|
55
|
+
return BigNumber.maximum(...highSlice).plus(BigNumber.minimum(...lowSlice)).dividedBy(2)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
return spanB.slice(0, (spanB.length -slice)+1)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
const calculateChikouSpan = (close, kijunSenPeriod) => close.slice(0, close.length - kijunSenPeriod)
|
|
63
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { findCrosses } from "../utilities.js";
|
|
2
|
+
import { getEMA } from "./ema.js";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export const MACD = (main, fastLine = 12, slowLine = 26, signalLine = 9) => {
|
|
6
|
+
const ohlcv = main.getData()
|
|
7
|
+
const data = ohlcv['close'];
|
|
8
|
+
const macd = getMACD(main.BigNumber, data, fastLine, slowLine, signalLine)
|
|
9
|
+
|
|
10
|
+
for(let k in macd)
|
|
11
|
+
{
|
|
12
|
+
main.addColumn(`macd_${k}`, macd[k]);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const getMACD = (BigNumber, data, fastLine = 12, slowLine = 26, signalLine = 9) => {
|
|
17
|
+
|
|
18
|
+
const fastEMA = getEMA(BigNumber, data, fastLine)
|
|
19
|
+
const slowEMA = getEMA(BigNumber, data, slowLine)
|
|
20
|
+
|
|
21
|
+
// Calculate the MACD line (diff)
|
|
22
|
+
const diff = fastEMA.map((o, i) => o.minus(slowEMA[i]));
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
// Calculate the MACD line (dea)
|
|
26
|
+
const dea = getEMA(BigNumber, diff, signalLine)
|
|
27
|
+
|
|
28
|
+
// Calculate the MACD line (histogram)
|
|
29
|
+
const histogram = diff.map((d, i) => d.minus(dea[i]))
|
|
30
|
+
const crosses = findCrosses(BigNumber, diff, dea)
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
diff,
|
|
34
|
+
dea,
|
|
35
|
+
histogram,
|
|
36
|
+
crosses
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getSMA } from './sma.js'
|
|
2
|
+
|
|
3
|
+
export const RelativeVolume = (main, size) => {
|
|
4
|
+
const ohlcv = main.getData()
|
|
5
|
+
const data = ohlcv['volume']
|
|
6
|
+
const relVolume = getRelativeVolume(main.BigNumber, data, size)
|
|
7
|
+
main.addColumn(`relative_volume_${size}`, relVolume)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const getRelativeVolume = (BigNumber, data, size = 10) => {
|
|
11
|
+
const sma = getSMA(BigNumber, data, size)
|
|
12
|
+
|
|
13
|
+
return data.map((v, i) => {
|
|
14
|
+
if (i === 0 || !sma[i - 2]) return NaN // or handle the edge case appropriately
|
|
15
|
+
return v.dividedBy(sma[i - 2])
|
|
16
|
+
}).filter(v => v !== NaN) // remove null values if necessary
|
|
17
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
export const SMA = (main, size) => {
|
|
3
|
+
const ohlcv = main.getData()
|
|
4
|
+
const data = ohlcv['close']
|
|
5
|
+
const sma = getSMA(main.BigNumber, data, size)
|
|
6
|
+
main.addColumn(`sma_${size}`, sma)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export const getSMA = (BigNumber, data, size) => {
|
|
11
|
+
|
|
12
|
+
let result = []
|
|
13
|
+
let sum = BigNumber(0)
|
|
14
|
+
let arr = []
|
|
15
|
+
|
|
16
|
+
for (let i = 0; i < data.length; i++) {
|
|
17
|
+
|
|
18
|
+
arr.push(data[i])
|
|
19
|
+
sum = sum.plus(data[i])
|
|
20
|
+
|
|
21
|
+
if (arr.length > size) {
|
|
22
|
+
sum = sum.minus(arr.shift())
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (i >= size - 1) {
|
|
26
|
+
result.push(sum.dividedBy(size))
|
|
27
|
+
} else {
|
|
28
|
+
|
|
29
|
+
result.push(sum.dividedBy(arr.length))
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export const MFI = (main, period = 14) => {
|
|
2
|
+
const ohlcv = main.getData()
|
|
3
|
+
const mfi = getMFI(main.BigNumber, ohlcv, period)
|
|
4
|
+
main.addColumn(`mfi_${period}`, mfi)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const getMFI = (BigNumber, ohlcv, period = 14) => {
|
|
8
|
+
const { high, low, close, volume } = ohlcv;
|
|
9
|
+
const zero = BigNumber(0)
|
|
10
|
+
const hundred = BigNumber(100)
|
|
11
|
+
const one = BigNumber(1)
|
|
12
|
+
const typicalPrices = [] // Typical price = (high + low + close) / 3
|
|
13
|
+
const rawMoneyFlows = [] // Raw money flow = typical price * volume
|
|
14
|
+
const positiveMoneyFlows = [] // Positive money flow
|
|
15
|
+
const negativeMoneyFlows = [] // Negative money flow
|
|
16
|
+
const moneyFlowRatios = [] // Money flow ratio = (14-day positive money flow) / (14-day negative money flow)
|
|
17
|
+
const moneyFlowIndexes = [] // Money flow index = 100 - (100 / (1 + money flow ratio))
|
|
18
|
+
|
|
19
|
+
// Calculate typical prices and raw money flows
|
|
20
|
+
for (let i = 0; i < close.length; i++) {
|
|
21
|
+
const typicalPrice = high[i].plus(low[i]).plus(close[i]).dividedBy(3);
|
|
22
|
+
const rawMoneyFlow = typicalPrice.times(volume[i]);
|
|
23
|
+
|
|
24
|
+
typicalPrices.push(typicalPrice)
|
|
25
|
+
rawMoneyFlows.push(rawMoneyFlow)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Calculate positive and negative money flows
|
|
29
|
+
for (let i = 1; i < typicalPrices.length; i++) {
|
|
30
|
+
if (typicalPrices[i].isGreaterThan(typicalPrices[i - 1])) {
|
|
31
|
+
positiveMoneyFlows.push(rawMoneyFlows[i])
|
|
32
|
+
negativeMoneyFlows.push(zero)
|
|
33
|
+
} else if (typicalPrices[i].isLessThan(typicalPrices[i - 1])) {
|
|
34
|
+
positiveMoneyFlows.push(zero)
|
|
35
|
+
negativeMoneyFlows.push(rawMoneyFlows[i])
|
|
36
|
+
} else {
|
|
37
|
+
positiveMoneyFlows.push(zero)
|
|
38
|
+
negativeMoneyFlows.push(zero)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Calculate money flow ratios
|
|
43
|
+
for (let i = period; i <= positiveMoneyFlows.length; i++) {
|
|
44
|
+
const positiveMoneyFlowSlice = positiveMoneyFlows.slice(i - period, i)
|
|
45
|
+
const negativeMoneyFlowSlice = negativeMoneyFlows.slice(i - period, i)
|
|
46
|
+
|
|
47
|
+
const positiveMoneyFlowSum = positiveMoneyFlowSlice.reduce((sum, value) => sum.plus(value), zero)
|
|
48
|
+
const negativeMoneyFlowSum = negativeMoneyFlowSlice.reduce((sum, value) => sum.plus(value), zero)
|
|
49
|
+
|
|
50
|
+
const moneyFlowRatio = positiveMoneyFlowSum.dividedBy(negativeMoneyFlowSum)
|
|
51
|
+
moneyFlowRatios.push(moneyFlowRatio)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Calculate money flow indexes
|
|
55
|
+
for (let i = 0; i < moneyFlowRatios.length; i++) {
|
|
56
|
+
const moneyFlowIndex = hundred.minus(hundred.dividedBy(one.plus(moneyFlowRatios[i])))
|
|
57
|
+
moneyFlowIndexes.push(moneyFlowIndex)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return moneyFlowIndexes
|
|
61
|
+
};
|
|
62
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { getEMA } from "../moving-averages/ema.js";
|
|
2
|
+
import { findCrosses } from "../utilities.js";
|
|
3
|
+
|
|
4
|
+
export const RSI = (main, period = 14, smoothingPeriod = 14) => {
|
|
5
|
+
|
|
6
|
+
const ohlcv = main.getData()
|
|
7
|
+
const data = ohlcv['close'];
|
|
8
|
+
const rsi = getRSI(main.BigNumber, data, period, smoothingPeriod)
|
|
9
|
+
const suffix = `${period}_${smoothingPeriod}`
|
|
10
|
+
|
|
11
|
+
for(let k in rsi)
|
|
12
|
+
{
|
|
13
|
+
main.addColumn(`${k}_${suffix}`, rsi[k]);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const getRSI = (BigNumber, data, period = 14, smoothingPeriod = 14) => {
|
|
19
|
+
if (data.length < period) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const zero = new BigNumber(0);
|
|
24
|
+
const hundred = new BigNumber(100);
|
|
25
|
+
const one = new BigNumber(1);
|
|
26
|
+
|
|
27
|
+
let rsi = [];
|
|
28
|
+
let gain = zero;
|
|
29
|
+
let loss = zero;
|
|
30
|
+
|
|
31
|
+
// Calculate the initial gain and loss
|
|
32
|
+
for (let i = 1; i <= period; i++) {
|
|
33
|
+
const thisVal = new BigNumber(data[i]);
|
|
34
|
+
const prevVal = new BigNumber(data[i - 1]);
|
|
35
|
+
const change = thisVal.minus(prevVal);
|
|
36
|
+
|
|
37
|
+
if (change.isLessThan(zero)) {
|
|
38
|
+
loss = loss.plus(change.abs());
|
|
39
|
+
} else {
|
|
40
|
+
gain = gain.plus(change);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Calculate the first RSI value
|
|
45
|
+
let avgGain = gain.dividedBy(period);
|
|
46
|
+
let avgLoss = loss.dividedBy(period);
|
|
47
|
+
let rs = avgLoss.isEqualTo(zero) ? zero : avgGain.dividedBy(avgLoss);
|
|
48
|
+
rsi.push(hundred.minus(hundred.dividedBy(one.plus(rs))));
|
|
49
|
+
|
|
50
|
+
// Calculate subsequent RSI values
|
|
51
|
+
for (let i = period + 1; i < data.length; i++) {
|
|
52
|
+
const thisVal = new BigNumber(data[i]);
|
|
53
|
+
const prevVal = new BigNumber(data[i - 1]);
|
|
54
|
+
const change = thisVal.minus(prevVal);
|
|
55
|
+
|
|
56
|
+
if (change.isLessThan(zero)) {
|
|
57
|
+
loss = change.abs();
|
|
58
|
+
gain = zero;
|
|
59
|
+
} else {
|
|
60
|
+
gain = change;
|
|
61
|
+
loss = zero;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
avgGain = avgGain.times(period - 1).plus(gain).dividedBy(period);
|
|
65
|
+
avgLoss = avgLoss.times(period - 1).plus(loss).dividedBy(period);
|
|
66
|
+
rs = avgLoss.isEqualTo(zero) ? zero : avgGain.dividedBy(avgLoss);
|
|
67
|
+
rsi.push(hundred.minus(hundred.dividedBy(one.plus(rs))));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const rsi_smoothed = getEMA(BigNumber, rsi, smoothingPeriod);
|
|
71
|
+
const rsi_crosses = findCrosses(BigNumber, rsi, rsi_smoothed);
|
|
72
|
+
|
|
73
|
+
return { rsi, rsi_smoothed, rsi_crosses};
|
|
74
|
+
};
|
package/src/utilities.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const findCrosses = (BigNumber, fast, slow) => {
|
|
2
|
+
let crosses = new Array(fast.length).fill(0)
|
|
3
|
+
let state = 0 // 0 for no initial state, 1 for uptrend, -1 for downtrend
|
|
4
|
+
|
|
5
|
+
// Determine the initial state based on the first values
|
|
6
|
+
const fast0 = new BigNumber(fast[0])
|
|
7
|
+
const slow0 = new BigNumber(slow[0])
|
|
8
|
+
|
|
9
|
+
if (fast0.isGreaterThan(slow0)) {
|
|
10
|
+
state = 1
|
|
11
|
+
crosses[0] = 1
|
|
12
|
+
} else if (fast0.isLessThan(slow0)) {
|
|
13
|
+
state = -1
|
|
14
|
+
crosses[0] = -1
|
|
15
|
+
} else {
|
|
16
|
+
state = 0
|
|
17
|
+
crosses[0] = 0
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
for (let i = 1; i < fast.length; i++) {
|
|
21
|
+
const fastI = new BigNumber(fast[i])
|
|
22
|
+
const slowI = new BigNumber(slow[i])
|
|
23
|
+
const fastPrev = new BigNumber(fast[i - 1])
|
|
24
|
+
const slowPrev = new BigNumber(slow[i - 1])
|
|
25
|
+
|
|
26
|
+
if (fastI.isGreaterThan(slowI) && fastPrev.isLessThanOrEqualTo(slowPrev)) {
|
|
27
|
+
// CrossUp detected
|
|
28
|
+
state = 1
|
|
29
|
+
crosses[i] = 1
|
|
30
|
+
} else if (fastI.isLessThan(slowI) && fastPrev.isGreaterThanOrEqualTo(slowPrev)) {
|
|
31
|
+
// CrossDown detected
|
|
32
|
+
state = -1
|
|
33
|
+
crosses[i] = -1
|
|
34
|
+
} else {
|
|
35
|
+
// Continue with the previous state
|
|
36
|
+
if (state === 1) {
|
|
37
|
+
crosses[i] = crosses[i - 1] + 1
|
|
38
|
+
} else if (state === -1) {
|
|
39
|
+
crosses[i] = crosses[i - 1] - 1
|
|
40
|
+
} else {
|
|
41
|
+
crosses[i] = 0
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return crosses.map(o => BigNumber(o))
|
|
47
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
|
|
2
|
+
const apiRoot = 'https://nasdaq.jaimelias.workers.dev/api'
|
|
3
|
+
const convertCurrencyToFloat = str => parseFloat(str.replace(/[,$]/g, ''))
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export const getOHLCV = async (symbol, limit) => {
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const oneDay = await (getOneDayOHLC(symbol, limit))
|
|
12
|
+
const latest = await getLatestOHLCV(symbol)
|
|
13
|
+
|
|
14
|
+
return [...oneDay, ...latest]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const getOneDayOHLC = async (symbol, limit) => {
|
|
18
|
+
|
|
19
|
+
const {fromDate, toDate} = getDateRange(limit)
|
|
20
|
+
const response = await fetch(`${apiRoot}/quote/${symbol}/chart?assetclass=stocks&fromdate=${fromDate}&todate=${toDate}`)
|
|
21
|
+
const data = await response.json()
|
|
22
|
+
const {chart} = data.data
|
|
23
|
+
const ohlcv = chart.map(({z, x})=> ({
|
|
24
|
+
open: convertCurrencyToFloat(z.open),
|
|
25
|
+
high: convertCurrencyToFloat(z.high),
|
|
26
|
+
low: convertCurrencyToFloat(z.low),
|
|
27
|
+
close: convertCurrencyToFloat(z.close),
|
|
28
|
+
volume: convertCurrencyToFloat(z.volume),
|
|
29
|
+
timestamp: x
|
|
30
|
+
}))
|
|
31
|
+
|
|
32
|
+
return ohlcv
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const getLatestOHLCV = async (symbol) => {
|
|
36
|
+
|
|
37
|
+
const response = await fetch(`${apiRoot}/quote/${symbol}/chart?assetClass=stocks`)
|
|
38
|
+
const data = await response.json()
|
|
39
|
+
const {chart, lastSalePrice, volume} = data.data
|
|
40
|
+
const allValues = chart.map(o => convertCurrencyToFloat(o.z.value))
|
|
41
|
+
|
|
42
|
+
return [{
|
|
43
|
+
open: allValues[0],
|
|
44
|
+
high: Math.max(...allValues),
|
|
45
|
+
low: Math.min(...allValues),
|
|
46
|
+
close: convertCurrencyToFloat(lastSalePrice),
|
|
47
|
+
volume: convertCurrencyToFloat(volume),
|
|
48
|
+
timestamp: chart[chart.length -1].x
|
|
49
|
+
}]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const getDateRange = limit => {
|
|
53
|
+
const toDate = new Date().toISOString().slice(0, 10) // Get today's date in yyyy-mm-dd format
|
|
54
|
+
|
|
55
|
+
const fromDate = new Date()
|
|
56
|
+
fromDate.setDate(fromDate.getDate() - limit)
|
|
57
|
+
const fromDateFormatted = fromDate.toISOString().slice(0, 10) // Get the date limit number of days ago in yyyy-mm-dd format
|
|
58
|
+
|
|
59
|
+
return { fromDate: fromDateFormatted, toDate }
|
|
60
|
+
};
|
package/test/test.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import OHLCV_INDICATORS from '../index.js'
|
|
2
|
+
import { getOHLCV } from './fetchLiveData.js'
|
|
3
|
+
|
|
4
|
+
const indicators = new OHLCV_INDICATORS();
|
|
5
|
+
const {BigNumber} = indicators
|
|
6
|
+
|
|
7
|
+
const TEST = async () => {
|
|
8
|
+
|
|
9
|
+
const ohlcv = await getOHLCV('AAPL', 230)
|
|
10
|
+
|
|
11
|
+
indicators
|
|
12
|
+
.init(ohlcv)
|
|
13
|
+
.EMA(21)
|
|
14
|
+
.EMA(9, 'ema21')
|
|
15
|
+
.SMA(200)
|
|
16
|
+
.SMA(100, 'sma_200')
|
|
17
|
+
.MACD(12, 26, 9)
|
|
18
|
+
.BollingerBands(20, 2)
|
|
19
|
+
.IchimokuCloud(9, 26, 52)
|
|
20
|
+
.RSI(14, 14)
|
|
21
|
+
.MFI(14)
|
|
22
|
+
.RelativeVolume(10)
|
|
23
|
+
.crossPairs([{fast: 'ema_9', slow: 'ema_21'}, {fast: 'close', slow: 'sma_200'}])
|
|
24
|
+
|
|
25
|
+
const dataSet = indicators.getData()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
for(let k in dataSet)
|
|
29
|
+
{
|
|
30
|
+
let firstValidIndex = 0
|
|
31
|
+
const row = dataSet[k]
|
|
32
|
+
const firstValue = (BigNumber.isBigNumber(row[0])) ? row[0].toNumber() : row[0]
|
|
33
|
+
const lastValue = (BigNumber.isBigNumber(row[row.length -1])) ? row[row.length -1].toNumber() : row[row.length -1]
|
|
34
|
+
|
|
35
|
+
//get the first numeric index
|
|
36
|
+
row.forEach(v => {
|
|
37
|
+
if(isNaN(v))
|
|
38
|
+
{
|
|
39
|
+
firstValidIndex++
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
console.log(`${k}: ${JSON.stringify({firstValue, firstValidIndex, lastValue})}`)
|
|
44
|
+
console.log('---\n')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log('<<<<<<<<<< HEADERS >>>>>>>>>>')
|
|
48
|
+
console.log(indicators.getHeaders())
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
TEST()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {resolve, dirname } from 'path'
|
|
2
|
+
import { fileURLToPath } from 'url'
|
|
3
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const entry = './index.js'
|
|
7
|
+
const output = {
|
|
8
|
+
filename: 'ohlcv-indicators.min.js',
|
|
9
|
+
path: resolve(__dirname, './dist'),
|
|
10
|
+
library: 'OHLCV_INDICATORS'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default {entry, output, mode: 'production', target: 'web'}
|