Skip to content

Commit bcd7f4a

Browse files
committed
StrategyPositionManager fixes.
1 parent 3246fed commit bcd7f4a

2 files changed

Lines changed: 98 additions & 35 deletions

File tree

Algo/Strategies/StrategyPositionManager.cs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class StrategyPositionManager(Func<string> strategyIdGetter)
1919
private readonly SyncObject _lock = new();
2020
private readonly Dictionary<(SecurityId secId, Portfolio pf), Position> _positions = [];
2121
private readonly Dictionary<Order, OrderExecInfo> _orderExecInfos = [];
22+
private readonly Dictionary<SecurityId, decimal> _lastPrices = [];
2223

2324
/// <summary>
2425
/// Per-position aggregates cache (blocked volume and active orders counters).
@@ -127,6 +128,7 @@ public void Reset()
127128
_orderExecInfos.Clear();
128129
_posAggs.Clear();
129130
_orderTracks.Clear();
131+
_lastPrices.Clear();
130132
}
131133
}
132134

@@ -141,7 +143,7 @@ private void UpdateAggregates(Order order, Position position)
141143
var agg = _posAggs.SafeAdd((order.Security.ToSecurityId(), order.Portfolio), _ => new());
142144

143145
var balance = order.Balance; // non-nullable balance
144-
var newActive = !order.State.IsFinal() && balance > 0;
146+
var newActive = order.State == OrderStates.Active && balance > 0;
145147

146148
if (!_orderTracks.TryGetValue(order, out var track))
147149
{
@@ -212,7 +214,8 @@ private void UpdateAggregates(Order order, Position position)
212214
}
213215

214216
var blockedNet = agg.BlockedBuy - agg.BlockedSell;
215-
position.BlockedValue = blockedNet == 0 ? null : blockedNet;
217+
var hasActiveOrders = (agg.BuyCount + agg.SellCount) > 0;
218+
position.BlockedValue = hasActiveOrders ? blockedNet : (position.BlockedValue is null ? null : 0m);
216219
position.BuyOrdersCount = agg.BuyCount == 0 ? null : agg.BuyCount;
217220
position.SellOrdersCount = agg.SellCount == 0 ? null : agg.SellCount;
218221
}
@@ -225,6 +228,12 @@ public void ProcessOrder(Order order)
225228
{
226229
ArgumentNullException.ThrowIfNull(order);
227230

231+
if (order.Balance < 0)
232+
throw new ArgumentException(LocalizedStrings.OrderBalanceNotEnough.Put(order.TransactionId, order.Balance), nameof(order));
233+
234+
if (order.State is OrderStates.None or OrderStates.Pending or OrderStates.Failed)
235+
return; // not yet active
236+
228237
var matchedAbs = order.GetMatchedVolume() ?? 0m; // cumulative executed volume (absolute)
229238
var signSide = order.Side == Sides.Sell ? -1 : 1;
230239

@@ -236,9 +245,16 @@ public void ProcessOrder(Order order)
236245

237246
lock (_lock)
238247
{
248+
var key = (order.Security.ToSecurityId(), order.Portfolio);
249+
var posExists = _positions.ContainsKey(key);
250+
251+
// If there are no executions yet and the order state is not Active and no position exists yet, ignore.
252+
if (matchedAbs == 0 && order.State != OrderStates.Active && !posExists)
253+
return;
254+
239255
position = GetOrCreate(order.Security, order.Portfolio, out isNew);
240256

241-
// ensure aggregates reflect current order state before fill handling
257+
// ensure aggregates reflect current order state before fill handling (to close blocked on Done, etc.)
242258
UpdateAggregates(order, position);
243259

244260
if (!_orderExecInfos.TryGetValue(order, out var execInfo))
@@ -294,6 +310,17 @@ public void ProcessOrder(Order order)
294310
if (deltaCommission != 0)
295311
position.Commission = posCommission + deltaCommission;
296312

313+
// Recompute position market value if we know last price for this security
314+
if (_lastPrices.TryGetValue(order.Security.ToSecurityId(), out var lastPrice))
315+
{
316+
var value = newQty.Abs() * lastPrice;
317+
position.CurrentPrice = value == 0 ? 0m : value;
318+
}
319+
else
320+
{
321+
position.CurrentPrice = null; // unknown market price
322+
}
323+
297324
position.LocalTime = order.LocalTime;
298325
position.LastChangeTime = order.ServerTime;
299326
}
@@ -318,12 +345,15 @@ public void UpdateCurrentPrice(SecurityId secId, decimal price, DateTimeOffset s
318345

319346
lock (_lock)
320347
{
348+
_lastPrices[secId] = price;
349+
321350
foreach (var ((kSecId, _), pos) in _positions)
322351
{
323352
if (!kSecId.Equals(secId))
324353
continue;
325354

326-
pos.CurrentPrice = price;
355+
var qty = pos.CurrentValue ?? 0m;
356+
pos.CurrentPrice = qty == 0 ? 0m : qty.Abs() * price;
327357
pos.LastChangeTime = serverTime;
328358
pos.LocalTime = localTime;
329359
(changed ??= []).Add(pos);

0 commit comments

Comments
 (0)