Business Data Types

In a brokerage application I worked on a few years ago, I believe we made a good start at defining a set of useful business classes. Not surprisingly, the first type of unit we addressed was money, which is not only the main preoccupation of much of business programming, but the fuel that makes much of it go.  Money, as defined in our application, has a magnitude and a currency. Since currencies are so important to our society, we do actually have 3-letter ISO codes for the different currencies - our package supported about 200 of them. In our package, a monetary value was always represented by the ISO code followed (with no intervening blanks) by an amount, whether for input or for output. Thus we might have CAD205.10, or USD3012.55. Each currency has a preferred number of decimals for amounts (although this does not apply to rates), except for some oddball "currencies" (such as Euro-Gold) that allow any number of decimals.  In our application we loaded the currency information at start of session from a database.

Note that rates have different precision requirements from monetary amounts.  The general rule for currency is that, at the end of a computation, they must be rounded to whatever the smallest denomination is in that currency, whereas rates can have any number of decimal places.  In the case of (US and Canadian) currency, the smallest unit is really the cent, but instead of 12345 cents we usually write $123.45.  This can be handled in most cases  by forcing rounding when the currency amount is turned into displayable format, e.g. by using some method built on the Java toString() method.   In addition, in our application we provided a round() method which did the same thing to non-display currency amounts.  It has been suggested that we should really distinguish between "non-final" and "final" currency values, but that would involve a vast increase in the number of methods over what we already have, so I think the above technique is adequate.  

In turn, the numeric value in our Monetary class was implemented using the new Java BigDecimal class which is now available, as of Java 1.5, replacing the previous one.  Even though they have explicit precision, floating point numbers are not appropriate for monetary amounts, although they work quite well for rates (another difference between rates and monetary amounts) or physical units such as distances. We chose the new BigDecimal, as its arithmetic has been consciously designed to give the same results that you would get yourself when doing decimal arithmetic by hand.   For instance, 3.82 + 2.18 becomes 6.00, not 6.

Before getting into the specific classes and methods, I would like to point out that money was really rather an easy class to define.  There is a much more complex issue revolving around the question of Quantity.  We have tentatively added a  class called Quantity to the project, but the real question is "Quantity of what?".   Quantity does not extend BigDecimal, as there are many things you can do with BigDecimal that you do not want to automatically be able to do with Quantity - i.e. you cannot normally multiply two Quantities together (unless it is two lengths to form an area), and conversely, a Quantity should almost always be a quantity of something.   Users can extend Quantity for their own purposes - this will usually entail adding the thing that it is a quantity of - what Denis Garneau (the very talented IBM architect who did most of the design work) calls its "quality".    You should not be able to add weight to length - or number of eggs to number of chickens!  Denis recently ran into a mysterious programming bug, which eventually turned out to be caused by someone adding a temperature to a weight!  Alternatively, Quantity could be an interface, rather than a class.   So you see, we are starting to stray into the area talked about in Physical Units.  Denis and I would like people to explore this area, especially in different problem domains, and write papers about it!  Specifically, a) should Quantity be a class or an interface? b) what extensions/implementations did you need to add to it for your particular domain of interest?

A partial set of Business Data Types can now be found on Business Data Types.

Monetary class

To give you a flavour, the constructors for our money class, called "Monetary", are as follows:
 

Constructors
Monetary(BigDecimal, Currency) 
Monetary constructor with value initialized from BigDecimal 
Monetary (String) 
Monetary constructor using currency + value in String, eg CAD25.74 

BigDecimal objects are (immutable) decimal numbers with explicit precision, while Currency objects represent the actual currencies themselves - thus one Currency object is US dollars, another is Canadian dollars, while yet another might be pounds sterling .   The first constructor is needed to build Monetary objects that are the result of a computation; the second to build them from the String representation.  So we needed the converse operation, which was a method called either toString() or  serialize().

The complete set of methods we provided for our Monetary class are as follows:
 

