npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

forex-position-size

v0.1.0

Published

Forex position size and pip value for any pair and account currency: yen pairs, crosses, gold and silver, indices, lot steps, commission, and prop-firm daily loss headroom. No dependencies.

Readme

Position size calculator: npm library, embeddable widget, MT5 script

How many lots risk the amount you chose, for the stop you chose. The same sum in three forms:

| Form | For | Where | | --- | --- | --- | | forex-position-size | Developers: pip value and lot size for any pair and account currency | npm | | The widget | Websites and blogs: a position size calculator in one line of HTML | widget/ | | PositionSizeFromLine.mq5 | MetaTrader 5: size a trade from a stop line you drag on the chart | mt5/ |

None of them needs an account, a key or a network call. You type the prices; nothing you type leaves the page or the terminal.

The sum:

pip value per lot = pip size x contract size, turned into the account's currency
lots              = money at risk / (stop in pips x pip value per lot + commission per lot)

rounded down to the lot step, so the trade never risks more than you set. A position size is arithmetic, not advice: it does not say where a stop belongs or how much to risk, and a stop can fill past its price.

For the working in full, with yen pairs, crosses and gold, read Forexia's guide to the forex lot size calculator. More tools: Forexia trading tools.

The library

npm install forex-position-size

ES module with TypeScript types, no dependencies, Node 18+ and every browser.

import { positionSize } from 'forex-position-size';

positionSize({ balance: 10000, riskPct: 1, stopPips: 20, pair: 'EURUSD' });
// { lots: 0.5, units: 50000, risk: 100, riskPct: 1, pipValuePerLot: 10, stopPips: 20, warnings: [], ... }

Worked examples

Each of these is a test in test/index.test.js, worked by hand first.

| Trade | Working | Lots | | --- | --- | --- | | EURUSD, USD account, $10,000, 1%, 20 pips | $100 / (20 x $10) | 0.50 | | USDJPY at 150, USD account, $10,000, 1%, 30 pips | pip = 1,000 JPY / 150 = $6.67; $100 / (30 x $6.67) | 0.50 | | EURGBP, USD account, GBPUSD 1.27, $5,000, 2%, 25 pips | pip = 10 GBP x 1.27 = $12.70; $100 / $317.50 = 0.315 | 0.31 (risks $98.43) | | GBPJPY, EUR account, EURJPY 160, EUR 20,000, 0.5%, 40 pips | pip = 1,000 JPY / 160 = EUR 6.25; EUR 100 / EUR 250 | 0.40 | | EURUSD, EUR account, entry 1.0800, stop 1.0750 | 50 pips; pip = $10 / 1.08 = EUR 9.26; EUR 100 / EUR 462.96 = 0.216 | 0.21 | | XAUUSD, entry 2400, stop 2390, $10,000, 1% | $10 x 100 oz = $1,000 a lot; $100 / $1,000 | 0.10 (10 oz) | | XAGUSD, entry 30.00, stop 29.50, $10,000, 2% | $0.50 x 5,000 oz = $2,500 a lot; $200 / $2,500 | 0.08 (400 oz) | | EURUSD, 20 pips, $7 commission a lot, $100 risk | $100 / ($200 + $7) = 0.483 | 0.48 | | US30 (index), $1 a point a lot, 100 points, $1,000 risk | $1,000 / $100 | 10 |

Which prices it needs

A pip is worth a fixed amount in the pair's quote currency (the second one). To turn it into the account's currency the sum needs one price:

| Pair and account | Price needed | Where it comes from | | --- | --- | --- | | Quoted in the account's currency (EURUSD on USD) | none | | | The account's currency first (USDJPY on USD) | the pair's own price | entry, or price | | A cross (EURGBP on USD) | the quote currency against the account's (GBPUSD) | rates |

rates takes prices keyed by pair, in either direction ({ GBPUSD: 1.27 } or { 'USD/GBP': 0.787 }), and goes through USD or EUR when there is no direct price. rateNeeded(pair, accountCurrency) tells you which price to ask your user for.

API

| Function | Returns | | --- | --- | | positionSize(input) | { lots, units, rawLots, risk, riskPct, targetRisk, stopPips, stopDistance, direction, pipValuePerLot, riskPerLot, pipSize, contractSize, lotStep, warnings } | | pipValue({ pair, accountCurrency, rates, price, lots }) | { perLot, perLotInQuote, value, pipSize, contractSize, quoteToAccount } | | rateNeeded(pair, accountCurrency) | { pair: 'GBPUSD', reason: 'conversion' \| 'own-price' } or null | | conversionRate(from, to, rates) | units of to per unit of from, or null | | roundLots(raw, { lotStep, minLot, maxLot }) | { lots, below, capped } | | dailyLossHeadroom(input) | { headroom, dailyFloor, overallFloor, floor, binding, breached, headroomPct } | | positionSizeWithinLimits(sizeInput, headroomInput) | positionSize, with the risk cut to the headroom | | parsePair, marketPair, pipSizeOf, contractSizeOf | helpers |

