Convert supertrend from pinescript to javascript - javascript

Firstly thanks for any help its a bit of a complex question!
I have a hobby of converting pinescript scripts to javascript which i then use to run through a backtester infra i built in js. Just to start i am doing this with many scripts successfully, the problem wont be that im sending candles wrong or something.
I am 95% sure that my problem lies within not doing the ATR correct. I have messed with this function a bit and get a little close but not exact. Im pretty sure i converted the supertrend part correct though.
Lets start with the pinescript script of supertrend by KivancOzbilgic:
//#version=4
study("Supertrend", overlay = true, format=format.price, precision=2, resolution="")
Periods = input(title="ATR Period", type=input.integer, defval=10)
src = input(hl2, title="Source")
Multiplier = input(title="ATR Multiplier", type=input.float, step=0.1, defval=3.0)
changeATR= input(title="Change ATR Calculation Method ?", type=input.bool, defval=true)
showsignals = input(title="Show Buy/Sell Signals ?", type=input.bool, defval=true)
highlighting = input(title="Highlighter On/Off ?", type=input.bool, defval=true)
atr2 = sma(tr, Periods)
atr= changeATR ? atr(Periods) : atr2
up=src-(Multiplier*atr)
up1 = nz(up[1],up)
up := close[1] > up1 ? max(up,up1) : up
dn=src+(Multiplier*atr)
dn1 = nz(dn[1], dn)
dn := close[1] < dn1 ? min(dn, dn1) : dn
trend = 1
trend := nz(trend[1], trend)
trend := trend == -1 and close > dn1 ? 1 : trend == 1 and close < up1 ? -1 : trend
upPlot = plot(trend == 1 ? up : na, title="Up Trend", style=plot.style_linebr, linewidth=2, color=color.green)
buySignal = trend == 1 and trend[1] == -1
plotshape(buySignal ? up : na, title="UpTrend Begins", location=location.absolute, style=shape.circle, size=size.tiny, color=color.green, transp=0)
plotshape(buySignal and showsignals ? up : na, title="Buy", text="Buy", location=location.absolute, style=shape.labelup, size=size.tiny, color=color.green, textcolor=color.white, transp=0)
dnPlot = plot(trend == 1 ? na : dn, title="Down Trend", style=plot.style_linebr, linewidth=2, color=color.red)
sellSignal = trend == -1 and trend[1] == 1
plotshape(sellSignal ? dn : na, title="DownTrend Begins", location=location.absolute, style=shape.circle, size=size.tiny, color=color.red, transp=0)
plotshape(sellSignal and showsignals ? dn : na, title="Sell", text="Sell", location=location.absolute, style=shape.labeldown, size=size.tiny, color=color.red, textcolor=color.white, transp=0)
mPlot = plot(ohlc4, title="", style=plot.style_circles, linewidth=0)
longFillColor = highlighting ? (trend == 1 ? color.green : color.white) : color.white
shortFillColor = highlighting ? (trend == -1 ? color.red : color.white) : color.white
fill(mPlot, upPlot, title="UpTrend Highligter", color=longFillColor)
fill(mPlot, dnPlot, title="DownTrend Highligter", color=shortFillColor)
alertcondition(buySignal, title="SuperTrend Buy", message="SuperTrend Buy!")
alertcondition(sellSignal, title="SuperTrend Sell", message="SuperTrend Sell!")
changeCond = trend != trend[1]
alertcondition(changeCond, title="SuperTrend Direction Change", message="SuperTrend has changed direction!")
Here is my implementation in javascript
// candles example (I provide 100 candles)
const candles = [{high: 10, low: 8, close: 9, open: 8.5}, ...]
static ATR = async (candles, multiplier, limit) => {
const lows = candles.map((candle) => +candle.low)
const highs = candles.map((candle) => +candle.high)
const closes = candles.map((candle) => +candle.close)
let TRResults = []
for (let x = 1; x < candles.length; x++) TRResults.push(Math.max(highs[x] - lows[x], Math.abs(highs[x] - closes[x - 1]), Math.abs(lows[x] - closes[x - 1])))
let RMA_TR_Results = [TRResults[0]]
const alpha = 1 / limit
for (let x = 1; x < TRResults.length; x++) RMA_TR_Results.push((alpha * TRResults[x]) + ((1 - alpha) * RMA_TR_Results[RMA_TR_Results.length - 1]))
return RMA_TR_Results[RMA_TR_Results.length - 1] * multiplier
}
static superTrend = async(candles, multiplier, limit) => {
let upperBands = []
let lowerBands = []
let superTrends = []
for (let i = 0; i < candles.length; i++) {
if (i >= limit * 4) {
const lastCandle = +candles[i - 1].close
const currentCandling = +candles[i].close
const candlesATR = await this.ATR(candles.slice(i - (limit * 4), limit * 4), multiplier, limit)
const basicUpperBand = ((+candles[i].high + +candles[i].low) / 2) - candlesATR
const basicLowerBand = ((+candles[i].high + +candles[i].low) / 2) + candlesATR
if (i === limit * 4) {
upperBands.push(basicUpperBand)
lowerBands.push(basicLowerBand)
superTrends.push(true)
} else {
const lastUpperBand = upperBands[upperBands.length - 1]
const lastLowerBand = lowerBands[lowerBands.length - 1]
upperBands.push(lastCandle > lastUpperBand ? Math.max(basicUpperBand, lastUpperBand) : basicUpperBand)
lowerBands.push(lastCandle < lastLowerBand ? Math.min(basicLowerBand, lastLowerBand) : basicLowerBand)
const lastSuperTrend = superTrends[superTrends.length - 1]
superTrends.push(!lastSuperTrend && currentCandling > lastLowerBand ? true : lastSuperTrend && currentCandling < lastUpperBand ? false : lastSuperTrend)
}
}
}
return superTrends[superTrends.length - 1]
}
// Running the super trend
const supertrendResult = await superTrend(candles, 2, 14)
Again any help is appreciated!
Heres some more resources i am using:
RMA Calculation (in pinescript)
pine_rma(source, length) =>
alpha = 1 / length
sum = 0.0
sum := na(sum[1]) ?
ta.sma(source, length) :
alpha * source + (1 - alpha) * nz(sum[1])
plot(pine_rma(close, 15))
TR Calculation
true range = max[(high - low), abs(high - previous close), abs (low - previous close)]

