# Build a Crypto Position-Risk Calculator in JavaScript

> Source: <https://dev.to/largo_winch_5b3d18d485af6/build-a-crypto-position-risk-calculator-in-javascript-195e>
> Published: 2026-08-31 05:17:22+00:00

AI disclosure:This article was prepared with AI assistance from my real trading notes, calculations, and review. The code and examples were checked before publication.

Leverage is often the first number traders look at, but it is not the number that tells us how many dollars a stop-loss may cost. A more useful pre-trade calculation combines position quantity, entry price, stop price, fees, and a slippage buffer.

I learned the importance of that calculation after losing about $100 on a leveraged trade where I forgot to place a stop. The market analysis was not the main failure; the position had no predefined protection. Since then, I have wanted a small tool that answers the risk question before an order is placed.

In this tutorial, we will build that tool in plain JavaScript.

For a linear USDT-margined position, the price loss between the entry and stop can be approximated as:

```
price loss = quantity × |entry price - stop price|
```

For example, a position of 1,500 tokens entered at `$0.620`

with a stop at `$0.608`

has a stop distance of `$0.012`

per token:

```
1,500 × 0.012 = $18
```

That `$18`

does not yet include trading fees or slippage.

Leverage changes the margin required to control the position. It does not change the dollar loss created by the same quantity moving through the same price distance.

```
function calculatePositionRisk({
  side,
  quantity,
  entryPrice,
  stopPrice,
  leverage = 1,
  entryFeeRate = 0,
  exitFeeRate = 0,
  slippageRate = 0,
}) {
  const numericInputs = {
    quantity,
    entryPrice,
    stopPrice,
    leverage,
    entryFeeRate,
    exitFeeRate,
    slippageRate,
  };

  for (const [name, value] of Object.entries(numericInputs)) {
    if (!Number.isFinite(value) || value < 0) {
      throw new TypeError(`${name} must be a non-negative number`);
    }
  }

  if (quantity === 0 || entryPrice === 0 || stopPrice === 0) {
    throw new RangeError("quantity and prices must be greater than zero");
  }

  if (leverage < 1) {
    throw new RangeError("leverage must be at least 1");
  }

  if (!['long', 'short'].includes(side)) {
    throw new RangeError("side must be 'long' or 'short'");
  }

  const stopIsProtective =
    side === 'long' ? stopPrice < entryPrice : stopPrice > entryPrice;

  if (!stopIsProtective) {
    throw new RangeError(`stopPrice is not protective for a ${side} position`);
  }

  const notional = quantity * entryPrice;
  const stopNotional = quantity * stopPrice;
  const priceLoss = quantity * Math.abs(entryPrice - stopPrice);
  const entryFee = notional * entryFeeRate;
  const estimatedExitFee = stopNotional * exitFeeRate;
  const slippageBuffer = stopNotional * slippageRate;
  const estimatedTotalRisk =
    priceLoss + entryFee + estimatedExitFee + slippageBuffer;
  const approximateMargin = notional / leverage;

  return {
    notional,
    approximateMargin,
    stopDistance: Math.abs(entryPrice - stopPrice),
    stopDistancePercent:
      (Math.abs(entryPrice - stopPrice) / entryPrice) * 100,
    priceLoss,
    entryFee,
    estimatedExitFee,
    slippageBuffer,
    estimatedTotalRisk,
  };
}
```

The validation catches a surprisingly common mistake: placing the stop on the wrong side of the entry. A protective stop for a long position must sit below the entry, while a protective stop for a short position must sit above it.

Fee rates vary by exchange, account tier, and order type, so they must be passed as inputs rather than hard-coded as universal values.

``` js
const result = calculatePositionRisk({
  side: 'long',
  quantity: 1500,
  entryPrice: 0.620,
  stopPrice: 0.608,
  leverage: 10,
  entryFeeRate: 0.0005,
  exitFeeRate: 0.0005,
  slippageRate: 0.0005,
});

console.table(result);
```

Rounded to two decimals, the important outputs are:

```
console.log({
  notional: result.notional.toFixed(2),
  margin: result.approximateMargin.toFixed(2),
  priceLoss: result.priceLoss.toFixed(2),
  totalRisk: result.estimatedTotalRisk.toFixed(2),
});
```

The notional value is approximately `$930`

, while the approximate margin at 10x is `$93`

. The price loss at the stop remains `$18`

, before the additional fee and slippage estimates.

We can also solve the problem in reverse: given a dollar-risk budget, what is the maximum quantity?

```
function calculateMaxQuantity({
  riskBudget,
  entryPrice,
  stopPrice,
  entryFeeRate = 0,
  exitFeeRate = 0,
  slippageRate = 0,
}) {
  const riskPerUnit =
    Math.abs(entryPrice - stopPrice) +
    entryPrice * entryFeeRate +
    stopPrice * exitFeeRate +
    stopPrice * slippageRate;

  if (!Number.isFinite(riskBudget) || riskBudget <= 0) {
    throw new RangeError('riskBudget must be greater than zero');
  }

  if (!Number.isFinite(riskPerUnit) || riskPerUnit <= 0) {
    throw new RangeError('risk per unit must be greater than zero');
  }

  return riskBudget / riskPerUnit;
}

const maxQuantity = calculateMaxQuantity({
  riskBudget: 25,
  entryPrice: 0.620,
  stopPrice: 0.608,
  entryFeeRate: 0.0005,
  exitFeeRate: 0.0005,
  slippageRate: 0.0005,
});

console.log(maxQuantity.toFixed(2));
```

In a real trading interface, I would round the result down to the exchange's permitted lot step. Rounding up could exceed the chosen risk budget.

This is deliberately a pre-trade estimate, not a liquidation engine or price predictor. It does not account for every contract specification, funding payment, tiered maintenance margin rule, partial fill, gap, or exchange-specific fee detail.

Before using the result, verify:

A stop order is protection, not a promise of an exact fill. Fast markets can move beyond the trigger price.

The useful question is not only “How much could this trade make?” It is “How much should it lose if the idea is wrong?”

A small calculator cannot make a trade safe, but it can force the risk decision to happen before the market starts moving. For me, that is already a meaningful improvement over relying on memory and emotion.

**Risk notice:** Trading cryptocurrency derivatives involves significant risk and may not be suitable for everyone. Leverage amplifies gains and losses. This tutorial is educational and describes personal experience; it is not financial advice.

If you have independently decided to consider OKX, my referral link is:

**Referral code:** `34107975`

This article contains a referral link, and I may receive a reward if the applicable program conditions are met. Eligibility, tasks, availability, and rewards vary by region and can change. Review the current terms shown in your own OKX account before registering. Using the link would also help me earn a little additional income for my family, but please never trade money you cannot afford to lose.
