private void AddActiveOperation(Operation op) { this._ActiveOperations.Add(op); var symData = GetSymbolData(op.Symbol); symData.AddActiveOperation(op); if (this.Config.SaveData) { lock (DbLock) DbActiveOperations.Upsert(op); } }
public async Task Update(TimeSlice slice) { LastUpdate = Market.Time; //update selected symbols var changes = await SymbolsFilter.UpdateAsync(slice); if (changes != SelectedSymbolsChanges.None) { var selectedForOperationsActive = this.ActiveOperations.Select(ao => ao.Symbol).GroupBy(ao => ao.Key).Select(g => g.First()).ToList(); //release feeds of unused symbols foreach (var sym in changes.RemovedSymbols) { SymbolData symbolData = GetSymbolData(sym); symbolData.IsSelectedForTrading = false; //if it doesn't have acrive operations if (!selectedForOperationsActive.Any(aos => aos.Key == sym.Key)) { if (symbolData.Feed != null) { symbolData.Feed.OnData -= Feed_OnData; this.ReleaseFeed(symbolData.Feed); symbolData.Feed = null; } } } //add feeds for added symbols and those that have open operations foreach (var sym in changes.AddedSymbols) { SymbolData symbolData = GetSymbolData(sym); symbolData.IsSelectedForTrading = true; } foreach (var sym in changes.AddedSymbols.Concat(selectedForOperationsActive)) { SymbolData symbolData = GetSymbolData(sym); if (symbolData.Feed == null) { symbolData.Feed = await this.GetSymbolFeed(sym.Key); symbolData.Feed.OnData -= Feed_OnData; symbolData.Feed.OnData += Feed_OnData; } } if (Sentry != null) { await Sentry.OnSymbolsChanged(changes); } if (Allocator != null) { Allocator.OnSymbolsChanged(changes); } if (Executor != null) { Executor.OnSymbolsChanged(changes); } if (RiskManager != null) { RiskManager.OnSymbolsChanged(changes); } } // register trades with their linked operations Operation[] oldOperations = null; List <Operation> operationsResumed = new List <Operation>(); foreach (ITrade trade in slice.Trades) { //first search in active operations var symData = _SymbolsData[trade.Symbol]; var activeOp = symData.ActiveOperations.FirstOrDefault(op => op.IsTradeAssociated(trade)); if (activeOp != null) { if (activeOp.AddTrade(trade)) { Logger.Info($"{Time} - New trade for operation {activeOp.ToString()}: {trade.ToString()}"); } } else { //let's search in closed operations by getting ligtweigth instances if (oldOperations == null) { //lock (DbLock) // oldOperations = oldOperations ?? DbClosedOperations.Find(o => o.CreationTime >= Market.Time.AddDays(-5)).ToArray(); lock (DbLock) oldOperations = QueryClosedOperations(doc => doc["CreationTime"].AsDateTime >= Market.Time.AddDays(-4)); } var oldOp = oldOperations.FirstOrDefault(op => op.IsTradeAssociated(trade)); //if we found an operation let's get a real instance if (oldOp != null) { oldOp = DbClosedOperations.FindById(oldOp.Id); if (oldOp == null) { Logger.Error($"Operation found then not found! {oldOp}."); } } if (oldOp != null) { operationsResumed.Add(oldOp); if (oldOp.AddTrade(trade)) { Logger.Info($"{Time} - New trade for 'old' operation {activeOp}: {trade.ToString()}"); } //check if it got resumed by this new trade if (!oldOp.IsClosed) { this.ResumeOperation(oldOp); Logger.Info($"Resuming 'old' operation {activeOp}."); } else { oldOp.Dispose(); //otherwise we must dispose it } } else { Logger.Debug($"{Time} - New trade {trade.ToString()} without any associated operation"); } } } //dispose the operations that we didn't use if (oldOperations != null) { foreach (var oper in oldOperations.Where(oo => !operationsResumed.Contains(oo))) { oper.Dispose(); } } // call OnUpdate await OnUpdate(slice); // get signals if (Sentry != null) { Sentry.UpdateAsync(slice); } //create operations if (Allocator != null) { Allocator.Update(slice); } //close operations that have been in close queue for enough time lock (DbLock) { this.Db?.BeginTrans(); List <Operation> operationsToClose = _ActiveOperations.Where(op => this.Time >= op.CloseDeadTime).ToList(); foreach (var op in operationsToClose) { if (op.AmountInvested > 0) { Logger.Info("{0} - Closing operation {1}.", Time, op.ToString("c")); } else { Logger.Debug("{0} - Closing operation {1}.", Time, op.ToString("c")); } op.Close(); _ActiveOperations.Remove(op); if (this.Config.SaveData || op.AmountInvested > 0) { this._ClosedOperations.Add(op); } this.SymbolsData[op.Symbol.Key].CloseOperation(op); if (this.Config.SaveData) { lock (DbLock) { //update database DbActiveOperations.Delete(op.Id); DbClosedOperations.Upsert(op); } } } this.Db?.Commit(); } //add new operations that have been created foreach (var op in slice.NewOperations) { AddActiveOperation(op); } //manage orders if (Executor != null) { await Executor.Update(slice); } //manage risk if (RiskManager != null) { await RiskManager.Update(slice); } while (Commands.Count > 0) { try { if (Commands.TryDequeue(out Command command)) { await command.Run(); } } catch (Exception ex) { Logger.Error($"Error while running command: {ex.Message}."); } } if (Config.SaveData) { SaveNonVolatileVars(); } }