private async Task LoadWtmCoins(CancellationToken token = default(CancellationToken))
        {
            List <AlgoCoin> tempCoinList = null;

            switch (Workers.CoinType)
            {
            case "All":
                tempCoinList = await WhatToMine.GetAllWtmSelectableCoins(false, token);

                break;

            case "Active":
                tempCoinList = await WhatToMine.GetAllWtmSelectableCoins(true, token);

                break;

            default:
                tempCoinList = await WhatToMine.GetWtmCoinNames(Workers.CoinType, token);

                break;
            }

            if (tempCoinList == null)
            {
                WtmCoins = null;
                if (!token.IsCancellationRequested)
                {
                    var t = Task.Run(() => MessageBox.Show("Could not download the list of coins from\nwww.whattomine.com", "Error", MessageBoxButton.OK, MessageBoxImage.Error));
                }
            }
            else
            {
                WtmCoins = new ObservableCollection <AlgoCoin>(tempCoinList);
            }
        }
        public static async Task <ObservableCollection <Algorithm> > GetWtmData(CancellationToken token = default(CancellationToken))
        {
            var allCoins = await WhatToMine.GetAllCoinsJson(token);

            if (allCoins == null)
            {
                return(null);
            }
            var sortedByAlgo = new ObservableCollection <Algorithm>();

            foreach (var coin in allCoins)
            {
                var algoName = (string)((Dictionary <string, object>)coin.Value)["algorithm"];
                var newCoin  = new CoinId
                {
                    Id     = (int)((Dictionary <string, object>)coin.Value)["id"],
                    Name   = coin.Key,
                    Symbol = (string)((Dictionary <string, object>)coin.Value)["tag"],
                    Status = (string)((Dictionary <string, object>)coin.Value)["status"],
                };
                newCoin.Show = string.Equals(newCoin.Status, "Active", StringComparison.InvariantCultureIgnoreCase) ? true : false;
                var algo = sortedByAlgo.FirstOrDefault(x => x.Name == algoName);
                if (algo != null)
                {
                    var algoCoin = algo.Coins.FirstOrDefault(x => x.Name == coin.Key);
                    if (algoCoin == null)
                    {
                        algo.Coins.Add(newCoin);
                    }
                }
                else
                {
                    var newAlgo = new Algorithm
                    {
                        Name  = algoName,
                        Coins = new ObservableCollection <CoinId>()
                    };
                    newAlgo.AddCoinsCollectionChanged();
                    newAlgo.Coins.Add(newCoin);
                    sortedByAlgo.Add(newAlgo);
                }
            }
            sortedByAlgo = new ObservableCollection <Algorithm>(sortedByAlgo.OrderBy(x => x.Name));
            return(sortedByAlgo);
        }
        public async Task <HistoricalData> GetNewPriceHistoryRecord(StreamWriter logFile)
        {
            var coinList = Workers.GetCoins(Workers.WorkerList);

            if (!coinList.Contains("Bitcoin"))
            {
                coinList.Add("Bitcoin");
            }

            var coinHashList = GetHashrates(coinList);

            var wtmRequestIntervalOld = WtmSettings.WtmRequestInterval;

            if (WtmSettings.SaveAllCoins)
            {
                var allCoins = await WhatToMine.GetAllWtmCoinNamesFromJson(true);

                var difference = allCoins.Except(coinList);
                foreach (var coin in difference)
                {
                    coinHashList[coin] = 1000;
                }
            }

            // Get WTM coin data
            var wtmDataResult = await WhatToMine.GetWtmCoinData(coinHashList, false, logFile).ConfigureAwait(false);

            if (wtmDataResult.result == WhatToMine.GetWtmCoinDataResult.CoinNotFound)
            {
                return(null);
            }
            var wtmDataDict = wtmDataResult.data;

            if (wtmDataDict != null)
            {
                return(new HistoricalData(DateTime.Now, wtmDataDict));
            }
            else
            {
                return(null);
            }
        }
