private static async Task <TradeOpportunity> GetOrUpdateTradeOpportunity(Opportunity opportunity, bool IsSimulation) { // Update requirements and maximum possible trade. var requirements = opportunity.UpdateRequirementsAndAmount().ToList(); // string stateCode = opportunity.RequirementsMet ? "RQ-MET" : "RQ-MISSING"; decimal oppValue = opportunity.GetMarginValuePercent(); // Create the trade value; var newTradeValue = new TradeOpportunityValue() { PotentialMargin = oppValue, Timestamp = DateTime.UtcNow, TradeOpportunityStateId = TradeOpportunityState.States.Single(x => x.Code == stateCode).Id }; // If the oppvalue is below treshold and doesn't exist yet, then ignore it. Else stop it. if (oppValue < SystemSetting.MinimumTradeOpportunityPercent) { TradeOpportunity tradOpp = null; tradeOpportunities.TryGetValue(opportunity.UniqueIdentifier, out tradOpp); if (tradOpp == null) { return(null); } else { var belowThresholdState = TradeOpportunityState.States.Single(x => x.Code == "EXP-BELOW"); newTradeValue.TradeOpportunityStateId = belowThresholdState.Id; await EndTradeOpportunity(tradOpp, belowThresholdState, newTradeValue); return(null); } } // Try getting the TradeOpp from the dictionary. If it doesn't exist, then construct it now var isNewOpportunity = false; var tradeOpp = tradeOpportunities.GetOrAdd(opportunity.UniqueIdentifier, (key) => { isNewOpportunity = true; return(new TradeOpportunity() { CurrencyId = opportunity.OpportunityBaseCurrency.Id, IsExecuted = false, IsSimulation = IsSimulation, StartTime = DateTime.UtcNow, TradeOpportunityTypeId = TradeOpportunityType.Types.Single(x => x.Code == opportunity.TypeCode).Id, TradeOpportunityStateId = TradeOpportunityState.States.Single(x => x.Code == stateCode).Id, Description = opportunity.UniqueIdentifier, LatestOpportunity = opportunity, LastestUpdate = DateTime.UtcNow }); } ); tradeOpp.LatestOpportunity = opportunity; tradeOpp.LastestUpdate = DateTime.UtcNow; // Before writing, we lock the semaphore. await tradeOpp.LockingSemaphore.WaitAsync(); // Lock try { // Otherwise the opportunity is still valid. // If this is a new opportunity, then we definitely need to save to DB. if (isNewOpportunity) { using (var ctx = new RBBotContext()) { requirements.ForEach(x => { x.TradeOpportunity = tradeOpp; tradeOpp.TradeOpportunityRequirements.Add(x); }); tradeOpp.TradeOpportunityValues.Add(newTradeValue); newTradeValue.TradeOpportunity = tradeOpp; ctx.TradeOpportunities.Add(tradeOpp); ctx.TradeOpportunityRequirements.AddRange(requirements); ctx.TradeOpportunityValues.Add(newTradeValue); await ctx.SaveChangesAsync(); } } else { // Else we take the requirements and see if anything changed. var requirementsToAdd = new List <TradeOpportunityRequirement>(); foreach (var req in requirements) { var toReq = tradeOpp.TradeOpportunityRequirements.Where(x => x.ItemIdentifier == req.ItemIdentifier && x.TradeOpportunityRequirementType == req.TradeOpportunityRequirementType).OrderBy(x => x.Timestamp).LastOrDefault(); if (toReq == null || toReq.RequirementMet != req.RequirementMet) { requirementsToAdd.Add(req); req.TradeOpportunityId = tradeOpp.Id; } } newTradeValue.TradeOpportunityId = tradeOpp.Id; using (var ctx = new RBBotContext()) { ctx.TradeOpportunityRequirements.AddRange(requirementsToAdd); ctx.TradeOpportunityValues.Add(newTradeValue); await ctx.SaveChangesAsync(); } } } finally { tradeOpp.LockingSemaphore.Release(); } // Return the trade opportunity object. return(tradeOpp); }