returns Methods
Monetary add (Monetary) 
add value of Monetary object to this Monetary object, creating another one 
Monetary convert(ExchRate) 
Convert this Monetary amount using an Exchange Rate, giving another Monetary amount of the target currency type. 
Monetary divide(BigDecimal) 
Divide this Monetary amount by a BigDecimal quantity, creating a new Monetary 
BigDecimal divide(Monetary) 
Divide value of this Monetary object by Monetary object, creating BigDecimal 
boolean eq(Monetary) 
Compare to see if monetary amount is equal to this one 
boolean ge(Monetary) 
Compare to see if this monetary amount is greater than or equal to specified one 
Currency getCurrency() 
Get currency from Monetary value 
boolean gt(Monetary) 
Compare to see if this monetary amount is greater than specified one 
boolean isPositive() 
Check if value is positive (> 0) 
boolean le(Monetary) 
Compare to see if this monetary amount is less than or equal to specified one 
boolean lt(Monetary) 
Compare to see if this monetary amount is less than specified one 
Monetary multiply(BigDecimal) 
Multiply this Monetary amount by a BigDecimal, giving another Monetary amount 
Monetary multiply(PCPrice) 
Multiply this Monetary amount by a Percent Price, returning a Monetary amount 
boolean ne(Monetary) 
Compare to see if a monetary amount is unequal to this one 
void round() 
Method to adjust precision of Monetary amount to that specified in Currency object, rounding as determined by active MathContext. 
String serialize() 
Generate a 'preference neutral' string from Monetary value. 
Monetary setCurrency(Currency) 
Create new Monetary with new currency - can only be done if currency is unknown (== null) 
Monetary setCurrency(String) 
Create new Monetary with new currency - can only be done if currency is unknown (== null)
Monetary subtract(Monetary) 
subtract value of Monetary object from this Monetary object, creating another one 
String toString() 
Create a String from this object

This list is to give you a flavour for the kinds of things we felt would be needed in this class.  As requirements for new methods came up in our application we added them, so if you don't see something in this list that you would expect, it's probably because the need didn't come up in our application.  You will see that a number of other classes (apart from the standard Java ones) are mentioned in the list given above and we will be describing them in what follows - although perhaps not to such a fine level of detail.

Monetary amounts cannot be multiplied together, but they can be added, subtracted, compared against each other, and so on.  In all these cases, the currency must be the same; if the two currencies involved are not identical, an error condition is raised.  In our implementation, this was a run-time test, as opposed to a compile-time test. In our view it wouldn't make sense to make every one of our 200 currencies a different class.

Monetary amounts can however be multiplied or divided by instances of the classes PCPrice and BigDecimal (more about PCPrice later).  These latter are both essentially dimensionless, and in general we can multiply or divide dimensioned values by dimensionless ones, giving a result that has the same dimension as the dimensioned quantity.

Note that we allowed a Monetary object to have an undefined currency.  In this state you cannot do anything with it except set the currency.  I am not sure if this function was ever used.

Monetary Price (MPrice) class and Percent Price (PCPrice)

This web page will be talking about the classes we actually used in our application, and what their capabilities were. Although we used BigDecimal for MPrice (in conjunction with Currency), we allowed an MPrice object to have more decimal places than monetary amounts.  I believe one of the types of floating point number might also work well for MPrice, but we went with BigDecimal..

For now, we are going to expand on the Monetary Price (MPrice) class.  This could be entered as a decimal value, but also as a fraction of the form "3 + 5 / 16" (usually referred to as a "vulgar fraction"), where the last integer was always a power of 2.  Supposedly, US stock exchanges are switching to decimal prices soon (or may already have done so), but at the time we built this system, they had not yet made the switchover.

We held the three integer fields, provided in a constructor, in the MPrice object, as well as the decimal value, which was computed if the price was entered in vulgar fraction form.  If the price was entered as a decimal, the denominator item of the integer fields was set to zero, and the other two integer fields were ignored. I managed to figure out a way to calculate the vulgar fraction from the decimal, and we provided a constructor to do this. Again I'm not sure if it was ever used.

