JuliaFinance / Roadmap

13 stars 1 forks source link

Update 2019-03-03 Ledgers and Accounting Modules #10

Open EricForgy opened 5 years ago

EricForgy commented 5 years ago

Just an update with some thoughts for next steps...

In my last update, we talked about this "totem pole" of packages I was putting together.

Simplified Account

Last time, although there was just a single underlying Account type, I defined five singleton types with the intention of supporting dispatch:

  1. GeneralLedger - A debit Account with no parent. Cannot post entries directly.
  2. DebitGroup - A debit Account with both a parent and children. Cannot post entries directly.
  3. CreditGroup - A credit Account with both a parent and children. Cannot post entries directly.
  4. DebitAccount - A debit Account with a parent but no children. Can post entries directly.
  5. CreditAccount - A crediit Account with a parent but no children. Can post entries directly.

Unfortunately, that idea leads to type instabilities, so I've removed these singleton types. Now, there is just Account. This is cleaner.

Load chart of accounts from CSV

I put together a fairly realistic chart of accounts for an insurance company in CSV format and added a method loadchart to load it to memory. I'll see about making a trimmed down sample I can upload.

Added Transaction

Last time, GeneralLedgers included an example:

function example()
    ledger = Account("NewCo","0000000")
    assets = Account(ledger,"Assets","1000000",true)
    liabilities = Account(ledger,"Liabilities","2000000",false)
    cash = Account(assets,"Cash","1010000",true)
    payable = Account(liabilities,"Accounts Payable","2010000",false)

    entry = Entry(cash,payable,Position(FI.USD,10.))
    return ledger, assets, liabilities, cash, payable, entry
end

To run the example from the REPL:

julia> ledger, assets, liabilities, cash, payable, entry = GL.example(); ledger
NewCo
├─ 1000000 Assets
│  └─ 1010000 Cash: 0.00 USD
└─ 2000000 Liabilities
   └─ 2010000 Accounts Payable: 0.00 USD

The purpose of this example is to demonstrate how to create a simple ledger and post an entry:

(Warning: The following syntax is no longer valid. See below for the updated syntax.)

julia> GL.post!(entry); ledger
NewCo
├─ 1000000 Assets
│  └─ 1010000 Cash: 10.00 USD
└─ 2000000 Liabilities
   └─ 2010000 Accounts Payable: 10.00 USD

This Entry debits Cash and credits Accounts Payable US$10.

Now, a Transaction is more complicated than a simple Entry. For example, a transaction could be "Buy an Insurance Policy". In my case, this transaction involves 4 entries:

  1. Cash is paid - Debit Bank Account and credit Gross Premium.
  2. Set up reserve - Debit Expense for Unearned Premium and credit Unearned Premium Reserve.
  3. Collect levy - Debit Bank account and credit Accounts Payable - Levy.
  4. Collect admin fee - Debit Administration Fee and credit Accounts Payable - Admin Fee.

But how much of what is collected from the customer is used for levy and what is the admin fee? These are functions of the amount paid.

With this example in mind, my initial design for a Transaction is (roughly):

struct Transaction
    _entries::Dict{String,Entry}
    _module::Function
end

i.e. a dictionary of entries and a module function to tell you how to post the entries via:

function post!(t::Transaction,a::Position)
    t._module(t._entries,a)
end

For my example, I define a method:

function buypolicy(e::Dict{String,Entry},a::Position)
    levy_rate = .04
    fee_rate = .045
    premium = a/(1+levy_rate)
    levy = premium*levy_rate
    fee = premium*fee_rate

    GL.post!(e["pay_cash"],premium)
    GL.post!(e["set_reserve"],premium)
    GL.post!(e["collect_levy"],levy)
    GL.post!(e["collect_fee"],fee)
end

and Transaction

Transaction(entries,buypolicy)

In other words, a transaction module is a function that takes a dictionary of entries and a transaction amount, performs some computations and finally posts to the respective accounts.

Note that I've hard coded the levy_rate and fee_rate. Ideally, these should be provided by an Entity which could depend on a Jurisdiction.

Also note that the post! method accepts both a transaction and an amount. My early version of an Entry had the amount as part of the type definition, but that has been removed, which explains why the syntax in the example provided no longer works. The new syntax for the example is:

