From 0c4f1114684d5c70fbbdeb47ff29b2eed0f070ad Mon Sep 17 00:00:00 2001 From: djbadders <34887832+djbadders@users.noreply.github.com> Date: Sat, 31 Aug 2019 17:14:27 +0100 Subject: [PATCH 01/10] Fix hang when calling Coinbase --- Core/Main/PTMagic.cs | 2 +- Core/MarketAnalyzer/BaseAnalyzer.cs | 1 + PTMagic/Program.cs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Core/Main/PTMagic.cs b/Core/Main/PTMagic.cs index 06070ba..73217da 100644 --- a/Core/Main/PTMagic.cs +++ b/Core/Main/PTMagic.cs @@ -63,7 +63,7 @@ namespace Core.Main private Dictionary> _globalMarketTrendChanges = new Dictionary>(); private Dictionary _singleMarketSettingsCount = new Dictionary(); Dictionary> _triggeredSingleMarketSettings = new Dictionary>(); - private static readonly object _lockObj = new object(); + private static volatile object _lockObj = new object(); public LogHelper Log { diff --git a/Core/MarketAnalyzer/BaseAnalyzer.cs b/Core/MarketAnalyzer/BaseAnalyzer.cs index 70648d8..d2522a6 100644 --- a/Core/MarketAnalyzer/BaseAnalyzer.cs +++ b/Core/MarketAnalyzer/BaseAnalyzer.cs @@ -26,6 +26,7 @@ namespace Core.MarketAnalyzer request.ContentType = "application/json"; request.UserAgent = "PTMagic.Import"; request.KeepAlive = true; + request.Timeout = 60000; HttpWebResponse httpResponse = null; diff --git a/PTMagic/Program.cs b/PTMagic/Program.cs index ffa8652..885fb28 100644 --- a/PTMagic/Program.cs +++ b/PTMagic/Program.cs @@ -7,7 +7,7 @@ using Core.Helper; using Core.Main.DataObjects.PTMagicData; using Microsoft.Extensions.DependencyInjection; -[assembly: AssemblyVersion("2.2.6")] +[assembly: AssemblyVersion("2.2.7")] [assembly: AssemblyProduct("PT Magic")] namespace PTMagic From da511f2edd2648d2720764f7925b9d8fbb0df395 Mon Sep 17 00:00:00 2001 From: djbadders <34887832+djbadders@users.noreply.github.com> Date: Fri, 6 Sep 2019 23:55:41 +0100 Subject: [PATCH 02/10] Fix cleanup of streams for http calls --- Core/MarketAnalyzer/BaseAnalyzer.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Core/MarketAnalyzer/BaseAnalyzer.cs b/Core/MarketAnalyzer/BaseAnalyzer.cs index d2522a6..f6deb22 100644 --- a/Core/MarketAnalyzer/BaseAnalyzer.cs +++ b/Core/MarketAnalyzer/BaseAnalyzer.cs @@ -29,14 +29,17 @@ namespace Core.MarketAnalyzer request.Timeout = 60000; HttpWebResponse httpResponse = null; + string jsonString = string.Empty; try { httpResponse = (HttpWebResponse)request.GetResponse(); - StreamReader jsonReader = new StreamReader(httpResponse.GetResponseStream()); - string jsonString = jsonReader.ReadToEnd(); - jsonReader.Close(); + using (StreamReader jsonReader = new StreamReader(httpResponse.GetResponseStream())) + { + jsonString = jsonReader.ReadToEnd(); + jsonReader.Close(); + } jsonObject = JsonConvert.DeserializeObject>(jsonString); @@ -44,6 +47,7 @@ namespace Core.MarketAnalyzer } catch (WebException ex) { + log.DoLogCritical(string.Format("Error whilst calling {0} \nError: {1}", url, ex.Message), ex); // Error calling the service but we got a response so dump it. string responseString = string.Empty; var encoding = httpResponse.CharacterSet == "" ? Encoding.UTF8 : Encoding.GetEncoding(httpResponse.CharacterSet); From 0c2606609184090e582795ff2bab98f1fc4eb000 Mon Sep 17 00:00:00 2001 From: djbadders <34887832+djbadders@users.noreply.github.com> Date: Sat, 21 Sep 2019 23:43:13 +0100 Subject: [PATCH 03/10] Fixed issues with exception handling for web calls --- Core/MarketAnalyzer/BaseAnalyzer.cs | 45 ++++++++++++++++------------- Core/MarketAnalyzer/Binance.cs | 2 +- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/Core/MarketAnalyzer/BaseAnalyzer.cs b/Core/MarketAnalyzer/BaseAnalyzer.cs index f6deb22..b007201 100644 --- a/Core/MarketAnalyzer/BaseAnalyzer.cs +++ b/Core/MarketAnalyzer/BaseAnalyzer.cs @@ -48,26 +48,31 @@ namespace Core.MarketAnalyzer catch (WebException ex) { log.DoLogCritical(string.Format("Error whilst calling {0} \nError: {1}", url, ex.Message), ex); - // Error calling the service but we got a response so dump it. - string responseString = string.Empty; - var encoding = httpResponse.CharacterSet == "" ? Encoding.UTF8 : Encoding.GetEncoding(httpResponse.CharacterSet); - using (var stream = httpResponse.GetResponseStream()) + if (ex.Response != null) { - var reader = new StreamReader(stream, encoding); - responseString = reader.ReadToEnd(); + // Error calling the service but we got a response so dump it. + string responseString = string.Empty; + var response = ((HttpWebResponse)ex.Response); + var encoding = response.CharacterSet == "" ? Encoding.UTF8 : Encoding.GetEncoding(response.CharacterSet); + + using (var stream = response.GetResponseStream()) + { + var reader = new StreamReader(stream, encoding); + responseString = reader.ReadToEnd(); + } + + log.DoLogCritical(String.Format("{0} - Response: ({1}) {2} : {3}", ex.Message, response.StatusCode, response.StatusDescription, responseString), ex); } - log.DoLogCritical(String.Format("{0} - Response: ({1}) {2} : {3}", ex.Message, httpResponse.StatusCode, httpResponse.StatusDescription, responseString), ex); - - throw ex; + throw; } catch (Exception ex) { log.DoLogCritical(ex.Message, ex); - } - return jsonObject; + throw; + } } public static Newtonsoft.Json.Linq.JObject GetSimpleJsonObjectFromURL(string url, LogHelper log, bool swallowException) @@ -351,15 +356,15 @@ namespace Core.MarketAnalyzer } public static List GetMarketTrendChanges( - string platform, - string mainMarket, - MarketTrend marketTrend, - List marketList, - Dictionary recentMarkets, - Dictionary trendMarkets, - string sortBy, - bool isGlobal, - PTMagicConfiguration systemConfiguration, + string platform, + string mainMarket, + MarketTrend marketTrend, + List marketList, + Dictionary recentMarkets, + Dictionary trendMarkets, + string sortBy, + bool isGlobal, + PTMagicConfiguration systemConfiguration, LogHelper log) { List result = new List(); diff --git a/Core/MarketAnalyzer/Binance.cs b/Core/MarketAnalyzer/Binance.cs index 1475824..7d75e28 100644 --- a/Core/MarketAnalyzer/Binance.cs +++ b/Core/MarketAnalyzer/Binance.cs @@ -416,7 +416,7 @@ namespace Core.MarketAnalyzer Dictionary tickMarkets = new Dictionary(); foreach (string key in markets.Keys) { - List tickRange = marketTicks[key].FindAll(t => t.Time <= tickTime); + List tickRange = marketTicks[key] != null ? marketTicks[key].FindAll(t => t.Time <= tickTime) : new List(); if (tickRange.Count > 0) { From 2db230ac2356e3bb57e0aacf260bd877e67de9bf Mon Sep 17 00:00:00 2001 From: djbadders <34887832+djbadders@users.noreply.github.com> Date: Sun, 13 Oct 2019 16:45:32 +0100 Subject: [PATCH 04/10] Cleaned up and checked compatability with PT 2.4 --- Core/ProfitTrailer/SettingsAPI.cs | 111 +--------------------------- Core/ProfitTrailer/SettingsFiles.cs | 8 -- 2 files changed, 1 insertion(+), 118 deletions(-) diff --git a/Core/ProfitTrailer/SettingsAPI.cs b/Core/ProfitTrailer/SettingsAPI.cs index 4aafa54..dea03b0 100644 --- a/Core/ProfitTrailer/SettingsAPI.cs +++ b/Core/ProfitTrailer/SettingsAPI.cs @@ -1,115 +1,17 @@ using System; using System.Collections.Generic; -using System.Linq; using System.IO; using System.Net; using System.Net.Security; -using System.Security.Cryptography.X509Certificates; -using System.Net.Http; using System.Text; -using System.Threading.Tasks; using Core.Main; using Core.Helper; -using Core.Main.DataObjects.PTMagicData; -using Newtonsoft.Json; namespace Core.ProfitTrailer { public static class SettingsAPI { - public static void GetInitialProfitTrailerSettings(PTMagicConfiguration systemConfiguration) - { - string html = ""; - string url = systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL + "api/data?token=" + systemConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken; - - try - { - HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); - request.AutomaticDecompression = DecompressionMethods.GZip; - - WebResponse response = request.GetResponse(); - Stream dataStream = response.GetResponseStream(); - StreamReader reader = new StreamReader(dataStream); - html = reader.ReadToEnd(); - reader.Close(); - response.Close(); - - } - catch (System.Exception) - { - throw; - } - - dynamic json = JsonConvert.DeserializeObject(html); - - systemConfiguration.GeneralSettings.Application.Exchange = json.exchange; - systemConfiguration.GeneralSettings.Application.TimezoneOffset = json.timeZoneOffset; - systemConfiguration.GeneralSettings.Application.StartBalance = json.startBalance; - systemConfiguration.GeneralSettings.Application.MainFiatCurrency = json.settings.currency; - } - public static List GetPropertyLinesFromAPI(string ptFileName, PTMagicConfiguration systemConfiguration, LogHelper log) - { - List result = null; - - try - { - ServicePointManager.Expect100Continue = true; - ServicePointManager.DefaultConnectionLimit = 9999; - ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; - ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(CertificateHelper.AllwaysGoodCertificate); - - HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL + "settingsapi/settings/load"); - httpWebRequest.ContentType = "application/x-www-form-urlencoded"; - httpWebRequest.Method = "POST"; - - // PT is using ordinary POST data, not JSON - string query = "fileName=" + ptFileName + "&configName=" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "&license=" + systemConfiguration.GeneralSettings.Application.ProfitTrailerLicense; - byte[] formData = Encoding.ASCII.GetBytes(query); - httpWebRequest.ContentLength = formData.Length; - - using (Stream stream = httpWebRequest.GetRequestStream()) - { - stream.Write(formData, 0, formData.Length); - } - - //using (StreamWriter streamWriter = new StreamWriter(httpWebRequest.GetRequestStream())) { - // string json = JsonConvert.SerializeObject(new { - // fileName = ptFileName, - // configName = systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName, - // license = systemConfiguration.GeneralSettings.Application.ProfitTrailerLicense - // }); - - // streamWriter.Write(json); - //} - - HttpWebResponse httpResponse = (HttpWebResponse)httpWebRequest.GetResponse(); - using (StreamReader streamReader = new StreamReader(httpResponse.GetResponseStream())) - { - string jsonResult = streamReader.ReadToEnd(); - result = JsonConvert.DeserializeObject>(jsonResult); - } - } - catch (WebException ex) - { - // Manual error handling as PT doesn't seem to provide a proper error response... - if (ex.Message.IndexOf("401") > -1) - { - log.DoLogError("Loading " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Unauthorized! The specified Profit Trailer license key '" + systemConfiguration.GetProfitTrailerLicenseKeyMasked() + "' is invalid!"); - } - else - { - log.DoLogCritical("Loading " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': " + ex.Message, ex); - } - - } - catch (Exception ex) - { - log.DoLogCritical("Loading " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': " + ex.Message, ex); - } - - return result; - } - + // Save config back to Profit Trailer public static void SendPropertyLinesToAPI(List pairsLines, List dcaLines, List indicatorsLines, PTMagicConfiguration systemConfiguration, LogHelper log) { int retryCount = 0; @@ -148,17 +50,6 @@ namespace Core.ProfitTrailer } log.DoLogDebug("Built POST request for Properties"); - //using (StreamWriter streamWriter = new StreamWriter(httpWebRequest.GetRequestStream())) { - // string json = JsonConvert.SerializeObject(new { - // fileName = ptFileName, - // configName = systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName, - // license = systemConfiguration.GeneralSettings.Application.ProfitTrailerLicense, - // saveData = propertiesString - // }); - - // streamWriter.Write(json); - //} - log.DoLogInfo("Sending Properties..."); HttpWebResponse httpResponse = (HttpWebResponse)httpWebRequest.GetResponse(); log.DoLogInfo("Properties sent!"); diff --git a/Core/ProfitTrailer/SettingsFiles.cs b/Core/ProfitTrailer/SettingsFiles.cs index 9b68e7c..8b7eebc 100644 --- a/Core/ProfitTrailer/SettingsFiles.cs +++ b/Core/ProfitTrailer/SettingsFiles.cs @@ -2,17 +2,9 @@ using System; using System.Collections.Generic; using System.Linq; using System.IO; -using System.Security.Permissions; -using System.Net; -using System.Net.Security; -using System.Security.Cryptography.X509Certificates; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; using Core.Main; using Core.Helper; using Core.Main.DataObjects.PTMagicData; -using Newtonsoft.Json; namespace Core.ProfitTrailer { From 7141b0d2cd124f334bfa1f09beb109bf853e3c56 Mon Sep 17 00:00:00 2001 From: djbadders <34887832+djbadders@users.noreply.github.com> Date: Sun, 13 Oct 2019 18:19:26 +0100 Subject: [PATCH 05/10] Updated to use PT API v2 --- Core/DataObjects/ProfitTrailerData.cs | 95 +++++++++++++-------------- Monitor/_Internal/BasePageModel.cs | 13 +--- 2 files changed, 46 insertions(+), 62 deletions(-) diff --git a/Core/DataObjects/ProfitTrailerData.cs b/Core/DataObjects/ProfitTrailerData.cs index 9e4551a..fb009a6 100644 --- a/Core/DataObjects/ProfitTrailerData.cs +++ b/Core/DataObjects/ProfitTrailerData.cs @@ -4,12 +4,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; -using System.Net.Http; -using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Core.Main.DataObjects.PTMagicData; @@ -31,48 +26,21 @@ namespace Core.Main.DataObjects { _systemConfiguration = systemConfiguration; - string html = ""; - string url = systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL + "api/data?token=" + systemConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken; - - // Get the data from PT - HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); - request.AutomaticDecompression = DecompressionMethods.GZip; - request.KeepAlive = true; - - WebResponse response = request.GetResponse(); - Stream dataStream = response.GetResponseStream(); - StreamReader reader = new StreamReader(dataStream); - html = reader.ReadToEnd(); - reader.Close(); - response.Close(); - - // Parse the JSON and build the data sets - dynamic rawPTData = JObject.Parse(html); - Parallel.Invoke(() => - { - _summary = BuildSummaryData(rawPTData); + { + _summary = BuildSummaryData(GetDataFromProfitTrailer("api/v2/data/misc")); }, () => { - if (rawPTData.sellLogData != null) - { - this.BuildSellLogData(rawPTData.sellLogData, _systemConfiguration); - } + this.BuildSellLogData(GetDataFromProfitTrailer("/api/v2/data/sales")); }, () => { - if (rawPTData.bbBuyLogData != null) - { - this.BuildBuyLogData(rawPTData.bbBuyLogData); - } + this.BuildBuyLogData(GetDataFromProfitTrailer("/api/v2/data/pbl", true)); }, () => { - if (rawPTData.dcaLogData != null) - { - this.BuildDCALogData(rawPTData.dcaLogData, rawPTData.gainLogData, rawPTData.pendingLogData, rawPTData.watchModeLogData, _systemConfiguration); - } + this.BuildDCALogData(GetDataFromProfitTrailer("/api/v2/data/dca", true), GetDataFromProfitTrailer("/api/v2/data/pairs", true), GetDataFromProfitTrailer("/api/v2/data/pending", true), GetDataFromProfitTrailer("/api/v2/data/watchmode", true)); }); // Convert local offset time to UTC @@ -182,8 +150,6 @@ namespace Core.Main.DataObjects (this.Summary.DustValue); } - - public double GetSnapshotBalance(DateTime snapshotDateTime) { double result = _systemConfiguration.GeneralSettings.Application.StartBalance; @@ -197,6 +163,38 @@ namespace Core.Main.DataObjects return result; } + private dynamic GetDataFromProfitTrailer(string callPath, bool arrayReturned = false) + { + string rawBody = ""; + string url = string.Format("{0}{1}?token={2}", _systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL, callPath, _systemConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken); + + // Get the data from PT + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + request.AutomaticDecompression = DecompressionMethods.GZip; + request.KeepAlive = true; + + WebResponse response = request.GetResponse(); + + using (Stream dataStream = response.GetResponseStream()) + { + StreamReader reader = new StreamReader(dataStream); + rawBody = reader.ReadToEnd(); + reader.Close(); + } + + response.Close(); + + // Parse the JSON and build the data sets + if (!arrayReturned) + { + return JObject.Parse(rawBody); + } + else + { + return JArray.Parse(rawBody); + } + } + private SummaryData BuildSummaryData(dynamic PTData) { return new SummaryData() @@ -210,9 +208,9 @@ namespace Core.Main.DataObjects }; } - private void BuildSellLogData(dynamic rawSellLogData, PTMagicConfiguration systemConfiguration) + private void BuildSellLogData(dynamic rawSellLogData) { - foreach (var rsld in rawSellLogData) + foreach (var rsld in rawSellLogData.data) { SellLogData sellLogData = new SellLogData(); sellLogData.SoldAmount = rsld.soldAmount; @@ -236,7 +234,7 @@ namespace Core.Main.DataObjects DateTimeOffset ptSoldDate = DateTimeOffset.Parse(dtDateTime.Year.ToString() + "-" + dtDateTime.Month.ToString("00") + "-" + dtDateTime.Day.ToString("00") + "T" + dtDateTime.Hour.ToString("00") + ":" + dtDateTime.Minute.ToString("00") + ":" + dtDateTime.Second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); // Convert UTC sales time to local offset time - TimeSpan offsetTimeSpan = TimeSpan.Parse(systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); + TimeSpan offsetTimeSpan = TimeSpan.Parse(_systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); ptSoldDate = ptSoldDate.ToOffset(offsetTimeSpan); sellLogData.SoldDate = ptSoldDate.DateTime; @@ -245,7 +243,7 @@ namespace Core.Main.DataObjects } } - private void BuildDCALogData(dynamic rawDCALogData, dynamic rawPairsLogData, dynamic rawPendingLogData, dynamic rawWatchModeLogData, PTMagicConfiguration systemConfiguration) + private void BuildDCALogData(dynamic rawDCALogData, dynamic rawPairsLogData, dynamic rawPendingLogData, dynamic rawWatchModeLogData) { foreach (var rdld in rawDCALogData) { @@ -327,7 +325,7 @@ namespace Core.Main.DataObjects DateTimeOffset ptFirstBoughtDate = DateTimeOffset.Parse(rdldDateTime.Year.ToString() + "-" + rdldDateTime.Month.ToString("00") + "-" + rdldDateTime.Day.ToString("00") + "T" + rdldDateTime.Hour.ToString("00") + ":" + rdldDateTime.Minute.ToString("00") + ":" + rdldDateTime.Second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); // Convert UTC bought time to local offset time - TimeSpan offsetTimeSpan = TimeSpan.Parse(systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); + TimeSpan offsetTimeSpan = TimeSpan.Parse(_systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); ptFirstBoughtDate = ptFirstBoughtDate.ToOffset(offsetTimeSpan); dcaLogData.FirstBoughtDate = ptFirstBoughtDate.DateTime; @@ -387,7 +385,7 @@ namespace Core.Main.DataObjects DateTimeOffset ptFirstBoughtDate = DateTimeOffset.Parse(rpldDateTime.Year.ToString() + "-" + rpldDateTime.Month.ToString("00") + "-" + rpldDateTime.Day.ToString("00") + "T" + rpldDateTime.Hour.ToString("00") + ":" + rpldDateTime.Minute.ToString("00") + ":" + rpldDateTime.Second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); // Convert UTC bought time to local offset time - TimeSpan offsetTimeSpan = TimeSpan.Parse(systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); + TimeSpan offsetTimeSpan = TimeSpan.Parse(_systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); ptFirstBoughtDate = ptFirstBoughtDate.ToOffset(offsetTimeSpan); dcaLogData.FirstBoughtDate = ptFirstBoughtDate.DateTime; @@ -447,7 +445,7 @@ namespace Core.Main.DataObjects DateTimeOffset ptFirstBoughtDate = DateTimeOffset.Parse(rpldDateTime.Year.ToString() + "-" + rpldDateTime.Month.ToString("00") + "-" + rpldDateTime.Day.ToString("00") + "T" + rpldDateTime.Hour.ToString("00") + ":" + rpldDateTime.Minute.ToString("00") + ":" + rpldDateTime.Second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); // Convert UTC bought time to local offset time - TimeSpan offsetTimeSpan = TimeSpan.Parse(systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); + TimeSpan offsetTimeSpan = TimeSpan.Parse(_systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); ptFirstBoughtDate = ptFirstBoughtDate.ToOffset(offsetTimeSpan); dcaLogData.FirstBoughtDate = ptFirstBoughtDate.DateTime; @@ -507,7 +505,7 @@ namespace Core.Main.DataObjects DateTimeOffset ptFirstBoughtDate = DateTimeOffset.Parse(rpldDateTime.Year.ToString() + "-" + rpldDateTime.Month.ToString("00") + "-" + rpldDateTime.Day.ToString("00") + "T" + rpldDateTime.Hour.ToString("00") + ":" + rpldDateTime.Minute.ToString("00") + ":" + rpldDateTime.Second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); // Convert UTC bought time to local offset time - TimeSpan offsetTimeSpan = TimeSpan.Parse(systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); + TimeSpan offsetTimeSpan = TimeSpan.Parse(_systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); ptFirstBoughtDate = ptFirstBoughtDate.ToOffset(offsetTimeSpan); dcaLogData.FirstBoughtDate = ptFirstBoughtDate.DateTime; @@ -519,9 +517,6 @@ namespace Core.Main.DataObjects _dcaLog.Add(dcaLogData); } - - - } private void BuildBuyLogData(dynamic rawBuyLogData) diff --git a/Monitor/_Internal/BasePageModel.cs b/Monitor/_Internal/BasePageModel.cs index 7557f37..14b5cbf 100644 --- a/Monitor/_Internal/BasePageModel.cs +++ b/Monitor/_Internal/BasePageModel.cs @@ -1,7 +1,6 @@ using System; using System.IO; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -10,8 +9,6 @@ using Core.Main; using Core.Helper; using Core.Main.DataObjects.PTMagicData; using Core.MarketAnalyzer; -using Core.ProfitTrailer; -using Microsoft.Extensions.Primitives; using System.Diagnostics; namespace Monitor._Internal @@ -22,7 +19,6 @@ namespace Monitor._Internal public string PTMagicBasePath = ""; public string PTMagicMonitorBasePath = ""; public PTMagicConfiguration PTMagicConfiguration = null; - public Summary Summary { get; set; } = new Summary(); public LogHelper Log = null; public string LatestVersion = ""; @@ -58,14 +54,7 @@ namespace Monitor._Internal PTMagicBasePath += Path.DirectorySeparatorChar; } - try - { - PTMagicConfiguration = new PTMagicConfiguration(PTMagicBasePath); - } - catch (Exception ex) - { - throw ex; - } + PTMagicConfiguration = new PTMagicConfiguration(PTMagicBasePath); IServiceProvider logProvider = ServiceHelper.BuildLoggerService(PTMagicBasePath); Log = logProvider.GetRequiredService(); From 377e443f6263797486076918dbc23bca41624233 Mon Sep 17 00:00:00 2001 From: djbadders <34887832+djbadders@users.noreply.github.com> Date: Sun, 13 Oct 2019 21:08:48 +0100 Subject: [PATCH 06/10] Performance optimised with caching and async loads --- Core/DataObjects/ProfitTrailerData.cs | 120 ++++++++++++++----- Monitor/Pages/BagAnalyzer.cshtml.cs | 2 +- Monitor/Pages/BuyAnalyzer.cshtml.cs | 2 +- Monitor/Pages/DCACalculator.cshtml.cs | 2 +- Monitor/Pages/SalesAnalyzer.cshtml.cs | 2 +- Monitor/Pages/_get/BagDetails.cshtml.cs | 2 +- Monitor/Pages/_get/BagList.cshtml.cs | 2 +- Monitor/Pages/_get/BuyList.cshtml.cs | 2 +- Monitor/Pages/_get/DashboardBottom.cshtml.cs | 2 +- Monitor/Pages/_get/DashboardTop.cshtml | 6 +- Monitor/Pages/_get/DashboardTop.cshtml.cs | 2 +- Monitor/Pages/_get/SalesList.cshtml.cs | 2 +- Monitor/_Internal/BasePageModel.cs | 20 ++++ Monitor/_Internal/BasePageModelSecure.cs | 11 -- 14 files changed, 123 insertions(+), 54 deletions(-) diff --git a/Core/DataObjects/ProfitTrailerData.cs b/Core/DataObjects/ProfitTrailerData.cs index fb009a6..99a206e 100644 --- a/Core/DataObjects/ProfitTrailerData.cs +++ b/Core/DataObjects/ProfitTrailerData.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.Linq; using System.Net; using System.Threading.Tasks; +using System.Diagnostics; using Newtonsoft.Json.Linq; using Core.Main.DataObjects.PTMagicData; @@ -21,28 +22,13 @@ namespace Core.Main.DataObjects private PTMagicConfiguration _systemConfiguration = null; private TransactionData _transactionData = null; private DateTimeOffset _dateTimeNow = Constants.confMinDate; + private DateTime _buyLogRefresh = DateTime.UtcNow, _sellLogRefresh = DateTime.UtcNow, _dcaLogRefresh = DateTime.UtcNow, _summaryRefresh = DateTime.UtcNow; + private volatile object _buyLock = new object(), _sellLock = new object(), _dcaLock = new object(), _summaryLock = new object(); public ProfitTrailerData(PTMagicConfiguration systemConfiguration) { _systemConfiguration = systemConfiguration; - Parallel.Invoke(() => - { - _summary = BuildSummaryData(GetDataFromProfitTrailer("api/v2/data/misc")); - }, - () => - { - this.BuildSellLogData(GetDataFromProfitTrailer("/api/v2/data/sales")); - }, - () => - { - this.BuildBuyLogData(GetDataFromProfitTrailer("/api/v2/data/pbl", true)); - }, - () => - { - this.BuildDCALogData(GetDataFromProfitTrailer("/api/v2/data/dca", true), GetDataFromProfitTrailer("/api/v2/data/pairs", true), GetDataFromProfitTrailer("/api/v2/data/pending", true), GetDataFromProfitTrailer("/api/v2/data/watchmode", true)); - }); - // Convert local offset time to UTC TimeSpan offsetTimeSpan = TimeSpan.Parse(systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); _dateTimeNow = DateTimeOffset.UtcNow.ToOffset(offsetTimeSpan); @@ -52,6 +38,19 @@ namespace Core.Main.DataObjects { get { + if (_summary == null || (DateTime.UtcNow > _summaryRefresh)) + { + lock (_summaryLock) + { + // Thread double locking + if (_summary == null || (DateTime.UtcNow > _summaryRefresh)) + { + _summary = BuildSummaryData(GetDataFromProfitTrailer("api/v2/data/misc")); + _summaryRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + } + } + } + return _summary; } } @@ -59,6 +58,20 @@ namespace Core.Main.DataObjects { get { + if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh)) + { + lock (_sellLock) + { + // Thread double locking + if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh)) + { + _sellLog.Clear(); + this.BuildSellLogData(GetDataFromProfitTrailer("/api/v2/data/sales")); + _sellLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1); + } + } + } + return _sellLog; } } @@ -67,7 +80,7 @@ namespace Core.Main.DataObjects { get { - return _sellLog.FindAll(sl => sl.SoldDate.Date == _dateTimeNow.DateTime.Date); + return SellLog.FindAll(sl => sl.SoldDate.Date == _dateTimeNow.DateTime.Date); } } @@ -75,7 +88,7 @@ namespace Core.Main.DataObjects { get { - return _sellLog.FindAll(sl => sl.SoldDate.Date == _dateTimeNow.DateTime.AddDays(-1).Date); + return SellLog.FindAll(sl => sl.SoldDate.Date == _dateTimeNow.DateTime.AddDays(-1).Date); } } @@ -83,7 +96,7 @@ namespace Core.Main.DataObjects { get { - return _sellLog.FindAll(sl => sl.SoldDate.Date >= _dateTimeNow.DateTime.AddDays(-7).Date); + return SellLog.FindAll(sl => sl.SoldDate.Date >= _dateTimeNow.DateTime.AddDays(-7).Date); } } @@ -91,7 +104,7 @@ namespace Core.Main.DataObjects { get { - return _sellLog.FindAll(sl => sl.SoldDate.Date >= _dateTimeNow.DateTime.AddDays(-30).Date); + return SellLog.FindAll(sl => sl.SoldDate.Date >= _dateTimeNow.DateTime.AddDays(-30).Date); } } @@ -99,6 +112,40 @@ namespace Core.Main.DataObjects { get { + if (_dcaLog == null || (DateTime.UtcNow > _dcaLogRefresh)) + { + lock (_dcaLock) + { + // Thread double locking + if (_dcaLog == null || (DateTime.UtcNow > _dcaLogRefresh)) + { + dynamic dcaData = null, pairsData = null, pendingData = null, watchData = null; + _dcaLog.Clear(); + + Parallel.Invoke(() => + { + dcaData = GetDataFromProfitTrailer("/api/v2/data/dca", true); + }, + () => + { + pairsData = GetDataFromProfitTrailer("/api/v2/data/pairs", true); + }, + () => + { + pendingData = GetDataFromProfitTrailer("/api/v2/data/pending", true); + + }, + () => + { + watchData = GetDataFromProfitTrailer("/api/v2/data/watchmode", true); + }); + + this.BuildDCALogData(dcaData, pairsData, pendingData, watchData); + _dcaLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.BagAnalyzerRefreshSeconds - 1); + } + } + } + return _dcaLog; } } @@ -107,6 +154,20 @@ namespace Core.Main.DataObjects { get { + if (_buyLog == null || (DateTime.UtcNow > _buyLogRefresh)) + { + lock (_buyLock) + { + // Thread double locking + if (_buyLog == null || (DateTime.UtcNow > _buyLogRefresh)) + { + _buyLog.Clear(); + this.BuildBuyLogData(GetDataFromProfitTrailer("/api/v2/data/pbl", true)); + _buyLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds - 1); + } + } + } + return _buyLog; } } @@ -122,31 +183,31 @@ namespace Core.Main.DataObjects public double GetCurrentBalance() { - return + return (this.Summary.Balance + this.Summary.PairsValue + this.Summary.DCAValue + this.Summary.PendingValue + this.Summary.DustValue); } - public double GetPairsBalance() + public double GetPairsBalance() { - return + return (this.Summary.PairsValue); } - public double GetDCABalance() + public double GetDCABalance() { - return + return (this.Summary.DCAValue); } - public double GetPendingBalance() + public double GetPendingBalance() { - return + return (this.Summary.PendingValue); } public double GetDustBalance() { - return + return (this.Summary.DustValue); } @@ -169,6 +230,7 @@ namespace Core.Main.DataObjects string url = string.Format("{0}{1}?token={2}", _systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL, callPath, _systemConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken); // Get the data from PT + Debug.WriteLine(String.Format("{0} - Calling '{1}'", DateTime.UtcNow, url)); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.AutomaticDecompression = DecompressionMethods.GZip; request.KeepAlive = true; diff --git a/Monitor/Pages/BagAnalyzer.cshtml.cs b/Monitor/Pages/BagAnalyzer.cshtml.cs index d45df15..e764992 100644 --- a/Monitor/Pages/BagAnalyzer.cshtml.cs +++ b/Monitor/Pages/BagAnalyzer.cshtml.cs @@ -21,7 +21,7 @@ namespace Monitor.Pages private void BindData() { - PTData = new ProfitTrailerData(PTMagicConfiguration); + PTData = this.PtDataObject; } } } diff --git a/Monitor/Pages/BuyAnalyzer.cshtml.cs b/Monitor/Pages/BuyAnalyzer.cshtml.cs index 6b3eda4..cf52a2b 100644 --- a/Monitor/Pages/BuyAnalyzer.cshtml.cs +++ b/Monitor/Pages/BuyAnalyzer.cshtml.cs @@ -21,7 +21,7 @@ namespace Monitor.Pages private void BindData() { - PTData = new ProfitTrailerData(PTMagicConfiguration); + PTData = this.PtDataObject; } } } diff --git a/Monitor/Pages/DCACalculator.cshtml.cs b/Monitor/Pages/DCACalculator.cshtml.cs index 9292df4..726e380 100644 --- a/Monitor/Pages/DCACalculator.cshtml.cs +++ b/Monitor/Pages/DCACalculator.cshtml.cs @@ -19,7 +19,7 @@ namespace Monitor.Pages private void BindData() { - PTData = new ProfitTrailerData(PTMagicConfiguration); + PTData = this.PtDataObject; } } } diff --git a/Monitor/Pages/SalesAnalyzer.cshtml.cs b/Monitor/Pages/SalesAnalyzer.cshtml.cs index 4e17555..dd8f41a 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml.cs +++ b/Monitor/Pages/SalesAnalyzer.cshtml.cs @@ -30,7 +30,7 @@ namespace Monitor.Pages private void BindData() { - PTData = new ProfitTrailerData(PTMagicConfiguration); + PTData = this.PtDataObject; // Convert local offset time to UTC TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); diff --git a/Monitor/Pages/_get/BagDetails.cshtml.cs b/Monitor/Pages/_get/BagDetails.cshtml.cs index cad873d..b6e651b 100644 --- a/Monitor/Pages/_get/BagDetails.cshtml.cs +++ b/Monitor/Pages/_get/BagDetails.cshtml.cs @@ -24,7 +24,7 @@ namespace Monitor.Pages { private void BindData() { DCAMarket = GetStringParameter("m", ""); - PTData = new ProfitTrailerData(PTMagicConfiguration); + PTData = this.PtDataObject; DCALogData = PTData.DCALog.Find(d => d.Market == DCAMarket); diff --git a/Monitor/Pages/_get/BagList.cshtml.cs b/Monitor/Pages/_get/BagList.cshtml.cs index d4f7e2b..2bd4bf9 100644 --- a/Monitor/Pages/_get/BagList.cshtml.cs +++ b/Monitor/Pages/_get/BagList.cshtml.cs @@ -23,7 +23,7 @@ namespace Monitor.Pages { SortFieldId = GetStringParameter("s", "ProfitPercent"); SortDirection = GetStringParameter("d", "DESC"); - PTData = new ProfitTrailerData(PTMagicConfiguration); + PTData = this.PtDataObject; } } } diff --git a/Monitor/Pages/_get/BuyList.cshtml.cs b/Monitor/Pages/_get/BuyList.cshtml.cs index 431b28d..5ab9180 100644 --- a/Monitor/Pages/_get/BuyList.cshtml.cs +++ b/Monitor/Pages/_get/BuyList.cshtml.cs @@ -23,7 +23,7 @@ namespace Monitor.Pages { SortFieldId = GetStringParameter("s", "ProfitPercent"); SortDirection = GetStringParameter("d", "DESC"); - PTData = new ProfitTrailerData(PTMagicConfiguration); + PTData = this.PtDataObject; } } } diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml.cs b/Monitor/Pages/_get/DashboardBottom.cshtml.cs index 0fc1208..56c8386 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml.cs +++ b/Monitor/Pages/_get/DashboardBottom.cshtml.cs @@ -28,7 +28,7 @@ namespace Monitor.Pages { } private void BindData() { - PTData = new ProfitTrailerData(PTMagicConfiguration); + PTData = this.PtDataObject; // Cleanup temp files FileHelper.CleanupFilesMinutes(PTMagicMonitorBasePath + "wwwroot" + System.IO.Path.DirectorySeparatorChar + "assets" + System.IO.Path.DirectorySeparatorChar + "tmp" + System.IO.Path.DirectorySeparatorChar, 5); diff --git a/Monitor/Pages/_get/DashboardTop.cshtml b/Monitor/Pages/_get/DashboardTop.cshtml index 5b2f611..bc1377f 100644 --- a/Monitor/Pages/_get/DashboardTop.cshtml +++ b/Monitor/Pages/_get/DashboardTop.cshtml @@ -51,9 +51,7 @@ string buyStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Model.Summary, buyLogEntry.BuyStrategies, buyLogEntry.BuyStrategy, isBuyStrategyTrue, isTrailingBuyActive); if (!Core.ProfitTrailer.StrategyHelper.IsValidStrategy(buyStrategyText, true)) { buyDisabled = true; - } - - buyLogEntry.PercChange = @buyLogEntry.PercChange * 100; + } @if (mps == null || mps.ActiveSingleSettings == null || mps.ActiveSingleSettings.Count == 0) { @@ -61,7 +59,7 @@ } else { @buyLogEntry.Market } - @buyLogEntry.PercChange.ToString("#,#0.00")% + @string.Format("{0}%", (buyLogEntry.PercChange * 100).ToString("#,#0.00")) @if (buyDisabled) { @Html.Raw(buyStrategyText) } else { diff --git a/Monitor/Pages/_get/DashboardTop.cshtml.cs b/Monitor/Pages/_get/DashboardTop.cshtml.cs index d13dfe8..9c4824e 100644 --- a/Monitor/Pages/_get/DashboardTop.cshtml.cs +++ b/Monitor/Pages/_get/DashboardTop.cshtml.cs @@ -20,7 +20,7 @@ namespace Monitor.Pages { } private void BindData() { - PTData = new ProfitTrailerData(PTMagicConfiguration); + PTData = this.PtDataObject; // Convert local offset time to UTC TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); diff --git a/Monitor/Pages/_get/SalesList.cshtml.cs b/Monitor/Pages/_get/SalesList.cshtml.cs index e8333cf..12ab483 100644 --- a/Monitor/Pages/_get/SalesList.cshtml.cs +++ b/Monitor/Pages/_get/SalesList.cshtml.cs @@ -29,7 +29,7 @@ namespace Monitor.Pages { salesDateString = GetStringParameter("d", ""); salesMonthString = GetStringParameter("m", ""); - PTData = new ProfitTrailerData(PTMagicConfiguration); + PTData = this.PtDataObject; if (!salesDateString.Equals("")) { SalesDate = SystemHelper.TextToDateTime(salesDateString, Constants.confMinDate); diff --git a/Monitor/_Internal/BasePageModel.cs b/Monitor/_Internal/BasePageModel.cs index 14b5cbf..b3ee870 100644 --- a/Monitor/_Internal/BasePageModel.cs +++ b/Monitor/_Internal/BasePageModel.cs @@ -10,6 +10,7 @@ using Core.Helper; using Core.Main.DataObjects.PTMagicData; using Core.MarketAnalyzer; using System.Diagnostics; +using Core.Main.DataObjects; namespace Monitor._Internal { @@ -28,6 +29,25 @@ namespace Monitor._Internal public string NotifyType = ""; public string MainFiatCurrencySymbol = "$"; + private volatile object _ptDataLock = new object(); + private static ProfitTrailerData _ptData = null; + + // Profit Trailer data accessor object + public ProfitTrailerData PtDataObject + { + get + { + if (_ptData == null) + { + lock (_ptDataLock) + { + _ptData = new ProfitTrailerData(PTMagicConfiguration); + } + } + + return _ptData; + } + } public void PreInit() { diff --git a/Monitor/_Internal/BasePageModelSecure.cs b/Monitor/_Internal/BasePageModelSecure.cs index a1fc1c4..12ff0ee 100644 --- a/Monitor/_Internal/BasePageModelSecure.cs +++ b/Monitor/_Internal/BasePageModelSecure.cs @@ -1,21 +1,10 @@ using System; -using System.IO; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; using Core.Main; using Core.Helper; -using Core.Main.DataObjects.PTMagicData; -using Core.MarketAnalyzer; -using Core.ProfitTrailer; -using Microsoft.Extensions.Primitives; namespace Monitor._Internal { - public class BasePageModelSecure : BasePageModel { public void Init() From 2ea0ec698a6ae152b0fcefca6a4f63d7017f0305 Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Tue, 15 Oct 2019 22:05:53 +0900 Subject: [PATCH 07/10] Bag Totals and Bug Fixes --- Core/DataObjects/ProfitTrailerData.cs | 2 +- Core/ProfitTrailer/StrategyHelper.cs | 2 +- Monitor/Pages/BagAnalyzer.cshtml | 2 +- Monitor/Pages/_get/DashboardTop.cshtml | 13 ++++++++++++- Monitor/Pages/_get/DashboardTop.cshtml.cs | 4 +++- Monitor/wwwroot/assets/js/analyzer-settings.js | 2 +- 6 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Core/DataObjects/ProfitTrailerData.cs b/Core/DataObjects/ProfitTrailerData.cs index 99a206e..e951049 100644 --- a/Core/DataObjects/ProfitTrailerData.cs +++ b/Core/DataObjects/ProfitTrailerData.cs @@ -615,7 +615,7 @@ namespace Core.Main.DataObjects buyStrategy.IsTrue = ((string)(bs.positive)).IndexOf("true", StringComparison.InvariantCultureIgnoreCase) > -1; // Is SOM? - buyLogData.IsSom = buyLogData.IsSom || buyStrategy.Name.Equals("som enabled", StringComparison.OrdinalIgnoreCase); + buyLogData.IsSom = buyLogData.IsSom || buyStrategy.Name.Contains("som enabled", StringComparison.OrdinalIgnoreCase); // Is the pair trailing? buyLogData.IsTrailing = buyLogData.IsTrailing || buyStrategy.IsTrailing; diff --git a/Core/ProfitTrailer/StrategyHelper.cs b/Core/ProfitTrailer/StrategyHelper.cs index ff30efe..7b0fbf1 100644 --- a/Core/ProfitTrailer/StrategyHelper.cs +++ b/Core/ProfitTrailer/StrategyHelper.cs @@ -336,7 +336,7 @@ namespace Core.ProfitTrailer case "anderson": result = String.Concat(strategyLetter, "AND"); break; - case "som enabled": + case "config som enabled": result = String.Concat(strategyLetter, "SOM"); break; case "max buy times": diff --git a/Monitor/Pages/BagAnalyzer.cshtml b/Monitor/Pages/BagAnalyzer.cshtml index e2a3e3f..de6e9a9 100644 --- a/Monitor/Pages/BagAnalyzer.cshtml +++ b/Monitor/Pages/BagAnalyzer.cshtml @@ -30,7 +30,7 @@ Market Trend Amount - Value + Cost DCA Buy Strats BS Value diff --git a/Monitor/Pages/_get/DashboardTop.cshtml b/Monitor/Pages/_get/DashboardTop.cshtml index bc1377f..e41e687 100644 --- a/Monitor/Pages/_get/DashboardTop.cshtml +++ b/Monitor/Pages/_get/DashboardTop.cshtml @@ -94,7 +94,7 @@ Market 24H Trend - Value + Cost DCA Buy Strats Sell Strats @@ -147,6 +147,11 @@ bool lostValue = false; lostValue = (dcaLogEntry.TotalCost == 0.0) || (dcaLogEntry.AverageBuyPrice == 0.0); + // Aggregate totals + Model.TotalBagCost = Model.TotalBagCost + dcaLogEntry.TotalCost; + double TradingFee = (dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) * 0.002; + Model.TotalBagValue = Model.TotalBagValue + ((dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) - TradingFee); + // Render the row @if (mps == null || mps.ActiveSingleSettings == null || mps.ActiveSingleSettings.Count == 0) { @@ -185,6 +190,12 @@ } + + Totals: + @Html.Raw(Model.TotalBagCost.ToString("#,#0.000000", new System.Globalization.CultureInfo("en-US"))) + + @Html.Raw((((Model.TotalBagValue - Model.TotalBagCost) / Model.TotalBagCost) * 100).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))% + diff --git a/Monitor/Pages/_get/DashboardTop.cshtml.cs b/Monitor/Pages/_get/DashboardTop.cshtml.cs index 9c4824e..d01629a 100644 --- a/Monitor/Pages/_get/DashboardTop.cshtml.cs +++ b/Monitor/Pages/_get/DashboardTop.cshtml.cs @@ -11,7 +11,7 @@ namespace Monitor.Pages { public class DashboardTopModel : _Internal.BasePageModelSecureAJAX { public ProfitTrailerData PTData = null; public DateTimeOffset DateTimeNow = Constants.confMinDate; - + public void OnGet() { // Initialize Config base.Init(); @@ -19,6 +19,8 @@ namespace Monitor.Pages { BindData(); } + public double TotalBagCost = 0; + public double TotalBagValue = 0; private void BindData() { PTData = this.PtDataObject; diff --git a/Monitor/wwwroot/assets/js/analyzer-settings.js b/Monitor/wwwroot/assets/js/analyzer-settings.js index 405a6c7..b273f0a 100644 --- a/Monitor/wwwroot/assets/js/analyzer-settings.js +++ b/Monitor/wwwroot/assets/js/analyzer-settings.js @@ -93,7 +93,7 @@ const PropertyTemplate = ({ settingType, settingName, propertyType, propertyKey,
- Any variable from PT's settings may be used! + Any variable from PT's settings may be used!
From aa08050fccf286d8aca0296e6a8f94c527a162eb Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Wed, 16 Oct 2019 10:05:19 +0900 Subject: [PATCH 08/10] Exchange fees --- Monitor/Pages/_get/DashboardTop.cshtml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Monitor/Pages/_get/DashboardTop.cshtml b/Monitor/Pages/_get/DashboardTop.cshtml index e41e687..eca954f 100644 --- a/Monitor/Pages/_get/DashboardTop.cshtml +++ b/Monitor/Pages/_get/DashboardTop.cshtml @@ -149,7 +149,18 @@ // Aggregate totals Model.TotalBagCost = Model.TotalBagCost + dcaLogEntry.TotalCost; - double TradingFee = (dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) * 0.002; + + if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance")) { + double ExchangeFee = 0.002; + } + if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex")) { + double ExchangeFee = 0.0025; + } + if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex")) { + double ExchangeFee = 0.0025; + } + + double TradingFee = (dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) * ExchangeFee; Model.TotalBagValue = Model.TotalBagValue + ((dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) - TradingFee); // Render the row From 24cb40326abe15613f7b0140e7fc322f3123adfc Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Wed, 16 Oct 2019 14:03:19 +0900 Subject: [PATCH 09/10] Added BinanceUS --- Core/Helper/SystemHelper.cs | 6 + Core/Main/PTMagic.cs | 8 +- Core/MarketAnalyzer/BinanceUS.cs | 455 ++++++++++++++++++++++ Monitor/Pages/MarketAnalyzer.cshtml | 26 +- Monitor/Pages/SettingsGeneral.cshtml | 7 + Monitor/Pages/_get/DashboardBottom.cshtml | 2 +- Monitor/Pages/_get/DashboardTop.cshtml | 11 +- 7 files changed, 508 insertions(+), 7 deletions(-) create mode 100644 Core/MarketAnalyzer/BinanceUS.cs diff --git a/Core/Helper/SystemHelper.cs b/Core/Helper/SystemHelper.cs index f576f9f..5d6c4e3 100644 --- a/Core/Helper/SystemHelper.cs +++ b/Core/Helper/SystemHelper.cs @@ -557,6 +557,9 @@ namespace Core.Helper case "Binance": result = "https://www.binance.com/trade.html?symbol=" + market; break; + case "BinanceUS": + result = "https://www.binance.us/trade.html?symbol=" + market; + break; case "Poloniex": result = "https://poloniex.com/exchange#" + market.ToLower(); break; @@ -578,6 +581,9 @@ namespace Core.Helper case "Binance": result = market + mainMarket; break; + case "BinanceUS": + result = market + mainMarket; + break; case "Poloniex": result = mainMarket + "_" + market; break; diff --git a/Core/Main/PTMagic.cs b/Core/Main/PTMagic.cs index 73217da..2c8d2a1 100644 --- a/Core/Main/PTMagic.cs +++ b/Core/Main/PTMagic.cs @@ -695,6 +695,7 @@ namespace Core.Main // Check if exchange is valid if (!this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance", StringComparison.InvariantCultureIgnoreCase) && !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex", StringComparison.InvariantCultureIgnoreCase) + && !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("BinanceUS", StringComparison.InvariantCultureIgnoreCase) && !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex", StringComparison.InvariantCultureIgnoreCase)) { this.Log.DoLogError("Exchange '" + this.PTMagicConfiguration.GeneralSettings.Application.Exchange + "' specified in settings.general.json is invalid! Terminating process..."); @@ -1091,7 +1092,7 @@ namespace Core.Main } else { - if (!this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance", StringComparison.InvariantCultureIgnoreCase) && !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex", StringComparison.InvariantCultureIgnoreCase) && !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex", StringComparison.InvariantCultureIgnoreCase)) + if (!this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance", StringComparison.InvariantCultureIgnoreCase) && !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("BinanceUS", StringComparison.InvariantCultureIgnoreCase) && !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex", StringComparison.InvariantCultureIgnoreCase) && !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex", StringComparison.InvariantCultureIgnoreCase)) { Log.DoLogError("Your setting for Application.Exchange in settings.general.json is invalid (" + this.PTMagicConfiguration.GeneralSettings.Application.Exchange + ")! Terminating process."); this.Timer.Stop(); @@ -1245,6 +1246,11 @@ namespace Core.Main // Get most recent market data from Binance this.ExchangeMarketList = Binance.GetMarketData(this.LastRuntimeSummary.MainMarket, this.MarketInfos, this.PTMagicConfiguration, this.Log); } + else if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("BinanceUS", StringComparison.InvariantCultureIgnoreCase)) + { + // Get most recent market data from BinanceUS + this.ExchangeMarketList = BinanceUS.GetMarketData(this.LastRuntimeSummary.MainMarket, this.MarketInfos, this.PTMagicConfiguration, this.Log); + } else if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex", StringComparison.InvariantCultureIgnoreCase)) { diff --git a/Core/MarketAnalyzer/BinanceUS.cs b/Core/MarketAnalyzer/BinanceUS.cs new file mode 100644 index 0000000..d13fe9f --- /dev/null +++ b/Core/MarketAnalyzer/BinanceUS.cs @@ -0,0 +1,455 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Text; +using Core.Main; +using Core.Helper; +using Core.Main.DataObjects.PTMagicData; +using Newtonsoft.Json; +using Core.ProfitTrailer; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Concurrent; + +namespace Core.MarketAnalyzer +{ + public class BinanceUS : BaseAnalyzer + { + public static double GetMainCurrencyPrice(string mainMarket, PTMagicConfiguration systemConfiguration, LogHelper log) + { + double result = 0; + + try + { + string baseUrl = "https://api.binance.us/api/v1/ticker/24hr?symbol=" + mainMarket + "USDT"; + + log.DoLogInfo("BinanceUS - Getting main market price..."); + Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, false); + if (jsonObject != null) + { + log.DoLogInfo("BinanceUS - Market data received for " + mainMarket + "USDT"); + + result = (double)jsonObject.GetValue("lastPrice"); + log.DoLogInfo("BinanceUS - Current price for " + mainMarket + "USDT: " + result.ToString("#,#0.00") + " USD"); + } + } + catch (Exception ex) + { + log.DoLogCritical(ex.Message, ex); + } + + return result; + } + + public static List GetMarketData(string mainMarket, Dictionary marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) + { + List result = new List(); + + string lastMarket = ""; + Newtonsoft.Json.Linq.JObject lastTicker = null; + try + { + string baseUrl = "https://api.binance.us/api/v1/ticker/24hr"; + + log.DoLogInfo("BinanceUS - Getting market data..."); + Newtonsoft.Json.Linq.JArray jsonArray = GetSimpleJsonArrayFromURL(baseUrl, log); + if (jsonArray.Count > 0) + { + double mainCurrencyPrice = 1; + if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)) + { + mainCurrencyPrice = BinanceUS.GetMainCurrencyPrice(mainMarket, systemConfiguration, log); + } + + log.DoLogInfo("BinanceUS - Market data received for " + jsonArray.Count.ToString() + " currencies"); + + if (mainCurrencyPrice > 0) + { + Dictionary markets = new Dictionary(); + foreach (Newtonsoft.Json.Linq.JObject currencyTicker in jsonArray) + { + string marketName = currencyTicker["symbol"].ToString(); + //New variables for filtering out bad markets + float marketLastPrice = currencyTicker["lastPrice"].ToObject(); + float marketVolume = currencyTicker["volume"].ToObject(); + if (marketName.EndsWith(mainMarket, StringComparison.InvariantCultureIgnoreCase)) + { + if (marketLastPrice > 0 && marketVolume > 0) + { + + // Set last values in case any error occurs + lastMarket = marketName; + lastTicker = currencyTicker; + + Market market = new Market(); + market.Position = markets.Count + 1; + market.Name = marketName; + market.Symbol = currencyTicker["symbol"].ToString(); + market.Price = SystemHelper.TextToDouble(currencyTicker["lastPrice"].ToString(), 0, "en-US"); + market.Volume24h = SystemHelper.TextToDouble(currencyTicker["quoteVolume"].ToString(), 0, "en-US"); + market.MainCurrencyPriceUSD = mainCurrencyPrice; + + markets.Add(market.Name, market); + + result.Add(market.Name); + } + else + { + //Let the user know that the problem market was ignored. + log.DoLogInfo("BinanceUS - Ignoring bad market data for " + marketName); + } + } + } + + BinanceUS.CheckFirstSeenDates(markets, ref marketInfos, systemConfiguration, log); + + BaseAnalyzer.SaveMarketInfosToFile(marketInfos, systemConfiguration, log); + + BinanceUS.CheckForMarketDataRecreation(mainMarket, markets, systemConfiguration, log); + + DateTime fileDateTime = DateTime.UtcNow; + + FileHelper.WriteTextToFile(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar, "MarketData_" + fileDateTime.ToString("yyyy-MM-dd_HH.mm") + ".json", JsonConvert.SerializeObject(markets), fileDateTime, fileDateTime); + + log.DoLogInfo("BinanceUS - Market data saved for " + markets.Count.ToString() + " markets with " + mainMarket + "."); + + FileHelper.CleanupFiles(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar, systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours); + log.DoLogInfo("BinanceUS - Market data cleaned."); + } + else + { + log.DoLogError("BinanceUS - Failed to get main market price for " + mainMarket + "."); + result = null; + } + } + } + catch (WebException ex) + { + if (ex.Response != null) + { + using (HttpWebResponse errorResponse = (HttpWebResponse)ex.Response) + { + using (StreamReader reader = new StreamReader(errorResponse.GetResponseStream())) + { + Dictionary errorData = JsonConvert.DeserializeObject>(reader.ReadToEnd()); + if (errorData != null) + { + string errorMessage = "Unable to get data from BinanceUS with URL '" + errorResponse.ResponseUri + "'!"; + if (errorData.ContainsKey("code")) + { + errorMessage += " - Code: " + errorData["code"]; + } + + if (errorData.ContainsKey("msg")) + { + errorMessage += " - Message: " + errorData["msg"]; + } + + log.DoLogError(errorMessage); + } + } + } + } + result = null; + } + catch (Exception ex) + { + log.DoLogCritical("Exception while getting data for '" + lastMarket + "': " + ex.Message, ex); + result = null; + } + + return result; + } + + public static void CheckFirstSeenDates(Dictionary markets, ref Dictionary marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) + { + log.DoLogInfo("BinanceUS - Checking first seen dates for " + markets.Count + " markets. This may take a while..."); + + int marketsChecked = 0; + + foreach (string key in markets.Keys) + { + // Save market info + MarketInfo marketInfo = null; + if (marketInfos.ContainsKey(key)) + { + marketInfo = marketInfos[key]; + } + + if (marketInfo == null) + { + marketInfo = new MarketInfo(); + marketInfo.Name = key; + marketInfos.Add(key, marketInfo); + marketInfo.FirstSeen = BinanceUS.GetFirstSeenDate(key, systemConfiguration, log); + } + else + { + if (marketInfo.FirstSeen == Constants.confMinDate) + { + marketInfo.FirstSeen = BinanceUS.GetFirstSeenDate(key, systemConfiguration, log); + } + } + marketInfo.LastSeen = DateTime.UtcNow; + + marketsChecked++; + + if ((marketsChecked % 20) == 0) + { + log.DoLogInfo("BinanceUS - Yes, I am still checking first seen dates... " + marketsChecked + "/" + markets.Count + " markets done..."); + } + } + } + + public static DateTime GetFirstSeenDate(string marketName, PTMagicConfiguration systemConfiguration, LogHelper log) + { + DateTime result = Constants.confMinDate; + + string baseUrl = "https://api.binance.us/api/v1/klines?interval=1d&symbol=" + marketName + "&limit=100"; + + log.DoLogDebug("BinanceUS - Getting first seen date for '" + marketName + "'..."); + + Newtonsoft.Json.Linq.JArray jsonArray = GetSimpleJsonArrayFromURL(baseUrl, log); + if (jsonArray.Count > 0) + { + result = Constants.Epoch.AddMilliseconds((Int64)jsonArray[0][0]); + log.DoLogDebug("BinanceUS - First seen date for '" + marketName + "' set to " + result.ToString()); + } + + return result; + } + + public static List GetMarketTicks(string marketName, int ticksNeeded, PTMagicConfiguration systemConfiguration, LogHelper log) + { + List result = new List(); + + try + { + Int64 endTime = (Int64)Math.Ceiling(DateTime.UtcNow.Subtract(Constants.Epoch).TotalMilliseconds); + int ticksLimit = 500; + string baseUrl = ""; + int ticksFetched = 0; + + if (ticksNeeded < ticksLimit) + { + ticksLimit = ticksNeeded; + } + + bool go = true; + while (ticksFetched < ticksNeeded && go) + { + baseUrl = "https://api.binance.us/api/v1/klines?interval=1m&symbol=" + marketName + "&endTime=" + endTime.ToString() + "&limit=" + ticksLimit.ToString(); + + log.DoLogDebug("BinanceUS - Getting " + ticksLimit.ToString() + " ticks for '" + marketName + "'..."); + Newtonsoft.Json.Linq.JArray jsonArray = GetSimpleJsonArrayFromURL(baseUrl, log); + if (jsonArray.Count > 0) + { + log.DoLogDebug("BinanceUS - " + jsonArray.Count.ToString() + " ticks received."); + + foreach (Newtonsoft.Json.Linq.JArray marketTick in jsonArray) + { + + MarketTick tick = new MarketTick(); + tick.Price = (double)marketTick[4]; + tick.Volume24h = (double)marketTick[7]; + tick.Time = Constants.Epoch.AddMilliseconds((Int64)marketTick[0]); + + result.Add(tick); + } + + ticksFetched = ticksFetched + jsonArray.Count; + endTime = endTime - ticksLimit * 60 * 1000; + if (ticksNeeded - ticksFetched < ticksLimit) + { + ticksLimit = ticksNeeded - ticksFetched; + } + } + else + { + log.DoLogDebug("BinanceUS - No ticks received."); + go = false; + } + } + } + catch (WebException ex) + { + if (ex.Response != null) + { + using (HttpWebResponse errorResponse = (HttpWebResponse)ex.Response) + { + using (StreamReader reader = new StreamReader(errorResponse.GetResponseStream())) + { + Dictionary errorData = JsonConvert.DeserializeObject>(reader.ReadToEnd()); + if (errorData != null) + { + string errorMessage = "Unable to get data from BinanceUS with URL '" + errorResponse.ResponseUri + "'!"; + if (errorData.ContainsKey("code")) + { + errorMessage += " - Code: " + errorData["code"]; + } + + if (errorData.ContainsKey("msg")) + { + errorMessage += " - Message: " + errorData["msg"]; + } + + log.DoLogError(errorMessage); + } + } + } + } + result = null; + } + catch (Exception ex) + { + log.DoLogCritical(ex.Message, ex); + } + + return result; + } + + public static void CheckForMarketDataRecreation(string mainMarket, Dictionary markets, PTMagicConfiguration systemConfiguration, LogHelper log) + { + string binanceUSDataDirectoryPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar; + + if (!Directory.Exists(binanceUSDataDirectoryPath)) + { + Directory.CreateDirectory(binanceUSDataDirectoryPath); + } + + DirectoryInfo dataDirectory = new DirectoryInfo(binanceUSDataDirectoryPath); + + // Check for existing market files + DateTime latestMarketDataFileDateTime = Constants.confMinDate; + List marketFiles = dataDirectory.EnumerateFiles("MarketData*").ToList(); + FileInfo latestMarketDataFile = null; + if (marketFiles.Count > 0) + { + latestMarketDataFile = marketFiles.OrderByDescending(mdf => mdf.LastWriteTimeUtc).First(); + latestMarketDataFileDateTime = latestMarketDataFile.LastWriteTimeUtc; + } + + if (latestMarketDataFileDateTime < DateTime.UtcNow.AddMinutes(-20)) + { + int lastMarketDataAgeInSeconds = (int)Math.Ceiling(DateTime.UtcNow.Subtract(latestMarketDataFileDateTime).TotalSeconds); + + // Go back in time and create market data + DateTime startDateTime = DateTime.UtcNow; + DateTime endDateTime = DateTime.UtcNow.AddHours(-systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours); + if (latestMarketDataFileDateTime != Constants.confMinDate && latestMarketDataFileDateTime > endDateTime) + { + // Existing market files too old => Recreate market data for configured timeframe + log.DoLogInfo("BinanceUS - Recreating market data for " + markets.Count + " markets over " + SystemHelper.GetProperDurationTime(lastMarketDataAgeInSeconds) + ". This may take a while..."); + endDateTime = latestMarketDataFileDateTime; + } + else + { + // No existing market files found => Recreate market data for configured timeframe + log.DoLogInfo("BinanceUS - Recreating market data for " + markets.Count + " markets over " + systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours + " hours. This may take a while..."); + } + + int totalTicks = (int)Math.Ceiling(startDateTime.Subtract(endDateTime).TotalMinutes); + + // Get Ticks for main market + List mainMarketTicks = new List(); + if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)) + { + mainMarketTicks = BinanceUS.GetMarketTicks(mainMarket + "USDT", totalTicks, systemConfiguration, log); + } + + // Get Ticks for all markets + log.DoLogDebug("BinanceUS - Getting ticks for '" + markets.Count + "' markets"); + ConcurrentDictionary> marketTicks = new ConcurrentDictionary>(); + + int ParallelThrottle = 4; + if (systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours > 200) + { + ParallelThrottle = 2; + log.DoLogInfo("----------------------------------------------------------------------------"); + log.DoLogInfo("StoreDataMaxHours is greater than 200. Historical data requests will be"); + log.DoLogInfo("throttled to avoid exceeding exchange data request limits. This initial "); + log.DoLogInfo("run could take more than 30 minutes. Please go outside for a walk..."); + log.DoLogInfo("----------------------------------------------------------------------------"); + } + + Parallel.ForEach(markets.Keys, + new ParallelOptions { MaxDegreeOfParallelism = ParallelThrottle}, + (key) => + { + if (!marketTicks.TryAdd(key, GetMarketTicks(key, totalTicks, systemConfiguration, log))) + { + // Failed to add ticks to dictionary + throw new Exception("Failed to add ticks for " + key + " to the memory dictionary, results may be incorrectly calculated!"); + } + + if ((marketTicks.Count % 10) == 0) + { + log.DoLogInfo("BinanceUS - No worries, I am still alive... " + marketTicks.Count + "/" + markets.Count + " markets done..."); + } + }); + + log.DoLogInfo("BinanceUS - Ticks completed."); + + log.DoLogInfo("BinanceUS - Creating initial market data ticks. This may take another while..."); + + // Go back in time and create market data + int completedTicks = 0; + if (marketTicks.Count > 0) + { + for (DateTime tickTime = startDateTime; tickTime >= endDateTime; tickTime = tickTime.AddMinutes(-1)) + { + completedTicks++; + + double mainCurrencyPrice = 1; + if (mainMarketTicks.Count > 0) + { + List mainCurrencyTickRange = mainMarketTicks.FindAll(t => t.Time <= tickTime); + if (mainCurrencyTickRange.Count > 0) + { + MarketTick mainCurrencyTick = mainCurrencyTickRange.OrderByDescending(t => t.Time).First(); + mainCurrencyPrice = mainCurrencyTick.Price; + } + } + + Dictionary tickMarkets = new Dictionary(); + foreach (string key in markets.Keys) + { + List tickRange = marketTicks[key] != null ? marketTicks[key].FindAll(t => t.Time <= tickTime) : new List(); + + if (tickRange.Count > 0) + { + MarketTick marketTick = tickRange.OrderByDescending(t => t.Time).First(); + + Market market = new Market(); + market.Position = markets.Count + 1; + market.Name = key; + market.Symbol = key; + market.Price = marketTick.Price; + //market.Volume24h = marketTick.Volume24h; + market.MainCurrencyPriceUSD = mainCurrencyPrice; + + tickMarkets.Add(market.Name, market); + } + } + + DateTime fileDateTime = new DateTime(tickTime.ToLocalTime().Year, tickTime.ToLocalTime().Month, tickTime.ToLocalTime().Day, tickTime.ToLocalTime().Hour, tickTime.ToLocalTime().Minute, 0).ToUniversalTime(); + + FileHelper.WriteTextToFile(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar, "MarketData_" + fileDateTime.ToString("yyyy-MM-dd_HH.mm") + ".json", JsonConvert.SerializeObject(tickMarkets), fileDateTime, fileDateTime); + + log.DoLogDebug("BinanceUS - Market data saved for tick " + fileDateTime.ToString() + " - MainCurrencyPrice=" + mainCurrencyPrice.ToString("#,#0.00") + " USD."); + + if ((completedTicks % 100) == 0) + { + log.DoLogInfo("BinanceUS - Our magicbots are still at work, hang on... " + completedTicks + "/" + totalTicks + " ticks done..."); + } + } + } + + log.DoLogInfo("BinanceUS - Initial market data created. Ready to go!"); + } + + } + } +} \ No newline at end of file diff --git a/Monitor/Pages/MarketAnalyzer.cshtml b/Monitor/Pages/MarketAnalyzer.cshtml index 95deaf9..a8d67c2 100644 --- a/Monitor/Pages/MarketAnalyzer.cshtml +++ b/Monitor/Pages/MarketAnalyzer.cshtml @@ -57,7 +57,29 @@ }
- } + } + else + if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("BinanceUS", StringComparison.InvariantCultureIgnoreCase)) { + string TvSymbol = "BINANCE:" + @Core.Helper.SystemHelper.GetMainCurrencySymbol(Model.Summary.MainMarket) + "USD"; +
+
+ +
+ } else if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex", StringComparison.InvariantCultureIgnoreCase)) { string TvSymbol = "BITTREX:" + @Core.Helper.SystemHelper.GetMainCurrencySymbol(Model.Summary.MainMarket) + "USD"; @@ -110,7 +132,7 @@
-