positionSize input: balance; riskPct or riskAmount (wins); stopPips, or entry and stop; pair; accountCurrency (default USD); rates; price; and, for your broker, lotStep (0.01), minLot, maxLot, commissionPerLot (round turn, in the account's currency), pipSize, contractSize. For an index or any symbol that is not a currency pair, pass quoteCurrency, pipSize (the move you count, such as 1 point) and contractSize (money per point on one lot).

Bad input throws a PositionSizeError with a code ('balance', 'risk', 'stop', 'rate', ...). Soft problems come back in warnings: a size below the smallest lot (then lots is 0 and the warning says what the smallest lot would risk), a size capped at maxLot, a stop under one pip, a risk above 10% of the balance, a metal sized from pips.

Conventions

  • A pip is 0.01 on JPY and HUF pairs, 0.0001 on other currency pairs.
  • Gold counts $1.00 as one pip, as Forexia's charts and lot size calculator do. Brokers differ: some call $0.10 or $0.01 a pip. The lots come out the same if the stop is the same distance in price, so for metals give entry and stop, or set pipSize.
  • Silver counts $0.01 as one pip. A lot is 100,000 units of a currency, 100 oz of gold, 5,000 oz of silver. Check your broker's contract size and set contractSize if it differs.

Prop firm daily loss limits

dailyLossHeadroom says how much more an account can lose today before it breaks its daily loss limit, or its overall limit if that is closer:

import { dailyLossHeadroom, positionSizeWithinLimits } from 'forex-position-size';

// $100,000 account, 5% daily and 10% overall limits, the day began at $101,000, equity now $99,500
dailyLossHeadroom({ initialBalance: 100000, dayStart: 101000, equity: 99500, dailyLossPct: 5, maxLossPct: 10 });
// { dailyFloor: 96000, overallFloor: 90000, floor: 96000, binding: 'daily', headroom: 3500, breached: false, ... }

// Size a 1% trade, cut to what the account can still lose
positionSizeWithinLimits(
  { balance: 100000, riskPct: 1, stopPips: 20, pair: 'EURUSD' },
  { initialBalance: 100000, dayStart: 101000, equity: 96600, dailyLossPct: 5 }
); // lots: 3 (risk cut from $1,000 to $600)

Firms count the daily limit differently: of the starting balance or the day's, from the day-start balance or the higher of balance and equity, at a server time of their own. Pass the figures your firm's rules name (dayStart, dailyLossBase, dailyLossAmount, overallFloor for a trailing drawdown you track, openRisk for trades already open). To evaluate a whole challenge (daily and overall loss, trailing drawdown, trading days, consistency, the news rule) against an account's history, use PropSim's prop-firm-rules, which documents each firm's basis.

The widget

A position size calculator for any website: one HTML file with its styles and the library inline, no cookie, no storage, no request. The reader types every price, so it works offline and needs no data licence.

<div class="forexia-position-size" data-pair="EURUSD" data-currency="USD"><a href="https://forexiapro.com/tradingtools?via=position-size">Forexia</a></div>
<script async src="https://forexiapro.github.io/position-size/embed.js"></script>

embed.js turns each .forexia-position-size into a frame of index.html and keeps its height right as the reader opens its settings. Every data-* attribute is passed on:

| Option | Values | | --- | --- | | pair | EURUSD, XAUUSD, ... | | currency | The account's currency: USD, EUR, GBP, ... | | balance, risk | Starting figures (risk in percent) | | stop | prices (entry and stop, the default) or pips | | theme | light, dark, auto | | accent | A hex colour without #, such as f5a524 | | flat | 1 for a transparent background | | border | 0 to drop the frame's border |

Or link the frame yourself: <iframe src="https://forexiapro.github.io/position-size/?pair=GBPJPY&currency=EUR&theme=dark" ...>.

The calculator's credit link says "Forexia", and only that, on purpose: a keyword-stuffed link inside a widget is the kind search engines treat as spam. It goes to Forexia's trading tools.

Build. widget/index.html is built from widget/template.html and src/index.js:

npm run build:widget            # writes widget/index.html
node scripts/build-widget.mjs --check   # fails if it is out of date (CI runs this)

The MT5 script

mt5/PositionSizeFromLine.mq5 sizes a trade from a horizontal line you drag.

  1. Copy it to MQL5/Scripts/ (File → Open Data Folder) and compile it in MetaEditor, or download it from the MQL5 Code Base.
  2. Drag it onto a chart. The first time, it draws a red dashed line, PS_Stop. Drag it to your stop.
  3. Run it again. It writes the lots, the money at risk and the working on the chart and in the Experts log.
  • Entry: the current price (Ask for a buy, Bid for a sell; a stop below the price is a buy), or a horizontal line named PS_Entry for a pending order (limit or stop, chosen from where the line sits). A line named PS_Target adds the take profit and shows the reward in R.
  • The sum: stop distance / SYMBOL_TRADE_TICK_SIZE x SYMBOL_TRADE_TICK_VALUE_LOSS (falling back to SYMBOL_TRADE_TICK_VALUE), plus your commission per lot; rounded down to SYMBOL_VOLUME_STEP and kept within SYMBOL_VOLUME_MIN, SYMBOL_VOLUME_MAX and SYMBOL_VOLUME_LIMIT. It works on any symbol the broker gives a tick value for: currencies, metals, indices, crypto. It cross-checks against OrderCalcProfit and logs a gap over 1%, and warns when the margin is more than the free margin or the stop is inside the broker's stops level.
  • Inputs: risk percent of balance or equity, or a fixed amount; commission per lot; the three line names; PlaceOrder (off by default) and Confirm (asks before sending); magic number, slippage, comment.

With PlaceOrder on, it asks, then sends the order with the stop (and target) attached. Try it on a demo account first.

Develop

npm test                 # node:test, no dependencies
npm run build:widget

Licence

MIT. See LICENSE.


Part of the Forexia network: InterestRateDecision.com · Forexia Academy · TradingSignalBot · PropSim · OpenCharts