julia> GL.post!(entry,Position(FI.USD,10.)); ledger
NewCo
├─ 1000000 Assets
│  └─ 1010000 Cash: 10.00 USD
└─ 2000000 Liabilities
   └─ 2010000 Accounts Payable: 10.00 USD

Alternatively, you could define

julia> usd = Position(FI.USD,1.0);

and then

julia> GL.post!(entry,10usd); ledger
NewCo
├─ 1000000 Assets
│  └─ 1010000 Cash: 10.00 USD
└─ 2000000 Liabilities
   └─ 2010000 Accounts Payable: 10.00 USD

Added Cash as a FinancialInstrument and using FixedDecimal

Above, I defined usd as a Position using FI.USD. FI is short for FinancialInstruments and USD is an instance of Cash<:FinancialInstrument.

Recall that Currencies.USD is a simple primordial singleton value type, i.e.

julia> typeof(Currencies.USD)
Currency{:USD}

As noted above, currencies can be viewed as either a value type or as a financial instrument. In some cases, but not all, viewing currency as a financial instrument (linked to an entity, e.g. a central bank) would be overkill so we keep Currencies.USD as a simple singleton value type.

In order to work with currency as a financial instrument, I introduced Cash<:FinancialInstrument, i.e.

julia> typeof(FI.USD)
Cash{Currency{:USD}}

Fundamentally, Cash is a contract issued by a central bank or monetary authority similar to the way Stock is issued by a corporation. If I own Cash, that means I have a Position in Cash. Mathematical operations are defined on Positions of FinancialInstruments rather than directly on FinancialInstruments. For example,

julia> typeof(usd)
Position{Cash{Currency{:USD}},FixedDecimal{Int64,2}}

julia> 3usd+4usd
7.00 USD

By default, if a Position is constructed using Cash as the FinancialInstrument, I use

julia> Currencies.unit(Currencies.USD)

to determine the official number of decimal places for that currency and use the FixedPointDecimals.jl package to specify the amount with the correct number of decimals, e.g.

julia> Position(FI.HKD,1000)
1000.00 HKD

julia> Position(FI.JPY,1000)
1000 JPY

julia> Position(FI.IQD,1000)
1000.000 IQD

This gives us more control on rounding to the "penny", makes displaying easier and I expect computations to be faster than Float64.

This is just the default behavior. If you want, you can still construct a cash position using Float64 (or any other <:Real) via:

julia> Position{typeof(FI.JPY),Float64}(1000)
1000.0 JPY

julia> typeof(ans)
Position{Cash{Currency{:JPY}},Float64}

Next steps

Despite the substantial effort so far, I am still only scratching the surface and things may need to be redesigned again before stabilizing.

More work on FinancialInstruments

For example, above, I showed how to handle the transaction: "Buy an Insurance Policy". I still haven't actually constructed an InsurancePolicy. An InsurancePolicy is a FinancialInstrument so if I buy a policy, I now have a Position in a FinancialInstrument called InsurancePolicy. The value at t=0 is known, but what about t+1? The value of my policy can depend on many things including interest rates and these should be Observables of a Market.

I expect an iterative process where what we do with FinancialInstruments impacts how we design Markets and how we design Markets will feed back to FinancialInstruments etc so I'll probably keep focusing on FinancialInstruments for now, but hope to look at Markets soon.

Real-Time Balances

One of my goals with this work is to be able to do simulations and real-time balance sheets. A BalanceSheet is a type of FinancialStatement so, in the foreseeable future, we should have a FinancialStatements.jl package, but before that, I'd like to build some capabiity for real-time account balances.

If all account balances change only when there is a transaction, doing real-time balances would be simple, but some account balances, e.g. Unearned Premium Reserve or other deprecations / appreciations, have an inherit time dependence.

A naive way to do this would be to post journal entries every millisecond, but that would pollute the journal. I'm sure there is a simple solution and I have some ideas, but just need to sit down and hash them out. The vision is to be able to simulate say 1 year of transactions in 10 seconds and watch how the balance sheet changes in real / accelerated time.

Financial Statements

Once some designs for financial instruments are hashed out and the path to real-time balances is clear, at some point soon, I'd like to start using the general ledger to build financial reports such a balance sheets and income statements.

As always, feedback is welcome 😊🙏