Market Trend Averages

+

Market Trends at @Model.PTMagicConfiguration.GeneralSettings.Application.Exchange

diff --git a/Monitor/Pages/SettingsGeneral.cshtml b/Monitor/Pages/SettingsGeneral.cshtml index 58df383..f5838cc 100644 --- a/Monitor/Pages/SettingsGeneral.cshtml +++ b/Monitor/Pages/SettingsGeneral.cshtml @@ -61,6 +61,13 @@ +
+ +
+ @Model.PTMagicConfiguration.GeneralSettings.Application.Exchange +
+
+
diff --git a/Monitor/Pages/_get/DashboardBottom.cshtml b/Monitor/Pages/_get/DashboardBottom.cshtml index 16bad91..137f0e9 100644 --- a/Monitor/Pages/_get/DashboardBottom.cshtml +++ b/Monitor/Pages/_get/DashboardBottom.cshtml @@ -53,7 +53,7 @@
-

Market Trendsmore

+

Market Trends at @Model.PTMagicConfiguration.GeneralSettings.Application.Exchangemore

diff --git a/Monitor/Pages/_get/DashboardTop.cshtml b/Monitor/Pages/_get/DashboardTop.cshtml index eca954f..8090158 100644 --- a/Monitor/Pages/_get/DashboardTop.cshtml +++ b/Monitor/Pages/_get/DashboardTop.cshtml @@ -150,14 +150,19 @@ // Aggregate totals Model.TotalBagCost = Model.TotalBagCost + dcaLogEntry.TotalCost; + double ExchangeFee = 0; + if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance")) { - double ExchangeFee = 0.002; + ExchangeFee = 0.002; + } + if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("BinanceUS")) { + ExchangeFee = 0.002; } if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex")) { - double ExchangeFee = 0.0025; + ExchangeFee = 0.0025; } if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex")) { - double ExchangeFee = 0.0025; + ExchangeFee = 0.0025; } double TradingFee = (dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) * ExchangeFee; From 6a9341615057c7dbf322f3b0cc70969d9b78cb42 Mon Sep 17 00:00:00 2001 From: HojouFotytu <36724681+HojouFotytu@users.noreply.github.com> Date: Thu, 17 Oct 2019 11:06:16 +0900 Subject: [PATCH 10/10] Trading fee update --- Monitor/Pages/_get/DashboardTop.cshtml | 28 +++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Monitor/Pages/_get/DashboardTop.cshtml b/Monitor/Pages/_get/DashboardTop.cshtml index 8090158..ade252f 100644 --- a/Monitor/Pages/_get/DashboardTop.cshtml +++ b/Monitor/Pages/_get/DashboardTop.cshtml @@ -149,20 +149,24 @@ // Aggregate totals Model.TotalBagCost = Model.TotalBagCost + dcaLogEntry.TotalCost; - double ExchangeFee = 0; - if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance")) { - ExchangeFee = 0.002; - } - if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("BinanceUS")) { - ExchangeFee = 0.002; - } - if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex")) { - ExchangeFee = 0.0025; - } - if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex")) { - ExchangeFee = 0.0025; + switch (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.ToLower()) + { + case "binance": + ExchangeFee = 0.002; + break; + case "binanceus": + ExchangeFee = 0.002; + break; + case "bittrex": + ExchangeFee = 0.0025; + break; + case "poloniex": + ExchangeFee = 0.0025; + break; + default: + break; } double TradingFee = (dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) * ExchangeFee;