Here are the constructors for Monetary Prices:

Constructors
MPrice(BigDecimal, Currency)
MPrice constructor with value initialized from BigDecimal value and Currency object
MPrice(BigDecimal, Currency, boolean)
MPrice constructor with value initialized from BigDecimal value, Currency object, and boolean indicating that it is to be held as a vulgar fraction
MPrice(int[], Currency)
MPrice constructor with value initialized from int[3] array and Currency object
MPrice(String)
MPrice constructor using currency + value in String.
MPrice(String, String)
MPrice constructor with value initialized from 2 Strings - value (vulgar or decimal), and currency abbreviation.


Here are some typical methods for this class:


returns Methods
MPrice
add (MPrice)
Add this Monetary Price to another, giving a Monetary Price.
MPrice
divPrice (BigDecimal)
Divide this MPrice amount by a BigDecimal quantity, creating a new MPrice
boolean
isMultipleOf (MPrice)
Determine if this MPrice is a multiple of the parameter MPrice - return true if it is.
MPrice
multPrice (BigDecimal)
Multiply this MPrice amount by a BigDecimal quantity, creating a new MPrice
String
serialize ()
Generate a 'preference neutral' string from Monetary Price.
MPrice
setPriceCurrency (Currency)
Create new MPrice with new currency - can only be done if currency is unknown (== null)
MPrice
setPriceCurrency (String)
Create new MPrice with new currency - can only be done if currency is unknown (== null)
MPrice
subtract (MPrice)
Subtract a Monetary Price from this one, giving a Monetary Price.
String
toString ()
Create a String from this object

Our brokerage application also required a different kind of price, which we called "PCPrice".  This stands for PerCentPrice and is basically a dimensionless quantity that is entered or displayed as a percentage, i.e. 20PCT represents the value .2.  PCPrice also supports "discount" and "premium", coded as DSC and PRM.  20DSC represents a value of .8, while 20PRM represents 1.2.   Although PCPrice has interesting ways of being displayed, it is dimensionless, so it is equivalent to a pure value. Because of this, it can extend BigDecimal, and its methods are basically whatever it inherits from BigDecimal, plus a few quirks due to the PCT/DSC/PRM convention, so I will not go into it further.  

Exchange Rate (ExchRate) class

Since we are dealing with multiple currencies, we need a way of exchanging money.  You can't add money of different currencies, so of course we need an ExchRate class - instances of this class can be applied to a Monetary value to convert it to Monetary of another currency.

Its constructors are as follows:

Constructors
ExchRate(BigDecimal, Currency, Currency, TimeStamp)
Exchange Rate constructor with value initialized from BigDecimal
ExchRate (String)
Exchange Rate constructor using single String, containing value, source currency, target currency, optional timestamp, separated by semicolons.
ExchRate(String, String, String, String)
Exchange Rate constructor with value initialized from 4 Strings (TimeStamp will use serialized form)

By the way, our TimeStamp class extends the Java Date class, which goes down to the millisecond, in spite of its name. The Java SQL TimeStamp class goes down to the nanosecond, but we didn't consider that necessary for our application, although we had methods to convert our time stamps from and to SQL time stamps. We held all time stamps in UTC format (Universal Time Code) as our application not only dealt in many currencies, but in many countries and time zones. More about that later.

The methods of ExchRate are as follows:

