For the complete documentation index, see llms.txt. This page is also available as Markdown.

Strategy card format

Knowledge package 0.6.1, checked 22 September 2026, build 03613bfa. If the copy you are reading does not carry that line, you have a cached or older revision: fetch this page again.

Machine contract: strategy-card.schema.json. Allowed fields: field-catalog.json. Completed educational example: examples/ma-entry-demo.json.

Identity and context

format is itb.strategy-card/0.5.0 and knowledge_version is 0.5.0: this revision adds the optional Bybit break-even pair; knowledge_policy is itb-only/1. id identifies the card, title provides a readable name and intent explains the idea. Select context_id from the catalogue for the exchange and DCA mode. Preserve that context's exact checks.schema_observation_id.

settings contains 20 form fields with the backtest. prefix and six profile. annotations. conditions contains condition instances. Each one needs exactly four keys: definition_id, role, fields and explanation. role is entry, filter or exit. The selected parameters live under fields, not under a key called parameters. Two instances of the same filter type are allowed, such as two MA filters with different periods; each counts toward the limit.

checks needs all four keys: schema_observation_id with your own observation string, and history_verified, backend_timezone_verified and deployed_engine_verified. The last three must be false: this package validates a card against its own contracts and certifies nothing about your history, the server's timezone or the deployed engine. Do not set them to true to make a card look complete.

Multi-pair test

An optional root symbols array turns the card into a multi-pair test: the same strategy is calculated on every listed pair separately. It is not a form field and does not live in settings.

  • profile.multi_enabled is true exactly when symbols is present, and false when it is absent. Any other combination is an error (multi.flag_required).

  • Each entry uses uppercase letters, digits and underscore, at most 40 characters, spelled as the exchange spells it: BTCUSDT, not btcusdt or BTC/USDT. A pair the form's loaded exchange list does not contain is not ticked and nothing reports it, so check the number of pairs in the form.

  • From 2 to 600 pairs, without repeats. One pair is not a multi-pair test: omit symbols and keep profile.multi_enabled false, or the card is rejected as multi.single_pair. 600 is the pair ceiling of the calculation service for a VIP account; a repeated pair is counted against that ceiling before duplicates are dropped, so a repeat can turn an accepted list into a refused one.

  • symbols[0] must equal backtest.symbol, the card's main pair. In a multi-pair link the form's own instrument selector does not follow the list, so do not describe this pair as the one the form will show. Every condition's Asset stays equal to that same pair, as in a single-pair card; the calculation service replaces it with the pair being calculated.

  • Calculating more than one pair is a VIP feature. Without VIP the calculation service refuses the run with 400 Too many symbols. The validator reports this as a warning, not an error: it never asserts what tier an account actually has. Tell the user before offering the link — see Preset links.

  • A multi-pair run is not a portfolio. Each pair trades the full deposit independently; more pairs means more results, not better ones.

examples/multi-symbol-demo.json is a completed educational multi-pair card, with its generator output and readable view. Older cards without symbols remain valid and unchanged.

Optional Bybit break-even pair

Every card carries profile.stop_be_enabled. False means the two backtest.stop_be_* rows are absent. True means both rows are present and valid together. Do not represent an off switch as two zeroes: trigger zero is invalid, while level zero is a valid enabled value.

The pair is available only in BYBIT contexts and cannot be combined with hedge. Both values are percentages from the current average position price; trigger is greater than zero, level is at least zero and strictly below trigger.

Values and origins

Each field record contains field_id, value, origin and note.

  • user: the user explicitly supplied the value.

  • illustrative: the number or selection belongs to an educational example.

  • derived: the value follows unambiguously from another field; explain the relationship.

  • schema_default: the default of a particular schema was retained; it is not a recommendation. Claim it only for a field whose catalogue entry actually carries a schema_default, and only when your value equals it. A field the catalogue marks with constant has no schema default at all: its value is fixed by the card profile, so record that one as derived. Claiming schema_default anywhere else is rejected as field.default_origin.

  • agent_choice: the assistant proposed an allowed parameter as a hypothesis; it is not an optimum or a calculation result.

  • unknown: the value is null, and note identifies the required question or source.

needs_input means that not every value came from the user; it permits unknown values and it does not block link generation. A card with any null value cannot be encoded, but a card whose missing answers were filled from a named source can be, and the generator reports every such value in assumed_values. complete_example is a fully populated educational example with classification=educational_example. complete_proposal is a populated hypothesis for the user's task with classification=user_draft. Both completed statuses require exactly one entry condition.

complete_proposal is the strict status: it means all eight of the symbol, start and end of the period, deposit, first-order size, both commissions and leverage came from the user and carry origin=user. Never pass an illustrative fee or an invented deposit off as a user input to reach it. A card where some of those came from elsewhere simply stays needs_input — that is not a failure, and it still produces a link. Keep null and origin=unknown only for a value with no defensible source, and ask for that one.

A draft without an entry may remain needs_input. In the reviewed ordinary BACKTEST a task without a MAIN condition is accepted and the entry is treated as always allowed, gated only by PRICE_LIMIT filters; this package still requires an explicit MAIN condition in a card so that the entry rule stays visible to the user. There is no "ready for live trading" status. In version 0.5, history_verified, backend_timezone_verified and deployed_engine_verified are always false. Store factual verification evidence in a separate report; the card does not certify it.

