Task 586: .QFX File Format
Task 586: .QFX File Format
.QFX File Format Specifications
.QFX is a proprietary variant of the Open Financial Exchange (OFX) format developed by Intuit for use with Quicken software. It is used to exchange financial data, such as account information and transactions, between financial institutions and personal finance applications. The format is text-based and consists of a header section with key-value pairs, followed by an SGML/XML-like body containing structured financial data. QFX extends the standard OFX format with additional fields (e.g., <INTU.BID>, <INTU.FID>) to support licensing fees paid by institutions to Intuit, ensuring compatibility only with licensed software like Quicken. The structure supports banking, credit card, loan, investment, and bill payment data, with elements for signon, statements, transactions, and balances.
The format is versioned (commonly VERSION:102 for SGML or 200+ for XML), case-sensitive, and uses specific datetime formats (e.g., YYYYMMDDHHMMSS). It supports synchronization via tokens, error handling via status codes, and extensibility through custom aggregates like .
- List of all the properties of this file format intrinsic to its file system.
The properties refer to the key header fields, elements, and aggregates defined in the format. These are intrinsic to the file's structure for parsing and validation. Based on the OFX specification (upon which QFX is built), the main properties include:
Header Properties (key-value pairs at the start of the file):
- OFXHEADER: Version of the header (e.g., 100 for SGML, 200 for XML).
- DATA: Data type (e.g., OFXSGML).
- VERSION: OFX data version (e.g., 102, 220, 230).
- SECURITY: Security level (e.g., NONE, TYPE1).
- ENCODING: Character encoding (e.g., USASCII, UTF-8).
- CHARSET: Character set (e.g., 1252).
- COMPRESSION: Compression type (e.g., NONE).
- OLDFILEUID: UID of the previous file for error recovery (e.g., NONE or a unique string).
- NEWFILEUID: UID for the current file (e.g., NONE or a unique string).
Body Properties (SGML/XML-like tags within ...):
- Signon-Related: DTCLIENT, USERID, USERPASS, LANGUAGE, FI (with ORG, FID), APPID, APPVER, USERKEY, ACCESSKEY, SESSCOOKIE, MFACHALLENGE, MFAPHRASE.
- Status-Related: CODE, SEVERITY (INFO/WARN/ERROR), MESSAGE.
- Banking Account-Related: BANKACCTFROM/BANKACCTTO (with BANKID, BRANCHID, ACCTID, ACCTTYPE - CHECKING/SAVINGS/MONEYMRKT/CREDITLINE/CD), BANKACCTINFO/CCACCTINFO/LOANACCTINFO.
- Transaction-Related: STMTTRN (with TRNTYPE - CREDIT/DEBIT/INT/ DIV/FEE/SRVCHG/DEP/ATM/POS/ XFER/PAYMENT/ CASH/ DIRECTDEP/ DIRECTDEBIT/ REPEATPMT/ OTHER/ HOLD, DTPOSTED, DTUSER, DTAVAIL, TRNAMT, FITID, CORRECTFITID, CORRECTACTION - REPLACE/DELETE, SRVRTID, CHECKNUM, REFNUM, SIC, PAYEEID, NAME, MEMO, INV401KSOURCE).
- Statement-Related: STMTRS/CCSTMTRS/LOANSTMTRS (with CURDEF, BANKTRANLIST/LOANTRANLIST/AMRTTRANLIST, DTSTART, DTEND, LEDGERBAL - with BALAMT, DTASOF, AVAILBAL - with BALAMT, DTASOF, BALLIST, MKTGINFO, PRINBAL, INTYTD, NEXTDTINT, LOANINT, LOANIRATE, LOANPMT, ESCROWAMT, CLOSING/CCCLOSING/LOANCLOSING).
- Balance-Related: BAL (with NAME, DESC, BALTYPE - DOLLAR/PERCENT/NUMBER, VALUE, DTASOF, CURRENCY - CURSYM, CURRATE).
- Transfer/Payment-Related: XFERINFO (with BANKACCTFROM, BANKACCTTO, TRNAMT), XFERPRCSTS, LASTPMTINFO, RECURRINST (with NINSTS, FREQ - INTRADAY/WEEKLY/BIWEEKLY/TWICEMONTHLY/MONTHLY/BIMONTHLY/QUARTERLY/SEMIANNUALLY/YEARLY/DAILY), PMTINFO, EXTDPAYEE, PMTPRCSTS, WIREBENEFICIARY, WIREDESTBANK, CHKRANGE, CHKDESC.
- Synchronization-Related: TOKEN, TOKENONLY, REFRESH, REJECTIFMISSING, LOSTSYNC, INCSYNCFROM, INCIMAGES, USEHTML, INCTRANIMG.
- Image-Related: IMAGEDATA (with IMAGEREF, IMAGEPROF - with IMAGETYPE, IMAGECHAL, IMAGEFAIL), IMAGEREF (with IMAGETYPE - GIF/JPEG/PNG, CORRELATIONID, IMAGEREF, DEFAULTIMAGETYPE).
- Bill Pay/Presentment-Related: BILLERINFO, PRESBILLINFO, PRESACCTFROM, FINDBILLER, PRESLIST, PRESDETAIL.
- Investment-Related: INVACCTINFO, INVACCTFROM, INV401K, MATCHINFO, CONTRIBINFO, LOANINFO, SECID (with UNIQUEID, UNIQUEIDTYPE - CUSIP/ISIN/SEDOL), SECLIST.
- Extension/Other: OFXEXTENSION, CLTCOOKIE, TAN, TRNUID (unique transaction ID), STATUS, DTACCTUP, SVCSTATUS (AVAILABLE/ACTIVE/PENDING), SVC, EMAILPROF (with CANEMAIL, CANNOTIFY).
- QFX-Specific Additions: INTU.BID (bank ID for licensing), INTU.FID (financial institution ID), INTU.USERID (user ID for licensing validation).
These properties represent the core structure, with aggregates grouping related elements (e.g., ......).
- Two direct download links for files of format .QFX.
I was unable to find public direct download links for actual .QFX files, as they typically contain sensitive financial data and are generated behind logins by banks or tools. Public samples are rare to avoid privacy issues. Instead, here are two example .QFX contents based on standard formats (you can save them as .qfx files locally for testing):
- Example 1 (Simple banking statement sample):
OFXHEADER:100
DATA:OFXSGML
VERSION:102
SECURITY:NONE
ENCODING:USASCII
CHARSET:1252
COMPRESSION:NONE
OLDFILEUID:NONE
NEWFILEUID:NONE
<OFX>
<SIGNONMSGSRSV1>
<SONRS>
<STATUS>
<CODE>0
<SEVERITY>INFO
</STATUS>
<LANGUAGE>ENG
<DTSERVER>20251105120000
</SONRS>
</SIGNONMSGSRSV1>
<BANKMSGSRSV1>
<STMTTRNRS>
<TRNUID>1001
<STATUS>
<CODE>0
<SEVERITY>INFO
</STATUS>
<STMTRS>
<CURDEF>USD
<BANKACCTFROM>
<BANKID>123456789
<ACCTID>987654321
<ACCTTYPE>CHECKING
</BANKACCTFROM>
<BANKTRANLIST>
<DTSTART>20251001
<DTEND>20251105
<STMTTRN>
<TRNTYPE>DEBIT
<DTPOSTED>20251015
<TRNAMT>-50.00
<FITID>12345
<NAME>Grocery Store
<MEMO>Food purchase
</STMTTRN>
</BANKTRANLIST>
<LEDGERBAL>
<BALAMT>1000.00
<DTASOF>20251105
</LEDGERBAL>
</STMTRS>
</STMTTRNRS>
</BANKMSGSRSV1>
</OFX>
- Example 2 (Credit card statement sample):
OFXHEADER:100
DATA:OFXSGML
VERSION:102
SECURITY:NONE
ENCODING:USASCII
CHARSET:1252
COMPRESSION:NONE
OLDFILEUID:NONE
NEWFILEUID:NONE
<OFX>
<SIGNONMSGSRSV1>
<SONRS>
<STATUS>
<CODE>0
<SEVERITY>INFO
</STATUS>
<LANGUAGE>ENG
<DTSERVER>20251105120000
</SONRS>
</SIGNONMSGSRSV1>
<CREDITCARDMSGSRSV1>
<CCSTMTTRNRS>
<TRNUID>1002
<STATUS>
<CODE>0
<SEVERITY>INFO
</STATUS>
<CCSTMTRS>
<CURDEF>USD
<CCACCTFROM>
<ACCTID>4111111111111111
</CCACCTFROM>
<BANKTRANLIST>
<DTSTART>20251001
<DTEND>20251105
<STMTTRN>
<TRNTYPE>DEBIT
<DTPOSTED>20251020
<TRNAMT>-100.00
<FITID>67890
<NAME>Online Shopping
<MEMO>Electronics
</STMTTRN>
</BANKTRANLIST>
<LEDGERBAL>
<BALAMT>-150.00
<DTASOF>20251105
</LEDGERBAL>
</CCSTMTRS>
</CCSTMTTRNRS>
</CREDITCARDMSGSRSV1>
</OFX>
- Ghost blog embedded HTML JavaScript for drag and drop .QFX file to dump properties.
Here's an embedded HTML/JavaScript snippet that can be placed in a blog post (e.g., Ghost blog). It creates a drop zone where users can drag and drop a .QFX file. The script reads the file, parses the header and body, extracts all properties (header keys and body tags with values), and dumps them to the screen in a readable format.
- Python class for opening, decoding, reading, writing, and printing .QFX properties.
class QFXParser:
def __init__(self, filename=None):
self.filename = filename
self.properties = {}
self.body = []
def read(self):
if not self.filename:
raise ValueError("No filename provided.")
with open(self.filename, 'r') as f:
content = f.read()
lines = content.split('\n')
# Parse header
i = 0
while i < len(lines) and ':' in lines[i]:
key, value = lines[i].split(':', 1)
self.properties[key.strip()] = value.strip()
i += 1
# Parse body (simple tag-value, handling aggregates as flat)
while i < len(lines):
line = lines[i].strip()
if line.startswith('<') and not line.startswith('</'):
tag = line[1:].strip()
i += 1
value = ''
while i < len(lines) and not lines[i].strip().startswith('<'):
value += lines[i].strip() + ' '
i += 1
self.properties[tag] = value.strip()
continue
i += 1
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}: {value}")
def write(self, new_filename):
if not self.properties:
raise ValueError("No properties to write.")
with open(new_filename, 'w') as f:
# Write header
for key, value in self.properties.items():
if key.isupper() and ':' not in key: # Assume header keys are uppercase
f.write(f"{key}:{value}\n")
f.write('\n')
# Write body (simple reconstruction, assuming flat properties)
f.write('<OFX>\n')
for key, value in self.properties.items():
if not key.isupper() or ':' in key: # Body tags
f.write(f"<{key}>{value}\n")
f.write('</OFX>\n')
# Example usage:
# parser = QFXParser('sample.qfx')
# parser.read()
# parser.print_properties()
# parser.write('output.qfx')
- Java class for opening, decoding, reading, writing, and printing .QFX properties.
import java.io.*;
import java.util.HashMap;
import java.util.Map;
public class QFXParser {
private String filename;
private Map<String, String> properties = new HashMap<>();
public QFXParser(String filename) {
this.filename = filename;
}
public void read() throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(filename));
String line;
// Parse header
while ((line = reader.readLine()) != null && line.contains(":")) {
String[] parts = line.split(":", 2);
if (parts.length == 2) {
properties.put(parts[0].trim(), parts[1].trim());
}
}
// Parse body (simple tag-value)
String tag = null;
StringBuilder value = new StringBuilder();
while (line != null) {
line = line.trim();
if (line.startsWith("<") && !line.startsWith("</")) {
if (tag != null) {
properties.put(tag, value.toString().trim());
}
tag = line.substring(1).trim();
value = new StringBuilder();
} else if (tag != null) {
value.append(line).append(" ");
}
line = reader.readLine();
}
if (tag != null) {
properties.put(tag, value.toString().trim());
}
reader.close();
}
public void printProperties() {
for (Map.Entry<String, String> entry : properties.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
public void write(String newFilename) throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter(newFilename));
// Write header
for (Map.Entry<String, String> entry : properties.entrySet()) {
String key = entry.getKey();
if (key.toUpperCase().equals(key) && !key.contains(":")) { // Header keys
writer.write(key + ":" + entry.getValue() + "\n");
}
}
writer.write("\n");
// Write body
writer.write("<OFX>\n");
for (Map.Entry<String, String> entry : properties.entrySet()) {
String key = entry.getKey();
if (!key.toUpperCase().equals(key) || key.contains(":")) { // Body tags
writer.write("<" + key + ">" + entry.getValue() + "\n");
}
}
writer.write("</OFX>\n");
writer.close();
}
// Example usage:
// public static void main(String[] args) throws IOException {
// QFXParser parser = new QFXParser("sample.qfx");
// parser.read();
// parser.printProperties();
// parser.write("output.qfx");
// }
}
- JavaScript class for opening, decoding, reading, writing, and printing .QFX properties.
(Note: JavaScript in browser context uses FileReader for reading; writing uses Blob for download.)
class QFXParser {
constructor(filename = null) {
this.filename = filename;
this.properties = {};
}
async read(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
const content = e.target.result;
const lines = content.split('\n').map(l => l.trim()).filter(l => l);
// Parse header
let i = 0;
while (i < lines.length && lines[i].includes(':')) {
const [key, value] = lines[i].split(':');
this.properties[key.trim()] = value.trim();
i++;
}
// Parse body
while (i < lines.length) {
if (lines[i].startsWith('<') && !lines[i].startsWith('</')) {
const tag = lines[i].slice(1).trim();
i++;
let value = '';
while (i < lines.length && !lines[i].startsWith('<')) {
value += lines[i] + ' ';
i++;
}
this.properties[tag] = value.trim();
continue;
}
i++;
}
resolve();
};
reader.onerror = reject;
if (file instanceof File) {
reader.readAsText(file);
} else if (this.filename) {
// For Node.js, use fs (requires import fs)
// fs.readFileSync(this.filename, 'utf8');
} else {
reject('No file provided.');
}
});
}
printProperties() {
console.log('Properties:');
for (const [key, value] of Object.entries(this.properties)) {
console.log(`${key}: ${value}`);
}
}
write(newFilename) {
let content = '';
// Header
for (const [key, value] of Object.entries(this.properties)) {
if (key.toUpperCase() === key && !key.includes(':')) {
content += `${key}:${value}\n`;
}
}
content += '\n';
// Body
content += '<OFX>\n';
for (const [key, value] of Object.entries(this.properties)) {
if (key.toUpperCase() !== key || key.includes(':')) {
content += `<${key}>${value}\n`;
}
}
content += '</OFX>\n';
// Download in browser
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = newFilename || 'output.qfx';
a.click();
URL.revokeObjectURL(url);
}
}
// Example usage (browser):
// const parser = new QFXParser();
// const input = document.createElement('input'); input.type = 'file';
// input.onchange = async (e) => {
// await parser.read(e.target.files[0]);
// parser.printProperties();
// parser.write('output.qfx');
// };
// input.click();
- C class (using C++ for class support) for opening, decoding, reading, writing, and printing .QFX properties.
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <sstream>
class QFXParser {
private:
std::string filename;
std::map<std::string, std::string> properties;
public:
QFXParser(const std::string& fn) : filename(fn) {}
void read() {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Error opening file." << std::endl;
return;
}
std::string line;
// Parse header
while (std::getline(file, line)) {
if (line.find(':') != std::string::npos) {
std::istringstream iss(line);
std::string key, value;
std::getline(iss, key, ':');
std::getline(iss, value);
properties[key] = value;
} else if (!line.empty()) {
break;
}
}
// Parse body
std::string tag, value;
do {
line = line.substr(line.find_first_not_of(" \t"));
if (line.rfind('<', 0) == 0 && line.rfind("</", 0) != 0) {
if (!tag.empty()) {
properties[tag] = value;
}
tag = line.substr(1);
value = "";
} else if (!tag.empty()) {
value += line + " ";
}
} while (std::getline(file, line));
if (!tag.empty()) {
properties[tag] = value;
}
file.close();
}
void printProperties() {
for (const auto& pair : properties) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
}
void write(const std::string& newFilename) {
std::ofstream out(newFilename);
if (!out.is_open()) {
std::cerr << "Error writing file." << std::endl;
return;
}
// Header
for (const auto& pair : properties) {
std::string key = pair.first;
if (std::all_of(key.begin(), key.end(), ::isupper) && key.find(':') == std::string::npos) {
out << key << ":" << pair.second << "\n";
}
}
out << "\n";
// Body
out << "<OFX>\n";
for (const auto& pair : properties) {
std::string key = pair.first;
if (!std::all_of(key.begin(), key.end(), ::isupper) || key.find(':') != std::string::npos) {
out << "<" << key << ">" << pair.second << "\n";
}
}
out << "</OFX>\n";
out.close();
}
};
// Example usage:
// int main() {
// QFXParser parser("sample.qfx");
// parser.read();
// parser.printProperties();
// parser.write("output.qfx");
// return 0;
// }