diff --git a/Core/DataObjects/PTMagicData.cs b/Core/DataObjects/PTMagicData.cs index c05e084..bee52ca 100644 --- a/Core/DataObjects/PTMagicData.cs +++ b/Core/DataObjects/PTMagicData.cs @@ -146,6 +146,9 @@ namespace Core.Main.DataObjects.PTMagicData [DefaultValue("")] public string AllowedMarkets { get; set; } = ""; + [DefaultValue(0)] + public int TrendThreshold { get; set; } = 0; + [DefaultValue(true)] public bool ExcludeMainCurrency { get; set; } = true; } diff --git a/Core/Helper/SystemHelper.cs b/Core/Helper/SystemHelper.cs index 73cd7d6..da10013 100644 --- a/Core/Helper/SystemHelper.cs +++ b/Core/Helper/SystemHelper.cs @@ -601,7 +601,7 @@ namespace Core.Helper result = market + mainMarket; break; case "BinanceFutures": - result = market + "_" + mainMarket; + result = market + mainMarket; break; case "Poloniex": result = mainMarket + "_" + market; diff --git a/Core/Main/PTMagic.cs b/Core/Main/PTMagic.cs index 7bdf293..2bffb45 100644 --- a/Core/Main/PTMagic.cs +++ b/Core/Main/PTMagic.cs @@ -1310,7 +1310,7 @@ namespace Core.Main // CoinMarketCap this.GlobalMarketTrendChanges = BaseAnalyzer.BuildMarketTrends("CoinMarketCap", this.LastRuntimeSummary.MainMarket, new List(), "", true, this.GlobalMarketTrendChanges, this.PTMagicConfiguration, this.Log); - // Bittrex + // Exchange foreach (MarketTrend marketTrend in this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.FindAll(mt => mt.Platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase))) { if (this.SingleMarketTrendChanges.ContainsKey(marketTrend.Name)) diff --git a/Core/MarketAnalyzer/BaseAnalyzer.cs b/Core/MarketAnalyzer/BaseAnalyzer.cs index 411f5c9..b60d0f0 100644 --- a/Core/MarketAnalyzer/BaseAnalyzer.cs +++ b/Core/MarketAnalyzer/BaseAnalyzer.cs @@ -18,7 +18,6 @@ namespace Core.MarketAnalyzer public static string GetJsonStringFromURL(string url, LogHelper log, (string header, string value)[] headers = null) { HttpClient webClient = null; - if (webClient == null) { webClient = new HttpClient(); @@ -52,9 +51,7 @@ namespace Core.MarketAnalyzer { // log.DoLogInfo("Calling URL: " + url); var response = webClient.GetAsync(url).Result; - string repsonseString = response.Content.ReadAsStringAsync().Result; - if (response.IsSuccessStatusCode) { return repsonseString; @@ -63,9 +60,7 @@ namespace Core.MarketAnalyzer { // Error var message = string.Format("Error whilst calling {0} - {1}", url, repsonseString); - log.DoLogError(message); - throw new Exception(message); } } @@ -73,13 +68,11 @@ namespace Core.MarketAnalyzer { // Conneciton timeout log.DoLogError(string.Format("Timeout whilst calling {0} - {1}", url, tcEx.Message)); - throw; } catch (Exception ex) { log.DoLogError(string.Format("Error whilst calling {0} \nError: {1}", url, ex.Message)); - throw; } } @@ -87,45 +80,34 @@ namespace Core.MarketAnalyzer public static Dictionary GetJsonFromURL(string url, LogHelper log, (string header, string value)[] headers = null) { Dictionary jsonObject = null; - string jsonString = GetJsonStringFromURL(url, log, headers); // Convert the response to JSON jsonObject = JsonConvert.DeserializeObject>(jsonString); - return jsonObject; } public static Newtonsoft.Json.Linq.JObject GetSimpleJsonObjectFromURL(string url, LogHelper log, (string header, string value)[] headers = null) { Newtonsoft.Json.Linq.JObject jsonObject = null; - string jsonString = GetJsonStringFromURL(url, log, headers); - jsonObject = JsonConvert.DeserializeObject(jsonString); - return jsonObject; } public static List GetSimpleJsonListFromURL(string url, LogHelper log) { List jsonObject = null; - string jsonString = GetJsonStringFromURL(url, log, null); - jsonObject = JsonConvert.DeserializeObject>(jsonString); - return jsonObject; } public static Newtonsoft.Json.Linq.JArray GetSimpleJsonArrayFromURL(string url, LogHelper log) { Newtonsoft.Json.Linq.JArray jsonObject = null; - string jsonString = GetJsonStringFromURL(url, log, null); - jsonObject = JsonConvert.DeserializeObject(jsonString); - return jsonObject; } @@ -138,7 +120,6 @@ namespace Core.MarketAnalyzer string baseUrl = "https://api.github.com/repos/PTMagicians/PTMagic/releases/latest"; Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, new (string header, string value)[] { ("User-Agent", "PTMagic.Import") }); - if (jsonObject != null) { result = jsonObject.GetValue("tag_name").ToString(); @@ -152,14 +133,12 @@ namespace Core.MarketAnalyzer { log.DoLogDebug("GitHub version check error: " + ex.Message); } - return result; } public static double GetMainFiatCurrencyRate(string currency, string FreeCurrencyAPI, LogHelper log) { double result = 1; - string baseUrl = "http://free.currencyconverterapi.com/api/v5/convert?q=USD_" + currency + "&compact=y&apiKey=" + FreeCurrencyAPI; log.DoLogDebug("http://free.currencyconverterapi.com - Getting latest exchange rates..."); @@ -171,7 +150,6 @@ namespace Core.MarketAnalyzer result = (double)jsonObject["USD_" + currency]["val"]; log.DoLogInfo("http://free.currencyconverterapi.com - Latest exchange rate for USD to " + currency + " is " + result); } - return result; } @@ -191,12 +169,10 @@ namespace Core.MarketAnalyzer log.DoLogDebug(ex.Message); } } - if (result == null) { result = new Dictionary(); } - return result; } @@ -208,7 +184,6 @@ namespace Core.MarketAnalyzer public static Dictionary GetMarketDataFromFile(PTMagicConfiguration systemConfiguration, LogHelper log, string platform, DateTime maxDateTime, string marketCaption) { Dictionary result = new Dictionary(); - DirectoryInfo dataDirectory = new DirectoryInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + platform + Path.DirectorySeparatorChar); // Get market files older than max datetime in descending order (newest file up top) @@ -239,7 +214,6 @@ namespace Core.MarketAnalyzer log.DoLogDebug(platform + " - " + marketCaption + " market data loaded (" + marketFile.LastWriteTimeUtc.ToString() + ")"); } } - try { // Get JSON object @@ -249,7 +223,6 @@ namespace Core.MarketAnalyzer { log.DoLogCritical(ex.Message, ex); } - return result; } @@ -312,7 +285,6 @@ namespace Core.MarketAnalyzer { sortedMarkets = new SortedDictionary(recentMarkets).OrderByDescending(m => m.Value.Volume24h); } - int marketCount = 1; foreach (KeyValuePair recentMarketPair in sortedMarkets) { @@ -336,7 +308,6 @@ namespace Core.MarketAnalyzer } Market recentMarket; - if (recentMarkets.TryGetValue(recentMarketPair.Key, out recentMarket)) { List ignoredMarkets = SystemHelper.ConvertTokenStringToList(marketTrend.IgnoredMarkets, ","); @@ -345,7 +316,6 @@ namespace Core.MarketAnalyzer log.DoLogDebug(platform + " - Market trend '" + marketTrend.Name + "' for '" + recentMarketPair.Key + "' is ignored in this trend."); continue; } - List allowedMarkets = SystemHelper.ConvertTokenStringToList(marketTrend.AllowedMarkets, ","); if (allowedMarkets.Count > 0 && !allowedMarkets.Contains(recentMarketPair.Value.Symbol)) { @@ -361,12 +331,10 @@ namespace Core.MarketAnalyzer } Market trendMarket; - if (trendMarkets.TryGetValue(recentMarketPair.Key, out trendMarket)) { double recentMarketPrice = recentMarket.Price; double trendMarketPrice = trendMarket.Price; - if (!platform.Equals("CoinMarketCap", StringComparison.InvariantCulture) && marketTrend.TrendCurrency.Equals("Fiat", StringComparison.InvariantCultureIgnoreCase)) { if (recentMarket.MainCurrencyPriceUSD > 0 && trendMarket.MainCurrencyPriceUSD > 0) @@ -388,9 +356,7 @@ namespace Core.MarketAnalyzer mtc.TrendDateTime = DateTime.UtcNow; result.Add(mtc); - log.DoLogDebug(platform + " - Market trend '" + marketTrend.Name + "' for '" + recentMarketPair.Key + "' (Vol. " + recentMarket.Volume24h.ToString("#,#0.00") + ") is " + trendMarketChange.ToString("#,#0.00") + "% in " + SystemHelper.GetProperDurationTime(marketTrend.TrendMinutes * 60).ToLower() + "."); - marketCount++; } else @@ -401,14 +367,11 @@ namespace Core.MarketAnalyzer } } } - if (marketTrend.MaxMarkets > 0 && isGlobal) { int maxMarkets = (marketTrend.MaxMarkets <= result.Count) ? marketTrend.MaxMarkets : result.Count; - result = result.GetRange(0, maxMarkets); } - return result; } @@ -422,35 +385,49 @@ namespace Core.MarketAnalyzer foreach (MarketTrend marketTrend in marketTrends) { log.DoLogInfo("Building market trend average for '" + marketTrend.Name + "'"); - if (globalMarketTrendChanges.ContainsKey(marketTrend.Name)) { List marketTrendChanges = globalMarketTrendChanges[marketTrend.Name]; if (marketTrendChanges != null && marketTrendChanges.Count > 0) { - - double averageTrendChange = marketTrendChanges.Average(mtc => mtc.TrendChange); - + double totalTrendChange = 0; + int trendChangeCount = marketTrendChanges.Count; + foreach (MarketTrendChange marketTrendChange in marketTrendChanges) + { + if (marketTrend.TrendThreshold != 0) + { + if ((marketTrendChange.TrendChange > marketTrend.TrendThreshold) || (marketTrendChange.TrendChange < (marketTrend.TrendThreshold * -1))) + { + log.DoLogInfo("Market trend '" + marketTrend.Name + "' is ignoring " + marketTrendChange.Market + " for exceeding TrendThreshold."); + trendChangeCount += -1; + } + else + { + totalTrendChange += marketTrendChange.TrendChange; + } + } + else + { + totalTrendChange += marketTrendChange.TrendChange; + } + } + double averageTrendChange = totalTrendChange / trendChangeCount; result.Add(marketTrend.Name, averageTrendChange); - log.DoLogInfo("Built average market trend change '" + marketTrend.Name + "' (" + averageTrendChange.ToString("#,#0.00") + "% in " + marketTrend.TrendMinutes.ToString() + " minutes) for " + marketTrendChanges.Count.ToString() + " markets."); } else { result.Add(marketTrend.Name, 0); - log.DoLogWarn("No market trend changes found for '" + marketTrend.Name + "' - returning 0%"); } } else { result.Add(marketTrend.Name, 0); - log.DoLogWarn("Market trend '" + marketTrend.Name + "' not found in globalMarketTrendChanges[] - returning 0%"); } } } - return result; } } diff --git a/Core/ProfitTrailer/StrategyHelper.cs b/Core/ProfitTrailer/StrategyHelper.cs index 7294cbd..e830c38 100644 --- a/Core/ProfitTrailer/StrategyHelper.cs +++ b/Core/ProfitTrailer/StrategyHelper.cs @@ -19,8 +19,8 @@ namespace Core.ProfitTrailer string strategyLetter = ""; string strategyNameOnly = strategyName; - // PT allows for "advanced_stats" to show details of the trailing logic. - if (result.Contains("STATS")) + // PT allows for "advanced_stats" to show details of the trailing logic and dynamic formulas. + if (result.Contains("STATS") || result.Contains("DYN")) { result = ""; } diff --git a/Monitor/Pages/MarketAnalyzer.cshtml b/Monitor/Pages/MarketAnalyzer.cshtml index 1acb5f6..c7e7e4c 100644 --- a/Monitor/Pages/MarketAnalyzer.cshtml +++ b/Monitor/Pages/MarketAnalyzer.cshtml @@ -162,6 +162,7 @@ else Name Markets Timeframe + Threshold % Change @@ -182,7 +183,14 @@ else @Core.Helper.SystemHelper.SplitCamelCase(marketTrend.Name) @marketCountString - @Core.Helper.SystemHelper.GetProperDurationTime(marketTrend.TrendMinutes * 60, false) + @Core.Helper.SystemHelper.GetProperDurationTime(marketTrend.TrendMinutes * 60, false) @if (marketTrend.TrendThreshold == 0) + { + -- + } + else + { + @marketTrend.TrendThreshold + } @trendChangeOutput% } diff --git a/Monitor/Pages/SettingsAnalyzer.cshtml.cs b/Monitor/Pages/SettingsAnalyzer.cshtml.cs index a6b1a4b..0fd8bb9 100644 --- a/Monitor/Pages/SettingsAnalyzer.cshtml.cs +++ b/Monitor/Pages/SettingsAnalyzer.cshtml.cs @@ -77,6 +77,7 @@ namespace Monitor.Pages mt.TrendCurrency = HttpContext.Request.Form[mtFormKey + "TrendCurrency"]; mt.IgnoredMarkets = HttpContext.Request.Form[mtFormKey + "IgnoredMarkets"]; mt.AllowedMarkets = HttpContext.Request.Form[mtFormKey + "AllowedMarkets"]; + mt.TrendThreshold = SystemHelper.TextToInteger(HttpContext.Request.Form[mtFormKey + "TrendThreshold"], mt.TrendThreshold); mt.DisplayGraph = HttpContext.Request.Form[mtFormKey + "DisplayGraph"].Equals("on"); mt.ExcludeMainCurrency = HttpContext.Request.Form[mtFormKey + "ExcludeMainCurrency"].Equals("on"); diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml b/Monitor/Pages/_get/DashboardBottom.cshtml index 4da9493..6508be7 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml +++ b/Monitor/Pages/_get/DashboardBottom.cshtml @@ -10,7 +10,7 @@ }
-
+
@if (!Model.TrendChartDataJSON.Equals("")) { @@ -48,7 +48,7 @@
-
+
@if (!Model.ProfitChartDataJSON.Equals("")) { @@ -74,6 +74,7 @@ Name Markets Timeframe + Threshold % Change @@ -95,6 +96,14 @@ @Core.Helper.SystemHelper.SplitCamelCase(marketTrend.Name) @marketCountString @Core.Helper.SystemHelper.GetProperDurationTime(marketTrend.TrendMinutes * 60, false) + @if (marketTrend.TrendThreshold == 0) + { + -- + } + else + { + @marketTrend.TrendThreshold + } @trendChangeOutput% } diff --git a/Monitor/Pages/_get/DashboardTop.cshtml b/Monitor/Pages/_get/DashboardTop.cshtml index 22d3ff4..2a2fb03 100644 --- a/Monitor/Pages/_get/DashboardTop.cshtml +++ b/Monitor/Pages/_get/DashboardTop.cshtml @@ -206,28 +206,38 @@ @Html.Raw(buyStrategyText) @Html.Raw(sellStrategyText) - @if (sellStrategyText.Contains("CROSSED")) - // if leverage, recalculate profit target + @if (!sellStrategyText.Contains("WATCHMODE")) { - string leverageText = sellStrategyText.Remove(0, sellStrategyText.IndexOf("CROSSED")+9); - leverage = leverageText.Remove(leverageText.IndexOf(".0)"), leverageText.Length - leverageText.IndexOf(".0)")); - leverageValue = double.Parse(leverage); - } - @if (sellStrategyText.Contains("ISOLATED")) - { - string leverageText = sellStrategyText.Remove(0, sellStrategyText.IndexOf("ISOLATED")+10); - leverage = leverageText.Remove(leverageText.IndexOf(".0)"), leverageText.Length - leverageText.IndexOf(".0)")); - leverageValue = double.Parse(leverage); - } - @if (leverageValue == 1) - { - @Html.Raw(dcaLogEntry.TargetGainValue.HasValue ? dcaLogEntry.TargetGainValue.Value.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%" : " ") + @if (sellStrategyText.Contains("CROSSED")) + // if leverage, recalculate profit target + { + string leverageText = sellStrategyText.Remove(0, sellStrategyText.IndexOf("CROSSED")+9); + leverage = leverageText.Remove(leverageText.IndexOf(".0)"), leverageText.Length - leverageText.IndexOf(".0)")); + leverageValue = double.Parse(leverage); + } + @if (sellStrategyText.Contains("ISOLATED")) + { + string leverageText = sellStrategyText.Remove(0, sellStrategyText.IndexOf("ISOLATED")+10); + leverage = leverageText.Remove(leverageText.IndexOf(".0)"), leverageText.Length - leverageText.IndexOf(".0)")); + leverageValue = double.Parse(leverage); + } + @if (leverageValue == 1) + { + @Html.Raw(dcaLogEntry.TargetGainValue.HasValue ? dcaLogEntry.TargetGainValue.Value.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%" : " ") + } + else + { + double TargetGain = leverageValue * dcaLogEntry.TargetGainValue.Value; + @Html.Raw(dcaLogEntry.TargetGainValue.HasValue ? TargetGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%" : " ") + } } else { - double leverageTargetGain = leverageValue * dcaLogEntry.TargetGainValue.Value; - @Html.Raw(dcaLogEntry.TargetGainValue.HasValue ? leverageTargetGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%" : " ") + } + + + @if (!@lostValue) { @dcaLogEntry.ProfitPercent.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% diff --git a/Monitor/Pages/_get/TickerWidgets.cshtml b/Monitor/Pages/_get/TickerWidgets.cshtml index e7fbae8..fa948f6 100644 --- a/Monitor/Pages/_get/TickerWidgets.cshtml +++ b/Monitor/Pages/_get/TickerWidgets.cshtml @@ -16,17 +16,17 @@ string iconColor = "text-success"; string ptMagicHealthIcon = "fa-heartbeat"; - string ptMagicHealthTooltip = "PT Magic is alive and healthy!"; - if (elapsedSecondsSinceRuntime > (intervalSeconds + intervalSeconds)) { + string ptMagicHealthTooltip = "PT Magic is alive and healthy!
Time elapsed since last run:"+ lastRuntime; + if (elapsedSecondsSinceRuntime > (intervalSeconds * 2)) { ptMagicHealthIcon = "fa-exclamation-triangle"; - ptMagicHealthTooltip = "PT Magic seems to have problems, check the logs!"; + ptMagicHealthTooltip = "PT Magic seems to have problems, check the logs! Time elapsed since last run: "+ Math.Round(elapsedSecondsSinceRuntime / 60, 1) + " mins."; iconColor = "text-danger"; } }
- @Core.Helper.SystemHelper.SplitCamelCase(Model.Summary.CurrentGlobalSetting.SettingName)@Html.Raw(" " + globalSettingInfoIcon) + @Core.Helper.SystemHelper.SplitCamelCase(Model.Summary.CurrentGlobalSetting.SettingName)@Html.Raw(" " + globalSettingInfoIcon)