planGroupTpSl
Diff a desired grouped TP/SL state against what the handler already holds, and decide per child whether to set, delete, or skip.
The conditional-order handler has no bulk endpoint — one signed request per quote — so on a merged position of N quotes a naive “apply” costs N signatures every time. This planner’s job is to make that fan-out as small as honestly possible:
- a side whose rounded trigger price and price type already match the confirmed snapshot is dropped from the leg, so
setQuoteTpSlreceives only what changed; when both sides drop, the child is skipped as"unchanged"; - a side cleared to
""while the handler holds a live order becomes adelete; cleared with nothing live, it is dropped; - unanchored or fully-closed children are skipped with a typed reason instead of throwing;
- with
referencePriceandconfigsupplied, each child runsvalidateTpSlagainst its resulting pair — so raising a take profit still gets checked against an untouched stop loss — and a failure becomes"invalid"carrying theTpSlValidationfor inline errors.
Rounding before comparing is what makes "100" and "100.0000" equal at pricePrecision: 4. Without it the handler’s own rounding would make every submit look like a change, and the SDK would resubmit unchanged orders forever.
A child can produce two actions — cancel one side, write the other — so actions is not one entry per child.
import { planGroupTpSl, setQuoteTpSl } from "@symmio/trading-core";
const plan = planGroupTpSl({
children,
desired: Object.fromEntries(children.map((child) => [child.key, { tp: { triggerPrice: "150" } }])),
pricePrecision: 4,
referencePrice: markPrice,
config: tpslConfig,
});
if (plan.hasInvalid) return;
for (const action of plan.sets) {
await setQuoteTpSl(config, { ...action, subAccount, pricePrecision: 4 });
}Parameters
The grouped position’s children with their confirmed snapshots.
desiredGroupTpSlDesiredMaprequiredDesired state per child key. A child with no entry is skipped as "nothing-to-do". An entry with an empty
triggerPrice clears that side.
pricePrecisionnumberrequiredMarket price precision. Trigger prices are rounded to it before diffing.
referencePricestringoptionalReference price for validation (decimal string). Pass the live mark price — a grouped position is validated
against what the trader is looking at, not against each child’s own open price. Omit (with config) to skip
validation.
Live handler /configs/ rules. Required for validation to run.
onlyreadonly string[]optionalRestrict planning to these child keys — the retry-failed-only path. Others are omitted entirely.
Returns
Every decision in child order, plus pre-split sets / deletes / skips, a hasInvalid submit gate and an isNoop
flag.
Related
GroupTpSlAction— the per-child decision shape.planGroupTpSlDelete— the cancel-all counterpart.useSetQuoteGroupTpSl— executes this plan with per-child progress.