Beispiel #4
0
        public async Task <SwitchResult> SwitchStandalone()
        {
            var streamServerAddress = BuildServerAddress(WtmSettings.ServerName, Constants.StreamServer);

            using (var logFile = new StreamWriter(Constants.SwitchLog, true))
            {
                if (new FileInfo(Constants.SwitchLog).Length > 0)
                {
                    logFile.WriteLine(string.Empty);
                }
                logFile.WriteLine(DateTime.Now);

                // Respect the DelayNextSwitchTime setting
                if (WtmSettings.DelayNextSwitchTime != 0)
                {
                    var    lastTime    = new DateTime();
                    var    lastTimeObj = GetRegistryKeyValue(Constants.SwitchRegistryKey, "LastSuccess");
                    string lastTimeStr = lastTimeObj as string;
                    if (lastTimeStr != null && lastTimeStr != string.Empty)
                    {
                        lastTime = Convert.ToDateTime(lastTimeStr, DateTimeFormatInfo.InvariantInfo).ToUniversalTime();
                        var now = DateTime.UtcNow;
                        var lastTimePlusDelay = lastTime.AddHours(WtmSettings.DelayNextSwitchTime);
                        if (now < lastTimePlusDelay)
                        {
                            logFile.WriteLine($"Because of the Delay option checked Auto Switch can proceed only after {lastTimePlusDelay.ToLocalTime()}.");
                            return(SwitchResult.DelayIsNotOver);
                        }
                    }
                }

                // Update Workers from server
                if (WtmSettings.ApplicationMode == "Client" && WtmSettings.UpdateWorkersFromServer)
                {
                    await UpdateWorkersFromServer(logFile);
                }

                //Find localhost name in Workers list (used in Standalone and Client modes )
                var    thisPcName = Environment.MachineName;
                Worker thisWorker = GetWorkerByPCName(thisPcName);
                if (thisWorker == null && WtmSettings.ApplicationMode != "Server")
                {
                    string msg = $"{thisPcName} was not found in any worker.";
                    logFile.WriteLine(msg);
                    return(SwitchResult.ThisPcIsNotListed);
                }

                // Get coin names from worker description
                var    coinList      = new List <string>();
                var    allCoinList   = new List <string>();
                var    workersList   = new List <Worker>();
                string noCoinChecked = string.Empty;
                if (thisWorker != null)
                {
                    noCoinChecked = $"No coin is checked as switchable in {thisWorker.Name}.";
                }
                else
                {
                    noCoinChecked = "No coin is checked as switchable.";
                }
                if (WtmSettings.ApplicationMode != "Server") // Standalone or Client mode - just the worker that contains localhost name
                {
                    workersList = new List <Worker> {
                        thisWorker
                    };
                }
                else // Server mode - all workers, all coins
                {
                    foreach (var w in Workers.WorkerList)
                    {
                        workersList.Add(w.Clone());
                    }
                    allCoinList = Workers.GetCoins(workersList, false, false);
                    if (allCoinList == null || allCoinList.Count == 0)
                    {
                        logFile.WriteLine(noCoinChecked);
                        return(SwitchResult.NothingToDo);
                    }
                    if (!allCoinList.Contains("Bitcoin"))
                    {
                        allCoinList.Add("Bitcoin");
                    }
                }
                coinList = Workers.GetCoins(workersList, false, true); // Only coins with Switch checked
                if (coinList == null || coinList.Count == 0)
                {
                    logFile.WriteLine(noCoinChecked);
                    return(SwitchResult.NothingToDo);
                }
                if (!coinList.Contains("Bitcoin"))
                {
                    coinList.Add("Bitcoin");
                }


                // Get WTM coins JSON data
                Dictionary <string, WtmData> wtmDataDict = new Dictionary <string, WtmData>();
                // Attempt to download data from local server
                HistoricalData localDataCopy = null;
                if (WtmSettings.ApplicationMode == "Client")
                {
                    var channel = Service.NewStreamChannel(streamServerAddress, TimeSpan.FromSeconds(60));
                    try
                    {
                        var response = await channel.GetWtmLocalDataAsync();

                        if (response != null)
                        {
                            var memoryStream = new MemoryStream();
                            await response.Stream.CopyToAsync(memoryStream);

                            localDataCopy = NetHelper.DeserializeFromStream <HistoricalData>(memoryStream);
                        }

                        if (localDataCopy == null)
                        {
                            throw new NullReferenceException("Local data from server are null.");
                        }
                    }
                    catch (Exception ex)
                    {
                        logFile.WriteLine($"Failed to download proxy data from local server {WtmSettings.ServerName}. " + ex.Message);
                        if (!WtmSettings.QueryWtmOnLocalServerFail)
                        {
                            return(SwitchResult.NoWtmData);
                        }
                    }
                    finally
                    {
                        NetHelper.CloseChannel(channel);
                    }

                    // Check if the received data are up to date
                    if (localDataCopy != null)
                    {
                        bool dataTimestampGood = EvaluateWtmDataTimeRange(localDataCopy.Date);
                        if (!dataTimestampGood && !WtmSettings.QueryWtmOnLocalServerFail)
                        {
                            logFile.WriteLine("The server cache data is expired."
                                              + " Make sure that AutoSwitch on the client launches later than on the server."
                                              + " For example, if AutoSwitch runs on a daily basis schedule AutoSwitch on the server to run at 9:00 and on the client machines at anyting between 10:00-23:59."
                                              + " This way the cache data obtained in the morning is valid for the rest of the day.");
                            return(SwitchResult.Terminate);
                        }
                        if (dataTimestampGood)
                        {
                            // Filter out coins downloded from server that are not checked for AutoSwitch locally
                            wtmDataDict = localDataCopy.PriceData.Where(x => coinList.Contains(x.Key)).ToDictionary(x => x.Key, x => x.Value);
                        }
                    }
                }
                // Download data from WhatToMine (Standalone or Server)
                if (wtmDataDict.Count == 0)
                {
                    StatusBarText = "Downloading coin definitions from whattomine.com...";
                    (Dictionary <string, WtmData> data, WhatToMine.GetWtmCoinDataResult result)wtmDataResult = (null, WhatToMine.GetWtmCoinDataResult.OK);
                    if (WtmSettings.ApplicationMode == "Server")
                    {
                        var allCoinHashList = GetHashrates(allCoinList);
                        wtmDataResult = await WhatToMine.GetWtmCoinData(allCoinHashList, false, logFile).ConfigureAwait(false);
                    }
                    else
                    {
                        var coinHashList = GetHashrates(coinList);
                        wtmDataResult = await WhatToMine.GetWtmCoinData(coinHashList, false, logFile).ConfigureAwait(false);
                    }
                    if (wtmDataResult.result == WhatToMine.GetWtmCoinDataResult.CoinNotFound)
                    {
                        return(SwitchResult.CoinNotFound);
                    }
                    wtmDataDict = wtmDataResult.data;
                    if (wtmDataDict == null)
                    {
                        return(SwitchResult.CoinNotFound);
                    }
                }

                // Update WTM coin data with historical averages from local database if opted to
                var  historicalDataList     = new List <HistoricalData>();
                bool historicalDataUpToDate = false;
                if (WtmSettings.UseHistoricalAverage)
                {
                    if (WtmSettings.ApplicationMode == "Client")
                    {
                        //Needs longer timeout for the server might be downloading all coins from whattomine.com
                        var channel = Service.NewStreamChannel(streamServerAddress, TimeSpan.FromSeconds(180));

                        try
                        {
                            var response = await channel.GetPriceHistoryAsync(new StreamDownloadRequest { Period = WtmSettings.HistoricalAveragePeriod }).ConfigureAwait(false);

                            if (response != null)
                            {
                                var memoryStream = new MemoryStream();
                                await response.Stream.CopyToAsync(memoryStream);

                                historicalDataList     = NetHelper.DeserializeFromStream <List <HistoricalData> >(memoryStream);
                                historicalDataUpToDate = true;
                            }
                        }
                        catch (Exception ex)
                        {
                            logFile.WriteLine($"Failed to obtain historical prices from local server {WtmSettings.ServerName}. " + ex.Message);
                            return(SwitchResult.Error);
                        }
                        finally
                        {
                            NetHelper.CloseChannel(channel);
                        }
                    }
                    else
                    {
                        var result = await UpdatePriceHistory();

                        if (result == UpdatePriceHistoryResult.Success || result == UpdatePriceHistoryResult.AlreadyUpToDate)
                        {
                            historicalDataList     = ReadHistoricalData(WtmSettings.HistoricalAveragePeriod);
                            historicalDataUpToDate = true;
                        }
                    }
                    // Calculate historical prices for wtmDataList
                    if (historicalDataUpToDate)
                    {
                        GetHistoricalAverages(wtmDataDict, historicalDataList);
                    }
                }

                // Update WtmLocalData if in server mode
                if (WtmSettings.ApplicationMode == "Server")
                {
                    if (wtmDataDict != null && wtmDataDict.Count != 0)
                    {
                        using (var db = new LiteDatabase(Constants.DataBase))
                        {
                            var collection = db.GetCollection <HistoricalData>(Constants.LightDB_WtmCacheCollection);
                            collection.Delete(LiteDB.Query.All());
                            collection.Insert(new HistoricalData(DateTime.Now, wtmDataDict));
                        }
                    }
                }

                // Abandon if in server mode and DontSwitchServer is checked
                if (WtmSettings.ApplicationMode == "Server" && WtmSettings.DontSwitchServer)
                {
                    return(SwitchResult.NoNeedToSwitch);
                }
                if (WtmSettings.ApplicationMode == "Server" && !WtmSettings.DontSwitchServer && thisWorker == null)
                {
                    logFile.WriteLine("The server cannot switch because it is not listed in any worker table.");
                    return(SwitchResult.ThisPcIsNotListed);
                }

                // Calculate profit table for this PC and analize it
                var            profitTables = WhatToMine.CreateProfitTables(wtmDataDict, workersList, (decimal)Workers.PowerCost, WtmSettings, true);
                var            profitTable = profitTables.First();
                var            currentCoinShortcut = Shortcut.GetCurrentCoin(); // Get current coin from Startup folder .lnk
                ProfitTableRow maxCoinRow = null; decimal maxProfit = 0; decimal currentProfit = 0;
                bool           nothingChecked = false;
                WhatToMine.GetProfit(profitTable, currentCoinShortcut, out maxCoinRow, out maxProfit, out currentProfit, out nothingChecked);
                if (nothingChecked)
                {
                    string msg = "Nothing to do. Make sure there are actual switchable coins defined in worker.";
                    logFile.WriteLine(msg);
                    return(SwitchResult.NothingToDo);
                }
                if (maxProfit == 0)
                {
                    string msg = $"There is no coin with profit above 0 for {thisPcName}.";
                    logFile.WriteLine(msg);
                    return(SwitchResult.NothingToDo);
                }

                //Check profitability and price margin
                bool currentCoinMatch             = currentCoinShortcut?.GetName() == maxCoinRow.Name;
                bool currentCoinPathMatch         = currentCoinShortcut?.Path == maxCoinRow.Path;
                bool currentPforitIsHigherOrEqual = maxProfit <= currentProfit;
                bool currentProfitIsWithinMargin  = false;
                if (WtmSettings.PriceMargin > 0)
                {
                    currentProfitIsWithinMargin = maxProfit <= (currentProfit + (currentProfit * WtmSettings.PriceMargin / 100));
                }

                if (currentPforitIsHigherOrEqual)
                {
                    string msg;
                    msg = $"No need to switch. {thisPcName} is already set to mine the most profitable coin {currentCoinShortcut?.GetNameAndSymbol()}.";
                    logFile.WriteLine(msg);
                    return(SwitchResult.NoNeedToSwitch);
                }
                if (currentProfitIsWithinMargin)
                {
                    string msg;
                    if (currentCoinMatch && !currentCoinPathMatch)
                    {
                        msg = $"No need to switch. {thisPcName} is set to mine {currentCoinShortcut?.GetNameAndSymbol()} started by \"{currentCoinShortcut?.Path}\". It is less profitable than {maxCoinRow.NameAndSymbol} started by \"{maxCoinRow.Path}\" but the difference is within price margin.";
                    }
                    else
                    {
                        msg = $"No need to switch. {thisPcName} is set to mine {currentCoinShortcut?.GetNameAndSymbol()}. It is less profitable than {maxCoinRow.NameAndSymbol} but the difference is within price margin.";
                    }
                    logFile.WriteLine(msg);
                    return(SwitchResult.NoNeedToSwitch);
                }

                // Check if executable path exists
                if (!File.Exists(maxCoinRow.Path))
                {
                    logFile.WriteLine($"{maxCoinRow.Path} - file does not exist on {thisPcName}.");
                    return(SwitchResult.CoinNotFound);
                }

                bool startOk = false; // Start miner process flag
                try
                {
                    Shortcut.CreateCoinShortcut(maxCoinRow.Name, maxCoinRow.Symbol, maxCoinRow.Algorithm, maxCoinRow.Path, maxCoinRow.Arguments);
                    if (WtmSettings.RestartComputer)
                    {
                        RestartComputerPending = true;
                    }
                    if (WtmSettings.RestartMiner)
                    {
                        // Kill processes from KillList
                        var killResponse = KillProcesses(WtmSettings.KillList);
                        if (!killResponse.success && killResponse.failList != null)
                        {
                            string errorMessage = $"Startup shortcut to mine {maxCoinRow.NameAndSymbol} has been created but some processes could not be killed:";
                            logFile.WriteLine(errorMessage);
                            foreach (var entry in killResponse.failList)
                            {
                                if (entry != string.Empty)
                                {
                                    logFile.WriteLine(entry);
                                }
                            }
                            logFile.WriteLine($"{Environment.MachineName} will restart.");
                            RestartComputerPending = true;
                            SetRegistryKeyValue(Constants.SwitchRegistryKey, "LastSuccess", DateTime.UtcNow.ToString("o"));
                            return(SwitchResult.SwitchedSuccessfully);
                        }

                        if (!System.IO.File.Exists(maxCoinRow.Path))
                        {
                            logFile.WriteLine($"ERROR: File not found. {maxCoinRow.Path}");
                            return(SwitchResult.Error);
                        }

                        // Start miner process
                        var     startInfo = new ProcessStartInfo(maxCoinRow.Path, maxCoinRow.Arguments);
                        Process process   = new Process();
                        process.StartInfo = startInfo;
                        startOk           = process.Start();
                        if (!startOk)
                        {
                            throw new Exception($"\"{Path.GetFileName(maxCoinRow.Path)}\" failed to start.");
                        }
                    }
                    SetRegistryKeyValue(Constants.SwitchRegistryKey, "LastSuccess", DateTime.UtcNow.ToString("o"));
                    logFile.WriteLine($"Switched to {maxCoinRow.NameAndSymbol} successfully.");
                    return(SwitchResult.SwitchedSuccessfully);
                }
                catch (Exception ex)
                {
                    logFile.WriteLine($"Failed to switch to {maxCoinRow.NameAndSymbol}: {ex.Message}");
                    if (WtmSettings.RestartMiner && !startOk)
                    {
                        RestartComputerPending = true;
                        logFile.WriteLine($"{Environment.MachineName} will restart.");
                    }
                    return(SwitchResult.Error);
                }
            }
        }
