public override void Save()
        {
            lock (this.SyncRoot)
            {
                try
                {
                    ITradingService tradingService  = Application.Resolve <ITradingService>();
                    string          accountFilePath = Path.Combine(Directory.GetCurrentDirectory(), tradingService.Config.AccountFilePath);

                    var data = new TradingAccountData
                    {
                        Balance      = balance,
                        TradingPairs = tradingPairs,
                    };

                    string accountJson = JsonConvert.SerializeObject(data, Formatting.Indented);
                    var    accountFile = new FileInfo(accountFilePath);
                    accountFile.Directory.Create();
                    File.WriteAllText(accountFile.FullName, accountJson);
                }
                catch (Exception ex)
                {
                    loggingService.Error("Unable to save account data", ex);
                }
            }
        }
        public override void Save()
        {
            lock (this.SyncRoot)
            {
                ITradingService tradingService         = Application.Resolve <ITradingService>();
                string          virtualAccountFilePath = Path.Combine(Directory.GetCurrentDirectory(), tradingService.Config.VirtualAccountFilePath);

                var data = new TradingAccountData
                {
                    Balance      = balance,
                    TradingPairs = tradingPairs
                };

                string virtualAccountJson = JsonConvert.SerializeObject(data, Formatting.Indented);
                var    virtualAccountFile = new FileInfo(virtualAccountFilePath);
                virtualAccountFile.Directory.Create();
                File.WriteAllText(virtualAccountFile.FullName, virtualAccountJson);
            }
        }
        public void Load()
        {
            lock (this.SyncRoot)
            {
                ITradingService tradingService         = Application.Resolve <ITradingService>();
                string          virtualAccountFilePath = Path.Combine(Directory.GetCurrentDirectory(), tradingService.Config.VirtualAccountFilePath);

                if (File.Exists(virtualAccountFilePath))
                {
                    string             virtualAccountJson = File.ReadAllText(virtualAccountFilePath);
                    TradingAccountData virtualAccountData = JsonConvert.DeserializeObject <TradingAccountData>(virtualAccountJson);

                    balance      = virtualAccountData.Balance;
                    tradingPairs = virtualAccountData.TradingPairs;
                }
                else
                {
                    balance      = tradingService.Config.VirtualAccountInitialBalance;
                    tradingPairs = new ConcurrentDictionary <string, TradingPair>();
                }
            }
        }
        public override void Refresh()
        {
            loggingService.Info("Refresh account...");

            decimal        newBalance       = 0;
            var            availableAmounts = new Dictionary <string, decimal>();
            var            availableTrades  = new Dictionary <string, IEnumerable <IOrderDetails> >();
            DateTimeOffset refreshStart     = DateTimeOffset.Now;

            // Preload account data without locking the account
            try
            {
                loggingService.Info("Get account data...");

                foreach (KeyValuePair <string, decimal> kvp in tradingService.Exchange.GetAvailableAmounts())
                {
                    string  currency = kvp.Key;
                    decimal amount   = kvp.Value;
                    string  pair     = currency + tradingService.Config.Market;

                    if (currency == tradingService.Config.Market)
                    {
                        newBalance = amount;
                    }
                    else if (!tradingService.Config.ExcludedPairs.Contains(pair))
                    {
                        try
                        {
                            IEnumerable <IOrderDetails> trades = tradingService.Exchange.GetTrades(pair);
                            availableTrades.Add(pair, trades);
                            availableAmounts.Add(pair, amount);
                        }
                        catch (Exception ex) when(ex.Message != null && ex.Message.Contains("Invalid symbol"))
                        {
                            loggingService.Info($"Skip invalid pair: {pair}");
                        }
                    }
                }

                loggingService.Info("Account data retrieved");
            }
            catch (Exception ex) when(!isInitialRefresh)
            {
                healthCheckService.UpdateHealthCheck(Constants.HealthChecks.AccountRefreshed, ex.Message, true);
                loggingService.Error("Unable to get account data", ex);
                notificationService.Notify("Unable to get account data");
                return;
            }

            // Lock the account and reapply all trades
            try
            {
                lock (this.SyncRoot)
                {
                    ConcurrentDictionary <string, TradingPair> tradingPairsSaved = null;
                    if (isInitialRefresh)
                    {
                        TradingAccountData data = this.LoadSavedData();
                        tradingPairsSaved = data?.TradingPairs ?? new ConcurrentDictionary <string, TradingPair>();
                    }
                    else
                    {
                        tradingPairsSaved = tradingPairs;
                    }
                    tradingPairs = new ConcurrentDictionary <string, TradingPair>();

                    foreach (KeyValuePair <string, IEnumerable <IOrderDetails> > kvp in availableTrades)
                    {
                        string  pair   = kvp.Key;
                        decimal amount = availableAmounts[pair];
                        IEnumerable <IOrderDetails> trades = kvp.Value;

                        foreach (IOrderDetails trade in trades)
                        {
                            if (trade.Date >= tradingService.Config.AccountInitialBalanceDate)
                            {
                                if (trade.Side == OrderSide.Buy)
                                {
                                    this.AddBuyOrder(trade);
                                }
                                else
                                {
                                    ITradeResult tradeResult = this.AddSellOrder(trade);
                                }

                                if (isInitialRefresh)
                                {
                                    tradingService.LogOrder(trade);
                                }
                            }
                        }

                        if (tradingPairs.TryGetValue(pair, out TradingPair tradingPair) && tradingPair.Amount != amount)
                        {
                            loggingService.Info($"Adjust amount for {pair}: {tradingPair.Amount:0.########} => {amount:0.########}");
                            tradingPair.Amount = amount;
                        }
                    }

                    foreach (string pair in tradingPairs.Keys.ToList())
                    {
                        if (tradingPairsSaved.TryGetValue(pair, out TradingPair saved))
                        {
                            tradingPairs[pair].Metadata = saved.Metadata ?? new OrderMetadata();
                        }
                    }

                    balance = newBalance;

                    // Add trades that were completed during account refresh
                    foreach (IOrderDetails order in tradingService.OrderHistory)
                    {
                        if (order.Date > refreshStart)
                        {
                            if (tradingPairs.TryGetValue(order.Pair, out TradingPair tradingPair))
                            {
                                if (!tradingPair.OrderIds.Contains(order.OrderId))
                                {
                                    loggingService.Info($"Add missing order for {order.Pair} ({order.OrderId})");
                                    this.AddOrder(order);
                                }
                            }
                            else
                            {
                                loggingService.Info($"Add missing order for {order.Pair} ({order.OrderId})");
                                this.AddOrder(order);
                            }
                        }
                    }

                    if (isInitialRefresh)
                    {
                        isInitialRefresh = false;
                    }

                    loggingService.Info($"Account refreshed. Balance: {balance}, Trading pairs: {tradingPairs.Count}");
                    healthCheckService.UpdateHealthCheck(Constants.HealthChecks.AccountRefreshed, $"Balance: {balance}, Trading pairs: {tradingPairs.Count}");
                }
            }
            catch (Exception ex)
            {
                tradingPairs.Clear();
                tradingService.SuspendTrading();
                healthCheckService.UpdateHealthCheck(Constants.HealthChecks.AccountRefreshed, ex.Message, true);
                loggingService.Error("Unable to refresh account", ex);
                notificationService.Notify("Unable to refresh account");
            }
        }