Writing these docs¶
This site is built with MkDocs and the Material theme, configured like the tradingmachine and UBI sites. Its hand-written pages live in docs/, and its API reference is generated from the docstrings in src/trading_agent every time the site is built.
Previewing and building¶
The toolchain is the docs extra in pyproject.toml:
.venv/bin/pip install -e ".[docs]"
.venv/bin/mkdocs serve -a 127.0.0.1:8130 # live preview, rebuilt on every save
.venv/bin/mkdocs build --strict # the full build, failing on any warning
The preview uses port 8130 so that it never clashes with the gateway on 8120. Always finish with mkdocs build --strict: a broken link, a page missing from the nav or a missing diagram stops the build.
Publishing¶
.github/workflows/docs.yml builds the site with mkdocs build --strict on every push and pull request, and publishes it to GitHub Pages from main, exactly as for tradingmachine. It installs only the packages in the docs extra, read from pyproject.toml. The site is published at https://pramodathani.github.io/trading-agent/ from the public repository pramodathani/trading-agent.
Diagrams¶
The animated diagrams are SVG files in docs/assets/diagrams/, included into pages with the snippets syntax inside a figure with the class diagram:
<figure class="diagram">
<svg class="ta-svg" viewBox="0 0 1000 580" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="desks-title desks-description">
<title id="desks-title">Four desks and one risk engine</title>
<desc id="desks-description">Each desk sends its orders through the same risk engine, which gives each stock to one desk at a time and enforces every limit.</desc>
<path class="wire" d="M240,95 L285,95 L285,230 L330,230"/>
<path class="wire" d="M240,205 L300,205 L300,250 L330,250"/>
<path class="wire" d="M240,315 L300,315 L300,290 L330,290"/>
<path class="wire" d="M240,425 L285,425 L285,310 L330,310"/>
<path class="wire" d="M560,270 L650,270"/>
<path class="wire" d="M445,370 L445,410"/>
<path class="wire" d="M650,420 L600,420 L600,340 L560,340"/>
<text class="small" x="572" y="262">approved</text>
<path id="desks-from-main" class="hidden-path" d="M135,95 L240,95 L285,95 L285,230 L330,230 L445,250"/>
<path id="desks-from-intraday" class="hidden-path" d="M135,205 L240,205 L300,205 L300,250 L330,250 L445,260 L560,270 L650,270 L725,270"/>
<path id="desks-from-scalper" class="hidden-path" d="M135,315 L240,315 L300,315 L300,290 L330,290 L445,280"/>
<path id="desks-refuse-scalper" class="hidden-path" d="M445,280 L330,290 L300,290 L300,315 L240,315 L135,315"/>
<path id="desks-from-positional" class="hidden-path" d="M135,425 L240,425 L285,425 L285,310 L330,310 L445,300"/>
<circle class="dot" r="6"><animateMotion dur="12s" repeatCount="indefinite" calcMode="linear" keyTimes="0;0.15;1" keyPoints="0;1;1"><mpath href="#desks-from-main"/></animateMotion></circle>
<circle class="dot" r="6"><animateMotion dur="12s" repeatCount="indefinite" calcMode="linear" keyTimes="0;0.2;0.45;1" keyPoints="0;0;1;1"><mpath href="#desks-from-intraday"/></animateMotion></circle>
<circle class="dot" r="6"><animateMotion dur="12s" repeatCount="indefinite" calcMode="linear" keyTimes="0;0.5;0.62;1" keyPoints="0;0;1;1"><mpath href="#desks-from-scalper"/></animateMotion></circle>
<circle class="dot alt" r="6"><animateMotion dur="12s" repeatCount="indefinite" calcMode="linear" keyTimes="0;0.64;0.76;1" keyPoints="0;0;1;1"><mpath href="#desks-refuse-scalper"/></animateMotion></circle>
<circle class="dot" r="6"><animateMotion dur="12s" repeatCount="indefinite" calcMode="linear" keyTimes="0;0.8;0.94;1" keyPoints="0;0;1;1"><mpath href="#desks-from-positional"/></animateMotion></circle>
<rect class="box" x="30" y="50" width="210" height="90" rx="8"/>
<text class="title" x="135" y="76" text-anchor="middle">main</text>
<text class="small" x="135" y="97" text-anchor="middle">Claude first; any product;</text>
<text class="small" x="135" y="112" text-anchor="middle">the conversation so far</text>
<rect class="box" x="30" y="160" width="210" height="90" rx="8"/>
<text class="title" x="135" y="186" text-anchor="middle">intraday</text>
<text class="small" x="135" y="207" text-anchor="middle">Claude first; mis; flat by 15:15</text>
<text class="small" x="135" y="222" text-anchor="middle">heartbeat 10 min; 09:05 15:05 15:35</text>
<rect class="box" x="30" y="270" width="210" height="90" rx="8"/>
<text class="title" x="135" y="296" text-anchor="middle">scalper</text>
<text class="small" x="135" y="317" text-anchor="middle">Qwen first; mis; bracket orders</text>
<text class="small" x="135" y="332" text-anchor="middle">heartbeat 2 min; 09:10 15:05</text>
<rect class="box" x="30" y="380" width="210" height="90" rx="8"/>
<text class="title" x="135" y="406" text-anchor="middle">positional</text>
<text class="small" x="135" y="427" text-anchor="middle">Claude first; cnc, nrml</text>
<text class="small" x="135" y="442" text-anchor="middle">08:45 12:30 15:40; no heartbeat</text>
<rect class="accent-box" x="330" y="170" width="230" height="200" rx="8"/>
<text class="title" x="445" y="196" text-anchor="middle">One risk engine</text>
<text class="small" x="445" y="220" text-anchor="middle">one desk per stock at a time</text>
<text class="small" x="445" y="242" text-anchor="middle">each desk's products only</text>
<text class="small" x="445" y="264" text-anchor="middle">a desk at its own loss limit</text>
<text class="small" x="445" y="279" text-anchor="middle">stops alone</text>
<text class="small" x="445" y="301" text-anchor="middle">the account at 15% a day</text>
<text class="small" x="445" y="316" text-anchor="middle">stops everyone</text>
<text class="small" x="445" y="345" text-anchor="middle">getting out is never blocked</text>
<rect class="store" x="330" y="410" width="230" height="80" rx="8"/>
<text class="title" x="445" y="436" text-anchor="middle">Risk ledger</text>
<text class="small" x="445" y="456" text-anchor="middle">who owns each stock now, and</text>
<text class="small" x="445" y="471" text-anchor="middle">who owned it today, for desk P&L</text>
<rect class="store" x="650" y="220" width="150" height="100" rx="8"/>
<text class="title" x="725" y="252" text-anchor="middle">UBI</text>
<text class="small" x="725" y="274" text-anchor="middle">tagged with the desk;</text>
<text class="small" x="725" y="289" text-anchor="middle">dry runs until you go live</text>
<rect class="box" x="650" y="380" width="150" height="80" rx="8"/>
<text class="title" x="725" y="406" text-anchor="middle">Market watcher</text>
<text class="small" x="725" y="426" text-anchor="middle">account and each</text>
<text class="small" x="725" y="441" text-anchor="middle">desk's P&L</text>
<rect class="box" x="830" y="50" width="150" height="130" rx="8"/>
<text class="title" x="905" y="76" text-anchor="middle">Every message</text>
<text class="small" x="905" y="98" text-anchor="middle">starts with the time,</text>
<text class="small" x="905" y="113" text-anchor="middle">what woke the desk</text>
<text class="small" x="905" y="128" text-anchor="middle">and the market line:</text>
<text class="small" x="905" y="150" text-anchor="middle">next trading day Mon,</text>
<text class="small" x="905" y="165" text-anchor="middle">holiday Tue (Dussehra)</text>
<circle cx="230" cy="520" r="6" class="dot"/>
<text x="244" y="520" dominant-baseline="middle">an order from a desk</text>
<circle cx="520" cy="520" r="6" class="dot alt"/>
<text x="534" y="520" dominant-baseline="middle">refused: the stock belongs to intraday</text>
</svg>
<figcaption>What the dots mean.</figcaption>
</figure>
Each SVG has the class ta-svg and no colours of its own: boxes, wires, text and dots take their colours from docs/stylesheets/extra.css, which defines them for the light and the dark theme. Every id in a diagram starts with the diagram's name, so two diagrams on one page never share an animation path. The diagrams began as the milestone diagrams of the build and were converted to this form.
The API reference¶
scripts/gen_ref_pages.py writes one page per module of src/trading_agent while the site builds, nothing on disk: the module as the title, then each public class in source order with its methods. It also writes the section's landing page, a tree of every class by inheritance, as tradingmachine's reference does. Its navigation goes to reference/SUMMARY.md for the literate-nav plugin, which is why mkdocs.yml says only - API reference: reference/.
The landing page must stay the first entry of that navigation. Without it, the section's only entry is the trading_agent package, which has its own index page, and Material then treats that page as the section's own index and hides it, together with the whole tree of modules under it, leaving the left menu empty.