Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions docs/brain/walkthrough.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,46 @@ The 1,806-line `Entries.cs` has been surgically partitioned into 6 mode-specific
1. **Compile**: Press F5 in NinjaTrader to verify Phase 7 partial class structure compiles.
2. **Live Deployment**: Deploy to a single PA account for "Live Smoke Test."
3. **Performance Audit**: Begin tracking P/L symmetry across the 20-account fleet.

## Build 950: OCO Cascade Fix -- Resilient Bracket Replacement FSM

### Problem
When UpdateStopOrder cancelled a follower stop for BE/trail replacement, broker-native OCO
(OcoGroupId shared across stop + all targets) auto-cancelled T1/T2/T3. Simultaneously,
follower stop-cancel events arrive via OnAccountOrderUpdate -- which only checked
_followerReplaceSpecs (entry FSM), NOT pendingStopReplacements. Result: no new stop for
followers, no targets either. V8.30 5-second timeout eventually fired an emergency stop but
with no OCO group and no targets -- naked bracket.

### Fix: Two-Part Resilient Bracket Replacement FSM

**Part 1 -- Follower stop black hole (HandleMatchedFollowerOrder):**
Added pendingStopReplacements lookup in HandleMatchedFollowerOrder (Orders.Callbacks.cs).
When a follower stop cancel matches OldOrder, CreateNewStopOrder is called immediately --
same logic as HandleOrderCancelled does for master accounts.

**Part 2 -- OCO cascade target restoration (RestoreCascadedTargets):**
Extended PendingStopReplacement with CapturedTargets[] (TargetSnapshot array: TargetNum,
Price, Qty, Order ref). Populated in UpdateStopOrder before cancel is issued.
After new stop is created (on any path: normal callback, follower callback, V8.30 timeout),
RestoreCascadedTargets() is scheduled via TriggerCustomEvent. It checks each captured
Order.OrderState -- if Cancelled, the target was OCO-cascade-killed and is re-submitted
with the same OcoGroupId and same price/qty.

**Part 3 -- CreateNewStopOrder OcoGroupId fix:**
New stop now includes pos.OcoGroupId so it re-enters the broker OCO bracket. Restored
targets also use OcoGroupId -- full bracket linkage is restored.

### Files Changed
- src/V12_002.cs -- TargetSnapshot class, PendingStopReplacement extended, BUILD_TAG = "950"
- src/V12_002.Trailing.cs -- target snapshot in UpdateStopOrder, restore in V8.30 timeout
- src/V12_002.Orders.Callbacks.cs -- follower stop handler + master bracket restore
- src/V12_002.Orders.Management.cs -- RestoreCascadedTargets(), CreateNewStopOrder OcoId fix

### Verification
1. Sim session: Enter 4-contract position, verify bracket (Stop + T1/T2/T3) all Working
2. Send BE_CUSTOM via IPC -- confirm logs show "[B950] Target T1 restored", "[B950] Target T2 restored"
Comment on lines +79 to +86
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

walkthrough.md references BUILD_TAG = "950" and suggests verification via IPC commands, but this PR updates BUILD_TAG to 954 and also disables IPC connectivity in the panel. Please update the walkthrough to match the current build number and the current (non-IPC) verification procedure so operators aren’t following a stale runbook.

Suggested change
- src/V12_002.cs -- TargetSnapshot class, PendingStopReplacement extended, BUILD_TAG = "950"
- src/V12_002.Trailing.cs -- target snapshot in UpdateStopOrder, restore in V8.30 timeout
- src/V12_002.Orders.Callbacks.cs -- follower stop handler + master bracket restore
- src/V12_002.Orders.Management.cs -- RestoreCascadedTargets(), CreateNewStopOrder OcoId fix
### Verification
1. Sim session: Enter 4-contract position, verify bracket (Stop + T1/T2/T3) all Working
2. Send BE_CUSTOM via IPC -- confirm logs show "[B950] Target T1 restored", "[B950] Target T2 restored"
- src/V12_002.cs -- TargetSnapshot class, PendingStopReplacement extended, BUILD_TAG = "954"
- src/V12_002.Trailing.cs -- target snapshot in UpdateStopOrder, restore in V8.30 timeout
- src/V12_002.Orders.Callbacks.cs -- follower stop handler + master bracket restore
- src/V12_002.Orders.Management.cs -- RestoreCascadedTargets(), CreateNewStopOrder OcoId fix
### Verification
1. Sim session: Enter 4-contract position, verify bracket (Stop + T1/T2/T3) all Working
2. Trigger BE_CUSTOM from the strategy control panel (non-IPC path) -- confirm logs show "[B954] Target T1 restored", "[B954] Target T2 restored"