returns Methods
Monetary
convert(Monetary)
Convert a Monetary amount using this Exchange Rate, giving another Monetary amount of the target currency type. This will only work if the currency of the Monetary amount matches the source currency of the Exchange Rate
boolean
eq(ExchRate)
Compare to see if this exchange rate is equal to specified one
boolean
ge(ExchRate)
Compare to see if this exchange rate is greater than or equal to specified one
Currency
getSourceCurrency ()
Get Source Currency
Currency
getTargetCurrency ()
Get Target Currency
TimeStamp
getTimeStamp ()
Get TimeStamp
boolean
gt(ExchRate)
Compare to see if this exchange rate is greater than specified one
ExchRate
invert()
This method inverts an Exchange Rate - a new Exchange Rate object is created with the currencies interchanged, and with a new value which is the reciprocal of this one
boolean
le(ExchRate)
Compare to see if this exchange rate is less than or equal to specified one
boolean
lt(ExchRate)
Compare to see if this exchange rate is less than specified one
boolean
ne(ExchRate)
Compare to see if this exchange rate is not equal to specified one
ExchRate
propagate(ExchRate)
This method combines this Exchange Rate with another Exchange Rate, resulting in a new Exchange Rate object.
String
serialize()
Generate a 'preference neutral' string from Exchange Rate.
String
toString()
Create a String from this object

Of particular interest are  invert() and propagate(): the former creates an exchange rate that is the inverse of the given one, while the latter combines two exchange rates to create a single resultant exchange rate.  In the first case, the inverse of the exchange rate 

from to rate
USD (US Dollars) CDN (Canadian) 1.6

would be 

from to rate
CDN USD 1 / 1.6 (=.625)

In the second case, combining 

from to rate
USD CDN 1.6

and 

from to rate
CDN GBP (Great Britain Pounds) .4

would result in

from to rate
USD GBP 1.6 x .4 (= .64)

You may perhaps have noticed that we didn't integrate the TimeStamp values into our Exchange Rate methods - this was put in because clearly exchange rates only apply at a particular point in time. Ideally, the convert() methods should have been tagged with a TimeStamp, and therefore most other methods dealing with the ExchRate class.  Now most computer applications apply at some moment in time - they could be called "synchronic", whereas in real life things change across time ("diachronic"). So it was reasonable to leave the time stamp off the exchange rate.  We left it in, however, as a pointer to a wider scope that we could implement at some time in the future.  In fact, in my experience, the time dimension is always the hardest aspect of  computer applications to implement. 

This also illustrates a problem with the "big bang" approach to system development: if you don't have time to "do it right the first time" (you never do), you must build in enough flexibility so that you can move there incrementally, preferably without destroying your working system.   

Date, TimeStamp and Time Zone (TimeTz) classes

This seems like a good time to talk about dates, timestamps and time zones.  Our application had to deal with time zones right around the world, as we had to support someone in Tokyo buying a stock that was listed on the Toronto Stock Exchange on the other side of the planet.   Perhaps in hindsight we should have given our classes different names, as Java has some of these classes, both in the Java.util and Java.sql packages, but in practice this didn't cause confusion as we hid the Java.util classes behind our own.   We couldn't hide the Java.sql classes, but the spelling difference (see below) helped! 

We did have to do a lot of thinking about what times and dates really mean, as this is something that the designers of most business applications never have to think about.   We decided to hold times in UTC (Coordinated Universal Time), which is based on Greenwich, UK, and does not change with Daylight Savings Time (DST).  So we had to know which time zones particular stock exchanges were in, and which time zones our users would be in.  When you are dealing with a 7/24, world-wide application, the only notation that tells you whether two events are simultaneous is UTC.  

A time zone not only determines how many hours ahead of or behind UTC the location is, but also when they switch from and to DST - if in fact they do (Japan does not switch nor does the province of Saskatchewan in Canada).  It turned out that the version of Java we were using had a very incomplete set of time zones, but an excellent mechanism for building new ones!   A major part of the problem, as Java has found out, is that there is no generally agreed on standard for time zone names.  Java (from 1.2 on) seems to use a convention of "area/city", but one of the areas is America, so there are zones with names like America/Winnipeg, which is not really acceptable in a Canadian application!  Not to mention America/Manaus, America/Tortola and America/St_Kitts, but for some reason Atlantic/Bermuda!   And Java certainly does not seem to know much about Newfoundland, which is famous (at least up here) for being 1/2 hour out of step with the next time zone (honest!) - it is actually 3.5 hours behind UTC (not counting DST) - Java has it as 4 hours behind, but it does say Canada/Newfoundland (I think because it treats it as a zone, rather than a city).  We therefore used the commonly used Canadian abbreviations where possible, but these are only really unique within Canada  and I do not know whether they are encoded in any standards document.  The problem of coming up with unique, worldwide zone designations needs to be addressed, but I don't really feel that the Java approach is adequate.  However, we should give them credit for trying!  In case you were wondering, it is not enough to just use the offset in hours from UTC, as different  time zones may have the same offset but different rules for beginning and ending DST.  

