Repository navigation
Add Trading212 brokerage model - #9982
nimakalantar wants to merge 1 commit into
Conversation
|
Planned follow-up work (not part of this PR):
It will be opened as a separate PR once this model lands. |
|
One thing to check in the description: "DefaultBrokerageModel already returns T+1 and leverage 1 for a cash equity account" is true only when The new constructor is Two options: default the parameter to |
Trading 212's equity accounts (Invest, Stocks ISA, SIPP) are cash accounts with no margin or short selling, so the default account type should be Cash. Override GetLeverage to return 1 so a Margin account type (including the SetBrokerageModel(BrokerageName.Trading212) factory path, which defaults to Margin) can no longer pick up DefaultBrokerageModel's 2x equity leverage. Addresses review feedback on QuantConnect#9982.
|
Thanks for taking a look! I think I've fixed this.
Context:
|
|
Read the new head (10c83c2). Cash default plus One path I could not find covered, from reading Did you see the Margin short-from-flat case behave differently when you ran the suite? |
OrderCrossesZero only flags a position passing through zero, so a sell from a flat or already-short position slipped past ValidateCrossZeroOrder and was accepted by CanSubmitOrder. On a cash account the buying power model later rejected it, but the SetBrokerageModel(BrokerageName.Trading212) path passes Margin, where leverage 1 still allowed the fresh short. Reject any order with a negative quantity when holdings are zero or short, and add coverage for sell-from-flat, add-to-short and buy-from-flat under both account types. Addresses review feedback on QuantConnect#9982.
|
I ran it, it is as you suspected: So Fixed in 44d4014 |
|
Read 44d4014: the One limit of the check, which may be fine to leave: it reads |
CanSubmitOrder evaluates the long-only rule against the security's holdings at submission time, so it does not account for other pending unfilled orders. Note this in the class remarks, matching the behavior of the other LEAN brokerage models. Addresses review feedback on QuantConnect#9982.
|
Agreed on both points — the check is holdings-based, and open-order tracking is out of scope since the other LEAN brokerage models behave the same way. I added the clarification to the class remarks in a147730: /// The long-only rule is evaluated against the security's holdings when the order is submitted,
/// matching the other LEAN brokerage models, so it does not account for other pending unfilled orders. |
BrokerageModel.Create forwards the account type it is given, so SetBrokerageModel(BrokerageName.Trading212) without an explicit Cash account models a margin account even though the constructor defaults to Cash. Spell that out on the constructor and on GetLeverage, which returns 1 either way. Addresses review feedback on QuantConnect#9982.
7bd7b10 to
1ea58e8
Compare
|
Read a147730: the two remarks lines say exactly what the check does, and "does not account for other pending unfilled orders" is the part a user would otherwise find out in a live account. Docs only, nothing else changed in the diff. One thing I could not tell from the PR: when a second full-position sell does slip through in the same time slice, does the Trading 212 API reject it on its side (insufficient quantity), or would it be accepted and net short? If it rejects, the remark could say the broker is the backstop for that case. |
|
I checked the Trading 212 API docs and it rejects on their side:
So the broker is the backstop for the same-slice gap, and the remark could say so. Happy to add that sentence if you think it adds value. |
|
Yes, I think one sentence is worth adding. The remark already says the check ignores pending orders, and a reader's next question is what stops a second full sell in a live account. Something like "the broker rejects an over-sell ( Is the order placement for this model handled in a separate brokerage-implementation PR? If so, it may be worth checking there that the rejection reaches the algorithm as an |
The long-only check ignores pending unfilled orders, so a same-slice duplicate sell can slip past it. Trading 212 rejects the second order with SellingEquityNotOwned, so the broker is the backstop and the duplicate is not accepted as a short in a live account. Addresses review feedback on QuantConnect#9982.
|
Added to the model remarks: /// The broker rejects an over-sell with <c>SellingEquityNotOwned</c>, so a same-slice duplicate is
/// not accepted as a short in a live account.On the implementation question: there isn't one. This PR is the order-rule model only — there is no |
f9dc9e5 to
21d6480
Compare
|
That wording reads right, and scoping this PR to the order-rule model with the brokerage as a separate PR is the cleaner split. One thing to decide before the live PR: when the broker rejects with |
Add an order-rule brokerage model for Trading 212 covering US-listed equities (ETFs included) with the market, limit, stop and stop-limit order types the public API accepts. It rejects unsupported security types, unsupported order types, contingent orders (OCO, OTO, brackets) and any order that would cross zero holdings. Trading 212's equity accounts (Invest, Stocks ISA and SIPP) are cash, long-only accounts with no margin. The model therefore defaults to AccountType.Cash and returns leverage 1 for every account type, including the SetBrokerageModel(BrokerageName.Trading212) path, which forwards Margin through BrokerageModel.Create. It also rejects sells that would open or add to a short position from flat or already-short holdings, since OrderCrossesZero only catches positions passing through zero. The long-only rule is evaluated against current holdings at submission time, matching the other LEAN brokerage models, so it does not account for other pending unfilled orders; Trading 212 itself rejects an over-sell with SellingEquityNotOwned, so a same-slice duplicate is not accepted as a short in a live account. Registers BrokerageName.Trading212 in BrokerageModel.Create and GetBrokerageName, and adds NUnit coverage for the supported and rejected order combinations, the account-type and leverage behavior, the short-selling guard and the factory round trip. Closes QuantConnect#9981.
21d6480 to
7fcbfcb
Compare
|
On paper: the check runs, but selecting the model is still opt-in. On message parity, I wouldn't force the full
For the live PR I'd map |
|
@arhancanli thanks for all your reviews so far, do you have any further comments before approving this? |
Description
Adds
Trading212BrokerageModel:AccountType.Cashand pins equity leverage to 1, so aMarginaccount type (including theSetBrokerageModel(BrokerageName.Trading212)path, which passesMargin) still gets no leverage. The default cash account keeps the T+1DelayedSettlementModel.This PR covers the order-rule model only. Fees and buying power are planned as follow-ups (see the comment below).
Related Issue
Closes #9981
Motivation and Context
Trading 212 was not represented in LEAN, so users had to run the broker's rules under an unrelated brokerage model. This model states the supported security and order types and the long-only constraint up front, so unsupported orders are rejected with a clear
NotSupportedmessage instead of failing later at the brokerage layer.Requires Documentation Change
Yes. The documentation's list of supported brokerage models should include Trading 212.
How Has This Been Tested?
dotnet test Tests/QuantConnect.Tests.csproj --filter "FullyQualifiedName~Trading212BrokerageModelTests"dotnet test Tests/QuantConnect.Tests.csproj --filter "FullyQualifiedName~Brokerages"Types of changes
Checklist:
bug-<issue#>-<description>orfeature-<issue#>-<description>