Brokerage fee calculator: tiered commission, bps fee with a floor, and a maker/taker exchange fee
Shows clamp/max for floors and caps, and match on a tier bucket.
A Soma program from the finance part of the verified corpus: 57 lines, cells FeeCalculator, FeeTests. It passes soma check with soma 3.2.4.
How it is built
The cell FeeCalculator declares 4 signals in its face: bps_fee(notional: Float, bps: Float, min_fee: Float) -> Float, tiered_commission(shares: Int) -> Float, exchange_fee(notional: Float, is_maker: Bool) -> Float and net_proceeds(notional: Float, bps: Float, min_fee: Float) -> Float. Handlers: bps_fee (line 14), tiered_commission (line 20), exchange_fee (line 31) and net_proceeds (line 37).
The file also holds 1 test cell (FeeTests) with 6 assertions, run by soma test.
The source
fee_calculator.cell
// Brokerage fee calculator: tiered commission, bps fee with a floor,
// and a maker/taker exchange fee. Shows clamp/max for floors and caps,
// and match on a tier bucket.
cell FeeCalculator {
face {
signal bps_fee(notional: Float, bps: Float, min_fee: Float) -> Float
signal tiered_commission(shares: Int) -> Float
signal exchange_fee(notional: Float, is_maker: Bool) -> Float
signal net_proceeds(notional: Float, bps: Float, min_fee: Float) -> Float
}
// fee = notional * bps/10000, but never below the minimum
on bps_fee(notional: Float, bps: Float, min_fee: Float) {
let raw = notional * bps / 10000.0
return max(raw, min_fee)
}
// per-share commission that drops as volume grows
on tiered_commission(shares: Int) {
let rate = match shares {
0..999 -> 0.010
1000..9999 -> 0.005
_ -> 0.002
}
let fee = shares * rate
return max(fee, 1.0)
}
// makers get a rebate (negative fee), takers pay
on exchange_fee(notional: Float, is_maker: Bool) {
if is_maker { return notional * -0.0001 }
return notional * 0.0003
}
// sell-side: what you keep after the fee
on net_proceeds(notional: Float, bps: Float, min_fee: Float) {
return notional - bps_fee(notional, bps, min_fee)
}
}
cell test FeeTests {
rules {
// 1M notional at 2 bps = 200
assert abs(bps_fee(1000000.0, 2.0, 1.0) - 200.0) < 0.0001
// tiny trade hits the minimum floor
assert abs(bps_fee(100.0, 2.0, 1.0) - 1.0) < 0.0001
// mid tier: 5000 shares * 0.005 = 25
assert abs(tiered_commission(5000) - 25.0) < 0.0001
// maker rebate is negative
assert exchange_fee(1000000.0, true) < 0.0
// taker pays
assert exchange_fee(1000000.0, false) > 0.0
assert abs(net_proceeds(10000.0, 5.0, 1.0) - 9995.0) < 0.0001
}
}What the toolchain says
$ soma check fee_calculator.cell
✓ All checks passed.$ soma test fee_calculator.cell
test FeeTests ...
✓ assert abs(bps_fee(1000000.0, 2.0, 1.0) - 200.0) < 0.0001
✓ assert abs(bps_fee(100.0, 2.0, 1.0) - 1.0) < 0.0001
✓ assert abs(tiered_commission(5000) - 25.0) < 0.0001
✓ assert exchange_fee(1000000.0, true) < 0.0
✓ assert exchange_fee(1000000.0, false) > 0.0
✓ assert abs(net_proceeds(10000.0, 5.0, 1.0) - 9995.0) < 0.0001
6 tests: 6 passed, 0 failed$ soma verify fee_calculator.cell
Verifying fee_calculator.cell...
No state machine in this program: nothing to prove beyond `soma check` (invariants and lifecycles are what verify proves).
VERIFY OK (vacuous — no state machine)