Human-readable view

Group fields as they appear in the panel, and write the card in the user's language: Russian when they write in Russian, Ukrainian when they write in Ukrainian, English otherwise. Names, option captions and the origin column all come from field-labels.json, which is published in full at the top of Settings and card schema under Names in Russian and Ukrainian; card_layout there also gives the block order and the layout of an indicator block. A value that has a caption is shown through that caption and translated with it: the wire value CL is the caption At bar close, and a Russian card shows the Russian name the pack gives for that caption, never CL itself. Only a value with no caption at all — a ticker, a timeframe such as 1h, a number, a moving-average type such as EMA — is printed as it stands, because that is what the panel prints. origin: "schema_default" is accepted only where the catalogue really publishes a schema_default for that field AND your value equals it in the context you chose. Condition fields carry one; context_overrides can replace it for a particular exchange and averaging mode, and the validator compares against the value for YOUR context, not the base one. The required main settings and the optional break-even pair carry no published default, so schema_default is never valid for them: when you keep one at the value the form itself shows, the origin is agent_choice and the note says so.

The card has three blocks, named the way the form names them: Settings, Money Management and Risk Management, in that order, followed by the condition sections Entry, Filters and Exit. Do not invent block names of your own, and do not translate these three yourself: the pack already carries them in all three languages. Assistants that reworded them produced headings that exist nowhere in the product, and a user has nothing on screen to match those against. Each block is a table of three columns: the name of the field, its value, and where the value came from. The titles of the blocks, the titles of the three condition sections and the three column headers are all published in field-labels.json in the user's language: copy them from there rather than translating them yourself. One row per field: never merge two fields into one row such as Deposit / First Order or SOS / DSOS / Martingale, because the user reads your table against a form where those are separate inputs.

The HTML example is in English because this documentation is; run the renderer with --lang ru or --lang uk for the other two. It helps a user copy settings but does not imitate a saved user task.

The bundled HTML renderer works with any validated catalogue card. Run python render_card.py --card your-card.json --output your-card.html. It creates English cards and the preset link from the same configuration, escapes supplied text, and loads no remote resources. Open the file locally; use the browser's Print / Save as PDF command for a printable copy. An incomplete card is rendered with its missing inputs and no link.

enum_labels contains the exact option labels; apply context_overrides to it along with enums and defaults. For example, the exit-group label for CR differs between DCA and ordinary mode. Use the signal guide for the exact mathematical meaning of each option code and the label to locate it in the interface. A code displayed literally by the interface is marked source_ui_literal.

The completed example uses EMA20, ordinary candles, bar close, timeframe 1h and LONG. All trading numbers, the selected symbol, period and fees are illustrative. The absence of filters, signal exits and additional orders is recorded explicitly. This demonstrates the format; it does not establish the idea's effectiveness.

Validation

Run python validate_card.py your-card.json --catalog field-catalog.json from the folder where you saved the files. The validator runs locally without third-party libraries or network access. It checks fields, types, context-specific values, limits and dependencies against the catalogue and condition contract. An unknown indicator or field, an unconfirmed condition constraint or an invalid condition value cannot pass into the link generator. For the main form fields, the checks also cover the source-observed bounds, their activation conditions, types, options, required fields, dates and profile relationships. Instrument-specific and runtime checks remain explicit. This is not a complete trading execution contract.

Integer signal-condition parameters must be exact JSON integers, without fractional notation, strings or rounding. This is an exact normalized input domain: some server paths convert values with int(value) and may discard the fractional part. Rejection of 20.5 by the validator therefore does not mean the server must reject it in the same way. If a fractional parameter has no explicit server step, its range alone does not create one. For example, the Bollinger width bounds do not justify assigning a mandatory step of 0.1.

A successful check means that the card is consistent with this package. It does not prove that the selected history exists, that the deployed server matches the studied code, or that a future calculation will produce a particular result. After validation, the generator converts the card to the panel's existing link format; the card JSON itself is not the product's import format.

sources contains only paths in the catalogue's allowed_sources, relative to the package root — file names such as field-catalog.json, never page addresses and never page names. If you read this documentation as web pages, translate each page you used into its package file with this map, because the two are named differently:

page
package file

AI strategy builder

README.md

Rules for AI assistants

00-agent-contract.md

From signal to trade

01-system-model.md

Compatibility and limits

02-compatibility.md

Strategy card format

03-card-format.md

Backtest workflow

04-backtest-workflow.md

Signal passports

05-signal-passports.md

Order-grid calculations

06-order-grid.md

Preset links

07-preset-links.md

Settings and card schema

field-catalog.json, main-settings-constraints.json and strategy-card.schema.json

a family reference, such as RSI fields

passports/<family>.md, plus field-catalog.json and condition-constraints.json for its numbers

So a card built from the rules page and the RSI reference names 00-agent-contract.md and passports/rsi.md, not assistant-rules or machine-rsi. External addresses, absolute paths, parent-directory traversal and unknown files are not allowed. unresolved preserves open questions even in a completed educational example. execution.backtest_executed and execution.live_trade_executed are always false; execution.results is always null. Store a real calculation report separately, referencing id and retaining a copy of the settings actually used.