Convert ANZ Plus bank statement PDFs to OFX format for seamless import into Actual Budget and other personal finance applications.
- π¨ Modern Web Interface - Drag-and-drop PDF upload with instant conversion
- π Multi-line Descriptions - Captures complete transaction details including merchant, location, and reference numbers
- π― Smart Truncation - Preserves merchant names in OFX NAME field by removing common prefixes
- π― Accurate Detection - Uses balance change analysis for reliable credit/debit identification
- π Collision-free IDs - Sequential FITID strategy ensures unique transaction IDs for duplicate prevention
- π Complete History - Converts all transactions without filtering to maintain accurate balances
- β‘ OFX v2.20 XML - Modern format compatible with Actual Budget and other finance apps
- π Comprehensive Logging - Production-ready logging with file rotation, JSON formatting, and monitoring support
Official multi-architecture image - automatically built and published via GitHub Actions:
# Pull and run from Docker Hub (no installation required!)
docker pull spydisec/anzplus-ofx-converter:latest
docker run -d -p 8000:8000 spydisec/anzplus-ofx-converter:latest
# Open your browser to http://localhost:8000Why Docker?
- β No Python installation required
- β Works on Windows, Mac, Linux
- β Multi-architecture support (amd64, arm64/Apple Silicon)
- β Production-ready configuration
- β Automatic health checks
- β Simple version pinning
Version Pinning (Production):
# Pin to specific version (recommended for stability)
docker run -d -p 8000:8000 spydisec/anzplus-ofx-converter:1.0.0
# Or always use latest
docker run -d -p 8000:8000 spydisec/anzplus-ofx-converter:latestCustom Configuration:
docker run -d -p 8000:8000 \
-e WORKERS=8 \
-e LOG_LEVEL=debug \
-e ENVIRONMENT=production \
spydisec/anzplus-ofx-converter:latestDocker Compose:
version: '3.8'
services:
anzplus-ofx-converter:
image: spydisec/anzplus-ofx-converter:latest
ports:
- "8000:8000"
environment:
- ENVIRONMENT=production
- WORKERS=4
- LOG_LEVEL=info
restart: unless-stoppedBuild Locally (Optional):
Most users should use the official image. For development:
# Windows
.\docker\build-local.ps1
# Linux/Mac
docker build -f docker/Dockerfile -t anzplus-ofx:local .π Full Docker documentation: docker/README.md
For local development or customization:
# Clone the repository
git clone https://github.com/spydisec/PDFtoOFX.git
cd PDFtoOFX
# Create and activate virtual environment (recommended)
python3 -m venv .venv
source .venv/bin/activate # On Linux/Mac
# or
.venv\Scripts\Activate.ps1 # On Windows
# Install dependencies
pip install -r requirements.txtπ For detailed installation instructions including Linux self-hosting, systemd service setup, and Nginx configuration, see INSTALLATION.md
Launch the elegant web app with drag-and-drop file upload:
python run_web.pyThen open http://localhost:8000 in your browser.
Web Interface Features:
- π¨ Minimalistic design with Tailwind CSS
- π€ Drag-and-drop PDF upload
- β‘ Real-time conversion progress
- π₯ One-click OFX download
- π Secure (files processed in memory, auto-deleted after download)
- π± Fully mobile responsive
python convert_pdf.py input.pdf output.ofxExample:
python convert_pdf.py statement.pdf statement.ofxOutput:
Converting: statement.pdf
Output to: statement.ofx
Step 1: Extracting text from PDF...
β Extracted 5438 characters
Step 2: Parsing transactions...
β Found 26 transactions
β Date range: 2026-01-02 to 2026-01-22
β Opening balance: $3117.92
β Closing balance: $232.16
Step 3: Generating OFX file...
β Generated 8661 bytes of OFX data
Step 4: Writing OFX file...
β Saved to statement.ofx
β
Conversion complete!
- Open Actual Budget
- Navigate to your account
- Click Import β Select File
- Choose your
.ofxfile - Review and approve transactions
Verify Duplicate Detection:
Re-import the same OFX file to confirm all transactions are detected as duplicates, validating the FITID strategy.
This converter is specifically designed for ANZ Plus digital PDF statements.
β Supported:
- ANZ Plus digital PDF statements
- Transaction format: Date, Description, Credit, Debit, Balance columns
- Multi-line transaction details
β Not Supported:
- Other ANZ products (ANZ Classic, ANZ Access, etc.) - different formats
- Scanned/image PDFs - no OCR functionality
- Other banks - requires bank-specific parsers
ANZ Plus PDF:
22 Jan VISA DEBIT PURCHASE CARD 1633 MYKI $25.00 $233.45
PAYMENTS MELBOURNE
Generated OFX:
<STMTTRN>
<TRNTYPE>DEBIT</TRNTYPE>
<DTPOSTED>20260122000000.000[+0:UTC]</DTPOSTED>
<TRNAMT>-25.00</TRNAMT>
<FITID>ANZ_20260122_0001</FITID>
<NAME>MYKI PAYMENTS MELBOURNE</NAME>
<MEMO>VISA DEBIT PURCHASE CARD 1633 MYKI PAYMENTS MELBOURNE</MEMO>
</STMTTRN>Improvements:
- NAME:
MYKI PAYMENTS MELBOURNE(merchant visible, smart truncation) - MEMO: Full description with all details
- Type: Correctly identified as DEBIT using balance analysis
- FITID:
ANZ_20260122_0001(unique, collision-free)
PDFtoOFX/
βββ README.md # This file
βββ ARCHITECTURE.md # Technical documentation
βββ LICENSE # MIT license
βββ pyproject.toml # Package configuration
βββ requirements.txt # Dependencies
βββ requirements-dev.txt # Development dependencies
β
βββ run_web.py # Web app launcher
βββ convert_pdf.py # CLI tool
β
βββ app/ # Application code
β βββ models.py # Pydantic data models
β βββ services/ # Core business logic
β β βββ anz_plus_parser.py # ANZ Plus PDF parser
β β βββ fitid_generator.py # Unique ID generation
β β βββ ofx_generator.py # OFX XML generation
β β βββ pdf_extractor.py # PDF text extraction
β βββ web/ # Web application
β βββ main.py # FastAPI app
β βββ routes.py # API endpoints
β βββ templates/ # HTML templates
β
βββ tests/ # Test suite
βββ test_converter.py # Unit & integration tests
- Python: 3.11 or higher
- Dependencies:
ofxtools>=0.9.6- OFX v2.20 XML generationpdfplumber>=0.11.9- PDF text extractionpydantic>=2.12.5- Data validationpython-dateutil- Date parsingfastapi>=0.109.0- Web frameworkuvicorn[standard]>=0.27.0- ASGI serverjinja2>=3.1.3- Template renderingpython-multipart>=0.0.6- File uploadsaiofiles>=23.2.1- Async file operations
Run the test suite:
# Run all tests
python -m pytest tests/ -v
# Run with coverage report
python -m pytest tests/ --cov=app --cov-report=htmlTest Results:
β
6/6 tests passing
β
91% code coverage
β
FITID collision detection verified
β
Multi-line description capture validated
β
Smart truncation tested
β
End-to-end conversion verified
# Create virtual environment
python -m venv .venv
# Activate (Windows)
.venv\Scripts\activate
# Activate (Linux/Mac)
source .venv/bin/activate
# Install development dependencies
pip install -r requirements-dev.txt- Follow PEP 8 guidelines
- Use type hints for all functions
- Write docstrings for public APIs
- Maximum line length: 100 characters
# Auto-reload on code changes
python run_web.py
# Or use uvicorn directly
uvicorn app.web.main:app --reload --port 8000# Format code
black app/ tests/
# Run linter
flake8 app/ tests/
# Type checking
mypy app/
# Run tests
pytest tests/ -vContributions are welcome! Here's how:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for new functionality
- Ensure all tests pass (
pytest tests/ -v) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Write tests for new features
- Maintain or improve code coverage (currently 91%)
- Follow existing code style and patterns
- Update documentation as needed
See ARCHITECTURE.md for detailed technical documentation.
High-level flow:
- Extract text from PDF using pdfplumber
- Parse transactions with regex patterns (supports multi-line descriptions)
- Truncate smartly (remove common prefixes, preserve merchant names)
- Detect credit/debit using balance change analysis
- Generate unique FITIDs using sequential counter
- Create OFX v2.20 XML using ofxtools library
- Single Bank: Currently only supports ANZ Plus PDF format
- Digital Only: Does not support scanned/image PDFs (no OCR)
- Date Handling: Uses current year if not found in PDF
- Manual Categorization: Transactions import uncategorized
Pull from Docker Hub:
docker pull spydisec/anzplus-ofx-converter:latest
docker run -d -p 8000:8000 --restart unless-stopped spydisec/anzplus-ofx-converter:latestUsing Docker Compose:
version: '3.8'
services:
anzplus-ofx:
image: spydisec/anzplus-ofx-converter:latest
ports:
- "8000:8000"
environment:
- WORKERS=4
- LOG_LEVEL=info
restart: unless-stoppedMulti-Architecture Support:
- Automatically pulls correct image for your platform
- Supports: linux/amd64 (Intel/AMD), linux/arm64 (Apple Silicon, ARM)
π Complete Docker guide: docker/README.md
python run_web.pyContainer-based (Docker):
- Render.com: Free tier available, auto-deploy from GitHub
- Railway.app: $5/month, easy Docker deployment
- Fly.io: Pay-as-you-go, multi-region, global edge
- AWS ECS/Fargate: Enterprise-grade container orchestration
- Google Cloud Run: Serverless containers, auto-scaling
Traditional hosting:
- Self-hosted VPS with systemd service (see INSTALLATION.md)
# Check if port 8000 is in use
netstat -ano | findstr :8000
# Use different port
uvicorn app.web.main:app --port 3000- Ensure PDF is from ANZ Plus (not ANZ Classic/Access)
- Verify PDF is not scanned/image-based
- Check PDF is not password-protected
- All transactions are now preserved (including ROUND UP)
- Verify opening and closing balances match your PDF
- Check for any filtered transactions
This project is licensed under the MIT License - see LICENSE file for details.
- ofxtools - OFX file generation
- pdfplumber - PDF text extraction
- Actual Budget - Personal finance management
- FastAPI - Modern web framework
- Tailwind CSS - Utility-first CSS
- HTMX - Dynamic HTML interactions
- Issues: GitHub Issues
- Installation Guide: INSTALLATION.md - Detailed setup for Linux, Docker, systemd, etc.
- Architecture: ARCHITECTURE.md - Technical documentation
- Logging: LOGGING.md - Comprehensive logging and monitoring guide
- Repository: github.com/spydisec/PDFtoOFX
Note: This project is not affiliated with ANZ Bank. It is an independent tool created for personal finance management.
Made with β€οΈ for the Actual Budget community