How Do I Document Parameter Changes Alongside Trading System Version Control Commits?

Listen to this article

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

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.

A forex trading system's frog looking at the camera with a calm expression, and a forex trading chart in the background

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.

  1. 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.
  2. Structured messages. Use a consistent grammar that both machines and humans can parse.
  3. 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/ and params/.
  • Message linter: for param commits, 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.

Test Your Knowledge
1. According to the article, what should you do when a new feature requires both code changes and tuned parameter values?
2. Why does the article recommend using git notes to attach backtest results?
3. Under the article's adaptation of semantic versioning, which change warrants a MINOR version bump?


Take a Random Walk
Not sure what to read next? Pick a level for a random article you haven't seen yet.