18 using Newtonsoft.Json;
19 using System.Threading;
23 using System.Threading.Tasks;
26 using System.Collections.Generic;
36 private static readonly TimeSpan LiveBrokerageCashSyncTime =
new TimeSpan(7, 45, 0);
38 private readonly
object _performCashSyncReentranceGuard =
new object();
39 private bool _syncedLiveBrokerageCashToday =
true;
40 private long _lastSyncTimeTicks = DateTime.UtcNow.Ticks;
87 public event EventHandler<BrokerageMessageEvent>
Message;
92 public string Name {
get; }
132 public abstract void Connect();
157 catch (Exception err)
182 catch (Exception err)
198 catch (Exception err)
212 Log.
Debug(
"Brokerage.OptionPositionAssigned(): " + e);
216 catch (Exception err)
230 Log.
Debug(
"Brokerage.OnOptionNotification(): " + e);
234 catch (Exception err)
248 Log.
Debug(
"Brokerage.OnNewBrokerageOrderNotification(): " + e);
252 catch (Exception err)
266 Log.
Debug(
"Brokerage.OnDelistingNotification(): " + e);
270 catch (Exception err)
284 Log.
Trace($
"Brokerage.OnAccountChanged(): {e}");
288 catch (Exception err)
304 Log.
Error(
"Brokerage.OnMessage(): " + e);
308 Log.
Trace(
"Brokerage.OnMessage(): " + e);
313 catch (Exception err)
324 protected virtual List<Holding>
GetAccountHoldings(Dictionary<string, string> brokerageData, IEnumerable<Security> securities)
328 Log.
Debug(
"Brokerage.GetAccountHoldings(): starting...");
331 if (brokerageData !=
null && brokerageData.Remove(
"live-holdings", out var value) && !
string.IsNullOrEmpty(value))
334 var result = JsonConvert.DeserializeObject<List<Holding>>(value);
337 return new List<Holding>();
339 Log.
Trace($
"Brokerage.GetAccountHoldings(): sourcing holdings from provided brokerage data, found {result.Count} entries");
343 return securities?.Where(security => security.Holdings.AbsoluteQuantity > 0)
344 .OrderBy(security => security.Symbol)
345 .Select(security =>
new Holding(security)).ToList() ??
new List<Holding>();
357 Log.
Debug(
"Brokerage.GetCashBalance(): starting...");
360 if (brokerageData !=
null && brokerageData.Remove(
"live-cash-balance", out var value) && !
string.IsNullOrEmpty(value))
363 var result = JsonConvert.DeserializeObject<List<CashAmount>>(value);
366 return new List<CashAmount>();
368 Log.
Trace($
"Brokerage.GetCashBalance(): sourcing cash balance from provided brokerage data, found {result.Count} entries");
372 return cashBook?.Select(x =>
new CashAmount(x.Value.Amount, x.Value.Symbol)).ToList() ??
new List<CashAmount>();
411 return Enumerable.Empty<
BaseData>();
424 return orderDirection
switch
428 _ =>
throw new ArgumentOutOfRangeException(nameof(orderDirection), orderDirection,
"Invalid order direction")
432 #region IBrokerageCashSynchronizer implementation
453 if (_syncedLiveBrokerageCashToday && currentTimeNewYork.Date !=
LastSyncDate)
455 _syncedLiveBrokerageCashToday =
false;
458 return !_syncedLiveBrokerageCashToday && currentTimeNewYork.TimeOfDay >= LiveBrokerageCashSyncTime;
473 if (!Monitor.TryEnter(_performCashSyncReentranceGuard))
475 Log.
Trace(
"Brokerage.PerformCashSync(): Reentrant call, cash sync not performed");
479 Log.
Trace(
"Brokerage.PerformCashSync(): Sync cash balance");
481 List<CashAmount> balances =
null;
486 catch (Exception err)
488 Log.
Error(err,
"Error in GetCashBalance:");
492 if (balances ==
null)
494 Log.
Trace(
"Brokerage.PerformCashSync(): No cash balances available, cash sync not performed");
499 foreach (var balance
in balances)
503 Log.
Trace($
"Brokerage.PerformCashSync(): Unexpected cash found {balance.Currency} {balance.Amount}",
true);
511 var cash = kvp.Value;
514 var balanceCash = balances.Find(balance => balance.Currency == cash.Symbol);
518 var delta = cash.Amount - balanceCash.Amount;
522 Log.
Trace($
"Brokerage.PerformCashSync(): {balanceCash.Currency} Delta: {delta:0.00}",
true);
529 Log.
Trace($
"Brokerage.PerformCashSync(): {cash.Symbol} was not found in brokerage cash balance, setting the amount to 0",
true);
533 _syncedLiveBrokerageCashToday =
true;
534 _lastSyncTimeTicks = currentTimeUtc.Ticks;
538 Monitor.Exit(_performCashSyncReentranceGuard);
543 Task.Delay(TimeSpan.FromSeconds(10)).ContinueWith(_ =>
546 if (getTimeSinceLastFill() <= TimeSpan.FromSeconds(20))
550 _syncedLiveBrokerageCashToday = false;
552 Log.Trace(
"Brokerage.PerformCashSync(): Unverified cash sync - resync required.");
556 Log.Trace(
"Brokerage.PerformCashSync(): Verified cash sync.");
558 algorithm.Portfolio.LogMarginInformation();