I found my problem. Its with the way did the splice
const candlesATR = await this.ATR(candles.slice(i - (limit * 4), limit * 4), multiplier, limit)
Should be:
const candlesATR = await this.ATR(candles.slice(i - (limit * 4), i), multiplier, limit)

Related

How to find the middle letter among the specified letters?

I've created a function that accepts 2 parameters to find the middle value between those parameters in an Alphabet variable.
Example:
the middle part between Q and U is S,
the middle part between R and U is ST,
the middle part between T and Z is W,
What I'm confused about is how do I take the value one by one starting at index 1 in the Alphabet variable?
function letterMiddleValue(a, b) {
let alpha1 = Alphabet.indexOf(a);
let alpha2 = Alphabet.indexOf(b);
let center = (alpha1 + alpha2) / 2;
let letterLength;
if (center % 2 == 1) {
letterLength = 1;
} else {
letterLength = 2;
}
return Alphabet.substring(center, center + letterLength);
}
var Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
console.log(letterMiddleValue("Q", "U"));
console.log(letterMiddleValue("R", "U"));
console.log(letterMiddleValue("T", "Z"));
I think there is an alternative approach, which is just to calculate the distance between and then decide whether to extract one or two characters.
This also copes with there being nothing between, or the b value coming lower down the alphabet (or not being in the defined Alphabet)
function letterMiddleValue(a, b) {
const aPos = Alphabet.indexOf(a);
const bPos = Alphabet.indexOf(b);
const len = bPos - aPos
if (len < 2) {
return '[none]'
}
const start = aPos + (len / 2)
const end = start + 1 + (len % 2)
return Alphabet.slice(Math.floor(start), end)
}
const Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
console.log(letterMiddleValue("Q", "U")); // S
console.log(letterMiddleValue("R", "U")); // ST
console.log(letterMiddleValue("T", "Z")); // W
console.log(letterMiddleValue("A", "B")); // none
console.log(letterMiddleValue("A", "C")); // B
console.log(letterMiddleValue("A", "D")); // BC
console.log(letterMiddleValue("A", "A")); // none
console.log(letterMiddleValue("Z", "A")); // none
console.log(letterMiddleValue("z", "A")); // none
console.log(letterMiddleValue("Z", "a")); // none
Center for r & u is 18.5. 18.5 % 2 is 0.5 so you need to check for 0.5 condition:
function letterMiddleValue(a, b) {
let alpha1 = Alphabet.indexOf(a);
let alpha2 = Alphabet.indexOf(b);
let center = (alpha1 + alpha2) / 2;
let letterLength;
if (center % 2 == 0.5) {
letterLength = 2;
} else {
letterLength = 1;
}
return Alphabet.substring(center, center + letterLength);
}
var Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
console.log(letterMiddleValue("Q", "U"));
console.log(letterMiddleValue("R", "U"));
console.log(letterMiddleValue("T", "Z"));
That's because how you calculate center.
(7 + 5) / 2 = 6.5
(6 + 10) / 2 = 8
and modulo gives back what's not dividable by 2
I hope this helps you
const findBetween = (first, last) => {
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const splited = alphabet.split("");
const firstIdx = splited.findIndex((char) => char == first);
const lastIdx = splited.findIndex((char) => char == last);
const result = [];
splited.forEach((char, i) => {
i > firstIdx && i < lastIdx && result.push(char);
});
return result.join("");
};
console.log("7-Find Middle Alphabet");
console.log("QU", findBetween("Q", "U"));
console.log("RU", findBetween("R", "U"));
console.log("TZ", findBetween("T", "Z"));
console.log("QZ", findBetween("Q", "Z"));

