-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Double Entry Accounting #4
base: master
Are you sure you want to change the base?
Conversation
Made changes to ensure a one-to-one mapping between accounts and contract types
@rafabap Let's start with the Excuse the Scala, but something like the following... abstract class Contract {
/** agent for whom the contract is a liability */
def issuer: Agent
/** agent for whom the contract is an asset */
def counterparty: Agent
/** Objective value of a contract */
def faceValue: Double
}
/** Immutable storage container for a particular agent's contracts. */
class BalanceSheet[+C <: Contract] private(agent: UUID, contracts: GenSet[C] ) {
/** Returns a new BalanceSheet with an additional contract .*/
def add[C1 >: C](contract: C1): BalanceSheet[C1] = {
new BalanceSheet(agent, contracts + contract)
}
/** Returns a new BalanceSheet after removing a particular contact. */
def remove(contract: C): BalanceSheet[C] = {
new BalanceSheet(agent, contracts - contract)
}
/** Collection of contracts that are assets. */
def assets: GenSet[C] = {
contracts.filter(contract => contract.counterparty == agent)
}
/** Collection of contracts that are liabilities. */
def liabilities: GenSet[C] = {
contracts.filter(contract => contract.issuer == agent)
}
/** The value of equity is just the difference between the value of assets and the value of liabilities.
* @note the value of equity is subjective and depends on the value function `v` used to value the
* underlying contracts.
*/
def equity(v: (Contract) => Double): Double = {
assets.map(v).sum - liabilities.map(v).sum
}
/** Leverage ratio */
def leverage(v: (Contract) => Double): Double = {
assets.map(v).sum / equity(v)
}
} |
@rafabap I am also confused about the difference between |
My most general comment is that I find the terminology very confusing. I generally do not think of I guess you could have something like... class DepositAccount private(issuer: UUID, counterparty: UUID, initialDeposit: Double) extends Contract {
val faceValue = initialDeposit
def debit(value: Double): DepositAccount = {
new DepositAccount(issuer, counterparty, initialDeposit - value)
}
def credit(value: Double): DepositAccount = {
new DepositAccount(issuer, counterparty, initialDeposit + value)
}
}
val agent: UUID = ???
val bank: UUID = ???
val deposit = DepositContract(bank, agent, 1e6)
in the above |
@rafabap I just realized that |
@rafabap Some specific comments about the
|
@rafabap Perhaps my lack of comfort with your terminology stems from my being used to Perry Mehrling's terminology. I am reading through the Double-Entry Book-Keeping Wikipedia entry to try and familiarize myself with your terms. Is this the best source? |
@davidrpugh sorry for the confusion! Just a few pints to clarify:
The types of Account only matter when defining the sign of debits and credits.
I see our accounting system as a hybrid composed of two parts. The first is the double entry rules, which requires separate accounts implementing their debits and credits. The second is the inventory: a way to keep lists of contracts. My solution is to combine the two by starting with a standard double-entry ledger havjng a few accounts, and then rather than having a single inventory for the agent, I split it in smaller inventories, each associated to one account. In addition, for clarity, I force that each account includes only one type of contract in its inventory. |
Account.java
Outdated
|
||
private double balance; | ||
|
||
// private Collateral collateralType; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove commented variables, unless it is a discussion point whether or not to add them.
Account.java
Outdated
protected HashSet<Contract> contracts; | ||
|
||
|
||
void addContract(Contract contract) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it intentional that these functions scope are the Java default? It's likely that's best, but I didn't know if some, like addContract, should be public?
Account.java
Outdated
return availableActions; | ||
} | ||
|
||
// public void setCollateralType(Collateral collateralType) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove?
String getName() { | ||
return name; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be a removeContract functionality?
String getName() { | ||
return name; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the user be able to pull a list of the existing contracts in the contract hashSet?
Book.java
Outdated
} | ||
|
||
// (dr asset, cr equity) | ||
assetAccount.debit(contract.getValue()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do contracts have a 'getValue()'? Perhaps they should, if they don't. We should discuss contract valuation and how it relates to this system.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if valuation shouldn't be external to the contract, e.g. by means of a function that accepts a contract and returns a value. If it is internal to the contract, then it will be difficult to have different types of valuation. Also, if valuation is done in a more functional way, it will become easier to plug together more complex types of valuation. But that's probably a separate discussion.
Book.java
Outdated
Account liabilityAccount = liabilityAccountsMap.get(liabilityType); | ||
|
||
//Todo: What do we do if we can't pay??!! At the moment I'm calling my owner to raise liquidity | ||
if (cashAccount.getBalance() < amount) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably decide how we're going to deal with errors in the library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@prauwolf Agreed. I don't know much about the Java conventions regarding checked vs unchecked exceptions, etc. I think we need some kind of LiquidityException
that will be thrown in the event that an agent does not have liquid resources available to make required payments. Ideally this would also be expressed in the return type of the method (which would force the caller of the method to explicitly deal with the possibility that an exception would be returned.
Book.java
Outdated
* If I've sold an asset, debit cash and credit asset | ||
* @param amount the *value* of the asset | ||
*/ | ||
public void sellAsset(double amount, Class<? extends Contract> assetType) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As implemented, what class should be calling 'sellAsset'?
Book.java
Outdated
* @param me the owner of the Book | ||
* @return an ArrayList of Actions that are available to me at this moment | ||
*/ | ||
public ArrayList<Action> getAvailableActions(Agent me) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's discuss actions in the code review. I really like the idea. Perhaps we should have some kind of interface which defines a list of actions, since we'd probably use them in a variety of contexts.
Book.java
Outdated
|
||
for (Contract contract : assetAccount.contracts) { | ||
Asset asset = (Asset) contract; | ||
if (asset.priceFell()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain this in a bit more detail in the review?
Book.java
Outdated
assetAccounts = new HashSet<>(); | ||
liabilityAccounts = new HashSet<>(); | ||
equityAccounts = new HashSet<>(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it strictly necessary to have different hash sets holding the same account objects? Although (I think) Java stores values by reference, I fear there might be situations where, for some reason, the account object gets copied and thus ends up being 'forked'.
Would it be better to use just one single data structure accounts
and use clever access functions to get and set the type of content that is relevant? For example, to return all accounts from accounts
in a lazy way (i.e. as a stream), you could define the following function (sorry, haven't checked if it compiles):
Stream<Object> getAccounts() {
return accounts.entrySet().stream.map(Map.Entry::getValue);
}
To return all accounts of a particular type, you could define the following function:
Stream<Object> getAccountsOfType(AccountType t) {
return accounts.entrySet().stream().filter(entry -> entry.getKey() == t).map(Map.Entry::getValue);
}
In order to turn the streams into lists, you could also define a general function:
List<?> toList(Stream<Object> s) {
return s.collect(Collectors.toList());
}
The same could, of course be done in the opposite direction, i.e. to set objects in the map. You could then get rid of the other data structures and only keep a single source of truth.
As I said, I haven't checked whether the snippets compile, so please take them with a pinch of salt :)
Book.java
Outdated
// These hashmaps are used to access the correct account for a given type of contract. | ||
// Note that separate hashmaps are needed for asset accounts and liability accounts: the same contract | ||
// type (such as Loan) can sometimes be an asset and sometimes a liability. | ||
assetAccountsMap = new HashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General question: is thread safety an issue at this point in time? If yes, then this would require some sort of synchronisation, because HashMaps and HashSets are (as far as I'm aware) not thread-safe.
@rafabap and @bherd Based on our discussion today we decided to move forward with a message passing solution for handling interaction between |
Made a static, legal `doubleEntry(Account debitAccount, Account creditAccount, double amount)` function
A `Ledger` now has: - a set of `Account`s - two sets of `Contract`s to act as inventory (one for assets and one for liabilities) By splitting the inventory from the accounts, I have avoided issues to do with searching of the inventories etc. The invariants maintained: - each `Account` corresponds to one type of `Contract`, and the sum of the value of the contracts of that type equals the balance of the account. This invariant is not hard-coded but will be. - Assets = Liabilities + Equity (guaranteed by the double entry)
Hi all, I have implemented most of the comments received so far and during the code review. The concurrent implementation of the I have followed David's advice and decided to split the inventories from the Accounts. Therefore, a Searching etc. of the inventory is done using functional calls as suggested by Ben. The invariants provided by the framework are:
|
Hybrid accounting system that uses inventories and double-entry accounting.
The Double-entry is simplified so that it uses only stock accounts (ASSETS, LIABILITIES, EQUITY) and ignores flow accounts (INCOME, EXPENSES). Each account has an inventory of contracts.
Each account can hold only one type of contract in its inventory.
The Agents interact with their accounts via a Book, that provides a series of operations given in BookAPI.