qif-1.1.1: A simple QIF file format parser / printer

Data.QIF

Description

A module for parsing or rendering QIF files.

QIF is a fairly braindead format designed for transfering financial data between applications. If you're writing a new financial application, you might want to find a newer format to share with other applications (if you can find one), and you definitely shouldn't use this as the database. For one thing, this format uses two-digit years, which is just kind of lazy. Also, it enforces absolutely no consistency constraints in terms of cross-references or transaction sums.

To parse a QIF file, I suggest using Data.Attoparsec.Text.Lazy and a lazy Text data structure, as follows:

   do txt <- Text.pack fmap readFile "my.qif"
case parse parseQIF txt of
Fail around _ err ->
fail ("Parse error (" ++ err ++ ") around :" ++
show (Text.take 10 around))
Done _ res ->
somethingInteresting res


To render a QIF file, you can run the builders from Data.Text.LazyBuilder directly, as so:

   Data.Text.Lazy.IO.writeFile "my.qif" (toLazyText (renderQIF myQIF))


# Documentation

data QIF Source #

The semantic content of a QIF file. (Explicitly this: very little semantic processing has gone into this data structure, and it could contain semantic errors in the underlying file. Checking for these things is your job.)

Instances

 Source # Methods(==) :: QIF -> QIF -> Bool #(/=) :: QIF -> QIF -> Bool # Source # MethodsshowsPrec :: Int -> QIF -> ShowS #show :: QIF -> String #showList :: [QIF] -> ShowS #

An empty QIF file.

The accounts associated with the QIF file. We hope. You might expect that there would be an invariant that qifAccounts would be the same as map fst qifInvestmentTransactions ++ map fst qifNormalTransactions. I would, and it'd be nice if you tried to maintain that in your code. But, unfortuntely, there's nothing in the QIF file format that requires this. So you should probably be careful, and make sure you handle the case in which this item mentions accounts not seen anywhere else, and the case in which qifNormalTransactions and qifInvestmentTransactions suddenly invent new accounts.

The list of categories saved in this QIF file. Like qifAccounts, there doesn't seem to be anything enforcing consistency in the actual QIF file. So you may find that this list mentions categories not referenced elsewhere -- which is not necessarily too surprising -- but also that there may be transactions that mention new categories unlisted in this field.

A cached list of securities. As with the other fields, be warned, as this is not required to be complete, as far as I can tell.

A list of investment accounts and the transactions associated with those accounts. Typically each Account should reference an account in qifAccount and include exactly the same date, but there's nothing in the file structure that enforces this invariant.

A list of non-investment accounts and the transactions associated with them. Again, one might expect that each Account here should reference an account in qifAccount, and contain exactly the same data, but there's nothing in the file structure that enforces this constraint.

Parse a QIF file. This function is purely a syntactic parse, and makes no attempt to verify that the data it parses makes sense. So please be a bit paranoid with all the numbers and strings you receive, and perform any validation you need on your own. Also, this function assumes that it is parsing only a QIF file, and that it should run to the end of the input.

Render out a QIF File. Because it's the order I've seen in my early example QIF files, this renders in the following order: account list, category list, investment accounts and their transactions, non-investment accounts and their transactions, and then security lists.

# Various lists in QIF

Parse the list of accounts associated with this QIF file.

Render the list of accounts associated with this QIF file.

Parse the list of categories (and the header for said list).

Render the header for the list of categories followed by each of the categories.

Parse a list of bank transactions. You should probably call this directly after parseAccountHeader and discovering that it's a Bank account. You should also not trust the results of this, as it does no consistency checking on your behalf.

Render a list of bank transactions. Please do any consistency checking you want before calling this. You probably also want to have called renderAccountHeader with an appropriate Bank account before calling this one.

Parse a list of investment entries. You probably should've called parseAccountHeader right before this and found an investment account.

Render a list of investment transactions. You should probably have just called renderAccountHeader with an investment account.

Parse a list of cash transactions. You should probably have just called parseAccountHeader and found a Cash account. You should probably also be a bit paranoid about checking over the date you read, as we perform no semantic checks on your behalf.

Render a list of cash transactions. You should have just called renderAccountHeader with a Cash account.