Beispiel #5
0
        private async void CalculateProfitCommand(object parameter)
        {
            CalculateProfit.SetCanExecute(false);
            LoadHistoricalCharts.SetCanExecute(false);
            SwitchManually.SetCanExecute(false);

            ProfitTablesEnabled = true;

            var coinList = Workers.GetCoins(Workers.WorkerList, true);

            if (!coinList.Contains("Bitcoin"))
            {
                coinList.Add("Bitcoin");
            }

            //Update Yahoo rates if necessary
            if (WtmSettings.UseYahooRates &&
                WtmSettings.DisplayCurrency != "USD" &&
                WtmSettings.DisplayCurrency != "BTC" &&
                WtmSettingsObject.DisplayCurrencyListDate.Date != DateTime.Today)
            {
                await WtmSettings.GetYahooRates();

                if (WtmSettings.DisplayCurrencyList.Count == 2)
                {
                    BypassUndo(() => WtmSettings.UseYahooRates = false);
                    MessageBox.Show($"{Constants.AppName} could not download the list of currencies from Yahoo. Values will be displayed in USD.", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }

            // Calculate mean hashrate
            var coinHashList = GetHashrates(coinList);

            // Get WTM coin data
            Dictionary <string, WtmData> wtmDataDict = null;

            (Dictionary <string, WtmData> data, WhatToMine.GetWtmCoinDataResult)wtmDataResult = (null, WhatToMine.GetWtmCoinDataResult.Fail);
            try
            {
                wtmDataResult = await WhatToMine.GetWtmCoinData(coinHashList, true);
            }
            catch (Exception ex)
            {
            }
            wtmDataDict = wtmDataResult.data;
            if (wtmDataDict == null)
            {
                CalculateProfitCommandQuit();
                return;
            }


            var    btc = wtmDataDict["Bitcoin"];
            string keyName;

            if (WtmSettings.Average24)
            {
                keyName = "exchange_rate24";
            }
            else
            {
                keyName = "exchange_rate";
            }

            string btcValue;

            btc.Json.TryGetValue(keyName, out btcValue);
            if (btcValue != null)
            {
                WtmSettings.DisplayCurrencyList["BTC"] = Helpers.StringToDecimal(btcValue);
            }
            else
            {
                MessageBox.Show("Failed to read BTC price from whattomine.com", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }

            // Form profit tables from wtmDataList
            ProfitTables.Tables.Clear();
            var workersChecked = Workers.WorkerList.Where(w => w.Query).ToList();
            var profitTables   = WhatToMine.CreateProfitTables(wtmDataDict, workersChecked, (decimal)Workers.PowerCost, WtmSettings);

            foreach (var table in profitTables)
            {
                ProfitTables.Tables.Add(table);
            }

            CalculateProfitCommandQuit();

            ScanLan.RaiseCanExecuteChanged();
            ScanLanCommand(null);
        }
Beispiel #6
0
        public static async Task <(Dictionary <string, WtmData> data, GetWtmCoinDataResult result)> GetWtmCoinData(IDictionary <string, double> coinHashList, bool interactive, StreamWriter logFile = null)
        {
            string statusBarText = "Downloading coin definitions from whattomine.com";

            ViewModel.Instance.StatusBarText = statusBarText + "...";

            int i = 0; int cnt = coinHashList.Count;
            var wtmRequestIntervalOld = ViewModel.Instance.WtmSettings.WtmRequestInterval;

            if (cnt > ViewModel.Instance.WtmSettings.DynamicRequestTrigger)
            {
                ViewModel.Instance.BypassUndo(() => {
                    if (ViewModel.Instance.WtmSettings.WtmRequestInterval < ViewModel.Instance.WtmSettings.DynamicRequestInterval)
                    {
                        ViewModel.Instance.WtmSettings.WtmRequestInterval = ViewModel.Instance.WtmSettings.DynamicRequestInterval;
                    }
                });
            }

            bool errorFlag            = false;
            bool continueFlag         = false;
            bool exitEarly            = false;

            CancellationTokenSource cancelSource = new CancellationTokenSource();
            CancellationToken       token        = cancelSource.Token;
            ProgressManager         pm           = null;


            if (interactive)
            {
                pm = new ProgressManager("Accessing whattomine.com...", cancelSource);
                pm.SetIndeterminate(true);
                pm.SetProgressMaxValue(cnt);
            }

            await RespectTimeLimit();

            var wtmLinks = await WhatToMine.GetWtmLinksFromJson(token).ConfigureAwait(false);

            if (token.IsCancellationRequested)
            {
                pm?.Close();
                return(null, GetWtmCoinDataResult.Fail);
            }
            if (wtmLinks == null)
            {
                string errorMessage = "Failed to get the list of available coins from whattomine.com.";
                if (interactive)
                {
                    pm?.Close();
                    MessageBox.Show(errorMessage, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                }
                else
                {
                    if (logFile != null)
                    {
                        logFile.WriteLine(errorMessage);
                    }
                }
                return(null, GetWtmCoinDataResult.Fail);
            }
            if (interactive && pm != null)
            {
                pm.SetIndeterminate(false);
                pm.SetText("Downloading " + i + " of " + cnt);
                pm.SetProgressValue(0);
            }

            var    wtmDataDict                = new Dictionary <string, WtmData>();
            string currentJsonStr             = string.Empty;
            string currentCoinHtml            = string.Empty;
            GetWtmCoinDataResult methodResult = GetWtmCoinDataResult.OK;

            foreach (var coin in coinHashList)
            {
                continueFlag = false;
                List <string> httpResults = new List <string>();
                WtmLinks      entry;
                wtmLinks.TryGetValue(coin.Key, out entry);
                if (entry == null)
                {
                    string errorMessage = $"{coin.Key} has not been found among coins at http://whattomine.com/calculators. Execution aborted.";
                    errorFlag = true; methodResult = GetWtmCoinDataResult.CoinNotFound;
                    if (interactive)
                    {
                        Helpers.ShowErrorMessage(errorMessage);
                    }
                    else
                    {
                        if (logFile != null)
                        {
                            logFile.WriteLine(errorMessage);
                        }
                    }
                    break;
                }

                //Check whattomine coin status
                if (!string.Equals(entry.Status, "Active", StringComparison.InvariantCultureIgnoreCase))
                {
                    MessageBoxResult response = MessageBoxResult.OK;
                    string           message  = $"The status of {coin.Key} is reported as \"{entry.Status}\" by whattomine.com.";
                    if (interactive)
                    {
                        response = MessageBox.Show(message, "Coin is not active", MessageBoxButton.OKCancel, MessageBoxImage.Warning);
                    }
                    else
                    {
                        logFile.WriteLine(message);
                    }
                    if (interactive && response == MessageBoxResult.Cancel)
                    {
                        errorFlag = true;
                        break;
                    }
                    continue;
                }

                await RespectTimeLimit();

                try
                {
                    var response = await WtmHttpClient.GetAsync(entry.JsonLink + "?hr=" + coin.Value.ToString(CultureInfo.InvariantCulture), token).ConfigureAwait(false);

                    var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                    if (result != null && result.Contains("errors"))
                    {
                        var wtmErrorDict = JsonConverter.ConvertFromJson <Dictionary <string, object> >(result);
                        if (wtmErrorDict != null)
                        {
                            object errorObj = null;
                            wtmErrorDict.TryGetValue("errors", out errorObj);
                            var errorList = errorObj as ArrayList;
                            if (errorList != null)
                            {
                                string errorMessage = string.Join(". ", errorList.ToArray());
                                if (errorMessage != null)
                                {
                                    throw new Exception(errorMessage);
                                }
                            }
                        }
                    }

                    response.EnsureSuccessStatusCode();
                    httpResults.Add(result);
                }
                catch (Exception e)
                {
                    if (interactive && e.Message == "A task was canceled.")
                    {
                        errorFlag = true; methodResult = GetWtmCoinDataResult.Fail;
                    }
                    else
                    {
                        string errorMessage = $"Failed to download {coin.Key} definition from whattomine.com.";
                        //continueFlag = true;
                        errorFlag    = true;
                        methodResult = GetWtmCoinDataResult.Fail;
                        if (interactive)
                        {
                            Helpers.ShowErrorMessage(errorMessage + "\n\n" + e.Message);
                        }
                        else
                        {
                            if (logFile != null)
                            {
                                logFile.WriteLine(errorMessage);
                            }
                        }
                    }
                }

                //if (continueFlag)
                //    continue;

                if (errorFlag || httpResults == null)
                {
                    break;
                }

                // Interpret JSON
                currentJsonStr = httpResults[0];
                Dictionary <string, string> json = new Dictionary <string, string>();
                json = JsonConverter.ConvertFromJson <Dictionary <string, string> >(currentJsonStr);

                if (json == null)
                {
                    string errorMessage = $"Failed to interpret {coin.Key} definition from whattomine.com.";
                    errorFlag = true; methodResult = GetWtmCoinDataResult.Fail;
                    if (interactive)
                    {
                        Helpers.ShowErrorMessage(errorMessage);
                    }
                    else
                    {
                        if (logFile != null)
                        {
                            logFile.WriteLine(errorMessage);
                        }
                    }
                    break;
                }
                else
                {
                    var defaultHashrate = coin.Value;
                    wtmDataDict.Add(coin.Key, new WtmData {
                        DefaultHashrate = defaultHashrate, Json = json
                    });
                }

                if (pm != null && !pm.IsAlive)
                {
                    exitEarly = true;
                    break;
                }

                i++;
                if (interactive && pm != null)
                {
                    pm.SetText("Downloaded " + i + " of " + cnt);
                    pm.SetProgressValue(i);
                }
                else
                {
                    ViewModel.Instance.StatusBarText = statusBarText + ": " + i + " of " + cnt;
                }
            }

            ViewModel.Instance.BypassUndo(() => ViewModel.Instance.WtmSettings.WtmRequestInterval = wtmRequestIntervalOld);
            ViewModel.Instance.UpdateNextJobStatus();

            if (!exitEarly && interactive)
            {
                pm?.Close();
            }
            if (errorFlag || exitEarly)
            {
                return(null, methodResult);
            }
            if (!interactive && (logFile != null))
            {
                var noun = wtmDataDict.Count == 1 ? "coin" : "coins";
                logFile.WriteLine($"The list of {wtmDataDict.Count} whattomine.com {noun} has been downloaded.");
            }
            return(wtmDataDict, GetWtmCoinDataResult.OK);
        }
        private void LoadHistoricalChartsCommand(object obj)
        {
            DateTime FromDate;
            DateTime ToDate;

            using (var db = new LiteDatabase(Constants.DataBase))
            {
                Helpers.MouseCursorWait();
                var collection = db.GetCollection <HistoricalData>(Constants.LightDB_HistoricalDataCollection);
                var allDays    = collection.FindAll();
                if (allDays.Count() == 0)
                {
                    MessageBox.Show("The historical prices database is empty. Set up 'Accumulate Historical Prices', collect some stats for a while and return then.", "Nothing to show", MessageBoxButton.OK, MessageBoxImage.Warning);
                    Helpers.MouseCursorNormal();
                    return;
                }
                var selectDatesWindow = new SelectDateRange();
                var viewModel         = new SelectDateRangeVM(allDays.First().Date, allDays.Last().Date);
                selectDatesWindow.DataContext = viewModel;
                Helpers.MouseCursorNormal();
                var dialogResult = selectDatesWindow.ShowDialog();
                if (dialogResult == false)
                {
                    return;
                }
                FromDate = viewModel.FromDate;
                ToDate   = viewModel.ToDate;
            }

            var workersChecked   = Workers.WorkerList.Where(w => w.Query).ToList();
            var historicalCharts = new Dictionary <string, HistoricalChart>();

            // Create a chart for each worker
            foreach (var worker in workersChecked)
            {
                //var coinListFullName = Workers.GetCoins(new List<Worker> { worker }, false, false, true);
                var chart = new HistoricalChart {
                    Name          = worker.Name,
                    Description   = worker.Description,
                    Computers     = new ObservableCollection <string>(worker.Computers),
                    Coins         = new ObservablePairCollection <string, ChartCoin>(),
                    PlotModel     = new PlotModel(),
                    DisplayCoinAs = Workers.DisplayCoinAs
                };
                chart.HookUpCoinsCollectionChanged();

                // Add AutoSwitch "coin"
                chart.Coins.Add("0, AutoSwitch", new ChartCoin {
                    Color = OxyColors.Red.ToBrush(), IsChecked = true, Name = "AutoSwitch", Symbol = "AutoSwitch"
                });
                var serie = new LineSeries
                {
                    StrokeThickness             = 2,
                    MarkerSize                  = 3,
                    Color                       = OxyColors.Red,
                    MarkerType                  = MarkerType.Circle,
                    CanTrackerInterpolatePoints = false,
                    Title                       = "AutoSwitch",
                    Smooth                      = false,
                    Tag = 0
                };
                chart.PlotModel.Series.Add(serie);


                // Add real coins
                int index = 1;
                foreach (var coinTable in worker.CoinList)
                {
                    var lockExpiry = FromDate.Date.Date.AddHours(WtmSettings.DelayNextSwitchTime);
                    var oxyColor   = ChartColors.Colors[chart.PlotModel.Series.Count % ChartColors.Colors.Count];
                    chart.Coins.Add($"{index}, {coinTable.FullName}", new ChartCoin {
                        Index       = index,
                        IsChecked   = true,
                        LockedUntil = lockExpiry,
                        Color       = oxyColor.ToBrush(),
                        Name        = coinTable.FullName,
                        Symbol      = coinTable.FullSymbol
                    });
                    var newSerie = new LineSeries
                    {
                        StrokeThickness             = 2,
                        MarkerSize                  = 3,
                        Color                       = oxyColor,
                        MarkerType                  = MarkerType.Circle,
                        CanTrackerInterpolatePoints = false,
                        Title                       = coinTable.FullName,
                        Smooth                      = false,
                        Tag = index
                    };
                    chart.PlotModel.Series.Add(newSerie);
                    index++;
                }

                CultureInfo currentCulture = CultureInfo.GetCultureInfo(CultureInfo.CurrentCulture.ToString());
                chart.PlotModel.Axes.Add(new DateTimeAxis
                {
                    IsPanEnabled           = true,
                    IsZoomEnabled          = false,
                    Position               = AxisPosition.Bottom,
                    StringFormat           = currentCulture.DateTimeFormat.ShortDatePattern,
                    MajorGridlineStyle     = LineStyle.Solid,
                    MinorGridlineStyle     = LineStyle.Dot,
                    TextColor              = ChartColors.DarkBlue,
                    TicklineColor          = ChartColors.DarkBlue,
                    MajorGridlineColor     = ChartColors.DarkBlue,
                    MinorGridlineThickness = 0.9,
                    AxislineThickness      = 0.9,
                    MajorGridlineThickness = 0.9,
                    IntervalLength         = 80
                });
                chart.PlotModel.Axes.Add(new LinearAxis
                {
                    IsZoomEnabled          = false,
                    Position               = AxisPosition.Left,
                    Title                  = "Daily profit",
                    Minimum                = 0,
                    TitleColor             = ChartColors.DarkBlue,
                    TextColor              = ChartColors.DarkBlue,
                    TicklineColor          = ChartColors.DarkBlue,
                    MajorGridlineColor     = ChartColors.DarkBlue,
                    MinorTicklineColor     = ChartColors.DarkBlue,
                    MinorGridlineColor     = ChartColors.DarkBlue,
                    MajorGridlineStyle     = LineStyle.Solid,
                    MinorGridlineStyle     = LineStyle.Dot,
                    AxislineThickness      = 0.9,
                    MinorGridlineThickness = 0.9,
                    MajorGridlineThickness = 0.9,
                });

                chart.PlotModel.IsLegendVisible         = false;
                chart.PlotModel.PlotAreaBorderThickness = new OxyThickness(0.9, 0.9, 0.9, 0.9);
                chart.PlotModel.PlotAreaBorderColor     = ChartColors.DarkBlue;
                chart.PlotModel.Padding       = new OxyThickness(0, 10, 0, -10);
                chart.PlotModel.DefaultColors = ChartColors.Colors;

                historicalCharts.Add(worker.Name, chart);
            }

            //Fill PlotModel with data
            using (var db = new LiteDatabase(Constants.DataBase))
            {
                var collection   = db.GetCollection <HistoricalData>(Constants.LightDB_HistoricalDataCollection);
                var selectedDays = collection.Find(x => x.Date >= FromDate && x.Date <= ToDate);

                if (selectedDays.FirstOrDefault() == null)
                {
                    MessageBox.Show("The time selection is missing actual records in database.", "ERROR", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }

                DateTime previousDay = selectedDays.FirstOrDefault().Date.Date;

                foreach (var day in selectedDays)
                {
                    // Check if there's a gap and fill the missing days with blank data
                    var difference = (day.Date.Date - previousDay.Date).Days;
                    if (difference > 1)
                    {
                        for (int i = 1; i <= difference - 1; i++)
                        {
                            foreach (var worker in workersChecked)
                            {
                                var index = 1;
                                var chart = historicalCharts[worker.Name]; // The chart that corresponds to the worker

                                chart.AutoSwitchCoinName = null;
                                var serie = chart.PlotModel.Series.First() as LineSeries;
                                var point = new DataPoint(DateTimeAxis.ToDouble(previousDay.Date.AddDays(i)), 0);
                                serie.Points.Add(point);

                                foreach (var coinTable in worker.CoinList)
                                {
                                    serie = chart.PlotModel.Series.FirstOrDefault(x => (int)x.Tag == index) as LineSeries;
                                    point = new DataPoint(DateTimeAxis.ToDouble(previousDay.Date.AddDays(i)), 0);
                                    serie.Points.Add(point);
                                    index++;
                                }
                            }
                        }
                    }

                    WtmData btc = null;
                    day.PriceData.TryGetValue("Bitcoin", out btc);

                    foreach (var worker in workersChecked)
                    {
                        var chart = historicalCharts[worker.Name]; // The chart that corresponds to the worker
                        var AutoSwitchPseudoCoin = chart.Coins["0, AutoSwitch"];

                        var index = 1;
                        foreach (var coinTable in worker.CoinList)
                        {
                            var currentChartCoin = chart.Coins[$"{index}, {coinTable.FullName}"];
                            if (currentChartCoin.Accumulator == null)
                            {
                                currentChartCoin.Accumulator = new Accumulator(coinTable.Coins);
                            }

                            // Calculate profit

                            var     profitResult = WhatToMine.CalculateProfit(coinTable, day.PriceData, btc, WtmSettings, (decimal)Workers.PowerCost);
                            decimal profit24     = profitResult.profit24;

                            currentChartCoin.TodaysProfit = profit24;

                            // Accumulate daily reward
                            foreach (var coin in coinTable.Coins)
                            {
                                if (day.PriceData.ContainsKey(coin.Name))
                                {
                                    decimal ratio     = (decimal)coin.Hashrate / (decimal)day.PriceData[coin.Name].DefaultHashrate;
                                    var     dayReward = Helpers.StringToDecimal(day.PriceData[coin.Name].Json["estimated_rewards"]) * ratio; // Daily reward in mined coin
                                    dayReward -= dayReward * (decimal)coinTable.Fees / 100;                                                  // Deduct fees
                                    currentChartCoin.Accumulator.AddValue(coin.Name, dayReward);
                                }
                                else
                                {
                                    currentChartCoin.Accumulator.AddValue(coin.Name, 0);
                                }
                            }
                            currentChartCoin.PowerAccumulator += coinTable.Power / 1000 * 24; // Accumulate daily power consumption

                            // Sell accumulated reward if lock is expired
                            bool invalidateCoin = false;
                            if (day.Date.Date >= currentChartCoin.LockedUntil.Date)
                            {
                                decimal btcRate = 0;
                                if (WtmSettings.Average24)
                                {
                                    btcRate = btc != null?Helpers.StringToDecimal(btc.Json["exchange_rate24"]) : 0;
                                }
                                else
                                {
                                    btcRate = btc != null?Helpers.StringToDecimal(btc.Json["exchange_rate"]) : 0;
                                }
                                foreach (var coin in currentChartCoin.Accumulator.CoinList)
                                {
                                    if (day.PriceData.ContainsKey(coin.Name))
                                    {
                                        var coinRate = WtmSettings.Average24 ?
                                                       Helpers.StringToDecimal(day.PriceData[coin.Name].Json["exchange_rate24"]) :
                                                       Helpers.StringToDecimal(day.PriceData[coin.Name].Json["exchange_rate"]);
                                        var revenue = coin.Value * coinRate * btcRate;
                                        currentChartCoin.TotalProfit += revenue;
                                        if (chart.AutoSwitchCoinName == coinTable.FullName)
                                        {
                                            AutoSwitchPseudoCoin.TotalProfit += revenue;
                                        }
                                    }
                                    else
                                    {
                                        invalidateCoin = true;
                                    }
                                }

                                if (!invalidateCoin)
                                {
                                    var powerExpense = (decimal)(currentChartCoin.PowerAccumulator * Workers.PowerCost);
                                    currentChartCoin.TotalProfit -= powerExpense;
                                    if (chart.AutoSwitchCoinName == coinTable.FullName)
                                    {
                                        AutoSwitchPseudoCoin.TotalProfit -= powerExpense;
                                    }
                                }
                                currentChartCoin.Accumulator.Clear();
                                currentChartCoin.PowerAccumulator = 0;
                                currentChartCoin.LockedUntil      = day.Date.Date.AddHours(WtmSettings.DelayNextSwitchTime);
                            }

                            // Add a new point for this day to the Graph
                            var point = new DataPoint(DateTimeAxis.ToDouble(day.Date), (double)profit24);
                            var serie = chart.PlotModel.Series.FirstOrDefault(x => (int)x.Tag == index) as LineSeries;
                            if (serie != null)
                            {
                                serie.Points.Add(point);
                            }

                            index++;
                        }// foreach CoinTable

                        ChartCoin currentAutoSwitchCoin = null;

                        if (chart.AutoSwitchCoinName != null)
                        {
                            currentAutoSwitchCoin = chart.Coins[chart.AutoSwitchCoinName];
                        }

                        // AutoSwitch emulator
                        if (day.Date.Date >= AutoSwitchPseudoCoin.LockedUntil.Date)
                        {
                            var     mostProfitableCoin = chart.GetMostProfitableCoin();
                            decimal maxProfit          = mostProfitableCoin.Value != null ? mostProfitableCoin.Value.TodaysProfit : 0;

                            if (chart.AutoSwitchCoinName == null)
                            {
                                chart.AutoSwitchCoinName = mostProfitableCoin.Key != null ? mostProfitableCoin.Key : null;
                                //AutoSwitchPseudoCoin.TotalProfit = mostProfitableCoin.Value != null ? mostProfitableCoin.Value.TotalProfit : 0;
                                currentAutoSwitchCoin = chart.AutoSwitchCoinName != null ? chart.Coins[chart.AutoSwitchCoinName] : null;
                            }

                            var currentProfit = chart.AutoSwitchCoinName != null ? chart.Coins[chart.AutoSwitchCoinName].TodaysProfit : 0;
                            if (maxProfit > (currentProfit + (currentProfit * WtmSettings.PriceMargin / 100))) // Switch coin in new profit is higher
                            {
                                chart.AutoSwitchCoinName         = mostProfitableCoin.Key != null ? mostProfitableCoin.Key : null;
                                AutoSwitchPseudoCoin.LockedUntil = day.Date.Date.AddHours(WtmSettings.DelayNextSwitchTime);
                            }
                        }

                        var currentAutoSwitchCoinProfit = currentAutoSwitchCoin != null ? currentAutoSwitchCoin.TodaysProfit : 0;
                        AutoSwitchPseudoCoin.TotalProfit += currentAutoSwitchCoinProfit;
                        var autoSwitchPoint = new DataPoint(DateTimeAxis.ToDouble(day.Date), (double)currentAutoSwitchCoinProfit);
                        var autoSwitchSerie = chart.PlotModel.Series.First() as LineSeries;
                        autoSwitchSerie.Points.Add(autoSwitchPoint);
                    } // foreach Worker

                    previousDay = day.Date.Date;
                }// foreach days
            }

            HistoricalCharts.Clear();
            foreach (var chart in historicalCharts)
            {
                HistoricalCharts.Add(chart.Value);
            }

            HistoricalChartsEnabled = true;
            Helpers.MouseCursorWait();
        }