9.2 KiB
Contributing to ta4j
ta4j has been around for years and serves a large, diverse user base. Contributions are very welcome, but long‑term maintainability takes precedence over quick wins. Please keep the sections below in mind before filing an issue or opening a PR.
Principles
- Public APIs are contracts. Moving classes between packages, renaming methods, or otherwise breaking binary/source compatibility forces every downstream user into a refactor. We only accept breaking changes when the value dramatically outweighs the disruption, and even then they must ship with deprecation shims and migration notes.
- Opinionated implementations belong outside the core. ta4j aims to be widely applicable. Highly subjective “feature bundles” (e.g., metric dashboards, bespoke reporting formats, hard-coded broker behaviors) are better published as separate modules or example projects. Keep contributions focused on reusable primitives.
- Additive code beats churn. New indicators, rules, serialization helpers, and documentation are great. Mechanical refactors (“just moved files around”) or stylistic changes with no behavioral impact rarely get merged.
- Tests tell the story. Every change—bug fix or feature—needs focused tests demonstrating the behavior and guarding against regressions.
-
Run this before opening or updating a PR:
mvn -B verifyThis matches the main CI path and keeps SpotBugs and JaCoCo advisory in the full contributor flow. -
Use focused local quality loops when iterating:
mvn -pl ta4j-core -am spotbugs:checkandmvn -pl ta4j-core -am test jacoco:report jacoco:checkThese are intentionally strict for the module you are changing, so you can tighten one tool at a time before rerunning the fullmvn -B verify. -
Fix formatting and license headers when needed:
mvn -B license:format formatter:formatFirst-time contributors almost always hit this; run the formatter command locally before your finalmvn -B verify.
Contribution checklist
- Use Java 25+ and Maven 3.9+. The build enforces these versions during Maven validation.
- Start with an issue for anything non-trivial. Use it to confirm fit with the Roadmap and to align on scope. Search existing issues before opening a new one.
- Fork & branch from
master.git clone https://github.com/<you>/ta4j.git cd ta4j git checkout -b feature/your-topic - Implement + test. Run the full build before pushing:
CI will fail if your changes are not formatted or lack the project license header. First-time contributors almost always hit this; run the command locally first. Update
mvn -B clean license:format formatter:format test installCHANGELOG.mdwhen you add, fix, or change behavior. - Open the PR against
ta4j/master. Draft PRs are encouraged for early feedback. Prefer well-formed commit messages.
Optional: Enable workflow linting hook - If you're modifying GitHub Actions workflows, enable the pre-push hook to catch syntax errors early:
git config core.hooksPath .githooks
Then install actionlint (e.g., brew install actionlint). The hook will automatically lint any modified files under .github/workflows/ before pushing.
Contribution priorities
- Items on the Roadmap.
- Additive indicators/criteria/rules that do not change existing behavior.
- Test coverage or documentation improvements.
- Bug fixes (smaller, localized fixes are easier to land; large refactors should be discussed first).
- API changes: only with clear justification, deprecation shims, and migration docs.
Coding expectations
- Favor clarity over cleverness; write the code you’d want to debug a year from now.
- Keep PRs scoped. If you find unrelated issues, file them or send separate PRs.
- Every new public class/method needs Javadoc with
@since <version>. - Use primitives for indicator parameters (e.g.,
int timeFrame). Convert toNuminside usingseries.numFactory(). - Do not cache
Numinstances globally—always obtain them from the relevant factory.
Indicator contributions
Open an issue to discuss the new indicator first. Every indicator must ship with matching tests:
src/main/java/org/ta4j/core/indicators/.../NewIndicator.javasrc/test/java/org/ta4j/core/indicators/.../NewIndicatorTest.java
Tagged test workflows
Regular PR and push CI skips test tags configured by ta4j.excludedTestTags.
Run tagged suites manually from GitHub Actions, or locally with:
xvfb-run mvn -B test -Dgroups=integration -Dta4j.excludedTestTags=analysis-demo,elliott-macro-cycle-replayxvfb-run mvn -B test -Dgroups=benchmark -Dta4j.excludedTestTags= -Dta4j.runBenchmarks=truexvfb-run mvn -B test -Dgroups=analysis-demo -Dta4j.excludedTestTags=elliott-macro-cycle-replay -Dta4j.analysisDemoInstrument=coinbase:BTC-USD -Dta4j.analysisDemoOutputDir=target/analysis-demos/elliott-wavexvfb-run mvn -B test -Dgroups=elliott-macro-cycle-replay -Dta4j.excludedTestTags= -Dtest=ElliottWaveMacroCycleDetectorTest
These examples match the Linux GitHub Actions runners. On macOS, use XQuartz or
run the Maven command without xvfb-run when your local display can satisfy
UI-dependent tests. On Windows, use WSL2, a CI runner, or an equivalent X server.
The dedicated workflows are:
Run Integration Tagged Tests(.github/workflows/test-tag-integration.yml)Run Benchmark Tagged Tests(.github/workflows/test-tag-benchmark.yml)Run Analysis Demo Tagged Tests(.github/workflows/test-tag-analysis-demo.yml)Run Elliott Macro Cycle Replay Tagged Tests(.github/workflows/test-tag-elliott-macro-cycle-replay.yml)
Scheduled runs are opt-in per tag. Set TA4J_TAGGED_TEST_<TAG>_SCHEDULE_ENABLED=true
and TA4J_TAGGED_TEST_<TAG>_SCHEDULE_SLOT=daily, weekly, or monthly.
Unset variables leave scheduled runs disabled, while manual workflow dispatches run
regardless of the schedule variables. The elliott-macro-cycle-replay workflow
is manual-only and requires a self-hosted runner labeled ta4j-macro-cycle-replay.
The analysis-demo tag is for examples that produce analysis reports and must
be the only JUnit tag on each tagged test or class.
Its workflow defaults to coinbase:BTC-USD, accepts provider-qualified manual
inputs such as coinbase:ETH-USD or coinbase:ETH/USD, and uploads generated
JSON, charts, and cached provider responses from target/analysis-demos/**.
Version 1 supports Coinbase instruments only. For scheduled analysis-demo runs,
weekly is the intended slot; use
TA4J_TAGGED_TEST_ANALYSIS_DEMO_SCHEDULE_ENABLED=true with
TA4J_TAGGED_TEST_ANALYSIS_DEMO_SCHEDULE_SLOT=weekly, and set
TA4J_ANALYSIS_DEMO_INSTRUMENT to override the scheduled instrument.
API lifecycle and @since policy
- Add
@since <version>to every newly introduced class and API member, using the introducing release version without-SNAPSHOT(for example0.22.4, not0.22.4-SNAPSHOT). - This gives us a reliable introduction point for deprecation tracking and lifecycle automation.
- New API is considered volatile for the next 5 minor releases after it is introduced.
- Example: API added in
0.22.4may still change incompatibly, or be removed, through0.27.4(inclusive). - Treat this window as experimental/beta and avoid production-critical dependency unless you explicitly accept migration risk.
Branching model
Enhancements, new features and fixes should be pushed to a fork of the master branch. Once completed they will be merged with the master branch during a pull request. GitHub actions are configured to run the tests, validate the licence header and source code format. After the PR has been merged a new SNAPSHOT will be deployed.
This development process is similar to github flow.
- Only the content of the master branch is going to become a release.
- There is no release branch nor a mandatory develop branch
Release Process
For maintainers, the release process is fully automated using GitHub Actions workflows. The process includes:
- Automated release scheduling: AI-powered scheduler analyzes changes and determines version bumps (patch/minor/major)
- Two-phase release workflow:
prepare-release.ymlprepares release commits and PRs,publish-release.ymlhandles tagging and deployment - Release health monitoring: Automated checks for tag reachability, version drift, and stale release PRs
- GitHub Release automation: Automatic creation of GitHub Releases with artifacts and release notes
For detailed information about the release process, see RELEASE_PROCESS.md in the main repository.
Quick tips
- Use
series.getBeginIndex()instead of0when iterating aBarSeries. - Remember the difference between
DecimalNum.min(...)andDecimalNum.minus(...). - When in doubt, ask. It’s easier (and faster) to course-correct early than to rework a large PR later.