Parse a list of credit card transactions. You should probably have just called parseAccountHeader and found a Cash account. You should probably also be a bit paranoid about checking over the date you read, as we perform no semantic checks on your behalf.

Render a list of credit card transactions. You should have just called renderAccountHeader with a CreditCard account.

Parse a list of transactions in an asset account. Again, you probably should have just called parseAccountHeader and found an Asset account, and you should make sure to do any data validation you care about. Because this library just doesn't care.

Render a list of transactions on an asset. Did you call renderAccountHeader before this with an asset account? You should have!

Last one! Parse a list of transactions about a liability. Probably a loan, which you may or may not regret. You *will* regret it, however, if you didn't call parseAccountHeader first and find a Liability account. You will also regret it if you don't do some input validation on what you get from this function.

Render a list of transactions about a liability, probably right after you called renderAccountHeader with a liability account.

Parse a list of securities out of the QIF file.

Render a list of securities.

# Account Information

data Account Source #

An account in the QIF file. This same structure applies for all the account types.

Instances

 Source # Methods(==) :: Account -> Account -> Bool #(/=) :: Account -> Account -> Bool # Source # MethodsshowList :: [Account] -> ShowS #

A blank account. Defaults to BankAccount for the type, with the obvious zeros, empty strings, and Nothings elsewhere.

The name of the account

The type of the account

The description of the account; in my limited experience this can (and most likely will) be empty.

For accounts with limits, the credit limit for the account.

The date at which the balance in the next field was current.

The current balance.

Parse an account.

Render an account.

The type of an account; should be fairly self-explanatory.

Instances

 Source # Methods Source # Methods Source # MethodsshowList :: [AccountType] -> ShowS #

Parse a fully-rendered account type (e.g, "!Type:Bank"), used for section headings.

Render a fully-rendered account type (e.g., "!Type:Bank"), used for section headings.

Parse the short version of an account type (e.g., Bank), which is used in a couple different places.

Render the short version of an account type (e.g., "Bank").

Sections full of transactions start with the header demarcating what account the transactions are in regard to. This parses that header, returning the account. Note that if you were expecting to be a somewhat reasonable standard, and just reference a previously-defined account, you're in for a disappointment. This is a completely fresh Account structure, and you'll have to match things up (and merge any differences) yourself.

Render the header that should proceed any list of transactions.

# Category Information

Whether a category is an income category or an expense category.

Constructors

 Income Expense

Instances

 Source # Methods Source # MethodsshowList :: [CategoryKind] -> ShowS #

data Category Source #

Information about a category that one might mark a transaction against.

Instances

 Source # Methods Source # MethodsshowList :: [Category] -> ShowS #

A blank category. We default categories to Expense.

The name of the category.

A description of the category in question. Often empty.

The kind of category; Expense or Income

Whether or not this category might be tax-related.

A budget amount, if a budget has been established and published.

A number describing the tax schedule to look at.

Parse a category.

Render a category.

# Transaction Information

data SplitItem Source #

When a single transaction is split across a couple categories, this is your friend.

Instances

 Source # Methods Source # MethodsshowList :: [SplitItem] -> ShowS #

An empty SplitItem. No texts, no money. So sad.

Any memo taken as part of this split.

The amount of money in this split.

The category associated with this split.

Parse a split. Note that some banking programs may end up emitting empty splits, and we don't do anything about that. So you might want to check if what you get back is emptyTransaction, or something morally similar.

Render a split. Please be sensible in what you emit; this function won't check your work for you.

## Standard Transactions (Bank, Credit Card, etc.)

A normal transaction, that doesn't include an action in the stock market.

Instances

 Source # Methods Source # MethodsshowList :: [Transaction] -> ShowS #

A transaction with no real data, that happened to occur on January 1st, 2000. Happy new year!

The date of the transaction.

The other party to the transaction.

Any memos taken about the transaction.

The total amount of the transaction.

The check or other number, as appropriate.

The category associated with the transaction, if provided.

Whether or not this transaction has cleared.

Whether or not this transaction is reimbursable.

Any splits assocaited with this transaction.

Parse a transaction. Note that this function only does parsing, not consistency checking. Thus, you may end up with a transaction whose splits do not sum to the total transaction amount, or is missing a category, etc.