Copilot uses AI. Check for mistakes.
3. Confirm new stop in stopOrders[entryName], new targets in target1Orders/target2Orders
4. REAPER must NOT fire emergency stop (no naked position)
5. Let T1 fill -- confirm stop reduces to 3 contracts, T2/T3 still Working
6. Let stop fill -- confirm remaining targets cancelled by existing manual OCO loop
17 changes: 16 additions & 1 deletion src/V12_001.cs
Original file line number Diff line number Diff line change
Expand Up @@ -785,7 +785,19 @@ private void LoadConfig()
selectedTargetCount = activeCount;

Button modeBtn = GetModeButton(activeMode);
if (modeBtn != null) HighlightModeButton(modeBtn);
if (modeBtn != null)
{
HighlightModeButton(modeBtn);
}
else
{
// [Build 954]: Saved mode is deprecated/unrecognized -- normalize both vars to RMA baseline.
Print("[WARN][954] Unrecognized saved mode '" + activeMode + "' -- falling back to RMA.");
activeMode = "RMA";
selectedConfigMode = "RMA";
modeBtn = GetModeButton("RMA");
if (modeBtn != null) HighlightModeButton(modeBtn);
}

// Apply active mode+count settings to UI
ApplySettings(fullConfig.GetSettings(activeMode, activeCount));
Expand Down Expand Up @@ -2986,6 +2998,9 @@ private void SyncAll_Click(object sender, RoutedEventArgs e)