My test task for trainee. Javascript - quadratic equation and game theory

I've been thinking that I found solution, but tests fail (I have no access to tests).
The task:
We want to organize a chess tournament.
One day - one game.
And as entry point we have number of days - that we can afford.
Tournament rules:
We have number of teams: if number is even - we divide number of teams by 2 and that's the number of games - so they fight 1 vs 1 in pairs, who lost is excluded from the competition.
For example : 20 teams => 10 games => 10 teams left.
And if the even number of teams left - we make the same iteration: divide by 2 into pairs.
10 teams - 5 games - 5 teams left.
And now we have Odd number!
If we have odd number from start or get it in process - number of games is counted by another rule:
everyone needs to play with everyone. So the formula is = n * (n-1) / 2.
For example 5 teams = 10 games.
Or any odd number (to sum it all - if we have Odd number we always ignore first rule with pairs).
So:
We have number of days and need to find all possible team numbers for this days, it can be more than one value, for example : 3 days, number of teams that is correct is 3 and 4;
Entry point 3, function logs 3 and 4;
If that's impossible to find teams - return -1;
1 ≤ 𝑁 ≤ 1000000
Here is my solution - It ouputs correct values, but tests fail
Can someone help to find what I did wrong?
(Sorry for code mess, I had different thoughts every time)
function solve(input) {
let quadraticEquation = (number) => {
let a = 1;
let b = -1;
let c = -number*2
if(a == 0)
return 'false';
let res = {};
let D = b * b - 4 * a * c;
let tmp = [];
if(D < 0)
return 'false';
res['discriminant'] = D;
if(D == 0)
res["quadratic roots"] = (-b + Math.sqrt(D)) / (2 * a);
else if(D > 0){
tmp.push((-b + Math.sqrt(D)) / (2 * a));
tmp.push((-b - Math.sqrt(D)) / (2 * a));
res["quadratic roots"] = tmp;
}
return tmp;
}
let start = input;
let xValues = quadraticEquation(start).map(item => Math.abs(item)).filter(item => item % 2 !== 0);
let arrayToUse = []
for(i = 0; i <= xValues[xValues.length-1]; i++) {
if( i === 1) {
continue;
}
if (!(i%2)) {
continue
}
arrayToUse.push(i);
}
const answer = [];
arrayToUse.forEach(item => {
let startValue = item;
let fightsNumber = item * (item - 1) / 2;
let loop = 0;
if (fightsNumber === start) {
answer.push([startValue, loop])
} else {
do {
loop++;
fightsNumber += startValue;
if (fightsNumber === start) {
answer.push([item, loop] )
}
startValue *= 2;
} while (fightsNumber < start)
}
})
function getTeams (answer) {
const finalResult = [];
answer.forEach(item => {
if(item[1] === 0) {
finalResult.push(item[0]);
} else {
let initialValue = item[0];
for(i=0; i < item[1]; i++) {
initialValue += item[0];
item[0] *= 2;
}
finalResult.push(initialValue);
}
})
let initialValue = 2;
let fightCounter = 0;
for (i = 0; i <= start; i++) {
fightCounter += initialValue / 2
if (fightCounter === start) {
finalResult.push(initialValue);
}
initialValue *= 2;
}
return finalResult;
}
let finalString = ''
const arrayToLog = getTeams(answer).sort((a,b) => a - b);
if(arrayToLog.length !== 0) {
arrayToLog.forEach(item => {
finalString += item + '\n';
})
} else {
finalString += -1;
}
return finalString;
}
console.log(solve(325))
console.log(solve(3))
console.log(solve(15))
console.log(solve(21))
console.log(solve(10))
console.log(solve(1))
console.log(solve(5))
console.log(solve(9))
Note: for those who didn't see it, this is a followup to the earlier question Backward recurssion.
That's a lot of code for a fairly simple problem. I don't have the inclination or the time right now to go through it carefully.
So here is how I might solve this problem, using brute force to look for all the resulting number of days for any number of players up to days + 1, and then filtering out what we want from that list:
const range = (lo, hi) =>
[... Array (hi - lo + 1)] .map ((_, i) => i + lo)
const qwe = (n) =>
n % 2 == 1
? n * (n - 1) / 2
: n / 2 + qwe (n / 2)
const solve = (days) => {
const daysByPlayers = range (1, days + 1) .map (qwe)
const res = daysByPlayers .reduce ((a, d, i) => d == days ? [...a, i + 1] : a, [])
return res .length == 0 ? [-1] : res
}
const tests = [325, 3, 15, 21, 10, 1, 5, 9]
tests .forEach (n => console .log (`${n}: ${JSON.stringify(solve(n))}`))
.as-console-wrapper {max-height: 100% !important; top: 0}
This matches your output as far as I can tell. But I don't know what tests you say are failing, so perhaps there's something wrong with this as well.
There is extra complexity in here because of your desire for a -1 output in the missing cases. If an empty array would serve, then this is cleaner:
const solve = (days) =>
rng (1, days + 1) .map (qwe)
.reduce ((a, d, i) => d == days ? [...a, i + 1] : a, [])
And there are many variants. This one returns the maximum number of players that can be accommodated in some number of days up to the given value:
const solve = (days) => {
const daysByPlayers = rng (1, days + 1) .map (qwe)
const max = Math.max (... daysByPlayers .filter (d => d <= days))
return days - daysByPlayers .reverse () .indexOf (max) + 1
}
And this one gives you all the possible number of players available to achieve that maximum:
const solve = (days) => {
const daysByPlayers = rng (1, days + 1) .map (qwe)
const max = Math.max (... daysByPlayers .filter (d => d <= days))
return daysByPlayers .reduce ((a, d, i) => d == max ? [...a, i + 1] : a, [])
}
(For instance, solve (20) would return [10, 16], which are the possible number of players to handle in exactly 15 days, the largest value found no larger than 20.)
And finally, we could list all the numbers of players we could accommodate in up to a certain number of days:
const solve = (days) => {
const daysByPlayers = rng (1, days + 1) .map (qwe)
const max = Math.max (... daysByPlayers .filter (d => d <= days))
return daysByPlayers .reduce ((a, d, i) => d <= max ? [...a, i + 1] : a, [])
}
For instance, solve (20) would return [1, 2, 3, 4, 5, 6, 8, 10, 12, 16], which are the only numbers of players that could be handled in no more than 20 days.
When I brought up the quadratic formula in the earlier question it was just to note that in certain instances, we can find an exact solution, solving for d in n * (n - 1) / 2) = d. That meant that if 8 * d + 1 is a perfect square, then we can choose n to be (1 + sqrt (8 * d + 1)) / 2 to find one solution. But that's not of any real help in the general case.

