Iteration Tools: AutoBacktest & Gravitas
You're iterating on indicator logic. Tweak, save, run. Tweak, save, run. Each manual cycle costs seconds of context switching — just enough to break flow.
Or you've added a parameter and want to know how sensitive the strategy is to it. Is ROI changing meaningfully, or are you just turning a knob that doesn't matter?
Two tools handle these two modes of iteration.
AutoBacktest
The problem
You change one line in your bot — adjust an EMA period, flip a comparison sign — and then:
- Switch to the terminal.
- Press up-arrow to find the last
python bot.pycommand. - Wait for the import, autorange warmup, and backtest to run.
- Look at the output. Decide on the next tweak.
- Repeat.
That switch-and-wait cycle adds up. Over an hour of tuning, it steals minutes. Over a week, it can cost more time than the optimization runs.
The solution
auto_backtest(bot, data) watches your bot script for file changes and re-runs the backtest automatically.
import qtradex as qx
from my_bot import EmaCrossover
bot = EmaCrossover()
data = qx.data("BTC", "1d", 365)
qx.auto_backtest(bot, data)
Save your bot file. The terminal window that's running auto_backtest picks up the change and runs the backtest again. The plot updates. You see if the change helped.
What happens on reload
When it detects a save, auto_backtest reloads the bot module, rebuilds the class, and re-runs the full cycle.
If the new version of your bot doesn't redefine self.tune, the previous tune values carry over. You can tweak indicator logic without losing your parameters. If you add, remove, or rename a tune key, the original tune no longer fits — the bot falls back to defaults.
When to use it
AutoBacktest is for logic iteration — changing indicators, strategy rules, signal logic. It's not for tuning parameters. If you're trying to find the right fast_ema value, use the optimizer. AutoBacktest just saves the manual re-run step.
Stop it with Ctrl-C.
Gravitas
The concept
Gravitas is a single scalar you can reference in your strategy. It's not a tune parameter. The framework doesn't read or act on it. It's just a dial you provide.
class MyBot(qx.BaseBot):
def __init__(self):
self.gravitas = 1.0
In your strategy or indicators, reference it anywhere:
def indicators(self, data, states):
threshold = self.gravitas * 1.5
...
The dial does nothing unless your code uses it. That's the point — you decide what it means.
Running the scan
From the dispatch optimizer menu, select "Gravitas". The framework prompts for three values:
- Min gravitas — the low end of the dial
- Max gravitas — the high end
- Number of tests — how many points between min and max to evaluate
It runs a backtest at each gravitas value and plots ROI vs gravitas.
Reading the plot
The shape of that plot tells you something about your strategy.
A flat line means your bot doesn't reference self.gravitas anywhere, or the logic referencing it has no effect on results. Every data point is the same backtest.
A slope means the dial affects your strategy. The steeper the slope, the more sensitive.
A curve with a peak tells you the optimal operating range. If ROI rises to a plateau then drops, you want the flattest part of that peak — the region where small changes in the dial don't swing results much. That's where your strategy is robust.
When to use it
Gravitas is for sensitivity analysis. Before you commit to a threshold or scaling factor, run the scan to see what range produces stable results. If a 10% change in gravitas swings ROI by 50%, you're in a fragile region — the strategy depends too heavily on the exact value.
Which to use when
| Situation | Tool |
|---|---|
| Changing indicator logic or strategy rules | AutoBacktest |
| Sensitivity analysis on a threshold or scaling factor | Gravitas |
AutoBacktest is for speed. Gravitas is for understanding.
Next steps
- AutoBacktest API reference
- Gravitas API reference
- Advanced bot building — custom warmup, plotting, lifecycle hooks