Render a transaction. This function assumes that you have performed any consistency checking you're going to do before writing out this transaction. It won't do any for you.

## Investment Account Transactions

Instances

 Source # Methods Source # MethodsshowList :: [TradeInfo] -> ShowS #

The security this trade was about. Note that while we probably should be doing some input validation on this, we're not. So if you're consuming this value, be a bit paranoid.

The share price of the security during the trade, if provided.

The amount of the share traded, if provided.

The annoying commission taken out of the trade, if provided. Note that QIF does differentiate between Nothing and (Just 0.00), for some reason.

The total amount of the trade.

### Transfer Information

Information about a transfer into an investment account. This probably looks like a normal transaction in a non-investment account, and each one probably has a sibling that is exactly that.

Instances

 Source # Methods Source # MethodsshowList :: [TransferInfo] -> ShowS #

An empty transfer that occurred on the given day.

The date of the transfer.

A summary of the transfer. Sometimes the other party in the transfer, or just a short name, and sometimes blank.

A memo or note about the transaction. Often blank, in our limited experience.

The amount of the transfer.

Whether or not the transfer has cleared.

The account with which this transaction took place ... usually. Sometimes this is empty. Make of that as you will.

Any splits associated with the transaction.

### Actual Investment Actions

An action in an investment account. These are the ones I've seen in QIF files shown to me. If you run into other ones, please file a bug or submit a patch.

Constructors

Instances

 Source # Methods Source # MethodsshowList :: [InvTransaction] -> ShowS #

The date of an investment account action, regardless of what kind of transaction it was.

Parse an investment transaction. Like it's sister function, parseTransaction, this function doesn't do any semantic validation. So it's possible that the date in the transaction doesn't make any sense. So ... that's on you.

Render an investment transaction. As you might expect, this doesn't check your work. So be careful.

# Security Types

The kinds of securities QIF files will reference.

Constructors

 Stock Bond CD MutualFund Index ETF MoneyMarket PreciousMetal Commodity StockOption Other

Instances

 Source # Methods Source # MethodsshowList :: [SecurityType] -> ShowS #

Parse a security type.

Render a security type.

data Security Source #

The information QIF keeps about a security.

Instances

 Source # Methods Source # MethodsshowList :: [Security] -> ShowS #

An empty security, forlorn and alone, with no name, no ticker, and no goals. Definitely a stock, though.

The name of the security.

The ticker symbol for the security. If I was a better person this would do some validation on the input.

The type of security.

The goal for the security. I think this is for things like "Buying a house" or "Saving for college", but I've never actually seen this used in the wild.

Parse a security. Performs no validation that the name makes sense, the ticker makes sense, or that the two go together. Good luck with that.

Render a security. You should probably make sure that your data structure makes sense before you write it, but that's your thing. This function won't judget you.

# Fixed-width quantities

A fixed width implementation of currency, based on the U.S. dollar. Future versions of this library that wish to support other currencies may wish to change this, or to abstract the rest of the library over a currency type.

Parse a currency. This is slightly differentiated from parseQuantity in that it will happily ignore a dollar sign placed in the correct location. Note that this will support negative amounts written as either "-$500" or as "$-500".

Render a currency. The boolean state whether or not to include a dollar sign. When dollar signs are included, negatives are written as "-$500" rather than "$-500".

type ShareQuantity = Fixed E4 Source #

A fixed-width implementation of quantities for shares. So far, I have seen sites report share quantities to up to four decimal points, henced the value.

Parse a share quantity. Currently an alias for parseQuantity.

Render a share quantity. Currently an alias for renderQuantity.

Parse a fixed-width number. Should parse negative values, as well. This does support QIF's annoying "5." notation, as well.

Render a quantity. As opposed to the parser, this output function will always represent numbers to their full precision.

# Old-school dates

Parse a date, using old-school, incredibly unwise, "mmddyy" formats. To simplify my life, this assumes that all dates start in 2000, rather than in 1970 or some other date. Thus, if you have data going back before 2000, you'll need to post-process this to the appropriate date, by subtracting 100 appropriately. Hopefully by 2100 noone will be using QIF anymore, and this won't matter.

Render the date in QIF's silly format.