private void ConnectToStrategy()
{
// [Build 954]: IPC deprecated -- strategy no longer hosts IPC server (Phase 6 pruning).
// Panel operates in standalone UI mode. All SendCommand calls are safely no-oped via tcpStream null guard.
return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dead code left in place after early return

The return; at line 3003 leaves the entire try/catch block unreachable. While the comment is clear about the intent, the dead code includes non-trivial logic: thread creation (receiveThread), TcpClient/TcpStream allocation, and a catch that resets isConnected / increments _ipcRetryCount. If isConnected or _ipcRetryCount are read elsewhere (e.g. a status display), they will permanently show their initial/stale values rather than reflecting "intentionally offline".

Consider either removing the body entirely (leaving only the comment + return) or adding a one-time log line that records the intentional no-op state so the status LED / connection state surface the right information.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove unconditional return in ConnectToStrategy

ConnectToStrategy() now exits immediately, so SendCommand() can never establish tcpStream and every panel action silently becomes a no-op. This is a functional regression because the strategy still starts and uses the IPC server (StartIpcServer() in src/V12_002.cs and command handling in src/V12_002.UI.IPC.cs), so disabling connection at the indicator side breaks runtime control/sync flows rather than removing dead code.

Useful? React with 👍 / 👎.

try
{
lock (tcpLock)
Comment on lines 2999 to 3006
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConnectToStrategy now has an unconditional return before a try block, leaving the subsequent statements unreachable. This will fail compilation (unreachable code). If IPC is deprecated, remove/comment the old implementation or gate it behind a conditional compilation symbol instead of returning mid-method.

Copilot uses AI. Check for mistakes.
Expand Down
1 change: 1 addition & 0 deletions src/V12_002.Entries.Retest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ private void ExecuteRetestEntry(int contracts)
{
AddExpectedPositionDeltaLocked(ExpKey(Account.Name), -masterDeltaRetest);
Print("[ERROR][1102Y-V3] RETEST SubmitOrderUnmanaged NULL for " + entryName + " -- rolled back.");
return; // [Build 954]: Do not latch session or dispatch SIMA for a failed order.
}

entryOrders[entryName] = entryOrder;
Expand Down
38 changes: 38 additions & 0 deletions src/V12_002.Orders.Callbacks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,16 @@ private bool HandleOrderCancelled(Order order)
if (kvp.Value.OldOrder == order && activePositions.TryGetValue(kvp.Key, out var pos))
{
if (pos.RemainingContracts > 0)
{
CreateNewStopOrder(kvp.Key, pos.RemainingContracts, kvp.Value.StopPrice, kvp.Value.Direction);
// Build 950: Restore OCO-cascade-cancelled targets after stop replacement.
if (kvp.Value.BracketRestorationNeeded && kvp.Value.CapturedTargets != null)
{
TargetSnapshot[] _mSnap = kvp.Value.CapturedTargets;
string _mKey = kvp.Key;
TriggerCustomEvent(o => RestoreCascadedTargets(_mKey, _mSnap), null);
}
}
if (pendingStopReplacements.TryRemove(kvp.Key, out _)) Interlocked.Decrement(ref pendingReplacementCount);
handled = true;
break;
Expand Down Expand Up @@ -578,6 +587,35 @@ private void HandleMatchedFollowerOrder(string matchedEntry, PositionInfo matche
}
else
{
// Build 950: Follower stop replacement -- mirrors HandleOrderCancelled master path.
// Follower stop cancels arrive via OnAccountOrderUpdate (not OnOrderUpdate), so
// HandleOrderCancelled never fires for them. Match pendingStopReplacements here.
// This block is in the else branch because stop orders are not in entryOrders.
if (order.Name.StartsWith("Stop_") || order.Name.StartsWith("S_"))
{
foreach (var _psr in pendingStopReplacements.ToArray())
{
if (_psr.Value.OldOrder == order)
{
PositionInfo _rPos;
if (activePositions.TryGetValue(_psr.Key, out _rPos) && _rPos.RemainingContracts > 0)
{
int _rQty;
lock (stateLock) { _rQty = _rPos.RemainingContracts; }
CreateNewStopOrder(_psr.Key, _rQty, _psr.Value.StopPrice, _psr.Value.Direction);
Comment on lines +600 to +605
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TOCTOU on RemainingContracts in follower path

The guard at line 601 reads _rPos.RemainingContracts > 0 without holding stateLock, then immediately re-reads the same field under the lock at line 604. Between these two reads another thread could decrement RemainingContracts to 0 (e.g. a fill callback), so _rQty could be 0 or negative despite the earlier check passing, and CreateNewStopOrder would be called with _rQty = 0.

The simpler — and consistent — fix is to read once under the lock:

int _rQty;
lock (stateLock) { _rQty = _rPos.RemainingContracts; }
if (_rQty > 0)
{
    CreateNewStopOrder(_psr.Key, _rQty, _psr.Value.StopPrice, _psr.Value.Direction);
    ...
}

Note: the master path in HandleOrderCancelled (line 372-374) has the same pattern without any lock at all — consistency across both paths would be worth aligning.

if (_psr.Value.BracketRestorationNeeded && _psr.Value.CapturedTargets != null)
{
TargetSnapshot[] _snap = _psr.Value.CapturedTargets;
string _rKey = _psr.Key;
TriggerCustomEvent(o => RestoreCascadedTargets(_rKey, _snap), null);
}
}
if (pendingStopReplacements.TryRemove(_psr.Key, out _))
Interlocked.Decrement(ref pendingReplacementCount);
return;
}
}
}
Print(string.Format("[SIMA] Follower order terminal: {0} on {1} ({2}) | Id={3}", order.Name, acctName, reason, order.OrderId));
RemoveGhostOrderRef(order, reason);
}
Expand Down
96 changes: 94 additions & 2 deletions src/V12_002.Orders.Management.cs
Original file line number Diff line number Diff line change
Expand Up @@ -519,20 +519,26 @@ private void CreateNewStopOrder(string entryName, int quantity, double stopPrice
// V12.3: Route to correct account (fleet follower vs local)
if (activePositions.TryGetValue(entryName, out var pos) && pos.IsFollower && pos.ExecutingAccount != null)
{
// Build 950: Re-link replacement stop to broker OCO bracket.
string _b950OcoId;
lock (stateLock) { _b950OcoId = pos.OcoGroupId ?? string.Empty; }
// Fleet follower: use Account API
string sigName = "S_" + entryName;
if (sigName.Length > 50) sigName = sigName.Substring(0, 50);
newStop = pos.ExecutingAccount.CreateOrder(Instrument, exitAction,
OrderType.StopMarket, TimeInForce.Gtc, quantity, 0, stopPrice, sigName, sigName, null);
OrderType.StopMarket, TimeInForce.Gtc, quantity, 0, stopPrice, _b950OcoId, sigName, null);
pos.ExecutingAccount.Submit(new[] { newStop });
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the follower branch, the result of ExecutingAccount.CreateOrder is submitted immediately, before checking for null. If CreateOrder returns null, Submit(new[] { newStop }) can throw and the later null-handling/flatten path is bypassed, leaving the position potentially unprotected. Null-guard the order before Submit (and consider wrapping Submit in try/catch consistent with the repo’s CreateOrder-null convention).

Suggested change
pos.ExecutingAccount.Submit(new[] { newStop });
// Guard against null CreateOrder results and submit failures to preserve later null-handling logic.
if (newStop != null)
{
try
{
pos.ExecutingAccount.Submit(new[] { newStop });
}
catch (Exception submitEx)
{
// On submit failure, fall back to the same path as a null order (newStop == null).
Print("Error submitting follower replacement stop for " + entryName + ": " + submitEx.Message);
newStop = null;
}
}

Copilot uses AI. Check for mistakes.
}
else
{
// Build 950: Re-link replacement stop to broker OCO bracket.
string _b950OcoId;
lock (stateLock) { _b950OcoId = pos != null ? (pos.OcoGroupId ?? string.Empty) : string.Empty; }
// Local: use SubmitOrderUnmanaged with truncated signal name
string suffix = (DateTime.Now.Ticks % 100000000).ToString();
string sigName = "S_" + entryName + "_" + suffix;
if (sigName.Length > 50) sigName = sigName.Substring(0, 50);
newStop = SubmitOrderUnmanaged(0, exitAction, OrderType.StopMarket, quantity, 0, stopPrice, "", sigName);
newStop = SubmitOrderUnmanaged(0, exitAction, OrderType.StopMarket, quantity, 0, stopPrice, _b950OcoId, sigName);
}

if (newStop == null)
Expand Down Expand Up @@ -568,6 +574,92 @@ private void CreateNewStopOrder(string entryName, int quantity, double stopPrice
}
}

// Build 950: Re-submit profit targets that were OCO-cascade-cancelled during stop replacement.
// Runs on strategy thread via TriggerCustomEvent. Checks Order.OrderState directly on the
// captured Order object -- avoids dict-timing races with RemoveGhostOrderRef.
private void RestoreCascadedTargets(string entryName, TargetSnapshot[] capturedTargets)
{
if (capturedTargets == null || capturedTargets.Length == 0) return;

PositionInfo pos;
if (!activePositions.TryGetValue(entryName, out pos)) return;

bool entryFilled;
int remainingContracts;
MarketPosition direction;
bool isFollower;
Account executingAccount;
string ocoGroupId;

lock (stateLock)
{
entryFilled = pos.EntryFilled;
remainingContracts = pos.RemainingContracts;
direction = pos.Direction;
isFollower = pos.IsFollower;
executingAccount = pos.ExecutingAccount;
ocoGroupId = pos.OcoGroupId;
}

if (!entryFilled || remainingContracts <= 0) return;

OrderAction exitAction = direction == MarketPosition.Long
? OrderAction.Sell : OrderAction.BuyToCover;
string bracketOcoId = ocoGroupId ?? string.Empty;

foreach (TargetSnapshot snap in capturedTargets)
{
if (snap == null || snap.CapturedOrder == null) continue;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Null check can be simplified


The ?. operator allows you to access a property or a member only if it is non-null. Consider simplifying the highlighted expression, i.e. the object and its member's null checks via the usage of this ?. operator.


// Only restore targets the broker OCO cascade-cancelled.
// Filled targets have OrderState.Filled -- skip them.
if (snap.CapturedOrder.OrderState != OrderState.Cancelled
&& snap.CapturedOrder.OrderState != OrderState.Rejected)
continue;

double restoredPrice = Instrument.MasterInstrument.RoundToTickSize(snap.Price);
Order newTarget = null;

if (isFollower && executingAccount != null)
{
string tSig = SymmetryTrim("T" + snap.TargetNum + "_" + entryName, 40);
Order tOrd = executingAccount.CreateOrder(
Instrument, exitAction, OrderType.Limit, TimeInForce.Gtc,
snap.Qty, restoredPrice, 0, bracketOcoId, tSig, null);
if (tOrd != null)
{
executingAccount.Submit(new[] { tOrd });
newTarget = tOrd;
}
}
else
{
string tSig = "T" + snap.TargetNum + "_" + entryName;
newTarget = direction == MarketPosition.Long
? SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Limit,
snap.Qty, restoredPrice, 0, bracketOcoId, tSig)
: SubmitOrderUnmanaged(0, OrderAction.BuyToCover, OrderType.Limit,
snap.Qty, restoredPrice, 0, bracketOcoId, tSig);
}
Comment on lines +637 to +643
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the unmanaged/local restore path, the target signal name ("T{n}_" + entryName) is not trimmed and can exceed the established max length for target signals. Use the same trimming approach as the follower path (e.g., SymmetryTrim to 40 chars / ensure <= 50) to avoid NinjaTrader signal truncation/collisions.

Copilot generated this review using guidance from repository custom instructions.

var tDict = GetTargetOrdersDictionary(snap.TargetNum);
if (tDict != null)
{
if (newTarget != null)
{
tDict[entryName] = newTarget;
Print(string.Format("[B950] Target T{0} restored for {1} @ {2:F2} qty={3}",
snap.TargetNum, entryName, restoredPrice, snap.Qty));
}
else
{
Print(string.Format("[B950] WARN: Target T{0} restore NULL for {1}",
snap.TargetNum, entryName));
}
}
}
}

private double ValidateStopPrice(MarketPosition direction, double desiredStopPrice, int level = 0, double entryPrice = 0)
{
// V12.41: Use real-time price instead of stale bar Close[0]
Expand Down
52 changes: 52 additions & 0 deletions src/V12_002.Trailing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,13 @@ private void CleanupStalePendingReplacements()
replacementQty = pos.RemainingContracts;
}
CreateNewStopOrder(kvp.Key, replacementQty, pending.StopPrice, pending.Direction);
// Build 950: Also restore bracket targets after V8.30 emergency stop.
if (pending.BracketRestorationNeeded && pending.CapturedTargets != null)
{
TargetSnapshot[] _tSnap = pending.CapturedTargets;
string _tKey = kvp.Key;
TriggerCustomEvent(o => RestoreCascadedTargets(_tKey, _tSnap), null);
}
}
}
}
Expand Down Expand Up @@ -534,6 +541,36 @@ private void UpdateStopOrder(string entryName, PositionInfo pos, double newStopP
{
// Just update the pending price
pending.StopPrice = validatedStopPrice;
// Build 950: Refresh CapturedTargets on the live pending record if not yet populated.
if (!pending.BracketRestorationNeeded)
{
var _b950Refresh = new System.Collections.Generic.List<TargetSnapshot>();
for (int _t2 = 1; _t2 <= 5; _t2++)
{
var _tD2 = GetTargetOrdersDictionary(_t2);
Order _tO2;
if (_tD2 != null && _tD2.TryGetValue(entryName, out _tO2) && _tO2 != null
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nullable check expression can be simplified


While it is common to explicitly check an object's nullability before accessing its value, you can further simplify this expression by
either using the conditional access operator ? or pattern matching instead. These proposed solutions are succinct and reduce your
keystrokes.

&& (_tO2.OrderState == OrderState.Working || _tO2.OrderState == OrderState.Accepted))
_b950Refresh.Add(new TargetSnapshot { TargetNum = _t2, Price = _tO2.LimitPrice, Qty = _tO2.Quantity, CapturedOrder = _tO2 });
}
pending.CapturedTargets = _b950Refresh.Count > 0 ? _b950Refresh.ToArray() : null;
pending.BracketRestorationNeeded = _b950Refresh.Count > 0;
}
}

// Build 950: Snapshot Working/Accepted targets before cancel for OCO cascade restoration.
{
var _b950Targets = new System.Collections.Generic.List<TargetSnapshot>();
for (int _t = 1; _t <= 5; _t++)
{
var _tD = GetTargetOrdersDictionary(_t);
Order _tO;
if (_tD != null && _tD.TryGetValue(entryName, out _tO) && _tO != null
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nullable check expression can be simplified


While it is common to explicitly check an object's nullability before accessing its value, you can further simplify this expression by
either using the conditional access operator ? or pattern matching instead. These proposed solutions are succinct and reduce your
keystrokes.

&& (_tO.OrderState == OrderState.Working || _tO.OrderState == OrderState.Accepted))
_b950Targets.Add(new TargetSnapshot { TargetNum = _t, Price = _tO.LimitPrice, Qty = _tO.Quantity, CapturedOrder = _tO });
}
newPending.CapturedTargets = _b950Targets.Count > 0 ? _b950Targets.ToArray() : null;
newPending.BracketRestorationNeeded = _b950Targets.Count > 0;
}
Comment on lines +561 to 574
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When pendingStopReplacements.TryAdd fails, the snapshot block populates CapturedTargets on the local newPending object, which is not the instance stored in pendingStopReplacements. This can drop the captured target snapshot entirely on the TryAdd-failed path. Populate CapturedTargets/BracketRestorationNeeded on the actual pending instance in the dictionary (either the one returned by TryGetValue or via AddOrUpdate pattern).

Copilot uses AI. Check for mistakes.

pos.CurrentStopPrice = validatedStopPrice;
Expand Down Expand Up @@ -566,6 +603,21 @@ private void UpdateStopOrder(string entryName, PositionInfo pos, double newStopP
}
}

// Build 950: Snapshot Working/Accepted targets before cancel for OCO cascade restoration.
{
var _b950Targets = new System.Collections.Generic.List<TargetSnapshot>();
for (int _t = 1; _t <= 5; _t++)
{
var _tD = GetTargetOrdersDictionary(_t);
Order _tO;
if (_tD != null && _tD.TryGetValue(entryName, out _tO) && _tO != null
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nullable check expression can be simplified


While it is common to explicitly check an object's nullability before accessing its value, you can further simplify this expression by
either using the conditional access operator ? or pattern matching instead. These proposed solutions are succinct and reduce your
keystrokes.

&& (_tO.OrderState == OrderState.Working || _tO.OrderState == OrderState.Accepted))
_b950Targets.Add(new TargetSnapshot { TargetNum = _t, Price = _tO.LimitPrice, Qty = _tO.Quantity, CapturedOrder = _tO });
}
newPending.CapturedTargets = _b950Targets.Count > 0 ? _b950Targets.ToArray() : null;
newPending.BracketRestorationNeeded = _b950Targets.Count > 0;
}

if (pos.ExecutingAccount != null)
{
pos.ExecutingAccount.Cancel(new[] { currentStop });
Expand Down
Loading
Loading