Problem calculating RSI from the Binance node API

I have written a program in javascript (Node) that is supposed to calculate the RSI using the Binance api.
The only thing is that my program does not calculate the "real" RSI. For example, if i set my period to be 14 days, as the formula says - I get an RSI value equal to ~28 and the real RSI being 38.
If i change the period and set it to 20, i get an RSI pretty close to the real one, with mine being 39 and the real one being 38.
I can't figure it out.what am i doing wrong. Any suggestion?
Here is my code :
const binance = require('node-binance-api')().options({
APIKEY: 'xxx',
APISECRET: 'xxx',
useServerTime: true,
test: true // True = SandboxMode
});
/* VARIABLES */
let listClose = [];
let changeUp = 0;
let changeDown = 0;
let last_closeHigh = 0;
let last_closeLow = 0;
let current_time = Date.now();
let period = 20;
function calculateRSI() {
console.log("Generating RSI");
binance.candlesticks("ETHBTC", "1d", (error, ticks, symbol) => {
for (i = 0; i < ticks.length; i++) {
let last_tick = ticks[i];
let [time, open, high, low, close, volume, closeTime, assetVolume, trades, buyBaseVolume, buyAssetVolume, ignored] = last_tick;
listClose.push(close);
if (i == ticks.length -1 ) {
for (x = 0; x < ticks.length; x++) {
previous_close = (parseFloat(listClose[x-1]));
current_close = (parseFloat(listClose[x]));
// HIGH
if (current_close > previous_close) {
upChange = current_close - previous_close;
changeUp += upChange;
if (x == ticks.length -1) {
last_closeHigh = current_close - previous_close;
}
}
// LOW
if (previous_close > current_close) {
downChange = previous_close - current_close;
changeDown += downChange;
if (x == ticks.length - 1) {
last_closeLow = previous_close - current_close;
}
}
if (x == ticks.length-1) {
AVGHigh = changeUp / period;
AVGLow = changeDown / period;
Upavg = (AVGHigh * (period -1) + last_closeHigh) / (period);
Downavg = (AVGLow * (period -1) + last_closeLow) / (period);
RS = Upavg / Downavg;
RSI = (100 - (100 / (1 + RS)));
console.log(RSI);
return RSI;
}
}
}
}
}, {
limit: period,
endTime: current_time
});
}
calculateRSI();
Sorry for jumping on this late...but the reason for this is using the Sandbox.
Binance Sandbox is awful and should never be used, it gives bad values.

