public static void GetProfit(ProfitTable profitTable, Shortcut currentlyMinedCoin, out ProfitTableRow maxCoinRow, out decimal maxProfit, out decimal currentProfit, out bool nothingChecked) { maxProfit = 0; maxCoinRow = null; nothingChecked = true; ProfitTableRow currentCoinRow = null; var coinName = currentlyMinedCoin?.GetName(); foreach (var row in profitTable.ProfitList) { if (currentlyMinedCoin != null && row.Name == coinName && row.Path == currentlyMinedCoin?.Path && row.Arguments == currentlyMinedCoin?.Arguments) { currentCoinRow = row; } if (row.Switchable) { nothingChecked = false; } if (row.Switchable && row.ProfitDay > maxProfit) { maxCoinRow = row; maxProfit = row.ProfitDay; } } currentProfit = 0; if (currentlyMinedCoin != null && currentCoinRow != null) { currentProfit = currentCoinRow.ProfitDay; } }
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); } } }
public static List <ProfitTable> CreateProfitTables(Dictionary <string, WtmData> wtmDataDict, List <Worker> workerList, decimal powerCost, WtmSettingsObject settings, bool switchableOnly = false) { var btc = wtmDataDict["Bitcoin"]; var profitList = new List <ProfitTableRow>(); var profitTables = new List <ProfitTable>(); int j = 1; var workerCount = workerList.Count; foreach (var worker in workerList) { foreach (var entry in worker.CoinList) { if (switchableOnly && !entry.Switch) { continue; } var profitResult = CalculateProfit(entry, wtmDataDict, btc, settings, powerCost); decimal profit24 = profitResult.profit24; decimal revenueAllCoins = profitResult.revenue; bool multicell = (entry.Coins.Count > 1); var newRow = new ProfitTableRow { Name = entry.FullName, Symbol = entry.FullSymbol, Algorithm = entry.FullAlgorithm, Hashrate = entry.FullHashrate, Multicell = multicell, Switchable = entry.Switch, Revenue = revenueAllCoins, ProfitDay = profit24, ProfitWeek = profit24 * 7, ProfitMonth = profit24 * 30, ProfitYear = profit24 * 365, Path = entry.Path ?? string.Empty, Arguments = entry.Arguments ?? string.Empty, Notes = entry.Notes, }; profitList.Add(newRow); newRow = null; } var newProfitTable = new ProfitTable { Name = worker.Name, Index = j, ThisPC = Helpers.ListContainsThisPC(worker.Computers), Computers = new ObservableCollection <Computer>(worker?.Computers.Select(computer => new Computer { Name = computer })), Description = worker.Description, ProfitList = profitList.OrderByDescending(p => p.ProfitDay).ToList() }; var firstCoin = newProfitTable.ProfitList.FirstOrDefault(); if (firstCoin != null) { firstCoin.ManualSwitch = true; } //Show the topmost coin as the new coin in Computers list newProfitTable.HookPropertyChanched(); newProfitTable.Row_PropertyChanged(newProfitTable.ProfitList.FirstOrDefault(), new PropertyChangedEventArgs("ManualSwitch")); profitTables.Add(newProfitTable); profitList.Clear(); j++; } return(profitTables); }