Functional Weave
Code in Rust

finance.markup-price

Selling price from a cost and a target margin or markup in basis points, with explicit rounding.

1.0.0 · published 2026-10-03 by charlie · Anterra

Pinned by 21 tests, run in TypeScript, Python and Rust.

What it does

The inverse of `finance.margin`: given a cost and a target, the price.

- **markup** is profit over cost: `price = cost × (1 + markup)`. A 50% markup on 100.00 is 150.00. - **margin** is profit over price: `price = cost / (1 - margin)`. A 50% margin on 100.00 is 200.00, not 150.00.

For example

  • markup_price(£60.00, 40%, margin, half-up) → £100.00 60.00 at a 40 percent margin is 100.00, as finance.margin says
  • markup_price(£100.00, 50%, markup, half-up) → £150.00 100.00 at a 50 percent markup is 150.00
  • markup_price(£100.00, 50%, margin, half-up) → £200.00 100.00 at a 50 percent margin is 200.00, not 150.00

The function

The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.

pub fn markup_price(cost: &Money, basis_points: i64, basis: &str, mode: &str) -> Money
costMoneywhat the item costs to buy or make; not negative
basis_pointsintthe target: 4000 is a 40% margin or a 40% markup, as basis says
basisPricingBasismargin (profit over price) or markup (profit over cost)
modeRoundingModehow to round the price to a minor unit; use up to never fall short of a margin
returnsMoney

The type it declares, generated into your project

// PricingBasis is a string in Rust, one of: "margin", "markup".
// Parameters take it as &str and results hold it as String.

Your code names it in one line, in the file that uses it

fune!(finance.markup-price@^1);  // then call markup_price(…)
impl/rust.rs · 51 lines · open · raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::math_round_div::round_div;  ← from math.round-div ^1.0.0 · built alongside by fune
use super::money_amount::{money, money_from_value, money_to_value, Money};  ← from money.amount ^1.0.0 · built alongside by fune

const MAX_SAFE: i128 = 9_007_199_254_740_991;

/// The selling price that hits a target margin or markup on `cost`.
///
/// Margin divides by the price and markup by the cost, so the same 5000 basis
/// points means 200.00 or 150.00 on a cost of 100.00; the basis is explicit.
///
/// # Panics
/// Panics on a negative cost, a margin of 100% or more, a markup below -100%,
/// an unknown basis, or a calculation beyond 2^53 - 1.
pub fn markup_price(cost: &Money, basis_points: i64, basis: &str, mode: &str) -> Money {
    if cost.minor < 0 {
        panic!("cost must not be negative, received {}", cost.minor);
    }
    let (numerator, denominator): (i128, i128) = match basis {
        "markup" => {
            if basis_points < -10000 {
                panic!("a markup of {} basis points gives a negative price", basis_points);
            }
            (cost.minor as i128 * (10000 + basis_points as i128), 10000)
        }
        "margin" => {
            if basis_points >= 10000 {
                panic!("a margin of {} basis points has no finite price", basis_points);
            }
            (cost.minor as i128 * 10000, 10000 - basis_points as i128)
        }
        other => panic!("unknown pricing basis \"{}\"", other),
    };
    // i64 could go further, but TypeScript cannot, and all three must agree.
    if numerator > MAX_SAFE {
        panic!("the price calculation exceeds 2^53 - 1");
    }
    money(
        round_div(numerator as i64, denominator as i64, mode),
        &cost.currency,
    )
}

pub fn fune_vector(args: &[Value]) -> Value {
    money_to_value(&markup_price(
        &money_from_value(&args[0]),
        args[1].as_i64(),
        args[2].as_str(),
        args[3].as_str(),
    ))
}

Install

fune build

With that line in your source, in a Rust project (language rust in fune.project), fune build resolves it and its 2 dependencies, pins them in fune.lock, downloads only the Rust package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. A crate’s build.rs runs it before every compile. Or pin a range in fune.project and build in one step:

fune add finance.markup-price
Download for Rust finance.markup-price-1.0.0-rust.fune · 11,824 bytes sha256 14b843beb0c500c2a066808deae53bbc96d9bc30d641cfe6fbcaf28011d30347

The manifest, vectors and README with only the Rust implementation. Install it without the registry with fune add ./finance.markup-price-1.0.0-rust.fune, or fetch it from a terminal with fune pull finance.markup-price@1.0.0:rust.