More concise alternative to JavaScript conditional ternary operator?

There are many cases where using a conditional ternary operator allows for preferring const over let:
let scaleFactor = 1;
if (prev.scale < 1 && current.scale < 1) {
scaleFactor = 5;
}
With ternary and const:
const scaleFactor =
prev.scale < 1 && current.scale < 1 ? 5 : 1;
I see and use this pattern a lot. Is there a more concise way to write this that I've been missing?
Update:
An example of alternative to ternary that is both shorter and more readable
const t = tX > 0
? 0
: tX < width - width * scale
? width - width * scale
: tX
const t = Math.max(Math.min(tX, 0), width - width * scale)
Although I completely agree with some of the people commenting on your question that short != concise, I do think your question is valid. For many cases where you have the pattern if set value to X else to Y, you can do an expression that involves the boolean conditions as factors. Some options:
if (C)
value = X
else
value = Y
can be converted to
value = C * X + !C * Y;
Same thing can be written as:
value = X + !C * (Y - X)
I am a game developer, and it's quite often that I need something like:
// 1 if the right arrow was pressed, -1 for the left, 0 otherwise
var changeInMovementX = hasRightArrowBeenPressed - hasLeftArrowBeenPressed;
// same for up and down
var changeInMovementY = hasTopArrowBeenPressed - hasDownArrowBeenPressed;
// move 1 pixel in the given directions
player.move(changeInMovementX, changeInMovementX);
For your specific example, you might consider something like:
const shouldIncreaseScale = prev.scale < 1 && current.scale < 1;
const scaleIncrease = 4;
const scaleFactor = 1 + shouldIncreaseScale * scaleIncrease;
In short, language feature like switch expression doesn't yet exist in Javascript. But there are a few things you can do depending on your liking
IIFE with switch statement that returns
const a = 5
const c1 = (() => {
switch (a) {
case 1: {
return 2
}
case 2: {
return 3
}
default: {
return 4
}
}
})()
console.log(c1)
IIFE with if statement
const c2 = (() => {
if (a === 1) {
return 2
} else if (a < 10 && b > 10) {
return 3
} else {
return 4
}
})()
console.log(c2)
custom helper
// define this once
const conditionalHelper = (cases, defaultValue) => {
for (let i = 0; i < cases.length; i++) {
const [predicate, value] = cases[i]
if (predicate()) {
return value
}
}
return defaultValue
}
const c3 = conditionalHelper(
[
[() => a === 1, 2],
[() => a < 10 && b > 10, 3],
],
4
)
console.log(c3)

