Most traders who use Git for their Expert Advisors version the code carefully and leave the parameters untracked. That gap matters. A strategy’s behaviour depends on its logic and its inputs together, and changing a stop-loss multiplier from 1.5 to 2.0 can alter a system more than a hundred lines of refactored code. So: how do I document parameter changes alongside trading system version control commits? The short answer is to treat parameters as first-class, versioned artefacts. Each change gets its own commit, a structured message, and a link to the evidence behind it.
Without that discipline, every forensic question is guesswork. Which settings were live during last March’s drawdown? Why did someone widen the trailing stop? Did that backtest use the old spread model or the new one? Nobody can answer reliably.
This article sets out a working method for answering those questions: how to separate configuration from logic, how to structure commits, how to tie backtests to specific revisions and how to enforce the process automatically. It assumes you already use Git and want rigour, not an introduction.
Table of Contents
- Separate Parameters from Logic First
- How Do I Document Parameter Changes Alongside Trading System Version Control Commits?
- Linking Backtests and Evidence to Each Commit
- Tags, Versioning and Deployment Records
- Enforcing the Process with Automation
- FAQ
- Conclusion
Separate Parameters from Logic First
You cannot document parameter changes cleanly if parameters are hard-coded into source files. A diff that mixes a logic change with a threshold change is ambiguous, because a reviewer cannot isolate the cause of any later shift in performance. The first step is structural: keep parameters out of the code.
Choose a configuration format
Your options depend on the platform:
- MT4/MT5 .set files: native to MetaTrader and easy to load in the Strategy Tester. Their weakness is encoding: MT5 often saves them as UTF-16 LE, which Git treats as binary.
- YAML or JSON: readable, easy to diff and parseable by Python tooling. This is the best choice for custom or multi-platform systems.
- TOML: strict typing and comment support, so it works well when parameter types matter.
For MetaTrader specifically, fix the encoding problem with a .gitattributes entry such as *.set working-tree-encoding=UTF-16LE eol=CRLF. Git then stores the file as UTF-8 internally and produces readable line-by-line diffs. Without this, every .set change appears as “Binary files differ”, which tells you nothing.
Make diffs deterministic
Sort keys alphabetically and keep one parameter per line. Strip optimisation ranges you are not using. A diff should show only the values that actually changed. Noise from reordered keys makes reviews slower and invites mistakes.