The whole function, every language, is one file too: finance.markup-price-1.0.0.fune, 15,136 bytes, sha256 bfaa16042b3d9d9018be2d19e2ead48b179a814facd04d7ea13796de6f55ac33. It installs into a project of any language.

Customise it in your app

The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.

before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.

// fune: before finance.markup-price

after — your function gets the result and the arguments, and returns the final result.

// fune: after finance.markup-price

replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.

// fune: replace math.round-div in finance.markup-price
// fune: replace money.amount in finance.markup-price

step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show finance.markup-price --steps.

// fune: step finance.markup-price after <n|label>

Tests

A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.

CaseArgumentsExpected
60.00 at a 40 percent margin is 100.00, as finance.margin says £60.00, 40%, margin, half-up → £100.00
100.00 at a 50 percent markup is 150.00 £100.00, 50%, markup, half-up → £150.00
100.00 at a 50 percent margin is 200.00, not 150.00 £100.00, 50%, margin, half-up → £200.00
6.99 at a 30.03 percent margin is 9.99, finance.margin backwards £6.99, 30.03%, margin, half-up → £9.99
6.99 at a 42.92 percent markup is 9.99, finance.margin backwards £6.99, 42.92%, markup, half-up → £9.99
10.00 at a 30 percent margin rounds half-up to 14.29 £10.00, 30%, margin, half-up → £14.29
rounding down misses a 30 percent margin: 14.28 is 29.97 percent £10.00, 30%, margin, down → £14.28
rounding up never falls short of the target £10.00, 30%, margin, up → £14.29
an exact half penny rounds to even: 1.5 becomes 2 £0.01, 50%, markup, half-even → £0.02
zero margin is the cost £43.21, 0%, margin, half-up → £43.21
Show the other 11 tests
CaseArgumentsExpected
zero markup is the cost £43.21, 0%, markup, half-up → £43.21
zero cost is a zero price £0.00, 40%, margin, half-up → £0.00
a negative markup is a discount below cost £100.00, -20%, markup, half-up → £80.00
a markup of -100 percent is a giveaway £5.00, -100%, markup, half-up → £0.00
a negative margin prices below cost, as finance.margin reports it £120.00, -20%, margin, half-up → £100.00
yen at a 20 percent margin ¥800, 20%, margin, half-up → ¥1,000
a 100 percent margin has no finite price £10.00, 100%, margin, half-up → error: has no finite price
a markup below -100 percent would be a negative price £10.00, -100.01%, markup, half-up → error: gives a negative price
a negative cost is an error -£10.00, 20%, markup, half-up → error: cost must not be negative
the basis has no default and must be named £10.00, 20%, profit, half-up → error: unknown pricing basis
a calculation beyond 2^53 - 1 is refused £9,007,199,254,741.00, 0%, margin, half-up → error: exceeds 2^53 - 1

More from the author

Confusing the two is the classic pricing mistake - a retailer aiming for a 40% margin who adds 40% to cost gets a 28.57% margin - so the basis is a required argument with no default.

The price is rounded once, to the currency's minor unit, with the `math.round-div` mode you pass. Rounding can land a fraction of a basis point either side of the target. Use `up` for a margin or markup target you must not fall below: 1,000 at a 30% margin is 1,428.57..., and `down` gives 1,428, which is a 29.97% margin, while `up` gives 1,429.

The results agree with `finance.margin`: several vectors here are that capability's vectors run backwards (60.00 at a 40% margin is 100.00; 6.99 at a 30.03% margin is 9.99; 120.00 at a -20% margin is 100.00), and feeding the price back into `finance.margin` returns the target to within its rounding.

Edges: a margin of 100% or more has no finite price and is an error. A negative margin or markup is a price below cost and is allowed (clearance, loss leaders); a markup of -100% is a price of zero, and below that the price would be negative, which is an error. A negative cost is an error. Zero cost gives a zero price on either basis. `cost × 10000` (or `cost × (10000 + markup)`) must stay within 2^53 - 1, which is costs up to about 900 million pounds; beyond that the calculation is refused rather than rounded.

Files

PathBytes
README.md1,666
impl/python.py1,599
impl/rust.rs1,872
impl/typescript.ts1,567
vectors.json5,487