How can I round to an arbitrary number of significant digits with JavaScript?

I tried below sample code
function sigFigs(n, sig) {
if ( n === 0 )
return 0
var mult = Math.pow(10,
sig - Math.floor(Math.log(n < 0 ? -n: n) / Math.LN10) - 1);
return Math.round(n * mult) / mult;
}
But this function is not working for inputs like
sigFigs(24730790,3) returns 24699999.999999996
and sigFigs(4.7152e-26,3) returns: 4.7200000000000004e-26
If anybody has working example please share.
Thanks.
You can try javascript inbuilt method-
Number( my_number.toPrecision(3) )
For Your case try
Number( 24730790.0.toPrecision(5) )
For your refrence and working example you can see link
First of all thanks to everybody, it would be a hard task without these snippets shared.
My value added, is the following snippet (see below for complete implementation)
parseFloat(number.toPrecision(precision))
Please note that if number is, for instance, 10000 and precision is 2, then number.toPrecision(precision) will be '1.0e+4' but parseFloat understands exponential notation.
It is also worth to say that, believe it or not, the algorithm using Math.pow and logarithms posted above, when run on test case formatNumber(5, 123456789) was giving a success on Mac (node v12) but rising and error on Windows (node v10). It was weird so we arrived at the solution above.
At the end I found this as the definitive implementation, taking advantage of all feedbacks provided in this post. Assuming we have a formatNumber.js file with the following content
/**
* Format number to significant digits.
*
* #param {Number} precision
* #param {Number} number
*
* #return {String} formattedValue
*/
export default function formatNumber (precision, number) {
if (typeof number === 'undefined' || number === null) return ''
if (number === 0) return '0'
const roundedValue = round(precision, number)
const floorValue = Math.floor(roundedValue)
const isInteger = Math.abs(floorValue - roundedValue) < Number.EPSILON
const numberOfFloorDigits = String(floorValue).length
const numberOfDigits = String(roundedValue).length
if (numberOfFloorDigits > precision) {
return String(floorValue)
} else {
const padding = isInteger ? precision - numberOfFloorDigits : precision - numberOfDigits + 1
if (padding > 0) {
if (isInteger) {
return `${String(floorValue)}.${'0'.repeat(padding)}`
} else {
return `${String(roundedValue)}${'0'.repeat(padding)}`
}
} else {
return String(roundedValue)
}
}
}
function round (precision, number) {
return parseFloat(number.toPrecision(precision))
}
If you use tape for tests, here there are some basic tests
import test from 'tape'
import formatNumber from '..path/to/formatNumber.js'
test('formatNumber', (t) => {
t.equal(formatNumber(4, undefined), '', 'undefined number returns an empty string')
t.equal(formatNumber(4, null), '', 'null number return an empty string')
t.equal(formatNumber(4, 0), '0')
t.equal(formatNumber(4, 1.23456789), '1.235')
t.equal(formatNumber(4, 1.23), '1.230')
t.equal(formatNumber(4, 123456789), '123500000')
t.equal(formatNumber(4, 1234567.890123), '1235000')
t.equal(formatNumber(4, 123.4567890123), '123.5')
t.equal(formatNumber(4, 12), '12.00')
t.equal(formatNumber(4, 1.2), '1.200')
t.equal(formatNumber(4, 1.234567890123), '1.235')
t.equal(formatNumber(4, 0.001234567890), '0.001235')
t.equal(formatNumber(5, 123456789), '123460000')
t.end()
})
How about automatic type casting, which takes care of exponential notation?
f = (x, n) => +x.toPrecision(n)
Testing:
> f (0.123456789, 6)
0.123457
> f (123456789, 6)
123457000
> f (-123456789, 6)
-123457000
> f (-0.123456789, 6)
-0.123457
> f (-0.123456789, 2)
-0.12
> f (123456789, 2)
120000000
And it returns a number and not a string.
Unfortunately the inbuilt method will give you silly results when the number is > 10, like exponent notation etc.
I made a function, which should solve the issue (maybe not the most elegant way of writing it but here it goes):
function(value, precision) {
if (value < 10) {
value = parseFloat(value).toPrecision(precision)
} else {
value = parseInt(value)
let significantValue = value
for (let i = value.toString().length; i > precision; i--) {
significantValue = Math.round(significantValue / 10)
}
for (let i = 0; significantValue.toString().length < value.toString().length; i++ ) {
significantValue = significantValue * 10
}
value = significantValue
}
return value
}
If you prefer having exponent notation for the higher numbers, feel free to use toPrecision() method.
if you want to specify significant figures left of the decimal place and replace extraneous placeholders with T B M K respectively
// example to 3 sigDigs (significant digits)
//54321 = 54.3M
//12300000 = 12.3M
const moneyFormat = (num, sigDigs) => {
var s = num.toString();
let nn = "";
for (let i = 0; i <= s.length; i++) {
if (s[i] !== undefined) {
if (i < sigDigs) nn += s[i];
else nn += "0";
}
}
nn = nn
.toString()
.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,")
.replace(",000,000,000", "B")
.replace(",000,000", "M")
.replace(",000", "k");
if (
nn[nn.length - 4] === "," &&
nn[nn.length - 2] === "0" &&
nn[nn.length - 1] === "0"
) {
let numLetter = "K";
if (parseInt(num) > 999999999999) numLetter = "T";
else if (parseInt(num) > 999999999) numLetter = "B";
else if (parseInt(num) > 999999) numLetter = "M";
console.log("numLetter: " + numLetter);
nn = nn.toString();
let nn2 = ""; // new number 2
for (let i = 0; i < nn.length - 4; i++) {
nn2 += nn[i];
}
nn2 += "." + nn[nn.length - 3] + numLetter;
nn = nn2;
}
return nn;
};

Categories

Resources