How Do I Document Parameter Changes Alongside Trading System Version Control Commits?
Once parameters live in their own files, the commit becomes your primary documentation unit. Three rules apply, with no exceptions.
- One concern per commit. A parameter change never shares a commit with a code change. If a new feature needs new parameters, commit the code with neutral defaults first, then commit the tuned values separately.
- Structured messages. Use a consistent grammar that both machines and humans can parse.
- Rationale in the body. The diff shows what changed. Only the message records why.
A message format that works
The Conventional Commits specification fits trading systems well. Add a dedicated param type and scope it by instrument or strategy:
param(eurusd-h1): widen ATR stop multiplier 1.5 -> 2.0
Rationale: stop-outs on London open spikes accounted for
the majority of losing trades in Q2 walk-forward window.
Evidence: run-2026-09-28-a (see notes)
Data: Dukascopy tick, 2019-01-01 to 2026-06-30
Spread model: variable, recorded
Expected effect: lower win-rate sensitivity to open volatility,
modestly higher average loss
Risk: per-trade risk unchanged (position sizing adjusts)
Reverts-if: OOS profit factor < 1.15 after 60 trades
Note the old and new values in the subject line. When you scan git log --oneline six months later, you see the change without opening a diff.
Mandatory body fields
- Rationale: the hypothesis, stated so that it can be falsified.
- Evidence: a backtest or forward-test identifier.
- Data: source and date range.
- Expected effect: what should change and in which direction.
- Reverts-if: a pre-committed kill condition.
The Reverts-if field is the most valuable and the most often skipped. Writing it before you see live results protects you against post-hoc rationalisation. If the condition triggers, you revert, and you don't argue with your past self.
Linking Backtests and Evidence to Each Commit
A parameter commit without evidence is just an opinion. Backtest reproducibility depends on binding each result to the exact code revision, parameter set and data snapshot that produced it.
Record a run manifest
Every test run should emit a small manifest file:
- Commit SHA of the code and parameter files
- SHA-256 hash of the price data file or data export
- Tester settings: model (every tick, real ticks), spread, commission, swap
- Account currency, leverage and initial deposit
- Summary metrics: net profit, profit factor, max drawdown, trade count, Sharpe
Store manifests in a runs/ directory, or in an external store if the reports are large. Never commit multi-megabyte HTML reports into the main history. Use Git LFS or object storage and commit only the manifest and a pointer.
Attach results after the fact with git notes
Results often arrive after the commit exists. Rewriting history to add them is destructive. Use git notes instead, which attach metadata to a commit without changing its SHA:
git notes --ref=backtest add -m "run-2026-09-28-a PF=1.31 DD=8.4% n=412" a1b2c3d
Notes live in a separate ref, so push them explicitly (git push origin refs/notes/backtest). Then git log --notes=backtest shows each parameter change next to its measured outcome. This is the closest thing to a lab notebook that Git provides.
Log rejected experiments too
This part is unglamorous but critical. If you tested twenty stop multipliers and committed one, the other nineteen still count. They define how many trials you ran, and that number matters for judging overfitting. Keep an experiments.csv or a dedicated branch recording every trial, including failures. A result looks less impressive once you know it was the best of forty attempts, and you should know that.
Tags, Versioning and Deployment Records
Commits record intent. Tags record what actually traded. Keep those two concepts distinct.
Apply semantic versioning to strategies
Adapt Semantic Versioning to trading systems:
- MAJOR: logic change that alters entry or exit rules. This requires a full re-validation.
- MINOR: parameter change that affects trade selection or risk, for example stop distance or filter thresholds.
- PATCH: cosmetic or operational change with no effect on trades, for example logging or magic number formatting.
A version string such as eurusd-trend v3.4.1 then tells you straight away whether two live periods are comparable. Performance under v3.4 and v3.5 should not be pooled without qualification.
Tag every deployment
Create an annotated tag at the moment a configuration goes live:
git tag -a live/eurusd-trend/2026-10-01 -m "Deployed v3.5.0 to IC Markets live #XXXX, risk 0.5%"
Include the broker, account identifier (masked), risk setting and deployment time. When live results diverge from the backtest, you check out the tag and test the exact configuration that traded. Without tags, you are reconstructing history from memory, and memory is unreliable.
Maintain a human-readable changelog
Generate CHANGELOG.md from your structured commits with a tool such as git-cliff or conventional-changelog. Group entries by version and type. That gives a reviewer, a business partner or an allocator a readable summary without them needing to know Git.
Enforcing the Process with Automation
Discipline fails under pressure. The process has to hold up during a drawdown, when you most want to tweak something quickly. Automate enforcement so that skipping steps is harder than following them.
Pre-commit and commit-msg hooks
- Mixed-change guard: reject any commit that touches both
src/andparams/. - Message linter: for
paramcommits, require the Rationale, Evidence and Reverts-if fields. commitlint handles this with a custom rule. - Schema validation: check parameter files against a JSON Schema with type and range limits. A risk-per-trade value of 5.0 instead of 0.5 should never reach a commit.
- Normalisation: automatically sort keys and fix encoding before staging.
Continuous integration checks
If you run headless backtests, through MT5's command-line tester or a Python engine, trigger a smoke test on every parameter commit. It doesn't need to be exhaustive. A short, fixed-window test that confirms the system still trades and that drawdown stays within bounds catches gross errors. The CI job can then write the result back as a git note automatically.
Protect the live branch
Use a live branch that only accepts merges through pull requests. Even if you trade alone, opening a PR forces a pause and a written review. Branch protection on GitHub or GitLab can require passing CI before merge. This is the cheapest risk control you will ever implement.
FAQ
Should parameter files live in the same repository as EA code?
Yes, for most setups. A single repository guarantees that a commit SHA identifies both logic and configuration together. Split them only if several independent strategies share one engine. Even then, pin the engine version inside each parameter repository.
How do I handle MT5 .set files that Git shows as binary?
Add *.set working-tree-encoding=UTF-16LE to .gitattributes. Git converts the files to UTF-8 internally and produces normal text diffs. Re-add existing files after the change so the conversion applies.
Is git notes safe for storing backtest results?
It is safe, but easy to lose. Notes are not pushed or fetched by default. Configure your remotes to include refs/notes/* explicitly, or mirror the data in committed manifest files as a backup.
How granular should parameter commits be?
One hypothesis per commit. If you change two parameters to test a single idea, one commit is fine. If they serve two separate ideas, split them. Otherwise you cannot attribute any change in performance.
Do I need semantic versioning as a solo trader?
Yes. Your future self is a separate reviewer with poor memory. Version numbers stop you from pooling performance statistics across configurations that aren't comparable.
Conclusion
So, how do I document parameter changes alongside trading system version control commits? Separate configuration from logic. Commit parameters on their own with structured messages that state the rationale, evidence and a pre-committed revert condition. Bind every backtest to an exact SHA and data hash, and attach results with git notes. Tag each live deployment, version strategies semantically and enforce the whole process with hooks and CI.
None of this is complicated. It is simply rigorous, and rigour is what separates a trading system from a collection of guesses. Start today: move your parameters into a tracked file, write your first param commit with a Reverts-if clause, and tag what is currently live.