The Java Date class includes time, but ours did not, which I feel is more logical.  Our TimeStamp class was more similar to the Java Date class, except that the Java Date class has a default time zone, which we felt would be confusing.  It's better to always have to state the time zone explicitly... when it matters....  Since all timestamps were held in UTC, we only needed time zones when handling input from, or output to, humans.   Our time zone class is actually a simple one: we just used the Java SimpleTimeZone class to build a list of time zones, identified by a 3-character abbreviation, "CST", "SST", etc., and then used the abbreviation as an optional part of a time stamp (default was UTC), e.g. 20021021T13:10:40678!CST.   Note that this can only be converted into a UTC time stamp if the date portion is present.

It actually took a little effort for old-guard programmers to start thinking in terms of time zones.  For example, the date does depend on the time zone: it can be Tuesday in Tokyo while it's Monday in Toronto, so you can convert from date plus time (UTC) to the local date, if you have the time zone, but you can't go from a date by itself  to UTC unless you know the time and time zone.  Actually there is an hour during the year, in time zones with DST, when the same local time repeats itself.

To make things even more interesting, the Java.sql package has Date and Timestamp (note the lower case 's'):  the SQL Date is very similar to ours, while Timestamp goes down to nanoseconds, which we really didn't need.  We provided conversion methods between our classes and the corresponding Java.sql ones, so that we could store our data in relational databases.

Bool class

Just to get this class out of the way, I will give a brief description of this class.  We decided we needed a Bool class mostly because we built a rules-driven decision tool which specified its rules in XML.  A patent is pending on this work: "Rules-Based Engine for Validating Financial Transactions".  I hope you can read legalese, as I can't, and I helped to write the thing!  

A couple of interesting attributes of this class are:

  • its serialized form is "1" or "0" - this is because "1" and "0" are more suitable for multi-language support than "true" and "false"
  • in its constructor we treated the null string as a true value.

The equals() method returns true if and only if the argument is not null and is a Boolean object that contains the same boolean value as this object.   For obvious reasons, we defined two public static final objects called TRUE and FALSE.

AccountId, Market, InstrumentId, and Symbol classes

We are now getting into an area which is less standardized (or standardizable). The lack of standardization in the industry means that we cannot specify classes that will be usable world-wide.  However, hopefully the following remarks will give some of the flavour of what we felt was needed for our particular application..

In our case, the account ID object was comprised of three parts:

- identifier (required)
- optional firm
- optional system ID

It was basically used as an identifier.

A Market object was identified by its Reuters code, and had attributes such as time zone, open and close times, list of holidays, country code and quote currency.   The open and close times were reloaded at the start of every day, as occasionally a particular market would be closed unexpectedly, for varying amounts of time, e.g. an hour or a half-day.  In addition the open and close times could be different for different financial instruments, so this information had to be stored, and reflected in the methods we provided.

InstrumentId was constructed out of a schema (a number) and a financial instrument ID. The schema numbers indicate what coding scheme is used for the financial instrument ID. The schema numbers are as follows:


1 CUSIP
2 SEDOL
3 QUIK
4 ISIN number
5 RIC code
6 ISO Currency code
7 ISO Country code
111 IBM security number
112 ADP security number
113 SVC
114 Canadian Mutual Funds
999 Unknown

A Symbol object identified something that is traded in a market.  It was constructed out of a market ID, symbol, and country code.  This reflects the fact that a symbol is only unique within a market.  Either market or country will usually be present - if both are missing, we used the default country.