[HN Gopher] Show HN: Numscript, a declarative language to model ...
       ___________________________________________________________________
        
       Show HN: Numscript, a declarative language to model financial
       transactions
        
       Numscript is a simple, declarative language that helps you model
       financial transactions. You can do quite a few things with it, such
       as modeling:  * Payments involving vouchers and a user's prepaid
       balance * Complex funds destination scenario where the customer
       gets cash back * Configurable user credit balance spending
       transactions  The main idea is to take the pain out of describing
       of a system dealing with money movements should behave in
       traditional languages such as JS/TS/Go/Ruby etc, landing an
       expressive way to model these movements of value.  It is
       voluntarily broad in applicability--our customers use it today for
       use-cases ranging from marketplaces funds orchestration to home-
       grown loan management systems.  Once those transactions are
       modeled, they are to be picked up and committed to a system-of-
       record, ledgering system or executed on a set of payments and
       banking APIs.  It was initially a DSL we bundled into our Core
       Ledger product at Formance (YCS21) but we're giving it more
       autonomy now and started to make it standalone, with the idea that
       anyone could eventually bolt Numscript on top of their ledgering
       system. We're also exploring to make it natively compatible with
       other backends.  As part of this un-bundling, we've just shipping a
       playground which lets you fiddle with it without installing
       anything at https://playground.numscript.org/ (it works best on
       desktop).  Happy to chime in on any questions!
        
       Author : superzamp
       Score  : 110 points
       Date   : 2024-09-19 15:59 UTC (7 hours ago)
        
 (HTM) web link (playground.numscript.org)
 (TXT) w3m dump (playground.numscript.org)
        
       | robk wrote:
       | This seems cool but what's the intention with the USD/2 notation?
       | Is that for fractional sub-cent precision in rounding situations?
        
         | superzamp wrote:
         | It's indeed relative to cents in a sense, the idea is to force
         | you to declare the precision of the monetary amount you're
         | expressing.
         | 
         | You can see various interpretation of what "USD" means in the
         | wild, as some APIs will happily parse USD 100 as $1.00 while
         | some others might parse USD 100 as $100.00.
         | 
         | So we recommend this explicit [ASSET/SCALE AMOUNT] notation,
         | where SCALE describes the negative power of ten to multiply the
         | AMOUNT with to obtain the decimal value in the given ASSET.
         | 
         | It makes subsequent interaction with external systems much
         | easier. You can read a bit more about it here [1].
         | 
         | [1] https://docs.formance.com/stack/unambiguous-monetary-
         | notatio...
        
           | flockonus wrote:
           | What about units that cost sub-cent? For examples, I've seen
           | private company stocks being $0.0001
           | 
           | Not sure why you'd need to make a note in the internal
           | representation, vs. leave adapters to handle conversions.
        
             | bbor wrote:
             | Not to speak for them, but I think you've understood the
             | point exactly. You need to be able to support arbitrary
             | precision, but that support needs to be _intentional_ to
             | avoid errors. And you have to record that decision
             | somewhere if adapters are to correctly handle your outputs;
             | why not in the unit name?
        
             | petesergeant wrote:
             | If I understand their docs correctly, that's equivalent to
             | [USD/4 1]
        
           | tway_GdBRwW wrote:
           | Used to work at a payments company. Yes, you need to be
           | *very* explicit in how you model currency amounts and
           | precision. See also earlier post about Canadian rounding
           | rules. Some of the "logic" is regulatory/compliance driven.
           | 
           | ref child post about stocks trading for 0.0001. Yes, those
           | are real trades and (probably) fully legal etc, but I'm not
           | sure the Fed recognizes currency amounts less than 1 US cent
           | ($0.01), so the accounting rules and tax rules might not
           | match expectations based on generalized floating point math.
        
             | euroderf wrote:
             | Doesn't the Fed recognise mills ? They are not extinct.
        
           | girvo wrote:
           | Just to add to this, having also implemented a production
           | payment system, we did the same thing. One needs to be very
           | explicit about the scale and how it should be rounded and
           | dealt with, and how to operate on two different scale
           | systems. Quite a fun challenge, though I do not miss the edge
           | cases.
           | 
           | Our system was a payment system for childcare management
           | software, interfacing with banks and the government directly.
        
       | vslira wrote:
       | Super interesting and thanks for sharing. If I understood the
       | license page on the repository correctly, the DSL is MIT-licensed
       | since it's not within the enterprise directory, right?
        
         | superzamp wrote:
         | Yes exactly! There's actually two implementations, one tightly
         | knit to our ledger product located at [1] and the new,
         | standalone one (used by the playground) at [2]. In any case, in
         | both implementations, the DSL is indeed MIT.
         | 
         | [1]
         | https://github.com/formancehq/stack/tree/main/components/led...
         | 
         | [2] https://github.com/formancehq/numscript
        
       | bbor wrote:
       | To this noob, this seems like a problem that was pretty
       | convincingly solved by double entry accounting ledgers, but from
       | your post it sounds like this isn't a replacement-of but rather
       | an addition-to that model. What's a situation where an imperative
       | approach would be preferable to the traditional declarative
       | approach? My depth of knowledge pretty much starts and ends with
       | the below document, so apologies if this is obvious to experts!
       | 
       | https://beancount.github.io/docs/the_double_entry_counting_m...
       | 
       | E: the problem being "tracking transactions". Yes?
        
         | superzamp wrote:
         | That's a very good question. So the DSL here operates an
         | agnostic source/dest transaction model, which is akin to the
         | credit/debit model sans the semantic baggage. The goal of this
         | model is indeed to be "tracking transactions" in the abstract
         | sense, having the benefit of not forcing accounting decisions
         | too early on when there is (still yet) none.
         | 
         | For example, if you create a transaction moving money from
         | "@stripe:main" to "@acct:123" and "@acct:234", you're merely
         | representing the fact that you want this money to be moved.
         | Wether the movement is clearing off a liability or generating
         | revenue is another concern that you (in our model) want to take
         | care of in a separate layer, that will also likely involve some
         | intense intentionality and iterations from your accounting
         | team.
         | 
         | In a sense, it's as close to accounting than it is to
         | warehousing money, moving unitary boxes of it from one location
         | to another.
         | 
         | These two models have the same amount of information per entry,
         | so they can actually be converted from one to another, enabling
         | you to also represent some accounting-ish transactions with
         | this DSL, e.g. with a send [USD/2 100] from @ar:invoices:1234
         | to @sales.
        
           | bbor wrote:
           | Fascinating, thanks for taking the time to educate! Makes
           | sense to me -- it seems this tool is purpose built for
           | situations where _tracking_ is complex enough to deserve
           | decoupling from _annotating_ or _interpreting_ , to put it in
           | my own kindergarten terms.
           | 
           | I don't have a need for this personally but I'll definitely
           | be bouncing this around in my head for a while, both
           | technically (JS _ALL_ the things!) and methodologically.
           | Accounting is pretty trivial when you don't have any income
           | to track!
        
           | mamidon wrote:
           | I'm living this life right now; except we baked in notions of
           | Assets/Liabilities/Income/Expenses into our ledger logic.
           | Only to realize our customers don't care and just want to do
           | whatever it is they've been doing.
        
       | abdullahkhalids wrote:
       | How would this handle Canadian guidelines for dealing with cents
       | in cash, where you round to the nearest 5c [1]?
       | Amounts ending in 1 cent and 2 cents are rounded down to the
       | nearest 10 cents;         Amounts ending in 3 cents and 4 cents
       | are rounded up to the nearest 5 cents;         Amounts ending in
       | 6 cents and 7 cents are rounded down to the nearest 5 cents;
       | Amounts ending in 8 cents and 9 cents are rounded up to the
       | nearest 10 cents;         Amounts ending in 0 cent and 5 cents
       | remain unchanged.
       | 
       | EDIT: I think if you send 12 cents,                   send
       | [CADCASH/2 12] ( source = @user1 destination = @user2 )
       | 
       | It should result in sending 10 cents.
       | "postings": [{"source": "user1",
       | "destination": "user2",                       "amount": 10,
       | "asset": "CADCASH/2"}]
       | 
       | Can this happen?
       | 
       | [1] https://www.canada.ca/en/revenue-agency/programs/about-
       | canad...
        
         | superzamp wrote:
         | Well that's definitely a good puzzle. I've tried to model it
         | for a bit, but it indeed looks like we'd need to add something
         | to the language to make it possible at all! Thanks for bringing
         | this up.
        
           | fallat wrote:
           | Same thing with income tax brackets. You need conditional
           | logic.
        
             | superzamp wrote:
             | For this particular case, I would say that tax-brackets
             | sort of logic can be expressed in the destination block
             | with ordered destinations.
             | 
             | For example, you could have something like this:
             | send [USD/2 *] (           source = @users:1234
             | destination = {             // first $1000 are taxed at 10%
             | max [USD/2 100000] to {               10% to @taxes
             | remaining kept             }             // Anything above
             | that is, taxed at 20%             remaining to {
             | 20% to @taxes               remaining kept             }
             | }         )
             | 
             | (You can test it on the playground, you'll just want to
             | feed the "users:1234" account with an initial balance in
             | the input section)
        
         | euroderf wrote:
         | The rules listed (1,2,6,7 round down; 3,4,8,9 round up). AFAIK
         | these are also the official Eurozone cash rules for countries
         | that choose not to circulate 1 and 2 eurocent coins. (Altho of
         | course, electronic transactions are exact to the penny.) So you
         | might want to cover this use case.
        
       | jimbokun wrote:
       | Are there converters to/from OFX for Numscript?
        
       | OutOfHere wrote:
       | Can this or Formance interact with credit card systems, checking
       | accounts, and layer 2 crypto wallets? If not, how is the money
       | even going to come in or out?
        
       | henning wrote:
       | Ah, what better input format than JSON, a poorly defined,
       | ambiguous format that freely mixes integers and floating points
       | and lacks supports for bigints and bigdecimals.
        
       ___________________________________________________________________
       (page generated 2024-09-19 23:00 UTC)