public string GetDescription() { var description = $"{FundName}({FundCode}) 以 {AvgCost.ToString("F4")} 的价格持有 {Share.ToString("F2")} 份," + $"根据 {NavDate.ToString("yyyy-MM-dd")} 的净值({FundNav.ToString("F4")}) 计算," + $"市值达 {MarketValue.ToString("F2")},盈利 {PositionProfit.ToString("F2")}," + $"实现 {PositionProfitRate.ToString("P")} 收益率"; if (Abilities?.Any() ?? false) { Abilities.ForEach(a => description += $"\n{a.GetDescription()}"); } return(description); }
public async Task <bool> SaveOrUpdateAsync(AvgCost avgCost) { var updOpts = new UpdateOptions { IsUpsert = true }; var updDef = new UpdateDefinitionBuilder <AvgCost>() .SetOnInsert(x => x.Customer, avgCost.Customer) .SetOnInsert(x => x.Symbol, avgCost.Symbol) .Set(x => x.Price, avgCost.Price) .Set(x => x.Quantity, avgCost.Quantity) .PushEach(x => x.History, avgCost.History); var result = await _collection.UpdateOneAsync(x => x.Customer == avgCost.Customer && x.Symbol == avgCost.Symbol, updDef, updOpts); return(result.IsAcknowledged); }
static async Task Main(string[] args) { var container = DependencyInjectionConfig.InitializeContainer(); var repository = container.GetInstance <IAvgCostRepository>(); const string TradesQueueName = "trades_queue"; const string RealTimePubExchange = "realtime_pub_exchange"; var factory = new ConnectionFactory() { HostName = "localhost", UserName = "******", Password = "******" }; var concurrentDictionary = new ConcurrentDictionary <string, AvgCost>(); using var connection = factory.CreateConnection(); using var channel = connection.CreateModel(); var tradesTransformBlock = new TransformBlock <Trade, AvgCost>(trade => { if (concurrentDictionary.TryGetValue(trade.Key(), out var avgCost)) { //Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} {avgCost} {avgCost.Quantity}"); avgCost.Quantity += trade.Quantity; if (avgCost.Quantity == 0) { avgCost.Price = 0; } else if (trade.Quantity > 0 && avgCost.Quantity > 0) { avgCost.Price = Math.Round(((avgCost.Price * avgCost.Quantity + trade.Price * trade.Quantity) / avgCost.Quantity), 6); } avgCost.History = new[] { trade }; concurrentDictionary.TryRemove(avgCost.Key(), out var avgCostOld); concurrentDictionary.TryAdd(avgCost.Key(), avgCost); return(avgCost); } avgCost = new AvgCost { Customer = trade.Client, Quantity = trade.Quantity, Symbol = trade.Symbol, Price = trade.Price, History = new[] { trade } }; concurrentDictionary.TryAdd(avgCost.Key(), avgCost); return(avgCost); }, new ExecutionDataflowBlockOptions { BoundedCapacity = 10000, EnsureOrdered = true }); var linkOptions = new DataflowLinkOptions { PropagateCompletion = true }; var broadcastBlock = new BroadcastBlock <AvgCost>(msg => msg); var pubActionBlock = new ActionBlock <AvgCost>(avgCost => { var basicProps = channel.CreateBasicProperties(); basicProps.Expiration = "10000"; channel.BasicPublish(RealTimePubExchange, $"{avgCost.Customer}", body: Encoding.UTF8.GetBytes(avgCost.ToString()), basicProperties: basicProps); }, new ExecutionDataflowBlockOptions { EnsureOrdered = true }); var batchBlock = new BatchBlock <AvgCost>(1000); var persistMongoActionBlock = new ActionBlock <IEnumerable <AvgCost> >(async avgCosts => await repository.SaveOrUpdateManyAsync(avgCosts)); tradesTransformBlock.LinkTo(broadcastBlock, linkOptions); broadcastBlock.LinkTo(pubActionBlock, linkOptions); broadcastBlock.LinkTo(batchBlock, linkOptions); batchBlock.LinkTo(persistMongoActionBlock, linkOptions); channel.ExchangeDeclare(RealTimePubExchange, ExchangeType.Topic); channel.QueueDeclare(TradesQueueName, true, false, false); channel.BasicQos(0, 1, false); var consumer = new EventingBasicConsumer(channel); consumer.Received += async(model, ea) => { var body = ea.Body; var trade = JsonSerializer.Deserialize <Trade>(Encoding.UTF8.GetString(body)); tradesTransformBlock.Post(trade); channel.BasicAck(ea.DeliveryTag, false); Console.WriteLine("Processed {0}", trade); }; channel.BasicConsume(TradesQueueName, false, consumer); await Task.WhenAll(tradesTransformBlock.Completion, batchBlock.Completion, pubActionBlock.Completion); tradesTransformBlock.Complete(); Console.ReadLine(); }