- Quick Start
- Development Setup
- Project Architecture
- Building the Project
- Contributing Code
- Development Guides
- Getting Help
- Appendix
Thanks for your interest in contributing! We welcome improvements of all sizes — from small bug fixes to larger features. To make collaboration smooth and reviews efficient, please keep the following guidelines in mind:
- Keep pull requests small and focused on a single feature or fix. The shorter and clearer the change, the easier it is to review.
- Use the standard code formatting, but avoid reformatting code that’s unrelated to your change.
- For larger changes, open a draft pull request early to outline your idea and gather feedback before investing too much time polishing.
- Follow existing code patterns and conventions throughout the project.
- Write concise comments where they help future readers understand important decisions.
- Add tests for calculation logic, especially in the
name.abuchen.portfoliobundle. Avoid modifying existing tests unless a breaking or incompatible change makes it necessary.
Eclipse IDE is required for Portfolio Performance development as the application is built on Eclipse RCP (Rich Client Platform).
Prerequisites
- Java 21 - Required minimum version (Download from Azul)
- Eclipse IDE for RCP and RAP Developers - Download the RCP package
Plugins from the Eclipse Marketplace
Install via Eclipse Marketplace (drag and drop the Install button to your workspace):
- Infinitest - Continuous testing
- ResourceBundle Editor - Translation file editing
- Checkstyle Plug-In - Code style checking
- SonarQube for IDE - Code quality analysis
- Launch Configuration DSL - Launch configuration management
Plugins from the Eclipse Update Site
Menu → Help → Install New Software → Select Latest Eclipse Simultaneous Release:
- M2E PDE Integration (under General Purpose Tools)
- Eclipse e4 Tools (under General Purpose Tools)
Note: depending on your Eclipse package, some of these may already be installed. Uncheck "Hide items that are already installed" at the bottom of the dialog to verify.
Configure Eclipse
Menu → Window → Preferences:
Java→Editor→Save Actions- ✓ Format Source Code → Format edited lines
- ✓ Organize imports
Java→Editor→Content Assist- ✓ Add import instead of qualified name
- ✓ Use static imports
Java→Editor→Content Assist→Favorites- Add these types for import suggestions:
name.abuchen.portfolio.util.TextUtilname.abuchen.portfolio.datatransfer.ExtractorUtilsname.abuchen.portfolio.datatransfer.ExtractorMatchersname.abuchen.portfolio.datatransfer.ExtractorTestUtilitiesname.abuchen.portfolio.junit.TestUtilities
- Add these types for import suggestions:
Java→Installed JREs- Add Java 21 JDK
Project Setup
- Fork the repository using GitHub's fork workflow to create your own copy
- Import projects: Within Eclipse, clone and import all projects
- Setup target platform:
- Open
portfolio-target-definitionproject - Open
portfolio-target-definition.targetfile (right-click → Open With → Target Editor) - Click "Set as Active Target Platform" (requires Internet, may take time)
- Open
- Add Launch Configuration view:
Menu→Window→Show View→Other...→Debug→Launch Configuration - Run the application: Select
Eclipse Application→PortfolioPerformanceand right-click Run - Run tests: Select
JUnit Plug-in Tests→PortfolioPerformance_TestsorPortfolioPerformance_UI_Tests- If you get
Required plug-in 'org.eclipse.pde.junit.runtime' could not be found, add your running Eclipse installation as a location in the target. Openportfolio-target-definition.targetin the Target Editor (same as in step 3), clickAdd...→Installation→ enter${eclipse_home}(works cross-platform) → Finish. Save the file. Keep this change in your working tree only, do not commit it. This bundle is part of the Eclipse PDE tooling rather than the application's runtime, so it intentionally isn't inportfolio-target-definition.target. CI uses Maven/Tycho, which has its own test runner, so this only affects running tests from inside Eclipse.
- If you get
Optional Language Packs
Install via Menu → Help → Install New Software:
- Update site:
https://download.eclipse.org/technology/babel/update-site/latest/ - Select desired language packs
- Force language with
-nlparameter:eclipse.exe -nl de
Portfolio Performance is built on Eclipse RCP (Rich Client Platform) with an E4 application model, providing a modular plugin-based architecture.
Key Technologies
- Eclipse RCP - Rich Client Platform framework
- OSGi Bundles - Modular plugin architecture with clear separation of concerns
- Maven + Tycho - Eclipse-specific build system
- Java 21 - Minimum required version
- Dependency Injection - Jakarta annotations (@Inject, @PostConstruct, @PreDestroy)
- Event-driven communication - E4 event broker + PropertyChangeSupport
Module Structure
portfolio-target-definition # Eclipse dependencies
↓
name.abuchen.portfolio # Core domain & business logic
↓
name.abuchen.portfolio.ui # User interface layer
↓
portfolio-product # Application packaging
Test Infrastructure:
name.abuchen.portfolio.tests # Core module tests
name.abuchen.portfolio.ui.tests # UI module tests
Finding Your Way
- Domain Layer:
name.abuchen.portfolio/src/name/abuchen/portfolio/model/- Core entities (Client, Portfolio, Account, Security) - PDF Importers:
name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/- Bank statement parsers - UI Components:
name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/- User interface
Run the application:
- Launch Configuration view →
Eclipse Application→PortfolioPerformance→ Right-click Run
Run tests:
- Core tests:
JUnit Plug-in Tests→PortfolioPerformance_Tests - UI tests:
JUnit Plug-in Tests→PortfolioPerformance_UI_Tests
Maven is used for CI/CD builds. Local development can use Eclipse or Maven.
Prerequisites:
JAVA_HOMEpoints to Java 21 JDK- Maven installed
Standard Build:
# Linux/macOS
export MAVEN_OPTS="-Xmx4g"
mvn -f portfolio-app/pom.xml clean verify
# Windows
set MAVEN_OPTS="-Xmx4g"
mvn -f portfolio-app/pom.xml clean verifyBuild Core Module Only:
mvn -f portfolio-app/pom.xml clean compile \
-pl :portfolio-target-definition,:name.abuchen.portfolio.pdfbox1,:name.abuchen.portfolio.pdfbox3,:name.abuchen.portfolio -am -amdRun Core Tests:
mvn -f portfolio-app/pom.xml verify -o \
-pl :portfolio-target-definition,:name.abuchen.portfolio.pdfbox1,:name.abuchen.portfolio.pdfbox3,:name.abuchen.portfolio,:name.abuchen.portfolio.junit,:name.abuchen.portfolio.tests -am -amdRun Single Test Class:
mvn -f portfolio-app/pom.xml verify -o \
-pl :portfolio-target-definition,:name.abuchen.portfolio.pdfbox1,:name.abuchen.portfolio.pdfbox3,:name.abuchen.portfolio,:name.abuchen.portfolio.junit,:name.abuchen.portfolio.tests -am -amd \
-Dtest=<fully qualified test class name>You can use the profile -Plocal-dev for a faster locale build. However, keep in mind that this skips the translation bundles, checkstyle checks, and other checks.
- Use
varkeyword where possible for local variables - Format source code using Eclipse formatter (configured in project)
- Exception: Do NOT reformat PDF importer files - carefully insert code manually
- Use
@formatter:offand@formatter:onto protect formatting
- Organize imports automatically (save action enabled)
- Add static imports for utility classes (ExtractorUtils, TextUtil, etc.)
- Add test cases for new functionality in
name.abuchen.portfolio.tests - No UI tests required
- Test all calculations and business logic thoroughly
- Use custom matchers when testing PDF importers
Commit Messages
Write commit messages in English:
Short summary (50 chars or less)
More detailed explanation if needed. Wrap at 72 characters.
Explain what and why, not how.
Closes #123
Issue: https://forum.portfolio-performance.info/t/...
- Link GitHub issues:
Closes #<ISSUE NUMBER>after empty line - Link forum threads:
Issue: https://...with forum post URL
Pull Request Process:
- Rebase your branch on latest
master(do NOT merge master into your branch) - Format your code (except PDF importers - manual formatting only)
- Create Pull Request via GitHub or GitHub Desktop
PDF importers extract transactions from bank and broker PDF statements. Each bank/broker has its own extractor class.
How it works:
- User selects PDF files via import menu or drag-and-drop
- PDFBox converts PDF to text (one string per line)
- Each extractor applies regex patterns to extract transactions
- Results are presented to user for review and import
Creating Debug Files:
File → Import → Debug: Create text from PDF...
- Remove all personal data
- Do NOT insert new lines, breaks, or spaces
- Keep original PDF text structure intact
Source Locations:
- Extractors:
name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/ - Tests:
name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/ - Naming:
BankNamePDFExtractor.javaandBankNamePDFExtractorTest.java
Current Reference Implementations:
- Baader Bank AG - Modern comprehensive implementation
- Comdirect Bank AG - Advanced post-processing
- Saxo Bank A/S - Complex pattern matching
Test File Naming Convention:
Use local language with two-digit counter:
Buy01.txt,Sell01.txt- Purchase and saleDividend01.txt- DividendsWertpapiereingang01.txt- Incoming securities (German example)GiroKontoauszug01.txt- Bank account statement (German example)
Modern Test Pattern (Preferred):
Use ExtractorMatchers for readable assertions:
@Test
public void testWertpapierKauf01()
{
var extractor = new ScalableCapitalPDFExtractor(new Client());
List<Exception> errors = new ArrayList<>();
var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Kauf01.txt"), errors);
assertThat(errors, empty());
assertThat(countSecurities(results), is(1L));
assertThat(countBuySell(results), is(1L));
assertThat(countAccountTransactions(results), is(0L));
assertThat(countAccountTransfers(results), is(0L));
assertThat(countItemsWithFailureMessage(results), is(0L));
assertThat(countSkippedItems(results), is(0L));
assertThat(results.size(), is(2));
new AssertImportActions().check(results, "EUR");
// check security
assertThat(results, hasItem(security( //
hasIsin("IE0008T6IUX0"), hasWkn(null), hasTicker(null), //
hasName("Vngrd Fds-ESG Dv.As-Pc Al ETF"), //
hasCurrencyCode("EUR"))));
// check transaction
assertThat(results, hasItem(purchase( //
hasDate("2024-12-12T13:12:51"), hasShares(3.00), //
hasSource("Kauf01.txt"), //
hasNote("Ord.-Nr.: SCALsin78vS5CYz"), //
hasAmount("EUR", 19.49), hasGrossValue("EUR", 18.50), //
hasTaxes("EUR", 0.00), hasFees("EUR", 0.99))));
}See BaaderBankPDFExtractorTest for examples.
Key Utilities:
- AbstractPDFExtractor - Base class
- ExtractorUtils - Amount conversion, tax/fee processing
- ExtractorMatchers - Modern test assertions
- TextUtil - String manipulation for PDF text
Important Notes:
- Do NOT auto-format PDF extractor files (use
@formatter:off/@formatter:on) - Test files must be UTF-8 encoded
- Anonymize personal data but preserve structure
- Match all special characters with
.(dot) in regex
The Interactive Broker Flex Query importer handles XML-compliant Interactive Broker Activity Statements.
Source Locations:
- Importer:
name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/ibflex/ - Tests:
name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/ibflex/
Reference:
Portfolio Performance is translated into multiple languages using POEditor.
Contributing Translations:
- Register with POEditor project
- Open a ticket for new languages
- Translations are merged before each release
For Code Contributors:
- Use Source → Externalize Strings in Eclipse
- Use ResourceBundle Editor for editing
- Translate new labels to all languages using DeepL
Label Naming Conventions:
Label- Short labelsMsg- Longer messagesColumn- Column headersPDF,CSV,Preferences- Specific area prefixes (use sparingly)
Trade calendars track trading-free days (weekends, holidays, exchange-specific closures).
Source Locations:
- Manager:
name.abuchen.portfolio/src/name/abuchen/portfolio/util/TradeCalendarManager.java - Calendar Class:
name.abuchen.portfolio/src/name/abuchen/portfolio/util/TradeCalendar.java - Tests:
name.abuchen.portfolio.tests/src/name/abuchen/portfolio/util/TradeCalendarTest.java
Structure:
code- Unique identifier (e.g.,nyse)description- Display label (e.g., "New York Stock Exchange")weekend- Default weekend days
All images and icons must be Creative Commons CC0 licensed.
Sources
- Use icons from iconmonstr.com only
- Register all images in Images.java
- Use SVG format with width and height set to 16px; add a fill color if needed
- Note: For historical reasons, some icons are still available in PNG format with two resolutions (@1x and @2x). New icons should use SVG format only.
Color Guidelines
- Passive state: #393E42 (dark, cool gray)
- Active state: #F18F01 (orange)
- Error state: #D11D1D (dark red)
See Color Code Reference for exact colors.
Test regular expressions at regex101.com.
Best Practices:
- Match special characters (
äöüÄÖÜß, circumflex, etc.) with.(dot) - PDF conversion varies - Escape special regex characters:
$^{[(|)]}*+?\ .match(" ... ")- Use anchors^...$.find(" ... ")- Do NOT add anchors (added automatically)
Patterns that Work Well
| Value | Example | Works Well |
|---|---|---|
| Date | 01.01.1970 | [\\d]{2}\\.[\\d]{2}\\.[\\d]{4} |
| Date | 1.1.1970 | [\\d]{1,2}\\.[\\d]{1,2}\\.[\\d]{4} |
| Time | 12:01 | [\\d]{2}\\:[\\d]{2} |
| ISIN | IE00BKM4GZ66 | [A-Z]{2}[A-Z0-9]{9}[0-9] |
| WKN | A111X9 | [A-Z0-9]{6} |
| Valoren | 1098758 | [A-Z0-9]{5,9} |
| SEDOL | B5B74S0 | [A-Z0-9]{7} |
| CUSIP | 11135F101 | [A-Z0-9]{9} |
| TickerSymbol | AAPL, BRK.B | [A-Z0-9]{1,6}(?:\\.[A-Z]{1,4})? |
| Crypto Ticker | BTC, ETH-BTC | [A-Z0-9]{1,5}(?:[\\-\\/][A-Z0-9]{1,5})? |
| Amount | 751,68 | [\\.,\\d]+ or [\\.\\d]+,[\\d]{2} |
| Amount | 74'120.00 | [\\.'\\d]+ |
| Amount | 20 120.00 | [\\.\\d\\s]+ |
| Currency | EUR | [A-Z]{3} |
| Currency Symbol | € or $ | \\p{Sc} |





