Skip to content

feature: add TerminalLink IsCfdTrade order property - #9689

Merged
Martin-Molinero merged 3 commits into
QuantConnect:masterfrom
AlexCatarino:feature/emsx-cfd-flag
Aug 13, 2026
Merged

feature: add TerminalLink IsCfdTrade order property#9689
Martin-Molinero merged 3 commits into
QuantConnect:masterfrom
AlexCatarino:feature/emsx-cfd-flag

Conversation

@AlexCatarino

@AlexCatarino AlexCatarino commented Aug 12, 2026

Copy link
Copy Markdown
Member

Description

Adds IsCfdTrade to TerminalLinkOrderProperties so an algorithm can book an EMSX order as a contract for differences instead of a regular trade.

On the EMSX trading ticket this is the Booking Type drop-down, whose options are Regular, CFD and TRS:

  • Only the CFD booking has an element in the EMSX API — EMSX_CFD_FLAG, a STRING ORDER-level field taking "0"/"1".
  • There is no booking-type element, and nothing for TRS (total return swap), anywhere in Bloomberg's EMSX element reference or in its CreateOrderAndRouteEx samples. EMSX_ASSET_CLASS is likewise limited to EQTY/OPT/FUT/MULTILEG_OPT.

Hence a bool rather than a three-valued enum. It defaults to false, which is the EMSX default and the value for which the brokerage sends no flag at all, so existing orders are unchanged on the wire. The XML summary is Bloomberg's own wording for the element.

AdditionalProperties

IsCfdTrade is a pass through over a new AdditionalProperties dictionary of EMSX element name → value, following BloombergFixOrderProperties, so the typed property and the raw element are one store and cannot disagree. The brokerage PR drains the dictionary onto the request, which also lets an algorithm set any EMSX element without waiting on a Lean release.

Two deliberate differences from FixOrderProperties:

  • No setter. A plain Python dict is not convertible to Dictionary<string, string> — pythonnet raises "'dict' value cannot be converted" — and the only way to assign wholesale would be Dictionary[str, str]() from Python, which is not an idiom we want to document. Swapping the instance would also detach the typed properties from their store. The dictionary starts empty and is add/remove only; clear() resets it, and works from both C# and Python (covered by a test).
  • Clone() is overridden to deep copy it. OrderProperties.Clone() is a MemberwiseClone, which would share the dictionary — and BrokerageExtensions.RemoveLocateFromNonShortOrder clones precisely so its edits never reach the caller's instance. There is a regression test.

Possible follow-up: ExtendedDictionary<TKey, TValue> would give the dictionary proper Python semantics; it is abstract, so it would need a concrete <string, string> subclass. Out of scope here, raising it as an option.

Related Issue

QuantConnect/Lean.Brokerages.TerminalLink#135, companion PR QuantConnect/Lean.Brokerages.TerminalLink#136.

Requires Documentation Change

Yes — IsCfdTrade and AdditionalProperties should be listed with the other TerminalLink order properties.

Tests

TerminalLinkOrderPropertiesTests, 10 passing, covering the default, the Clone isolation, and the Python surface: is_cfd_trade, writing an entry through additional_properties, clear(), and that the dictionary cannot be replaced.

🤖 Generated with Claude Code

@AlexCatarino
AlexCatarino force-pushed the feature/emsx-cfd-flag branch from bffeabc to 31d4e42 Compare August 12, 2026 13:15
Adds IsCfdTrade to TerminalLinkOrderProperties so an algorithm can book an
EMSX order as a contract for differences instead of a regular trade. The
companion Lean.Brokerages.TerminalLink PR maps it to EMSX_CFD_FLAG on the
CreateOrderAndRouteEx request.

On the EMSX trading ticket this is the "Booking Type" drop-down, whose options
are Regular, CFD and TRS. Only the CFD booking has an element in the EMSX API
(EMSX_CFD_FLAG, "0"/"1"); there is no booking-type element and nothing for TRS
in Bloomberg's element reference, so the property is a bool rather than a
three-valued enum. Defaults to false, which is the EMSX default and the value
for which the brokerage sends no flag at all, leaving existing orders on the
wire byte-identical.

The property is a pass through over a new AdditionalProperties dictionary of
EMSX element name to value, following BloombergFixOrderProperties, so the
typed property and the raw element cannot disagree. The dictionary starts
empty and is add/remove only - it has no setter, since a plain Python dict is
not convertible to Dictionary<string, string> and swapping the instance would
detach the typed properties from their store. Callers reset it with clear().
Clone deep copies it so the locate cleanup in BrokerageExtensions, which edits
a copy, cannot reach the algorithm's own instance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@AlexCatarino
AlexCatarino force-pushed the feature/emsx-cfd-flag branch from 31d4e42 to 471889f Compare August 12, 2026 21:24
AlexCatarino and others added 2 commits August 12, 2026 23:24
Addresses the review suggestion to make IsCfdTrade a pass through like the
Bloomberg FIX properties do, over a new AdditionalProperties dictionary keyed
by EMSX element name, so the typed property and the raw element are one store
and cannot disagree.

Uses BaseExtendedDictionary rather than Dictionary so the dictionary behaves
like a Python dict. pythonnet has no conversion from a plain dict, so
    properties.additional_properties = { "EMSX_CFD_FLAG": "1" }
throws at runtime; update() takes a PyObject and gives callers the bulk load
they reach for, alongside get(), pop(), setdefault() and clear(). Both the
working idiom and the failing one are covered by tests.

Clone deep copies the dictionary. OrderProperties.Clone is a MemberwiseClone,
which would share it, and BrokerageExtensions.RemoveLocateFromNonShortOrder
clones precisely so its edits never reach the instance the algorithm holds on
to; there is a regression test.

Setting IsCfdTrade to false removes the entry rather than writing "0", so the
dictionary only ever carries what is explicitly on and an untouched order goes
out exactly as before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Gives the FIX custom tags the same Python dict semantics TerminalLink just
got. update() takes a PyObject, so a Python algorithm can bulk load its tags
from a plain dict, which plain assignment cannot do; get(), pop(), setdefault()
and clear() come along with it. Clone copies into a new instance as before.

BaseExtendedDictionary implements IDictionary but not IReadOnlyDictionary, so
the GetValueOrDefault extension no longer resolves. The Bloomberg locate
passthroughs read through a GetTag helper instead, mirroring SetTag, and the
transaction handler test uses a local helper of its own. Note that the null
conditional form cannot be used with out var - the call is conditional, so the
variable is not definitely assigned.

The subclasses BloombergFixOrderProperties, TradingTechnologiesOrderProperties
and the obsolete FixOrderProperites inherit the property and Clone unchanged;
InteractiveBrokersFixOrderProperties extends OrderProperties despite the name
and is unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Martin-Molinero
Martin-Molinero merged commit e80c238 into QuantConnect:master Aug 13, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants