diff --git a/.gitignore b/.gitignore index fcceb73..4d8e3fb 100644 --- a/.gitignore +++ b/.gitignore @@ -8,8 +8,9 @@ _[Dd]ata _[Rr]eleases _backups LocalProfitTrailer -PTMagic/settings.*.json +PTMagic/settings.* Monitor/appsettings.json +Monitor/Monitor Release/ diff --git a/Core/DataObjects/PTMagicData.cs b/Core/DataObjects/PTMagicData.cs index 0ae24c4..350760f 100644 --- a/Core/DataObjects/PTMagicData.cs +++ b/Core/DataObjects/PTMagicData.cs @@ -3,29 +3,35 @@ using System.Collections.Generic; using System.ComponentModel; using System.Text; -namespace Core.Main.DataObjects.PTMagicData { +namespace Core.Main.DataObjects.PTMagicData +{ #region Settings Objects - public class GeneralSettingsWrapper { + public class GeneralSettingsWrapper + { public GeneralSettings GeneralSettings { get; set; } } - public class AnalyzerSettingsWrapper { + public class AnalyzerSettingsWrapper + { public AnalyzerSettings AnalyzerSettings { get; set; } } - public class SecureSettingsWrapper { + public class SecureSettingsWrapper + { public SecureSettings SecureSettings { get; set; } } #region GeneralSettings - public class GeneralSettings { + public class GeneralSettings + { public Application Application { get; set; } public Monitor Monitor { get; set; } public Backup Backup { get; set; } public Telegram Telegram { get; set; } } - public class Application { + public class Application + { public bool IsEnabled { get; set; } = true; public bool TestMode { get; set; } = true; public bool EnableBetaFeatures { get; set; } = false; @@ -44,7 +50,8 @@ namespace Core.Main.DataObjects.PTMagicData { public string CoinMarketCapAPIKey { get; set; } } - public class Monitor { + public class Monitor + { private string _rootUrl = "/"; public bool IsPasswordProtected { get; set; } = true; @@ -65,23 +72,28 @@ namespace Core.Main.DataObjects.PTMagicData { public string LinkPlatform { get; set; } = "TradingView"; public string DefaultDCAMode { get; set; } = "Simple"; - public string RootUrl { - get { + public string RootUrl + { + get + { if (!_rootUrl.EndsWith("/")) _rootUrl += "/"; return _rootUrl; } - set { + set + { _rootUrl = value; } } } - public class Backup { + public class Backup + { public bool IsEnabled { get; set; } = true; public int MaxHours { get; set; } = 48; } - public class Telegram { + public class Telegram + { public bool IsEnabled { get; set; } = false; public string BotToken { get; set; } public Int64 ChatId { get; set; } @@ -90,20 +102,23 @@ namespace Core.Main.DataObjects.PTMagicData { #endregion #region AnalyzerSettings - public class AnalyzerSettings { + public class AnalyzerSettings + { public MarketAnalyzer MarketAnalyzer { get; set; } public List GlobalSettings { get; set; } public List SingleMarketSettings { get; set; } } - public class MarketAnalyzer { + public class MarketAnalyzer + { public int StoreDataMaxHours { get; set; } public int IntervalMinutes { get; set; } = 5; public bool ExcludeMainCurrency { get; set; } = true; public List MarketTrends { get; set; } } - public class MarketTrend { + public class MarketTrend + { public string Name { get; set; } public string Platform { get; set; } = "Exchange"; @@ -130,7 +145,8 @@ namespace Core.Main.DataObjects.PTMagicData { public bool ExcludeMainCurrency { get; set; } = true; } - public class GlobalSetting { + public class GlobalSetting + { public string SettingName { get; set; } public string TriggerConnection { get; set; } = "AND"; public List Triggers { get; set; } = new List(); @@ -139,7 +155,8 @@ namespace Core.Main.DataObjects.PTMagicData { public Dictionary IndicatorsProperties { get; set; } = new Dictionary(); } - public class SingleMarketSetting { + public class SingleMarketSetting + { public string SettingName { get; set; } public string TriggerConnection { get; set; } = "AND"; @@ -170,7 +187,8 @@ namespace Core.Main.DataObjects.PTMagicData { public Dictionary IndicatorsProperties { get; set; } = new Dictionary(); } - public class Trigger { + public class Trigger + { [DefaultValue("")] public string MarketTrendName { get; set; } = ""; @@ -193,7 +211,8 @@ namespace Core.Main.DataObjects.PTMagicData { public int AgeDaysLowerThan { get; set; } = 0; } - public class OffTrigger { + public class OffTrigger + { [DefaultValue("")] public string MarketTrendName { get; set; } = ""; @@ -218,7 +237,8 @@ namespace Core.Main.DataObjects.PTMagicData { #endregion #region SecureSettings - public class SecureSettings { + public class SecureSettings + { public string MonitorPassword { get; set; } = ""; } #endregion @@ -226,7 +246,8 @@ namespace Core.Main.DataObjects.PTMagicData { #endregion #region Market Analyzer Objects - public class Market { + public class Market + { public int Position; public string Name = ""; public string Symbol = ""; @@ -236,13 +257,15 @@ namespace Core.Main.DataObjects.PTMagicData { public double MainCurrencyPriceUSD = 0.0; } - public class MarketTick { + public class MarketTick + { public double Volume24h = 0.0; public double Price = 0.0; public DateTime Time = Constants.confMinDate; } - public class MarketTrendChange { + public class MarketTrendChange + { public string MarketTrendName = ""; public string Market = ""; public double LastPrice = 0.0; @@ -252,7 +275,8 @@ namespace Core.Main.DataObjects.PTMagicData { public DateTime TrendDateTime = Constants.confMinDate; } - public class MarketInfo { + public class MarketInfo + { public string Name = ""; public DateTime FirstSeen = Constants.confMinDate; public DateTime LastSeen = Constants.confMaxDate; @@ -260,7 +284,8 @@ namespace Core.Main.DataObjects.PTMagicData { #endregion #region Summary Objects - public class Summary { + public class Summary + { public string Version { get; set; } = ""; public DateTime LastRuntime { get; set; } = Constants.confMinDate; public int LastRuntimeSeconds { get; set; } = 0; @@ -298,19 +323,22 @@ namespace Core.Main.DataObjects.PTMagicData { public List DCASellStrategies { get; set; } = new List(); } - public class StrategySummary { + public class StrategySummary + { public string Name { get; set; } = ""; public double Value { get; set; } = 0; } - public class GlobalSettingSummary { + public class GlobalSettingSummary + { public string SettingName { get; set; } public DateTime SwitchDateTime { get; set; } public int ActiveSeconds { get; set; } = 0; public Dictionary MarketTrendChanges { get; set; } = new Dictionary(); } - public class MarketPairSummary { + public class MarketPairSummary + { public bool IsTradingEnabled { get; set; } = false; public bool IsSOMActive { get; set; } = false; public bool IsDCAEnabled { get; set; } = false; @@ -330,12 +358,14 @@ namespace Core.Main.DataObjects.PTMagicData { #endregion #region Transaction Objects - public class Transaction { + public class Transaction + { public string GUID { get; set; } = ""; public DateTime UTCDateTime { get; set; } = Constants.confMinDate; public double Amount { get; set; } = 0.0; - public DateTime GetLocalDateTime(string offset) { + public DateTime GetLocalDateTime(string offset) + { DateTimeOffset result = this.UTCDateTime; // Convert UTC sales time to local offset time @@ -348,14 +378,16 @@ namespace Core.Main.DataObjects.PTMagicData { #endregion #region SingleMarketSettingSummary Objects - public class SingleMarketSettingSummary { + public class SingleMarketSettingSummary + { public string Market { get; set; } = ""; public DateTime ActivationDateTimeUTC { get; set; } = Constants.confMinDate; public SingleMarketSetting SingleMarketSetting { get; set; } = null; public TriggerSnapshot TriggerSnapshot { get; set; } = null; } - public class TriggerSnapshot { + public class TriggerSnapshot + { public Dictionary RelevantTriggers { get; set; } = new Dictionary(); public List MatchedTriggersContent { get; set; } = new List(); public double LastPrice { get; set; } = 0; @@ -365,14 +397,16 @@ namespace Core.Main.DataObjects.PTMagicData { #region Profit Trailer JSON Objects - public class PTData { + public class PTData + { public List SellLogData { get; set; } = new List(); public List DCALogData { get; set; } = new List(); public List GainLogData { get; set; } = new List(); public List bbBuyLogData { get; set; } = new List(); } - public class sellLogData { + public class sellLogData + { public double soldAmount { get; set; } public SoldDate soldDate { get; set; } public int boughtTimes { get; set; } @@ -382,7 +416,8 @@ namespace Core.Main.DataObjects.PTMagicData { public double currentPrice { get; set; } } - public class SellLogData { + public class SellLogData + { public double SoldAmount { get; set; } public DateTime SoldDate { get; set; } public int BoughtTimes { get; set; } @@ -395,30 +430,35 @@ namespace Core.Main.DataObjects.PTMagicData { public double SoldValue { get; set; } } - public class SoldDate { + public class SoldDate + { public Date date { get; set; } public Time time { get; set; } } - public class FirstBoughtDate { + public class FirstBoughtDate + { public Date date { get; set; } public Time time { get; set; } } - public class Date { + public class Date + { public int year { get; set; } public int month { get; set; } public int day { get; set; } } - public class Time { + public class Time + { public int hour { get; set; } public int minute { get; set; } public int second { get; set; } public int nano { get; set; } } - public class AverageCalculator { + public class AverageCalculator + { public double totalCost { get; set; } public double totalAmount { get; set; } public double totalAmountWithSold { get; set; } @@ -430,7 +470,8 @@ namespace Core.Main.DataObjects.PTMagicData { public double fee { get; set; } } - public class PTStrategy { + public class PTStrategy + { public string type { get; set; } public string name { get; set; } public double entryValue { get; set; } @@ -442,7 +483,8 @@ namespace Core.Main.DataObjects.PTMagicData { public string positive { get; set; } } - public class dcaLogData { + public class dcaLogData + { public int boughtTimes { get; set; } = 0; public string market { get; set; } public string positive { get; set; } @@ -461,7 +503,8 @@ namespace Core.Main.DataObjects.PTMagicData { public List sellStrategies { get; set; } } - public class Strategy { + public class Strategy + { public string Type { get; set; } public string Name { get; set; } public double EntryValue { get; set; } @@ -474,7 +517,8 @@ namespace Core.Main.DataObjects.PTMagicData { public bool IsTrue { get; set; } } - public class DCALogData { + public class DCALogData + { public int BoughtTimes { get; set; } public double CurrentLowBBValue { get; set; } public double CurrentHighBBValue { get; set; } @@ -497,7 +541,8 @@ namespace Core.Main.DataObjects.PTMagicData { public List SellStrategies { get; set; } = new List(); } - public class buyLogData { + public class buyLogData + { public string market { get; set; } public string positive { get; set; } public double BBLow { get; set; } @@ -512,7 +557,8 @@ namespace Core.Main.DataObjects.PTMagicData { public List buyStrategies { get; set; } } - public class BuyLogData { + public class BuyLogData + { public double CurrentLowBBValue { get; set; } public double CurrentHighBBValue { get; set; } public double BBTrigger { get; set; } diff --git a/Core/DataObjects/ProfitTrailerData.cs b/Core/DataObjects/ProfitTrailerData.cs index 3e75db6..014371c 100644 --- a/Core/DataObjects/ProfitTrailerData.cs +++ b/Core/DataObjects/ProfitTrailerData.cs @@ -8,9 +8,11 @@ using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using Core.Main.DataObjects.PTMagicData; -namespace Core.Main.DataObjects { +namespace Core.Main.DataObjects +{ - public class ProfitTrailerData { + public class ProfitTrailerData + { private List _sellLog = new List(); private List _dcaLog = new List(); private List _buyLog = new List(); @@ -19,7 +21,8 @@ namespace Core.Main.DataObjects { private TransactionData _transactionData = null; private DateTimeOffset _dateTimeNow = Constants.confMinDate; - public ProfitTrailerData(string ptmBasePath, PTMagicConfiguration systemConfiguration) { + public ProfitTrailerData(string ptmBasePath, PTMagicConfiguration systemConfiguration) + { _ptmBasePath = ptmBasePath; _systemConfiguration = systemConfiguration; @@ -36,17 +39,20 @@ namespace Core.Main.DataObjects { throw new Exception("Unable to load Profit Trailer data file at: " + ptDataFilePath); } } - + PTData rawPTData = JsonConvert.DeserializeObject(File.ReadAllText(ptDataFilePath)); - if (rawPTData.SellLogData != null) { + if (rawPTData.SellLogData != null) + { this.BuildSellLogData(rawPTData.SellLogData, _systemConfiguration); } - if (rawPTData.bbBuyLogData != null) { + if (rawPTData.bbBuyLogData != null) + { this.BuildBuyLogData(rawPTData.bbBuyLogData, _systemConfiguration); } - if (rawPTData.DCALogData != null) { + if (rawPTData.DCALogData != null) + { this.BuildDCALogData(rawPTData.DCALogData, rawPTData.GainLogData, _systemConfiguration); } @@ -55,54 +61,70 @@ namespace Core.Main.DataObjects { _dateTimeNow = DateTimeOffset.UtcNow.ToOffset(offsetTimeSpan); } - public List SellLog { - get { + public List SellLog + { + get + { return _sellLog; } } - public List SellLogToday { - get { + public List SellLogToday + { + get + { return _sellLog.FindAll(sl => sl.SoldDate.Date == _dateTimeNow.DateTime.Date); } } - public List SellLogYesterday { - get { + public List SellLogYesterday + { + get + { return _sellLog.FindAll(sl => sl.SoldDate.Date == _dateTimeNow.DateTime.AddDays(-1).Date); } } - public List SellLogLast7Days { - get { + public List SellLogLast7Days + { + get + { return _sellLog.FindAll(sl => sl.SoldDate.Date >= _dateTimeNow.DateTime.AddDays(-7).Date); } } - public List DCALog { - get { + public List DCALog + { + get + { return _dcaLog; } } - public List BuyLog { - get { + public List BuyLog + { + get + { return _buyLog; } } - public TransactionData TransactionData { - get { + public TransactionData TransactionData + { + get + { if (_transactionData == null) _transactionData = new TransactionData(_ptmBasePath); return _transactionData; } } - public double GetCurrentBalance() { + public double GetCurrentBalance() + { return this.GetSnapshotBalance(DateTime.Now.ToUniversalTime()); } - public double GetSnapshotBalance(DateTime snapshotDateTime) { + public double GetSnapshotBalance(DateTime snapshotDateTime) + { double result = _systemConfiguration.GeneralSettings.Application.StartBalance; result += this.SellLog.FindAll(sl => sl.SoldDate.Date < snapshotDateTime.Date).Sum(sl => sl.Profit); @@ -111,8 +133,10 @@ namespace Core.Main.DataObjects { return result; } - private void BuildSellLogData(List rawSellLogData, PTMagicConfiguration systemConfiguration) { - foreach (sellLogData rsld in rawSellLogData) { + private void BuildSellLogData(List rawSellLogData, PTMagicConfiguration systemConfiguration) + { + foreach (sellLogData rsld in rawSellLogData) + { SellLogData sellLogData = new SellLogData(); sellLogData.SoldAmount = rsld.soldAmount; sellLogData.BoughtTimes = rsld.boughtTimes; @@ -140,8 +164,10 @@ namespace Core.Main.DataObjects { } } - private void BuildDCALogData(List rawDCALogData, List rawPairsLogData, PTMagicConfiguration systemConfiguration) { - foreach (dcaLogData rdld in rawDCALogData) { + private void BuildDCALogData(List rawDCALogData, List rawPairsLogData, PTMagicConfiguration systemConfiguration) + { + foreach (dcaLogData rdld in rawDCALogData) + { DCALogData dcaLogData = new DCALogData(); dcaLogData.Amount = rdld.averageCalculator.totalAmount; dcaLogData.BoughtTimes = rdld.boughtTimes; @@ -161,12 +187,17 @@ namespace Core.Main.DataObjects { dcaLogData.SellStrategy = rdld.sellStrategy; if (dcaLogData.SellStrategy == null) dcaLogData.SellStrategy = ""; - if (rdld.positive != null) { + if (rdld.positive != null) + { dcaLogData.IsTrailing = rdld.positive.IndexOf("trailing", StringComparison.InvariantCultureIgnoreCase) > -1; dcaLogData.IsTrue = rdld.positive.IndexOf("true", StringComparison.InvariantCultureIgnoreCase) > -1; - } else { - if (rdld.buyStrategies != null) { - foreach (PTStrategy bs in rdld.buyStrategies) { + } + else + { + if (rdld.buyStrategies != null) + { + foreach (PTStrategy bs in rdld.buyStrategies) + { Strategy buyStrategy = new Strategy(); buyStrategy.Type = bs.type; buyStrategy.Name = bs.name; @@ -183,8 +214,10 @@ namespace Core.Main.DataObjects { } } - if (rdld.sellStrategies != null) { - foreach (PTStrategy ss in rdld.sellStrategies) { + if (rdld.sellStrategies != null) + { + foreach (PTStrategy ss in rdld.sellStrategies) + { Strategy sellStrategy = new Strategy(); sellStrategy.Type = ss.type; sellStrategy.Name = ss.name; @@ -204,7 +237,8 @@ namespace Core.Main.DataObjects { // Profit Trailer bought times are saved in UTC - if (rdld.averageCalculator.firstBoughtDate != null) { + if (rdld.averageCalculator.firstBoughtDate != null) + { DateTimeOffset ptFirstBoughtDate = DateTimeOffset.Parse(rdld.averageCalculator.firstBoughtDate.date.year.ToString() + "-" + rdld.averageCalculator.firstBoughtDate.date.month.ToString("00") + "-" + rdld.averageCalculator.firstBoughtDate.date.day.ToString("00") + "T" + rdld.averageCalculator.firstBoughtDate.time.hour.ToString("00") + ":" + rdld.averageCalculator.firstBoughtDate.time.minute.ToString("00") + ":" + rdld.averageCalculator.firstBoughtDate.time.second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); // Convert UTC bought time to local offset time @@ -212,14 +246,17 @@ namespace Core.Main.DataObjects { ptFirstBoughtDate = ptFirstBoughtDate.ToOffset(offsetTimeSpan); dcaLogData.FirstBoughtDate = ptFirstBoughtDate.DateTime; - } else { + } + else + { dcaLogData.FirstBoughtDate = Constants.confMinDate; } _dcaLog.Add(dcaLogData); } - foreach (dcaLogData rpld in rawPairsLogData) { + foreach (dcaLogData rpld in rawPairsLogData) + { DCALogData dcaLogData = new DCALogData(); dcaLogData.Amount = rpld.averageCalculator.totalAmount; dcaLogData.BoughtTimes = 0; @@ -237,8 +274,10 @@ namespace Core.Main.DataObjects { if (dcaLogData.SellStrategy == null) dcaLogData.SellStrategy = ""; dcaLogData.IsTrailing = false; - if (rpld.sellStrategies != null) { - foreach (PTStrategy ss in rpld.sellStrategies) { + if (rpld.sellStrategies != null) + { + foreach (PTStrategy ss in rpld.sellStrategies) + { Strategy sellStrategy = new Strategy(); sellStrategy.Type = ss.type; sellStrategy.Name = ss.name; @@ -256,7 +295,8 @@ namespace Core.Main.DataObjects { } // Profit Trailer bought times are saved in UTC - if (rpld.averageCalculator.firstBoughtDate != null) { + if (rpld.averageCalculator.firstBoughtDate != null) + { DateTimeOffset ptFirstBoughtDate = DateTimeOffset.Parse(rpld.averageCalculator.firstBoughtDate.date.year.ToString() + "-" + rpld.averageCalculator.firstBoughtDate.date.month.ToString("00") + "-" + rpld.averageCalculator.firstBoughtDate.date.day.ToString("00") + "T" + rpld.averageCalculator.firstBoughtDate.time.hour.ToString("00") + ":" + rpld.averageCalculator.firstBoughtDate.time.minute.ToString("00") + ":" + rpld.averageCalculator.firstBoughtDate.time.second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); // Convert UTC bought time to local offset time @@ -264,7 +304,9 @@ namespace Core.Main.DataObjects { ptFirstBoughtDate = ptFirstBoughtDate.ToOffset(offsetTimeSpan); dcaLogData.FirstBoughtDate = ptFirstBoughtDate.DateTime; - } else { + } + else + { dcaLogData.FirstBoughtDate = Constants.confMinDate; } @@ -272,8 +314,10 @@ namespace Core.Main.DataObjects { } } - private void BuildBuyLogData(List rawBuyLogData, PTMagicConfiguration systemConfiguration) { - foreach (buyLogData rbld in rawBuyLogData) { + private void BuildBuyLogData(List rawBuyLogData, PTMagicConfiguration systemConfiguration) + { + foreach (buyLogData rbld in rawBuyLogData) + { BuyLogData buyLogData = new BuyLogData(); buyLogData.Market = rbld.market; buyLogData.ProfitPercent = rbld.profit; @@ -286,14 +330,19 @@ namespace Core.Main.DataObjects { buyLogData.CurrentHighBBValue = rbld.BBHigh; buyLogData.BBTrigger = rbld.BBTrigger; - if (buyLogData.BuyStrategy == null) buyLogData.BuyStrategy = ""; + if (buyLogData.BuyStrategy == null) buyLogData.BuyStrategy = ""; - if (rbld.positive != null) { + if (rbld.positive != null) + { buyLogData.IsTrailing = rbld.positive.IndexOf("trailing", StringComparison.InvariantCultureIgnoreCase) > -1; buyLogData.IsTrue = rbld.positive.IndexOf("true", StringComparison.InvariantCultureIgnoreCase) > -1; - } else { - if (rbld.buyStrategies != null) { - foreach (PTStrategy bs in rbld.buyStrategies) { + } + else + { + if (rbld.buyStrategies != null) + { + foreach (PTStrategy bs in rbld.buyStrategies) + { Strategy buyStrategy = new Strategy(); buyStrategy.Type = bs.type; buyStrategy.Name = bs.name; diff --git a/Core/DataObjects/TransactionData.cs b/Core/DataObjects/TransactionData.cs index 7332355..80515c9 100644 --- a/Core/DataObjects/TransactionData.cs +++ b/Core/DataObjects/TransactionData.cs @@ -8,25 +8,32 @@ using Newtonsoft.Json; using Core.Main.DataObjects.PTMagicData; using Core.Helper; -namespace Core.Main.DataObjects { +namespace Core.Main.DataObjects +{ - public class TransactionData { + public class TransactionData + { private List _transactions = new List(); - public TransactionData(string basePath) { + public TransactionData(string basePath) + { string transactionsFilePath = basePath + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "Transactions.json"; - if (File.Exists(transactionsFilePath)) { + if (File.Exists(transactionsFilePath)) + { this._transactions = JsonConvert.DeserializeObject>(File.ReadAllText(transactionsFilePath)); } } - public List Transactions { - get { + public List Transactions + { + get + { return _transactions; } } - public void SaveTransactions(string basePath) { + public void SaveTransactions(string basePath) + { FileHelper.WriteTextToFile(basePath + Constants.PTMagicPathData + Path.DirectorySeparatorChar, "Transactions.json", JsonConvert.SerializeObject(this.Transactions)); } } diff --git a/Core/Helper/CertificateHelper.cs b/Core/Helper/CertificateHelper.cs index 636b78c..0e7b41b 100644 --- a/Core/Helper/CertificateHelper.cs +++ b/Core/Helper/CertificateHelper.cs @@ -9,10 +9,13 @@ using System.Net.Http; using System.Text; using System.Threading.Tasks; -namespace Core.Helper { - public static class CertificateHelper { +namespace Core.Helper +{ + public static class CertificateHelper + { - public static bool AllwaysGoodCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors policyErrors) { + public static bool AllwaysGoodCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors policyErrors) + { return true; } } diff --git a/Core/Helper/EncryptionHelper.cs b/Core/Helper/EncryptionHelper.cs index 99a8368..395f6b6 100644 --- a/Core/Helper/EncryptionHelper.cs +++ b/Core/Helper/EncryptionHelper.cs @@ -7,29 +7,39 @@ using System.Security.Cryptography; using System.Collections.Specialized; using System.Configuration; -namespace Core.Helper { - public class EncryptionHelper { +namespace Core.Helper +{ + public class EncryptionHelper + { #region Properties - public static string CryptoMainSaltValue { - get { + public static string CryptoMainSaltValue + { + get + { return "b3+Pz.~L2EK>((/xnTbWdTo:/5_$hq8ja8yOq% j}M6zTM"; } } @@ -39,7 +49,8 @@ namespace Core.Helper { #region Methoden #region Passwortverschlüsselung - public static string CreateHash(string password, string randomSalt) { + public static string CreateHash(string password, string randomSalt) + { // Generate a random salt byte[] salt = Encoding.UTF8.GetBytes(EncryptionHelper.CryptoMainSaltValue + randomSalt); byte[] hash = PBKDF2(password, salt, 64000, 24); @@ -47,19 +58,23 @@ namespace Core.Helper { return Convert.ToBase64String(hash); } - public static bool SlowEquals(string aHash, string bHash) { + public static bool SlowEquals(string aHash, string bHash) + { byte[] a = Encoding.UTF8.GetBytes(aHash); byte[] b = Encoding.UTF8.GetBytes(bHash); uint diff = (uint)a.Length ^ (uint)b.Length; - for (int i = 0; i < a.Length && i < b.Length; i++) { + for (int i = 0; i < a.Length && i < b.Length; i++) + { diff |= (uint)(a[i] ^ b[i]); } return diff == 0; } - private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes) { - using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt)) { + private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes) + { + using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt)) + { pbkdf2.IterationCount = iterations; return pbkdf2.GetBytes(outputBytes); } @@ -69,19 +84,23 @@ namespace Core.Helper { #endregion #region Standardverschlüsselung - public static string Encrypt(string plainText) { + public static string Encrypt(string plainText) + { return Encrypt(plainText, EncryptionHelper.CryptoPassPhrase, EncryptionHelper.CryptoSaltValue, "SHA512", 2, EncryptionHelper.CryptoInitVector, 256); } - public static string Decrypt(string cipherText) { + public static string Decrypt(string cipherText) + { return Decrypt(cipherText, EncryptionHelper.CryptoPassPhrase, EncryptionHelper.CryptoSaltValue, "SHA512", 2, EncryptionHelper.CryptoInitVector, 256, true); } - public static string Encrypt(string plainText, string passPhrase) { + public static string Encrypt(string plainText, string passPhrase) + { return Encrypt(plainText, passPhrase, EncryptionHelper.CryptoSaltValue, "SHA512", 2, EncryptionHelper.CryptoInitVector, 256); } - public static string Decrypt(string cipherText, string passPhrase) { + public static string Decrypt(string cipherText, string passPhrase) + { return Decrypt(cipherText, passPhrase, EncryptionHelper.CryptoSaltValue, "SHA512", 2, EncryptionHelper.CryptoInitVector, 256, true); } @@ -129,7 +148,8 @@ namespace Core.Helper { string hashAlgorithm, int passwordIterations, string initVector, - int keySize) { + int keySize) + { // Convert strings into byte arrays. byte[] initVectorBytes = Encoding.UTF8.GetBytes(initVector); byte[] saltValueBytes = Encoding.UTF8.GetBytes(saltValue); @@ -235,8 +255,10 @@ namespace Core.Helper { int passwordIterations, string initVector, int keySize, - bool doDecrypt) { - if (doDecrypt) { + bool doDecrypt) + { + if (doDecrypt) + { // Convert strings defining encryption key characteristics into byte // arrays. byte[] initVectorBytes = Encoding.UTF8.GetBytes(initVector); @@ -292,7 +314,9 @@ namespace Core.Helper { // Return decrypted string. return plainText; - } else { + } + else + { return ""; } } diff --git a/Core/Helper/FileHelper.cs b/Core/Helper/FileHelper.cs index b3abf53..f2bb1c9 100644 --- a/Core/Helper/FileHelper.cs +++ b/Core/Helper/FileHelper.cs @@ -2,72 +2,91 @@ using System.IO; using Core.Main; -namespace Core.Helper { - public static class FileHelper { - public static void WriteTextToFile(string folderPath, string fileName, string text) { +namespace Core.Helper +{ + public static class FileHelper + { + public static void WriteTextToFile(string folderPath, string fileName, string text) + { FileHelper.WriteTextToFile(folderPath, fileName, text, Constants.confMinDate, Constants.confMinDate); } - public static void WriteTextToFile(string folderPath, string fileName, string text, DateTime creationTime, DateTime lastWriteTime) { - if (!Directory.Exists(folderPath)) { + public static void WriteTextToFile(string folderPath, string fileName, string text, DateTime creationTime, DateTime lastWriteTime) + { + if (!Directory.Exists(folderPath)) + { Directory.CreateDirectory(folderPath); } File.WriteAllText(folderPath + fileName, text); - if (creationTime != Constants.confMinDate) { + if (creationTime != Constants.confMinDate) + { File.SetCreationTimeUtc(folderPath + fileName, creationTime); } - if (lastWriteTime != Constants.confMinDate) { + if (lastWriteTime != Constants.confMinDate) + { File.SetLastWriteTimeUtc(folderPath + fileName, lastWriteTime); } } - public static void CreateBackup(string filePath, string backupFolder) { + public static void CreateBackup(string filePath, string backupFolder) + { FileHelper.CreateBackup(filePath, backupFolder, ""); } - public static void CreateBackup(string filePath, string backupFolder, string backupFileName) { - if (!Directory.Exists(backupFolder)) { + public static void CreateBackup(string filePath, string backupFolder, string backupFileName) + { + if (!Directory.Exists(backupFolder)) + { Directory.CreateDirectory(backupFolder); } FileInfo file = new FileInfo(filePath); string backupFilePath = backupFolder + DateTime.Now.ToString("yyyy-MM-dd_HH.mm.ss") + "_" + file.Name; - if (!backupFileName.Equals("")) { + if (!backupFileName.Equals("")) + { backupFilePath = backupFolder + backupFileName; } File.Copy(file.FullName, backupFilePath, true); } - public static void CleanupFilesMinutes(string folderPath, int maxMinutes) { - if (!Directory.Exists(folderPath)) { + public static void CleanupFilesMinutes(string folderPath, int maxMinutes) + { + if (!Directory.Exists(folderPath)) + { Directory.CreateDirectory(folderPath); } DirectoryInfo folder = new DirectoryInfo(folderPath); - foreach (FileInfo file in folder.GetFiles()) { + foreach (FileInfo file in folder.GetFiles()) + { DateTime maxAge = DateTime.Now.AddMinutes(-maxMinutes); - if (file.LastWriteTime < maxAge) { + if (file.LastWriteTime < maxAge) + { File.Delete(file.FullName); } } } - public static void CleanupFiles(string folderPath, int maxHours) { - if (!Directory.Exists(folderPath)) { + public static void CleanupFiles(string folderPath, int maxHours) + { + if (!Directory.Exists(folderPath)) + { Directory.CreateDirectory(folderPath); } DirectoryInfo folder = new DirectoryInfo(folderPath); - foreach (FileInfo file in folder.GetFiles()) { + foreach (FileInfo file in folder.GetFiles()) + { DateTime maxAge = DateTime.Now.AddHours(-(maxHours + 1)); - if (file.LastWriteTime < maxAge) { + if (file.LastWriteTime < maxAge) + { File.Delete(file.FullName); } } diff --git a/Core/Helper/LogHelper.cs b/Core/Helper/LogHelper.cs index 6750ef9..698b14a 100644 --- a/Core/Helper/LogHelper.cs +++ b/Core/Helper/LogHelper.cs @@ -3,31 +3,39 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NLog.Extensions.Logging; -namespace Core.Helper { - public class LogHelper { +namespace Core.Helper +{ + public class LogHelper + { private readonly ILogger log; - public LogHelper(ILogger logger) { + public LogHelper(ILogger logger) + { log = logger; } - public void DoLogInfo(string message) { + public void DoLogInfo(string message) + { if (log.IsEnabled(LogLevel.Information)) log.LogInformation(message); } - public void DoLogWarn(string message) { + public void DoLogWarn(string message) + { if (log.IsEnabled(LogLevel.Warning)) log.LogWarning(message); } - public void DoLogError(string message) { + public void DoLogError(string message) + { if (log.IsEnabled(LogLevel.Error)) log.LogError(message); } - public void DoLogCritical(string message, System.Exception ex) { + public void DoLogCritical(string message, System.Exception ex) + { if (log.IsEnabled(LogLevel.Critical)) log.LogCritical(ex, message); } - public void DoLogDebug(string message) { + public void DoLogDebug(string message) + { if (log.IsEnabled(LogLevel.Debug)) log.LogDebug(message); } } diff --git a/Core/Helper/ServiceHelper.cs b/Core/Helper/ServiceHelper.cs index 3050547..21e1e29 100644 --- a/Core/Helper/ServiceHelper.cs +++ b/Core/Helper/ServiceHelper.cs @@ -3,14 +3,18 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NLog.Extensions.Logging; -namespace Core.Helper { - public static class ServiceHelper { +namespace Core.Helper +{ + public static class ServiceHelper + { - public static IServiceProvider BuildLoggerService() { + public static IServiceProvider BuildLoggerService() + { return ServiceHelper.BuildLoggerService(""); } - public static IServiceProvider BuildLoggerService(string basePath) { + public static IServiceProvider BuildLoggerService(string basePath) + { ServiceCollection services = new ServiceCollection(); services.AddTransient(); diff --git a/Core/Helper/SystemHelper.cs b/Core/Helper/SystemHelper.cs index b3c968e..219a414 100644 --- a/Core/Helper/SystemHelper.cs +++ b/Core/Helper/SystemHelper.cs @@ -11,10 +11,13 @@ using System.Text; using System.Globalization; using Core.Main; -namespace Core.Helper { +namespace Core.Helper +{ - public class SystemHelper { - private static bool AllwaysGoodCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors policyErrors) { + public class SystemHelper + { + private static bool AllwaysGoodCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors policyErrors) + { return true; } @@ -23,23 +26,32 @@ namespace Core.Helper { /// /// The string to check. /// True, if the string is numeric. - public static bool IsNumeric(string s) { - try { + public static bool IsNumeric(string s) + { + try + { Int32.Parse(s); - } catch { + } + catch + { return false; } return true; } - public static bool IsInteger(double d) { + public static bool IsInteger(double d) + { return d % 1 == 0; } - public static bool IsBoolean(string s) { - try { + public static bool IsBoolean(string s) + { + try + { Boolean.Parse(s); - } catch { + } + catch + { return false; } return true; @@ -50,19 +62,27 @@ namespace Core.Helper { /// /// The string to check. /// True, if the string is a double value. - public static bool IsDouble(string s) { - try { + public static bool IsDouble(string s) + { + try + { Double.Parse(s); - } catch { + } + catch + { return false; } return true; } - public static bool IsDouble(string s, string culture) { - try { + public static bool IsDouble(string s, string culture) + { + try + { Double.Parse(s, new CultureInfo(culture)); - } catch { + } + catch + { return false; } return true; @@ -73,10 +93,14 @@ namespace Core.Helper { /// /// The string to check. /// True, if the string is a DateTime value. - public static bool IsDateTime(string s) { - try { + public static bool IsDateTime(string s) + { + try + { DateTime.Parse(s); - } catch { + } + catch + { return false; } return true; @@ -88,12 +112,15 @@ namespace Core.Helper { /// Zu konvertierender Text. /// Der Vorgabewert für den Fall, dass keine gültige Zahl eingegeben wurde. /// Den Text als Integer. Wenn die Konvertierung fehlschlägt, dann wird der Defaultwert zurückgegeben. - public static int TextToInteger(string text, int defaultValue) { + public static int TextToInteger(string text, int defaultValue) + { int result = defaultValue; - try { + try + { string localText = text.Replace(".", ""); result = Convert.ToInt32(localText.Trim()); - } catch { } + } + catch { } return result; } @@ -104,23 +131,30 @@ namespace Core.Helper { /// Zu konvertierender Text. /// Der Vorgabewert für den Fall, dass keine gültige Zahl eingegeben wurde. /// Den Text als Integer64. Wenn die Konvertierung fehlschlägt, dann wird der Defaultwert zurückgegeben. - public static Int64 TextToInteger64(string text, Int64 defaultValue) { + public static Int64 TextToInteger64(string text, Int64 defaultValue) + { Int64 result = defaultValue; - try { + try + { string localText = text.Replace(".", ""); result = Convert.ToInt64(localText.Trim()); - } catch { } + } + catch { } return result; } - public static double TextToDouble(string text, double defaultValue, string culture) { + public static double TextToDouble(string text, double defaultValue, string culture) + { double result = defaultValue; - try { - if (!string.IsNullOrEmpty(text)) { + try + { + if (!string.IsNullOrEmpty(text)) + { double.TryParse(text, NumberStyles.Any, new System.Globalization.CultureInfo(culture), out result); } - } catch { } + } + catch { } return result; } @@ -131,20 +165,26 @@ namespace Core.Helper { /// Zu konvertierender Text. /// Der Vorgabewert für den Fall, dass keine gültige DateTime eingegeben wurde. /// Den Text als DateTime. Wenn die Konvertierung fehlschlägt, dann wird der Defaultwert zurückgegeben. - public static DateTime TextToDateTime(string text, DateTime defaultValue) { + public static DateTime TextToDateTime(string text, DateTime defaultValue) + { DateTime result = defaultValue; - try { + try + { result = Convert.ToDateTime(text.Trim()); - } catch { } + } + catch { } return result; } - public static DateTime TextToDateTime(string text, DateTime defaultValue, string culture) { + public static DateTime TextToDateTime(string text, DateTime defaultValue, string culture) + { DateTime result = defaultValue; - try { + try + { result = Convert.ToDateTime(text.Trim(), new System.Globalization.CultureInfo(culture)); - } catch { } + } + catch { } return result; } @@ -155,32 +195,46 @@ namespace Core.Helper { /// Zu konvertierender Text. /// Der Vorgabewert für den Fall, dass keine gültige Boolean eingegeben wurde. /// Den Text als Boolean. Wenn die Konvertierung fehlschlägt, dann wird der Defaultwert zurückgegeben. - public static bool TextToBoolean(string text, bool defaultValue) { + public static bool TextToBoolean(string text, bool defaultValue) + { bool result = defaultValue; - try { + try + { result = Convert.ToBoolean(text.Trim()); - } catch { - try { + } + catch + { + try + { int intValue = Convert.ToInt32(text.Trim()); result = intValue == 0 ? false : true; - } catch { } + } + catch { } } return result; } - public static string SplitCamelCase(string s) { + public static string SplitCamelCase(string s) + { string result = ""; string whiteList = "ABCDEFGHIJKLMNOPQRSTUVWXYZÄÜÖßabcdefghijklmnopqrstuvwxyzäüö0123456789_- "; - if (!string.IsNullOrEmpty(s)) { - for (int i = 0; i < s.Length; i++) { - if (char.IsUpper(s[i]) || char.IsNumber(s[i])) { - if (i > 0 && whiteList.Contains(s[i - 1].ToString())) { - if (char.IsUpper(s[i])) { + if (!string.IsNullOrEmpty(s)) + { + for (int i = 0; i < s.Length; i++) + { + if (char.IsUpper(s[i]) || char.IsNumber(s[i])) + { + if (i > 0 && whiteList.Contains(s[i - 1].ToString())) + { + if (char.IsUpper(s[i])) + { if (!char.IsUpper(s[i - 1]) && !char.IsNumber(s[i - 1])) result += " "; - } else if (char.IsNumber(s[i])) { + } + else if (char.IsNumber(s[i])) + { if (!char.IsNumber(s[i - 1])) result += " "; } } @@ -198,10 +252,13 @@ namespace Core.Helper { /// Text to clear. /// Allowed characters. /// The cleared text. - public static string StripBadCode(string text, string allowedCharacters) { + public static string StripBadCode(string text, string allowedCharacters) + { StringBuilder sb = new StringBuilder(); - if (text != null) { - for (int i = 0; i < text.Length; i++) { + if (text != null) + { + for (int i = 0; i < text.Length; i++) + { if (allowedCharacters.Contains(text[i].ToString())) sb.Append(text[i]); } } @@ -209,10 +266,13 @@ namespace Core.Helper { return sb.ToString(); } - public static bool CheckForBadCode(string text, string allowedCharacters) { + public static bool CheckForBadCode(string text, string allowedCharacters) + { bool result = false; - for (int i = 0; i < text.Length; i++) { - if (!allowedCharacters.Contains(text[i].ToString())) { + for (int i = 0; i < text.Length; i++) + { + if (!allowedCharacters.Contains(text[i].ToString())) + { result = true; break; } @@ -227,10 +287,12 @@ namespace Core.Helper { /// Der Text, der gekürzt werden soll. /// Die maximale Länge, auf die der Text gekürzt werden soll. /// Der gekürzte Text. - public static string CutText(string text, int maxLength, bool addDots) { + public static string CutText(string text, int maxLength, bool addDots) + { string result = text; - if (result.Length > maxLength) { + if (result.Length > maxLength) + { result = result.Substring(0, maxLength); if (addDots) result += "..."; @@ -242,10 +304,12 @@ namespace Core.Helper { /// /// Ermittelt den Teilstring eines Zeitstring, der die Stunden darstellt. /// - public static string GetHourFromString(string timeString) { + public static string GetHourFromString(string timeString) + { string result = ""; - if (timeString.Contains(":")) { + if (timeString.Contains(":")) + { string[] arrTime = timeString.Split(":".ToCharArray()); result = arrTime[0]; } @@ -256,10 +320,12 @@ namespace Core.Helper { /// /// Ermittelt den Teilstring eines Zeitstring, der die Minuten darstellt. /// - public static string GetMinutesFromString(string timeString) { + public static string GetMinutesFromString(string timeString) + { string result = ""; - if (timeString.Contains(":")) { + if (timeString.Contains(":")) + { string[] arrTime = timeString.Split(":".ToCharArray()); result = arrTime[1]; } @@ -267,12 +333,15 @@ namespace Core.Helper { return result; } - public static List ConvertTokenStringToList(string tokenizedString, string separator) { + public static List ConvertTokenStringToList(string tokenizedString, string separator) + { List result = new List(); - if (!String.IsNullOrEmpty(tokenizedString) && !String.IsNullOrEmpty(separator)) { + if (!String.IsNullOrEmpty(tokenizedString) && !String.IsNullOrEmpty(separator)) + { string[] arrTokens = tokenizedString.Split(separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < arrTokens.Length; i++) { + for (int i = 0; i < arrTokens.Length; i++) + { result.Add(arrTokens[i].Trim()); } } @@ -280,12 +349,15 @@ namespace Core.Helper { return result; } - public static List ConvertTokenStringToListInt(string tokenizedString, string separator) { + public static List ConvertTokenStringToListInt(string tokenizedString, string separator) + { List result = new List(); - if (!String.IsNullOrEmpty(tokenizedString) && !String.IsNullOrEmpty(separator)) { + if (!String.IsNullOrEmpty(tokenizedString) && !String.IsNullOrEmpty(separator)) + { string[] arrTokens = tokenizedString.Split(separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < arrTokens.Length; i++) { + for (int i = 0; i < arrTokens.Length; i++) + { result.Add(Convert.ToInt32(arrTokens[i])); } } @@ -293,25 +365,31 @@ namespace Core.Helper { return result; } - public static string ConvertListToTokenString(List tokenList, string separator, bool cropDoubleSeparators) { + public static string ConvertListToTokenString(List tokenList, string separator, bool cropDoubleSeparators) + { string result = ""; - if (tokenList.Count > 0) { - for (int i = 0; i < tokenList.Count; i++) { + if (tokenList.Count > 0) + { + for (int i = 0; i < tokenList.Count; i++) + { result += tokenList[i].Trim() + separator; } - if (cropDoubleSeparators)result = result.Replace(separator + separator, ""); + if (cropDoubleSeparators) result = result.Replace(separator + separator, ""); } return result; } - public static string ConvertListToTokenString(List tokenList, string separator) { + public static string ConvertListToTokenString(List tokenList, string separator) + { string result = ""; - if (tokenList.Count > 0) { - for (int i = 0; i < tokenList.Count; i++) { + if (tokenList.Count > 0) + { + for (int i = 0; i < tokenList.Count; i++) + { result += tokenList[i].ToString() + separator; } @@ -322,22 +400,27 @@ namespace Core.Helper { return result; } - public static List ConvertToObjectList(List inputList) { + public static List ConvertToObjectList(List inputList) + { List result = new List(); - foreach (T item in inputList) { + foreach (T item in inputList) + { result.Add(item); } return result; } - public static Hashtable ConvertTokenStringToHashtable(string tokenizedString, string pairSeparator, string fieldSeperator) { + public static Hashtable ConvertTokenStringToHashtable(string tokenizedString, string pairSeparator, string fieldSeperator) + { Hashtable result = new Hashtable(); - if (!String.IsNullOrEmpty(tokenizedString) && !String.IsNullOrEmpty(pairSeparator) && !String.IsNullOrEmpty(fieldSeperator)) { + if (!String.IsNullOrEmpty(tokenizedString) && !String.IsNullOrEmpty(pairSeparator) && !String.IsNullOrEmpty(fieldSeperator)) + { string[] arrTokens = tokenizedString.Split(pairSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < arrTokens.Length; i++) { + for (int i = 0; i < arrTokens.Length; i++) + { string[] arrKeyValuePair = arrTokens[i].Split(fieldSeperator.ToCharArray()); result.Add(arrKeyValuePair[0], arrKeyValuePair[1]); @@ -347,11 +430,14 @@ namespace Core.Helper { return result; } - public static string ConvertHashtableToTokenString(Hashtable tokenHashtable, string pairSeparator, string fieldSeperator) { + public static string ConvertHashtableToTokenString(Hashtable tokenHashtable, string pairSeparator, string fieldSeperator) + { string result = ""; - if (tokenHashtable.Keys.Count > 0) { - foreach (string key in tokenHashtable.Keys) { + if (tokenHashtable.Keys.Count > 0) + { + foreach (string key in tokenHashtable.Keys) + { result += key + fieldSeperator + tokenHashtable[key] + pairSeparator; } @@ -362,7 +448,8 @@ namespace Core.Helper { return result; } - public static string GetProperDurationTime(int durationSeconds, bool includeDays = true) { + public static string GetProperDurationTime(int durationSeconds, bool includeDays = true) + { string result = ""; int days = (int)Math.Floor((double)durationSeconds / (60.0 * 60.0 * 24.0)); @@ -372,21 +459,25 @@ namespace Core.Helper { int minutes = (int)Math.Floor((double)durationSeconds / 60.0) - (hours * 60) - (days * 24 * 60); int seconds = durationSeconds - (minutes * 60) - (hours * 60 * 60) - (days * 24 * 60 * 60); - if (days > 0) { + if (days > 0) + { result += days.ToString() + "d"; } - if (hours > 0) { + if (hours > 0) + { if (days > 0) result += " "; result += hours.ToString() + "h"; } - if (minutes > 0) { + if (minutes > 0) + { if (hours > 0 || days > 0) result += " "; result += minutes.ToString() + "m"; } - if (seconds > 0) { + if (seconds > 0) + { if (minutes > 0 || hours > 0 || days > 0) result += " "; result += seconds.ToString() + "s"; } @@ -394,24 +485,30 @@ namespace Core.Helper { return result; } - public static void AddValueToStringBuilder(StringBuilder sb, string value, int length, bool fillField, string delimiter) { - if (!string.IsNullOrEmpty(value)) { + public static void AddValueToStringBuilder(StringBuilder sb, string value, int length, bool fillField, string delimiter) + { + if (!string.IsNullOrEmpty(value)) + { if (value.Length > length) sb.Append(value.Substring(0, length)); // Beschneiden - else { + else + { if (fillField) sb.Append(value.PadRight(length)); else sb.Append(value); } - } else { + } + else + { if (fillField) sb.Append(string.Empty.PadRight(length)); } sb.Append(delimiter); } - public static bool UrlIsReachable(string url) { + public static bool UrlIsReachable(string url) + { ServicePointManager.Expect100Continue = true; ServicePointManager.DefaultConnectionLimit = 9999; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; @@ -421,29 +518,39 @@ namespace Core.Helper { request.Timeout = 10000; request.Method = "GET"; - try { - using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) { + try + { + using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) + { return response.StatusCode == HttpStatusCode.OK; } - } catch (WebException) { + } + catch (WebException) + { return false; } } - public static string GetMarketLink(string platform, string exchange, string market, string mainMarket) { + public static string GetMarketLink(string platform, string exchange, string market, string mainMarket) + { string result = "#"; - if (platform.Equals("TradingView")) { + if (platform.Equals("TradingView")) + { result = "https://www.tradingview.com/chart/?symbol=" + exchange.ToUpper() + ":"; string pairName = SystemHelper.StripBadCode(market, Constants.WhiteListMinimal); - if (pairName.StartsWith(mainMarket)) { + if (pairName.StartsWith(mainMarket)) + { pairName = pairName.Replace(mainMarket, "") + mainMarket; } result += pairName; - } else { - switch (exchange) { + } + else + { + switch (exchange) + { case "Bittrex": result = "https://bittrex.com/Market/Index?MarketName=" + market; break; @@ -459,10 +566,12 @@ namespace Core.Helper { return result; } - public static string GetFullMarketName(string mainMarket, string market, string exchange) { + public static string GetFullMarketName(string mainMarket, string market, string exchange) + { string result = market; - switch (exchange) { + switch (exchange) + { case "Bittrex": result = mainMarket + "-" + market; break; @@ -478,12 +587,14 @@ namespace Core.Helper { return result; } - public static string GetTradingViewSymbol(string exchange, string market, string mainMarket) { + public static string GetTradingViewSymbol(string exchange, string market, string mainMarket) + { string result = exchange.ToUpper() + ":"; string pairName = SystemHelper.StripBadCode(market, Constants.WhiteListMinimal); - if (pairName.StartsWith(mainMarket)) { + if (pairName.StartsWith(mainMarket)) + { pairName = pairName.Replace(mainMarket, "") + mainMarket; } @@ -493,53 +604,68 @@ namespace Core.Helper { return result; } - public static string GetCurrencySymbol(string code) { + public static string GetCurrencySymbol(string code) + { string result = code; - try { + try + { System.Globalization.RegionInfo regionInfo = (from culture in System.Globalization.CultureInfo.GetCultures(System.Globalization.CultureTypes.AllCultures) where culture.Name.Length > 0 && !culture.IsNeutralCulture let region = new System.Globalization.RegionInfo(culture.LCID) where String.Equals(region.ISOCurrencySymbol, code, StringComparison.InvariantCultureIgnoreCase) select region).First(); result = regionInfo.CurrencySymbol; - } catch { + } + catch + { } return result; } - public static string PropertyToString(object property) { + public static string PropertyToString(object property) + { string result = property.ToString(); - if (!property.ToString().Equals("true", StringComparison.InvariantCultureIgnoreCase) && !property.ToString().Equals("false", StringComparison.InvariantCultureIgnoreCase)) { - try { + if (!property.ToString().Equals("true", StringComparison.InvariantCultureIgnoreCase) && !property.ToString().Equals("false", StringComparison.InvariantCultureIgnoreCase)) + { + try + { double resultDouble = Convert.ToDouble(property); result = resultDouble.ToString(new System.Globalization.CultureInfo("en-US")); - } catch { } - } else { + catch + { + } + } + else + { result = property.ToString().ToLower(); } return result; } - public static bool IsRecentVersion(string currentVersion, string latestVersion) { + public static bool IsRecentVersion(string currentVersion, string latestVersion) + { bool result = true; List currentVersionInfo = SystemHelper.ConvertTokenStringToListInt(currentVersion, "."); List latestVersionInfo = SystemHelper.ConvertTokenStringToListInt(latestVersion, "."); - if (currentVersionInfo[0] < latestVersionInfo[0]) { + if (currentVersionInfo[0] < latestVersionInfo[0]) + { result = false; } - if (currentVersionInfo[0] == latestVersionInfo[0] && currentVersionInfo[1] < latestVersionInfo[1]) { + if (currentVersionInfo[0] == latestVersionInfo[0] && currentVersionInfo[1] < latestVersionInfo[1]) + { result = false; } - if (currentVersionInfo[0] == latestVersionInfo[0] && currentVersionInfo[1] == latestVersionInfo[1] && currentVersionInfo[2] < latestVersionInfo[2]) { + if (currentVersionInfo[0] == latestVersionInfo[0] && currentVersionInfo[1] == latestVersionInfo[1] && currentVersionInfo[2] < latestVersionInfo[2]) + { result = false; } diff --git a/Core/Helper/TelegramHelper.cs b/Core/Helper/TelegramHelper.cs index d2d5ed0..16754ac 100644 --- a/Core/Helper/TelegramHelper.cs +++ b/Core/Helper/TelegramHelper.cs @@ -4,18 +4,26 @@ using Telegram.Bot; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types; -namespace Core.Helper { - public static class TelegramHelper { - public static void SendMessage(string botToken, Int64 chatId, string message, bool useSilentMode, LogHelper log) { - if (!botToken.Equals("") && chatId != 0) { - try { +namespace Core.Helper +{ + public static class TelegramHelper + { + public static void SendMessage(string botToken, Int64 chatId, string message, bool useSilentMode, LogHelper log) + { + if (!botToken.Equals("") && chatId != 0) + { + try + { TelegramBotClient botClient = new TelegramBotClient(botToken); System.Threading.Tasks.Task sentMessage = botClient.SendTextMessageAsync(chatId, message, ParseMode.Markdown, false, useSilentMode); - if (sentMessage.IsCompleted) { + if (sentMessage.IsCompleted) + { log.DoLogDebug("Telegram message sent to ChatId " + chatId.ToString() + " on Bot Token '" + botToken + "'"); } - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical("Exception sending telegram message to ChatId " + chatId.ToString() + " on Bot Token '" + botToken + "'", ex); } } diff --git a/Core/Helper/ZIPHelper.cs b/Core/Helper/ZIPHelper.cs index 47f4d8f..e144014 100644 --- a/Core/Helper/ZIPHelper.cs +++ b/Core/Helper/ZIPHelper.cs @@ -6,20 +6,25 @@ using System.IO; using System.Text; using ICSharpCode.SharpZipLib.Zip; -namespace Core.Helper { - - public class ZIPHelper { +namespace Core.Helper +{ - public static bool CreateZipFile(ArrayList filePaths, string outputPath) { + public class ZIPHelper + { + + public static bool CreateZipFile(ArrayList filePaths, string outputPath) + { bool result = true; ZipOutputStream pack = new ZipOutputStream(File.Create(outputPath)); - try { + try + { // set compression level pack.SetLevel(5); - foreach (string filePath in filePaths) { + foreach (string filePath in filePaths) + { FileStream fs = File.OpenRead(filePath); // allocate buffer @@ -32,9 +37,13 @@ namespace Core.Helper { pack.Write(buffer, 0, buffer.Length); } - } catch { + } + catch + { result = false; - } finally { + } + finally + { pack.Finish(); pack.Close(); } @@ -42,15 +51,20 @@ namespace Core.Helper { return result; } - public static ArrayList ExtractFileFromZipFile(string filePath, string destinationPath, bool isInvoicePackage) { + public static ArrayList ExtractFileFromZipFile(string filePath, string destinationPath, bool isInvoicePackage) + { ArrayList result = new ArrayList(); ZipFile zip = new ZipFile(File.OpenRead(filePath)); - try { - foreach (ZipEntry entry in zip) { - if (entry.IsFile) { + try + { + foreach (ZipEntry entry in zip) + { + if (entry.IsFile) + { string fileName = entry.Name; - if (isInvoicePackage) { + if (isInvoicePackage) + { fileName = fileName.Replace("unsigned", "signed"); } @@ -58,25 +72,32 @@ namespace Core.Helper { Stream inputStream = zip.GetInputStream(entry); FileStream fileStream = new FileStream(destinationPath + fileName, FileMode.Create); - try { + try + { CopyStream(inputStream, fileStream); - } finally { + } + finally + { fileStream.Close(); inputStream.Close(); } } } - } finally { + } + finally + { zip.Close(); } return result; } - private static void CopyStream(Stream input, Stream output) { + private static void CopyStream(Stream input, Stream output) + { byte[] buffer = new byte[0x1000]; int read; - while ((read = input.Read(buffer, 0, buffer.Length)) > 0) { + while ((read = input.Read(buffer, 0, buffer.Length)) > 0) + { output.Write(buffer, 0, read); } } diff --git a/Core/Main/Constants.cs b/Core/Main/Constants.cs index b2eeebc..923cea4 100644 --- a/Core/Main/Constants.cs +++ b/Core/Main/Constants.cs @@ -2,8 +2,10 @@ using System.Collections.Generic; using System.Text; -namespace Core.Main { - public static class Constants { +namespace Core.Main +{ + public static class Constants + { // Minimales Datum (für NULL) public static DateTime confMinDate = (DateTime)System.Data.SqlTypes.SqlDateTime.MinValue; public static DateTime confMaxDate = (DateTime)System.Data.SqlTypes.SqlDateTime.MaxValue; diff --git a/Core/Main/PTMagic.cs b/Core/Main/PTMagic.cs index db32a41..623e227 100644 --- a/Core/Main/PTMagic.cs +++ b/Core/Main/PTMagic.cs @@ -13,10 +13,13 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; -namespace Core.Main { - public class PTMagic { +namespace Core.Main +{ + public class PTMagic + { - public PTMagic(LogHelper log) { + public PTMagic(LogHelper log) + { this.Log = log; } @@ -62,351 +65,466 @@ namespace Core.Main { private Dictionary _singleMarketSettingsCount = new Dictionary(); Dictionary> _triggeredSingleMarketSettings = new Dictionary>(); - public LogHelper Log { - get { + public LogHelper Log + { + get + { return _log; } - set { + set + { _log = value; } } - public PTMagicConfiguration PTMagicConfiguration { - get { + public PTMagicConfiguration PTMagicConfiguration + { + get + { return _systemConfiguration; } - set { + set + { _systemConfiguration = value; } } - public System.Timers.Timer Timer { - get { + public System.Timers.Timer Timer + { + get + { return _timer; } - set { + set + { _timer = value; } } - public Summary LastRuntimeSummary { - get { + public Summary LastRuntimeSummary + { + get + { return _lastRuntimeSummary; } - set { + set + { _lastRuntimeSummary = value; } } - public int State { - get { + public int State + { + get + { return _state; } - set { + set + { _state = value; } } - public int RunCount { - get { + public int RunCount + { + get + { return _runCount; } - set { + set + { _runCount = value; } } - public int TotalElapsedSeconds { - get { + public int TotalElapsedSeconds + { + get + { return _totalElapsedSeconds; } - set { + set + { _totalElapsedSeconds = value; } } - public int ProfitTrailerMajorVersion { - get { + public int ProfitTrailerMajorVersion + { + get + { return _profitTrailerMajorVersion; } - set { + set + { _profitTrailerMajorVersion = value; } } - public bool GlobalSettingWritten { - get { + public bool GlobalSettingWritten + { + get + { return _globalSettingWritten; } - set { + set + { _globalSettingWritten = value; } } - public bool SingleMarketSettingWritten { - get { + public bool SingleMarketSettingWritten + { + get + { return _singleMarketSettingWritten; } - set { + set + { _singleMarketSettingWritten = value; } } - public bool EnforceSettingsReapply { - get { + public bool EnforceSettingsReapply + { + get + { return _enforceSettingsReapply; } - set { + set + { _enforceSettingsReapply = value; } } - public DateTime LastSettingsChange { - get { + public DateTime LastSettingsChange + { + get + { return _lastSettingsChange; } - set { + set + { _lastSettingsChange = value; } } - public DateTime LastVersionCheck { - get { + public DateTime LastVersionCheck + { + get + { return _lastVersionCheck; } - set { + set + { _lastVersionCheck = value; } } - public DateTime LastFiatCurrencyCheck { - get { + public DateTime LastFiatCurrencyCheck + { + get + { return _lastFiatCurrencyCheck; } - set { + set + { _lastFiatCurrencyCheck = value; } } - public DateTime LastSettingFileCheck { - get { + public DateTime LastSettingFileCheck + { + get + { return _lastSettingFileCheck; } - set { + set + { _lastSettingFileCheck = value; } } - public DateTime LastRuntime { - get { + public DateTime LastRuntime + { + get + { return _lastRuntime; } - set { + set + { _lastRuntime = value; } } - public string DefaultSettingName { - get { + public string DefaultSettingName + { + get + { return _defaultSettingName; } - set { + set + { _defaultSettingName = value; } } - public string LastSetting { - get { + public string LastSetting + { + get + { return _lastSetting; } - set { + set + { _lastSetting = value; } } - public string ActiveSetting { - get { + public string ActiveSetting + { + get + { return _activeSetting; } - set { + set + { _activeSetting = value; } } - public string PairsFileName { - get { + public string PairsFileName + { + get + { return _pairsFileName; } - set { + set + { _pairsFileName = value; } } - public string DCAFileName { - get { + public string DCAFileName + { + get + { return _dcaFileName; } - set { + set + { _dcaFileName = value; } } - public string IndicatorsFileName { - get { + public string IndicatorsFileName + { + get + { return _indicatorsFileName; } - set { + set + { _indicatorsFileName = value; } } - public Version CurrentVersion { - get { + public Version CurrentVersion + { + get + { return _currentVersion; } - set { + set + { _currentVersion = value; } } - public string LatestVersion { - get { + public string LatestVersion + { + get + { return _latestVersion; } - set { + set + { _latestVersion = value; } } - public string LastMainFiatCurrency { - get { + public string LastMainFiatCurrency + { + get + { return _lastMainFiatCurrency; } - set { + set + { _lastMainFiatCurrency = value; } } - public double LastMainFiatCurrencyExchangeRate { - get { + public double LastMainFiatCurrencyExchangeRate + { + get + { return _lastMainFiatCurrencyExchangeRate; } - set { + set + { _lastMainFiatCurrencyExchangeRate = value; } } - public List SingleMarketSettingSummaries { - get { + public List SingleMarketSettingSummaries + { + get + { return _singleMarketSettingSummaries; } - set { + set + { _singleMarketSettingSummaries = value; } } - public List PairsLines { - get { + public List PairsLines + { + get + { return _pairsLines; } - set { + set + { _pairsLines = value; } } - public List DCALines { - get { + public List DCALines + { + get + { return _dcaLines; } - set { + set + { _dcaLines = value; } } - public List IndicatorsLines { - get { + public List IndicatorsLines + { + get + { return _indicatorsLines; } - set { + set + { _indicatorsLines = value; } } - public List ExchangeMarketList { - get { + public List ExchangeMarketList + { + get + { return _exchangeMarketList; } - set { + set + { _exchangeMarketList = value; } } - public List MarketList { - get { + public List MarketList + { + get + { return _marketList; } - set { + set + { _marketList = value; } } - public Dictionary MarketInfos { - get { + public Dictionary MarketInfos + { + get + { return _marketInfos; } - set { + set + { _marketInfos = value; } } - public Dictionary> SingleMarketTrendChanges { - get { + public Dictionary> SingleMarketTrendChanges + { + get + { return _singleMarketTrendChanges; } - set { + set + { _singleMarketTrendChanges = value; } } - public Dictionary> GlobalMarketTrendChanges { - get { + public Dictionary> GlobalMarketTrendChanges + { + get + { return _globalMarketTrendChanges; } - set { + set + { _globalMarketTrendChanges = value; } } - public Dictionary AverageMarketTrendChanges { - get { + public Dictionary AverageMarketTrendChanges + { + get + { return _averageMarketTrendChanges; } - set { + set + { _averageMarketTrendChanges = value; } } - public Dictionary SingleMarketSettingsCount { - get { + public Dictionary SingleMarketSettingsCount + { + get + { return _singleMarketSettingsCount; } - set { + set + { _singleMarketSettingsCount = value; } } - public Dictionary> TriggeredSingleMarketSettings { - get { + public Dictionary> TriggeredSingleMarketSettings + { + get + { return _triggeredSingleMarketSettings; } - set { + set + { _triggeredSingleMarketSettings = value; } } #endregion #region PTMagic Startup Methods - public bool StartProcess() { + public bool StartProcess() + { bool result = true; this.Log.DoLogInfo(""); @@ -421,27 +539,32 @@ namespace Core.Main { this.Log.DoLogInfo("Starting PTMagic in " + Directory.GetCurrentDirectory()); this.Log.DoLogInfo("with .NET Core: " + Path.GetDirectoryName(typeof(object).Assembly.Location)); - if (!this.RunStartupChecks()) { + if (!this.RunStartupChecks()) + { return false; } - if (!this.InitializeConfiguration()) { + if (!this.InitializeConfiguration()) + { return false; } _configCheckResult = this.RunConfigurationChecks(); - if (!_configCheckResult) { + if (!_configCheckResult) + { this.Log.DoLogInfo("Starting configuration check retry in 5 seconds..."); System.Timers.Timer configCheckTimer = new System.Timers.Timer(5000); configCheckTimer.Enabled = true; configCheckTimer.Elapsed += new System.Timers.ElapsedEventHandler(this.ConfigCheckTimer_Elapsed); - while (!_configCheckResult && _configCheckRetryCount < 10) { + while (!_configCheckResult && _configCheckRetryCount < 10) + { Thread.Sleep(100); } configCheckTimer.Stop(); } - if (!_configCheckResult) { + if (!_configCheckResult) + { return false; } @@ -454,16 +577,19 @@ namespace Core.Main { return result; } - public bool RunStartupChecks() { + public bool RunStartupChecks() + { bool result = true; // Startup checks - if (!File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "settings.general.json")) { + if (!File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "settings.general.json")) + { this.Log.DoLogError("File 'settings.general.json' not found! Please review the setup steps on the wiki and double check every step that involves copying files!"); result = false; } - if (!File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "settings.analyzer.json")) { + if (!File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "settings.analyzer.json")) + { this.Log.DoLogError("File 'settings.analyzer.json' not found! Please review the setup steps on the wiki and double check every step that involves copying files!"); result = false; } @@ -471,15 +597,19 @@ namespace Core.Main { return result; } - public bool InitializeConfiguration() { + public bool InitializeConfiguration() + { bool result = true; - try { + try + { this.PTMagicConfiguration = new PTMagicConfiguration(); this.Log.DoLogInfo("Configuration loaded. Found " + this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.Count.ToString() + " Market Trends, " + this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Count.ToString() + " Global Settings and " + this.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings.Count.ToString() + " Single Market Settings."); - } catch (Exception ex) { + } + catch (Exception ex) + { result = false; this.Log.DoLogCritical("Error loading configuration!", ex); throw (ex); @@ -488,63 +618,81 @@ namespace Core.Main { return result; } - public bool RunConfigurationChecks() { + public bool RunConfigurationChecks() + { bool result = true; // Check for valid default setting GlobalSetting defaultSetting = this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(s => s.SettingName.Equals("default", StringComparison.InvariantCultureIgnoreCase)); - if (defaultSetting == null) { + if (defaultSetting == null) + { defaultSetting = this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(s => s.SettingName.IndexOf("default", StringComparison.InvariantCultureIgnoreCase) > -1); - if (defaultSetting != null) { + if (defaultSetting != null) + { this.Log.DoLogDebug("No setting named 'default' found, taking '" + defaultSetting.SettingName + "' as default."); this.DefaultSettingName = defaultSetting.SettingName; - } else { + } + else + { this.Log.DoLogError("No 'default' setting found! Terminating process..."); result = false; } - } else { + } + else + { this.DefaultSettingName = defaultSetting.SettingName; } // 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("Poloniex", 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..."); result = false; } // Check if the program is enabled - if (this.PTMagicConfiguration.GeneralSettings.Application.IsEnabled) { + if (this.PTMagicConfiguration.GeneralSettings.Application.IsEnabled) + { if (this.PTMagicConfiguration.GeneralSettings.Application.TestMode) this.Log.DoLogInfo("TESTMODE ENABLED - No files will be changed!"); // Check for PT Directory DirectoryInfo ptRoot = new DirectoryInfo(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath); - if (ptRoot.Exists) { + if (ptRoot.Exists) + { this.Log.DoLogInfo("Profit Trailer directory found"); // Run checks dependant on PT version this.ProfitTrailerMajorVersion = this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMajorVersion; - if (this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMajorVersion < 2) { + if (this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMajorVersion < 2) + { result = RunProfitTrailerTradingFilesChecks(); - } else { + } + else + { result = RunProfitTrailerSettingsAPIChecks(); } - } else { + } + else + { this.Log.DoLogError("Profit Trailer directory not found (" + this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + ")"); result = false; } // Check for CoinMarketCap API Key - if (!this.PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey.Equals("")) { - this.Log.DoLogInfo("CoinMarketCap API KEY found"); - } - else { - this.Log.DoLogInfo("No CoinMarketCap API KEY specified! You can't use CoinMarketCap in your settings.analyzer.json"); + if (!this.PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey.Equals("")) + { + this.Log.DoLogInfo("CoinMarketCap API KEY found"); } - } - - else { + else + { + this.Log.DoLogInfo("No CoinMarketCap API KEY specified! You can't use CoinMarketCap in your settings.analyzer.json"); + } + } + + else + { this.Log.DoLogWarn("PTMagic disabled, shutting down..."); result = false; } @@ -552,7 +700,8 @@ namespace Core.Main { return result; } - private bool RunProfitTrailerTradingFilesChecks() { + private bool RunProfitTrailerTradingFilesChecks() + { bool result = true; this.Log.DoLogInfo("========== STARTING CHECKS FOR Profit Trailer 1.x =========="); @@ -560,110 +709,151 @@ namespace Core.Main { // Check for settings directory "trading" string ptTradingPath = this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar; DirectoryInfo ptTrading = new DirectoryInfo(ptTradingPath); - if (ptTrading.Exists) { + if (ptTrading.Exists) + { this.Log.DoLogInfo("Profit Trailer 1.x check: Trading directory found"); #region File Checks this.Log.DoLogInfo("Profit Trailer 1.x check: Checking for Pairs Properties file"); - if (File.Exists(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.PairsFileName)) { + if (File.Exists(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.PairsFileName)) + { this.Log.DoLogInfo("Profit Trailer 1.x check: PAIRS.PROPERTIES found!"); - } else { + } + else + { this.PairsFileName = "PAIRS.properties"; - if (File.Exists(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.PairsFileName)) { + if (File.Exists(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.PairsFileName)) + { this.Log.DoLogInfo("Profit Trailer 1.x check: PAIRS.properties found!"); - } else { + } + else + { this.Log.DoLogError("Profit Trailer 1.x check: No 'PAIRS.properties' found in " + this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar); result = false; } } this.Log.DoLogInfo("Profit Trailer 1.x check: Checking for DCA Properties file"); - if (File.Exists(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.DCAFileName)) { + if (File.Exists(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.DCAFileName)) + { this.Log.DoLogInfo("Profit Trailer 1.x check: DCA.PROPERTIES found!"); - } else { + } + else + { this.DCAFileName = "DCA.properties"; - if (File.Exists(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.DCAFileName)) { + if (File.Exists(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.DCAFileName)) + { this.Log.DoLogInfo("Profit Trailer 1.x check: DCA.properties found!"); - } else { + } + else + { this.Log.DoLogError("Profit Trailer 1.x check: No 'DCA.properties' found in " + this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar); result = false; } } this.Log.DoLogInfo("Profit Trailer 1.x check: Checking for Indicators Properties file"); - if (File.Exists(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.IndicatorsFileName)) { + if (File.Exists(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.IndicatorsFileName)) + { this.Log.DoLogInfo("Profit Trailer 1.x check: INDICATORS.PROPERTIES found!"); - } else { + } + else + { this.IndicatorsFileName = "INDICATORS.properties"; - if (File.Exists(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.IndicatorsFileName)) { + if (File.Exists(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.IndicatorsFileName)) + { this.Log.DoLogInfo("Profit Trailer 1.x check: INDICATORS.properties found!"); - } else { + } + else + { this.Log.DoLogError("Profit Trailer 1.x check: No 'INDICATORS.properties' found in " + this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar); result = false; } } #endregion - } else { + } + else + { this.Log.DoLogError("Profit Trailer 1.x check: Trading settings directory not found (" + ptTradingPath + ")"); result = false; } - - if (result) { + + if (result) + { this.Log.DoLogInfo("========== CHECKS FOR Profit Trailer 1.x COMPLETED! =========="); - } else { - this.Log.DoLogInfo("========== CHECKS FOR Profit Trailer 1.x FAILED! =========="); + } + else + { + this.Log.DoLogInfo("========== CHECKS FOR Profit Trailer 1.x FAILED! =========="); } return result; } - private bool RunProfitTrailerSettingsAPIChecks() { + private bool RunProfitTrailerSettingsAPIChecks() + { bool result = true; this.Log.DoLogInfo("========== STARTING CHECKS FOR Profit Trailer 2.x =========="); // Check for PT license key - if (!this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerLicense.Equals("")) { + if (!this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerLicense.Equals("")) + { this.Log.DoLogInfo("Profit Trailer 2.x check: Profit Trailer license found"); - } else { + } + else + { this.Log.DoLogError("Profit Trailer 2.x check: No Profit Trailer license key specified! The license key is necessary to adjust your Profit Trailer settings since 2.0"); result = false; } // Check for PT default setting key - if (!this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName.Equals("")) { + if (!this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName.Equals("")) + { this.Log.DoLogInfo("Profit Trailer 2.x check: Profit Trailer default setting name specified"); - } else { + } + else + { this.Log.DoLogError("Profit Trailer 2.x check: No Profit Trailer default setting name specified! The default setting name is necessary to adjust your Profit Trailer settings since 2.0"); result = false; } // Check for PT monitor - if (!this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL.Equals("")) { + if (!this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL.Equals("")) + { this.Log.DoLogInfo("Profit Trailer 2.x check: Profit Trailer monitor URL found"); - } else { + } + else + { this.Log.DoLogError("Profit Trailer 2.x check: No Profit Trailer monitor URL specified! The monitor URL is necessary to adjust your Profit Trailer settings since 2.0"); result = false; } // Check if PT monitor is reachable - if (SystemHelper.UrlIsReachable(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL)) { + if (SystemHelper.UrlIsReachable(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL)) + { this.Log.DoLogInfo("Profit Trailer 2.x check: Profit Trailer monitor connection test succeeded"); - } else { + } + else + { this.Log.DoLogError("Profit Trailer 2.x check: Your Profit Trailer monitor (" + this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL + ") is not available! Make sure your Profit Trailer bot is up and running and your monitor is accessible."); result = false; } - if (result) { + if (result) + { this.Log.DoLogInfo("========== CHECKS FOR Profit Trailer 2.x COMPLETED! =========="); - } else { - this.Log.DoLogInfo("========== CHECKS FOR Profit Trailer 2.x FAILED! =========="); + } + else + { + this.Log.DoLogInfo("========== CHECKS FOR Profit Trailer 2.x FAILED! =========="); } return result; } - public void StartPTMagicIntervalTimer() { + public void StartPTMagicIntervalTimer() + { this.Timer = new System.Timers.Timer(this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 60 * 1000); this.Timer.Enabled = true; this.Timer.Elapsed += new System.Timers.ElapsedEventHandler(this.PTMagicIntervalTimer_Elapsed); @@ -676,25 +866,30 @@ namespace Core.Main { #endregion #region PTMagic Interval Methods - public void ConfigCheckTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { + public void ConfigCheckTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + { // Reinit config in case the user changed something this.InitializeConfiguration(); _configCheckResult = this.RunConfigurationChecks(); _configCheckRetryCount++; - if (!_configCheckResult) { + if (!_configCheckResult) + { this.Log.DoLogError("Configuration check retry " + _configCheckRetryCount + "/10 failed, starting next retry in 5 seconds..."); } } - public void PTMagicIntervalTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { + public void PTMagicIntervalTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + { // Check if the bot is idle - if (this.State == Constants.PTMagicBotState_Idle) { + if (this.State == Constants.PTMagicBotState_Idle) + { this.RunCount++; bool headerLinesAdded = false; this.EnforceSettingsReapply = this.HaveSettingsChanged(); - if (PTMagicConfiguration.GeneralSettings.Application.IsEnabled) { + if (PTMagicConfiguration.GeneralSettings.Application.IsEnabled) + { // Validate settings this.ValidateSettings(); @@ -768,22 +963,31 @@ namespace Core.Main { // Change state to Finished / Stopped this.State = Constants.PTMagicBotState_Idle; - } else { + } + else + { this.State = Constants.PTMagicBotState_Idle; Log.DoLogWarn("PTMagic disabled, shutting down until next raid..."); } - } else { - if (this.RunCount > 1) { + } + else + { + if (this.RunCount > 1) + { Log.DoLogWarn("PTMagic already raiding since " + this.LastRuntime.ToString() + " - Process frozen? Checking things..."); - if (File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "LastRuntimeSummary.json")) { + if (File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "LastRuntimeSummary.json")) + { FileInfo fiLastSummary = new FileInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "LastRuntimeSummary.json"); - if (fiLastSummary.LastWriteTime < DateTime.Now.AddMinutes(-(this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 2))) { + if (fiLastSummary.LastWriteTime < DateTime.Now.AddMinutes(-(this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 2))) + { Log.DoLogWarn("PTMagic seems to have frozen after raid " + this.RunCount.ToString() + ", but don't worry I will sacrifice some Magicbots to get this running again..."); this.State = Constants.PTMagicBotState_Idle; Log.DoLogInfo("PTMagic status resetted, waiting for the next raid to be good to go again."); } - } else { + } + else + { Log.DoLogWarn("No LastRuntimeSummary.json found after raid " + this.RunCount.ToString() + ", trying to reset PT Magic status..."); this.State = Constants.PTMagicBotState_Idle; Log.DoLogInfo("PTMagic status resetted, waiting for the next raid to be good to go again."); @@ -792,30 +996,39 @@ namespace Core.Main { } } - private bool HaveSettingsChanged() { + private bool HaveSettingsChanged() + { bool result = false; FileInfo generalSettingsFile = new FileInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "settings.general.json"); FileInfo analyzerSettingsFile = new FileInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "settings.analyzer.json"); - if (generalSettingsFile.LastWriteTime > this.LastSettingFileCheck || analyzerSettingsFile.LastWriteTime > this.LastSettingFileCheck) { + if (generalSettingsFile.LastWriteTime > this.LastSettingFileCheck || analyzerSettingsFile.LastWriteTime > this.LastSettingFileCheck) + { Log.DoLogInfo("Detected configuration changes. Reloading settings..."); - try { + try + { PTMagicConfiguration = new PTMagicConfiguration(); GlobalSetting defaultSetting = this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(s => s.SettingName.Equals("default", StringComparison.InvariantCultureIgnoreCase)); - if (defaultSetting == null) { + if (defaultSetting == null) + { defaultSetting = this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(s => s.SettingName.IndexOf("default", StringComparison.InvariantCultureIgnoreCase) > -1); - if (defaultSetting != null) { + if (defaultSetting != null) + { Log.DoLogDebug("No setting named 'default' found, taking '" + defaultSetting.SettingName + "' as default."); this.DefaultSettingName = defaultSetting.SettingName; - } else { + } + else + { Log.DoLogError("No 'default' setting found! Terminating process..."); this.Timer.Stop(); Exception ex = new Exception("No 'default' setting found!Terminating process..."); throw ex; } - } else { + } + else + { this.DefaultSettingName = defaultSetting.SettingName; } @@ -823,7 +1036,8 @@ namespace Core.Main { this.LastSettingFileCheck = DateTime.Now; result = true; - if (this.Timer.Interval != this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 60 * 1000) { + if (this.Timer.Interval != this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 60 * 1000) + { Log.DoLogInfo("Setting for 'IntervalMinutes' changed in MarketAnalyzer, setting new timer..."); this.Timer.Stop(); this.Timer.Interval = this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 60 * 1000; @@ -832,31 +1046,43 @@ namespace Core.Main { } SettingsFiles.CheckPresets(this.PTMagicConfiguration, this.Log, true); - } catch (Exception ex) { + } + catch (Exception ex) + { Log.DoLogCritical("Error loading new configuration!", ex); } - } else { + } + else + { result = SettingsFiles.CheckPresets(this.PTMagicConfiguration, this.Log, false); } return result; } - private void ValidateSettings() { + private void ValidateSettings() + { // Check for a valid exchange - if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange == null) { + if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange == null) + { Log.DoLogError("Your setting for Application.Exchange in settings.general.json is invalid (null)! Terminating process."); this.Timer.Stop(); Exception ex = new Exception("Your setting for Application.Exchange in settings.general.json is invalid (null)! Terminating process."); throw ex; - } else { - if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("")) { + } + else + { + if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("")) + { Log.DoLogError("Your setting for Application.Exchange in settings.general.json is invalid (empty)! Terminating process."); this.Timer.Stop(); Exception ex = new Exception("Your setting for Application.Exchange in settings.general.json is invalid (empty)! Terminating process."); throw ex; - } 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)) { + } + 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)) + { Log.DoLogError("Your setting for Application.Exchange in settings.general.json is invalid (" + this.PTMagicConfiguration.GeneralSettings.Application.Exchange + ")! Terminating process."); this.Timer.Stop(); Exception ex = new Exception("Your setting for Application.Exchange in settings.general.json is invalid (" + this.PTMagicConfiguration.GeneralSettings.Application.Exchange + ")! Terminating process."); @@ -866,30 +1092,38 @@ namespace Core.Main { } } - private void CheckLatestGitHubVersion(string currentVersion) { + private void CheckLatestGitHubVersion(string currentVersion) + { // Get latest version number - if (this.LastVersionCheck < DateTime.Now.AddMinutes(-30)) { + if (this.LastVersionCheck < DateTime.Now.AddMinutes(-30)) + { this.LatestVersion = BaseAnalyzer.GetLatestGitHubRelease(this.Log, currentVersion); this.LastVersionCheck = DateTime.Now; - if (!SystemHelper.IsRecentVersion(currentVersion, this.LatestVersion)) { + if (!SystemHelper.IsRecentVersion(currentVersion, this.LatestVersion)) + { this.Log.DoLogWarn("Your bot is out of date! The most recent version of PTMagic is " + this.LatestVersion); } } } - private void GetMainFiatCurrencyDetails() { + private void GetMainFiatCurrencyDetails() + { this.LastRuntimeSummary.MainFiatCurrency = this.LastMainFiatCurrency; this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = this.LastMainFiatCurrencyExchangeRate; - if (this.LastFiatCurrencyCheck < DateTime.Now.AddHours(-12) && !this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency.Equals("USD", StringComparison.InvariantCultureIgnoreCase)) { - try { + if (this.LastFiatCurrencyCheck < DateTime.Now.AddHours(-12) && !this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency.Equals("USD", StringComparison.InvariantCultureIgnoreCase)) + { + try + { this.LastRuntimeSummary.MainFiatCurrency = this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency; this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = BaseAnalyzer.GetMainFiatCurrencyRate(this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency, this.Log); this.LastMainFiatCurrency = this.LastRuntimeSummary.MainFiatCurrency; this.LastMainFiatCurrencyExchangeRate = this.LastRuntimeSummary.MainFiatCurrencyExchangeRate; this.LastFiatCurrencyCheck = DateTime.Now; - } catch (Exception ex) { + } + catch (Exception ex) + { // Fallback to USD in case something went wrong this.Log.DoLogError("Fixer.io exchange rate check error: " + ex.Message); @@ -901,15 +1135,19 @@ namespace Core.Main { } } - private void LoadCurrentProfitTrailerProperties(string pairsPropertiesPath, string dcaPropertiesPath, string indicatorsPropertiesPath) { - if (this.ProfitTrailerMajorVersion == 1) { + private void LoadCurrentProfitTrailerProperties(string pairsPropertiesPath, string dcaPropertiesPath, string indicatorsPropertiesPath) + { + if (this.ProfitTrailerMajorVersion == 1) + { // Load current PT properties from files (Valid for PT 1.x) this.Log.DoLogInfo("Loading current Profit Trailer properties from files..."); this.PairsLines = File.ReadLines(pairsPropertiesPath).ToList(); this.DCALines = File.ReadLines(dcaPropertiesPath).ToList(); this.IndicatorsLines = File.ReadLines(indicatorsPropertiesPath).ToList(); - } else { + } + else + { // Load current PT properties from API (Valid for PT 2.x and above) this.Log.DoLogInfo("Loading current Profit Trailer properties from API..."); @@ -918,9 +1156,12 @@ namespace Core.Main { this.IndicatorsLines = SettingsAPI.GetPropertyLinesFromAPI("INDICATORS", this.PTMagicConfiguration, this.Log); } - if (this.PairsLines != null && this.DCALines != null && this.IndicatorsLines != null) { + if (this.PairsLines != null && this.DCALines != null && this.IndicatorsLines != null) + { this.Log.DoLogInfo("Properties loaded - P (" + this.PairsLines.Count.ToString() + " lines) - D (" + this.DCALines.Count.ToString() + " lines) - I (" + this.IndicatorsLines.Count.ToString() + " lines)."); - } else { + } + else + { this.Log.DoLogError("Unable to load all Profit Trailer properties! Waiting for the next interval to retry..."); Exception ex = new Exception("Unable to load all Profit Trailer properties! Waiting for the next interval to retry..."); this.State = 0; @@ -933,61 +1174,85 @@ namespace Core.Main { this.LastRuntimeSummary.ProfitTrailerMajorVersion = this.ProfitTrailerMajorVersion; } - private void LoadSMSSummaries() { + private void LoadSMSSummaries() + { this.Log.DoLogInfo("Loading Single Market Setting Summaries..."); this.SingleMarketSettingSummaries = new List(); - if (File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "SingleMarketSettingSummary.json")) { - try { + if (File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "SingleMarketSettingSummary.json")) + { + try + { Dictionary smsVerificationResult = new Dictionary(); // Cleanup SMS Summaries in case a SMS got removed - foreach (SingleMarketSettingSummary smsSummary in JsonConvert.DeserializeObject>(System.IO.File.ReadAllText(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "SingleMarketSettingSummary.json"))) { + foreach (SingleMarketSettingSummary smsSummary in JsonConvert.DeserializeObject>(System.IO.File.ReadAllText(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "SingleMarketSettingSummary.json"))) + { string smsName = smsSummary.SingleMarketSetting.SettingName; bool smsIsValid = false; - if (smsVerificationResult.ContainsKey(smsName)) { + if (smsVerificationResult.ContainsKey(smsName)) + { smsIsValid = smsVerificationResult[smsName]; - } else { + } + else + { SingleMarketSetting sms = this.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings.Find(s => s.SettingName.Equals(smsName)); - if (sms != null) { + if (sms != null) + { smsIsValid = true; smsVerificationResult.Add(smsName, true); - } else { + } + else + { smsVerificationResult.Add(smsName, false); } } - if (smsIsValid) { + if (smsIsValid) + { this.SingleMarketSettingSummaries.Add(smsSummary); } } this.Log.DoLogInfo("Single Market Setting Summaries loaded."); - } catch { } + } + catch { } } } - private void BuildMarketData() { + private void BuildMarketData() + { - if (!this.PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey.Equals("")) { - // Get most recent market data from CMC - string cmcMarketDataResult = CoinMarketCap.GetMarketData(this.PTMagicConfiguration, this.Log); + if (!this.PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey.Equals("")) + { + // Get most recent market data from CMC + string cmcMarketDataResult = CoinMarketCap.GetMarketData(this.PTMagicConfiguration, this.Log); + } + else + { + this.Log.DoLogInfo("No CMC API-Key specified. No CMC Data will be pulled"); } - if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex", StringComparison.InvariantCultureIgnoreCase)) { + if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex", StringComparison.InvariantCultureIgnoreCase)) + { // Get most recent market data from Bittrex this.ExchangeMarketList = Bittrex.GetMarketData(this.LastRuntimeSummary.MainMarket, this.MarketInfos, this.PTMagicConfiguration, this.Log); - } else if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance", StringComparison.InvariantCultureIgnoreCase)) { + } + else if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance", StringComparison.InvariantCultureIgnoreCase)) + { // 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("Poloniex", StringComparison.InvariantCultureIgnoreCase)) { + } + else if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex", StringComparison.InvariantCultureIgnoreCase)) + { // Get most recent market data from Poloniex this.ExchangeMarketList = Poloniex.GetMarketData(this.LastRuntimeSummary.MainMarket, this.MarketInfos, this.PTMagicConfiguration, this.Log); } // Check if problems occured during the Exchange contact - if (this.ExchangeMarketList == null) { + if (this.ExchangeMarketList == null) + { Exception ex = new Exception("Unable to contact " + this.PTMagicConfiguration.GeneralSettings.Application.Exchange + " for fresh market data. Trying again in " + this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes + " minute(s)."); Log.DoLogError(ex.Message); this.State = Constants.PTMagicBotState_Idle; @@ -995,27 +1260,37 @@ namespace Core.Main { } } - private void BuildMarketList() { + private void BuildMarketList() + { string marketPairs = SettingsHandler.GetMarketPairs(this.PTMagicConfiguration, this.PairsLines, this.Log); - if (marketPairs.ToLower().Equals("all") || marketPairs.ToLower().Equals("false") || marketPairs.ToLower().Equals("true") || marketPairs.Equals("")) { + if (marketPairs.ToLower().Equals("all") || marketPairs.ToLower().Equals("false") || marketPairs.ToLower().Equals("true") || marketPairs.Equals("")) + { this.MarketList = this.ExchangeMarketList; - } else { - if (this.ProfitTrailerMajorVersion == 1) { + } + else + { + if (this.ProfitTrailerMajorVersion == 1) + { this.MarketList = SystemHelper.ConvertTokenStringToList(marketPairs, ","); - } else { + } + else + { // Since PT 2.0 the main market is no longer included in the market list so we need to rebuild the list List originalMarketList = SystemHelper.ConvertTokenStringToList(marketPairs, ","); - foreach (string market in originalMarketList) { + foreach (string market in originalMarketList) + { this.MarketList.Add(SystemHelper.GetFullMarketName(this.LastRuntimeSummary.MainMarket, market, this.PTMagicConfiguration.GeneralSettings.Application.Exchange)); } } } } - private void ValidateMarketList() { + private void ValidateMarketList() + { // Check if markets are valid for the selected main market List validMarkets = this.MarketList.FindAll(m => m.IndexOf(this.LastRuntimeSummary.MainMarket, StringComparison.InvariantCultureIgnoreCase) > -1); - if (validMarkets.Count == 0) { + if (validMarkets.Count == 0) + { Exception ex = new Exception("No valid pairs found for main market '" + this.LastRuntimeSummary.MainMarket + "' in configured pars list (" + SystemHelper.ConvertListToTokenString(this.MarketList, ",", true) + ")! Terminating process..."); Log.DoLogError(ex.Message); this.State = Constants.PTMagicBotState_Idle; @@ -1024,7 +1299,8 @@ namespace Core.Main { } } - private void BuildGlobalMarketTrends() { + private void BuildGlobalMarketTrends() + { this.Log.DoLogInfo("Build global market trends..."); this.SingleMarketTrendChanges = BaseAnalyzer.BuildMarketTrends("Exchange", this.LastRuntimeSummary.MainMarket, this.MarketList, "Volume", false, new Dictionary>(), this.PTMagicConfiguration, this.Log); this.GlobalMarketTrendChanges = new Dictionary>(); @@ -1033,10 +1309,13 @@ namespace Core.Main { this.GlobalMarketTrendChanges = BaseAnalyzer.BuildMarketTrends("CoinMarketCap", this.LastRuntimeSummary.MainMarket, new List(), "", true, this.GlobalMarketTrendChanges, this.PTMagicConfiguration, this.Log); // Bittrex - foreach (MarketTrend marketTrend in this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.FindAll(mt => mt.Platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase))) { - if (this.SingleMarketTrendChanges.ContainsKey(marketTrend.Name)) { + foreach (MarketTrend marketTrend in this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.FindAll(mt => mt.Platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase))) + { + if (this.SingleMarketTrendChanges.ContainsKey(marketTrend.Name)) + { int maxMarkets = this.SingleMarketTrendChanges[marketTrend.Name].Count; - if (marketTrend.MaxMarkets > 0 && marketTrend.MaxMarkets <= this.SingleMarketTrendChanges[marketTrend.Name].Count) { + if (marketTrend.MaxMarkets > 0 && marketTrend.MaxMarkets <= this.SingleMarketTrendChanges[marketTrend.Name].Count) + { maxMarkets = marketTrend.MaxMarkets; } @@ -1049,48 +1328,63 @@ namespace Core.Main { this.Log.DoLogInfo("Global market trends built."); } - private void CheckGlobalSettingsTriggers(ref GlobalSetting triggeredSetting, ref List matchedTriggers) { + private void CheckGlobalSettingsTriggers(ref GlobalSetting triggeredSetting, ref List matchedTriggers) + { this.Log.DoLogInfo("Checking global settings triggers..."); - foreach (GlobalSetting globalSetting in this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings) { + foreach (GlobalSetting globalSetting in this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings) + { // Reset triggers for each setting matchedTriggers = new List(); - if (globalSetting.Triggers.Count > 0) { + if (globalSetting.Triggers.Count > 0) + { this.Log.DoLogInfo("Checking triggers for '" + globalSetting.SettingName + "'..."); List triggerResults = new List(); - foreach (Trigger trigger in globalSetting.Triggers) { + foreach (Trigger trigger in globalSetting.Triggers) + { MarketTrend marketTrend = this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.Find(mt => mt.Name == trigger.MarketTrendName); - if (marketTrend != null) { + if (marketTrend != null) + { // Get market trend change for trigger - if (this.AverageMarketTrendChanges.ContainsKey(marketTrend.Name)) { + if (this.AverageMarketTrendChanges.ContainsKey(marketTrend.Name)) + { double averageMarketTrendChange = this.AverageMarketTrendChanges[marketTrend.Name]; - if (averageMarketTrendChange >= trigger.MinChange && averageMarketTrendChange < trigger.MaxChange) { + if (averageMarketTrendChange >= trigger.MinChange && averageMarketTrendChange < trigger.MaxChange) + { // Trigger met! this.Log.DoLogInfo("Trigger '" + trigger.MarketTrendName + "' triggered! TrendChange = " + averageMarketTrendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"); string triggerContent = trigger.MarketTrendName + " - "; - if (trigger.MinChange != Constants.MinTrendChange) { + if (trigger.MinChange != Constants.MinTrendChange) + { triggerContent += " - Min: " + trigger.MinChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"; } - if (trigger.MaxChange != Constants.MaxTrendChange) { + if (trigger.MaxChange != Constants.MaxTrendChange) + { triggerContent += " - Max: " + trigger.MaxChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"; } matchedTriggers.Add(triggerContent); triggerResults.Add(true); - } else { + } + else + { this.Log.DoLogDebug("Trigger '" + trigger.MarketTrendName + "' not triggered. TrendChange = " + averageMarketTrendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"); triggerResults.Add(false); } - } else { + } + else + { this.Log.DoLogError("Trigger '" + trigger.MarketTrendName + "' not found in this.AverageMarketTrendChanges[] (" + SystemHelper.ConvertListToTokenString(this.AverageMarketTrendChanges.Keys.ToList(), ",", true) + "). Unable to load recent trends?"); triggerResults.Add(false); } - } else { + } + else + { this.Log.DoLogWarn("Market Trend '" + trigger.MarketTrendName + "' not found! Trigger ignored!"); triggerResults.Add(false); } @@ -1098,7 +1392,8 @@ namespace Core.Main { // Check if all triggers have to get triggered or just one bool settingTriggered = false; - switch (globalSetting.TriggerConnection.ToLower()) { + switch (globalSetting.TriggerConnection.ToLower()) + { case "and": settingTriggered = triggerResults.FindAll(tr => tr == false).Count == 0; break; @@ -1108,7 +1403,8 @@ namespace Core.Main { } // Setting got triggered -> Activate it! - if (settingTriggered) { + if (settingTriggered) + { triggeredSetting = globalSetting; break; } @@ -1116,27 +1412,33 @@ namespace Core.Main { } } - private void ActivateSetting(ref bool headerLinesAdded, ref GlobalSetting triggeredSetting, ref List matchedTriggers) { + private void ActivateSetting(ref bool headerLinesAdded, ref GlobalSetting triggeredSetting, ref List matchedTriggers) + { string activeSettingName = SettingsHandler.GetActiveSetting(this, ref headerLinesAdded); - if (activeSettingName.Equals("") && this.PTMagicConfiguration.GeneralSettings.Application.TestMode) { + if (activeSettingName.Equals("") && this.PTMagicConfiguration.GeneralSettings.Application.TestMode) + { activeSettingName = this.ActiveSetting; } - if (this.EnforceSettingsReapply) { + if (this.EnforceSettingsReapply) + { this.Log.DoLogInfo("Reapply '" + activeSettingName + "' as the settings.analyzer.json or a preset file got changed."); } GlobalSetting activeSetting = this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(s => s.SettingName.Equals(activeSettingName, StringComparison.InvariantCultureIgnoreCase)); - if (this.EnforceSettingsReapply || !activeSettingName.Equals(triggeredSetting.SettingName, StringComparison.InvariantCultureIgnoreCase)) { + if (this.EnforceSettingsReapply || !activeSettingName.Equals(triggeredSetting.SettingName, StringComparison.InvariantCultureIgnoreCase)) + { this.Log.DoLogInfo("Setting '" + activeSettingName + "' currently active. Checking for flood protection..."); // If the setting we are about to activate is the default one, do not list matched triggers - if (triggeredSetting.SettingName.Equals(this.DefaultSettingName, StringComparison.InvariantCultureIgnoreCase)) { + if (triggeredSetting.SettingName.Equals(this.DefaultSettingName, StringComparison.InvariantCultureIgnoreCase)) + { matchedTriggers = new List(); } // Check if flood protection is active - if (this.EnforceSettingsReapply || !this.LastSetting.Equals(triggeredSetting.SettingName, StringComparison.InvariantCultureIgnoreCase) || this.LastSettingsChange <= DateTime.UtcNow.AddMinutes(-PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes)) { + if (this.EnforceSettingsReapply || !this.LastSetting.Equals(triggeredSetting.SettingName, StringComparison.InvariantCultureIgnoreCase) || this.LastSettingsChange <= DateTime.UtcNow.AddMinutes(-PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes)) + { // Setting not set => Change setting this.Log.DoLogInfo("Switching global settings to '" + triggeredSetting.SettingName + "'..."); @@ -1150,16 +1452,20 @@ namespace Core.Main { // Build Telegram message string telegramMessage = this.PTMagicConfiguration.GeneralSettings.Application.InstanceName + ": Setting switched to '*" + SystemHelper.SplitCamelCase(triggeredSetting.SettingName) + "*'."; - if (matchedTriggers.Count > 0) { + if (matchedTriggers.Count > 0) + { telegramMessage += "\n\n*Matching Triggers:*"; - foreach (string triggerResult in matchedTriggers) { + foreach (string triggerResult in matchedTriggers) + { telegramMessage += "\n" + triggerResult; } } - if (this.AverageMarketTrendChanges.Keys.Count > 0) { + if (this.AverageMarketTrendChanges.Keys.Count > 0) + { telegramMessage += "\n\n*Market Trends:*"; - foreach (string key in this.AverageMarketTrendChanges.Keys) { + foreach (string key in this.AverageMarketTrendChanges.Keys) + { telegramMessage += "\n" + key + ": " + this.AverageMarketTrendChanges[key].ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"; } } @@ -1170,13 +1476,17 @@ namespace Core.Main { this.LastSetting = activeSettingName; this.LastSettingsChange = DateTime.UtcNow; - } else { + } + else + { this.Log.DoLogInfo("Flood protection active until " + this.LastSettingsChange.AddMinutes(PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes).ToString() + " (UTC). Not switching settings to '" + triggeredSetting.SettingName + "'!"); this.LastRuntimeSummary.FloodProtectedSetting = triggeredSetting; this.LastRuntimeSummary.CurrentGlobalSetting = activeSetting; } - } else { + } + else + { matchedTriggers = new List(); // Setting already set => Do nothing @@ -1188,67 +1498,83 @@ namespace Core.Main { this.ActiveSetting = this.LastRuntimeSummary.CurrentGlobalSetting.SettingName; } - private void ApplySingleMarketSettings() { - if (this.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings.Count > 0) { + private void ApplySingleMarketSettings() + { + if (this.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings.Count > 0) + { this.Log.DoLogInfo("Checking single market settings triggers for " + this.MarketList.Count.ToString() + " markets..."); int marketPairProcess = 1; Dictionary> matchedMarketTriggers = new Dictionary>(); - foreach (string marketPair in this.MarketList) { + foreach (string marketPair in this.MarketList) + { this.Log.DoLogDebug("'" + marketPair + "' - Checking triggers (" + marketPairProcess.ToString() + "/" + this.MarketList.Count.ToString() + ")..."); bool stopTriggers = false; - foreach (SingleMarketSetting marketSetting in this.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings) { + foreach (SingleMarketSetting marketSetting in this.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings) + { List matchedSingleMarketTriggers = new List(); // Check ignore markets List ignoredMarkets = SystemHelper.ConvertTokenStringToList(marketSetting.IgnoredMarkets, ","); - if (ignoredMarkets.Contains(marketPair)) { + if (ignoredMarkets.Contains(marketPair)) + { this.Log.DoLogDebug("'" + marketPair + "' - Is ignored in '" + marketSetting.SettingName + "'."); continue; } // Check allowed markets List allowedMarkets = SystemHelper.ConvertTokenStringToList(marketSetting.AllowedMarkets, ","); - if (allowedMarkets.Count > 0 && !allowedMarkets.Contains(marketPair)) { + if (allowedMarkets.Count > 0 && !allowedMarkets.Contains(marketPair)) + { this.Log.DoLogDebug("'" + marketPair + "' - Is not allowed in '" + marketSetting.SettingName + "'."); continue; } // Check ignore global settings List ignoredGlobalSettings = SystemHelper.ConvertTokenStringToList(marketSetting.IgnoredGlobalSettings, ","); - if (ignoredGlobalSettings.Contains(this.ActiveSetting)) { + if (ignoredGlobalSettings.Contains(this.ActiveSetting)) + { this.Log.DoLogDebug("'" + marketPair + "' - '" + this.ActiveSetting + "' - Is ignored in '" + marketSetting.SettingName + "'."); continue; } // Check allowed global settings List allowedGlobalSettings = SystemHelper.ConvertTokenStringToList(marketSetting.AllowedGlobalSettings, ","); - if (allowedGlobalSettings.Count > 0 && !allowedGlobalSettings.Contains(this.ActiveSetting)) { + if (allowedGlobalSettings.Count > 0 && !allowedGlobalSettings.Contains(this.ActiveSetting)) + { this.Log.DoLogDebug("'" + marketPair + "' - '" + this.ActiveSetting + "' - Is not allowed in '" + marketSetting.SettingName + "'."); continue; } #region Checking Off Triggers SingleMarketSettingSummary smss = this.SingleMarketSettingSummaries.Find(s => s.Market.Equals(marketPair, StringComparison.InvariantCultureIgnoreCase) && s.SingleMarketSetting.SettingName.Equals(marketSetting.SettingName, StringComparison.InvariantCultureIgnoreCase)); - if (smss != null) { - if (marketSetting.OffTriggers != null) { - if (marketSetting.OffTriggers.Count > 0) { + if (smss != null) + { + if (marketSetting.OffTriggers != null) + { + if (marketSetting.OffTriggers.Count > 0) + { this.Log.DoLogDebug("'" + marketPair + "' - Checking off triggers '" + marketSetting.SettingName + "'..."); List offTriggerResults = new List(); - foreach (OffTrigger offTrigger in marketSetting.OffTriggers) { - if (offTrigger.HoursSinceTriggered > 0) { + foreach (OffTrigger offTrigger in marketSetting.OffTriggers) + { + if (offTrigger.HoursSinceTriggered > 0) + { #region Check for Activation time period trigger int smsActiveHours = (int)Math.Floor(DateTime.UtcNow.Subtract(smss.ActivationDateTimeUTC).TotalHours); - if (smsActiveHours >= offTrigger.HoursSinceTriggered) { + if (smsActiveHours >= offTrigger.HoursSinceTriggered) + { // Trigger met! this.Log.DoLogDebug("'" + marketPair + "' - SMS already active for " + smsActiveHours.ToString() + " hours. Trigger matched!"); offTriggerResults.Add(true); - } else { + } + else + { // Trigger not met! this.Log.DoLogDebug("'" + marketPair + "' - SMS only active for " + smsActiveHours.ToString() + " hours. Trigger not matched!"); @@ -1256,19 +1582,26 @@ namespace Core.Main { offTriggerResults.Add(false); } #endregion - } else if (offTrigger.Min24hVolume > 0 || offTrigger.Max24hVolume < Constants.Max24hVolume) { + } + else if (offTrigger.Min24hVolume > 0 || offTrigger.Max24hVolume < Constants.Max24hVolume) + { #region Check for 24h volume trigger List marketTrendChanges = this.SingleMarketTrendChanges[this.SingleMarketTrendChanges.Keys.Last()]; - if (marketTrendChanges.Count > 0) { + if (marketTrendChanges.Count > 0) + { MarketTrendChange mtc = marketTrendChanges.Find(m => m.Market.Equals(marketPair, StringComparison.InvariantCultureIgnoreCase)); - if (mtc != null) { - if (mtc.Volume24h >= offTrigger.Min24hVolume && mtc.Volume24h <= offTrigger.Max24hVolume) { + if (mtc != null) + { + if (mtc.Volume24h >= offTrigger.Min24hVolume && mtc.Volume24h <= offTrigger.Max24hVolume) + { // Trigger met! this.Log.DoLogDebug("'" + marketPair + "' - 24h volume off trigger matched! 24h volume = " + mtc.Volume24h.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket); offTriggerResults.Add(true); - } else { + } + else + { // Trigger not met! this.Log.DoLogDebug("'" + marketPair + "' - 24h volume off trigger not matched! 24h volume = " + mtc.Volume24h.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket); @@ -1279,24 +1612,32 @@ namespace Core.Main { } #endregion - } else { + } + else + { #region Check for market trend triggers - if (this.SingleMarketTrendChanges.ContainsKey(offTrigger.MarketTrendName)) { + if (this.SingleMarketTrendChanges.ContainsKey(offTrigger.MarketTrendName)) + { List marketTrendChanges = this.SingleMarketTrendChanges[offTrigger.MarketTrendName]; - if (marketTrendChanges.Count > 0) { + if (marketTrendChanges.Count > 0) + { double averageMarketTrendChange = marketTrendChanges.Average(m => m.TrendChange); MarketTrendChange mtc = marketTrendChanges.Find(m => m.Market.Equals(marketPair, StringComparison.InvariantCultureIgnoreCase)); - if (mtc != null) { + if (mtc != null) + { // Get trend change according to configured relation double trendChange = mtc.TrendChange; - if (offTrigger.MarketTrendRelation.Equals(Constants.MarketTrendRelationRelative)) { + if (offTrigger.MarketTrendRelation.Equals(Constants.MarketTrendRelationRelative)) + { // Build pair trend change relative to the global market trend trendChange = trendChange - averageMarketTrendChange; - } else if (offTrigger.MarketTrendRelation.Equals(Constants.MarketTrendRelationRelativeTrigger)) { + } + else if (offTrigger.MarketTrendRelation.Equals(Constants.MarketTrendRelationRelativeTrigger)) + { // Build pair trend change relative to the trigger price double currentPrice = mtc.LastPrice; @@ -1306,20 +1647,27 @@ namespace Core.Main { } // Get market trend change for trigger - if (trendChange >= offTrigger.MinChange && trendChange < offTrigger.MaxChange) { + if (trendChange >= offTrigger.MinChange && trendChange < offTrigger.MaxChange) + { // Trigger met! this.Log.DoLogDebug("'" + marketPair + "' - Off Trigger '" + offTrigger.MarketTrendName + "' triggered! TrendChange (" + offTrigger.MarketTrendRelation + ") = " + trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"); offTriggerResults.Add(true); - } else { + } + else + { this.Log.DoLogDebug("'" + marketPair + "' - Off Trigger '" + offTrigger.MarketTrendName + "' not triggered. TrendChange (" + offTrigger.MarketTrendRelation + ") = " + trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"); offTriggerResults.Add(false); } - } else { + } + else + { offTriggerResults.Add(false); } - } else { + } + else + { offTriggerResults.Add(false); } } @@ -1329,7 +1677,8 @@ namespace Core.Main { // Check if all off triggers have to get triggered or just one bool settingOffTriggered = false; - switch (marketSetting.OffTriggerConnection.ToLower()) { + switch (marketSetting.OffTriggerConnection.ToLower()) + { case "and": settingOffTriggered = offTriggerResults.FindAll(tr => tr == false).Count == 0; break; @@ -1339,14 +1688,19 @@ namespace Core.Main { } // Setting got off triggered, remove it from the summary - if (settingOffTriggered) { + if (settingOffTriggered) + { this.Log.DoLogDebug("'" + marketPair + "' - '" + marketSetting.SettingName + "' off triggered!"); this.SingleMarketSettingSummaries.Remove(smss); smss = null; - } else { + } + else + { this.Log.DoLogDebug("'" + marketPair + "' - '" + marketSetting.SettingName + "' not off triggered!"); } - } else { + } + else + { this.Log.DoLogDebug("'" + marketPair + "' - '" + marketSetting.SettingName + "' has no off triggers -> triggering off!"); this.SingleMarketSettingSummaries.Remove(smss); smss = null; @@ -1355,41 +1709,51 @@ namespace Core.Main { } #endregion - if (marketSetting.Triggers.Count > 0 && !stopTriggers) { + if (marketSetting.Triggers.Count > 0 && !stopTriggers) + { #region Checking Triggers this.Log.DoLogDebug("'" + marketPair + "' - Checking triggers for '" + marketSetting.SettingName + "'..."); List triggerResults = new List(); Dictionary relevantTriggers = new Dictionary(); int triggerIndex = 0; - foreach (Trigger trigger in marketSetting.Triggers) { + foreach (Trigger trigger in marketSetting.Triggers) + { - if (trigger.Min24hVolume > 0 || trigger.Max24hVolume < Constants.Max24hVolume) { + if (trigger.Min24hVolume > 0 || trigger.Max24hVolume < Constants.Max24hVolume) + { #region Check for 24h volume trigger List marketTrendChanges = this.SingleMarketTrendChanges[this.SingleMarketTrendChanges.Keys.Last()]; - if (marketTrendChanges.Count > 0) { + if (marketTrendChanges.Count > 0) + { MarketTrendChange mtc = marketTrendChanges.Find(m => m.Market.Equals(marketPair, StringComparison.InvariantCultureIgnoreCase)); - if (mtc != null) { + if (mtc != null) + { - if (mtc.Volume24h >= trigger.Min24hVolume && mtc.Volume24h <= trigger.Max24hVolume) { + if (mtc.Volume24h >= trigger.Min24hVolume && mtc.Volume24h <= trigger.Max24hVolume) + { // Trigger met! this.Log.DoLogDebug("'" + marketPair + "' - 24h volume trigger matched! 24h volume = " + mtc.Volume24h.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket); relevantTriggers.Add(triggerIndex, mtc.Volume24h); string triggerContent = "24h Volume"; - if (trigger.Min24hVolume > 0) { + if (trigger.Min24hVolume > 0) + { triggerContent += " - Min: " + trigger.Min24hVolume.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket; } - if (trigger.Max24hVolume < Constants.Max24hVolume) { + if (trigger.Max24hVolume < Constants.Max24hVolume) + { triggerContent += " - Max: " + trigger.Max24hVolume.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket; } matchedSingleMarketTriggers.Add(marketSetting.SettingName + ": " + triggerContent + " - 24h volume = " + mtc.Volume24h.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket); triggerResults.Add(true); - } else { + } + else + { this.Log.DoLogDebug("'" + marketPair + "' - 24h volume trigger not matched. 24h volume = " + mtc.Volume24h.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket); triggerResults.Add(false); } @@ -1397,52 +1761,68 @@ namespace Core.Main { } #endregion - } else if (trigger.AgeDaysLowerThan > 0) { + } + else if (trigger.AgeDaysLowerThan > 0) + { #region Check for age trigger MarketInfo marketInfo = null; - if (this.MarketInfos.ContainsKey(marketPair)) { + if (this.MarketInfos.ContainsKey(marketPair)) + { marketInfo = this.MarketInfos[marketPair]; } - if (marketInfo != null) { + if (marketInfo != null) + { int marketAge = (int)Math.Floor(DateTime.Now.ToUniversalTime().Subtract(marketInfo.FirstSeen).TotalDays); - if (marketAge < trigger.AgeDaysLowerThan) { + if (marketAge < trigger.AgeDaysLowerThan) + { matchedSingleMarketTriggers.Add(marketSetting.SettingName + ": '" + marketPair + "' is only " + marketAge.ToString() + " days old on this exchange. Trigger matched!"); this.Log.DoLogDebug("'" + marketPair + "' - Is only " + marketAge.ToString() + " days old on this exchange. Trigger matched!"); relevantTriggers.Add(triggerIndex, marketAge); triggerResults.Add(true); - } else { + } + else + { this.Log.DoLogDebug("'" + marketPair + "' - Age Trigger not triggered. Is already " + marketAge.ToString() + " days old on this exchange."); triggerResults.Add(false); } - } else { + } + else + { matchedSingleMarketTriggers.Add("Age for '" + marketPair + "' not found, trigger matched just to be safe!"); this.Log.DoLogDebug("'" + marketPair + "' - Age not found, trigger matched just to be safe!"); triggerResults.Add(true); } #endregion - } else { + } + else + { #region Check for market trend triggers - if (this.SingleMarketTrendChanges.ContainsKey(trigger.MarketTrendName)) { + if (this.SingleMarketTrendChanges.ContainsKey(trigger.MarketTrendName)) + { List marketTrendChanges = this.SingleMarketTrendChanges[trigger.MarketTrendName]; - if (marketTrendChanges.Count > 0) { + if (marketTrendChanges.Count > 0) + { double averageMarketTrendChange = marketTrendChanges.Average(m => m.TrendChange); MarketTrendChange mtc = marketTrendChanges.Find(m => m.Market.Equals(marketPair, StringComparison.InvariantCultureIgnoreCase)); - if (mtc != null) { + if (mtc != null) + { // Get trend change according to configured relation double trendChange = mtc.TrendChange; - if (trigger.MarketTrendRelation.Equals(Constants.MarketTrendRelationRelative)) { + if (trigger.MarketTrendRelation.Equals(Constants.MarketTrendRelationRelative)) + { // Build pair trend change relative to the global market trend trendChange = trendChange - averageMarketTrendChange; } // Get market trend change for trigger - if (trendChange >= trigger.MinChange && trendChange < trigger.MaxChange) { + if (trendChange >= trigger.MinChange && trendChange < trigger.MaxChange) + { // Trigger met! this.Log.DoLogDebug("'" + marketPair + "' - Trigger '" + trigger.MarketTrendName + "' triggered! TrendChange (" + trigger.MarketTrendRelation + ") = " + trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"); @@ -1450,37 +1830,51 @@ namespace Core.Main { relevantTriggers.Add(triggerIndex, trendChange); string triggerContent = trigger.MarketTrendName + " - "; - if (trigger.MinChange != Constants.MinTrendChange) { + if (trigger.MinChange != Constants.MinTrendChange) + { triggerContent += " - Min: " + trigger.MinChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"; } - if (trigger.MaxChange != Constants.MaxTrendChange) { + if (trigger.MaxChange != Constants.MaxTrendChange) + { triggerContent += " - Max: " + trigger.MaxChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"; } matchedSingleMarketTriggers.Add(marketSetting.SettingName + ": " + triggerContent + " - TrendChange (" + trigger.MarketTrendRelation + ") = " + trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"); triggerResults.Add(true); - } else { + } + else + { this.Log.DoLogDebug("'" + marketPair + "' - Trigger '" + trigger.MarketTrendName + "' not triggered. TrendChange (" + trigger.MarketTrendRelation + ") = " + trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"); triggerResults.Add(false); } - } else { + } + else + { this.Log.DoLogDebug("'" + marketPair + "' - No market trend change found for '" + trigger.MarketTrendName + "'! Coin just got released? Trigger ignored!"); triggerResults.Add(false); } - } else { + } + else + { this.Log.DoLogWarn("'" + marketPair + "' - No market trend changes found for '" + trigger.MarketTrendName + "'! Trigger ignored!"); triggerResults.Add(false); } - } else { + } + else + { MarketTrend marketTrend = this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.Find(mt => mt.Name.Equals(trigger.MarketTrendName, StringComparison.InvariantCultureIgnoreCase)); - if (marketTrend != null) { - if (!marketTrend.Platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase)) { + if (marketTrend != null) + { + if (!marketTrend.Platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase)) + { this.Log.DoLogWarn("Market Trend '" + trigger.MarketTrendName + "' is invalid for single market settings! Only trends using the platform 'Exchange' are valid for single market settings."); triggerResults.Add(false); } - } else { + } + else + { this.Log.DoLogWarn("Market Trend '" + trigger.MarketTrendName + "' not found! Trigger ignored!"); triggerResults.Add(false); } @@ -1492,7 +1886,8 @@ namespace Core.Main { // Check if all triggers have to get triggered or just one bool settingTriggered = false; - switch (marketSetting.TriggerConnection.ToLower()) { + switch (marketSetting.TriggerConnection.ToLower()) + { case "and": settingTriggered = triggerResults.FindAll(tr => tr == false).Count == 0; break; @@ -1505,25 +1900,32 @@ namespace Core.Main { bool isFreshTrigger = true; // Setting not triggered -> Check if it is already active as a long term SMS using Off Triggers - if (!settingTriggered) { + if (!settingTriggered) + { this.Log.DoLogDebug("'" + marketPair + "' - SMS '" + marketSetting.SettingName + "' not triggered, checking for long term activation."); - if (smss != null) { - if (marketSetting.OffTriggers != null) { - if (marketSetting.OffTriggers.Count > 0) { + if (smss != null) + { + if (marketSetting.OffTriggers != null) + { + if (marketSetting.OffTriggers.Count > 0) + { this.Log.DoLogDebug("'" + marketPair + "' - SMS '" + marketSetting.SettingName + "' has off triggers, starting special trigger..."); // Setting already active and using off triggers -> set as triggered settingTriggered = true; isFreshTrigger = false; matchedSingleMarketTriggers = new List(); - foreach (string matchedTriggerContent in smss.TriggerSnapshot.MatchedTriggersContent) { - if (matchedTriggerContent.StartsWith(marketSetting.SettingName + ":")) { + foreach (string matchedTriggerContent in smss.TriggerSnapshot.MatchedTriggersContent) + { + if (matchedTriggerContent.StartsWith(marketSetting.SettingName + ":")) + { matchedSingleMarketTriggers.Add(matchedTriggerContent); } } int removalLength = matchedSingleMarketTriggers.Count - marketSetting.Triggers.Count; - if (removalLength > 0) { + if (removalLength > 0) + { matchedSingleMarketTriggers.RemoveRange(0, removalLength); } @@ -1534,39 +1936,54 @@ namespace Core.Main { } // Setting got triggered -> Activate it! - if (settingTriggered) { + if (settingTriggered) + { this.Log.DoLogDebug("'" + marketPair + "' - '" + marketSetting.SettingName + "' triggered!"); // Save matched triggers to get displayed in the comment lines - if (!matchedMarketTriggers.ContainsKey(marketPair)) { + if (!matchedMarketTriggers.ContainsKey(marketPair)) + { matchedMarketTriggers.Add(marketPair, matchedSingleMarketTriggers); - } else { + } + else + { matchedMarketTriggers[marketPair].AddRange(matchedSingleMarketTriggers); } - if (!this.TriggeredSingleMarketSettings.ContainsKey(marketPair)) { + if (!this.TriggeredSingleMarketSettings.ContainsKey(marketPair)) + { List smsList = new List(); smsList.Add(marketSetting); this.TriggeredSingleMarketSettings.Add(marketPair, smsList); - } else { + } + else + { this.TriggeredSingleMarketSettings[marketPair].Add(marketSetting); } // Counting triggered setting - if (!this.SingleMarketSettingsCount.ContainsKey(marketSetting.SettingName)) { + if (!this.SingleMarketSettingsCount.ContainsKey(marketSetting.SettingName)) + { this.SingleMarketSettingsCount.Add(marketSetting.SettingName, 1); - } else { + } + else + { this.SingleMarketSettingsCount[marketSetting.SettingName]++; } - if (isFreshTrigger) { + if (isFreshTrigger) + { this.Log.DoLogDebug("'" + marketPair + "' - SMS '" + marketSetting.SettingName + "' saving summary data..."); // Check if this setting is already active for this market - if (smss == null || marketSetting.RefreshOffTriggers) { - if (smss == null) { + if (smss == null || marketSetting.RefreshOffTriggers) + { + if (smss == null) + { smss = new SingleMarketSettingSummary(); - } else { + } + else + { this.SingleMarketSettingSummaries.Remove(smss); } @@ -1580,9 +1997,11 @@ namespace Core.Main { smss.TriggerSnapshot.MatchedTriggersContent = matchedSingleMarketTriggers; List marketTrendChanges = this.SingleMarketTrendChanges[this.SingleMarketTrendChanges.Keys.Last()]; - if (marketTrendChanges.Count > 0) { + if (marketTrendChanges.Count > 0) + { MarketTrendChange mtc = marketTrendChanges.Find(m => m.Market.Equals(marketPair, StringComparison.InvariantCultureIgnoreCase)); - if (mtc != null) { + if (mtc != null) + { smss.TriggerSnapshot.Last24hVolume = mtc.Volume24h; smss.TriggerSnapshot.LastPrice = mtc.LastPrice; } @@ -1591,29 +2010,36 @@ namespace Core.Main { this.SingleMarketSettingSummaries.Add(smss); this.Log.DoLogDebug("'" + marketPair + "' - SMS '" + marketSetting.SettingName + "' summary data saved."); - } else { + } + else + { this.Log.DoLogDebug("'" + marketPair + "' - SMS '" + marketSetting.SettingName + "' already active for this market and no refresh allowed."); } } // Stop processing other settings if configured - if (marketSetting.StopProcessWhenTriggered) { + if (marketSetting.StopProcessWhenTriggered) + { stopTriggers = true; } - } else { + } + else + { this.Log.DoLogDebug("'" + marketPair + "' - '" + marketSetting.SettingName + "' not triggered!"); } } } - if ((marketPairProcess % 10) == 0) { + if ((marketPairProcess % 10) == 0) + { this.Log.DoLogInfo("What are you looking at? " + marketPairProcess + "/" + this.MarketList.Count + " markets done..."); } marketPairProcess++; } - if (this.TriggeredSingleMarketSettings.Count > 0) { + if (this.TriggeredSingleMarketSettings.Count > 0) + { // Write single market settings this.Log.DoLogInfo("Building single market settings for '" + this.TriggeredSingleMarketSettings.Count.ToString() + "' markets..."); @@ -1622,21 +2048,28 @@ namespace Core.Main { this.SingleMarketSettingWritten = true; this.Log.DoLogInfo("Building single market settings completed."); - } else { + } + else + { this.Log.DoLogInfo("No settings triggered for single markets."); // Remove single market settings if no triggers are met - if necessary this.SingleMarketSettingWritten = SettingsHandler.RemoveSingleMarketSettings(this); } - } else { + } + else + { this.Log.DoLogInfo("No single market settings found."); } } - private void SaveProfitTrailerProperties(bool headerLinesAdded, string pairsPropertiesPath, string dcaPropertiesPath, string indicatorsPropertiesPath) { - if (headerLinesAdded || this.GlobalSettingWritten || this.SingleMarketSettingWritten) { - if (this.ProfitTrailerMajorVersion == 1) { + private void SaveProfitTrailerProperties(bool headerLinesAdded, string pairsPropertiesPath, string dcaPropertiesPath, string indicatorsPropertiesPath) + { + if (headerLinesAdded || this.GlobalSettingWritten || this.SingleMarketSettingWritten) + { + if (this.ProfitTrailerMajorVersion == 1) + { // Save current PT properties to files (Valid for PT 1.x) this.Log.DoLogInfo("Saving properties files..."); @@ -1656,7 +2089,9 @@ namespace Core.Main { if (!this.PTMagicConfiguration.GeneralSettings.Application.TestMode) File.WriteAllLines(indicatorsPropertiesPath, this.IndicatorsLines); this.Log.DoLogInfo("All properties files saved!"); - } else { + } + else + { // Save current PT properties to API (Valid for PT 2.x and above) this.Log.DoLogInfo("Saving properties using API..."); @@ -1672,12 +2107,15 @@ namespace Core.Main { this.Log.DoLogInfo("All properties saved!"); } - } else { + } + else + { this.Log.DoLogInfo("Nothing changed, no files touched!"); } } - private void SaveSingleMarketSettingsSummary() { + private void SaveSingleMarketSettingsSummary() + { JsonSerializerSettings smsSummaryJsonSettings = new JsonSerializerSettings(); smsSummaryJsonSettings.NullValueHandling = NullValueHandling.Ignore; smsSummaryJsonSettings.DefaultValueHandling = DefaultValueHandling.Ignore; @@ -1687,7 +2125,8 @@ namespace Core.Main { this.Log.DoLogInfo("Single Market Settings Summary saved."); } - private void SaveRuntimeSummary(bool headerLinesAdded) { + private void SaveRuntimeSummary(bool headerLinesAdded) + { DateTime endTime = DateTime.Now; int elapsedSeconds = (int)Math.Round(endTime.Subtract(this.LastRuntime).TotalSeconds, 0); @@ -1696,18 +2135,23 @@ namespace Core.Main { this.Log.DoLogInfo("Building LastRuntimeSummary.json for your monitor..."); // Load existing runtime summary and read ongoing data - if (File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "LastRuntimeSummary.json")) { - try { + if (File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "LastRuntimeSummary.json")) + { + try + { Summary summary = JsonConvert.DeserializeObject(System.IO.File.ReadAllText(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "LastRuntimeSummary.json")); - if (summary != null) { + if (summary != null) + { // Last setting switch in case the app got restarted and has no history - if (this.LastRuntimeSummary.LastGlobalSettingSwitch == Constants.confMinDate) { + if (this.LastRuntimeSummary.LastGlobalSettingSwitch == Constants.confMinDate) + { this.LastRuntimeSummary.LastGlobalSettingSwitch = summary.LastGlobalSettingSwitch; } // Market trend changes history for graph data - foreach (string key in summary.MarketTrendChanges.Keys) { + foreach (string key in summary.MarketTrendChanges.Keys) + { this.LastRuntimeSummary.MarketTrendChanges.Add(key, summary.MarketTrendChanges[key].FindAll(mtc => mtc.TrendDateTime >= DateTime.Now.AddHours(-PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours))); } @@ -1716,7 +2160,9 @@ namespace Core.Main { this.Log.DoLogInfo("Summary: Loaded old LastRuntimeSummary.json to keep data."); } - } catch (Exception ex) { + } + catch (Exception ex) + { this.Log.DoLogCritical("Summary: Error loading old summary (" + ex.Message + "). Creating new one.", ex); } } @@ -1725,14 +2171,16 @@ namespace Core.Main { // Change setting summary GlobalSettingSummary lastSettingSummary = null; - if (this.LastRuntimeSummary.LastGlobalSettingSwitch == this.LastRuntimeSummary.LastRuntime || this.LastRuntimeSummary.GlobalSettingSummary.Count == 0) { + if (this.LastRuntimeSummary.LastGlobalSettingSwitch == this.LastRuntimeSummary.LastRuntime || this.LastRuntimeSummary.GlobalSettingSummary.Count == 0) + { // Setting got switched this run, add a new setting summary GlobalSettingSummary gss = new GlobalSettingSummary(); gss.SettingName = this.LastRuntimeSummary.CurrentGlobalSetting.SettingName; gss.SwitchDateTime = this.LastRuntimeSummary.LastRuntime.ToUniversalTime(); - if (this.LastRuntimeSummary.GlobalSettingSummary.Count > 0) { + if (this.LastRuntimeSummary.GlobalSettingSummary.Count > 0) + { lastSettingSummary = this.LastRuntimeSummary.GlobalSettingSummary.OrderByDescending(lss => lss.SwitchDateTime).First(); lastSettingSummary.ActiveSeconds = (int)Math.Ceiling(DateTime.Now.ToUniversalTime().Subtract(lastSettingSummary.SwitchDateTime).TotalSeconds); } @@ -1740,10 +2188,13 @@ namespace Core.Main { this.LastRuntimeSummary.GlobalSettingSummary.Add(gss); lastSettingSummary = this.LastRuntimeSummary.GlobalSettingSummary.OrderByDescending(lss => lss.SwitchDateTime).First(); - } else { + } + else + { // Setting did not get switched, update data - if (this.LastRuntimeSummary.GlobalSettingSummary.Count > 0) { + if (this.LastRuntimeSummary.GlobalSettingSummary.Count > 0) + { lastSettingSummary = this.LastRuntimeSummary.GlobalSettingSummary.OrderByDescending(lss => lss.SwitchDateTime).First(); lastSettingSummary.ActiveSeconds = (int)Math.Ceiling(DateTime.Now.ToUniversalTime().Subtract(lastSettingSummary.SwitchDateTime).TotalSeconds); } @@ -1753,9 +2204,11 @@ namespace Core.Main { this.Log.DoLogInfo("Summary: Save market trend changes for summary."); // Save market trend changes for the summary - foreach (string key in this.AverageMarketTrendChanges.Keys) { + foreach (string key in this.AverageMarketTrendChanges.Keys) + { List mtChanges = new List(); - if (this.LastRuntimeSummary.MarketTrendChanges.ContainsKey(key)) { + if (this.LastRuntimeSummary.MarketTrendChanges.ContainsKey(key)) + { mtChanges = this.LastRuntimeSummary.MarketTrendChanges[key]; } @@ -1765,11 +2218,15 @@ namespace Core.Main { newChange.TrendDateTime = this.LastRuntimeSummary.LastRuntime; mtChanges.Add(newChange); - if (lastSettingSummary != null) { - if (!lastSettingSummary.MarketTrendChanges.ContainsKey(key)) { + if (lastSettingSummary != null) + { + if (!lastSettingSummary.MarketTrendChanges.ContainsKey(key)) + { GlobalSetting gs = this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(g => g.SettingName.Equals(lastSettingSummary.SettingName)); - if (gs != null) { - if (gs.SettingName.Equals("Default", StringComparison.InvariantCultureIgnoreCase) || gs.Triggers.Find(t => t.MarketTrendName.Equals(key)) != null) { + if (gs != null) + { + if (gs.SettingName.Equals("Default", StringComparison.InvariantCultureIgnoreCase) || gs.Triggers.Find(t => t.MarketTrendName.Equals(key)) != null) + { lastSettingSummary.MarketTrendChanges.Add(key, newChange); } } @@ -1848,102 +2305,131 @@ namespace Core.Main { this.LastRuntimeSummary.DCATrigger = dcaDefaultTrigger; // Get configured DCA triggers - for (int dca = 1; dca <= maxDCALevel; dca++) { + for (int dca = 1; dca <= maxDCALevel; dca++) + { string dcaTriggerString = SettingsHandler.GetCurrentPropertyValue(dcaProperties, "buy_trigger_" + dca.ToString(), "DEFAULT_DCA_buy_trigger_" + dca.ToString()); - if (!dcaTriggerString.Equals("")) { + if (!dcaTriggerString.Equals("")) + { double dcaTrigger = SystemHelper.TextToDouble(dcaTriggerString, 0, "en-US"); this.LastRuntimeSummary.DCATriggers.Add(dca, dcaTrigger); - } else { + } + else + { if (this.LastRuntimeSummary.DCALevels == 0) this.LastRuntimeSummary.DCALevels = dca - 1; break; } } // Get configured DCA percentages - if (this.ProfitTrailerMajorVersion >= 2) { + if (this.ProfitTrailerMajorVersion >= 2) + { string dcaDefaultPercentageString = SettingsHandler.GetCurrentPropertyValue(dcaProperties, "DEFAULT_DCA_buy_percentage", ""); double dcaDefaultPercentage = SystemHelper.TextToDouble(dcaDefaultPercentageString, 0, "en-US"); this.LastRuntimeSummary.DCAPercentage = dcaDefaultPercentage; - for (int dca = 1; dca <= maxDCALevel; dca++) { + for (int dca = 1; dca <= maxDCALevel; dca++) + { string dcaPercentageString = SettingsHandler.GetCurrentPropertyValue(dcaProperties, "DEFAULT_DCA_buy_percentage_" + dca.ToString(), ""); - if (!dcaPercentageString.Equals("")) { + if (!dcaPercentageString.Equals("")) + { double dcaPercentage = SystemHelper.TextToDouble(dcaPercentageString, 0, "en-US"); this.LastRuntimeSummary.DCAPercentages.Add(dca, dcaPercentage); - } else { + } + else + { if (this.LastRuntimeSummary.DCALevels == 0) this.LastRuntimeSummary.DCALevels = dca - 1; break; } } - } else { + } + else + { this.LastRuntimeSummary.DCAPercentage = 100; } // Get configured Buy Strategies - for (char c = 'A'; c <= 'Z'; c++) { + for (char c = 'A'; c <= 'Z'; c++) + { string buyStrategyName = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "DEFAULT_" + c + "_buy_strategy", ""); - if (!buyStrategyName.Equals("")) { + if (!buyStrategyName.Equals("")) + { StrategySummary buyStrategy = new StrategySummary(); buyStrategy.Name = buyStrategyName; buyStrategy.Value = SystemHelper.TextToDouble(SettingsHandler.GetCurrentPropertyValue(pairsProperties, "DEFAULT_" + c + "_buy_value", ""), 0, "en-US"); this.LastRuntimeSummary.BuyStrategies.Add(buyStrategy); - } else { + } + else + { break; } } // Get configured Sell Strategies - for (char c = 'A'; c <= 'Z'; c++) { + for (char c = 'A'; c <= 'Z'; c++) + { string sellStrategyName = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "DEFAULT_" + c + "_sell_strategy", ""); - if (!sellStrategyName.Equals("")) { + if (!sellStrategyName.Equals("")) + { StrategySummary sellStrategy = new StrategySummary(); sellStrategy.Name = sellStrategyName; sellStrategy.Value = SystemHelper.TextToDouble(SettingsHandler.GetCurrentPropertyValue(pairsProperties, "DEFAULT_" + c + "_sell_value", ""), 0, "en-US"); this.LastRuntimeSummary.SellStrategies.Add(sellStrategy); - } else { + } + else + { break; } } // Get configured DCA Buy Strategies - for (char c = 'A'; c <= 'Z'; c++) { + for (char c = 'A'; c <= 'Z'; c++) + { string buyStrategyName = SettingsHandler.GetCurrentPropertyValue(dcaProperties, "DEFAULT_DCA_" + c + "_buy_strategy", ""); - if (!buyStrategyName.Equals("")) { + if (!buyStrategyName.Equals("")) + { StrategySummary buyStrategy = new StrategySummary(); buyStrategy.Name = buyStrategyName; buyStrategy.Value = SystemHelper.TextToDouble(SettingsHandler.GetCurrentPropertyValue(dcaProperties, "DEFAULT_DCA_" + c + "_buy_value", ""), 0, "en-US"); this.LastRuntimeSummary.DCABuyStrategies.Add(buyStrategy); - } else { + } + else + { break; } } // Get configured DCA Sell Strategies - for (char c = 'A'; c <= 'Z'; c++) { + for (char c = 'A'; c <= 'Z'; c++) + { string sellStrategyName = SettingsHandler.GetCurrentPropertyValue(dcaProperties, "DEFAULT_DCA_" + c + "_sell_strategy", ""); - if (!sellStrategyName.Equals("")) { + if (!sellStrategyName.Equals("")) + { StrategySummary sellStrategy = new StrategySummary(); sellStrategy.Name = sellStrategyName; sellStrategy.Value = SystemHelper.TextToDouble(SettingsHandler.GetCurrentPropertyValue(dcaProperties, "DEFAULT_DCA_" + c + "_sell_value", ""), 0, "en-US"); this.LastRuntimeSummary.DCASellStrategies.Add(sellStrategy); - } else { + } + else + { break; } } // Get current main currency price Dictionary recentMarkets = BaseAnalyzer.GetMarketDataFromFile(this.PTMagicConfiguration, this.Log, "Exchange", DateTime.UtcNow, "Recent"); - if (recentMarkets.Keys.Count > 0) { + if (recentMarkets.Keys.Count > 0) + { this.LastRuntimeSummary.MainMarketPrice = recentMarkets.First().Value.MainCurrencyPriceUSD; - if (!this.LastRuntimeSummary.MainFiatCurrency.Equals("USD", StringComparison.InvariantCultureIgnoreCase)) { + if (!this.LastRuntimeSummary.MainFiatCurrency.Equals("USD", StringComparison.InvariantCultureIgnoreCase)) + { this.LastRuntimeSummary.MainMarketPrice = this.LastRuntimeSummary.MainMarketPrice * this.LastRuntimeSummary.MainFiatCurrencyExchangeRate; } } @@ -1952,7 +2438,8 @@ namespace Core.Main { this.Log.DoLogInfo("Summary: Getting current single market properties..."); // Get current single market settings from PAIRS.PROPERTIES for each configured market - foreach (string marketPair in this.MarketList) { + foreach (string marketPair in this.MarketList) + { MarketPairSummary mpSummary = new MarketPairSummary(); mpSummary.CurrentBuyValue = defaultBuyValue; mpSummary.CurrentTrailingBuy = defaultTrailingBuy; @@ -1962,13 +2449,16 @@ namespace Core.Main { mpSummary.IsSOMActive = defaultSOMActive; mpSummary.ActiveSingleSettings = new List(); - if (this.MarketList.Contains(marketPair)) { + if (this.MarketList.Contains(marketPair)) + { // Pair is allowed for trading, check for individual values mpSummary.IsTradingEnabled = true; - if (this.TriggeredSingleMarketSettings.Count > 0) { - if (this.TriggeredSingleMarketSettings.ContainsKey(marketPair)) { + if (this.TriggeredSingleMarketSettings.Count > 0) + { + if (this.TriggeredSingleMarketSettings.ContainsKey(marketPair)) + { mpSummary.ActiveSingleSettings = this.TriggeredSingleMarketSettings[marketPair]; } } @@ -1976,30 +2466,38 @@ namespace Core.Main { string marketPairSimple = marketPair.Replace(this.LastRuntimeSummary.MainMarket, "").Replace("_", "").Replace("-", ""); // Get configured Buy Strategies - for (char c = 'A'; c <= 'Z'; c++) { + for (char c = 'A'; c <= 'Z'; c++) + { string buyStrategyName = SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPairSimple + "_" + c + "_buy_strategy", ""); - if (!buyStrategyName.Equals("")) { + if (!buyStrategyName.Equals("")) + { StrategySummary buyStrategy = new StrategySummary(); buyStrategy.Name = buyStrategyName; buyStrategy.Value = SystemHelper.TextToDouble(SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPair + "_" + c + "_buy_value", ""), 0, "en-US"); mpSummary.BuyStrategies.Add(buyStrategy); - } else { + } + else + { break; } } if (mpSummary.BuyStrategies.Count == 0) mpSummary.BuyStrategies = this.LastRuntimeSummary.BuyStrategies; // Get configured Sell Strategies - for (char c = 'A'; c <= 'Z'; c++) { + for (char c = 'A'; c <= 'Z'; c++) + { string sellStrategyName = SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPairSimple + "_" + c + "_sell_strategy", ""); - if (!sellStrategyName.Equals("")) { + if (!sellStrategyName.Equals("")) + { StrategySummary sellStrategy = new StrategySummary(); sellStrategy.Name = sellStrategyName; sellStrategy.Value = SystemHelper.TextToDouble(SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPairSimple + "_" + c + "_sell_value", ""), 0, "en-US"); mpSummary.SellStrategies.Add(sellStrategy); - } else { + } + else + { break; } } @@ -2032,10 +2530,13 @@ namespace Core.Main { } // Get market trend values for each market pair - foreach (string marketTrendName in this.SingleMarketTrendChanges.Keys) { - if (this.SingleMarketTrendChanges.ContainsKey(marketTrendName)) { + foreach (string marketTrendName in this.SingleMarketTrendChanges.Keys) + { + if (this.SingleMarketTrendChanges.ContainsKey(marketTrendName)) + { MarketTrendChange mtc = this.SingleMarketTrendChanges[marketTrendName].Find(m => m.Market == marketPair); - if (mtc != null) { + if (mtc != null) + { double marketTrendChange = mtc.TrendChange; mpSummary.MarketTrendChanges.Add(marketTrendName, marketTrendChange); @@ -2045,7 +2546,8 @@ namespace Core.Main { } } - if (!this.LastRuntimeSummary.MarketSummary.ContainsKey(marketPair)) { + if (!this.LastRuntimeSummary.MarketSummary.ContainsKey(marketPair)) + { this.LastRuntimeSummary.MarketSummary.Add(marketPair, mpSummary); } } @@ -2055,25 +2557,32 @@ namespace Core.Main { string serialziedJson = JsonConvert.SerializeObject(this.LastRuntimeSummary); // Save the summary JSON file - try { + try + { FileHelper.WriteTextToFile(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar, "LastRuntimeSummary.json", serialziedJson); this.Log.DoLogInfo("Summary: LastRuntimeSummary.json saved."); - } catch (Exception ex) { + } + catch (Exception ex) + { this.Log.DoLogCritical("Exception while writing LastRuntimeSummary.json", ex); - try { + try + { this.Log.DoLogInfo("Summary: Retrying one more time to save LastRuntimeSummary.json."); FileHelper.WriteTextToFile(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar, "LastRuntimeSummary.json", serialziedJson); this.Log.DoLogInfo("Summary: LastRuntimeSummary.json saved."); - } catch (Exception ex2) { + } + catch (Exception ex2) + { this.Log.DoLogCritical("Nope, another Exception while writing LastRuntimeSummary.json", ex2); } } string logsPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathLogs + Path.DirectorySeparatorChar; - if (Directory.Exists(logsPath)) { + if (Directory.Exists(logsPath)) + { FileHelper.CleanupFiles(logsPath, 24 * 3); this.Log.DoLogInfo("Cleaned up logfiles."); } @@ -2082,7 +2591,8 @@ namespace Core.Main { this.Log.DoLogInfo("##########################################################"); this.Log.DoLogInfo("#******************* RAID SUMMARY ********************#"); this.Log.DoLogInfo("+ PT Magic Version: " + this.LastRuntimeSummary.Version); - if (!SystemHelper.IsRecentVersion(this.LastRuntimeSummary.Version, this.LatestVersion)) { + if (!SystemHelper.IsRecentVersion(this.LastRuntimeSummary.Version, this.LatestVersion)) + { this.Log.DoLogWarn("+ Your version is out of date! The most recent version is " + this.LatestVersion); } this.Log.DoLogInfo("+ Proft Trailer Major Version: " + PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMajorVersion.ToString()); @@ -2092,7 +2602,8 @@ namespace Core.Main { this.Log.DoLogInfo("+ Global setting changed: " + ((this.LastRuntimeSummary.LastGlobalSettingSwitch == this.LastRuntimeSummary.LastRuntime) ? "Yes" : "No") + " " + ((this.LastRuntimeSummary.FloodProtectedSetting != null) ? "(Flood protection!)" : "")); this.Log.DoLogInfo("+ Files changed: " + (((headerLinesAdded || this.GlobalSettingWritten || this.SingleMarketSettingWritten) && !this.PTMagicConfiguration.GeneralSettings.Application.TestMode) ? "Yes" : "No")); this.Log.DoLogInfo("+ Markets with active single market settings: " + this.TriggeredSingleMarketSettings.Count.ToString()); - foreach (string activeSMS in this.SingleMarketSettingsCount.Keys) { + foreach (string activeSMS in this.SingleMarketSettingsCount.Keys) + { this.Log.DoLogInfo("+ " + activeSMS + ": " + this.SingleMarketSettingsCount[activeSMS].ToString()); } this.Log.DoLogInfo("+ " + this.TotalElapsedSeconds.ToString() + " Magicbots killed in " + this.RunCount.ToString() + " raids on Cryptodragon's Lair " + this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes.ToString() + "."); @@ -2104,7 +2615,8 @@ namespace Core.Main { this.Log.DoLogInfo(""); } - private void Cleanup() { + private void Cleanup() + { this.GlobalSettingWritten = false; this.SingleMarketSettingWritten = false; this.EnforceSettingsReapply = false; diff --git a/Core/Main/PTMagicConfiguration.cs b/Core/Main/PTMagicConfiguration.cs index 3b93bb0..2d9c3dd 100644 --- a/Core/Main/PTMagicConfiguration.cs +++ b/Core/Main/PTMagicConfiguration.cs @@ -7,23 +7,29 @@ using Newtonsoft.Json; using Core.Helper; using Core.Main.DataObjects.PTMagicData; -namespace Core.Main { +namespace Core.Main +{ - public class PTMagicConfiguration { + public class PTMagicConfiguration + { private GeneralSettings _generalSettings = null; private AnalyzerSettings _analyzerSettings = null; private SecureSettings _secureSettings = null; - public PTMagicConfiguration() { + public PTMagicConfiguration() + { LoadSettings(Directory.GetCurrentDirectory()); } - public PTMagicConfiguration(string basePath) { + public PTMagicConfiguration(string basePath) + { LoadSettings(basePath); } - private void LoadSettings(string basePath) { - if (!basePath.EndsWith(Path.DirectorySeparatorChar)) { + private void LoadSettings(string basePath) + { + if (!basePath.EndsWith(Path.DirectorySeparatorChar)) + { basePath += Path.DirectorySeparatorChar; } @@ -33,27 +39,33 @@ namespace Core.Main { AnalyzerSettingsWrapper asw = JsonConvert.DeserializeObject(File.ReadAllText(basePath + "settings.analyzer.json")); _analyzerSettings = asw.AnalyzerSettings; - if (!_generalSettings.Application.ProfitTrailerPath.EndsWith(Path.DirectorySeparatorChar)) { + if (!_generalSettings.Application.ProfitTrailerPath.EndsWith(Path.DirectorySeparatorChar)) + { _generalSettings.Application.ProfitTrailerPath += Path.DirectorySeparatorChar; } - if (!_generalSettings.Application.ProfitTrailerMonitorURL.EndsWith("/")) { + if (!_generalSettings.Application.ProfitTrailerMonitorURL.EndsWith("/")) + { _generalSettings.Application.ProfitTrailerMonitorURL += "/"; } - if (File.Exists(basePath + "settings.secure.json")) { + if (File.Exists(basePath + "settings.secure.json")) + { SecureSettingsWrapper ssw = JsonConvert.DeserializeObject(File.ReadAllText(basePath + "settings.secure.json")); _secureSettings = ssw.SecureSettings; } } - public string GetProfitTrailerLicenseKeyMasked() { + public string GetProfitTrailerLicenseKeyMasked() + { string result = ""; - if (!this.GeneralSettings.Application.ProfitTrailerLicense.Equals("")) { + if (!this.GeneralSettings.Application.ProfitTrailerLicense.Equals("")) + { result = this.GeneralSettings.Application.ProfitTrailerLicense.Substring(0, 4); - for (int i = 1; i < this.GeneralSettings.Application.ProfitTrailerLicense.Length - 8; i++) { + for (int i = 1; i < this.GeneralSettings.Application.ProfitTrailerLicense.Length - 8; i++) + { result += "*"; } @@ -63,26 +75,33 @@ namespace Core.Main { return result; } - public GeneralSettings GeneralSettings { - get { + public GeneralSettings GeneralSettings + { + get + { return _generalSettings; } } - public AnalyzerSettings AnalyzerSettings { - get { + public AnalyzerSettings AnalyzerSettings + { + get + { return _analyzerSettings; } } - public SecureSettings SecureSettings { - get { + public SecureSettings SecureSettings + { + get + { if (_secureSettings == null) _secureSettings = new SecureSettings(); return _secureSettings; } } - public void WriteGeneralSettings(string basePath) { + public void WriteGeneralSettings(string basePath) + { GeneralSettingsWrapper gsWrapper = new GeneralSettingsWrapper(); gsWrapper.GeneralSettings = this.GeneralSettings; @@ -91,7 +110,8 @@ namespace Core.Main { FileHelper.WriteTextToFile(basePath, "settings.general.json", JsonConvert.SerializeObject(gsWrapper, Formatting.Indented)); } - public void WriteAnalyzerSettings(string basePath) { + public void WriteAnalyzerSettings(string basePath) + { AnalyzerSettingsWrapper asWrapper = new AnalyzerSettingsWrapper(); asWrapper.AnalyzerSettings = this.AnalyzerSettings; @@ -104,7 +124,8 @@ namespace Core.Main { FileHelper.WriteTextToFile(basePath, "settings.analyzer.json", JsonConvert.SerializeObject(asWrapper, Formatting.Indented, settings)); } - public void WriteSecureSettings(string password, string basePath) { + public void WriteSecureSettings(string password, string basePath) + { string passwordEncrypted = EncryptionHelper.Encrypt(password); this.SecureSettings.MonitorPassword = passwordEncrypted; diff --git a/Core/MarketAnalyzer/BaseAnalyzer.cs b/Core/MarketAnalyzer/BaseAnalyzer.cs index cadee3b..3355e57 100644 --- a/Core/MarketAnalyzer/BaseAnalyzer.cs +++ b/Core/MarketAnalyzer/BaseAnalyzer.cs @@ -9,13 +9,17 @@ using Core.Main; using Core.Helper; using Core.Main.DataObjects.PTMagicData; -namespace Core.MarketAnalyzer { - public class BaseAnalyzer { - public static Dictionary GetJsonFromURL(string url, LogHelper log, string api) { +namespace Core.MarketAnalyzer +{ + public class BaseAnalyzer + { + public static Dictionary GetJsonFromURL(string url, LogHelper log, string api) + { Dictionary jsonObject = null; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); - if (api != ""){ + if (api != "") + { request.Headers.Add("X-CMC_PRO_API_KEY", api); } @@ -23,7 +27,8 @@ namespace Core.MarketAnalyzer { request.UserAgent = "PTMagic.Import"; request.KeepAlive = true; - try { + try + { HttpWebResponse httpResponse = (HttpWebResponse)request.GetResponse(); StreamReader jsonReader = new StreamReader(httpResponse.GetResponseStream()); @@ -33,17 +38,22 @@ namespace Core.MarketAnalyzer { jsonObject = JsonConvert.DeserializeObject>(jsonString); return jsonObject; - } catch (WebException ex) { + } + catch (WebException ex) + { log.DoLogCritical(ex.Message, ex); throw ex; - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical(ex.Message, ex); } return jsonObject; } - public static Newtonsoft.Json.Linq.JObject GetSimpleJsonObjectFromURL(string url, LogHelper log, bool swallowException) { + public static Newtonsoft.Json.Linq.JObject GetSimpleJsonObjectFromURL(string url, LogHelper log, bool swallowException) + { Newtonsoft.Json.Linq.JObject jsonObject = null; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); @@ -51,7 +61,8 @@ namespace Core.MarketAnalyzer { request.UserAgent = "PTMagic.Import"; request.KeepAlive = true; - try { + try + { HttpWebResponse httpResponse = (HttpWebResponse)request.GetResponse(); StreamReader jsonReader = new StreamReader(httpResponse.GetResponseStream()); @@ -61,17 +72,27 @@ namespace Core.MarketAnalyzer { jsonObject = JsonConvert.DeserializeObject(jsonString); return jsonObject; - } catch (WebException ex) { - if (swallowException) { + } + catch (WebException ex) + { + if (swallowException) + { // Do nothing, as we do not want to get this logged. Only uncritical functions uses this - } else { + } + else + { log.DoLogCritical("Url: " + url + " Message: " + ex.Message, ex); throw ex; } - } catch (Exception ex) { - if (swallowException) { + } + catch (Exception ex) + { + if (swallowException) + { // Do nothing, as we do not want to get this logged. Only uncritical functions uses this - } else { + } + else + { log.DoLogCritical("Url: " + url + " Message: " + ex.Message, ex); throw ex; } @@ -80,7 +101,8 @@ namespace Core.MarketAnalyzer { return jsonObject; } - public static List GetSimpleJsonListFromURL(string url, LogHelper log) { + public static List GetSimpleJsonListFromURL(string url, LogHelper log) + { List jsonObject = null; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); @@ -88,7 +110,8 @@ namespace Core.MarketAnalyzer { request.UserAgent = "PTMagic.Import"; request.KeepAlive = true; - try { + try + { HttpWebResponse httpResponse = (HttpWebResponse)request.GetResponse(); StreamReader jsonReader = new StreamReader(httpResponse.GetResponseStream()); @@ -98,17 +121,22 @@ namespace Core.MarketAnalyzer { jsonObject = JsonConvert.DeserializeObject>(jsonString); return jsonObject; - } catch (WebException ex) { + } + catch (WebException ex) + { log.DoLogCritical(ex.Message, ex); throw ex; - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical(ex.Message, ex); } return jsonObject; } - public static Newtonsoft.Json.Linq.JArray GetSimpleJsonArrayFromURL(string url, LogHelper log) { + public static Newtonsoft.Json.Linq.JArray GetSimpleJsonArrayFromURL(string url, LogHelper log) + { Newtonsoft.Json.Linq.JArray jsonObject = null; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); @@ -116,7 +144,8 @@ namespace Core.MarketAnalyzer { request.UserAgent = "PTMagic.Import"; request.KeepAlive = true; - try { + try + { HttpWebResponse httpResponse = (HttpWebResponse)request.GetResponse(); StreamReader jsonReader = new StreamReader(httpResponse.GetResponseStream()); @@ -126,43 +155,56 @@ namespace Core.MarketAnalyzer { jsonObject = JsonConvert.DeserializeObject(jsonString); return jsonObject; - } catch (WebException ex) { + } + catch (WebException ex) + { log.DoLogCritical(ex.Message, ex); throw ex; - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical(ex.Message, ex); } return jsonObject; } - public static string GetLatestGitHubRelease(LogHelper log, string defaultVersion) { + public static string GetLatestGitHubRelease(LogHelper log, string defaultVersion) + { string result = defaultVersion; - try { + try + { string baseUrl = "https://api.github.com/repos/legedric/ptmagic/releases/latest"; Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, true); - if (jsonObject != null) { + if (jsonObject != null) + { result = jsonObject.GetValue("tag_name").ToString(); } - } catch (WebException ex) { + } + catch (WebException ex) + { log.DoLogDebug("GitHub version check error: " + ex.Message); - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogDebug("GitHub version check error: " + ex.Message); } return result; } - public static double GetMainFiatCurrencyRate(string currency, LogHelper log) { + public static double GetMainFiatCurrencyRate(string currency, LogHelper log) + { double result = 1; string baseUrl = "http://free.currencyconverterapi.com/api/v5/convert?q=USD_" + currency + "&compact=y"; log.DoLogDebug("http://free.currencyconverterapi.com - Getting latest exchange rates..."); Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, false); - if (jsonObject != null) { + if (jsonObject != null) + { log.DoLogDebug("http://free.currencyconverterapi.com - Received latest exchange rates."); result = (double)jsonObject["USD_" + currency]["val"]; @@ -172,30 +214,38 @@ namespace Core.MarketAnalyzer { return result; } - public static Dictionary GetMarketInfosFromFile(PTMagicConfiguration systemConfiguration, LogHelper log) { + public static Dictionary GetMarketInfosFromFile(PTMagicConfiguration systemConfiguration, LogHelper log) + { Dictionary result = new Dictionary(); string marketInfoFilePath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar + "MarketInfo.json"; - if (File.Exists(marketInfoFilePath)) { - try { + if (File.Exists(marketInfoFilePath)) + { + try + { result = JsonConvert.DeserializeObject>(System.IO.File.ReadAllText(marketInfoFilePath)); - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogDebug(ex.Message); } } - if (result == null) { + if (result == null) + { result = new Dictionary(); } return result; } - public static void SaveMarketInfosToFile(Dictionary marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static void SaveMarketInfosToFile(Dictionary marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) + { FileHelper.WriteTextToFile(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar, "MarketInfo.json", JsonConvert.SerializeObject(marketInfos)); } - public static Dictionary GetMarketDataFromFile(PTMagicConfiguration systemConfiguration, LogHelper log, string platform, DateTime maxDateTime, string marketCaption) { + 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); @@ -207,11 +257,14 @@ namespace Core.MarketAnalyzer { .ToArray().OrderByDescending(f => f.LastWriteTimeUtc).ToList(); FileInfo marketFile = null; - if (marketFiles.Count > 0) { + if (marketFiles.Count > 0) + { marketFile = marketFiles.First(); log.DoLogDebug(platform + " - " + marketCaption + " market data loaded (" + marketFile.LastWriteTimeUtc.ToString() + ")"); - } else { + } + else + { log.DoLogDebug(platform + " - Not able to load " + marketCaption + " market data. Loading next youngest market file instead."); // Get market files younger than max datetime in ascending order (oldest file up top) @@ -219,33 +272,44 @@ namespace Core.MarketAnalyzer { .Select(x => { x.Refresh(); return x; }) .Where(x => x.LastWriteTimeUtc >= maxDateTime) .ToArray().OrderBy(f => f.LastWriteTimeUtc).ToList(); - if (marketFiles.Count > 0) { + if (marketFiles.Count > 0) + { marketFile = marketFiles.First(); log.DoLogDebug(platform + " - " + marketCaption + " market data loaded (" + marketFile.LastWriteTimeUtc.ToString() + ")"); } } - try { + try + { // Get JSON object result = JsonConvert.DeserializeObject>(marketFile.OpenText().ReadToEnd()); - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical(ex.Message, ex); } return result; } - public static Dictionary> BuildMarketTrends(string platform, string mainMarket, List marketList, string sortBy, bool isGlobal, Dictionary> output, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static Dictionary> BuildMarketTrends(string platform, string mainMarket, List marketList, string sortBy, bool isGlobal, Dictionary> output, PTMagicConfiguration systemConfiguration, LogHelper log) + { - try { + try + { List marketTrends = systemConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.FindAll(mt => mt.Platform.Equals(platform, StringComparison.InvariantCultureIgnoreCase)); - if (marketTrends.Count > 0) { + if (marketTrends.Count > 0) + { Dictionary recentMarkets = BaseAnalyzer.GetMarketDataFromFile(systemConfiguration, log, platform, DateTime.Now.ToUniversalTime(), "Recent"); - foreach (MarketTrend marketTrend in marketTrends) { - if (platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase)) { + foreach (MarketTrend marketTrend in marketTrends) + { + if (platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase)) + { log.DoLogInfo(platform + " - Building market trend changes for '" + marketTrend.Name + "' on main market '" + mainMarket + "' with " + marketList.Count.ToString() + " markets..."); - } else { + } + else + { log.DoLogInfo(platform + " - Building market trend changes for '" + marketTrend.Name + "'..."); } @@ -259,33 +323,42 @@ namespace Core.MarketAnalyzer { } } - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical(ex.Message, ex); } return output; } - public static List GetMarketTrendChanges(string platform, string mainMarket, MarketTrend marketTrend, List marketList, Dictionary recentMarkets, Dictionary trendMarkets, string sortBy, bool isGlobal, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static List GetMarketTrendChanges(string platform, string mainMarket, MarketTrend marketTrend, List marketList, Dictionary recentMarkets, Dictionary trendMarkets, string sortBy, bool isGlobal, PTMagicConfiguration systemConfiguration, LogHelper log) + { List result = new List(); var sortedMarkets = new SortedDictionary(recentMarkets).OrderBy(m => m.Value.Position); - if (sortBy.Equals("Volume")) { + if (sortBy.Equals("Volume")) + { sortedMarkets = new SortedDictionary(recentMarkets).OrderByDescending(m => m.Value.Volume24h); } int marketCount = 1; - foreach (KeyValuePair recentMarketPair in sortedMarkets) { - if (marketList.Count == 0 || marketList.Contains(recentMarketPair.Key)) { + foreach (KeyValuePair recentMarketPair in sortedMarkets) + { + if (marketList.Count == 0 || marketList.Contains(recentMarketPair.Key)) + { bool excludeMainCurrency = systemConfiguration.AnalyzerSettings.MarketAnalyzer.ExcludeMainCurrency; - if (!marketTrend.ExcludeMainCurrency) { + if (!marketTrend.ExcludeMainCurrency) + { excludeMainCurrency = marketTrend.ExcludeMainCurrency; } - if (platform.Equals("CoinMarketCap", StringComparison.InvariantCulture) && excludeMainCurrency) { + if (platform.Equals("CoinMarketCap", StringComparison.InvariantCulture) && excludeMainCurrency) + { // Check if this is the main currency (only for CoinMarketCap) - if (recentMarketPair.Value.Symbol.Equals(mainMarket, StringComparison.InvariantCultureIgnoreCase)) { + if (recentMarketPair.Value.Symbol.Equals(mainMarket, StringComparison.InvariantCultureIgnoreCase)) + { // If the current market is the main currency, skip it continue; @@ -293,27 +366,33 @@ namespace Core.MarketAnalyzer { } Market recentMarket = recentMarkets[recentMarketPair.Key]; - if (trendMarkets.ContainsKey(recentMarketPair.Key)) { + if (trendMarkets.ContainsKey(recentMarketPair.Key)) + { List ignoredMarkets = SystemHelper.ConvertTokenStringToList(marketTrend.IgnoredMarkets, ","); - if (ignoredMarkets.Contains(recentMarketPair.Value.Symbol)) { + if (ignoredMarkets.Contains(recentMarketPair.Value.Symbol)) + { 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)) { + if (allowedMarkets.Count > 0 && !allowedMarkets.Contains(recentMarketPair.Value.Symbol)) + { log.DoLogDebug(platform + " - Market trend '" + marketTrend.Name + "' for '" + recentMarketPair.Key + "' is not allowed in this trend."); continue; } Market trendMarket = trendMarkets[recentMarketPair.Key]; - if (trendMarket != null) { + if (trendMarket != null) + { 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) { + if (!platform.Equals("CoinMarketCap", StringComparison.InvariantCulture) && marketTrend.TrendCurrency.Equals("Fiat", StringComparison.InvariantCultureIgnoreCase)) + { + if (recentMarket.MainCurrencyPriceUSD > 0 && trendMarket.MainCurrencyPriceUSD > 0) + { recentMarketPrice = recentMarketPrice * recentMarket.MainCurrencyPriceUSD; trendMarketPrice = trendMarketPrice * trendMarket.MainCurrencyPriceUSD; } @@ -340,7 +419,8 @@ namespace Core.MarketAnalyzer { } } - if (marketTrend.MaxMarkets > 0 && isGlobal) { + if (marketTrend.MaxMarkets > 0 && isGlobal) + { int maxMarkets = (marketTrend.MaxMarkets <= result.Count) ? marketTrend.MaxMarkets : result.Count; result = result.GetRange(0, maxMarkets); @@ -349,29 +429,38 @@ namespace Core.MarketAnalyzer { return result; } - public static Dictionary BuildGlobalMarketTrends(Dictionary> globalMarketTrendChanges, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static Dictionary BuildGlobalMarketTrends(Dictionary> globalMarketTrendChanges, PTMagicConfiguration systemConfiguration, LogHelper log) + { Dictionary result = new Dictionary(); List marketTrends = systemConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends; - if (marketTrends.Count > 0) { - foreach (MarketTrend marketTrend in marketTrends) { + if (marketTrends.Count > 0) + { + foreach (MarketTrend marketTrend in marketTrends) + { log.DoLogInfo("Building market trend average for '" + marketTrend.Name + "'"); - if (globalMarketTrendChanges.ContainsKey(marketTrend.Name)) { + if (globalMarketTrendChanges.ContainsKey(marketTrend.Name)) + { List marketTrendChanges = globalMarketTrendChanges[marketTrend.Name]; - if (marketTrendChanges != null && marketTrendChanges.Count > 0) { + if (marketTrendChanges != null && marketTrendChanges.Count > 0) + { double averageTrendChange = marketTrendChanges.Average(mtc => mtc.TrendChange); 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 { + } + else + { result.Add(marketTrend.Name, 0); log.DoLogWarn("No market trend changes found for '" + marketTrend.Name + "' - returning 0%"); } - } else { + } + else + { result.Add(marketTrend.Name, 0); log.DoLogWarn("Market trend '" + marketTrend.Name + "' not found in globalMarketTrendChanges[] - returning 0%"); diff --git a/Core/MarketAnalyzer/Binance.cs b/Core/MarketAnalyzer/Binance.cs index dfbe20a..1b31c0d 100644 --- a/Core/MarketAnalyzer/Binance.cs +++ b/Core/MarketAnalyzer/Binance.cs @@ -10,52 +10,71 @@ using Newtonsoft.Json; using Core.ProfitTrailer; using System.Net; -namespace Core.MarketAnalyzer { - public class Binance : BaseAnalyzer { - public static double GetMainCurrencyPrice(string mainMarket, PTMagicConfiguration systemConfiguration, LogHelper log) { +namespace Core.MarketAnalyzer +{ + public class Binance : BaseAnalyzer + { + public static double GetMainCurrencyPrice(string mainMarket, PTMagicConfiguration systemConfiguration, LogHelper log) + { double result = 0; - try { + try + { string baseUrl = "https://api.binance.com/api/v1/ticker/24hr?symbol=" + mainMarket + "USDT"; log.DoLogInfo("Binance - Getting main market price..."); Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, false); - if (jsonObject != null) { + if (jsonObject != null) + { log.DoLogInfo("Binance - Market data received for " + mainMarket + "USDT"); result = (double)jsonObject.GetValue("lastPrice"); log.DoLogInfo("Binance - Current price for " + mainMarket + "USDT: " + result.ToString("#,#0.00") + " USD"); } - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical(ex.Message, ex); } return result; } - public static List GetMarketData(string mainMarket, Dictionary marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) { + 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 { + try + { string baseUrl = "https://api.binance.com/api/v1/ticker/24hr"; log.DoLogInfo("Binance - Getting market data..."); Newtonsoft.Json.Linq.JArray jsonArray = GetSimpleJsonArrayFromURL(baseUrl, log); - if (jsonArray.Count > 0) { + if (jsonArray.Count > 0) + { double mainCurrencyPrice = 1; - if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)) { + if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)) + { mainCurrencyPrice = Binance.GetMainCurrencyPrice(mainMarket, systemConfiguration, log); } log.DoLogInfo("Binance - Market data received for " + jsonArray.Count.ToString() + " currencies"); - if (mainCurrencyPrice > 0) { + if (mainCurrencyPrice > 0) + { Dictionary markets = new Dictionary(); - foreach (Newtonsoft.Json.Linq.JObject currencyTicker in jsonArray) { + foreach (Newtonsoft.Json.Linq.JObject currencyTicker in jsonArray) + { string marketName = currencyTicker["symbol"].ToString(); - if (marketName.EndsWith(mainMarket, StringComparison.InvariantCultureIgnoreCase)) { + //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; @@ -72,6 +91,12 @@ namespace Core.MarketAnalyzer { markets.Add(market.Name, market); result.Add(market.Name); + } + else + { + //Let the user know that the problem market was ignored. + log.DoLogInfo("Binance - Ignoring bad market data for " + marketName); + } } } @@ -89,23 +114,33 @@ namespace Core.MarketAnalyzer { FileHelper.CleanupFiles(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar, systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours); log.DoLogInfo("Binance - Market data cleaned."); - } else { + } + else + { log.DoLogError("Binance - 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())) { + } + 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) { + if (errorData != null) + { string errorMessage = "Unable to get data from Binance with URL '" + errorResponse.ResponseUri + "'!"; - if (errorData.ContainsKey("code")) { + if (errorData.ContainsKey("code")) + { errorMessage += " - Code: " + errorData["code"]; } - if (errorData.ContainsKey("msg")) { + if (errorData.ContainsKey("msg")) + { errorMessage += " - Message: " + errorData["msg"]; } @@ -115,7 +150,9 @@ namespace Core.MarketAnalyzer { } } result = null; - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical("Exception while getting data for '" + lastMarket + "': " + ex.Message, ex); result = null; } @@ -123,24 +160,31 @@ namespace Core.MarketAnalyzer { return result; } - public static void CheckFirstSeenDates(Dictionary markets, ref Dictionary marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static void CheckFirstSeenDates(Dictionary markets, ref Dictionary marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) + { log.DoLogInfo("Binance - Checking first seen dates for " + markets.Count + " markets. This may take a while..."); int marketsChecked = 0; - foreach (string key in markets.Keys) { + foreach (string key in markets.Keys) + { // Save market info MarketInfo marketInfo = null; - if (marketInfos.ContainsKey(key)) { + if (marketInfos.ContainsKey(key)) + { marketInfo = marketInfos[key]; } - if (marketInfo == null) { + if (marketInfo == null) + { marketInfo = new MarketInfo(); marketInfo.Name = key; marketInfos.Add(key, marketInfo); marketInfo.FirstSeen = Binance.GetFirstSeenDate(key, systemConfiguration, log); - } else { - if (marketInfo.FirstSeen == Constants.confMinDate) { + } + else + { + if (marketInfo.FirstSeen == Constants.confMinDate) + { marketInfo.FirstSeen = Binance.GetFirstSeenDate(key, systemConfiguration, log); } } @@ -148,13 +192,15 @@ namespace Core.MarketAnalyzer { marketsChecked++; - if ((marketsChecked % 20) == 0) { + if ((marketsChecked % 20) == 0) + { log.DoLogInfo("Binance - Yes, I am still checking first seen dates... " + marketsChecked + "/" + markets.Count + " markets done..."); } } } - public static DateTime GetFirstSeenDate(string marketName, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static DateTime GetFirstSeenDate(string marketName, PTMagicConfiguration systemConfiguration, LogHelper log) + { DateTime result = Constants.confMinDate; string baseUrl = "https://api.binance.com/api/v1/klines?interval=1d&symbol=" + marketName + "&limit=100"; @@ -162,7 +208,8 @@ namespace Core.MarketAnalyzer { log.DoLogDebug("Binance - Getting first seen date for '" + marketName + "'..."); Newtonsoft.Json.Linq.JArray jsonArray = GetSimpleJsonArrayFromURL(baseUrl, log); - if (jsonArray.Count > 0) { + if (jsonArray.Count > 0) + { result = Constants.Epoch.AddMilliseconds((Int64)jsonArray[0][0]); log.DoLogDebug("Binance - First seen date for '" + marketName + "' set to " + result.ToString()); } @@ -170,29 +217,35 @@ namespace Core.MarketAnalyzer { return result; } - public static List GetMarketTicks(string marketName, int ticksNeeded, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static List GetMarketTicks(string marketName, int ticksNeeded, PTMagicConfiguration systemConfiguration, LogHelper log) + { List result = new List(); - try { + try + { Int64 endTime = (Int64)Math.Ceiling(DateTime.Now.ToUniversalTime().Subtract(Constants.Epoch).TotalMilliseconds); int ticksLimit = 500; string baseUrl = ""; int ticksFetched = 0; - if (ticksNeeded < ticksLimit) { + if (ticksNeeded < ticksLimit) + { ticksLimit = ticksNeeded; } bool go = true; - while (ticksFetched < ticksNeeded && go) { + while (ticksFetched < ticksNeeded && go) + { baseUrl = "https://api.binance.com/api/v1/klines?interval=1m&symbol=" + marketName + "&endTime=" + endTime.ToString() + "&limit=" + ticksLimit.ToString(); log.DoLogDebug("Binance - Getting " + ticksLimit.ToString() + " ticks for '" + marketName + "'..."); Newtonsoft.Json.Linq.JArray jsonArray = GetSimpleJsonArrayFromURL(baseUrl, log); - if (jsonArray.Count > 0) { + if (jsonArray.Count > 0) + { log.DoLogDebug("Binance - " + jsonArray.Count.ToString() + " ticks received."); - foreach (Newtonsoft.Json.Linq.JArray marketTick in jsonArray) { + foreach (Newtonsoft.Json.Linq.JArray marketTick in jsonArray) + { MarketTick tick = new MarketTick(); tick.Price = (double)marketTick[4]; @@ -204,26 +257,37 @@ namespace Core.MarketAnalyzer { ticksFetched = ticksFetched + jsonArray.Count; endTime = endTime - ticksLimit * 60 * 1000; - if (ticksNeeded - ticksFetched < ticksLimit) { + if (ticksNeeded - ticksFetched < ticksLimit) + { ticksLimit = ticksNeeded - ticksFetched; } - } else { + } + else + { log.DoLogDebug("Binance - 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())) { + } + 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) { + if (errorData != null) + { string errorMessage = "Unable to get data from Binance with URL '" + errorResponse.ResponseUri + "'!"; - if (errorData.ContainsKey("code")) { + if (errorData.ContainsKey("code")) + { errorMessage += " - Code: " + errorData["code"]; } - if (errorData.ContainsKey("msg")) { + if (errorData.ContainsKey("msg")) + { errorMessage += " - Message: " + errorData["msg"]; } @@ -233,17 +297,21 @@ namespace Core.MarketAnalyzer { } } result = null; - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical(ex.Message, ex); } return result; } - public static void CheckForMarketDataRecreation(string mainMarket, Dictionary markets, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static void CheckForMarketDataRecreation(string mainMarket, Dictionary markets, PTMagicConfiguration systemConfiguration, LogHelper log) + { string binanceDataDirectoryPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar; - if (!Directory.Exists(binanceDataDirectoryPath)) { + if (!Directory.Exists(binanceDataDirectoryPath)) + { Directory.CreateDirectory(binanceDataDirectoryPath); } @@ -253,22 +321,27 @@ namespace Core.MarketAnalyzer { DateTime latestMarketDataFileDateTime = Constants.confMinDate; List marketFiles = dataDirectory.EnumerateFiles("MarketData*").ToList(); FileInfo latestMarketDataFile = null; - if (marketFiles.Count > 0) { + if (marketFiles.Count > 0) + { latestMarketDataFile = marketFiles.OrderByDescending(mdf => mdf.LastWriteTimeUtc).First(); latestMarketDataFileDateTime = latestMarketDataFile.LastWriteTimeUtc; } - if (latestMarketDataFileDateTime < DateTime.Now.ToUniversalTime().AddMinutes(-20)) { + if (latestMarketDataFileDateTime < DateTime.Now.ToUniversalTime().AddMinutes(-20)) + { int lastMarketDataAgeInSeconds = (int)Math.Ceiling(DateTime.Now.ToUniversalTime().Subtract(latestMarketDataFileDateTime).TotalSeconds); // Go back in time and create market data DateTime startDateTime = DateTime.Now.ToUniversalTime(); DateTime endDateTime = DateTime.Now.ToUniversalTime().AddHours(-systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours); - if (latestMarketDataFileDateTime != Constants.confMinDate && latestMarketDataFileDateTime > endDateTime) { + if (latestMarketDataFileDateTime != Constants.confMinDate && latestMarketDataFileDateTime > endDateTime) + { // Existing market files too old => Recreate market data for configured timeframe log.DoLogInfo("Binance - Recreating market data for " + markets.Count + " markets over " + SystemHelper.GetProperDurationTime(lastMarketDataAgeInSeconds) + ". This may take a while..."); endDateTime = latestMarketDataFileDateTime; - } else { + } + else + { // No existing market files found => Recreate market data for configured timeframe log.DoLogInfo("Binance - Recreating market data for " + markets.Count + " markets over " + systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours + " hours. This may take a while..."); } @@ -277,17 +350,20 @@ namespace Core.MarketAnalyzer { // Get Ticks for main market List mainMarketTicks = new List(); - if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)) { + if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)) + { mainMarketTicks = Binance.GetMarketTicks(mainMarket + "USDT", totalTicks, systemConfiguration, log); } // Get Ticks for all markets log.DoLogDebug("Binance - Getting ticks for '" + markets.Count + "' markets"); Dictionary> marketTicks = new Dictionary>(); - foreach (string key in markets.Keys) { + foreach (string key in markets.Keys) + { marketTicks.Add(key, Binance.GetMarketTicks(key, totalTicks, systemConfiguration, log)); - if ((marketTicks.Count % 10) == 0) { + if ((marketTicks.Count % 10) == 0) + { log.DoLogInfo("Binance - No worries, I am still alive... " + marketTicks.Count + "/" + markets.Count + " markets done..."); } } @@ -298,24 +374,30 @@ namespace Core.MarketAnalyzer { // 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)) { + if (marketTicks.Count > 0) + { + for (DateTime tickTime = startDateTime; tickTime >= endDateTime; tickTime = tickTime.AddMinutes(-1)) + { completedTicks++; double mainCurrencyPrice = 1; - if (mainMarketTicks.Count > 0) { + if (mainMarketTicks.Count > 0) + { List mainCurrencyTickRange = mainMarketTicks.FindAll(t => t.Time <= tickTime); - if (mainCurrencyTickRange.Count > 0) { + 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) { + foreach (string key in markets.Keys) + { List tickRange = marketTicks[key].FindAll(t => t.Time <= tickTime); - if (tickRange.Count > 0) { + if (tickRange.Count > 0) + { MarketTick marketTick = tickRange.OrderByDescending(t => t.Time).First(); Market market = new Market(); @@ -336,7 +418,8 @@ namespace Core.MarketAnalyzer { log.DoLogDebug("Binance - Market data saved for tick " + fileDateTime.ToString() + " - MainCurrencyPrice=" + mainCurrencyPrice.ToString("#,#0.00") + " USD."); - if ((completedTicks % 100) == 0) { + if ((completedTicks % 100) == 0) + { log.DoLogInfo("Binance - Our magicbots are still at work, hang on... " + completedTicks + "/" + totalTicks + " ticks done..."); } } diff --git a/Core/MarketAnalyzer/Bittrex.cs b/Core/MarketAnalyzer/Bittrex.cs index 67aee7a..ef9a624 100644 --- a/Core/MarketAnalyzer/Bittrex.cs +++ b/Core/MarketAnalyzer/Bittrex.cs @@ -9,55 +9,71 @@ using Core.Main.DataObjects.PTMagicData; using Newtonsoft.Json; using Core.ProfitTrailer; -namespace Core.MarketAnalyzer { - public class Bittrex : BaseAnalyzer { - public static double GetMainCurrencyPrice(string mainMarket, PTMagicConfiguration systemConfiguration, LogHelper log) { +namespace Core.MarketAnalyzer +{ + public class Bittrex : BaseAnalyzer + { + public static double GetMainCurrencyPrice(string mainMarket, PTMagicConfiguration systemConfiguration, LogHelper log) + { double result = 0; - try { + try + { string baseUrl = "https://bittrex.com/api/v1.1/public/getmarketsummary?market=USDT-" + mainMarket; log.DoLogInfo("Bittrex - Getting main market price..."); Dictionary jsonObject = GetJsonFromURL(baseUrl, log, ""); - if (jsonObject.Count > 0) { - if (jsonObject["success"]) { + if (jsonObject.Count > 0) + { + if (jsonObject["success"]) + { log.DoLogInfo("Bittrex - Market data received for USDT-" + mainMarket); result = jsonObject["result"][0]["Last"]; log.DoLogInfo("Bittrex - Current price for USDT-" + mainMarket + ": " + result.ToString("#,#0.00") + " USD"); } } - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical(ex.Message, ex); } return result; } - public static List GetMarketData(string mainMarket, Dictionary marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) { + 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 { + try + { string baseUrl = "https://bittrex.com/api/v2.0/pub/markets/GetMarketSummaries"; log.DoLogInfo("Bittrex - Getting market data..."); Dictionary jsonObject = GetJsonFromURL(baseUrl, log, ""); - if (jsonObject.Count > 0) { - if (jsonObject["success"]) { + if (jsonObject.Count > 0) + { + if (jsonObject["success"]) + { log.DoLogInfo("Bittrex - Market data received for " + jsonObject["result"].Count.ToString() + " currencies"); double mainCurrencyPrice = 1; - if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)) { + if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)) + { mainCurrencyPrice = Bittrex.GetMainCurrencyPrice(mainMarket, systemConfiguration, log); } - if (mainCurrencyPrice > 0) { + if (mainCurrencyPrice > 0) + { Dictionary markets = new Dictionary(); - foreach (Newtonsoft.Json.Linq.JObject currencyTicker in jsonObject["result"]) { + foreach (Newtonsoft.Json.Linq.JObject currencyTicker in jsonObject["result"]) + { string marketName = currencyTicker["Summary"]["MarketName"].ToString(); - if (marketName.StartsWith(mainMarket, StringComparison.InvariantCultureIgnoreCase)) { + if (marketName.StartsWith(mainMarket, StringComparison.InvariantCultureIgnoreCase)) + { // Set last values in case any error occurs lastMarket = marketName; @@ -77,11 +93,13 @@ namespace Core.MarketAnalyzer { // Save market info MarketInfo marketInfo = null; - if (marketInfos.ContainsKey(marketName)) { + if (marketInfos.ContainsKey(marketName)) + { marketInfo = marketInfos[marketName]; } - if (marketInfo == null) { + if (marketInfo == null) + { marketInfo = new MarketInfo(); marketInfo.Name = marketName; marketInfos.Add(marketName, marketInfo); @@ -103,13 +121,17 @@ namespace Core.MarketAnalyzer { FileHelper.CleanupFiles(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar, systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours); log.DoLogInfo("Bittrex - Market data cleaned."); - } else { + } + else + { log.DoLogError("Bittrex - Failed to get main market price for " + mainMarket + "."); result = null; } } } - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical("Exception while getting data for '" + lastMarket + "': " + ex.Message, ex); result = null; } @@ -117,20 +139,26 @@ namespace Core.MarketAnalyzer { return result; } - public static List GetMarketTicks(string marketName, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static List GetMarketTicks(string marketName, PTMagicConfiguration systemConfiguration, LogHelper log) + { List result = new List(); - try { + try + { string baseUrl = "https://bittrex.com/Api/v2.0/pub/market/GetTicks?tickInterval=oneMin&marketName=" + marketName; log.DoLogDebug("Bittrex - Getting ticks for '" + marketName + "'..."); Dictionary jsonObject = GetJsonFromURL(baseUrl, log, ""); - if (jsonObject.Count > 0) { - if (jsonObject["success"]) { - if (jsonObject["result"] != null) { + if (jsonObject.Count > 0) + { + if (jsonObject["success"]) + { + if (jsonObject["result"] != null) + { log.DoLogDebug("Bittrex - " + jsonObject["result"].Count.ToString() + " ticks received."); - foreach (var marketTick in jsonObject["result"]) { + foreach (var marketTick in jsonObject["result"]) + { MarketTick tick = new MarketTick(); tick.Price = (double)marketTick["C"]; @@ -138,22 +166,28 @@ namespace Core.MarketAnalyzer { result.Add(tick); } - } else { + } + else + { log.DoLogDebug("Bittrex - No ticks received."); } } } - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical(ex.Message, ex); } return result; } - public static void CheckForMarketDataRecreation(string mainMarket, Dictionary markets, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static void CheckForMarketDataRecreation(string mainMarket, Dictionary markets, PTMagicConfiguration systemConfiguration, LogHelper log) + { string bittrexDataDirectoryPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar; - if (!Directory.Exists(bittrexDataDirectoryPath)) { + if (!Directory.Exists(bittrexDataDirectoryPath)) + { Directory.CreateDirectory(bittrexDataDirectoryPath); } @@ -163,39 +197,47 @@ namespace Core.MarketAnalyzer { DateTime latestMarketDataFileDateTime = Constants.confMinDate; List marketFiles = dataDirectory.EnumerateFiles("MarketData*").ToList(); FileInfo latestMarketDataFile = null; - if (marketFiles.Count > 0) { + if (marketFiles.Count > 0) + { latestMarketDataFile = marketFiles.OrderByDescending(mdf => mdf.LastWriteTimeUtc).First(); latestMarketDataFileDateTime = latestMarketDataFile.LastWriteTimeUtc; } - if (latestMarketDataFileDateTime < DateTime.Now.ToUniversalTime().AddMinutes(-20)) { + if (latestMarketDataFileDateTime < DateTime.Now.ToUniversalTime().AddMinutes(-20)) + { int lastMarketDataAgeInSeconds = (int)Math.Ceiling(DateTime.Now.ToUniversalTime().Subtract(latestMarketDataFileDateTime).TotalSeconds); // Go back in time and create market data DateTime startDateTime = DateTime.Now.ToUniversalTime(); DateTime endDateTime = DateTime.Now.ToUniversalTime().AddHours(-systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours); - if (latestMarketDataFileDateTime != Constants.confMinDate && latestMarketDataFileDateTime > endDateTime) { + if (latestMarketDataFileDateTime != Constants.confMinDate && latestMarketDataFileDateTime > endDateTime) + { // Existing market files too old => Recreate market data for configured timeframe log.DoLogInfo("Bittrex - Recreating market data for " + markets.Count + " markets over " + SystemHelper.GetProperDurationTime(lastMarketDataAgeInSeconds) + ". This may take a while..."); endDateTime = latestMarketDataFileDateTime; - } else { + } + else + { // No existing market files found => Recreate market data for configured timeframe log.DoLogInfo("Bittrex - Recreating market data for " + markets.Count + " markets over " + systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours + " hours. This may take a while..."); } // Get Ticks for main market List mainMarketTicks = new List(); - if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)) { + if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)) + { mainMarketTicks = Bittrex.GetMarketTicks("USDT-" + mainMarket, systemConfiguration, log); } // Get Ticks for all markets log.DoLogDebug("Bittrex - Getting ticks for '" + markets.Count + "' markets"); Dictionary> marketTicks = new Dictionary>(); - foreach (string key in markets.Keys) { + foreach (string key in markets.Keys) + { marketTicks.Add(key, Bittrex.GetMarketTicks(key, systemConfiguration, log)); - if ((marketTicks.Count % 10) == 0) { + if ((marketTicks.Count % 10) == 0) + { log.DoLogInfo("Bittrex - No worries, I am still alive... " + marketTicks.Count + "/" + markets.Count + " markets done..."); } } @@ -206,24 +248,30 @@ namespace Core.MarketAnalyzer { int totalTicks = (int)Math.Ceiling(startDateTime.Subtract(endDateTime).TotalMinutes); int completedTicks = 0; - if (marketTicks.Count > 0) { - for (DateTime tickTime = startDateTime.ToUniversalTime(); tickTime >= endDateTime.ToUniversalTime(); tickTime = tickTime.AddMinutes(-1)) { + if (marketTicks.Count > 0) + { + for (DateTime tickTime = startDateTime.ToUniversalTime(); tickTime >= endDateTime.ToUniversalTime(); tickTime = tickTime.AddMinutes(-1)) + { completedTicks++; double mainCurrencyPrice = 1; - if (mainMarketTicks.Count > 0) { + if (mainMarketTicks.Count > 0) + { List mainCurrencyTickRange = mainMarketTicks.FindAll(t => t.Time <= tickTime); - if (mainCurrencyTickRange.Count > 0) { + 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) { + foreach (string key in markets.Keys) + { List tickRange = marketTicks[key].FindAll(t => t.Time <= tickTime); - if (tickRange.Count > 0) { + if (tickRange.Count > 0) + { MarketTick marketTick = tickRange.OrderByDescending(t => t.Time).First(); Market market = new Market(); @@ -244,7 +292,8 @@ namespace Core.MarketAnalyzer { log.DoLogDebug("Bittrex - Market data saved for tick " + fileDateTime.ToString() + " - MainCurrencyPrice=" + mainCurrencyPrice.ToString("#,#0.00") + " USD."); - if ((completedTicks % 100) == 0) { + if ((completedTicks % 100) == 0) + { log.DoLogInfo("Bittrex - Our magicbots are still at work, hang on... " + completedTicks + "/" + totalTicks + " ticks done..."); } } diff --git a/Core/MarketAnalyzer/CoinMarketCap.cs b/Core/MarketAnalyzer/CoinMarketCap.cs index dda7cf3..193575d 100644 --- a/Core/MarketAnalyzer/CoinMarketCap.cs +++ b/Core/MarketAnalyzer/CoinMarketCap.cs @@ -8,11 +8,15 @@ using Core.Helper; using Core.Main.DataObjects.PTMagicData; using Newtonsoft.Json; -namespace Core.MarketAnalyzer { - public class CoinMarketCap : BaseAnalyzer { - public static string GetMarketData(PTMagicConfiguration systemConfiguration, LogHelper log) { +namespace Core.MarketAnalyzer +{ + public class CoinMarketCap : BaseAnalyzer + { + public static string GetMarketData(PTMagicConfiguration systemConfiguration, LogHelper log) + { string result = ""; - try { + try + { string baseUrl = "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?limit=200"; string cmcAPI = systemConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey; @@ -20,29 +24,34 @@ namespace Core.MarketAnalyzer { Dictionary jsonObject = GetJsonFromURL(baseUrl, log, cmcAPI); - if (jsonObject.Count > 0) { - if (jsonObject["data"] != null) { + if (jsonObject.Count > 0) + { + if (jsonObject["data"] != null) + { log.DoLogInfo("CoinMarketCap - Market data received for " + jsonObject["data"].Count + " currencies"); Dictionary markets = new Dictionary(); - for (int i = 0; i < jsonObject["data"].Count; i++){ - - if (jsonObject["data"][i]["quote"]["USD"] != null){ - Market market = new Market(); - market.Position = markets.Count + 1; - market.Name = jsonObject["data"][i]["name"].ToString(); - market.Symbol = jsonObject["data"][i]["symbol"].ToString(); - market.Price = (double)jsonObject["data"][i]["quote"]["USD"]["price"]; - market.Volume24h = (double)jsonObject["data"][i]["quote"]["USD"]["volume_24h"]; - if (!String.IsNullOrEmpty(jsonObject["data"][i]["quote"]["USD"]["percent_change_24h"].ToString())) { - market.TrendChange24h = (double)jsonObject["data"][i]["quote"]["USD"]["percent_change_24h"]; - } + for (int i = 0; i < jsonObject["data"].Count; i++) + { - markets.Add(market.Name, market); + if (jsonObject["data"][i]["quote"]["USD"] != null) + { + Market market = new Market(); + market.Position = markets.Count + 1; + market.Name = jsonObject["data"][i]["name"].ToString(); + market.Symbol = jsonObject["data"][i]["symbol"].ToString(); + market.Price = (double)jsonObject["data"][i]["quote"]["USD"]["price"]; + market.Volume24h = (double)jsonObject["data"][i]["quote"]["USD"]["volume_24h"]; + if (!String.IsNullOrEmpty(jsonObject["data"][i]["quote"]["USD"]["percent_change_24h"].ToString())) + { + market.TrendChange24h = (double)jsonObject["data"][i]["quote"]["USD"]["percent_change_24h"]; + } + + markets.Add(market.Name, market); } } - + CoinMarketCap.CheckForMarketDataRecreation(markets, systemConfiguration, log); DateTime fileDateTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, 0).ToUniversalTime(); @@ -56,7 +65,9 @@ namespace Core.MarketAnalyzer { log.DoLogInfo("CoinMarketCap - Market data cleaned."); } } - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical(ex.Message, ex); result = ex.Message; } @@ -64,10 +75,12 @@ namespace Core.MarketAnalyzer { return result; } - public static void CheckForMarketDataRecreation(Dictionary markets, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static void CheckForMarketDataRecreation(Dictionary markets, PTMagicConfiguration systemConfiguration, LogHelper log) + { string coinMarketCapDataDirectoryPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathCoinMarketCap + Path.DirectorySeparatorChar; - if (!Directory.Exists(coinMarketCapDataDirectoryPath)) { + if (!Directory.Exists(coinMarketCapDataDirectoryPath)) + { Directory.CreateDirectory(coinMarketCapDataDirectoryPath); } @@ -80,33 +93,43 @@ namespace Core.MarketAnalyzer { bool build24hMarketDataFile = false; FileInfo marketFile = null; - if (marketFiles.Count > 0) { + if (marketFiles.Count > 0) + { marketFile = marketFiles.First(); - if (marketFile.LastWriteTimeUtc <= DateTime.Now.AddHours(-24).AddMinutes(-systemConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes).AddSeconds(-10)) { + if (marketFile.LastWriteTimeUtc <= DateTime.Now.AddHours(-24).AddMinutes(-systemConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes).AddSeconds(-10)) + { log.DoLogDebug("CoinMarketCap - 24h market data file too old (" + marketFile.LastWriteTimeUtc.ToString() + "). Rebuilding data..."); build24hMarketDataFile = true; } - } else { + } + else + { marketFiles = dataDirectory.EnumerateFiles("MarketData*") .Select(x => { x.Refresh(); return x; }) .Where(x => x.LastWriteTimeUtc >= DateTime.Now.AddHours(-24)) .ToArray().OrderBy(f => f.LastWriteTimeUtc).ToList(); - if (marketFiles.Count > 0) { + if (marketFiles.Count > 0) + { marketFile = marketFiles.First(); - if (marketFile.LastWriteTimeUtc >= DateTime.Now.AddHours(-24).AddMinutes(systemConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes).AddSeconds(10)) { + if (marketFile.LastWriteTimeUtc >= DateTime.Now.AddHours(-24).AddMinutes(systemConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes).AddSeconds(10)) + { log.DoLogDebug("CoinMarketCap - 24h market data file too young (" + marketFile.LastWriteTimeUtc.ToString() + "). Rebuilding data..."); build24hMarketDataFile = true; } - } else { + } + else + { log.DoLogDebug("CoinMarketCap - 24h market data not found. Rebuilding data..."); build24hMarketDataFile = true; } } - if (build24hMarketDataFile) { + if (build24hMarketDataFile) + { Dictionary markets24h = new Dictionary(); - foreach (string key in markets.Keys) { + foreach (string key in markets.Keys) + { Market market24h = new Market(); market24h.Position = markets.Count + 1; market24h.Name = markets[key].Name; diff --git a/Core/MarketAnalyzer/Poloniex.cs b/Core/MarketAnalyzer/Poloniex.cs index 2d8b546..091fef1 100644 --- a/Core/MarketAnalyzer/Poloniex.cs +++ b/Core/MarketAnalyzer/Poloniex.cs @@ -9,54 +9,69 @@ using Core.Main.DataObjects.PTMagicData; using Newtonsoft.Json; using Core.ProfitTrailer; -namespace Core.MarketAnalyzer { - public class Poloniex : BaseAnalyzer { - public static double GetMainCurrencyPrice(string mainMarket, PTMagicConfiguration systemConfiguration, LogHelper log) { +namespace Core.MarketAnalyzer +{ + public class Poloniex : BaseAnalyzer + { + public static double GetMainCurrencyPrice(string mainMarket, PTMagicConfiguration systemConfiguration, LogHelper log) + { double result = 0; - try { + try + { string baseUrl = "https://bittrex.com/api/v1.1/public/getmarketsummary?market=USDT-" + mainMarket; log.DoLogInfo("Poloniex - Getting main market price..."); Dictionary jsonObject = GetJsonFromURL(baseUrl, log, ""); - if (jsonObject.Count > 0) { - if (jsonObject["success"]) { + if (jsonObject.Count > 0) + { + if (jsonObject["success"]) + { log.DoLogInfo("Poloniex - Market data received for USDT_" + mainMarket); result = jsonObject["result"][0]["Last"]; log.DoLogInfo("Poloniex - Current price for USDT_" + mainMarket + ": " + result.ToString("#,#0.00") + " USD"); } } - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical(ex.Message, ex); } return result; } - public static List GetMarketData(string mainMarket, Dictionary marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static List GetMarketData(string mainMarket, Dictionary marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) + { List result = new List(); string lastMarket = ""; KeyValuePair lastTicker = new KeyValuePair(); - try { + try + { string baseUrl = "https://poloniex.com/public?command=returnTicker"; log.DoLogInfo("Poloniex - Getting market data..."); Dictionary jsonObject = GetJsonFromURL(baseUrl, log, ""); - if (jsonObject.Count > 0) { + if (jsonObject.Count > 0) + { log.DoLogInfo("Poloniex - Market data received for " + jsonObject.Count.ToString() + " currencies"); double mainCurrencyPrice = 1; - if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)) { + if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)) + { mainCurrencyPrice = Poloniex.GetMainCurrencyPrice(mainMarket, systemConfiguration, log); } - if (mainCurrencyPrice > 0) { + if (mainCurrencyPrice > 0) + { Dictionary markets = new Dictionary(); - foreach (KeyValuePair currencyTicker in jsonObject) { + foreach (KeyValuePair currencyTicker in jsonObject) + { string marketName = currencyTicker.Key.ToString(); - if (marketName.StartsWith(mainMarket, StringComparison.InvariantCultureIgnoreCase)) { + if (marketName.StartsWith(mainMarket, StringComparison.InvariantCultureIgnoreCase)) + { // Set last values in case any error occurs lastMarket = marketName; @@ -91,12 +106,16 @@ namespace Core.MarketAnalyzer { FileHelper.CleanupFiles(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar, systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours); log.DoLogInfo("Poloniex - Market data cleaned."); - } else { + } + else + { log.DoLogError("Poloniex - Failed to get main market price for " + mainMarket + "."); result = null; } } - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical("Exception while getting data for '" + lastMarket + "': " + ex.Message, ex); result = null; } @@ -104,23 +123,30 @@ namespace Core.MarketAnalyzer { return result; } - public static void CheckFirstSeenDates(Dictionary markets, ref Dictionary marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static void CheckFirstSeenDates(Dictionary markets, ref Dictionary marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) + { log.DoLogInfo("Poloniex - Checking first seen dates for " + markets.Count + " markets. This may take a while..."); int marketsChecked = 0; - foreach (string key in markets.Keys) { + foreach (string key in markets.Keys) + { // Save market info MarketInfo marketInfo = null; - if (marketInfos.ContainsKey(key)) { + if (marketInfos.ContainsKey(key)) + { marketInfo = marketInfos[key]; } - if (marketInfo == null) { + if (marketInfo == null) + { marketInfo = new MarketInfo(); marketInfo.Name = key; marketInfos.Add(key, marketInfo); marketInfo.FirstSeen = Poloniex.GetFirstSeenDate(key, systemConfiguration, log); - } else { - if (marketInfo.FirstSeen == Constants.confMinDate) { + } + else + { + if (marketInfo.FirstSeen == Constants.confMinDate) + { marketInfo.FirstSeen = Poloniex.GetFirstSeenDate(key, systemConfiguration, log); } } @@ -128,13 +154,15 @@ namespace Core.MarketAnalyzer { marketsChecked++; - if ((marketsChecked % 20) == 0) { + if ((marketsChecked % 20) == 0) + { log.DoLogInfo("Poloniex - Yes, I am still checking first seen dates... " + marketsChecked + "/" + markets.Count + " markets done..."); } } } - public static DateTime GetFirstSeenDate(string marketName, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static DateTime GetFirstSeenDate(string marketName, PTMagicConfiguration systemConfiguration, LogHelper log) + { DateTime result = Constants.confMinDate; Int64 startTime = (Int64)Math.Ceiling(DateTime.Now.ToUniversalTime().AddDays(-100).Subtract(Constants.Epoch).TotalSeconds); @@ -143,7 +171,8 @@ namespace Core.MarketAnalyzer { log.DoLogDebug("Poloniex - Getting first seen date for '" + marketName + "'..."); List jsonObject = GetSimpleJsonListFromURL(baseUrl, log); - if (jsonObject.Count > 0) { + if (jsonObject.Count > 0) + { var marketTick = jsonObject[0]; result = Constants.Epoch.AddSeconds((int)marketTick["date"]); @@ -153,19 +182,23 @@ namespace Core.MarketAnalyzer { return result; } - public static List GetMarketTicks(string marketName, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static List GetMarketTicks(string marketName, PTMagicConfiguration systemConfiguration, LogHelper log) + { List result = new List(); - try { + try + { Int64 startTime = (Int64)Math.Ceiling(DateTime.Now.ToUniversalTime().AddHours(-systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours).Subtract(Constants.Epoch).TotalSeconds); string baseUrl = "https://poloniex.com/public?command=returnChartData&period=300&start=" + startTime.ToString() + "&end=9999999999¤cyPair=" + marketName; log.DoLogDebug("Poloniex - Getting ticks for '" + marketName + "'..."); List jsonObject = GetSimpleJsonListFromURL(baseUrl, log); - if (jsonObject.Count > 0) { + if (jsonObject.Count > 0) + { log.DoLogDebug("Poloniex - " + jsonObject.Count.ToString() + " ticks received."); - foreach (var marketTick in jsonObject) { + foreach (var marketTick in jsonObject) + { MarketTick tick = new MarketTick(); tick.Price = (double)marketTick["close"]; @@ -174,17 +207,21 @@ namespace Core.MarketAnalyzer { result.Add(tick); } } - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical(ex.Message, ex); } return result; } - public static void CheckForMarketDataRecreation(string mainMarket, Dictionary markets, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static void CheckForMarketDataRecreation(string mainMarket, Dictionary markets, PTMagicConfiguration systemConfiguration, LogHelper log) + { string poloniexDataDirectoryPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar; - if (!Directory.Exists(poloniexDataDirectoryPath)) { + if (!Directory.Exists(poloniexDataDirectoryPath)) + { Directory.CreateDirectory(poloniexDataDirectoryPath); } @@ -194,39 +231,47 @@ namespace Core.MarketAnalyzer { DateTime latestMarketDataFileDateTime = Constants.confMinDate; List marketFiles = dataDirectory.EnumerateFiles("MarketData*").ToList(); FileInfo latestMarketDataFile = null; - if (marketFiles.Count > 0) { + if (marketFiles.Count > 0) + { latestMarketDataFile = marketFiles.OrderByDescending(mdf => mdf.LastWriteTimeUtc).First(); latestMarketDataFileDateTime = latestMarketDataFile.LastWriteTimeUtc; } - if (latestMarketDataFileDateTime < DateTime.Now.ToUniversalTime().AddMinutes(-(systemConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 3))) { + if (latestMarketDataFileDateTime < DateTime.Now.ToUniversalTime().AddMinutes(-(systemConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 3))) + { int lastMarketDataAgeInSeconds = (int)Math.Ceiling(DateTime.Now.ToUniversalTime().Subtract(latestMarketDataFileDateTime).TotalSeconds); // Go back in time and create market data DateTime startDateTime = DateTime.Now.ToUniversalTime(); DateTime endDateTime = DateTime.Now.ToUniversalTime().AddHours(-systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours); - if (latestMarketDataFileDateTime != Constants.confMinDate && latestMarketDataFileDateTime > endDateTime) { + if (latestMarketDataFileDateTime != Constants.confMinDate && latestMarketDataFileDateTime > endDateTime) + { // Existing market files too old => Recreate market data for configured timeframe log.DoLogInfo("Poloniex - Recreating market data for " + markets.Count + " markets over " + SystemHelper.GetProperDurationTime(lastMarketDataAgeInSeconds) + ". This may take a while..."); endDateTime = latestMarketDataFileDateTime; - } else { + } + else + { // No existing market files found => Recreate market data for configured timeframe log.DoLogInfo("Poloniex - Recreating market data for " + markets.Count + " markets over " + systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours + " hours. This may take a while..."); } // Get Ticks for main market List mainMarketTicks = new List(); - if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)) { + if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)) + { mainMarketTicks = Poloniex.GetMarketTicks("USDT_" + mainMarket, systemConfiguration, log); } // Get Ticks for all markets log.DoLogDebug("Poloniex - Getting ticks for '" + markets.Count + "' markets"); Dictionary> marketTicks = new Dictionary>(); - foreach (string key in markets.Keys) { + foreach (string key in markets.Keys) + { marketTicks.Add(key, Poloniex.GetMarketTicks(key, systemConfiguration, log)); - if ((marketTicks.Count % 10) == 0) { + if ((marketTicks.Count % 10) == 0) + { log.DoLogInfo("Poloniex - No worries, I am still alive... " + marketTicks.Count + "/" + markets.Count + " markets done..."); } } @@ -238,24 +283,30 @@ namespace Core.MarketAnalyzer { // Go back in time and create market data int totalTicks = (int)Math.Ceiling(startDateTime.Subtract(endDateTime).TotalMinutes); int completedTicks = 0; - if (marketTicks.Count > 0) { - for (DateTime tickTime = startDateTime; tickTime >= endDateTime; tickTime = tickTime.AddMinutes(-5)) { + if (marketTicks.Count > 0) + { + for (DateTime tickTime = startDateTime; tickTime >= endDateTime; tickTime = tickTime.AddMinutes(-5)) + { completedTicks++; double mainCurrencyPrice = 1; - if (mainMarketTicks.Count > 0) { + if (mainMarketTicks.Count > 0) + { List mainCurrencyTickRange = mainMarketTicks.FindAll(t => t.Time <= tickTime); - if (mainCurrencyTickRange.Count > 0) { + 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) { + foreach (string key in markets.Keys) + { List tickRange = marketTicks[key].FindAll(t => t.Time <= tickTime); - if (tickRange.Count > 0) { + if (tickRange.Count > 0) + { MarketTick marketTick = tickRange.OrderByDescending(t => t.Time).First(); Market market = new Market(); @@ -277,7 +328,8 @@ namespace Core.MarketAnalyzer { log.DoLogDebug("Poloniex - Market data saved for tick " + tickTime.ToLocalTime().ToString() + " - MainCurrencyPrice=" + mainCurrencyPrice.ToString("#,#0.00") + " USD."); - if ((completedTicks % 100) == 0) { + if ((completedTicks % 100) == 0) + { log.DoLogInfo("Poloniex - Our magicbots are still at work, hang on... " + completedTicks + "/" + totalTicks + " ticks done..."); } } diff --git a/Core/ProfitTrailer/SettingsAPI.cs b/Core/ProfitTrailer/SettingsAPI.cs index ed3f33c..f26b428 100644 --- a/Core/ProfitTrailer/SettingsAPI.cs +++ b/Core/ProfitTrailer/SettingsAPI.cs @@ -13,12 +13,16 @@ using Core.Helper; using Core.Main.DataObjects.PTMagicData; using Newtonsoft.Json; -namespace Core.ProfitTrailer { - public static class SettingsAPI { - public static List GetPropertyLinesFromAPI(string ptFileName, PTMagicConfiguration systemConfiguration, LogHelper log) { +namespace Core.ProfitTrailer +{ + public static class SettingsAPI + { + public static List GetPropertyLinesFromAPI(string ptFileName, PTMagicConfiguration systemConfiguration, LogHelper log) + { List result = null; - try { + try + { ServicePointManager.Expect100Continue = true; ServicePointManager.DefaultConnectionLimit = 9999; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; @@ -33,7 +37,8 @@ namespace Core.ProfitTrailer { byte[] formData = Encoding.ASCII.GetBytes(query); httpWebRequest.ContentLength = formData.Length; - using (Stream stream = httpWebRequest.GetRequestStream()) { + using (Stream stream = httpWebRequest.GetRequestStream()) + { stream.Write(formData, 0, formData.Length); } @@ -48,33 +53,44 @@ namespace Core.ProfitTrailer { //} HttpWebResponse httpResponse = (HttpWebResponse)httpWebRequest.GetResponse(); - using (StreamReader streamReader = new StreamReader(httpResponse.GetResponseStream())) { + using (StreamReader streamReader = new StreamReader(httpResponse.GetResponseStream())) + { string jsonResult = streamReader.ReadToEnd(); result = JsonConvert.DeserializeObject>(jsonResult); } - } catch (WebException ex) { + } + catch (WebException ex) + { // Manual error handling as PT doesn't seem to provide a proper error response... - if (ex.Message.IndexOf("401") > -1) { + 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 { + } + else + { log.DoLogCritical("Loading " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': " + ex.Message, ex); } - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical("Loading " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': " + ex.Message, ex); } return result; } - public static void SendPropertyLinesToAPI(string ptFileName, List lines, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static void SendPropertyLinesToAPI(string ptFileName, List lines, PTMagicConfiguration systemConfiguration, LogHelper log) + { int retryCount = 0; int maxRetries = 3; bool transferCompleted = false; bool transferCanceled = false; - while (!transferCompleted && !transferCanceled) { - try { + while (!transferCompleted && !transferCanceled) + { + try + { ServicePointManager.Expect100Continue = true; ServicePointManager.DefaultConnectionLimit = 9999; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; @@ -94,7 +110,8 @@ namespace Core.ProfitTrailer { byte[] formData = Encoding.ASCII.GetBytes(query); httpWebRequest.ContentLength = formData.Length; - using (Stream stream = httpWebRequest.GetRequestStream()) { + using (Stream stream = httpWebRequest.GetRequestStream()) + { stream.Write(formData, 0, formData.Length); } log.DoLogDebug("Built POST request for " + ptFileName + ".properties."); @@ -117,34 +134,51 @@ namespace Core.ProfitTrailer { log.DoLogDebug(ptFileName + ".properties response object closed."); transferCompleted = true; - } catch (WebException ex) { + } + catch (WebException ex) + { // Manual error handling as PT doesn't seem to provide a proper error response... - if (ex.Message.IndexOf("401") > -1) { + if (ex.Message.IndexOf("401") > -1) + { log.DoLogError("Saving " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Unauthorized! The specified Profit Trailer license key '" + systemConfiguration.GetProfitTrailerLicenseKeyMasked() + "' is invalid!"); transferCanceled = true; - } else if (ex.Message.IndexOf("timed out") > -1) { + } + else if (ex.Message.IndexOf("timed out") > -1) + { // Handle timeout seperately retryCount++; - if (retryCount <= maxRetries) { + if (retryCount <= maxRetries) + { log.DoLogError("Saving " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Timeout! Starting retry number " + retryCount + "/" + maxRetries.ToString() + "!"); - } else { + } + else + { transferCanceled = true; log.DoLogError("Saving " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Timeout! Canceling transfer after " + maxRetries.ToString() + " failed retries."); } - } else { + } + else + { log.DoLogCritical("Saving " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': " + ex.Message, ex); transferCanceled = true; } - } catch (TimeoutException ex) { + } + catch (TimeoutException ex) + { retryCount++; - if (retryCount <= maxRetries) { + if (retryCount <= maxRetries) + { log.DoLogError("Saving " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Timeout (" + ex.Message + ")! Starting retry number " + retryCount + "/" + maxRetries.ToString() + "!"); - } else { + } + else + { transferCanceled = true; log.DoLogError("Saving " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Timeout (" + ex.Message + ")! Canceling transfer after " + maxRetries.ToString() + " failed retries."); } - } catch (Exception ex) { + } + catch (Exception ex) + { log.DoLogCritical("Saving " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': " + ex.Message, ex); transferCanceled = true; } diff --git a/Core/ProfitTrailer/SettingsFiles.cs b/Core/ProfitTrailer/SettingsFiles.cs index ba0e751..b3b579a 100644 --- a/Core/ProfitTrailer/SettingsFiles.cs +++ b/Core/ProfitTrailer/SettingsFiles.cs @@ -13,14 +13,18 @@ using Core.Helper; using Core.Main.DataObjects.PTMagicData; using Newtonsoft.Json; -namespace Core.ProfitTrailer { - public static class SettingsFiles { - public static string GetActiveSetting(PTMagicConfiguration systemConfiguration, string pairsFileName, string dcaFileName, string indicatorsFileName, LogHelper log) { +namespace Core.ProfitTrailer +{ + public static class SettingsFiles + { + public static string GetActiveSetting(PTMagicConfiguration systemConfiguration, string pairsFileName, string dcaFileName, string indicatorsFileName, LogHelper log) + { string pairsPropertiesPath = systemConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + pairsFileName; string result = SettingsFiles.GetActiveSettingFromFile(pairsPropertiesPath, systemConfiguration, log); - if (result.Equals("")) { + if (result.Equals("")) + { SettingsFiles.WriteHeaderLines(pairsPropertiesPath, "Default", systemConfiguration); string dcaPropertiesPath = systemConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + dcaFileName; @@ -34,7 +38,8 @@ namespace Core.ProfitTrailer { return result; } - public static void WriteHeaderLines(string filePath, string settingName, PTMagicConfiguration systemConfiguration) { + public static void WriteHeaderLines(string filePath, string settingName, PTMagicConfiguration systemConfiguration) + { // Writing Header lines List lines = File.ReadAllLines(filePath).ToList(); lines.Insert(0, ""); @@ -47,15 +52,20 @@ namespace Core.ProfitTrailer { if (!systemConfiguration.GeneralSettings.Application.TestMode) File.WriteAllLines(filePath, lines); } - public static string GetActiveSettingFromFile(string filePath, PTMagicConfiguration systemConfiguration, LogHelper log) { + public static string GetActiveSettingFromFile(string filePath, PTMagicConfiguration systemConfiguration, LogHelper log) + { string result = ""; - if (File.Exists(filePath)) { + if (File.Exists(filePath)) + { StreamReader sr = new StreamReader(filePath); - try { + try + { string line = sr.ReadLine(); - while (line != null) { - if (line.IndexOf("PTMagic_ActiveSetting", StringComparison.InvariantCultureIgnoreCase) > -1) { + while (line != null) + { + if (line.IndexOf("PTMagic_ActiveSetting", StringComparison.InvariantCultureIgnoreCase) > -1) + { result = line.Replace("PTMagic_ActiveSetting", "", StringComparison.InvariantCultureIgnoreCase); result = result.Replace("#", ""); result = result.Replace("=", "").Trim(); @@ -64,7 +74,10 @@ namespace Core.ProfitTrailer { } line = sr.ReadLine(); } - } catch { } finally { + } + catch { } + finally + { sr.Close(); } } @@ -73,31 +86,39 @@ namespace Core.ProfitTrailer { } - public static List GetPresetFileLinesAsList(string settingName, string fileName, PTMagicConfiguration systemConfiguration) { + public static List GetPresetFileLinesAsList(string settingName, string fileName, PTMagicConfiguration systemConfiguration) + { return SettingsFiles.GetPresetFileLinesAsList(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar, settingName, fileName, systemConfiguration); } - public static List GetPresetFileLinesAsList(string baseFolderPath, string settingName, string fileName, PTMagicConfiguration systemConfiguration) { + public static List GetPresetFileLinesAsList(string baseFolderPath, string settingName, string fileName, PTMagicConfiguration systemConfiguration) + { fileName = fileName.Replace(".PROPERTIES", ".properties"); string settingPropertiesPath = baseFolderPath + Constants.PTMagicPathPresets + Path.DirectorySeparatorChar + settingName + Path.DirectorySeparatorChar + fileName; List result = new List(); - if (File.Exists(settingPropertiesPath)) { + if (File.Exists(settingPropertiesPath)) + { result = File.ReadAllLines(settingPropertiesPath).ToList(); } return result; } - public static bool CheckPresets(PTMagicConfiguration systemConfiguration, LogHelper log, bool forceCheck) { - if (!forceCheck) { + public static bool CheckPresets(PTMagicConfiguration systemConfiguration, LogHelper log, bool forceCheck) + { + if (!forceCheck) + { // If the check is not enforced, check for file changes string[] presetFilePaths = Directory.GetFiles(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathPresets, "*.*", SearchOption.AllDirectories); - foreach (string presetFilePath in presetFilePaths) { - if (presetFilePath.IndexOf(".properties", StringComparison.InvariantCultureIgnoreCase) > -1) { + foreach (string presetFilePath in presetFilePaths) + { + if (presetFilePath.IndexOf(".properties", StringComparison.InvariantCultureIgnoreCase) > -1) + { FileInfo presetFile = new FileInfo(presetFilePath); - if (presetFile.LastWriteTime > DateTime.Now.AddMinutes(-systemConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes).AddSeconds(2)) { + if (presetFile.LastWriteTime > DateTime.Now.AddMinutes(-systemConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes).AddSeconds(2)) + { // File has changed recently, force preparation check log.DoLogInfo("Preset files changed, enforcing preparation check..."); @@ -109,20 +130,28 @@ namespace Core.ProfitTrailer { } - if (forceCheck) { + if (forceCheck) + { log.DoLogInfo("Checking automated settings for presets..."); - foreach (GlobalSetting setting in systemConfiguration.AnalyzerSettings.GlobalSettings) { - if (setting.PairsProperties != null) { - if (setting.PairsProperties.ContainsKey("File")) { + foreach (GlobalSetting setting in systemConfiguration.AnalyzerSettings.GlobalSettings) + { + if (setting.PairsProperties != null) + { + if (setting.PairsProperties.ContainsKey("File")) + { setting.PairsProperties["File"] = SystemHelper.PropertyToString(setting.PairsProperties["File"]).Replace(".PROPERTIES", ".properties"); // Check preset PAIRS.PROPERTIES for header lines string settingPairsPropertiesPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathPresets + Path.DirectorySeparatorChar + setting.SettingName + Path.DirectorySeparatorChar + setting.PairsProperties["File"]; string headerPairsSetting = SettingsFiles.GetActiveSettingFromFile(settingPairsPropertiesPath, systemConfiguration, log); - if (headerPairsSetting.Equals("")) { - if (File.Exists(settingPairsPropertiesPath)) { + if (headerPairsSetting.Equals("")) + { + if (File.Exists(settingPairsPropertiesPath)) + { SettingsFiles.WriteHeaderLines(settingPairsPropertiesPath, setting.SettingName, systemConfiguration); - } else { + } + else + { Exception ex = new Exception("Not able to find preset file " + SystemHelper.PropertyToString(setting.PairsProperties["File"]) + " for '" + setting.SettingName + "'"); log.DoLogCritical("Not able to find preset file " + SystemHelper.PropertyToString(setting.PairsProperties["File"]) + " for '" + setting.SettingName + "'", ex); throw ex; @@ -134,17 +163,23 @@ namespace Core.ProfitTrailer { } } - if (setting.DCAProperties != null) { - if (setting.DCAProperties.ContainsKey("File")) { + if (setting.DCAProperties != null) + { + if (setting.DCAProperties.ContainsKey("File")) + { setting.DCAProperties["File"] = SystemHelper.PropertyToString(setting.DCAProperties["File"]).Replace(".PROPERTIES", ".properties"); // Check preset DCA.PROPERTIES for header lines string settingDCAPropertiesPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathPresets + Path.DirectorySeparatorChar + setting.SettingName + Path.DirectorySeparatorChar + setting.DCAProperties["File"]; string headerDCASetting = SettingsFiles.GetActiveSettingFromFile(settingDCAPropertiesPath, systemConfiguration, log); - if (headerDCASetting.Equals("")) { - if (File.Exists(settingDCAPropertiesPath)) { + if (headerDCASetting.Equals("")) + { + if (File.Exists(settingDCAPropertiesPath)) + { SettingsFiles.WriteHeaderLines(settingDCAPropertiesPath, setting.SettingName, systemConfiguration); - } else { + } + else + { Exception ex = new Exception("Not able to find preset file " + SystemHelper.PropertyToString(setting.DCAProperties["File"]) + " for '" + setting.SettingName + "'"); log.DoLogCritical("Not able to find preset file " + SystemHelper.PropertyToString(setting.DCAProperties["File"]) + " for '" + setting.SettingName + "'", ex); throw ex; @@ -155,17 +190,23 @@ namespace Core.ProfitTrailer { } } - if (setting.IndicatorsProperties != null) { - if (setting.IndicatorsProperties.ContainsKey("File")) { + if (setting.IndicatorsProperties != null) + { + if (setting.IndicatorsProperties.ContainsKey("File")) + { setting.IndicatorsProperties["File"] = SystemHelper.PropertyToString(setting.IndicatorsProperties["File"]).Replace(".PROPERTIES", ".properties"); // Check preset INDICATORS.PROPERTIES for header lines string settingIndicatorsPropertiesPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathPresets + Path.DirectorySeparatorChar + setting.SettingName + Path.DirectorySeparatorChar + setting.IndicatorsProperties["File"]; string headerIndicatorsSetting = SettingsFiles.GetActiveSettingFromFile(settingIndicatorsPropertiesPath, systemConfiguration, log); - if (headerIndicatorsSetting.Equals("")) { - if (File.Exists(settingIndicatorsPropertiesPath)) { + if (headerIndicatorsSetting.Equals("")) + { + if (File.Exists(settingIndicatorsPropertiesPath)) + { SettingsFiles.WriteHeaderLines(settingIndicatorsPropertiesPath, setting.SettingName, systemConfiguration); - } else { + } + else + { Exception ex = new Exception("Not able to find preset file " + SystemHelper.PropertyToString(setting.IndicatorsProperties["File"]) + " for '" + setting.SettingName + "'"); log.DoLogCritical("Not able to find preset file " + SystemHelper.PropertyToString(setting.IndicatorsProperties["File"]) + " for '" + setting.SettingName + "'", ex); throw ex; diff --git a/Core/ProfitTrailer/SettingsHandler.cs b/Core/ProfitTrailer/SettingsHandler.cs index 40db063..0f3401e 100644 --- a/Core/ProfitTrailer/SettingsHandler.cs +++ b/Core/ProfitTrailer/SettingsHandler.cs @@ -13,13 +13,18 @@ using Core.Helper; using Core.Main.DataObjects.PTMagicData; using Newtonsoft.Json; -namespace Core.ProfitTrailer { - public static class SettingsHandler { - public static string GetMainMarket(PTMagicConfiguration systemConfiguration, List pairsLines, LogHelper log) { +namespace Core.ProfitTrailer +{ + public static class SettingsHandler + { + public static string GetMainMarket(PTMagicConfiguration systemConfiguration, List pairsLines, LogHelper log) + { string result = ""; - foreach (string line in pairsLines) { - if (line.Replace(" ", "").StartsWith("MARKET", StringComparison.InvariantCultureIgnoreCase)) { + foreach (string line in pairsLines) + { + if (line.Replace(" ", "").StartsWith("MARKET", StringComparison.InvariantCultureIgnoreCase)) + { result = line.Replace("MARKET", "", StringComparison.InvariantCultureIgnoreCase); result = result.Replace("#", ""); result = result.Replace("=", "").Trim(); @@ -30,11 +35,14 @@ namespace Core.ProfitTrailer { return result; } - public static string GetMarketPairs(PTMagicConfiguration systemConfiguration, List pairsLines, LogHelper log) { + public static string GetMarketPairs(PTMagicConfiguration systemConfiguration, List pairsLines, LogHelper log) + { string result = ""; - foreach (string line in pairsLines) { - if (line.Replace(" ", "").StartsWith("ALL_enabled_pairs", StringComparison.InvariantCultureIgnoreCase) || line.Replace(" ", "").StartsWith("enabled_pairs", StringComparison.InvariantCultureIgnoreCase)) { + foreach (string line in pairsLines) + { + if (line.Replace(" ", "").StartsWith("ALL_enabled_pairs", StringComparison.InvariantCultureIgnoreCase) || line.Replace(" ", "").StartsWith("enabled_pairs", StringComparison.InvariantCultureIgnoreCase)) + { result = line.Replace("ALL_enabled_pairs", "", StringComparison.InvariantCultureIgnoreCase); result = result.Replace("enabled_pairs", "", StringComparison.InvariantCultureIgnoreCase); result = result.Replace("#", ""); @@ -46,11 +54,14 @@ namespace Core.ProfitTrailer { return result; } - public static string GetActiveSetting(PTMagic ptmagicInstance, ref bool headerLinesAdded) { + public static string GetActiveSetting(PTMagic ptmagicInstance, ref bool headerLinesAdded) + { string result = ""; - foreach (string line in ptmagicInstance.PairsLines) { - if (line.IndexOf("PTMagic_ActiveSetting", StringComparison.InvariantCultureIgnoreCase) > -1) { + foreach (string line in ptmagicInstance.PairsLines) + { + if (line.IndexOf("PTMagic_ActiveSetting", StringComparison.InvariantCultureIgnoreCase) > -1) + { result = line.Replace("PTMagic_ActiveSetting", "", StringComparison.InvariantCultureIgnoreCase); result = result.Replace("#", ""); result = result.Replace("=", "").Trim(); @@ -59,7 +70,8 @@ namespace Core.ProfitTrailer { } } - if (result.Equals("")) { + if (result.Equals("")) + { SettingsHandler.WriteHeaderLines("Pairs", ptmagicInstance); SettingsHandler.WriteHeaderLines("DCA", ptmagicInstance); SettingsHandler.WriteHeaderLines("Indicators", ptmagicInstance); @@ -69,7 +81,8 @@ namespace Core.ProfitTrailer { return result; } - public static void WriteHeaderLines(string fileType, PTMagic ptmagicInstance) { + public static void WriteHeaderLines(string fileType, PTMagic ptmagicInstance) + { List fileLines = (List)ptmagicInstance.GetType().GetProperty(fileType + "Lines").GetValue(ptmagicInstance, null); // Writing Header lines @@ -83,16 +96,23 @@ namespace Core.ProfitTrailer { ptmagicInstance.GetType().GetProperty(fileType + "Lines").SetValue(ptmagicInstance, fileLines); } - public static Dictionary GetPropertiesAsDictionary(List propertyLines) { + public static Dictionary GetPropertiesAsDictionary(List propertyLines) + { Dictionary result = new Dictionary(); - foreach (string line in propertyLines) { - if (!line.StartsWith("#", StringComparison.InvariantCultureIgnoreCase)) { + foreach (string line in propertyLines) + { + if (!line.StartsWith("#", StringComparison.InvariantCultureIgnoreCase)) + { string[] lineContentArray = line.Split("="); - if (lineContentArray.Length == 2) { - if (!result.ContainsKey(lineContentArray[0].Trim())) { + if (lineContentArray.Length == 2) + { + if (!result.ContainsKey(lineContentArray[0].Trim())) + { result.Add(lineContentArray[0].Trim(), lineContentArray[1].Trim()); - } else { + } + else + { result[lineContentArray[0].Trim()] = lineContentArray[1].Trim(); } } @@ -102,41 +122,51 @@ namespace Core.ProfitTrailer { return result; } - public static string GetCurrentPropertyValue(Dictionary properties, string propertyKey, string fallbackPropertyKey) { + public static string GetCurrentPropertyValue(Dictionary properties, string propertyKey, string fallbackPropertyKey) + { string result = ""; - if (properties.ContainsKey(propertyKey)) { + if (properties.ContainsKey(propertyKey)) + { result = properties[propertyKey]; - } else if (!fallbackPropertyKey.Equals("") && properties.ContainsKey(fallbackPropertyKey)) { + } + else if (!fallbackPropertyKey.Equals("") && properties.ContainsKey(fallbackPropertyKey)) + { result = properties[fallbackPropertyKey]; } return result; } - public static void CompileProperties(PTMagic ptmagicInstance, GlobalSetting setting) { + public static void CompileProperties(PTMagic ptmagicInstance, GlobalSetting setting) + { SettingsHandler.BuildPropertyLines("Pairs", ptmagicInstance, setting); SettingsHandler.BuildPropertyLines("DCA", ptmagicInstance, setting); SettingsHandler.BuildPropertyLines("Indicators", ptmagicInstance, setting); } - public static void BuildPropertyLines(string fileType, PTMagic ptmagicInstance, GlobalSetting setting) { + public static void BuildPropertyLines(string fileType, PTMagic ptmagicInstance, GlobalSetting setting) + { List result = new List(); List fileLines = (List)ptmagicInstance.GetType().GetProperty(fileType + "Lines").GetValue(ptmagicInstance, null); Dictionary properties = (Dictionary)setting.GetType().GetProperty(fileType + "Properties").GetValue(setting, null); - if (properties != null) { + if (properties != null) + { // Building Properties - if (!setting.SettingName.Equals(ptmagicInstance.DefaultSettingName, StringComparison.InvariantCultureIgnoreCase) && ptmagicInstance.PTMagicConfiguration.GeneralSettings.Application.AlwaysLoadDefaultBeforeSwitch && !properties.ContainsKey("File")) { + if (!setting.SettingName.Equals(ptmagicInstance.DefaultSettingName, StringComparison.InvariantCultureIgnoreCase) && ptmagicInstance.PTMagicConfiguration.GeneralSettings.Application.AlwaysLoadDefaultBeforeSwitch && !properties.ContainsKey("File")) + { // Load default settings as basis for the switch GlobalSetting defaultSetting = ptmagicInstance.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(a => a.SettingName.Equals(ptmagicInstance.DefaultSettingName, StringComparison.InvariantCultureIgnoreCase)); - if (defaultSetting != null) { + if (defaultSetting != null) + { Dictionary defaultProperties = new Dictionary(); - switch (fileType.ToLower()) { + switch (fileType.ToLower()) + { case "pairs": defaultProperties = defaultSetting.PairsProperties; break; @@ -148,39 +178,53 @@ namespace Core.ProfitTrailer { break; } - if (defaultProperties.ContainsKey("File")) { + if (defaultProperties.ContainsKey("File")) + { fileLines = SettingsFiles.GetPresetFileLinesAsList(defaultSetting.SettingName, defaultProperties["File"].ToString(), ptmagicInstance.PTMagicConfiguration); } } - } else { + } + else + { // Check if settings are configured in a seperate file - if (properties.ContainsKey("File")) { + if (properties.ContainsKey("File")) + { fileLines = SettingsFiles.GetPresetFileLinesAsList(setting.SettingName, properties["File"].ToString(), ptmagicInstance.PTMagicConfiguration); } } - foreach (string line in fileLines) { - if (line.IndexOf("PTMagic_ActiveSetting", StringComparison.InvariantCultureIgnoreCase) > -1) { + foreach (string line in fileLines) + { + if (line.IndexOf("PTMagic_ActiveSetting", StringComparison.InvariantCultureIgnoreCase) > -1) + { // Setting current active setting result.Add("# PTMagic_ActiveSetting = " + setting.SettingName); - } else if (line.IndexOf("PTMagic_LastChanged", StringComparison.InvariantCultureIgnoreCase) > -1) { + } + else if (line.IndexOf("PTMagic_LastChanged", StringComparison.InvariantCultureIgnoreCase) > -1) + { // Setting last change datetime result.Add("# PTMagic_LastChanged = " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString()); - } else if (line.IndexOf("PTMagic_SingleMarketSettings", StringComparison.InvariantCultureIgnoreCase) > -1) { + } + else if (line.IndexOf("PTMagic_SingleMarketSettings", StringComparison.InvariantCultureIgnoreCase) > -1) + { // Single Market Settings will get overwritten every single run => crop the lines break; - } else { + } + else + { // Writing property items int oldResultCount = result.Count; - if (properties != null) { - foreach (string settingProperty in properties.Keys) { + if (properties != null) + { + foreach (string settingProperty in properties.Keys) + { result = SettingsHandler.BuildPropertyLine(result, setting.SettingName, line, properties, settingProperty); } } @@ -192,32 +236,40 @@ namespace Core.ProfitTrailer { ptmagicInstance.GetType().GetProperty(fileType + "Lines").SetValue(ptmagicInstance, result); } - public static List BuildPropertyLine(List result, string settingName, string line, Dictionary properties, string settingProperty) { + public static List BuildPropertyLine(List result, string settingName, string line, Dictionary properties, string settingProperty) + { int valueMode = Constants.ValueModeDefault; string propertyKey = settingProperty; // Check for offset values - if (propertyKey.IndexOf("_OFFSETPERCENT") > -1) { + if (propertyKey.IndexOf("_OFFSETPERCENT") > -1) + { valueMode = Constants.ValueModeOffsetPercent; propertyKey = propertyKey.Replace("_OFFSETPERCENT", ""); - } else if (propertyKey.IndexOf("_OFFSET") > -1) { + } + else if (propertyKey.IndexOf("_OFFSET") > -1) + { valueMode = Constants.ValueModeOffset; propertyKey = propertyKey.Replace("_OFFSET", ""); } - if (line.StartsWith(propertyKey + " ", StringComparison.InvariantCultureIgnoreCase) || line.StartsWith(propertyKey + "=", StringComparison.InvariantCultureIgnoreCase)) { + if (line.StartsWith(propertyKey + " ", StringComparison.InvariantCultureIgnoreCase) || line.StartsWith(propertyKey + "=", StringComparison.InvariantCultureIgnoreCase)) + { string newValueString = SystemHelper.PropertyToString(properties[settingProperty]); - if (newValueString.ToLower().Equals("true") || newValueString.ToLower().Equals("false")) { + if (newValueString.ToLower().Equals("true") || newValueString.ToLower().Equals("false")) + { newValueString = newValueString.ToLower(); } string oldValueString = line.Replace(propertyKey, "").Replace("=", "").Trim(); - switch (valueMode) { + switch (valueMode) + { case Constants.ValueModeOffset: // Offset value by a fixed amount double offsetValue = SystemHelper.TextToDouble(newValueString, 0, "en-US"); - if (offsetValue != 0) { + if (offsetValue != 0) + { double oldValue = SystemHelper.TextToDouble(oldValueString, 0, "en-US"); newValueString = Math.Round((oldValue + offsetValue), 8).ToString(new System.Globalization.CultureInfo("en-US")); } @@ -225,7 +277,8 @@ namespace Core.ProfitTrailer { case Constants.ValueModeOffsetPercent: // Offset value by percentage double offsetValuePercent = SystemHelper.TextToDouble(newValueString, 0, "en-US"); - if (offsetValuePercent != 0) { + if (offsetValuePercent != 0) + { double oldValue = SystemHelper.TextToDouble(oldValueString, 0, "en-US"); if (oldValue < 0) offsetValuePercent = offsetValuePercent * -1; double oldValueOffset = (oldValue * (offsetValuePercent / 100)); @@ -239,12 +292,15 @@ namespace Core.ProfitTrailer { line = propertyKey + " = " + newValueString; string previousLine = result.Last(); - if (previousLine.IndexOf("PTMagic Changed Line", StringComparison.InvariantCultureIgnoreCase) > -1) { + if (previousLine.IndexOf("PTMagic Changed Line", StringComparison.InvariantCultureIgnoreCase) > -1) + { previousLine = "# PTMagic changed line for setting '" + settingName + "' on " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString(); result.RemoveAt(result.Count - 1); result.Add(previousLine); - } else { + } + else + { string editLine = "# PTMagic changed line for setting '" + settingName + "' on " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString(); result.Add(editLine); } @@ -254,8 +310,10 @@ namespace Core.ProfitTrailer { return result; } - public static void CompileSingleMarketProperties(PTMagic ptmagicInstance, Dictionary> matchedTriggers) { - try { + public static void CompileSingleMarketProperties(PTMagic ptmagicInstance, Dictionary> matchedTriggers) + { + try + { List globalPairsLines = new List(); List globalDCALines = new List(); List globalIndicatorsLines = new List(); @@ -264,12 +322,16 @@ namespace Core.ProfitTrailer { List newDCALines = new List(); List newIndicatorsLines = new List(); - foreach (string pairsLine in ptmagicInstance.PairsLines) { - if (pairsLine.IndexOf("PTMagic_SingleMarketSettings", StringComparison.InvariantCultureIgnoreCase) > -1) { + foreach (string pairsLine in ptmagicInstance.PairsLines) + { + if (pairsLine.IndexOf("PTMagic_SingleMarketSettings", StringComparison.InvariantCultureIgnoreCase) > -1) + { // Single Market Settings will get overwritten every single run => crop the lines break; - } else { + } + else + { string globalPairsLine = pairsLine; globalPairsLines.Add(globalPairsLine); @@ -280,12 +342,16 @@ namespace Core.ProfitTrailer { newPairsLines.Add("# ########################################################################"); newPairsLines.Add(""); - foreach (string dcaLine in ptmagicInstance.DCALines) { - if (dcaLine.IndexOf("PTMagic_SingleMarketSettings", StringComparison.InvariantCultureIgnoreCase) > -1) { + foreach (string dcaLine in ptmagicInstance.DCALines) + { + if (dcaLine.IndexOf("PTMagic_SingleMarketSettings", StringComparison.InvariantCultureIgnoreCase) > -1) + { // Single Market Settings will get overwritten every single run => crop the lines break; - } else { + } + else + { string globalDCALine = dcaLine; globalDCALines.Add(globalDCALine); @@ -296,12 +362,16 @@ namespace Core.ProfitTrailer { newDCALines.Add("# ########################################################################"); newDCALines.Add(""); - foreach (string indicatorsLine in ptmagicInstance.IndicatorsLines) { - if (indicatorsLine.IndexOf("PTMagic_SingleMarketSettings", StringComparison.InvariantCultureIgnoreCase) > -1) { + foreach (string indicatorsLine in ptmagicInstance.IndicatorsLines) + { + if (indicatorsLine.IndexOf("PTMagic_SingleMarketSettings", StringComparison.InvariantCultureIgnoreCase) > -1) + { // Single Market Settings will get overwritten every single run => crop the lines break; - } else { + } + else + { string globalIndicatorsLine = indicatorsLine; globalIndicatorsLines.Add(globalIndicatorsLine); @@ -316,35 +386,49 @@ namespace Core.ProfitTrailer { newIndicatorsLines.Add("# ########################################################################"); newIndicatorsLines.Add(""); - foreach (string marketPair in ptmagicInstance.TriggeredSingleMarketSettings.Keys.OrderBy(k => k)) { + foreach (string marketPair in ptmagicInstance.TriggeredSingleMarketSettings.Keys.OrderBy(k => k)) + { Dictionary pairsPropertiesToApply = new Dictionary(); Dictionary dcaPropertiesToApply = new Dictionary(); Dictionary indicatorsPropertiesToApply = new Dictionary(); // Build Properties as a whole list so that a single coin also has only one block with single market settings applied to it - foreach (SingleMarketSetting setting in ptmagicInstance.TriggeredSingleMarketSettings[marketPair]) { + foreach (SingleMarketSetting setting in ptmagicInstance.TriggeredSingleMarketSettings[marketPair]) + { ptmagicInstance.Log.DoLogInfo("Building single market settings '" + setting.SettingName + "' for '" + marketPair + "'..."); - foreach (string settingPairsProperty in setting.PairsProperties.Keys) { - if (!pairsPropertiesToApply.ContainsKey(settingPairsProperty)) { + foreach (string settingPairsProperty in setting.PairsProperties.Keys) + { + if (!pairsPropertiesToApply.ContainsKey(settingPairsProperty)) + { pairsPropertiesToApply.Add(settingPairsProperty, setting.PairsProperties[settingPairsProperty]); - } else { + } + else + { pairsPropertiesToApply[settingPairsProperty] = setting.PairsProperties[settingPairsProperty]; } } - foreach (string settingDCAProperty in setting.DCAProperties.Keys) { - if (!dcaPropertiesToApply.ContainsKey(settingDCAProperty)) { + foreach (string settingDCAProperty in setting.DCAProperties.Keys) + { + if (!dcaPropertiesToApply.ContainsKey(settingDCAProperty)) + { dcaPropertiesToApply.Add(settingDCAProperty, setting.DCAProperties[settingDCAProperty]); - } else { + } + else + { dcaPropertiesToApply[settingDCAProperty] = setting.DCAProperties[settingDCAProperty]; } } - foreach (string settingIndicatorsProperty in setting.IndicatorsProperties.Keys) { - if (!indicatorsPropertiesToApply.ContainsKey(settingIndicatorsProperty)) { + foreach (string settingIndicatorsProperty in setting.IndicatorsProperties.Keys) + { + if (!indicatorsPropertiesToApply.ContainsKey(settingIndicatorsProperty)) + { indicatorsPropertiesToApply.Add(settingIndicatorsProperty, setting.IndicatorsProperties[settingIndicatorsProperty]); - } else { + } + else + { indicatorsPropertiesToApply[settingIndicatorsProperty] = setting.IndicatorsProperties[settingIndicatorsProperty]; } } @@ -365,68 +449,90 @@ namespace Core.ProfitTrailer { ptmagicInstance.PairsLines = globalPairsLines; ptmagicInstance.DCALines = globalDCALines; ptmagicInstance.IndicatorsLines = globalIndicatorsLines; - } catch (Exception ex) { + } + catch (Exception ex) + { ptmagicInstance.Log.DoLogCritical("Critical error while writing settings!", ex); throw (ex); } } - public static List BuildPropertyLinesForSingleMarketSetting(int ptMajorVersion, string mainMarket, string marketPair, List appliedSettings, Dictionary properties, Dictionary> matchedTriggers, Dictionary fullProperties, List newPropertyLines, PTMagicConfiguration systemConfiguration, LogHelper log) { - if (properties.Keys.Count > 0) { + public static List BuildPropertyLinesForSingleMarketSetting(int ptMajorVersion, string mainMarket, string marketPair, List appliedSettings, Dictionary properties, Dictionary> matchedTriggers, Dictionary fullProperties, List newPropertyLines, PTMagicConfiguration systemConfiguration, LogHelper log) + { + if (properties.Keys.Count > 0) + { string appliedSettingsStringList = ""; - foreach (SingleMarketSetting sms in appliedSettings) { + foreach (SingleMarketSetting sms in appliedSettings) + { if (!appliedSettingsStringList.Equals("")) appliedSettingsStringList += ", "; appliedSettingsStringList += sms.SettingName; } newPropertyLines.Add("# " + marketPair + " - Current active settings: " + appliedSettingsStringList); newPropertyLines.Add("# Matching triggers:"); - foreach (string matchingTrigger in matchedTriggers[marketPair]) { + foreach (string matchingTrigger in matchedTriggers[marketPair]) + { newPropertyLines.Add("# " + matchingTrigger); } - foreach (string settingProperty in properties.Keys) { + foreach (string settingProperty in properties.Keys) + { int valueMode = Constants.ValueModeDefault; string propertyKey = settingProperty; // Check for offset values - if (propertyKey.IndexOf("_OFFSETPERCENT") > -1) { + if (propertyKey.IndexOf("_OFFSETPERCENT") > -1) + { valueMode = Constants.ValueModeOffsetPercent; propertyKey = propertyKey.Replace("_OFFSETPERCENT", ""); - } else if (propertyKey.IndexOf("_OFFSET") > -1) { + } + else if (propertyKey.IndexOf("_OFFSET") > -1) + { valueMode = Constants.ValueModeOffset; propertyKey = propertyKey.Replace("_OFFSET", ""); } string newValueString = SystemHelper.PropertyToString(properties[settingProperty]); - if (newValueString.ToLower().Equals("true") || newValueString.ToLower().Equals("false")) { + if (newValueString.ToLower().Equals("true") || newValueString.ToLower().Equals("false")) + { newValueString = newValueString.ToLower(); } string propertyMarketName = marketPair; - if (ptMajorVersion > 1) { + if (ptMajorVersion > 1) + { // Adjust market pair name for PT 2.0 and above propertyMarketName = propertyMarketName.Replace(mainMarket, "").Replace("_", "").Replace("-", ""); } string propertyKeyString = ""; - if (propertyKey.StartsWith("ALL", StringComparison.InvariantCultureIgnoreCase)) { + if (propertyKey.StartsWith("ALL", StringComparison.InvariantCultureIgnoreCase)) + { propertyKeyString = propertyKey.Replace("ALL", propertyMarketName, StringComparison.InvariantCultureIgnoreCase); - } else if (propertyKey.StartsWith("DEFAULT", StringComparison.InvariantCultureIgnoreCase)) { + } + else if (propertyKey.StartsWith("DEFAULT", StringComparison.InvariantCultureIgnoreCase)) + { propertyKeyString = propertyKey.Replace("DEFAULT", propertyMarketName, StringComparison.InvariantCultureIgnoreCase); - } else { - if (propertyKey.StartsWith("_", StringComparison.InvariantCultureIgnoreCase)) { + } + else + { + if (propertyKey.StartsWith("_", StringComparison.InvariantCultureIgnoreCase)) + { propertyKeyString = propertyMarketName + propertyKey; - } else { + } + else + { propertyKeyString = propertyMarketName + "_" + propertyKey; } } - switch (valueMode) { + switch (valueMode) + { case Constants.ValueModeOffset: // Offset value by a fixed amount double offsetValue = SystemHelper.TextToDouble(newValueString, 0, "en-US"); - if (offsetValue != 0) { + if (offsetValue != 0) + { double oldValue = SystemHelper.TextToDouble(SettingsHandler.GetCurrentPropertyValue(fullProperties, propertyKey, propertyKey.Replace("ALL_", "DEFAULT_")), 0, "en-US"); newValueString = Math.Round((oldValue + offsetValue), 8).ToString(new System.Globalization.CultureInfo("en-US")); } @@ -434,7 +540,8 @@ namespace Core.ProfitTrailer { case Constants.ValueModeOffsetPercent: // Offset value by percentage double offsetValuePercent = SystemHelper.TextToDouble(newValueString, 0, "en-US"); - if (offsetValuePercent != 0) { + if (offsetValuePercent != 0) + { double oldValue = SystemHelper.TextToDouble(SettingsHandler.GetCurrentPropertyValue(fullProperties, propertyKey, propertyKey.Replace("ALL_", "DEFAULT_")), 0, "en-US"); if (oldValue < 0) offsetValuePercent = offsetValuePercent * -1; double oldValueOffset = (oldValue * (offsetValuePercent / 100)); @@ -453,21 +560,27 @@ namespace Core.ProfitTrailer { return newPropertyLines; } - public static bool RemoveSingleMarketSettings(PTMagic ptmagicInstance) { + public static bool RemoveSingleMarketSettings(PTMagic ptmagicInstance) + { bool result = false; - try { + try + { List cleanedUpPairsLines = new List(); List cleanedUpDCALines = new List(); List cleanedUpIndicatorsLines = new List(); bool removedPairsSingleMarketSettings = false; - foreach (string pairsLine in ptmagicInstance.PairsLines) { - if (pairsLine.IndexOf("PTMagic_SingleMarketSettings", StringComparison.InvariantCultureIgnoreCase) > -1) { + foreach (string pairsLine in ptmagicInstance.PairsLines) + { + if (pairsLine.IndexOf("PTMagic_SingleMarketSettings", StringComparison.InvariantCultureIgnoreCase) > -1) + { // Single Market Settings will get overwritten every single run => crop the lines removedPairsSingleMarketSettings = true; break; - } else { + } + else + { string newPairsLine = pairsLine; cleanedUpPairsLines.Add(newPairsLine); @@ -475,13 +588,17 @@ namespace Core.ProfitTrailer { } bool removedDCASingleMarketSettings = false; - foreach (string dcaLine in ptmagicInstance.DCALines) { - if (dcaLine.IndexOf("PTMagic_SingleMarketSettings", StringComparison.InvariantCultureIgnoreCase) > -1) { + foreach (string dcaLine in ptmagicInstance.DCALines) + { + if (dcaLine.IndexOf("PTMagic_SingleMarketSettings", StringComparison.InvariantCultureIgnoreCase) > -1) + { // Single Market Settings will get overwritten every single run => crop the lines removedDCASingleMarketSettings = true; break; - } else { + } + else + { string newDCALine = dcaLine; cleanedUpDCALines.Add(newDCALine); @@ -489,13 +606,17 @@ namespace Core.ProfitTrailer { } bool removedIndicatorsSingleMarketSettings = false; - foreach (string indicatorsLine in ptmagicInstance.IndicatorsLines) { - if (indicatorsLine.IndexOf("PTMagic_SingleMarketSettings", StringComparison.InvariantCultureIgnoreCase) > -1) { + foreach (string indicatorsLine in ptmagicInstance.IndicatorsLines) + { + if (indicatorsLine.IndexOf("PTMagic_SingleMarketSettings", StringComparison.InvariantCultureIgnoreCase) > -1) + { // Single Market Settings will get overwritten every single run => crop the lines removedIndicatorsSingleMarketSettings = true; break; - } else { + } + else + { string newIndicatorsLine = indicatorsLine; cleanedUpIndicatorsLines.Add(newIndicatorsLine); @@ -507,13 +628,15 @@ namespace Core.ProfitTrailer { ptmagicInstance.IndicatorsLines = cleanedUpIndicatorsLines; result = removedPairsSingleMarketSettings && removedDCASingleMarketSettings && removedIndicatorsSingleMarketSettings; - } catch (Exception ex) { + } + catch (Exception ex) + { ptmagicInstance.Log.DoLogCritical("Critical error while writing settings!", ex); } return result; } - + } } diff --git a/Core/ProfitTrailer/StrategyHelper.cs b/Core/ProfitTrailer/StrategyHelper.cs index 2c1d5d5..c3859b2 100644 --- a/Core/ProfitTrailer/StrategyHelper.cs +++ b/Core/ProfitTrailer/StrategyHelper.cs @@ -13,12 +13,16 @@ using Core.Helper; using Core.Main.DataObjects.PTMagicData; using Newtonsoft.Json; -namespace Core.ProfitTrailer { - public static class StrategyHelper { - public static string GetStrategyShortcut(string strategyName, bool onlyValidStrategies) { +namespace Core.ProfitTrailer +{ + public static class StrategyHelper + { + public static string GetStrategyShortcut(string strategyName, bool onlyValidStrategies) + { string result = strategyName; - switch (strategyName.ToLower()) { + switch (strategyName.ToLower()) + { case "lowbb": result = "LBB"; break; @@ -122,8 +126,10 @@ namespace Core.ProfitTrailer { break; } - if (onlyValidStrategies) { - if (strategyName.IndexOf("SOM") > -1 || strategyName.IndexOf("MAX") > -1 || strategyName.IndexOf("MIN") > -1 || strategyName.IndexOf("PRICE") > -1 || strategyName.IndexOf("BLACK") > -1 || strategyName.IndexOf("INSUFFICIENT") > -1 || strategyName.IndexOf("COST") > -1 || strategyName.IndexOf("TIMEOUT") > -1) { + if (onlyValidStrategies) + { + if (strategyName.IndexOf("SOM") > -1 || strategyName.IndexOf("MAX") > -1 || strategyName.IndexOf("MIN") > -1 || strategyName.IndexOf("PRICE") > -1 || strategyName.IndexOf("BLACK") > -1 || strategyName.IndexOf("INSUFFICIENT") > -1 || strategyName.IndexOf("COST") > -1 || strategyName.IndexOf("TIMEOUT") > -1) + { result = ""; } } @@ -131,15 +137,19 @@ namespace Core.ProfitTrailer { return result; } - public static bool IsValidStrategy(string strategyName) { + public static bool IsValidStrategy(string strategyName) + { return StrategyHelper.IsValidStrategy(strategyName, false); } - public static bool IsValidStrategy(string strategyName, bool checkForAnyInvalid) { + public static bool IsValidStrategy(string strategyName, bool checkForAnyInvalid) + { bool result = false; - if (!checkForAnyInvalid) { - switch (strategyName.ToLower()) { + if (!checkForAnyInvalid) + { + switch (strategyName.ToLower()) + { case "lowbb": case "highbb": case "gain": @@ -165,7 +175,9 @@ namespace Core.ProfitTrailer { default: break; } - } else { + } + else + { if (strategyName.IndexOf("max", StringComparison.InvariantCultureIgnoreCase) == -1 && strategyName.IndexOf("min", StringComparison.InvariantCultureIgnoreCase) == -1 && strategyName.IndexOf("som", StringComparison.InvariantCultureIgnoreCase) == -1 @@ -175,7 +187,8 @@ namespace Core.ProfitTrailer { && strategyName.IndexOf("insufficient", StringComparison.InvariantCultureIgnoreCase) == -1 && strategyName.IndexOf("timeout", StringComparison.InvariantCultureIgnoreCase) == -1 && strategyName.IndexOf("spread", StringComparison.InvariantCultureIgnoreCase) == -1 - && strategyName.IndexOf("pairs", StringComparison.InvariantCultureIgnoreCase) == -1) { + && strategyName.IndexOf("pairs", StringComparison.InvariantCultureIgnoreCase) == -1) + { result = true; } } @@ -183,10 +196,12 @@ namespace Core.ProfitTrailer { return result; } - public static int GetStrategyValueDecimals(string strategyName) { + public static int GetStrategyValueDecimals(string strategyName) + { int result = 0; - switch (strategyName.ToLower()) { + switch (strategyName.ToLower()) + { case "lowbb": case "highbb": result = 8; @@ -220,34 +235,52 @@ namespace Core.ProfitTrailer { return result; } - public static string GetStrategyText(Summary summary, List strategies, string strategyText, bool isTrue, bool isTrailingBuyActive) { - if (strategies.Count > 0) { - foreach (Strategy strategy in strategies) { + public static string GetStrategyText(Summary summary, List strategies, string strategyText, bool isTrue, bool isTrailingBuyActive) + { + if (strategies.Count > 0) + { + foreach (Strategy strategy in strategies) + { string textClass = (strategy.IsTrue) ? "label-success" : "label-danger"; - if (!StrategyHelper.IsValidStrategy(strategy.Name)) { + if (!StrategyHelper.IsValidStrategy(strategy.Name)) + { strategyText += "" + StrategyHelper.GetStrategyShortcut(strategy.Name, false) + " "; - } else { + } + else + { strategyText += "" + StrategyHelper.GetStrategyShortcut(strategy.Name, false) + " "; } } - if (isTrailingBuyActive) { + if (isTrailingBuyActive) + { strategyText += " "; } - } else { - if (isTrue) { + } + else + { + if (isTrue) + { strategyText = "" + StrategyHelper.GetStrategyShortcut(strategyText, true) + ""; - if (isTrailingBuyActive) { + if (isTrailingBuyActive) + { strategyText += " "; } - } else { - if (StrategyHelper.IsValidStrategy(strategyText)) { + } + else + { + if (StrategyHelper.IsValidStrategy(strategyText)) + { strategyText = "" + StrategyHelper.GetStrategyShortcut(strategyText, true) + ""; - } else if (strategyText.Equals("")) { + } + else if (strategyText.Equals("")) + { strategyText = summary.DCABuyStrategy; strategyText = "" + StrategyHelper.GetStrategyShortcut(strategyText, true) + ""; - } else { + } + else + { strategyText = "" + StrategyHelper.GetStrategyShortcut(strategyText, false) + " "; } } @@ -256,43 +289,63 @@ namespace Core.ProfitTrailer { return strategyText; } - public static string GetCurrentValueText(List strategies, string strategyText, double bbValue, double simpleValue, bool includeShortcut) { + public static string GetCurrentValueText(List strategies, string strategyText, double bbValue, double simpleValue, bool includeShortcut) + { string result = ""; - if (strategies.Count > 0) { - foreach (Strategy strategy in strategies) { - if (StrategyHelper.IsValidStrategy(strategy.Name)) { + if (strategies.Count > 0) + { + foreach (Strategy strategy in strategies) + { + if (StrategyHelper.IsValidStrategy(strategy.Name)) + { if (!result.Equals("")) result += "
"; string decimalFormat = ""; int decimals = StrategyHelper.GetStrategyValueDecimals(strategy.Name); - for (int d = 1; d <= decimals; d++) { + for (int d = 1; d <= decimals; d++) + { decimalFormat += "0"; } - if (includeShortcut) { + if (includeShortcut) + { result += "" + StrategyHelper.GetStrategyShortcut(strategy.Name, true) + " "; } - if (StrategyHelper.GetStrategyShortcut(strategy.Name, true).IndexOf("and", StringComparison.InvariantCultureIgnoreCase) > -1) { + if (StrategyHelper.GetStrategyShortcut(strategy.Name, true).IndexOf("and", StringComparison.InvariantCultureIgnoreCase) > -1) + { result += simpleValue.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")); - } else { - if (decimals == 0) { - if (!SystemHelper.IsInteger(strategy.CurrentValue)) { + } + else + { + if (decimals == 0) + { + if (!SystemHelper.IsInteger(strategy.CurrentValue)) + { result += strategy.CurrentValue.ToString("#,#", new System.Globalization.CultureInfo("en-US")); - } else { + } + else + { result += strategy.CurrentValue.ToString("#,#0", new System.Globalization.CultureInfo("en-US")); } - } else { + } + else + { result += strategy.CurrentValue.ToString("#,#0." + decimalFormat, new System.Globalization.CultureInfo("en-US")); } } } } - } else { - if (StrategyHelper.GetStrategyShortcut(strategyText, true).IndexOf("bb", StringComparison.InvariantCultureIgnoreCase) > -1) { + } + else + { + if (StrategyHelper.GetStrategyShortcut(strategyText, true).IndexOf("bb", StringComparison.InvariantCultureIgnoreCase) > -1) + { result = bbValue.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")); - } else { + } + else + { result = simpleValue.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%"; } } @@ -300,45 +353,67 @@ namespace Core.ProfitTrailer { return result; } - public static string GetTriggerValueText(Summary summary, List strategies, string strategyText, double bbValue, double simpleValue, int buyLevel, bool includeShortcut) { + public static string GetTriggerValueText(Summary summary, List strategies, string strategyText, double bbValue, double simpleValue, int buyLevel, bool includeShortcut) + { string result = ""; - if (strategies.Count > 0) { - foreach (Strategy strategy in strategies) { - if (StrategyHelper.IsValidStrategy(strategy.Name)) { + if (strategies.Count > 0) + { + foreach (Strategy strategy in strategies) + { + if (StrategyHelper.IsValidStrategy(strategy.Name)) + { if (!result.Equals("")) result += "
"; string decimalFormat = ""; int decimals = StrategyHelper.GetStrategyValueDecimals(strategy.Name); - for (int d = 1; d <= decimals; d++) { + for (int d = 1; d <= decimals; d++) + { decimalFormat += "0"; } - if (includeShortcut) { + if (includeShortcut) + { result += "" + StrategyHelper.GetStrategyShortcut(strategy.Name, true) + " "; } - if (StrategyHelper.GetStrategyShortcut(strategy.Name, true).IndexOf("and", StringComparison.InvariantCultureIgnoreCase) > -1) { + if (StrategyHelper.GetStrategyShortcut(strategy.Name, true).IndexOf("and", StringComparison.InvariantCultureIgnoreCase) > -1) + { result += strategy.TriggerValue.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")); - } else { - if (decimals == 0) { - if (!SystemHelper.IsInteger(strategy.EntryValue)) { + } + else + { + if (decimals == 0) + { + if (!SystemHelper.IsInteger(strategy.EntryValue)) + { result += strategy.EntryValue.ToString(new System.Globalization.CultureInfo("en-US")); - } else { + } + else + { result += strategy.EntryValue.ToString("#,#0", new System.Globalization.CultureInfo("en-US")); } - } else { + } + else + { result += strategy.EntryValue.ToString("#,#0." + decimalFormat, new System.Globalization.CultureInfo("en-US")); } } } } - } else { - if (StrategyHelper.GetStrategyShortcut(strategyText, true).IndexOf("bb", StringComparison.InvariantCultureIgnoreCase) > -1) { + } + else + { + if (StrategyHelper.GetStrategyShortcut(strategyText, true).IndexOf("bb", StringComparison.InvariantCultureIgnoreCase) > -1) + { result = bbValue.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")); - } else { - if (simpleValue == Constants.MinTrendChange) { - if (summary.DCATriggers.ContainsKey(buyLevel + 1)) { + } + else + { + if (simpleValue == Constants.MinTrendChange) + { + if (summary.DCATriggers.ContainsKey(buyLevel + 1)) + { simpleValue = summary.DCATriggers[buyLevel + 1]; } } diff --git a/Monitor/Pages/BagAnalyzer.cshtml.cs b/Monitor/Pages/BagAnalyzer.cshtml.cs index a4a6dee..0408e0c 100644 --- a/Monitor/Pages/BagAnalyzer.cshtml.cs +++ b/Monitor/Pages/BagAnalyzer.cshtml.cs @@ -6,17 +6,21 @@ using Core.Main; using Core.Main.DataObjects; using Core.Main.DataObjects.PTMagicData; -namespace Monitor.Pages { - public class BagAnalyzerModel : _Internal.BasePageModelSecure { +namespace Monitor.Pages +{ + public class BagAnalyzerModel : _Internal.BasePageModelSecure + { public ProfitTrailerData PTData = null; - public void OnGet() { + public void OnGet() + { base.Init(); BindData(); } - private void BindData() { + private void BindData() + { PTData = new ProfitTrailerData(PTMagicBasePath, PTMagicConfiguration); } } diff --git a/Monitor/Pages/BuyAnalyzer.cshtml.cs b/Monitor/Pages/BuyAnalyzer.cshtml.cs index dc2676c..929de0b 100644 --- a/Monitor/Pages/BuyAnalyzer.cshtml.cs +++ b/Monitor/Pages/BuyAnalyzer.cshtml.cs @@ -6,17 +6,21 @@ using Core.Main; using Core.Main.DataObjects; using Core.Main.DataObjects.PTMagicData; -namespace Monitor.Pages { - public class BuyAnalyzerModel : _Internal.BasePageModelSecure { +namespace Monitor.Pages +{ + public class BuyAnalyzerModel : _Internal.BasePageModelSecure + { public ProfitTrailerData PTData = null; - public void OnGet() { + public void OnGet() + { base.Init(); BindData(); } - private void BindData() { + private void BindData() + { PTData = new ProfitTrailerData(PTMagicBasePath, PTMagicConfiguration); } } diff --git a/Monitor/Pages/DCACalculator.cshtml.cs b/Monitor/Pages/DCACalculator.cshtml.cs index af7277d..f4133c9 100644 --- a/Monitor/Pages/DCACalculator.cshtml.cs +++ b/Monitor/Pages/DCACalculator.cshtml.cs @@ -4,17 +4,21 @@ using Core.Main; using Core.Main.DataObjects; using Core.Main.DataObjects.PTMagicData; -namespace Monitor.Pages { - public class DCACalculatorModel : _Internal.BasePageModelSecure { +namespace Monitor.Pages +{ + public class DCACalculatorModel : _Internal.BasePageModelSecure + { public ProfitTrailerData PTData = null; - public void OnGet() { + public void OnGet() + { base.Init(); BindData(); } - private void BindData() { + private void BindData() + { PTData = new ProfitTrailerData(PTMagicBasePath, PTMagicConfiguration); } } diff --git a/Monitor/Pages/Error.cshtml.cs b/Monitor/Pages/Error.cshtml.cs index 0207673..448cfde 100644 --- a/Monitor/Pages/Error.cshtml.cs +++ b/Monitor/Pages/Error.cshtml.cs @@ -6,14 +6,17 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Mvc.RazorPages; -namespace Monitor.Pages { - public class ErrorModel : PageModel { +namespace Monitor.Pages +{ + public class ErrorModel : PageModel + { public string RequestId { get; set; } public IExceptionHandlerFeature Exception = null; public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - public void OnGet() { + public void OnGet() + { Exception = HttpContext.Features.Get(); RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; diff --git a/Monitor/Pages/Index.cshtml.cs b/Monitor/Pages/Index.cshtml.cs index 17b8ed7..04830e9 100644 --- a/Monitor/Pages/Index.cshtml.cs +++ b/Monitor/Pages/Index.cshtml.cs @@ -1,7 +1,10 @@ -namespace Monitor.Pages { - public class IndexModel : _Internal.BasePageModelSecure { +namespace Monitor.Pages +{ + public class IndexModel : _Internal.BasePageModelSecure + { - public void OnGet() { + public void OnGet() + { base.Init(); } } diff --git a/Monitor/Pages/Login.cshtml.cs b/Monitor/Pages/Login.cshtml.cs index 19cccf9..1c1f972 100644 --- a/Monitor/Pages/Login.cshtml.cs +++ b/Monitor/Pages/Login.cshtml.cs @@ -4,29 +4,37 @@ using Microsoft.AspNetCore.Mvc; using Core.Main; using Core.Helper; -namespace Monitor.Pages { - public class LoginModel : _Internal.BasePageModel { +namespace Monitor.Pages +{ + public class LoginModel : _Internal.BasePageModel + { public string CurrentPassword = ""; - public void OnGet() { + public void OnGet() + { base.PreInit(); CurrentPassword = PTMagicConfiguration.SecureSettings.MonitorPassword; - if (CurrentPassword.Equals("")) { + if (CurrentPassword.Equals("")) + { Response.Redirect(PTMagicConfiguration.GeneralSettings.Monitor.RootUrl + "SetupPassword"); } } - public void OnPost(string password, string cbRememberMe) { + public void OnPost(string password, string cbRememberMe) + { base.PreInit(); string encryptedPassword = EncryptionHelper.Encrypt(password); - if (encryptedPassword.Equals(PTMagicConfiguration.SecureSettings.MonitorPassword)) { + if (encryptedPassword.Equals(PTMagicConfiguration.SecureSettings.MonitorPassword)) + { HttpContext.Session.SetString("LoggedIn" + PTMagicConfiguration.GeneralSettings.Monitor.Port.ToString(), DateTime.Now.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'")); - if (cbRememberMe != null) { - if (cbRememberMe.Equals("on", StringComparison.InvariantCultureIgnoreCase)) { + if (cbRememberMe != null) + { + if (cbRememberMe.Equals("on", StringComparison.InvariantCultureIgnoreCase)) + { CookieOptions cookieOption = new CookieOptions(); cookieOption.Expires = DateTime.Now.AddYears(1); diff --git a/Monitor/Pages/ManageSMS.cshtml.cs b/Monitor/Pages/ManageSMS.cshtml.cs index a3f13d5..f18a520 100644 --- a/Monitor/Pages/ManageSMS.cshtml.cs +++ b/Monitor/Pages/ManageSMS.cshtml.cs @@ -7,40 +7,53 @@ using Core.Main; using Core.Main.DataObjects.PTMagicData; using Newtonsoft.Json; -namespace Monitor.Pages { - public class ManageSMSModel : _Internal.BasePageModelSecure { +namespace Monitor.Pages +{ + public class ManageSMSModel : _Internal.BasePageModelSecure + { public List SingleMarketSettingSummaries = new List(); - public void OnGet() { + public void OnGet() + { base.Init(); BindData(); } - private void BindData() { - if (System.IO.File.Exists(PTMagicBasePath + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "SingleMarketSettingSummary.json")) { - try { + private void BindData() + { + if (System.IO.File.Exists(PTMagicBasePath + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "SingleMarketSettingSummary.json")) + { + try + { SingleMarketSettingSummaries = JsonConvert.DeserializeObject>(System.IO.File.ReadAllText(PTMagicBasePath + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "SingleMarketSettingSummary.json")); - } catch { } + } + catch { } } string notification = GetStringParameter("n", ""); - if (notification.Equals("SettingReset")) { + if (notification.Equals("SettingReset")) + { NotifyHeadline = "Setting Reset!"; NotifyMessage = "The setting will get reset on the next interval!"; NotifyType = "success"; } } - public double GetTrendChange(string marketTrend, MarketPairSummary mps, TriggerSnapshot ts, string marketTrendRelation) { + public double GetTrendChange(string marketTrend, MarketPairSummary mps, TriggerSnapshot ts, string marketTrendRelation) + { double result = 0; - if (mps.MarketTrendChanges.ContainsKey(marketTrend)) { + if (mps.MarketTrendChanges.ContainsKey(marketTrend)) + { result = mps.MarketTrendChanges[marketTrend]; double averageMarketTrendChange = Summary.MarketTrendChanges[marketTrend].OrderByDescending(mtc => mtc.TrendDateTime).First().TrendChange; - if (marketTrendRelation.Equals(Constants.MarketTrendRelationAbsolute, StringComparison.InvariantCulture)) { + if (marketTrendRelation.Equals(Constants.MarketTrendRelationAbsolute, StringComparison.InvariantCulture)) + { result = result - averageMarketTrendChange; - } else if (marketTrendRelation.Equals(Constants.MarketTrendRelationRelativeTrigger, StringComparison.InvariantCulture)) { + } + else if (marketTrendRelation.Equals(Constants.MarketTrendRelationRelativeTrigger, StringComparison.InvariantCulture)) + { double currentPrice = mps.LatestPrice; double triggerPrice = ts.LastPrice; double triggerTrend = (currentPrice - triggerPrice) / triggerPrice * 100; diff --git a/Monitor/Pages/MarketAnalyzer.cshtml.cs b/Monitor/Pages/MarketAnalyzer.cshtml.cs index 5a81200..f495c46 100644 --- a/Monitor/Pages/MarketAnalyzer.cshtml.cs +++ b/Monitor/Pages/MarketAnalyzer.cshtml.cs @@ -6,41 +6,54 @@ using Core.Main; using Core.Helper; using Core.Main.DataObjects.PTMagicData; -namespace Monitor.Pages { - public class MarketAnalyzerModel : _Internal.BasePageModelSecure { +namespace Monitor.Pages +{ + public class MarketAnalyzerModel : _Internal.BasePageModelSecure + { public List MarketTrends { get; set; } = new List(); public string TrendChartDataJSON = ""; - public void OnGet() { + public void OnGet() + { base.Init(); - + BindData(); } - private void BindData() { + private void BindData() + { // Get market trends MarketTrends = PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.OrderBy(mt => mt.TrendMinutes).ThenByDescending(mt => mt.Platform).ToList(); BuildMarketTrendChartData(); } - private void BuildMarketTrendChartData() { - if (MarketTrends.Count > 0) { + private void BuildMarketTrendChartData() + { + if (MarketTrends.Count > 0) + { TrendChartDataJSON = "["; int mtIndex = 0; - foreach (MarketTrend mt in MarketTrends) { - if (mt.DisplayGraph) { + foreach (MarketTrend mt in MarketTrends) + { + if (mt.DisplayGraph) + { string lineColor = ""; - if (mtIndex < Constants.ChartLineColors.Length) { + if (mtIndex < Constants.ChartLineColors.Length) + { lineColor = Constants.ChartLineColors[mtIndex]; - } else { + } + else + { lineColor = Constants.ChartLineColors[mtIndex - 20]; } - if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) { + if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) + { List marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name]; - if (marketTrendChangeSummaries.Count > 0) { + if (marketTrendChangeSummaries.Count > 0) + { if (!TrendChartDataJSON.Equals("[")) TrendChartDataJSON += ","; TrendChartDataJSON += "{"; @@ -53,9 +66,11 @@ namespace Monitor.Pages { DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours); DateTime endDateTime = currentDateTime; int trendChartTicks = 0; - for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) { + for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) + { List tickRange = marketTrendChangeSummaries.FindAll(m => m.TrendDateTime >= tickTime).OrderBy(m => m.TrendDateTime).ToList(); - if (tickRange.Count > 0) { + if (tickRange.Count > 0) + { MarketTrendChange mtc = tickRange.First(); if (tickTime != startDateTime) TrendChartDataJSON += ",\n"; if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; @@ -67,7 +82,8 @@ namespace Monitor.Pages { // Add most recent tick List latestTickRange = marketTrendChangeSummaries.OrderByDescending(m => m.TrendDateTime).ToList(); - if (latestTickRange.Count > 0) { + if (latestTickRange.Count > 0) + { MarketTrendChange mtc = latestTickRange.First(); if (trendChartTicks > 0) TrendChartDataJSON += ",\n"; if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0; diff --git a/Monitor/Pages/PresetFiles.cshtml.cs b/Monitor/Pages/PresetFiles.cshtml.cs index 702df82..fabec0a 100644 --- a/Monitor/Pages/PresetFiles.cshtml.cs +++ b/Monitor/Pages/PresetFiles.cshtml.cs @@ -7,26 +7,33 @@ using Core.Helper; using Core.Main.DataObjects.PTMagicData; using System.Globalization; -namespace Monitor.Pages { - public class PresetFilesModel : _Internal.BasePageModelSecure { +namespace Monitor.Pages +{ + public class PresetFilesModel : _Internal.BasePageModelSecure + { public List GlobalSettingsWithPresets = new List(); - public void OnGet() { + public void OnGet() + { base.Init(); BindData(); } - public void BindData() { + public void BindData() + { string notification = GetStringParameter("n", ""); - if (notification.Equals("PresetFileSaved")) { + if (notification.Equals("PresetFileSaved")) + { NotifyHeadline = "Preset File Saved!"; NotifyMessage = "The preset file was saved and will be applied during the next interval."; NotifyType = "success"; } - foreach (GlobalSetting globalSetting in PTMagicConfiguration.AnalyzerSettings.GlobalSettings) { - if (globalSetting.PairsProperties.ContainsKey("File") || globalSetting.DCAProperties.ContainsKey("File") || globalSetting.IndicatorsProperties.ContainsKey("File")) { + foreach (GlobalSetting globalSetting in PTMagicConfiguration.AnalyzerSettings.GlobalSettings) + { + if (globalSetting.PairsProperties.ContainsKey("File") || globalSetting.DCAProperties.ContainsKey("File") || globalSetting.IndicatorsProperties.ContainsKey("File")) + { GlobalSettingsWithPresets.Add(globalSetting); } } diff --git a/Monitor/Pages/SalesAnalyzer.cshtml.cs b/Monitor/Pages/SalesAnalyzer.cshtml.cs index b5466aa..02e9e78 100644 --- a/Monitor/Pages/SalesAnalyzer.cshtml.cs +++ b/Monitor/Pages/SalesAnalyzer.cshtml.cs @@ -7,8 +7,10 @@ using Core.Helper; using Core.Main.DataObjects; using Core.Main.DataObjects.PTMagicData; -namespace Monitor.Pages { - public class SalesAnalyzer : _Internal.BasePageModelSecure { +namespace Monitor.Pages +{ + public class SalesAnalyzer : _Internal.BasePageModelSecure + { public ProfitTrailerData PTData = null; public string TradesChartDataJSON = ""; public string ProfitChartDataJSON = ""; @@ -18,13 +20,15 @@ namespace Monitor.Pages { public Dictionary MonthlyGains = new Dictionary(); public DateTimeOffset DateTimeNow = Constants.confMinDate; - public void OnGet() { + public void OnGet() + { base.Init(); - + BindData(); } - private void BindData() { + private void BindData() + { PTData = new ProfitTrailerData(PTMagicBasePath, PTMagicConfiguration); // Convert local offset time to UTC @@ -35,10 +39,12 @@ namespace Monitor.Pages { BuildSalesChartData(); } - private void BuildTopMarkets() { + private void BuildTopMarkets() + { var markets = PTData.SellLog.GroupBy(m => m.Market); Dictionary topMarketsDic = new Dictionary(); - foreach (var market in markets) { + foreach (var market in markets) + { double totalProfit = PTData.SellLog.FindAll(m => m.Market == market.Key).Sum(m => m.Profit); topMarketsDic.Add(market.Key, totalProfit); @@ -46,8 +52,10 @@ namespace Monitor.Pages { TopMarkets = new SortedDictionary(topMarketsDic).OrderByDescending(m => m.Value).Take(PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets); } - private void BuildSalesChartData() { - if (PTData.SellLog.Count > 0) { + private void BuildSalesChartData() + { + if (PTData.SellLog.Count > 0) + { MinSellLogDate = PTData.SellLog.OrderBy(sl => sl.SoldDate).First().SoldDate.Date; DateTime graphStartDate = DateTimeNow.DateTime.Date.AddDays(-30); if (MinSellLogDate > graphStartDate) graphStartDate = MinSellLogDate; @@ -55,8 +63,10 @@ namespace Monitor.Pages { int tradeDayIndex = 0; string tradesPerDayJSON = ""; string profitPerDayJSON = ""; - for (DateTime salesDate = graphStartDate; salesDate <= DateTimeNow.DateTime.Date; salesDate = salesDate.AddDays(1)) { - if (tradeDayIndex > 0) { + for (DateTime salesDate = graphStartDate; salesDate <= DateTimeNow.DateTime.Date; salesDate = salesDate.AddDays(1)) + { + if (tradeDayIndex > 0) + { tradesPerDayJSON += ",\n"; profitPerDayJSON += ",\n"; } @@ -87,7 +97,8 @@ namespace Monitor.Pages { ProfitChartDataJSON += "}"; ProfitChartDataJSON += "]"; - for (DateTime salesDate = DateTimeNow.DateTime.Date; salesDate >= MinSellLogDate; salesDate = salesDate.AddDays(-1)) { + for (DateTime salesDate = DateTimeNow.DateTime.Date; salesDate >= MinSellLogDate; salesDate = salesDate.AddDays(-1)) + { List salesDateSales = PTData.SellLog.FindAll(sl => sl.SoldDate.Date == salesDate); double salesDateProfit = salesDateSales.Sum(sl => sl.Profit); double salesDateStartBalance = PTData.GetSnapshotBalance(salesDate); @@ -98,7 +109,8 @@ namespace Monitor.Pages { DateTime minSellLogMonthDate = new DateTime(MinSellLogDate.Year, MinSellLogDate.Month, 1).Date; DateTime salesMonthStartDate = new DateTime(DateTimeNow.DateTime.Year, DateTimeNow.DateTime.Month, 1).Date; - for (DateTime salesMonthDate = salesMonthStartDate.Date; salesMonthDate >= minSellLogMonthDate; salesMonthDate = salesMonthDate.AddMonths(-1)) { + for (DateTime salesMonthDate = salesMonthStartDate.Date; salesMonthDate >= minSellLogMonthDate; salesMonthDate = salesMonthDate.AddMonths(-1)) + { List salesMonthSales = PTData.SellLog.FindAll(sl => sl.SoldDate.Date.Month == salesMonthDate.Month && sl.SoldDate.Date.Year == salesMonthDate.Year); double salesDateProfit = salesMonthSales.Sum(sl => sl.Profit); double salesDateStartBalance = PTData.GetSnapshotBalance(salesMonthDate); diff --git a/Monitor/Pages/SettingsAnalyzer.cshtml b/Monitor/Pages/SettingsAnalyzer.cshtml index 7b9bb05..7c20569 100644 --- a/Monitor/Pages/SettingsAnalyzer.cshtml +++ b/Monitor/Pages/SettingsAnalyzer.cshtml @@ -68,7 +68,6 @@

Market Trends (@Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.Count)

-
-
- - -
+
-
- - -
+
@@ -138,26 +119,33 @@
-

Market Analyzer

+
+
+ +
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
- -
- -
- +
+ +
+ +
+
+
@@ -167,7 +155,22 @@
@if (Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.Count > 0) { @foreach (Core.Main.DataObjects.PTMagicData.MarketTrend mt in Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends) { -
+ } } else {
@@ -183,7 +186,22 @@
@if (Model.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Count > 0) { @foreach (Core.Main.DataObjects.PTMagicData.GlobalSetting gs in Model.PTMagicConfiguration.AnalyzerSettings.GlobalSettings) { -
+ } } else {
@@ -199,7 +217,22 @@
@if (Model.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings.Count > 0) { @foreach (Core.Main.DataObjects.PTMagicData.SingleMarketSetting sms in Model.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings) { -
+ } } else {
@@ -211,7 +244,6 @@
}
-
@@ -299,18 +331,14 @@ $('.settings-markettrend.new').buildMarketTrendSettings(); break; case 'GlobalSetting': - $('html, body').scrollTop($('#MarketAnalyzer_GlobalSettings > [data-settingname="' + dataTarget + '"]').offset().top - 100); - $('#MarketAnalyzer_GlobalSettings > [data-settingname="' + dataTarget + '"]').before('
'); + $('#MarketAnalyzer_GlobalSettings').append('
'); + $('html, body').scrollTop($('#MarketAnalyzer_GlobalSettings').offset().top + $('#MarketAnalyzer_GlobalSettings').height() - 100); $('.settings-globalsetting.new').buildGlobalSettings(); break; case 'SingleMarketSetting': - if (dataDirection === 'before') { - $('html, body').scrollTop($('#MarketAnalyzer_SingleMarketSettings > [data-settingname="' + dataTarget + '"]').offset().top - 100); - $('#MarketAnalyzer_SingleMarketSettings > [data-settingname="' + dataTarget + '"]').before('
'); - } else { - $('html, body').scrollTop($('#MarketAnalyzer_SingleMarketSettings > [data-settingname="' + dataTarget + '"]').offset().top + $('#MarketAnalyzer_SingleMarketSettings > [data-settingname="' + dataTarget + '"]').height() - 100); - $('#MarketAnalyzer_SingleMarketSettings > [data-settingname="' + dataTarget + '"]').after('
'); - } + + $('#MarketAnalyzer_SingleMarketSettings').append('
'); + $('html, body').scrollTop($('#MarketAnalyzer_SingleMarketSettings').offset().top + $('#MarketAnalyzer_SingleMarketSettings').height() - 100); $('.settings-singlemarketsetting.new').buildSingleMarketSettings(); break; } diff --git a/Monitor/Pages/SettingsAnalyzer.cshtml.cs b/Monitor/Pages/SettingsAnalyzer.cshtml.cs index 570e4bd..0a1209a 100644 --- a/Monitor/Pages/SettingsAnalyzer.cshtml.cs +++ b/Monitor/Pages/SettingsAnalyzer.cshtml.cs @@ -7,22 +7,27 @@ using Core.Helper; using Core.Main.DataObjects.PTMagicData; using Microsoft.Extensions.Primitives; -namespace Monitor.Pages { - public class SettingsAnalyzerModel : _Internal.BasePageModelSecure { +namespace Monitor.Pages +{ + public class SettingsAnalyzerModel : _Internal.BasePageModelSecure + { public string ValidationMessage = ""; - public void OnGet() { + public void OnGet() + { base.Init(); string notification = GetStringParameter("n", ""); - if (notification.Equals("BackupRestored")) { + if (notification.Equals("BackupRestored")) + { NotifyHeadline = "Backup restored!"; NotifyMessage = "Your backup of settings.analyzer.json was successfully restored."; NotifyType = "success"; } } - public void OnPost() { + public void OnPost() + { base.Init(); PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours = SystemHelper.TextToInteger(HttpContext.Request.Form["MarketAnalyzer_StoreDataMaxHours"], PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours); @@ -42,18 +47,23 @@ namespace Monitor.Pages { NotifyType = "success"; } - private void SaveMarketTrends(List formKeys) { + private void SaveMarketTrends(List formKeys) + { List newMarketTrends = new List(); List marketTrendFormKeys = formKeys.FindAll(k => k.StartsWith("MarketAnalyzer_MarketTrend_") && k.EndsWith("|Name")); - foreach (string marketTrendFormKey in marketTrendFormKeys) { + foreach (string marketTrendFormKey in marketTrendFormKeys) + { MarketTrend mt = null; string originalNameSimplified = marketTrendFormKey.Replace("MarketAnalyzer_MarketTrend_", "").Replace("|Name", ""); string mtFormKey = "MarketAnalyzer_MarketTrend_" + originalNameSimplified + "|"; - if (originalNameSimplified.Equals("")) { + if (originalNameSimplified.Equals("")) + { mt = new MarketTrend(); - } else { + } + else + { mt = PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.Find(m => SystemHelper.StripBadCode(m.Name, Constants.WhiteListNames).Equals(originalNameSimplified)); } @@ -72,18 +82,23 @@ namespace Monitor.Pages { PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends = newMarketTrends; } - private void SaveGlobalSettings(List formKeys) { + private void SaveGlobalSettings(List formKeys) + { List newGlobalMarketSettings = new List(); List globalSettingFormKeys = formKeys.FindAll(k => k.StartsWith("MarketAnalyzer_GlobalSetting_") && k.EndsWith("|SettingName")); - foreach (string globalSettingFormKey in globalSettingFormKeys) { + foreach (string globalSettingFormKey in globalSettingFormKeys) + { GlobalSetting gs = null; string originalNameSimplified = globalSettingFormKey.Replace("MarketAnalyzer_GlobalSetting_", "").Replace("|SettingName", ""); string gsFormKey = "MarketAnalyzer_GlobalSetting_" + originalNameSimplified + "|"; - if (originalNameSimplified.Equals("")) { + if (originalNameSimplified.Equals("")) + { gs = new GlobalSetting(); - } else { + } + else + { gs = PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(s => SystemHelper.StripBadCode(s.SettingName, Constants.WhiteListNames).Equals(originalNameSimplified)); } @@ -91,20 +106,26 @@ namespace Monitor.Pages { gs.TriggerConnection = HttpContext.Request.Form[gsFormKey + "TriggerConnection"]; // Triggers - if (!gs.SettingName.Equals("Default", StringComparison.InvariantCultureIgnoreCase)) { + if (!gs.SettingName.Equals("Default", StringComparison.InvariantCultureIgnoreCase)) + { List newTriggers = new List(); List globalSettingTriggerFormKeys = formKeys.FindAll(k => k.StartsWith(gsFormKey + "Trigger_") && k.EndsWith("|MarketTrendName")); - foreach (string globalSettingTriggerFormKey in globalSettingTriggerFormKeys) { + foreach (string globalSettingTriggerFormKey in globalSettingTriggerFormKeys) + { Trigger trigger = null; string originalTriggerNameSimplified = globalSettingTriggerFormKey.Replace(gsFormKey + "Trigger_", "").Replace("|MarketTrendName", ""); string tFormKey = gsFormKey + "Trigger_" + originalTriggerNameSimplified + "|"; - for (int f = 0; f < HttpContext.Request.Form[tFormKey + "MarketTrendName"].Count; f++) { + for (int f = 0; f < HttpContext.Request.Form[tFormKey + "MarketTrendName"].Count; f++) + { - if (originalTriggerNameSimplified.Equals("")) { + if (originalTriggerNameSimplified.Equals("")) + { trigger = new Trigger(); - } else { + } + else + { trigger = gs.Triggers.Find(t => SystemHelper.StripBadCode(t.MarketTrendName, Constants.WhiteListNames).Equals(originalTriggerNameSimplified)); } @@ -135,18 +156,23 @@ namespace Monitor.Pages { PTMagicConfiguration.AnalyzerSettings.GlobalSettings = newGlobalMarketSettings; } - private void SaveSingleMarketSettings(List formKeys) { + private void SaveSingleMarketSettings(List formKeys) + { List newSingleMarketMarketSettings = new List(); List singleMarketSettingFormKeys = formKeys.FindAll(k => k.StartsWith("MarketAnalyzer_SingleMarketSetting_") && k.EndsWith("|SettingName")); - foreach (string singleMarketSettingFormKey in singleMarketSettingFormKeys) { + foreach (string singleMarketSettingFormKey in singleMarketSettingFormKeys) + { SingleMarketSetting sms = null; string originalNameSimplified = singleMarketSettingFormKey.Replace("MarketAnalyzer_SingleMarketSetting_", "").Replace("|SettingName", ""); string smsFormKey = "MarketAnalyzer_SingleMarketSetting_" + originalNameSimplified + "|"; - if (originalNameSimplified.Equals("")) { + if (originalNameSimplified.Equals("")) + { sms = new SingleMarketSetting(); - } else { + } + else + { sms = PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings.Find(s => SystemHelper.StripBadCode(s.SettingName, Constants.WhiteListNames).Equals(originalNameSimplified)); } @@ -160,16 +186,21 @@ namespace Monitor.Pages { #region Triggers List newTriggers = new List(); List singleMarketSettingTriggerFormKeys = formKeys.FindAll(k => k.StartsWith(smsFormKey + "Trigger_") && k.EndsWith("|MarketTrendName")); - foreach (string singleMarketSettingTriggerFormKey in singleMarketSettingTriggerFormKeys) { + foreach (string singleMarketSettingTriggerFormKey in singleMarketSettingTriggerFormKeys) + { Trigger trigger = null; string originalTriggerNameSimplified = singleMarketSettingTriggerFormKey.Replace(smsFormKey + "Trigger_", "").Replace("|MarketTrendName", ""); string tFormKey = smsFormKey + "Trigger_" + originalTriggerNameSimplified + "|"; - for (int f = 0; f < HttpContext.Request.Form[tFormKey + "MarketTrendName"].Count; f++) { - if (originalTriggerNameSimplified.Equals("")) { + for (int f = 0; f < HttpContext.Request.Form[tFormKey + "MarketTrendName"].Count; f++) + { + if (originalTriggerNameSimplified.Equals("")) + { trigger = new Trigger(); - } else { + } + else + { trigger = sms.Triggers.Find(t => SystemHelper.StripBadCode(t.MarketTrendName, Constants.WhiteListNames).Equals(originalTriggerNameSimplified)); } @@ -183,13 +214,15 @@ namespace Monitor.Pages { } List singleMarketSettingCoinAgeTriggerFormKeys = formKeys.FindAll(k => k.StartsWith(smsFormKey + "Trigger_AgeDaysLowerThan")); - foreach (string singleMarketSettingCoinAgeTriggerFormKey in singleMarketSettingCoinAgeTriggerFormKeys) { + foreach (string singleMarketSettingCoinAgeTriggerFormKey in singleMarketSettingCoinAgeTriggerFormKeys) + { Trigger trigger = null; string originalTriggerIndex = singleMarketSettingCoinAgeTriggerFormKey.Replace(smsFormKey + "Trigger_AgeDaysLowerThan", ""); string tFormKey = smsFormKey + "Trigger_AgeDaysLowerThan" + originalTriggerIndex; - for (int f = 0; f < HttpContext.Request.Form[tFormKey].Count; f++) { + for (int f = 0; f < HttpContext.Request.Form[tFormKey].Count; f++) + { trigger = new Trigger(); trigger.AgeDaysLowerThan = SystemHelper.TextToInteger(HttpContext.Request.Form[tFormKey][f], 0); @@ -199,13 +232,15 @@ namespace Monitor.Pages { } List singleMarketSetting24hVolumeTriggerFormKeys = formKeys.FindAll(k => k.StartsWith(smsFormKey + "Trigger_24hVolume") && k.EndsWith("|Min24hVolume")); - foreach (string singleMarketSetting24hVolumeTriggerFormKey in singleMarketSetting24hVolumeTriggerFormKeys) { + foreach (string singleMarketSetting24hVolumeTriggerFormKey in singleMarketSetting24hVolumeTriggerFormKeys) + { Trigger trigger = null; string originalTriggerIndex = singleMarketSetting24hVolumeTriggerFormKey.Replace(smsFormKey + "Trigger_24hVolume", "").Replace("|Min24hVolume", ""); string tFormKey = smsFormKey + "Trigger_24hVolume" + originalTriggerIndex + "|"; - for (int f = 0; f < HttpContext.Request.Form[tFormKey + "Min24hVolume"].Count; f++) { + for (int f = 0; f < HttpContext.Request.Form[tFormKey + "Min24hVolume"].Count; f++) + { trigger = new Trigger(); trigger.Min24hVolume = SystemHelper.TextToDouble(HttpContext.Request.Form[tFormKey + "Min24hVolume"][f], 0, "en-US"); @@ -221,16 +256,21 @@ namespace Monitor.Pages { #region Off Triggers List newOffTriggers = new List(); List singleMarketSettingOffTriggerFormKeys = formKeys.FindAll(k => k.StartsWith(smsFormKey + "OffTrigger_") && k.EndsWith("|MarketTrendName")); - foreach (string singleMarketSettingOffTriggerFormKey in singleMarketSettingOffTriggerFormKeys) { + foreach (string singleMarketSettingOffTriggerFormKey in singleMarketSettingOffTriggerFormKeys) + { OffTrigger offTrigger = null; string originalOffTriggerNameSimplified = singleMarketSettingOffTriggerFormKey.Replace(smsFormKey + "OffTrigger_", "").Replace("|MarketTrendName", ""); string tFormKey = smsFormKey + "OffTrigger_" + originalOffTriggerNameSimplified + "|"; - for (int f = 0; f < HttpContext.Request.Form[tFormKey + "MarketTrendName"].Count; f++) { - if (originalOffTriggerNameSimplified.Equals("")) { + for (int f = 0; f < HttpContext.Request.Form[tFormKey + "MarketTrendName"].Count; f++) + { + if (originalOffTriggerNameSimplified.Equals("")) + { offTrigger = new OffTrigger(); - } else { + } + else + { offTrigger = sms.OffTriggers.Find(t => SystemHelper.StripBadCode(t.MarketTrendName, Constants.WhiteListNames).Equals(originalOffTriggerNameSimplified)); } @@ -244,13 +284,15 @@ namespace Monitor.Pages { } List singleMarketSettingHoursActiveOffTriggerFormKeys = formKeys.FindAll(k => k.StartsWith(smsFormKey + "OffTrigger_HoursSinceTriggered")); - foreach (string singleMarketSettingHoursActiveOffTriggerFormKey in singleMarketSettingHoursActiveOffTriggerFormKeys) { + foreach (string singleMarketSettingHoursActiveOffTriggerFormKey in singleMarketSettingHoursActiveOffTriggerFormKeys) + { OffTrigger offTrigger = null; string originalOffTriggerIndex = singleMarketSettingHoursActiveOffTriggerFormKey.Replace(smsFormKey + "OffTrigger_HoursSinceTriggered", ""); string tFormKey = smsFormKey + "OffTrigger_HoursSinceTriggered" + originalOffTriggerIndex; - for (int f = 0; f < HttpContext.Request.Form[tFormKey].Count; f++) { + for (int f = 0; f < HttpContext.Request.Form[tFormKey].Count; f++) + { offTrigger = new OffTrigger(); offTrigger.HoursSinceTriggered = SystemHelper.TextToInteger(HttpContext.Request.Form[tFormKey][f], 0); @@ -260,13 +302,15 @@ namespace Monitor.Pages { } List singleMarketSetting24hVolumeOffTriggerFormKeys = formKeys.FindAll(k => k.StartsWith(smsFormKey + "OffTrigger_24hVolume") && k.EndsWith("|Min24hVolume")); - foreach (string singleMarketSetting24hVolumeOffTriggerFormKey in singleMarketSetting24hVolumeOffTriggerFormKeys) { + foreach (string singleMarketSetting24hVolumeOffTriggerFormKey in singleMarketSetting24hVolumeOffTriggerFormKeys) + { OffTrigger offTrigger = null; string originalOffTriggerIndex = singleMarketSetting24hVolumeOffTriggerFormKey.Replace(smsFormKey + "OffTrigger_24hVolume", "").Replace("|Min24hVolume", ""); string tFormKey = smsFormKey + "OffTrigger_24hVolume" + originalOffTriggerIndex + "|"; - for (int f = 0; f < HttpContext.Request.Form[tFormKey + "Min24hVolume"].Count; f++) { + for (int f = 0; f < HttpContext.Request.Form[tFormKey + "Min24hVolume"].Count; f++) + { offTrigger = new OffTrigger(); offTrigger.Min24hVolume = SystemHelper.TextToDouble(HttpContext.Request.Form[tFormKey + "Min24hVolume"][f], 0, "en-US"); @@ -296,29 +340,39 @@ namespace Monitor.Pages { PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings = newSingleMarketMarketSettings; } - private Dictionary GetProfitTrailerProperties(List formKeys, string sFormKey, string propertyType) { + private Dictionary GetProfitTrailerProperties(List formKeys, string sFormKey, string propertyType) + { Dictionary result = new Dictionary(); List globalSettingPairsPropertiesFormKeys = formKeys.FindAll(k => k.StartsWith(sFormKey + propertyType + "Property_") && k.IndexOf("|Value") == -1); - foreach (string globalSettingPairsFormKey in globalSettingPairsPropertiesFormKeys) { + foreach (string globalSettingPairsFormKey in globalSettingPairsPropertiesFormKeys) + { string originalKeySimplified = globalSettingPairsFormKey.Replace(sFormKey + propertyType + "Property_", ""); string propertyFormKey = sFormKey + propertyType + "Property_" + originalKeySimplified; - for (int f = 0; f < HttpContext.Request.Form[propertyFormKey].Count; f++) { + for (int f = 0; f < HttpContext.Request.Form[propertyFormKey].Count; f++) + { string propertyKey = HttpContext.Request.Form[propertyFormKey][f] + HttpContext.Request.Form[propertyFormKey + "|ValueMode"][f]; string propertyValueString = HttpContext.Request.Form[propertyFormKey + "|Value"][f]; object propertyValue = new object(); - if (propertyValueString.Equals("true", StringComparison.InvariantCultureIgnoreCase) | propertyValueString.Equals("false", StringComparison.InvariantCultureIgnoreCase)) { + if (propertyValueString.Equals("true", StringComparison.InvariantCultureIgnoreCase) | propertyValueString.Equals("false", StringComparison.InvariantCultureIgnoreCase)) + { propertyValue = Convert.ToBoolean(propertyValueString); - } else { - if (SystemHelper.IsDouble(propertyValueString, "en-US")) { + } + else + { + if (SystemHelper.IsDouble(propertyValueString, "en-US")) + { propertyValue = SystemHelper.TextToDouble(propertyValueString, 0, "en-US"); - if (((double)propertyValue % 1) == 0) { + if (((double)propertyValue % 1) == 0) + { propertyValue = Convert.ToInt32(propertyValue); } - } else { + } + else + { propertyValue = propertyValueString; } } diff --git a/Monitor/Pages/SettingsGeneral.cshtml b/Monitor/Pages/SettingsGeneral.cshtml index a7335e7..8ae2ac8 100644 --- a/Monitor/Pages/SettingsGeneral.cshtml +++ b/Monitor/Pages/SettingsGeneral.cshtml @@ -41,102 +41,116 @@
-

Application

+
+
+ +
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ @Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMajorVersion +
+
-
- -
- @Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMajorVersion -
-
+
+ +
+ @Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath +
+
-
- -
- @Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath -
-
+
+ +
+ @Model.PTMagicConfiguration.GetProfitTrailerLicenseKeyMasked() +
+
-
- -
- @Model.PTMagicConfiguration.GetProfitTrailerLicenseKeyMasked() -
-
+ - +
+ +
+ @Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName +
+
-
- -
- @Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- +
+ +
+ +
+
+
@@ -146,130 +160,137 @@
-

Monitor

+
+
+
+

MONITOR

+
+
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ @Model.PTMagicConfiguration.GeneralSettings.Monitor.Port +
+
-
- -
- @Model.PTMagicConfiguration.GeneralSettings.Monitor.Port -
-
+
+ +
+ @Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl +
+
-
- -
- @Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
- -
- -
- +
+ +
+ +
+
+
@@ -279,19 +300,26 @@
-

Backup

+
+
+
+

BACKUP

+
+
+
+ +
+ +
+
-
- -
- -
-
- -
- -
- +
+ +
+ +
+
+
@@ -301,42 +329,49 @@
-

Telegram

+
+
+
+

TELEGRAM

+
+
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
- -
- -
- +
+ +
+ +
+
+
diff --git a/Monitor/Pages/SettingsGeneral.cshtml.cs b/Monitor/Pages/SettingsGeneral.cshtml.cs index 395b73e..899e5eb 100644 --- a/Monitor/Pages/SettingsGeneral.cshtml.cs +++ b/Monitor/Pages/SettingsGeneral.cshtml.cs @@ -6,11 +6,14 @@ using Core.Main; using Core.Helper; using Core.Main.DataObjects.PTMagicData; -namespace Monitor.Pages { - public class SettingsGeneralModel : _Internal.BasePageModelSecure { +namespace Monitor.Pages +{ + public class SettingsGeneralModel : _Internal.BasePageModelSecure + { public string ValidationMessage = ""; - private string GetTimezoneOffsetString(TimeZoneInfo tzi) { + private string GetTimezoneOffsetString(TimeZoneInfo tzi) + { string result = ""; result += (tzi.BaseUtcOffset >= TimeSpan.Zero) ? "+" : "-"; @@ -21,15 +24,19 @@ namespace Monitor.Pages { return result; } - public string GetTimezoneSelection() { + public string GetTimezoneSelection() + { string result = ""; List tzOffsetList = new List(); - foreach (TimeZoneInfo tzi in TimeZoneInfo.GetSystemTimeZones()) { + foreach (TimeZoneInfo tzi in TimeZoneInfo.GetSystemTimeZones()) + { string offsetString = this.GetTimezoneOffsetString(tzi); - if (!tzOffsetList.Contains(offsetString)) { + if (!tzOffsetList.Contains(offsetString)) + { string selected = ""; - if (PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Equals(offsetString, StringComparison.InvariantCultureIgnoreCase)) { + if (PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Equals(offsetString, StringComparison.InvariantCultureIgnoreCase)) + { selected = " selected=\"selected\""; } @@ -41,18 +48,21 @@ namespace Monitor.Pages { return result; } - public void OnGet() { + public void OnGet() + { base.Init(); string notification = GetStringParameter("n", ""); - if (notification.Equals("BackupRestored")) { + if (notification.Equals("BackupRestored")) + { NotifyHeadline = "Backup restored!"; NotifyMessage = "Your backup of settings.general.json was successfully restored."; NotifyType = "success"; } } - public void OnPost() { + public void OnPost() + { base.Init(); PTMagicConfiguration.GeneralSettings.Application.IsEnabled = HttpContext.Request.Form["Application_IsEnabled"].Equals("on"); @@ -63,6 +73,7 @@ namespace Monitor.Pages { PTMagicConfiguration.GeneralSettings.Application.AlwaysLoadDefaultBeforeSwitch = HttpContext.Request.Form["Application_AlwaysLoadDefaultBeforeSwitch"].Equals("on"); PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes = SystemHelper.TextToInteger(HttpContext.Request.Form["Application_FloodProtectionMinutes"], PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes); PTMagicConfiguration.GeneralSettings.Application.InstanceName = HttpContext.Request.Form["Application_InstanceName"]; + PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey = HttpContext.Request.Form["Application_CoinMarketCapAPIKey"]; PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected = HttpContext.Request.Form["Monitor_IsPasswordProtected"].Equals("on"); PTMagicConfiguration.GeneralSettings.Monitor.OpenBrowserOnStart = HttpContext.Request.Form["Monitor_OpenBrowserOnStart"].Equals("on"); diff --git a/Monitor/Pages/SetupPassword.cshtml.cs b/Monitor/Pages/SetupPassword.cshtml.cs index 34e48ab..0a0910c 100644 --- a/Monitor/Pages/SetupPassword.cshtml.cs +++ b/Monitor/Pages/SetupPassword.cshtml.cs @@ -5,20 +5,26 @@ using System.Threading.Tasks; using Newtonsoft.Json; using Core.Main; -namespace Monitor.Pages { - public class SetupPasswordModel : _Internal.BasePageModel { +namespace Monitor.Pages +{ + public class SetupPasswordModel : _Internal.BasePageModel + { public string ValidationMessage = ""; - public void OnGet() { + public void OnGet() + { base.PreInit(); } - public void OnPost(string password, string passwordConfirm) { - if (!password.Equals(passwordConfirm)) { + public void OnPost(string password, string passwordConfirm) + { + if (!password.Equals(passwordConfirm)) + { ValidationMessage = "Password does not match the confirmation!"; } - if (ModelState.IsValid) { + if (ModelState.IsValid) + { base.PreInit(); PTMagicConfiguration.WriteSecureSettings(password, PTMagicBasePath); diff --git a/Monitor/Pages/StatusSummary.cshtml.cs b/Monitor/Pages/StatusSummary.cshtml.cs index 95258d3..9b47c10 100644 --- a/Monitor/Pages/StatusSummary.cshtml.cs +++ b/Monitor/Pages/StatusSummary.cshtml.cs @@ -6,31 +6,39 @@ using Core.Main; using Core.Helper; using Core.Main.DataObjects.PTMagicData; -namespace Monitor.Pages { - public class StatusSummaryModel : _Internal.BasePageModelSecure { +namespace Monitor.Pages +{ + public class StatusSummaryModel : _Internal.BasePageModelSecure + { public List MarketsWithSingleSettings = new List(); public string SettingsDistribution24hChartDataJSON = ""; public string SettingsDistribution3dChartDataJSON = ""; private Dictionary settingsChartColors = new Dictionary(); - public void OnGet() { + public void OnGet() + { base.Init(); - + BindData(); } - private void BindData() { + private void BindData() + { BuildMarketsWithSingleSettings(); BuildChartColors(); Build24hChartData(); Build3dChartData(); } - private void BuildMarketsWithSingleSettings() { + private void BuildMarketsWithSingleSettings() + { // Get markets with active single settings - foreach (string key in Summary.MarketSummary.Keys) { - if (Summary.MarketSummary[key].ActiveSingleSettings != null) { - if (Summary.MarketSummary[key].ActiveSingleSettings.Count > 0) { + foreach (string key in Summary.MarketSummary.Keys) + { + if (Summary.MarketSummary[key].ActiveSingleSettings != null) + { + if (Summary.MarketSummary[key].ActiveSingleSettings.Count > 0) + { MarketsWithSingleSettings.Add(key); } } @@ -38,13 +46,18 @@ namespace Monitor.Pages { MarketsWithSingleSettings.Sort(); } - private void BuildChartColors() { + private void BuildChartColors() + { int settingIndex = 0; - foreach (GlobalSetting globalSetting in PTMagicConfiguration.AnalyzerSettings.GlobalSettings) { + foreach (GlobalSetting globalSetting in PTMagicConfiguration.AnalyzerSettings.GlobalSettings) + { string chartColor = ""; - if (settingIndex < Constants.ChartLineColors.Length) { + if (settingIndex < Constants.ChartLineColors.Length) + { chartColor = Constants.ChartLineColors[settingIndex]; - } else { + } + else + { chartColor = Constants.ChartLineColors[settingIndex - 20]; } @@ -54,40 +67,54 @@ namespace Monitor.Pages { } } - private void Build24hChartData() { - if (Summary.GlobalSettingSummary.Count > 0) { + private void Build24hChartData() + { + if (Summary.GlobalSettingSummary.Count > 0) + { DateTime dateTime24hAgo = DateTime.Now.AddHours(-24); List gsSummaries24h = Summary.GlobalSettingSummary.FindAll(gss => gss.SwitchDateTime >= dateTime24hAgo); IEnumerable gsNames24h = gsSummaries24h.GroupBy(gss => gss.SettingName).Select(group => group.First()); - if (Summary.GlobalSettingSummary.FindAll(gss => gss.SwitchDateTime <= dateTime24hAgo).Count > 0) { + if (Summary.GlobalSettingSummary.FindAll(gss => gss.SwitchDateTime <= dateTime24hAgo).Count > 0) + { GlobalSettingSummary gsBefore24h = Summary.GlobalSettingSummary.FindAll(gss => gss.SwitchDateTime <= dateTime24hAgo).OrderByDescending(gss => gss.SwitchDateTime).First(); - if (gsBefore24h != null) { + if (gsBefore24h != null) + { DateTime gsSwitchedOffDateTime = gsBefore24h.SwitchDateTime.AddSeconds(gsBefore24h.ActiveSeconds); - if (gsSwitchedOffDateTime > dateTime24hAgo) { + if (gsSwitchedOffDateTime > dateTime24hAgo) + { gsBefore24h.ActiveSeconds = (int)Math.Floor(gsSwitchedOffDateTime.Subtract(dateTime24hAgo).TotalSeconds); gsSummaries24h.Add(gsBefore24h); - if (gsNames24h.Select(gss => gss.SettingName.Equals(gsBefore24h.SettingName)) == null) { + if (gsNames24h.Select(gss => gss.SettingName.Equals(gsBefore24h.SettingName)) == null) + { gsNames24h.Append(gsBefore24h); } } } } - if (gsNames24h.Count() > 0) { + if (gsNames24h.Count() > 0) + { SettingsDistribution24hChartDataJSON = "["; int gssIndex = 0; double totalCoveredSeconds = gsSummaries24h.Sum(gs => gs.ActiveSeconds); - foreach (GlobalSettingSummary gss in gsNames24h) { + foreach (GlobalSettingSummary gss in gsNames24h) + { string lineColor = ""; - if (settingsChartColors.ContainsKey(gss.SettingName)) { + if (settingsChartColors.ContainsKey(gss.SettingName)) + { lineColor = settingsChartColors[gss.SettingName]; - } else { - if (gssIndex < Constants.ChartLineColors.Length) { + } + else + { + if (gssIndex < Constants.ChartLineColors.Length) + { lineColor = Constants.ChartLineColors[gssIndex]; - } else { + } + else + { lineColor = Constants.ChartLineColors[gssIndex - 20]; } } @@ -110,40 +137,54 @@ namespace Monitor.Pages { } } - private void Build3dChartData() { - if (Summary.GlobalSettingSummary.Count > 0) { + private void Build3dChartData() + { + if (Summary.GlobalSettingSummary.Count > 0) + { DateTime dateTime3dAgo = DateTime.Now.AddHours(-72); List gsSummaries3d = Summary.GlobalSettingSummary.FindAll(gss => gss.SwitchDateTime >= dateTime3dAgo); IEnumerable gsNames3d = gsSummaries3d.GroupBy(gss => gss.SettingName).Select(group => group.First()); - if (Summary.GlobalSettingSummary.FindAll(gss => gss.SwitchDateTime <= dateTime3dAgo).Count > 0) { + if (Summary.GlobalSettingSummary.FindAll(gss => gss.SwitchDateTime <= dateTime3dAgo).Count > 0) + { GlobalSettingSummary gsBefore3d = Summary.GlobalSettingSummary.FindAll(gss => gss.SwitchDateTime <= dateTime3dAgo).OrderByDescending(gss => gss.SwitchDateTime).First(); - if (gsBefore3d != null) { + if (gsBefore3d != null) + { DateTime gsSwitchedOffDateTime = gsBefore3d.SwitchDateTime.AddSeconds(gsBefore3d.ActiveSeconds); - if (gsSwitchedOffDateTime > dateTime3dAgo) { + if (gsSwitchedOffDateTime > dateTime3dAgo) + { gsBefore3d.ActiveSeconds = (int)Math.Floor(gsSwitchedOffDateTime.Subtract(dateTime3dAgo).TotalSeconds); gsSummaries3d.Add(gsBefore3d); - if (gsNames3d.Select(gss => gss.SettingName.Equals(gsBefore3d.SettingName)) == null) { + if (gsNames3d.Select(gss => gss.SettingName.Equals(gsBefore3d.SettingName)) == null) + { gsNames3d.Append(gsBefore3d); } } } } - if (gsNames3d.Count() > 0) { + if (gsNames3d.Count() > 0) + { SettingsDistribution3dChartDataJSON = "["; int gssIndex = 0; double totalCoveredSeconds = gsSummaries3d.Sum(gs => gs.ActiveSeconds); - foreach (GlobalSettingSummary gss in gsNames3d) { + foreach (GlobalSettingSummary gss in gsNames3d) + { string lineColor = ""; - if (settingsChartColors.ContainsKey(gss.SettingName)) { + if (settingsChartColors.ContainsKey(gss.SettingName)) + { lineColor = settingsChartColors[gss.SettingName]; - } else { - if (gssIndex < Constants.ChartLineColors.Length) { + } + else + { + if (gssIndex < Constants.ChartLineColors.Length) + { lineColor = Constants.ChartLineColors[gssIndex]; - } else { + } + else + { lineColor = Constants.ChartLineColors[gssIndex - 20]; } } diff --git a/Monitor/Pages/Transactions.cshtml.cs b/Monitor/Pages/Transactions.cshtml.cs index e6f544b..8312f30 100644 --- a/Monitor/Pages/Transactions.cshtml.cs +++ b/Monitor/Pages/Transactions.cshtml.cs @@ -8,22 +8,27 @@ using Core.Main.DataObjects; using Core.Main.DataObjects.PTMagicData; using System.Globalization; -namespace Monitor.Pages { - public class TransactionsModel : _Internal.BasePageModelSecure { +namespace Monitor.Pages +{ + public class TransactionsModel : _Internal.BasePageModelSecure + { public TransactionData TransactionData = null; public string ValidationMessage = ""; - public void OnGet() { + public void OnGet() + { base.Init(); BindData(); } - private void BindData() { + private void BindData() + { TransactionData = new TransactionData(PTMagicBasePath); } - public void OnPost() { + public void OnPost() + { base.Init(); BindData(); @@ -31,11 +36,13 @@ namespace Monitor.Pages { SaveTransaction(); } - private void SaveTransaction() { + private void SaveTransaction() + { double transactionAmount = 0; DateTimeOffset transactionDateTime = Constants.confMinDate; - try { + try + { transactionAmount = SystemHelper.TextToDouble(HttpContext.Request.Form["Transaction_Amount"], transactionAmount, "en-US"); //transactionDateTime = DateTimeOffset.Parse(HttpContext.Request.Form["Transaction_Date"] + " " + HttpContext.Request.Form["Transaction_Time"], CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); DateTime tmp = DateTime.Parse(HttpContext.Request.Form["Transaction_Date"] + " " + HttpContext.Request.Form["Transaction_Time"], CultureInfo.InvariantCulture, DateTimeStyles.None); @@ -43,14 +50,21 @@ namespace Monitor.Pages { // Convert local offset time to UTC TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", "")); transactionDateTime = new DateTimeOffset(tmp, offsetTimeSpan); - } catch { } + } + catch { } - if (transactionAmount == 0) { + if (transactionAmount == 0) + { ValidationMessage = "Please enter a valid amount in the format 123.45!"; - } else { - if (transactionDateTime == Constants.confMinDate) { + } + else + { + if (transactionDateTime == Constants.confMinDate) + { ValidationMessage = "Please select a valid date and time!"; - } else { + } + else + { TransactionData.Transactions.Add(new Transaction() { GUID = Guid.NewGuid().ToString(), Amount = transactionAmount, UTCDateTime = transactionDateTime.UtcDateTime }); TransactionData.SaveTransactions(PTMagicBasePath); diff --git a/Monitor/Pages/_get/BagDetails.cshtml b/Monitor/Pages/_get/BagDetails.cshtml index a629775..1362126 100644 --- a/Monitor/Pages/_get/BagDetails.cshtml +++ b/Monitor/Pages/_get/BagDetails.cshtml @@ -101,7 +101,7 @@
diff --git a/Monitor/Pages/_get/BagList.cshtml b/Monitor/Pages/_get/BagList.cshtml index e41f223..ed578dc 100644 --- a/Monitor/Pages/_get/BagList.cshtml +++ b/Monitor/Pages/_get/BagList.cshtml @@ -73,7 +73,8 @@ string sellStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Model.Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, isSellStrategyTrue, isTrailingSellActive); string currentSellValueText = Core.ProfitTrailer.StrategyHelper.GetCurrentValueText(dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, dcaLogEntry.CurrentHighBBValue, dcaLogEntry.ProfitPercent, true); string triggerSellValueText = Core.ProfitTrailer.StrategyHelper.GetTriggerValueText(Model.Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, dcaLogEntry.BBTrigger, dcaLogEntry.SellTrigger, 0, true); - + double currentFiatValue = Math.Round(dcaLogEntry.Amount * dcaLogEntry.CurrentPrice * Model.Summary.MainMarketPrice, 2); + @if (mps != null && (mps.ActiveSingleSettings == null || mps.ActiveSingleSettings.Count == 0)) { @dcaLogEntry.Market @@ -111,7 +112,7 @@ @dcaLogEntry.CurrentPrice.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) @dcaLogEntry.AverageBuyPrice.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) - @dcaLogEntry.TotalCost.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) + @Html.Raw(@dcaLogEntry.TotalCost.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) + " (" + Model.MainFiatCurrencySymbol + currentFiatValue.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + ")") Details } diff --git a/Monitor/Pages/_get/DashboardTop.cshtml b/Monitor/Pages/_get/DashboardTop.cshtml index 4eb595a..1017e67 100644 --- a/Monitor/Pages/_get/DashboardTop.cshtml +++ b/Monitor/Pages/_get/DashboardTop.cshtml @@ -85,7 +85,9 @@ DCA Buy Strats Sell Strats - Profit + + Profit + Cost @@ -127,6 +129,8 @@ buyDisabled = true; } + double currentFiatValue = Math.Round(dcaLogEntry.Amount * dcaLogEntry.CurrentPrice * Model.Summary.MainMarketPrice, 2); + string sellStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Model.Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, isSellStrategyTrue, isTrailingSellActive); @@ -154,8 +158,10 @@ @if (isTrailingSellActive) { } - @dcaLogEntry.ProfitPercent.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% + + @dcaLogEntry.ProfitPercent.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% + @Html.Raw(dcaLogEntry.TotalCost.ToString("#,#0.000000", new System.Globalization.CultureInfo("en-US")) + " (" + Model.MainFiatCurrencySymbol + @currentFiatValue.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + ")") } diff --git a/Monitor/ViewComponents/PairIconViewComponent.cs b/Monitor/ViewComponents/PairIconViewComponent.cs index 7ce23e0..93a08fd 100644 --- a/Monitor/ViewComponents/PairIconViewComponent.cs +++ b/Monitor/ViewComponents/PairIconViewComponent.cs @@ -4,11 +4,15 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -namespace Monitor.ViewComponents { - public class PairIconViewComponent :ViewComponent { - public async Task InvokeAsync(Core.Main.DataObjects.PTMagicData.MarketPairSummary mps) { +namespace Monitor.ViewComponents +{ + public class PairIconViewComponent : ViewComponent + { + public async Task InvokeAsync(Core.Main.DataObjects.PTMagicData.MarketPairSummary mps) + { IViewComponentResult result = null; - await Task.Run(() => { + await Task.Run(() => + { result = View(mps); }); return result; diff --git a/Monitor/_Internal/BasePageModel.cs b/Monitor/_Internal/BasePageModel.cs index 58af014..9b2f9f8 100644 --- a/Monitor/_Internal/BasePageModel.cs +++ b/Monitor/_Internal/BasePageModel.cs @@ -14,9 +14,11 @@ using Core.ProfitTrailer; using Microsoft.Extensions.Primitives; using System.Diagnostics; -namespace Monitor._Internal { +namespace Monitor._Internal +{ - public class BasePageModel : PageModel { + public class BasePageModel : PageModel + { public string PTMagicBasePath = ""; public string PTMagicMonitorBasePath = ""; public PTMagicConfiguration PTMagicConfiguration = null; @@ -31,13 +33,16 @@ namespace Monitor._Internal { public string MainFiatCurrencySymbol = "$"; - public void PreInit() { + public void PreInit() + { PTMagicMonitorBasePath = Directory.GetCurrentDirectory(); - if (!System.IO.File.Exists(PTMagicMonitorBasePath + Path.DirectorySeparatorChar + "appsettings.json")) { + if (!System.IO.File.Exists(PTMagicMonitorBasePath + Path.DirectorySeparatorChar + "appsettings.json")) + { PTMagicMonitorBasePath += Path.DirectorySeparatorChar + "Monitor"; } - if (!PTMagicMonitorBasePath.EndsWith(Path.DirectorySeparatorChar)) { + if (!PTMagicMonitorBasePath.EndsWith(Path.DirectorySeparatorChar)) + { PTMagicMonitorBasePath += Path.DirectorySeparatorChar; } @@ -48,14 +53,18 @@ namespace Monitor._Internal { PTMagicBasePath = config.GetValue("PTMagicBasePath"); - if (!PTMagicBasePath.EndsWith(Path.DirectorySeparatorChar)) { + if (!PTMagicBasePath.EndsWith(Path.DirectorySeparatorChar)) + { PTMagicBasePath += Path.DirectorySeparatorChar; } - try { + try + { PTMagicConfiguration = new PTMagicConfiguration(PTMagicBasePath); - } catch (Exception ex) { + } + catch (Exception ex) + { throw ex; } @@ -67,44 +76,62 @@ namespace Monitor._Internal { MainFiatCurrencySymbol = SystemHelper.GetCurrencySymbol(Summary.MainFiatCurrency); - try { + try + { // Get latest release from GitHub - if (!String.IsNullOrEmpty(HttpContext.Session.GetString("LatestVersion"))) { + if (!String.IsNullOrEmpty(HttpContext.Session.GetString("LatestVersion"))) + { LatestVersion = HttpContext.Session.GetString("LatestVersion"); - } else { + } + else + { LatestVersion = BaseAnalyzer.GetLatestGitHubRelease(Log, Summary.Version); HttpContext.Session.SetString("LatestVersion", LatestVersion); } - - } catch { } - try { + } + catch { } + + try + { // Get current bot version - if (!String.IsNullOrEmpty(HttpContext.Session.GetString("CurrentBotVersion"))) { + if (!String.IsNullOrEmpty(HttpContext.Session.GetString("CurrentBotVersion"))) + { CurrentBotVersion = HttpContext.Session.GetString("CurrentBotVersion"); - } else { + } + else + { string ptMagicBotDllPath = PTMagicBasePath + "PTMagic.dll"; - if (System.IO.File.Exists(ptMagicBotDllPath)) { + if (System.IO.File.Exists(ptMagicBotDllPath)) + { FileVersionInfo ptMagicDllInfo = FileVersionInfo.GetVersionInfo(ptMagicBotDllPath); CurrentBotVersion = ptMagicDllInfo.ProductVersion.Substring(0, ptMagicDllInfo.ProductVersion.LastIndexOf(".")); HttpContext.Session.SetString("CurrentBotVersion", CurrentBotVersion); - } else { + } + else + { CurrentBotVersion = Summary.Version; } } - } catch { + } + catch + { CurrentBotVersion = Summary.Version; } } - protected string GetStringParameter(string paramName, string defaultValue) { + protected string GetStringParameter(string paramName, string defaultValue) + { string result = defaultValue; - if (HttpContext.Request.Query.ContainsKey(paramName)) { + if (HttpContext.Request.Query.ContainsKey(paramName)) + { result = HttpContext.Request.Query[paramName]; - } else if (HttpContext.Request.Method.Equals("POST") && HttpContext.Request.Form.ContainsKey(paramName)) { + } + else if (HttpContext.Request.Method.Equals("POST") && HttpContext.Request.Form.ContainsKey(paramName)) + { result = HttpContext.Request.Form[paramName]; } @@ -117,19 +144,29 @@ namespace Monitor._Internal { /// Name des Parameters /// Defaultvalue, wenn Parameter nicht vorhanden ist. /// Der Wert des Parameters als Integer. - protected int GetIntParameter(string paramName, int defaultValue) { + protected int GetIntParameter(string paramName, int defaultValue) + { int result = defaultValue; - if (HttpContext.Request.Query.ContainsKey(paramName)) { - try { + if (HttpContext.Request.Query.ContainsKey(paramName)) + { + try + { result = Int32.Parse(HttpContext.Request.Query[paramName]); - } catch { + } + catch + { result = defaultValue; } - } else if (HttpContext.Request.Method.Equals("POST") && HttpContext.Request.Form.ContainsKey(paramName)) { - try { + } + else if (HttpContext.Request.Method.Equals("POST") && HttpContext.Request.Form.ContainsKey(paramName)) + { + try + { result = Int32.Parse(HttpContext.Request.Form[paramName]); - } catch { + } + catch + { result = defaultValue; } } diff --git a/Monitor/_Internal/BasePageModelSecure.cs b/Monitor/_Internal/BasePageModelSecure.cs index 6c6b5ec..5ea9620 100644 --- a/Monitor/_Internal/BasePageModelSecure.cs +++ b/Monitor/_Internal/BasePageModelSecure.cs @@ -13,26 +13,34 @@ using Core.MarketAnalyzer; using Core.ProfitTrailer; using Microsoft.Extensions.Primitives; -namespace Monitor._Internal { +namespace Monitor._Internal +{ - public class BasePageModelSecure : BasePageModel { - public void Init() { + public class BasePageModelSecure : BasePageModel + { + public void Init() + { base.PreInit(); - if (String.IsNullOrEmpty(HttpContext.Session.GetString("LoggedIn" + PTMagicConfiguration.GeneralSettings.Monitor.Port.ToString())) && PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected) { + if (String.IsNullOrEmpty(HttpContext.Session.GetString("LoggedIn" + PTMagicConfiguration.GeneralSettings.Monitor.Port.ToString())) && PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected) + { bool redirectToLogin = true; - if (Request.Cookies.ContainsKey("PTMRememberMeKey")) { + if (Request.Cookies.ContainsKey("PTMRememberMeKey")) + { string rememberMeKey = Request.Cookies["PTMRememberMeKey"]; - if (!rememberMeKey.Equals("")) { + if (!rememberMeKey.Equals("")) + { string encryptedPassword = EncryptionHelper.Decrypt(Request.Cookies["PTMRememberMeKey"]); - if (encryptedPassword.Equals(PTMagicConfiguration.SecureSettings.MonitorPassword)) { + if (encryptedPassword.Equals(PTMagicConfiguration.SecureSettings.MonitorPassword)) + { HttpContext.Session.SetString("LoggedIn" + PTMagicConfiguration.GeneralSettings.Monitor.Port.ToString(), DateTime.Now.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'")); redirectToLogin = false; } } } - if (redirectToLogin) { + if (redirectToLogin) + { HttpContext.Response.Redirect(PTMagicConfiguration.GeneralSettings.Monitor.RootUrl + "Login"); } } diff --git a/Monitor/_Internal/BasePageModelSecureAJAX.cs b/Monitor/_Internal/BasePageModelSecureAJAX.cs index 7948288..21d2117 100644 --- a/Monitor/_Internal/BasePageModelSecureAJAX.cs +++ b/Monitor/_Internal/BasePageModelSecureAJAX.cs @@ -13,26 +13,34 @@ using Core.MarketAnalyzer; using Core.ProfitTrailer; using Microsoft.Extensions.Primitives; -namespace Monitor._Internal { +namespace Monitor._Internal +{ - public class BasePageModelSecureAJAX : BasePageModel { - public void Init() { + public class BasePageModelSecureAJAX : BasePageModel + { + public void Init() + { base.PreInit(); - if (String.IsNullOrEmpty(HttpContext.Session.GetString("LoggedIn" + PTMagicConfiguration.GeneralSettings.Monitor.Port.ToString())) && PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected) { + if (String.IsNullOrEmpty(HttpContext.Session.GetString("LoggedIn" + PTMagicConfiguration.GeneralSettings.Monitor.Port.ToString())) && PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected) + { bool redirectToLogin = true; - if (Request.Cookies.ContainsKey("PTMRememberMeKey")) { + if (Request.Cookies.ContainsKey("PTMRememberMeKey")) + { string rememberMeKey = Request.Cookies["PTMRememberMeKey"]; - if (!rememberMeKey.Equals("")) { + if (!rememberMeKey.Equals("")) + { string encryptedPassword = EncryptionHelper.Decrypt(Request.Cookies["PTMRememberMeKey"]); - if (encryptedPassword.Equals(PTMagicConfiguration.SecureSettings.MonitorPassword)) { + if (encryptedPassword.Equals(PTMagicConfiguration.SecureSettings.MonitorPassword)) + { HttpContext.Session.SetString("LoggedIn" + PTMagicConfiguration.GeneralSettings.Monitor.Port.ToString(), DateTime.Now.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'")); redirectToLogin = false; } } } - if (redirectToLogin) { + if (redirectToLogin) + { HttpContext.Response.Redirect(PTMagicConfiguration.GeneralSettings.Monitor.RootUrl + "_get/ReturnToLogin"); } } diff --git a/Monitor/wwwroot/assets/js/waves.js b/Monitor/wwwroot/assets/js/waves.js index ae55d90..8df0b48 100644 --- a/Monitor/wwwroot/assets/js/waves.js +++ b/Monitor/wwwroot/assets/js/waves.js @@ -336,7 +336,7 @@ Tabs /*! VelocityJS.org (1.2.2). (C) 2014 Julian Shapiro. MIT @license: en.wikipedia.org/wiki/MIT_License */ /*! VelocityJS.org jQuery Shim (1.0.1). (C) 2014 The jQuery Foundation. MIT @license: en.wikipedia.org/wiki/MIT_License. */ !function(e){function t(e){var t=e.length,r=$.type(e);return"function"===r||$.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===r||0===t||"number"==typeof t&&t>0&&t-1 in e}if(!e.jQuery){var $=function(e,t){return new $.fn.init(e,t)};$.isWindow=function(e){return null!=e&&e==e.window},$.type=function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?a[o.call(e)]||"object":typeof e},$.isArray=Array.isArray||function(e){return"array"===$.type(e)},$.isPlainObject=function(e){var t;if(!e||"object"!==$.type(e)||e.nodeType||$.isWindow(e))return!1;try{if(e.constructor&&!n.call(e,"constructor")&&!n.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}for(t in e);return void 0===t||n.call(e,t)},$.each=function(e,r,a){var n,o=0,i=e.length,s=t(e);if(a){if(s)for(;i>o&&(n=r.apply(e[o],a),n!==!1);o++);else for(o in e)if(n=r.apply(e[o],a),n===!1)break}else if(s)for(;i>o&&(n=r.call(e[o],o,e[o]),n!==!1);o++);else for(o in e)if(n=r.call(e[o],o,e[o]),n===!1)break;return e},$.data=function(e,t,a){if(void 0===a){var n=e[$.expando],o=n&&r[n];if(void 0===t)return o;if(o&&t in o)return o[t]}else if(void 0!==t){var n=e[$.expando]||(e[$.expando]=++$.uuid);return r[n]=r[n]||{},r[n][t]=a,a}},$.removeData=function(e,t){var a=e[$.expando],n=a&&r[a];n&&$.each(t,function(e,t){delete n[t]})},$.extend=function(){var e,t,r,a,n,o,i=arguments[0]||{},s=1,l=arguments.length,u=!1;for("boolean"==typeof i&&(u=i,i=arguments[s]||{},s++),"object"!=typeof i&&"function"!==$.type(i)&&(i={}),s===l&&(i=this,s--);l>s;s++)if(null!=(n=arguments[s]))for(a in n)e=i[a],r=n[a],i!==r&&(u&&r&&($.isPlainObject(r)||(t=$.isArray(r)))?(t?(t=!1,o=e&&$.isArray(e)?e:[]):o=e&&$.isPlainObject(e)?e:{},i[a]=$.extend(u,o,r)):void 0!==r&&(i[a]=r));return i},$.queue=function(e,r,a){function n(e,r){var a=r||[];return null!=e&&(t(Object(e))?!function(e,t){for(var r=+t.length,a=0,n=e.length;r>a;)e[n++]=t[a++];if(r!==r)for(;void 0!==t[a];)e[n++]=t[a++];return e.length=n,e}(a,"string"==typeof e?[e]:e):[].push.call(a,e)),a}if(e){r=(r||"fx")+"queue";var o=$.data(e,r);return a?(!o||$.isArray(a)?o=$.data(e,r,n(a)):o.push(a),o):o||[]}},$.dequeue=function(e,t){$.each(e.nodeType?[e]:e,function(e,r){t=t||"fx";var a=$.queue(r,t),n=a.shift();"inprogress"===n&&(n=a.shift()),n&&("fx"===t&&a.unshift("inprogress"),n.call(r,function(){$.dequeue(r,t)}))})},$.fn=$.prototype={init:function(e){if(e.nodeType)return this[0]=e,this;throw new Error("Not a DOM node.")},offset:function(){var t=this[0].getBoundingClientRect?this[0].getBoundingClientRect():{top:0,left:0};return{top:t.top+(e.pageYOffset||document.scrollTop||0)-(document.clientTop||0),left:t.left+(e.pageXOffset||document.scrollLeft||0)-(document.clientLeft||0)}},position:function(){function e(){for(var e=this.offsetParent||document;e&&"html"===!e.nodeType.toLowerCase&&"static"===e.style.position;)e=e.offsetParent;return e||document}var t=this[0],e=e.apply(t),r=this.offset(),a=/^(?:body|html)$/i.test(e.nodeName)?{top:0,left:0}:$(e).offset();return r.top-=parseFloat(t.style.marginTop)||0,r.left-=parseFloat(t.style.marginLeft)||0,e.style&&(a.top+=parseFloat(e.style.borderTopWidth)||0,a.left+=parseFloat(e.style.borderLeftWidth)||0),{top:r.top-a.top,left:r.left-a.left}}};var r={};$.expando="velocity"+(new Date).getTime(),$.uuid=0;for(var a={},n=a.hasOwnProperty,o=a.toString,i="Boolean Number String Function Array Date RegExp Object Error".split(" "),s=0;sn;++n){var o=u(r,e,a);if(0===o)return r;var i=l(r,e,a)-t;r-=i/o}return r}function p(){for(var t=0;b>t;++t)w[t]=l(t*x,e,a)}function f(t,r,n){var o,i,s=0;do i=r+(n-r)/2,o=l(i,e,a)-t,o>0?n=i:r=i;while(Math.abs(o)>h&&++s=y?c(t,s):0==l?s:f(t,r,r+x)}function g(){V=!0,(e!=r||a!=n)&&p()}var m=4,y=.001,h=1e-7,v=10,b=11,x=1/(b-1),S="Float32Array"in t;if(4!==arguments.length)return!1;for(var P=0;4>P;++P)if("number"!=typeof arguments[P]||isNaN(arguments[P])||!isFinite(arguments[P]))return!1;e=Math.min(e,1),a=Math.min(a,1),e=Math.max(e,0),a=Math.max(a,0);var w=S?new Float32Array(b):new Array(b),V=!1,C=function(t){return V||g(),e===r&&a===n?t:0===t?0:1===t?1:l(d(t),r,n)};C.getControlPoints=function(){return[{x:e,y:r},{x:a,y:n}]};var T="generateBezier("+[e,r,a,n]+")";return C.toString=function(){return T},C}function u(e,t){var r=e;return g.isString(e)?v.Easings[e]||(r=!1):r=g.isArray(e)&&1===e.length?s.apply(null,e):g.isArray(e)&&2===e.length?b.apply(null,e.concat([t])):g.isArray(e)&&4===e.length?l.apply(null,e):!1,r===!1&&(r=v.Easings[v.defaults.easing]?v.defaults.easing:h),r}function c(e){if(e){var t=(new Date).getTime(),r=v.State.calls.length;r>1e4&&(v.State.calls=n(v.State.calls));for(var o=0;r>o;o++)if(v.State.calls[o]){var s=v.State.calls[o],l=s[0],u=s[2],f=s[3],d=!!f,m=null;f||(f=v.State.calls[o][3]=t-16);for(var y=Math.min((t-f)/u.duration,1),h=0,b=l.length;b>h;h++){var S=l[h],w=S.element;if(i(w)){var V=!1;if(u.display!==a&&null!==u.display&&"none"!==u.display){if("flex"===u.display){var C=["-webkit-box","-moz-box","-ms-flexbox","-webkit-flex"];$.each(C,function(e,t){x.setPropertyValue(w,"display",t)})}x.setPropertyValue(w,"display",u.display)}u.visibility!==a&&"hidden"!==u.visibility&&x.setPropertyValue(w,"visibility",u.visibility);for(var T in S)if("element"!==T){var k=S[T],A,F=g.isString(k.easing)?v.Easings[k.easing]:k.easing;if(1===y)A=k.endValue;else{var E=k.endValue-k.startValue;if(A=k.startValue+E*F(y,u,E),!d&&A===k.currentValue)continue}if(k.currentValue=A,"tween"===T)m=A;else{if(x.Hooks.registered[T]){var j=x.Hooks.getRoot(T),H=i(w).rootPropertyValueCache[j];H&&(k.rootPropertyValue=H)}var N=x.setPropertyValue(w,T,k.currentValue+(0===parseFloat(A)?"":k.unitType),k.rootPropertyValue,k.scrollData);x.Hooks.registered[T]&&(i(w).rootPropertyValueCache[j]=x.Normalizations.registered[j]?x.Normalizations.registered[j]("extract",null,N[1]):N[1]),"transform"===N[0]&&(V=!0)}}u.mobileHA&&i(w).transformCache.translate3d===a&&(i(w).transformCache.translate3d="(0px, 0px, 0px)",V=!0),V&&x.flushTransformCache(w)}}u.display!==a&&"none"!==u.display&&(v.State.calls[o][2].display=!1),u.visibility!==a&&"hidden"!==u.visibility&&(v.State.calls[o][2].visibility=!1),u.progress&&u.progress.call(s[1],s[1],y,Math.max(0,f+u.duration-t),f,m),1===y&&p(o)}}v.State.isTicking&&P(c)}function p(e,t){if(!v.State.calls[e])return!1;for(var r=v.State.calls[e][0],n=v.State.calls[e][1],o=v.State.calls[e][2],s=v.State.calls[e][4],l=!1,u=0,c=r.length;c>u;u++){var p=r[u].element;if(t||o.loop||("none"===o.display&&x.setPropertyValue(p,"display",o.display),"hidden"===o.visibility&&x.setPropertyValue(p,"visibility",o.visibility)),o.loop!==!0&&($.queue(p)[1]===a||!/\.velocityQueueEntryFlag/i.test($.queue(p)[1]))&&i(p)){i(p).isAnimating=!1,i(p).rootPropertyValueCache={};var f=!1;$.each(x.Lists.transforms3D,function(e,t){var r=/^scale/.test(t)?1:0,n=i(p).transformCache[t];i(p).transformCache[t]!==a&&new RegExp("^\\("+r+"[^.]").test(n)&&(f=!0,delete i(p).transformCache[t])}),o.mobileHA&&(f=!0,delete i(p).transformCache.translate3d),f&&x.flushTransformCache(p),x.Values.removeClass(p,"velocity-animating")}if(!t&&o.complete&&!o.loop&&u===c-1)try{o.complete.call(n,n)}catch(d){setTimeout(function(){throw d},1)}s&&o.loop!==!0&&s(n),i(p)&&o.loop===!0&&!t&&($.each(i(p).tweensContainer,function(e,t){/^rotate/.test(e)&&360===parseFloat(t.endValue)&&(t.endValue=0,t.startValue=360),/^backgroundPosition/.test(e)&&100===parseFloat(t.endValue)&&"%"===t.unitType&&(t.endValue=0,t.startValue=100)}),v(p,"reverse",{loop:!0,delay:o.delay})),o.queue!==!1&&$.dequeue(p,o.queue)}v.State.calls[e]=!1;for(var g=0,m=v.State.calls.length;m>g;g++)if(v.State.calls[g]!==!1){l=!0;break}l===!1&&(v.State.isTicking=!1,delete v.State.calls,v.State.calls=[])}var f=function(){if(r.documentMode)return r.documentMode;for(var e=7;e>4;e--){var t=r.createElement("div");if(t.innerHTML="",t.getElementsByTagName("span").length)return t=null,e}return a}(),d=function(){var e=0;return t.webkitRequestAnimationFrame||t.mozRequestAnimationFrame||function(t){var r=(new Date).getTime(),a;return a=Math.max(0,16-(r-e)),e=r+a,setTimeout(function(){t(r+a)},a)}}(),g={isString:function(e){return"string"==typeof e},isArray:Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},isFunction:function(e){return"[object Function]"===Object.prototype.toString.call(e)},isNode:function(e){return e&&e.nodeType},isNodeList:function(e){return"object"==typeof e&&/^\[object (HTMLCollection|NodeList|Object)\]$/.test(Object.prototype.toString.call(e))&&e.length!==a&&(0===e.length||"object"==typeof e[0]&&e[0].nodeType>0)},isWrapped:function(e){return e&&(e.jquery||t.Zepto&&t.Zepto.zepto.isZ(e))},isSVG:function(e){return t.SVGElement&&e instanceof t.SVGElement},isEmptyObject:function(e){for(var t in e)return!1;return!0}},$,m=!1;if(e.fn&&e.fn.jquery?($=e,m=!0):$=t.Velocity.Utilities,8>=f&&!m)throw new Error("Velocity: IE8 and below require jQuery to be loaded before Velocity.");if(7>=f)return void(jQuery.fn.velocity=jQuery.fn.animate);var y=400,h="swing",v={State:{isMobile:/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),isAndroid:/Android/i.test(navigator.userAgent),isGingerbread:/Android 2\.3\.[3-7]/i.test(navigator.userAgent),isChrome:t.chrome,isFirefox:/Firefox/i.test(navigator.userAgent),prefixElement:r.createElement("div"),prefixMatches:{},scrollAnchor:null,scrollPropertyLeft:null,scrollPropertyTop:null,isTicking:!1,calls:[]},CSS:{},Utilities:$,Redirects:{},Easings:{},Promise:t.Promise,defaults:{queue:"",duration:y,easing:h,begin:a,complete:a,progress:a,display:a,visibility:a,loop:!1,delay:!1,mobileHA:!0,_cacheValues:!0},init:function(e){$.data(e,"velocity",{isSVG:g.isSVG(e),isAnimating:!1,computedStyle:null,tweensContainer:null,rootPropertyValueCache:{},transformCache:{}})},hook:null,mock:!1,version:{major:1,minor:2,patch:2},debug:!1};t.pageYOffset!==a?(v.State.scrollAnchor=t,v.State.scrollPropertyLeft="pageXOffset",v.State.scrollPropertyTop="pageYOffset"):(v.State.scrollAnchor=r.documentElement||r.body.parentNode||r.body,v.State.scrollPropertyLeft="scrollLeft",v.State.scrollPropertyTop="scrollTop");var b=function(){function e(e){return-e.tension*e.x-e.friction*e.v}function t(t,r,a){var n={x:t.x+a.dx*r,v:t.v+a.dv*r,tension:t.tension,friction:t.friction};return{dx:n.v,dv:e(n)}}function r(r,a){var n={dx:r.v,dv:e(r)},o=t(r,.5*a,n),i=t(r,.5*a,o),s=t(r,a,i),l=1/6*(n.dx+2*(o.dx+i.dx)+s.dx),u=1/6*(n.dv+2*(o.dv+i.dv)+s.dv);return r.x=r.x+l*a,r.v=r.v+u*a,r}return function a(e,t,n){var o={x:-1,v:0,tension:null,friction:null},i=[0],s=0,l=1e-4,u=.016,c,p,f;for(e=parseFloat(e)||500,t=parseFloat(t)||20,n=n||null,o.tension=e,o.friction=t,c=null!==n,c?(s=a(e,t),p=s/n*u):p=u;;)if(f=r(f||o,p),i.push(1+f.x),s+=16,!(Math.abs(f.x)>l&&Math.abs(f.v)>l))break;return c?function(e){return i[e*(i.length-1)|0]}:s}}();v.Easings={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},spring:function(e){return 1-Math.cos(4.5*e*Math.PI)*Math.exp(6*-e)}},$.each([["ease",[.25,.1,.25,1]],["ease-in",[.42,0,1,1]],["ease-out",[0,0,.58,1]],["ease-in-out",[.42,0,.58,1]],["easeInSine",[.47,0,.745,.715]],["easeOutSine",[.39,.575,.565,1]],["easeInOutSine",[.445,.05,.55,.95]],["easeInQuad",[.55,.085,.68,.53]],["easeOutQuad",[.25,.46,.45,.94]],["easeInOutQuad",[.455,.03,.515,.955]],["easeInCubic",[.55,.055,.675,.19]],["easeOutCubic",[.215,.61,.355,1]],["easeInOutCubic",[.645,.045,.355,1]],["easeInQuart",[.895,.03,.685,.22]],["easeOutQuart",[.165,.84,.44,1]],["easeInOutQuart",[.77,0,.175,1]],["easeInQuint",[.755,.05,.855,.06]],["easeOutQuint",[.23,1,.32,1]],["easeInOutQuint",[.86,0,.07,1]],["easeInExpo",[.95,.05,.795,.035]],["easeOutExpo",[.19,1,.22,1]],["easeInOutExpo",[1,0,0,1]],["easeInCirc",[.6,.04,.98,.335]],["easeOutCirc",[.075,.82,.165,1]],["easeInOutCirc",[.785,.135,.15,.86]]],function(e,t){v.Easings[t[0]]=l.apply(null,t[1])});var x=v.CSS={RegEx:{isHex:/^#([A-f\d]{3}){1,2}$/i,valueUnwrap:/^[A-z]+\((.*)\)$/i,wrappedValueAlreadyExtracted:/[0-9.]+ [0-9.]+ [0-9.]+( [0-9.]+)?/,valueSplit:/([A-z]+\(.+\))|(([A-z0-9#-.]+?)(?=\s|$))/gi},Lists:{colors:["fill","stroke","stopColor","color","backgroundColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor","outlineColor"],transformsBase:["translateX","translateY","scale","scaleX","scaleY","skewX","skewY","rotateZ"],transforms3D:["transformPerspective","translateZ","scaleZ","rotateX","rotateY"]},Hooks:{templates:{textShadow:["Color X Y Blur","black 0px 0px 0px"],boxShadow:["Color X Y Blur Spread","black 0px 0px 0px 0px"],clip:["Top Right Bottom Left","0px 0px 0px 0px"],backgroundPosition:["X Y","0% 0%"],transformOrigin:["X Y Z","50% 50% 0px"],perspectiveOrigin:["X Y","50% 50%"]},registered:{},register:function(){for(var e=0;e=f)switch(e){case"name":return"filter";case"extract":var a=r.toString().match(/alpha\(opacity=(.*)\)/i);return r=a?a[1]/100:1;case"inject":return t.style.zoom=1,parseFloat(r)>=1?"":"alpha(opacity="+parseInt(100*parseFloat(r),10)+")"}else switch(e){case"name":return"opacity";case"extract":return r;case"inject":return r}}},register:function(){9>=f||v.State.isGingerbread||(x.Lists.transformsBase=x.Lists.transformsBase.concat(x.Lists.transforms3D));for(var e=0;en&&(n=1),o=!/(\d)$/i.test(n);break;case"skew":o=!/(deg|\d)$/i.test(n);break;case"rotate":o=!/(deg|\d)$/i.test(n)}return o||(i(r).transformCache[t]="("+n+")"),i(r).transformCache[t]}}}();for(var e=0;e=f||3!==o.split(" ").length||(o+=" 1"),o;case"inject":return 8>=f?4===n.split(" ").length&&(n=n.split(/\s+/).slice(0,3).join(" ")):3===n.split(" ").length&&(n+=" 1"),(8>=f?"rgb":"rgba")+"("+n.replace(/\s+/g,",").replace(/\.(\d)+(?=,)/g,"")+")"}}}()}},Names:{camelCase:function(e){return e.replace(/-(\w)/g,function(e,t){return t.toUpperCase()})},SVGAttribute:function(e){var t="width|height|x|y|cx|cy|r|rx|ry|x1|x2|y1|y2";return(f||v.State.isAndroid&&!v.State.isChrome)&&(t+="|transform"),new RegExp("^("+t+")$","i").test(e)},prefixCheck:function(e){if(v.State.prefixMatches[e])return[v.State.prefixMatches[e],!0];for(var t=["","Webkit","Moz","ms","O"],r=0,a=t.length;a>r;r++){var n;if(n=0===r?e:t[r]+e.replace(/^\w/,function(e){return e.toUpperCase()}),g.isString(v.State.prefixElement.style[n]))return v.State.prefixMatches[e]=n,[n,!0]}return[e,!1]}},Values:{hexToRgb:function(e){var t=/^#?([a-f\d])([a-f\d])([a-f\d])$/i,r=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,a;return e=e.replace(t,function(e,t,r,a){return t+t+r+r+a+a}),a=r.exec(e),a?[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],16)]:[0,0,0]},isCSSNullValue:function(e){return 0==e||/^(none|auto|transparent|(rgba\(0, ?0, ?0, ?0\)))$/i.test(e)},getUnitType:function(e){return/^(rotate|skew)/i.test(e)?"deg":/(^(scale|scaleX|scaleY|scaleZ|alpha|flexGrow|flexHeight|zIndex|fontWeight)$)|((opacity|red|green|blue|alpha)$)/i.test(e)?"":"px"},getDisplayType:function(e){var t=e&&e.tagName.toString().toLowerCase();return/^(b|big|i|small|tt|abbr|acronym|cite|code|dfn|em|kbd|strong|samp|var|a|bdo|br|img|map|object|q|script|span|sub|sup|button|input|label|select|textarea)$/i.test(t)?"inline":/^(li)$/i.test(t)?"list-item":/^(tr)$/i.test(t)?"table-row":/^(table)$/i.test(t)?"table":/^(tbody)$/i.test(t)?"table-row-group":"block"},addClass:function(e,t){e.classList?e.classList.add(t):e.className+=(e.className.length?" ":"")+t},removeClass:function(e,t){e.classList?e.classList.remove(t):e.className=e.className.toString().replace(new RegExp("(^|\\s)"+t.split(" ").join("|")+"(\\s|$)","gi")," ")}},getPropertyValue:function(e,r,n,o){function s(e,r){function n(){u&&x.setPropertyValue(e,"display","none")}var l=0;if(8>=f)l=$.css(e,r);else{var u=!1;if(/^(width|height)$/.test(r)&&0===x.getPropertyValue(e,"display")&&(u=!0,x.setPropertyValue(e,"display",x.Values.getDisplayType(e))),!o){if("height"===r&&"border-box"!==x.getPropertyValue(e,"boxSizing").toString().toLowerCase()){var c=e.offsetHeight-(parseFloat(x.getPropertyValue(e,"borderTopWidth"))||0)-(parseFloat(x.getPropertyValue(e,"borderBottomWidth"))||0)-(parseFloat(x.getPropertyValue(e,"paddingTop"))||0)-(parseFloat(x.getPropertyValue(e,"paddingBottom"))||0);return n(),c}if("width"===r&&"border-box"!==x.getPropertyValue(e,"boxSizing").toString().toLowerCase()){var p=e.offsetWidth-(parseFloat(x.getPropertyValue(e,"borderLeftWidth"))||0)-(parseFloat(x.getPropertyValue(e,"borderRightWidth"))||0)-(parseFloat(x.getPropertyValue(e,"paddingLeft"))||0)-(parseFloat(x.getPropertyValue(e,"paddingRight"))||0);return n(),p}}var d;d=i(e)===a?t.getComputedStyle(e,null):i(e).computedStyle?i(e).computedStyle:i(e).computedStyle=t.getComputedStyle(e,null),"borderColor"===r&&(r="borderTopColor"),l=9===f&&"filter"===r?d.getPropertyValue(r):d[r],(""===l||null===l)&&(l=e.style[r]),n()}if("auto"===l&&/^(top|right|bottom|left)$/i.test(r)){var g=s(e,"position");("fixed"===g||"absolute"===g&&/top|left/i.test(r))&&(l=$(e).position()[r]+"px")}return l}var l;if(x.Hooks.registered[r]){var u=r,c=x.Hooks.getRoot(u);n===a&&(n=x.getPropertyValue(e,x.Names.prefixCheck(c)[0])),x.Normalizations.registered[c]&&(n=x.Normalizations.registered[c]("extract",e,n)),l=x.Hooks.extractValue(u,n)}else if(x.Normalizations.registered[r]){var p,d;p=x.Normalizations.registered[r]("name",e),"transform"!==p&&(d=s(e,x.Names.prefixCheck(p)[0]),x.Values.isCSSNullValue(d)&&x.Hooks.templates[r]&&(d=x.Hooks.templates[r][1])),l=x.Normalizations.registered[r]("extract",e,d)}if(!/^[\d-]/.test(l))if(i(e)&&i(e).isSVG&&x.Names.SVGAttribute(r))if(/^(height|width)$/i.test(r))try{l=e.getBBox()[r]}catch(g){l=0}else l=e.getAttribute(r);else l=s(e,x.Names.prefixCheck(r)[0]);return x.Values.isCSSNullValue(l)&&(l=0),v.debug>=2&&console.log("Get "+r+": "+l),l},setPropertyValue:function(e,r,a,n,o){var s=r;if("scroll"===r)o.container?o.container["scroll"+o.direction]=a:"Left"===o.direction?t.scrollTo(a,o.alternateValue):t.scrollTo(o.alternateValue,a);else if(x.Normalizations.registered[r]&&"transform"===x.Normalizations.registered[r]("name",e))x.Normalizations.registered[r]("inject",e,a),s="transform",a=i(e).transformCache[r];else{if(x.Hooks.registered[r]){var l=r,u=x.Hooks.getRoot(r);n=n||x.getPropertyValue(e,u),a=x.Hooks.injectValue(l,a,n),r=u}if(x.Normalizations.registered[r]&&(a=x.Normalizations.registered[r]("inject",e,a),r=x.Normalizations.registered[r]("name",e)),s=x.Names.prefixCheck(r)[0],8>=f)try{e.style[s]=a}catch(c){v.debug&&console.log("Browser does not support ["+a+"] for ["+s+"]")}else i(e)&&i(e).isSVG&&x.Names.SVGAttribute(r)?e.setAttribute(r,a):e.style[s]=a;v.debug>=2&&console.log("Set "+r+" ("+s+"): "+a)}return[s,a]},flushTransformCache:function(e){function t(t){return parseFloat(x.getPropertyValue(e,t))}var r="";if((f||v.State.isAndroid&&!v.State.isChrome)&&i(e).isSVG){var a={translate:[t("translateX"),t("translateY")],skewX:[t("skewX")],skewY:[t("skewY")],scale:1!==t("scale")?[t("scale"),t("scale")]:[t("scaleX"),t("scaleY")],rotate:[t("rotateZ"),0,0]};$.each(i(e).transformCache,function(e){/^translate/i.test(e)?e="translate":/^scale/i.test(e)?e="scale":/^rotate/i.test(e)&&(e="rotate"),a[e]&&(r+=e+"("+a[e].join(" ")+") ",delete a[e])})}else{var n,o;$.each(i(e).transformCache,function(t){return n=i(e).transformCache[t],"transformPerspective"===t?(o=n,!0):(9===f&&"rotateZ"===t&&(t="rotate"),void(r+=t+n+" "))}),o&&(r="perspective"+o+" "+r)}x.setPropertyValue(e,"transform",r)}};x.Hooks.register(),x.Normalizations.register(),v.hook=function(e,t,r){var n=a;return e=o(e),$.each(e,function(e,o){if(i(o)===a&&v.init(o),r===a)n===a&&(n=v.CSS.getPropertyValue(o,t));else{var s=v.CSS.setPropertyValue(o,t,r);"transform"===s[0]&&v.CSS.flushTransformCache(o),n=s}}),n};var S=function(){function e(){return l?T.promise||null:f}function n(){function e(e){function p(e,t){var r=a,i=a,s=a;return g.isArray(e)?(r=e[0],!g.isArray(e[1])&&/^[\d-]/.test(e[1])||g.isFunction(e[1])||x.RegEx.isHex.test(e[1])?s=e[1]:(g.isString(e[1])&&!x.RegEx.isHex.test(e[1])||g.isArray(e[1]))&&(i=t?e[1]:u(e[1],o.duration),e[2]!==a&&(s=e[2]))):r=e,t||(i=i||o.easing),g.isFunction(r)&&(r=r.call(n,w,P)),g.isFunction(s)&&(s=s.call(n,w,P)),[r||0,i,s]}function f(e,t){var r,a;return a=(t||"0").toString().toLowerCase().replace(/[%A-z]+$/,function(e){return r=e,""}),r||(r=x.Values.getUnitType(e)),[a,r]}function d(){var e={myParent:n.parentNode||r.body,position:x.getPropertyValue(n,"position"),fontSize:x.getPropertyValue(n,"fontSize")},a=e.position===N.lastPosition&&e.myParent===N.lastParent,o=e.fontSize===N.lastFontSize;N.lastParent=e.myParent,N.lastPosition=e.position,N.lastFontSize=e.fontSize;var s=100,l={};if(o&&a)l.emToPx=N.lastEmToPx,l.percentToPxWidth=N.lastPercentToPxWidth,l.percentToPxHeight=N.lastPercentToPxHeight;else{var u=i(n).isSVG?r.createElementNS("http://www.w3.org/2000/svg","rect"):r.createElement("div");v.init(u),e.myParent.appendChild(u),$.each(["overflow","overflowX","overflowY"],function(e,t){v.CSS.setPropertyValue(u,t,"hidden")}),v.CSS.setPropertyValue(u,"position",e.position),v.CSS.setPropertyValue(u,"fontSize",e.fontSize),v.CSS.setPropertyValue(u,"boxSizing","content-box"),$.each(["minWidth","maxWidth","width","minHeight","maxHeight","height"],function(e,t){v.CSS.setPropertyValue(u,t,s+"%")}),v.CSS.setPropertyValue(u,"paddingLeft",s+"em"),l.percentToPxWidth=N.lastPercentToPxWidth=(parseFloat(x.getPropertyValue(u,"width",null,!0))||1)/s,l.percentToPxHeight=N.lastPercentToPxHeight=(parseFloat(x.getPropertyValue(u,"height",null,!0))||1)/s,l.emToPx=N.lastEmToPx=(parseFloat(x.getPropertyValue(u,"paddingLeft"))||1)/s,e.myParent.removeChild(u)}return null===N.remToPx&&(N.remToPx=parseFloat(x.getPropertyValue(r.body,"fontSize"))||16),null===N.vwToPx&&(N.vwToPx=parseFloat(t.innerWidth)/100,N.vhToPx=parseFloat(t.innerHeight)/100),l.remToPx=N.remToPx,l.vwToPx=N.vwToPx,l.vhToPx=N.vhToPx,v.debug>=1&&console.log("Unit ratios: "+JSON.stringify(l),n),l}if(o.begin&&0===w)try{o.begin.call(m,m)}catch(y){setTimeout(function(){throw y},1)}if("scroll"===k){var S=/^x$/i.test(o.axis)?"Left":"Top",V=parseFloat(o.offset)||0,C,A,F;o.container?g.isWrapped(o.container)||g.isNode(o.container)?(o.container=o.container[0]||o.container,C=o.container["scroll"+S],F=C+$(n).position()[S.toLowerCase()]+V):o.container=null:(C=v.State.scrollAnchor[v.State["scrollProperty"+S]],A=v.State.scrollAnchor[v.State["scrollProperty"+("Left"===S?"Top":"Left")]],F=$(n).offset()[S.toLowerCase()]+V),s={scroll:{rootPropertyValue:!1,startValue:C,currentValue:C,endValue:F,unitType:"",easing:o.easing,scrollData:{container:o.container,direction:S,alternateValue:A}},element:n},v.debug&&console.log("tweensContainer (scroll): ",s.scroll,n)}else if("reverse"===k){if(!i(n).tweensContainer)return void $.dequeue(n,o.queue);"none"===i(n).opts.display&&(i(n).opts.display="auto"),"hidden"===i(n).opts.visibility&&(i(n).opts.visibility="visible"),i(n).opts.loop=!1,i(n).opts.begin=null,i(n).opts.complete=null,b.easing||delete o.easing,b.duration||delete o.duration,o=$.extend({},i(n).opts,o);var E=$.extend(!0,{},i(n).tweensContainer);for(var j in E)if("element"!==j){var H=E[j].startValue;E[j].startValue=E[j].currentValue=E[j].endValue,E[j].endValue=H,g.isEmptyObject(b)||(E[j].easing=o.easing),v.debug&&console.log("reverse tweensContainer ("+j+"): "+JSON.stringify(E[j]),n)}s=E}else if("start"===k){var E;i(n).tweensContainer&&i(n).isAnimating===!0&&(E=i(n).tweensContainer),$.each(h,function(e,t){if(RegExp("^"+x.Lists.colors.join("$|^")+"$").test(e)){var r=p(t,!0),n=r[0],o=r[1],i=r[2];if(x.RegEx.isHex.test(n)){for(var s=["Red","Green","Blue"],l=x.Values.hexToRgb(n),u=i?x.Values.hexToRgb(i):a,c=0;cO;O++){var z={delay:F.delay,progress:F.progress};O===R-1&&(z.display=F.display,z.visibility=F.visibility,z.complete=F.complete),S(m,"reverse",z)}return e()}};v=$.extend(S,v),v.animate=S;var P=t.requestAnimationFrame||d;return v.State.isMobile||r.hidden===a||r.addEventListener("visibilitychange",function(){r.hidden?(P=function(e){return setTimeout(function(){e(!0)},16)},c()):P=t.requestAnimationFrame||d}),e.Velocity=v,e!==t&&(e.fn.velocity=S,e.fn.velocity.defaults=v.defaults),$.each(["Down","Up"],function(e,t){v.Redirects["slide"+t]=function(e,r,n,o,i,s){var l=$.extend({},r),u=l.begin,c=l.complete,p={height:"",marginTop:"",marginBottom:"",paddingTop:"",paddingBottom:""},f={};l.display===a&&(l.display="Down"===t?"inline"===v.CSS.Values.getDisplayType(e)?"inline-block":"block":"none"),l.begin=function(){u&&u.call(i,i);for(var r in p){f[r]=e.style[r];var a=v.CSS.getPropertyValue(e,r);p[r]="Down"===t?[a,0]:[0,a]}f.overflow=e.style.overflow,e.style.overflow="hidden"},l.complete=function(){for(var t in f)e.style[t]=f[t];c&&c.call(i,i),s&&s.resolver(i)},v(e,p,l)}}),$.each(["In","Out"],function(e,t){v.Redirects["fade"+t]=function(e,r,n,o,i,s){var l=$.extend({},r),u={opacity:"In"===t?1:0},c=l.complete;l.complete=n!==o-1?l.begin=null:function(){c&&c.call(i,i),s&&s.resolver(i)},l.display===a&&(l.display="In"===t?"auto":"none"),v(this,u,l)}}),v}(window.jQuery||window.Zepto||window,window,document)});;!function(a,b,c,d){"use strict";function k(a,b,c){return setTimeout(q(a,c),b)}function l(a,b,c){return Array.isArray(a)?(m(a,c[b],c),!0):!1}function m(a,b,c){var e;if(a)if(a.forEach)a.forEach(b,c);else if(a.length!==d)for(e=0;e-1}function x(a){return a.trim().split(/\s+/g)}function y(a,b,c){if(a.indexOf&&!c)return a.indexOf(b);for(var d=0;dc[b]}):d.sort()),d}function B(a,b){for(var c,f,g=b[0].toUpperCase()+b.slice(1),h=0;h1&&!c.firstMultiple?c.firstMultiple=gb(b):1===e&&(c.firstMultiple=!1);var f=c.firstInput,g=c.firstMultiple,h=g?g.center:f.center,i=b.center=hb(d);b.timeStamp=j(),b.deltaTime=b.timeStamp-f.timeStamp,b.angle=lb(h,i),b.distance=kb(h,i),eb(c,b),b.offsetDirection=jb(b.deltaX,b.deltaY),b.scale=g?nb(g.pointers,d):1,b.rotation=g?mb(g.pointers,d):0,fb(c,b);var k=a.element;v(b.srcEvent.target,k)&&(k=b.srcEvent.target),b.target=k}function eb(a,b){var c=b.center,d=a.offsetDelta||{},e=a.prevDelta||{},f=a.prevInput||{};(b.eventType===O||f.eventType===Q)&&(e=a.prevDelta={x:f.deltaX||0,y:f.deltaY||0},d=a.offsetDelta={x:c.x,y:c.y}),b.deltaX=e.x+(c.x-d.x),b.deltaY=e.y+(c.y-d.y)}function fb(a,b){var f,g,h,j,c=a.lastInterval||b,e=b.timeStamp-c.timeStamp;if(b.eventType!=R&&(e>N||c.velocity===d)){var k=c.deltaX-b.deltaX,l=c.deltaY-b.deltaY,m=ib(e,k,l);g=m.x,h=m.y,f=i(m.x)>i(m.y)?m.x:m.y,j=jb(k,l),a.lastInterval=b}else f=c.velocity,g=c.velocityX,h=c.velocityY,j=c.direction;b.velocity=f,b.velocityX=g,b.velocityY=h,b.direction=j}function gb(a){for(var b=[],c=0;ce;)c+=a[e].clientX,d+=a[e].clientY,e++;return{x:h(c/b),y:h(d/b)}}function ib(a,b,c){return{x:b/a||0,y:c/a||0}}function jb(a,b){return a===b?S:i(a)>=i(b)?a>0?T:U:b>0?V:W}function kb(a,b,c){c||(c=$);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return Math.sqrt(d*d+e*e)}function lb(a,b,c){c||(c=$);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return 180*Math.atan2(e,d)/Math.PI}function mb(a,b){return lb(b[1],b[0],_)-lb(a[1],a[0],_)}function nb(a,b){return kb(b[0],b[1],_)/kb(a[0],a[1],_)}function rb(){this.evEl=pb,this.evWin=qb,this.allow=!0,this.pressed=!1,ab.apply(this,arguments)}function wb(){this.evEl=ub,this.evWin=vb,ab.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function Ab(){this.evTarget=yb,this.evWin=zb,this.started=!1,ab.apply(this,arguments)}function Bb(a,b){var c=z(a.touches),d=z(a.changedTouches);return b&(Q|R)&&(c=A(c.concat(d),"identifier",!0)),[c,d]}function Eb(){this.evTarget=Db,this.targetIds={},ab.apply(this,arguments)}function Fb(a,b){var c=z(a.touches),d=this.targetIds;if(b&(O|P)&&1===c.length)return d[c[0].identifier]=!0,[c,c];var e,f,g=z(a.changedTouches),h=[],i=this.target;if(f=c.filter(function(a){return v(a.target,i)}),b===O)for(e=0;eh&&(b.push(a),h=b.length-1):e&(Q|R)&&(c=!0),0>h||(b[h]=a,this.callback(this.manager,e,{pointers:b,changedPointers:[a],pointerType:f,srcEvent:a}),c&&b.splice(h,1))}});var xb={touchstart:O,touchmove:P,touchend:Q,touchcancel:R},yb="touchstart",zb="touchstart touchmove touchend touchcancel";p(Ab,ab,{handler:function(a){var b=xb[a.type];if(b===O&&(this.started=!0),this.started){var c=Bb.call(this,a,b);b&(Q|R)&&0===c[0].length-c[1].length&&(this.started=!1),this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:J,srcEvent:a})}}});var Cb={touchstart:O,touchmove:P,touchend:Q,touchcancel:R},Db="touchstart touchmove touchend touchcancel";p(Eb,ab,{handler:function(a){var b=Cb[a.type],c=Fb.call(this,a,b);c&&this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:J,srcEvent:a})}}),p(Gb,ab,{handler:function(a,b,c){var d=c.pointerType==J,e=c.pointerType==L;if(d)this.mouse.allow=!1;else if(e&&!this.mouse.allow)return;b&(Q|R)&&(this.mouse.allow=!0),this.callback(a,b,c)},destroy:function(){this.touch.destroy(),this.mouse.destroy()}});var Hb=B(f.style,"touchAction"),Ib=Hb!==d,Jb="compute",Kb="auto",Lb="manipulation",Mb="none",Nb="pan-x",Ob="pan-y";Pb.prototype={set:function(a){a==Jb&&(a=this.compute()),Ib&&(this.manager.element.style[Hb]=a),this.actions=a.toLowerCase().trim()},update:function(){this.set(this.manager.options.touchAction)},compute:function(){var a=[];return m(this.manager.recognizers,function(b){r(b.options.enable,[b])&&(a=a.concat(b.getTouchAction()))}),Qb(a.join(" "))},preventDefaults:function(a){if(!Ib){var b=a.srcEvent,c=a.offsetDirection;if(this.manager.session.prevented)return b.preventDefault(),void 0;var d=this.actions,e=w(d,Mb),f=w(d,Ob),g=w(d,Nb);return e||f&&c&X||g&&c&Y?this.preventSrc(b):void 0}},preventSrc:function(a){this.manager.session.prevented=!0,a.preventDefault()}};var Rb=1,Sb=2,Tb=4,Ub=8,Vb=Ub,Wb=16,Xb=32;Yb.prototype={defaults:{},set:function(a){return n(this.options,a),this.manager&&this.manager.touchAction.update(),this},recognizeWith:function(a){if(l(a,"recognizeWith",this))return this;var b=this.simultaneous;return a=_b(a,this),b[a.id]||(b[a.id]=a,a.recognizeWith(this)),this},dropRecognizeWith:function(a){return l(a,"dropRecognizeWith",this)?this:(a=_b(a,this),delete this.simultaneous[a.id],this)},requireFailure:function(a){if(l(a,"requireFailure",this))return this;var b=this.requireFail;return a=_b(a,this),-1===y(b,a)&&(b.push(a),a.requireFailure(this)),this},dropRequireFailure:function(a){if(l(a,"dropRequireFailure",this))return this;a=_b(a,this);var b=y(this.requireFail,a);return b>-1&&this.requireFail.splice(b,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(a){return!!this.simultaneous[a.id]},emit:function(a){function d(d){b.manager.emit(b.options.event+(d?Zb(c):""),a)}var b=this,c=this.state;Ub>c&&d(!0),d(),c>=Ub&&d(!0)},tryEmit:function(a){return this.canEmit()?this.emit(a):(this.state=Xb,void 0)},canEmit:function(){for(var a=0;af?T:U,c=f!=this.pX,d=Math.abs(a.deltaX)):(e=0===g?S:0>g?V:W,c=g!=this.pY,d=Math.abs(a.deltaY))),a.direction=e,c&&d>b.threshold&&e&b.direction},attrTest:function(a){return ac.prototype.attrTest.call(this,a)&&(this.state&Sb||!(this.state&Sb)&&this.directionTest(a))},emit:function(a){this.pX=a.deltaX,this.pY=a.deltaY;var b=$b(a.direction);b&&this.manager.emit(this.options.event+b,a),this._super.emit.call(this,a)}}),p(cc,ac,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[Mb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.scale-1)>this.options.threshold||this.state&Sb)},emit:function(a){if(this._super.emit.call(this,a),1!==a.scale){var b=a.scale<1?"in":"out";this.manager.emit(this.options.event+b,a)}}}),p(dc,Yb,{defaults:{event:"press",pointers:1,time:500,threshold:5},getTouchAction:function(){return[Kb]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distanceb.time;if(this._input=a,!d||!c||a.eventType&(Q|R)&&!e)this.reset();else if(a.eventType&O)this.reset(),this._timer=k(function(){this.state=Vb,this.tryEmit()},b.time,this);else if(a.eventType&Q)return Vb;return Xb},reset:function(){clearTimeout(this._timer)},emit:function(a){this.state===Vb&&(a&&a.eventType&Q?this.manager.emit(this.options.event+"up",a):(this._input.timeStamp=j(),this.manager.emit(this.options.event,this._input)))}}),p(ec,ac,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[Mb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.rotation)>this.options.threshold||this.state&Sb)}}),p(fc,ac,{defaults:{event:"swipe",threshold:10,velocity:.65,direction:X|Y,pointers:1},getTouchAction:function(){return bc.prototype.getTouchAction.call(this)},attrTest:function(a){var c,b=this.options.direction;return b&(X|Y)?c=a.velocity:b&X?c=a.velocityX:b&Y&&(c=a.velocityY),this._super.attrTest.call(this,a)&&b&a.direction&&a.distance>this.options.threshold&&i(c)>this.options.velocity&&a.eventType&Q},emit:function(a){var b=$b(a.direction);b&&this.manager.emit(this.options.event+b,a),this.manager.emit(this.options.event,a)}}),p(gc,Yb,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:2,posThreshold:10},getTouchAction:function(){return[Lb]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distanceO;O++){var z={delay:F.delay,progress:F.progress};O===R-1&&(z.display=F.display,z.visibility=F.visibility,z.complete=F.complete),S(m,"reverse",z)}return e()}};v=$.extend(S,v),v.animate=S;var P=t.requestAnimationFrame||d;return v.State.isMobile||r.hidden===a||r.addEventListener("visibilitychange",function(){r.hidden?(P=function(e){return setTimeout(function(){e(!0)},16)},c()):P=t.requestAnimationFrame||d}),e.Velocity=v,e!==t&&(e.fn.velocity=S,e.fn.velocity.defaults=v.defaults),$.each(["Down","Up"],function(e,t){v.Redirects["slide"+t]=function(e,r,n,o,i,s){var l=$.extend({},r),u=l.begin,c=l.complete,p={height:"",marginTop:"",marginBottom:"",paddingTop:"",paddingBottom:""},f={};l.display===a&&(l.display="Down"===t?"inline"===v.CSS.Values.getDisplayType(e)?"inline-block":"block":"none"),l.begin=function(){u&&u.call(i,i);for(var r in p){f[r]=e.style[r];var a=v.CSS.getPropertyValue(e,r);p[r]="Down"===t?[a,0]:[0,a]}f.overflow=e.style.overflow,e.style.overflow="hidden"},l.complete=function(){for(var t in f)e.style[t]=f[t];c&&c.call(i,i),s&&s.resolver(i)},v(e,p,l)}}),$.each(["In","Out"],function(e,t){v.Redirects["fade"+t]=function(e,r,n,o,i,s){var l=$.extend({},r),u={opacity:"In"===t?1:0},c=l.complete;l.complete=n!==o-1?l.begin=null:function(){c&&c.call(i,i),s&&s.resolver(i)},l.display===a&&(l.display="In"===t?"auto":"none"),v(this,u,l)}}),v}(window.jQuery||window.Zepto||window,window,document)});;!function(a,b,c,d){"use strict";function k(a,b,c){return setTimeout(q(a,c),b)}function l(a,b,c){return Array.isArray(a)?(m(a,c[b],c),!0):!1}function m(a,b,c){var e;if(a)if(a.forEach)a.forEach(b,c);else if(a.length!==d)for(e=0;e-1}function x(a){return a.trim().split(/\s+/g)}function y(a,b,c){if(a.indexOf&&!c)return a.indexOf(b);for(var d=0;dc[b]}):d.sort()),d}function B(a,b){for(var c,f,g=b[0].toUpperCase()+b.slice(1),h=0;h1&&!c.firstMultiple?c.firstMultiple=gb(b):1===e&&(c.firstMultiple=!1);var f=c.firstInput,g=c.firstMultiple,h=g?g.center:f.center,i=b.center=hb(d);b.timeStamp=j(),b.deltaTime=b.timeStamp-f.timeStamp,b.angle=lb(h,i),b.distance=kb(h,i),eb(c,b),b.offsetDirection=jb(b.deltaX,b.deltaY),b.scale=g?nb(g.pointers,d):1,b.rotation=g?mb(g.pointers,d):0,fb(c,b);var k=a.element;v(b.srcEvent.target,k)&&(k=b.srcEvent.target),b.target=k}function eb(a,b){var c=b.center,d=a.offsetDelta||{},e=a.prevDelta||{},f=a.prevInput||{};(b.eventType===O||f.eventType===Q)&&(e=a.prevDelta={x:f.deltaX||0,y:f.deltaY||0},d=a.offsetDelta={x:c.x,y:c.y}),b.deltaX=e.x+(c.x-d.x),b.deltaY=e.y+(c.y-d.y)}function fb(a,b){var f,g,h,j,c=a.lastInterval||b,e=b.timeStamp-c.timeStamp;if(b.eventType!=R&&(e>N||c.velocity===d)){var k=c.deltaX-b.deltaX,l=c.deltaY-b.deltaY,m=ib(e,k,l);g=m.x,h=m.y,f=i(m.x)>i(m.y)?m.x:m.y,j=jb(k,l),a.lastInterval=b}else f=c.velocity,g=c.velocityX,h=c.velocityY,j=c.direction;b.velocity=f,b.velocityX=g,b.velocityY=h,b.direction=j}function gb(a){for(var b=[],c=0;ce;)c+=a[e].clientX,d+=a[e].clientY,e++;return{x:h(c/b),y:h(d/b)}}function ib(a,b,c){return{x:b/a||0,y:c/a||0}}function jb(a,b){return a===b?S:i(a)>=i(b)?a>0?T:U:b>0?V:W}function kb(a,b,c){c||(c=$);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return Math.sqrt(d*d+e*e)}function lb(a,b,c){c||(c=$);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return 180*Math.atan2(e,d)/Math.PI}function mb(a,b){return lb(b[1],b[0],_)-lb(a[1],a[0],_)}function nb(a,b){return kb(b[0],b[1],_)/kb(a[0],a[1],_)}function rb(){this.evEl=pb,this.evWin=qb,this.allow=!0,this.pressed=!1,ab.apply(this,arguments)}function wb(){this.evEl=ub,this.evWin=vb,ab.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function Ab(){this.evTarget=yb,this.evWin=zb,this.started=!1,ab.apply(this,arguments)}function Bb(a,b){var c=z(a.touches),d=z(a.changedTouches);return b&(Q|R)&&(c=A(c.concat(d),"identifier",!0)),[c,d]}function Eb(){this.evTarget=Db,this.targetIds={},ab.apply(this,arguments)}function Fb(a,b){var c=z(a.touches),d=this.targetIds;if(b&(O|P)&&1===c.length)return d[c[0].identifier]=!0,[c,c];var e,f,g=z(a.changedTouches),h=[],i=this.target;if(f=c.filter(function(a){return v(a.target,i)}),b===O)for(e=0;eh&&(b.push(a),h=b.length-1):e&(Q|R)&&(c=!0),0>h||(b[h]=a,this.callback(this.manager,e,{pointers:b,changedPointers:[a],pointerType:f,srcEvent:a}),c&&b.splice(h,1))}});var xb={touchstart:O,touchmove:P,touchend:Q,touchcancel:R},yb="touchstart",zb="touchstart touchmove touchend touchcancel";p(Ab,ab,{handler:function(a){var b=xb[a.type];if(b===O&&(this.started=!0),this.started){var c=Bb.call(this,a,b);b&(Q|R)&&0===c[0].length-c[1].length&&(this.started=!1),this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:J,srcEvent:a})}}});var Cb={touchstart:O,touchmove:P,touchend:Q,touchcancel:R},Db="touchstart touchmove touchend touchcancel";p(Eb,ab,{handler:function(a){var b=Cb[a.type],c=Fb.call(this,a,b);c&&this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:J,srcEvent:a})}}),p(Gb,ab,{handler:function(a,b,c){var d=c.pointerType==J,e=c.pointerType==L;if(d)this.mouse.allow=!1;else if(e&&!this.mouse.allow)return;b&(Q|R)&&(this.mouse.allow=!0),this.callback(a,b,c)},destroy:function(){this.touch.destroy(),this.mouse.destroy()}});var Hb=B(f.style,"touchAction"),Ib=Hb!==d,Jb="compute",Kb="auto",Lb="manipulation",Mb="none",Nb="pan-x",Ob="pan-y";Pb.prototype={set:function(a){a==Jb&&(a=this.compute()),Ib&&(this.manager.element.style[Hb]=a),this.actions=a.toLowerCase().trim()},update:function(){this.set(this.manager.options.touchAction)},compute:function(){var a=[];return m(this.manager.recognizers,function(b){r(b.options.enable,[b])&&(a=a.concat(b.getTouchAction()))}),Qb(a.join(" "))},preventDefaults:function(a){if(!Ib){var b=a.srcEvent,c=a.offsetDirection;if(this.manager.session.prevented)return b.preventDefault(),void 0;var d=this.actions,e=w(d,Mb),f=w(d,Ob),g=w(d,Nb);return e||f&&c&X||g&&c&Y?this.preventSrc(b):void 0}},preventSrc:function(a){this.manager.session.prevented=!0,a.preventDefault()}};var Rb=1,Sb=2,Tb=4,Ub=8,Vb=Ub,Wb=16,Xb=32;Yb.prototype={defaults:{},set:function(a){return n(this.options,a),this.manager&&this.manager.touchAction.update(),this},recognizeWith:function(a){if(l(a,"recognizeWith",this))return this;var b=this.simultaneous;return a=_b(a,this),b[a.id]||(b[a.id]=a,a.recognizeWith(this)),this},dropRecognizeWith:function(a){return l(a,"dropRecognizeWith",this)?this:(a=_b(a,this),delete this.simultaneous[a.id],this)},requireFailure:function(a){if(l(a,"requireFailure",this))return this;var b=this.requireFail;return a=_b(a,this),-1===y(b,a)&&(b.push(a),a.requireFailure(this)),this},dropRequireFailure:function(a){if(l(a,"dropRequireFailure",this))return this;a=_b(a,this);var b=y(this.requireFail,a);return b>-1&&this.requireFail.splice(b,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(a){return!!this.simultaneous[a.id]},emit:function(a){function d(d){b.manager.emit(b.options.event+(d?Zb(c):""),a)}var b=this,c=this.state;Ub>c&&d(!0),d(),c>=Ub&&d(!0)},tryEmit:function(a){return this.canEmit()?this.emit(a):(this.state=Xb,void 0)},canEmit:function(){for(var a=0;af?T:U,c=f!=this.pX,d=Math.abs(a.deltaX)):(e=0===g?S:0>g?V:W,c=g!=this.pY,d=Math.abs(a.deltaY))),a.direction=e,c&&d>b.threshold&&e&b.direction},attrTest:function(a){return ac.prototype.attrTest.call(this,a)&&(this.state&Sb||!(this.state&Sb)&&this.directionTest(a))},emit:function(a){this.pX=a.deltaX,this.pY=a.deltaY;var b=$b(a.direction);b&&this.manager.emit(this.options.event+b,a),this._super.emit.call(this,a)}}),p(cc,ac,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[Mb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.scale-1)>this.options.threshold||this.state&Sb)},emit:function(a){if(this._super.emit.call(this,a),1!==a.scale){var b=a.scale<1?"in":"out";this.manager.emit(this.options.event+b,a)}}}),p(dc,Yb,{defaults:{event:"press",pointers:1,time:500,threshold:5},getTouchAction:function(){return[Kb]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distanceb.time;if(this._input=a,!d||!c||a.eventType&(Q|R)&&!e)this.reset();else if(a.eventType&O)this.reset(),this._timer=k(function(){this.state=Vb,this.tryEmit()},b.time,this);else if(a.eventType&Q)return Vb;return Xb},reset:function(){clearTimeout(this._timer)},emit:function(a){this.state===Vb&&(a&&a.eventType&Q?this.manager.emit(this.options.event+"up",a):(this._input.timeStamp=j(),this.manager.emit(this.options.event,this._input)))}}),p(ec,ac,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[Mb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.rotation)>this.options.threshold||this.state&Sb)}}),p(fc,ac,{defaults:{event:"swipe",threshold:10,velocity:.65,direction:X|Y,pointers:1},getTouchAction:function(){return bc.prototype.getTouchAction.call(this)},attrTest:function(a){var c,b=this.options.direction;return b&(X|Y)?c=a.velocity:b&X?c=a.velocityX:b&Y&&(c=a.velocityY),this._super.attrTest.call(this,a)&&b&a.direction&&a.distance>this.options.threshold&&i(c)>this.options.velocity&&a.eventType&Q},emit:function(a){var b=$b(a.direction);b&&this.manager.emit(this.options.event+b,a),this.manager.emit(this.options.event,a)}}),p(gc,Yb,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:2,posThreshold:10},getTouchAction:function(){return[Lb]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance - - - - - - - - + + + + + + + + diff --git a/PTMagic/Program.cs b/PTMagic/Program.cs index 4ff0b71..d3dd747 100644 --- a/PTMagic/Program.cs +++ b/PTMagic/Program.cs @@ -7,12 +7,15 @@ using Core.Helper; using Core.Main.DataObjects.PTMagicData; using Microsoft.Extensions.DependencyInjection; -[assembly: AssemblyVersion("2.0.5")] +[assembly: AssemblyVersion("2.0.6")] [assembly: AssemblyProduct("PT Magic")] -namespace PTMagic { - class Program { - static void Main(string[] args) { +namespace PTMagic +{ + class Program + { + static void Main(string[] args) + { // Init PTMagic Core.Main.PTMagic ptMagic = new Core.Main.PTMagic(ServiceHelper.BuildLoggerService().GetRequiredService()); ptMagic.CurrentVersion = Assembly.GetExecutingAssembly().GetName().Version; @@ -21,7 +24,8 @@ namespace PTMagic { ptMagic.StartProcess(); // Keep the app running - for (; ; ) { + for (; ; ) + { Thread.Sleep(100); } } diff --git a/PTMagic/_defaults/_default settings PT 1.x/_default settings BTC or ETH/Monitor/appsettings.json b/PTMagic/_defaults/_default settings PT 1.x/_default settings BTC or ETH/Monitor/appsettings.json deleted file mode 100644 index 0c38f1b..0000000 --- a/PTMagic/_defaults/_default settings PT 1.x/_default settings BTC or ETH/Monitor/appsettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "PTMagicBasePath": "YOUR PT MAGIC PATH", // Path to your Profit Trailer Magic main directory (use double backslashes for windows like C:\\PTMagic\\) - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Warning" - } - } -} diff --git a/PTMagic/_defaults/_default settings PT 1.x/_default settings BTC or ETH/nlog.config b/PTMagic/_defaults/_default settings PT 1.x/_default settings BTC or ETH/nlog.config deleted file mode 100644 index adb7382..0000000 --- a/PTMagic/_defaults/_default settings PT 1.x/_default settings BTC or ETH/nlog.config +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/PTMagic/_defaults/_default settings PT 1.x/_default settings BTC or ETH/settings.analyzer.json b/PTMagic/_defaults/_default settings PT 1.x/_default settings BTC or ETH/settings.analyzer.json deleted file mode 100644 index 15d0b47..0000000 --- a/PTMagic/_defaults/_default settings PT 1.x/_default settings BTC or ETH/settings.analyzer.json +++ /dev/null @@ -1,241 +0,0 @@ -{ - "AnalyzerSettings": { - "MarketAnalyzer": { - "StoreDataMaxHours": 24, // Number of hours to store market data - "IntervalMinutes": 5, // Interval in minutes for PTMagic to check market trends and triggers - "ExcludeMainCurrency": true, // Excludes the main currency (for example BTC) from market trend analysis - "MarketTrends": [ - { - "Name": "CMC24h", // UNIQUE market trend name (to be referenced by your triggers below) - "Platform": "CoinMarketCap", // Platform to grab prices from (Allowed values are: CoinMarketCap, Exchange) - "MaxMarkets": 50, // Number of markets/pairs to analyze sorted by 24h volume - "TrendMinutes": 1440 // Number of minutes to build a trend (1440 = 24h, 720 = 12h, 60 = 1h) - }, - { - "Name": "Exchange1h", - "Platform": "Exchange", - "MaxMarkets": 50, - "TrendMinutes": 60, - "TrendCurrency": "Market" // Trend Currency to build the trend against. If set to "Fiat", the trend will take the USD value of your main currency into account to build the trend. (Allowed values are: Fiat, Market) - }, - { - "Name": "Exchange12h", - "Platform": "Exchange", - "MaxMarkets": 50, - "TrendMinutes": 720, - "TrendCurrency": "Market" - }, - { - "Name": "Exchange24h", - "Platform": "Exchange", - "MaxMarkets": 50, - "TrendMinutes": 1440, - "TrendCurrency": "Market" - } - ] - }, - "GlobalSettings": [ // Global settings for Profit Trailer properties - { - "SettingName": "EndOfTheWorld", // UNIQUE name of your setting - "TriggerConnection": "AND", // Define if triggers will be connected by AND or OR - "Triggers": [ // Your triggers for this setting - { - "MarketTrendName": "Exchange1h", // Reference to the market trend specified above - "MaxChange": 0 // Maximum trend change % for this setting to get triggered - }, - { - "MarketTrendName": "Exchange12h", - "MaxChange": -1 - }, - { - "MarketTrendName": "Exchange24h", - "MaxChange": -5 - } - ], - "PairsProperties": { // Properties for PAIRS.PROPERTIES - "ALL_sell_only_mode": true, - "ALL_trailing_profit": 0.1 - }, - "DCAProperties": { // Properties for DCA.PROPERTIES - "trailing_profit": 0.1 - } - }, - { - "SettingName": "TankingDown", - "TriggerConnection": "AND", - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MaxChange": 0 - }, - { - "MarketTrendName": "Exchange12h", - "MaxChange": 0 - }, - { - "MarketTrendName": "Exchange24h", - "MaxChange": -3, - "MinChange": -5 - } - ], - "PairsProperties": { - "ALL_buy_value": -1.1, - "ALL_trailing_buy": 0.39, - "ALL_trailing_profit": 0.1 - }, - "DCAProperties": { - "trailing_buy": 0.1 - } - }, - { - "SettingName": "BearSighted", - "TriggerConnection": "AND", - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MaxChange": 1 - }, - { - "MarketTrendName": "Exchange12h", - "MaxChange": 0 - }, - { - "MarketTrendName": "Exchange24h", - "MaxChange": -1, - "MinChange": -3 - } - ], - "PairsProperties": { - "ALL_buy_value": -0.9, - "ALL_trailing_profit": 0.15 - }, - "DCAProperties": { - "trailing_profit": 0.15 - } - }, - { - "SettingName": "ReadyForLiftOff", - "TriggerConnection": "AND", - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MinChange": -1 - }, - { - "MarketTrendName": "Exchange12h", - "MinChange": 0 - }, - { - "MarketTrendName": "Exchange24h", - "MinChange": 1, - "MaxChange": 3 - } - ], - "PairsProperties": { - "ALL_trailing_buy": 0.2, - "ALL_sell_value": 1.1 - }, - "DCAProperties": { - "sell_value": 1.1 - } - }, - { - "SettingName": "ToTheMoon", - "TriggerConnection": "AND", - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MinChange": 0 - }, - { - "MarketTrendName": "Exchange12h", - "MinChange": 1 - }, - { - "MarketTrendName": "Exchange24h", - "MinChange": 3 - } - ], - "PairsProperties": { - "ALL_trailing_buy": 0.15, - "ALL_sell_value": 1.3 - }, - "DCAProperties": { - "sell_value": 1.3 - } - }, - { - "SettingName": "Default", - "PairsProperties": { - "File": "PAIRS.properties" - }, - "DCAProperties": { - "File": "DCA.properties" - }, - "IndicatorsProperties": { - "File": "INDICATORS.properties" - } - } - ], - "SingleMarketSettings": [ // Single market/pair settings for Profit Trailer properties - { - "SettingName": "BlacklistNewCoins", - "StopProcessWhenTriggered": true, - "Triggers": [ - { - "AgeDaysLowerThan": 14 - } - ], - "PairsProperties": { - "ALL_trading_enabled": false, - "ALL_sell_only_mode": true, - "ALL_DCA_enabled": false - } - }, - { - "SettingName": "PumpNDumpProtection", - "TriggerConnection": "OR", - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "Relative", // The relation of the single market trend. Relative = Single market trend compared relative to the market trend / Absolute = Single market trend viewn on its own - "MinChange": 10 - }, - { - "MarketTrendName": "Exchange12h", - "MarketTrendRelation": "Relative", - "MinChange": 10 - }, - { - "MarketTrendName": "Exchange24h", - "MarketTrendRelation": "Relative", - "MinChange": 10 - } - ], - "PairsProperties": { - "ALL_trailing_profit_OFFSETPERCENT": -10, - "ALL_sell_value_OFFSETPERCENT": -30, - "ALL_sell_only_mode": true, - "ALL_DCA_enabled": false - } - }, - { - "SettingName": "FreefallBlock", - "TriggerConnection": "OR", - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "Absolute", - "MaxChange": -5 - } - ], - "PairsProperties": { - "ALL_trailing_profit_OFFSETPERCENT": -10, - "ALL_sell_value_OFFSETPERCENT": -30, - "ALL_sell_only_mode": true, - "ALL_DCA_enabled": false - } - } - ] - } -} \ No newline at end of file diff --git a/PTMagic/_defaults/_default settings PT 1.x/_default settings BTC or ETH/settings.general.json b/PTMagic/_defaults/_default settings PT 1.x/_default settings BTC or ETH/settings.general.json deleted file mode 100644 index c543b7a..0000000 --- a/PTMagic/_defaults/_default settings PT 1.x/_default settings BTC or ETH/settings.general.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "GeneralSettings": { - "Application": { - "IsEnabled": true, // Enables the PTMagic bot (needs restart to take effect) - "TestMode": false, // If TestMode is active, no properties files will be changed - "ProfitTrailerPath": "YOUR PROFIT TRAILER PATH", // Path to your Profit Trailer main directory (use double backslashes for windows like C:\\ProfitTrailer\\) - "Exchange": "Bittrex", // The exchange your are running Profit Trailer on - "StartBalance": 0, // The balance you had in your wallet when you started working with Profit Trailer - "TimezoneOffset": "+0:00", // Your timezone offset from UTC time - "MainFiatCurrency": "USD", // Your main fiat currency that will be used in the monitor - "AlwaysLoadDefaultBeforeSwitch": true, // If this is enabled, PTMagic will always load default settings before switching to another setting - "FloodProtectionMinutes": 15, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute - "InstanceName": "PT Magic", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages. In case you are running more than one bot, you may set different names to separate them - "CoinMarketCapAPIKey": "" //CoinMarketCap Api - }, - "Monitor": { - "IsPasswordProtected": true, // Defines if your monitor will be asking to setup a password on its first start - "OpenBrowserOnStart": false, // If active, a browser window will open as soon as you start the monitor - "Port": 5000, // The port you want to run your monitor on - "RootUrl": "/", // The root Url of your monitor - "GraphIntervalMinutes": 60, // The interval for the monitor market trend graph to draw points - "GraphMaxTimeframeHours": 24, // This will enable you to define the timeframe that your graph for market trends covers - "RefreshSeconds": 30, // The refresh interval of your monitor main page - "LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it - "MaxTopMarkets": 20, // The amount of top markets being shown in your Sales Analyzer - "MaxDailySummaries": 10, // The amount of "Last Days" being shown in your Sales Analyzer - "MaxMonthlySummaries": 10 // The amount of "Last Months" being shown in your Sales Analyzer - }, - "Backup": { - "IsEnabled": true, // Enables a backup procedure for your properties files. Before every switch PTMagic will backup the current properties - "MaxHours": 12 // Max number of hours to keep backup files - }, - "Telegram": { - "IsEnabled": false, // Enables PT Magic to send Telegram messages - "BotToken": "", // Your Telegram bot token - "ChatId": 0, // Your Telegram Chat ID - "SilentMode": false // If SilentMode is active, no notification sound or vibration will happen when the bot sends a Telegram message - } - } -} \ No newline at end of file diff --git a/PTMagic/_defaults/_default settings PT 1.x/_default settings USDT/Monitor/appsettings.json b/PTMagic/_defaults/_default settings PT 1.x/_default settings USDT/Monitor/appsettings.json deleted file mode 100644 index 0c38f1b..0000000 --- a/PTMagic/_defaults/_default settings PT 1.x/_default settings USDT/Monitor/appsettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "PTMagicBasePath": "YOUR PT MAGIC PATH", // Path to your Profit Trailer Magic main directory (use double backslashes for windows like C:\\PTMagic\\) - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Warning" - } - } -} diff --git a/PTMagic/_defaults/_default settings PT 1.x/_default settings USDT/nlog.config b/PTMagic/_defaults/_default settings PT 1.x/_default settings USDT/nlog.config deleted file mode 100644 index adb7382..0000000 --- a/PTMagic/_defaults/_default settings PT 1.x/_default settings USDT/nlog.config +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/PTMagic/_defaults/_default settings PT 1.x/_default settings USDT/settings.analyzer.json b/PTMagic/_defaults/_default settings PT 1.x/_default settings USDT/settings.analyzer.json deleted file mode 100644 index 8039751..0000000 --- a/PTMagic/_defaults/_default settings PT 1.x/_default settings USDT/settings.analyzer.json +++ /dev/null @@ -1,242 +0,0 @@ -{ - "AnalyzerSettings": { - "MarketAnalyzer": { - "StoreDataMaxHours": 24, // Number of hours to store market data - "IntervalMinutes": 1, // Interval in minutes for PTMagic to check market trends and triggers - "ExcludeMainCurrency": true, // Excludes the main currency (for example BTC) from market trend analysis - "MarketTrends": [ - { - "Name": "CMC24h", // UNIQUE market trend name (to be referenced by your triggers below) - "Platform": "CoinMarketCap", // Platform to grab prices from (Allowed values are: CoinMarketCap, Exchange) - "MaxMarkets": 50, // Number of markets/pairs to analyze sorted by 24h volume - "TrendMinutes": 1440 // Number of minutes to build a trend (1440 = 24h, 720 = 12h, 60 = 1h) - }, - { - "Name": "Exchange1h", - "Platform": "Exchange", - "MaxMarkets": 50, - "TrendMinutes": 60, - "TrendCurrency": "Market", // Trend Currency to build the trend against. If set to "Fiat", the trend will take the USD value of your main currency into account to build the trend. (Allowed values are: Fiat, Market) - "IgnoredMarkets": "BNBBTC" // Comma separated list of markets you want to be ignored in this market trend. - }, - { - "Name": "Exchange12h", - "Platform": "Exchange", - "MaxMarkets": 50, - "TrendMinutes": 720, - "TrendCurrency": "Market", - "IgnoredMarkets": "BNBBTC" - }, - { - "Name": "Exchange24h", - "Platform": "Exchange", - "MaxMarkets": 50, - "TrendMinutes": 1440, - "TrendCurrency": "Market", - "IgnoredMarkets": "BNBBTC" - } - ] - }, - "GlobalSettings": [ // Global settings for Profit Trailer properties - { - "SettingName": "EndOfTheWorld", // UNIQUE name of your setting - "TriggerConnection": "AND", // Define if triggers will be connected by AND or OR - "Triggers": [ // Your triggers for this setting - { - "MarketTrendName": "Exchange12h", // Reference to the market trend specified above - "MaxChange": -1 // Maximum trend change % for this setting to get triggered - }, - { - "MarketTrendName": "Exchange24h", - "MaxChange": -5 - } - ], - "PairsProperties": { // Properties for PAIRS.PROPERTIES - "ALL_sell_only_mode": true, - "ALL_trailing_profit_OFFSETPERCENT": -50 - }, - "DCAProperties": { // Properties for DCA.PROPERTIES - "trailing_profit_OFFSETPERCENT": -50 - } - }, - { - "SettingName": "TankingDown", - "TriggerConnection": "AND", - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MaxChange": 1 - }, - { - "MarketTrendName": "Exchange12h", - "MaxChange": 0 - }, - { - "MarketTrendName": "Exchange24h", - "MaxChange": -3, - "MinChange": -5 - } - ], - "PairsProperties": { - "ALL_buy_value_OFFSETPERCENT": -10, - "ALL_trailing_buy_OFFSETPERCENT": 10, - "ALL_trailing_profit_OFFSETPERCENT": -25 - }, - "DCAProperties": { - "trailing_buy_OFFSETPERCENT": -25 - } - }, - { - "SettingName": "BearSighted", - "TriggerConnection": "AND", - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MaxChange": 1 - }, - { - "MarketTrendName": "Exchange12h", - "MaxChange": 0 - }, - { - "MarketTrendName": "Exchange24h", - "MaxChange": -1, - "MinChange": -3 - } - ], - "PairsProperties": { - "ALL_buy_value_OFFSETPERCENT": -5, - "ALL_trailing_profit_OFFSETPERCENT": -10 - }, - "DCAProperties": { - "trailing_profit_OFFSETPERCENT": -10 - } - }, - { - "SettingName": "ReadyForLiftOff", - "TriggerConnection": "AND", - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MinChange": -1 - }, - { - "MarketTrendName": "Exchange12h", - "MinChange": 0 - }, - { - "MarketTrendName": "Exchange24h", - "MinChange": 1, - "MaxChange": 3 - } - ], - "PairsProperties": { - "ALL_trailing_buy_OFFSETPERCENT": -25, - "ALL_sell_value_OFFSETPERCENT": 10 - }, - "DCAProperties": { - "sell_value_OFFSETPERCENT": 10 - } - }, - { - "SettingName": "ToTheMoon", - "TriggerConnection": "AND", - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MinChange": -1 - }, - { - "MarketTrendName": "Exchange12h", - "MinChange": 1 - }, - { - "MarketTrendName": "Exchange24h", - "MinChange": 3 - } - ], - "PairsProperties": { - "ALL_trailing_buy_OFFSETPERCENT": -50, - "ALL_sell_value_OFFSETPERCENT": 25 - }, - "DCAProperties": { - "sell_value_OFFSETPERCENT": 25 - } - }, - { - "SettingName": "Default", - "PairsProperties": { - "File": "PAIRS.PROPERTIES" - }, - "DCAProperties": { - "File": "DCA.PROPERTIES" - }, - "IndicatorsProperties": { - "File": "INDICATORS.PROPERTIES" - } - } - ], - "SingleMarketSettings": [ // Single market/pair settings for Profit Trailer properties - { - "SettingName": "BlacklistNewCoins", - "StopProcessWhenTriggered": true, - "Triggers": [ - { - "AgeDaysLowerThan": 14 - } - ], - "PairsProperties": { - "ALL_trading_enabled": false, - "ALL_sell_only_mode": true, - "ALL_DCA_enabled": false - } - }, - { - "SettingName": "PumpNDumpProtection", - "TriggerConnection": "OR", - "StopProcessWhenTriggered": true, - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "Relative", // The relation of the single market trend. Relative = Single market trend compared relative to the market trend / Absolute = Single market trend viewn on its own - "MinChange": 5 - }, - { - "MarketTrendName": "Exchange12h", - "MarketTrendRelation": "Relative", - "MinChange": 5 - }, - { - "MarketTrendName": "Exchange24h", - "MarketTrendRelation": "Relative", - "MinChange": 5 - } - ], - "PairsProperties": { - "ALL_trailing_profit_OFFSETPERCENT": -10, - "ALL_sell_value_OFFSETPERCENT": -30, - "ALL_sell_only_mode": true, - "ALL_DCA_enabled": false - } - }, - { - "SettingName": "FreefallBlock", - "TriggerConnection": "OR", - "StopProcessWhenTriggered": true, - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "Absolute", - "MaxChange": -5 - } - ], - "PairsProperties": { - "ALL_trailing_profit_OFFSETPERCENT": -10, - "ALL_sell_value_OFFSETPERCENT": -30, - "ALL_sell_only_mode": true, - "ALL_DCA_enabled": false - } - } - ] - } -} \ No newline at end of file diff --git a/PTMagic/_defaults/_default settings PT 1.x/_default settings USDT/settings.general.json b/PTMagic/_defaults/_default settings PT 1.x/_default settings USDT/settings.general.json deleted file mode 100644 index c543b7a..0000000 --- a/PTMagic/_defaults/_default settings PT 1.x/_default settings USDT/settings.general.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "GeneralSettings": { - "Application": { - "IsEnabled": true, // Enables the PTMagic bot (needs restart to take effect) - "TestMode": false, // If TestMode is active, no properties files will be changed - "ProfitTrailerPath": "YOUR PROFIT TRAILER PATH", // Path to your Profit Trailer main directory (use double backslashes for windows like C:\\ProfitTrailer\\) - "Exchange": "Bittrex", // The exchange your are running Profit Trailer on - "StartBalance": 0, // The balance you had in your wallet when you started working with Profit Trailer - "TimezoneOffset": "+0:00", // Your timezone offset from UTC time - "MainFiatCurrency": "USD", // Your main fiat currency that will be used in the monitor - "AlwaysLoadDefaultBeforeSwitch": true, // If this is enabled, PTMagic will always load default settings before switching to another setting - "FloodProtectionMinutes": 15, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute - "InstanceName": "PT Magic", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages. In case you are running more than one bot, you may set different names to separate them - "CoinMarketCapAPIKey": "" //CoinMarketCap Api - }, - "Monitor": { - "IsPasswordProtected": true, // Defines if your monitor will be asking to setup a password on its first start - "OpenBrowserOnStart": false, // If active, a browser window will open as soon as you start the monitor - "Port": 5000, // The port you want to run your monitor on - "RootUrl": "/", // The root Url of your monitor - "GraphIntervalMinutes": 60, // The interval for the monitor market trend graph to draw points - "GraphMaxTimeframeHours": 24, // This will enable you to define the timeframe that your graph for market trends covers - "RefreshSeconds": 30, // The refresh interval of your monitor main page - "LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it - "MaxTopMarkets": 20, // The amount of top markets being shown in your Sales Analyzer - "MaxDailySummaries": 10, // The amount of "Last Days" being shown in your Sales Analyzer - "MaxMonthlySummaries": 10 // The amount of "Last Months" being shown in your Sales Analyzer - }, - "Backup": { - "IsEnabled": true, // Enables a backup procedure for your properties files. Before every switch PTMagic will backup the current properties - "MaxHours": 12 // Max number of hours to keep backup files - }, - "Telegram": { - "IsEnabled": false, // Enables PT Magic to send Telegram messages - "BotToken": "", // Your Telegram bot token - "ChatId": 0, // Your Telegram Chat ID - "SilentMode": false // If SilentMode is active, no notification sound or vibration will happen when the bot sends a Telegram message - } - } -} \ No newline at end of file diff --git a/PTMagic/_defaults/_default settings PT 2.x/_default settings BTC or ETH/settings.analyzer.json b/PTMagic/_defaults/_default settings PT 2.x/_default settings BTC or ETH/settings.analyzer.json index 3377939..124697b 100644 --- a/PTMagic/_defaults/_default settings PT 2.x/_default settings BTC or ETH/settings.analyzer.json +++ b/PTMagic/_defaults/_default settings PT 2.x/_default settings BTC or ETH/settings.analyzer.json @@ -1,171 +1,238 @@ -{ +// +// The settings below offer a basic example of some of the options available when using PTMagic. +// You should take your time and adjust these settings according to your own personal preferences. +// Always test your PTMagic settings by running a Profit Trailer bot in TESTMODE, to make sure +// it is performing as you expect. +// +// For more information on these settings, see the wiki at: https://github.com/PTMagicians/PTMagic/wiki/settings.analyzer + + +{ "AnalyzerSettings": { "MarketAnalyzer": { - "StoreDataMaxHours": 24, // Number of hours to store market data - "IntervalMinutes": 5, // Interval in minutes for PTMagic to check market trends and triggers + "StoreDataMaxHours": 48, // Number of hours to store market data + "IntervalMinutes": 2, // Interval in minutes for PTMagic to check market trends and triggers "ExcludeMainCurrency": true, // Excludes the main currency (for example BTC) from market trend analysis "MarketTrends": [ { - "Name": "CMC24h", // UNIQUE market trend name (to be referenced by your triggers below) - "Platform": "CoinMarketCap", // Platform to grab prices from (Allowed values are: CoinMarketCap, Exchange) - "MaxMarkets": 50, // Number of markets/pairs to analyze sorted by 24h volume - "TrendMinutes": 1440 // Number of minutes to build a trend (1440 = 24h, 720 = 12h, 60 = 1h) + "Name": "15m", // UNIQUE market trend name (to be referenced by your triggers below) + "Platform": "Exchange", // Platform to grab prices from (Allowed values are: CoinMarketCap, Exchange) + "MaxMarkets": 50, // Number of markets/pairs to analyze sorted by 24h volume + "TrendMinutes": 15, // Number of minutes to build a trend (1440 = 24h, 720 = 12h, 60 = 1h) + "TrendCurrency": "Market", // Trend Currency to build the trend against. If set to "Fiat", the trend will + // take the USD value of your main currency into account to build the trend. + // (Allowed values are: Fiat, Market) + "DisplayGraph": false, // Use this trend in the graph on the PTM Monitor dashboard and market analyzer + "DisplayOnMarketAnalyzerList": false // Disply this trend for all coins on the PTM Monitor market analyzer }, { - "Name": "Exchange1h", + "Name": "1h", "Platform": "Exchange", "MaxMarkets": 50, "TrendMinutes": 60, - "TrendCurrency": "Market" // Trend Currency to build the trend against. If set to "Fiat", the trend will take the USD value of your main currency into account to build the trend. (Allowed values are: Fiat, Market) + "TrendCurrency": "Market", + "DisplayGraph": true, + "DisplayOnMarketAnalyzerList": true }, { - "Name": "Exchange12h", + "Name": "6h", + "Platform": "Exchange", + "MaxMarkets": 50, + "TrendMinutes": 360, + "TrendCurrency": "Market", + "DisplayGraph": true, + "DisplayOnMarketAnalyzerList": true + }, + { + "Name": "12h", "Platform": "Exchange", "MaxMarkets": 50, "TrendMinutes": 720, - "TrendCurrency": "Market" + "TrendCurrency": "Market", + "DisplayGraph": true, + "DisplayOnMarketAnalyzerList": true }, { - "Name": "Exchange24h", + "Name": "24h", "Platform": "Exchange", "MaxMarkets": 50, "TrendMinutes": 1440, - "TrendCurrency": "Market" + "TrendCurrency": "Market", + "DisplayGraph": true, + "DisplayOnMarketAnalyzerList": true } ] }, + // ================================ GLOBAL SETTINGS ================================ + // "GlobalSettings": [ // Global settings for Profit Trailer properties + // + // ----------------------------- { - "SettingName": "EndOfTheWorld", // UNIQUE name of your setting + "SettingName": "EndOfTheWorld", // ANY UNIQUE name of your setting "TriggerConnection": "AND", // Define if triggers will be connected by AND or OR - "Triggers": [ // Your triggers for this setting + "Triggers": [ // Your triggers for this setting. You can use any of your defined trends from above { - "MarketTrendName": "Exchange1h", // Reference to the market trend specified above - "MaxChange": 0 // Maximum trend change % for this setting to get triggered + "MarketTrendName": "1h", // Reference to the market trend specified above + "MaxChange": 0 // The maximum value for this trigger to be true. (Any value below "0" will trigger this) }, { - "MarketTrendName": "Exchange12h", - "MaxChange": -1 + "MarketTrendName": "12h", + "MaxChange": -2 }, { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "24h", "MaxChange": -5 } ], "PairsProperties": { // Properties for PAIRS.PROPERTIES + // Any valid setting from https://wiki.profittrailer.com/doku.php?id=pairs.properties can be used here. + // You can use a specific value, or apply a discrete OFFSET or OFFSETPERCENT to the value in your default PAIRS setting. "DEFAULT_sell_only_mode_enabled": true, "DEFAULT_trailing_profit_OFFSETPERCENT": -50 }, "DCAProperties": { // Properties for DCA.PROPERTIES - "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -50 + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -75 } }, + // ----------------------------- { "SettingName": "TankingDown", "TriggerConnection": "AND", "Triggers": [ { - "MarketTrendName": "Exchange1h", + "MarketTrendName": "15m", + "MaxChange": 0 + }, + { + "MarketTrendName": "1h", "MaxChange": 0 }, { - "MarketTrendName": "Exchange12h", + "MarketTrendName": "12h", "MaxChange": 0 }, { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "24h", // Any value between -5 and -3 will make this trigger true. "MaxChange": -3, - "MinChange": -5 + "MinChange": -5 // The minimum value for this trigger to be true. (Any value above "-5" will trigger this) } ], "PairsProperties": { - "DEFAULT_A_buy_value_OFFSETPERCENT": -10, - "DEFAULT_B_buy_value_OFFSETPERCENT": -10, - "DEFAULT_trailing_buy_OFFSETPERCENT": 10, + "max_trading_pairs_OFFSET": -2, + "DEFAULT_min_buy_volume_OFFSETPERCENT": 100, + //"DEFAULT_initial_cost_OFFSETPERCENT": -50, + //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": -50, + "DEFAULT_trailing_buy_OFFSETPERCENT": 25, "DEFAULT_trailing_profit_OFFSETPERCENT": -25 }, "DCAProperties": { - "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -25 + //"DEFAULT_DCA_rebuy_timeout_OFFSETPERCENT": 100, + "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": 25, + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -50 + }, + "IndicatorsProperties": { } }, + // ----------------------------- { "SettingName": "BearSighted", "TriggerConnection": "AND", "Triggers": [ { - "MarketTrendName": "Exchange1h", + "MarketTrendName": "1h", "MaxChange": 1 }, { - "MarketTrendName": "Exchange12h", + "MarketTrendName": "12h", "MaxChange": 0 }, { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "24h", "MaxChange": -1, "MinChange": -3 } ], "PairsProperties": { - "DEFAULT_A_buy_value_OFFSETPERCENT": -5, - "DEFAULT_B_buy_value_OFFSETPERCENT": -5, + "max_trading_pairs_OFFSET": -1, + //"DEFAULT_initial_cost_OFFSETPERCENT": -25, + //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": -25, + "DEFAULT_trailing_buy_OFFSETPERCENT": 10, "DEFAULT_trailing_profit_OFFSETPERCENT": -10 }, "DCAProperties": { - "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -10 + "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": 10, + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -10, + }, + "IndicatorsProperties": { } }, + // ----------------------------- { "SettingName": "ReadyForLiftOff", "TriggerConnection": "AND", "Triggers": [ { - "MarketTrendName": "Exchange1h", - "MinChange": -1 - }, - { - "MarketTrendName": "Exchange12h", + "MarketTrendName": "1h", "MinChange": 0 }, { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "12h", + "MinChange": 0 + }, + { + "MarketTrendName": "24h", "MinChange": 1, "MaxChange": 3 } ], "PairsProperties": { - "DEFAULT_trailing_buy_OFFSETPERCENT": -25, + "max_trading_pairs_OFFSET": 1, + //"DEFAULT_initial_cost_OFFSETPERCENT": 10, + //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": 10, + "DEFAULT_trailing_buy_OFFSETPERCENT": -10, "DEFAULT_A_sell_value_OFFSETPERCENT": 10 }, "DCAProperties": { - "DEFAULT_DCA_A_sell_value_OFFSETPERCENT": 10 + "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -10, + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 10, + }, + "IndicatorsProperties": { } }, + // ----------------------------- { "SettingName": "ToTheMoon", "TriggerConnection": "AND", "Triggers": [ { - "MarketTrendName": "Exchange1h", - "MinChange": 0 - }, - { - "MarketTrendName": "Exchange12h", + "MarketTrendName": "1h", "MinChange": 1 }, { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "12h", + "MinChange": 1 + }, + { + "MarketTrendName": "24h", "MinChange": 3 } ], "PairsProperties": { - "DEFAULT_trailing_buy_OFFSETPERCENT": -50, - "DEFAULT_A_sell_value_OFFSETPERCENT": 25 + "max_trading_pairs_OFFSET": 2, + //"DEFAULT_initial_cost_OFFSETPERCENT": 20, + //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": 20, + "DEFAULT_trailing_buy_OFFSETPERCENT": -10, + "DEFAULT_A_sell_value_OFFSETPERCENT": 20 }, "DCAProperties": { - "DEFAULT_DCA_A_sell_value_OFFSETPERCENT": 25 + "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -20, + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 20, + }, + "IndicatorsProperties": { } }, + // ----------------------------- { "SettingName": "Default", "PairsProperties": { @@ -179,13 +246,18 @@ } } ], + // ================================ COIN-SPECIFIC SETTINGS ================================ + // "SingleMarketSettings": [ // Single market/pair settings for Profit Trailer properties + // Any setting from https://wiki.profittrailer.com/doku.php?id=pairs.properties + // marked as CS (coin-specific) can be used here. + // Only coins that meet the triggered conditions will have the settings applied. { - "SettingName": "BlacklistNewCoins", + "SettingName": "BlacklistCoins", "StopProcessWhenTriggered": true, "Triggers": [ { - "AgeDaysLowerThan": 14 + "AgeDaysLowerThan": 21 } ], "PairsProperties": { @@ -194,24 +266,33 @@ "DEFAULT_DCA_enabled": false } }, + // ----------------------------- { "SettingName": "PumpNDumpProtection", "TriggerConnection": "OR", "Triggers": [ { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "Relative", // The relation of the single market trend. Relative = Single market trend compared relative to the market trend / Absolute = Single market trend viewn on its own - "MinChange": 10 + "MarketTrendName": "1h", + "MarketTrendRelation": "Relative", // The relation of the single market trend. Relative = Single market + // trend compared relative to the market trend + // Absolute = Single market trend viewed on its own + "MinChange": 8 }, { - "MarketTrendName": "Exchange12h", + "MarketTrendName": "12h", "MarketTrendRelation": "Relative", "MinChange": 10 }, { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "24h", "MarketTrendRelation": "Relative", - "MinChange": 10 + "MinChange": 12 + } + ], + "OffTriggers": [ + { + "HoursSinceTriggered": 3 // Any coin that triggers this setting, will remain under this setting + // for 3 hours, since the last time it triggered. } ], "PairsProperties": { @@ -219,17 +300,23 @@ "DEFAULT_DCA_enabled": false } }, + // ----------------------------- { "SettingName": "FreefallBlock", "TriggerConnection": "OR", "Triggers": [ { - "MarketTrendName": "Exchange1h", + "MarketTrendName": "1h", "MarketTrendRelation": "Absolute", "MaxChange": -5 } ], - "PairsProperties": { + "OffTriggers": [ + { + "HoursSinceTriggered": 1 + } + ], + "PairsProperties": { "DEFAULT_sell_only_mode_enabled": true, "DEFAULT_DCA_enabled": false } diff --git a/PTMagic/_defaults/_default settings PT 2.x/_default settings USDT/settings.analyzer.json b/PTMagic/_defaults/_default settings PT 2.x/_default settings USDT/settings.analyzer.json index ccf10f6..124697b 100644 --- a/PTMagic/_defaults/_default settings PT 2.x/_default settings USDT/settings.analyzer.json +++ b/PTMagic/_defaults/_default settings PT 2.x/_default settings USDT/settings.analyzer.json @@ -1,190 +1,263 @@ -{ +// +// The settings below offer a basic example of some of the options available when using PTMagic. +// You should take your time and adjust these settings according to your own personal preferences. +// Always test your PTMagic settings by running a Profit Trailer bot in TESTMODE, to make sure +// it is performing as you expect. +// +// For more information on these settings, see the wiki at: https://github.com/PTMagicians/PTMagic/wiki/settings.analyzer + + +{ "AnalyzerSettings": { "MarketAnalyzer": { - "StoreDataMaxHours": 24, // Number of hours to store market data - "IntervalMinutes": 1, // Interval in minutes for PTMagic to check market trends and triggers + "StoreDataMaxHours": 48, // Number of hours to store market data + "IntervalMinutes": 2, // Interval in minutes for PTMagic to check market trends and triggers "ExcludeMainCurrency": true, // Excludes the main currency (for example BTC) from market trend analysis "MarketTrends": [ { - "Name": "CMC24h", // UNIQUE market trend name (to be referenced by your triggers below) - "Platform": "CoinMarketCap", // Platform to grab prices from (Allowed values are: CoinMarketCap, Exchange) - "MaxMarkets": 50, // Number of markets/pairs to analyze sorted by 24h volume - "TrendMinutes": 1440 // Number of minutes to build a trend (1440 = 24h, 720 = 12h, 60 = 1h) + "Name": "15m", // UNIQUE market trend name (to be referenced by your triggers below) + "Platform": "Exchange", // Platform to grab prices from (Allowed values are: CoinMarketCap, Exchange) + "MaxMarkets": 50, // Number of markets/pairs to analyze sorted by 24h volume + "TrendMinutes": 15, // Number of minutes to build a trend (1440 = 24h, 720 = 12h, 60 = 1h) + "TrendCurrency": "Market", // Trend Currency to build the trend against. If set to "Fiat", the trend will + // take the USD value of your main currency into account to build the trend. + // (Allowed values are: Fiat, Market) + "DisplayGraph": false, // Use this trend in the graph on the PTM Monitor dashboard and market analyzer + "DisplayOnMarketAnalyzerList": false // Disply this trend for all coins on the PTM Monitor market analyzer }, { - "Name": "Exchange1h", + "Name": "1h", "Platform": "Exchange", "MaxMarkets": 50, "TrendMinutes": 60, - "TrendCurrency": "Market", // Trend Currency to build the trend against. If set to "Fiat", the trend will take the USD value of your main currency into account to build the trend. (Allowed values are: Fiat, Market) - "IgnoredMarkets": "BNBBTC" // Comma separated list of markets you want to be ignored in this market trend. + "TrendCurrency": "Market", + "DisplayGraph": true, + "DisplayOnMarketAnalyzerList": true }, { - "Name": "Exchange12h", + "Name": "6h", + "Platform": "Exchange", + "MaxMarkets": 50, + "TrendMinutes": 360, + "TrendCurrency": "Market", + "DisplayGraph": true, + "DisplayOnMarketAnalyzerList": true + }, + { + "Name": "12h", "Platform": "Exchange", "MaxMarkets": 50, "TrendMinutes": 720, "TrendCurrency": "Market", - "IgnoredMarkets": "BNBBTC" + "DisplayGraph": true, + "DisplayOnMarketAnalyzerList": true }, { - "Name": "Exchange24h", + "Name": "24h", "Platform": "Exchange", "MaxMarkets": 50, "TrendMinutes": 1440, "TrendCurrency": "Market", - "IgnoredMarkets": "BNBBTC" + "DisplayGraph": true, + "DisplayOnMarketAnalyzerList": true } ] }, + // ================================ GLOBAL SETTINGS ================================ + // "GlobalSettings": [ // Global settings for Profit Trailer properties + // + // ----------------------------- { - "SettingName": "EndOfTheWorld", // UNIQUE name of your setting + "SettingName": "EndOfTheWorld", // ANY UNIQUE name of your setting "TriggerConnection": "AND", // Define if triggers will be connected by AND or OR - "Triggers": [ // Your triggers for this setting + "Triggers": [ // Your triggers for this setting. You can use any of your defined trends from above { - "MarketTrendName": "Exchange12h", // Reference to the market trend specified above - "MaxChange": -1 // Maximum trend change % for this setting to get triggered + "MarketTrendName": "1h", // Reference to the market trend specified above + "MaxChange": 0 // The maximum value for this trigger to be true. (Any value below "0" will trigger this) }, { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "12h", + "MaxChange": -2 + }, + { + "MarketTrendName": "24h", "MaxChange": -5 } ], "PairsProperties": { // Properties for PAIRS.PROPERTIES + // Any valid setting from https://wiki.profittrailer.com/doku.php?id=pairs.properties can be used here. + // You can use a specific value, or apply a discrete OFFSET or OFFSETPERCENT to the value in your default PAIRS setting. "DEFAULT_sell_only_mode_enabled": true, "DEFAULT_trailing_profit_OFFSETPERCENT": -50 }, "DCAProperties": { // Properties for DCA.PROPERTIES - "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -50 + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -75 } }, + // ----------------------------- { "SettingName": "TankingDown", "TriggerConnection": "AND", "Triggers": [ { - "MarketTrendName": "Exchange1h", - "MaxChange": 1 + "MarketTrendName": "15m", + "MaxChange": 0 }, - { - "MarketTrendName": "Exchange12h", + { + "MarketTrendName": "1h", "MaxChange": 0 }, { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "12h", + "MaxChange": 0 + }, + { + "MarketTrendName": "24h", // Any value between -5 and -3 will make this trigger true. "MaxChange": -3, - "MinChange": -5 + "MinChange": -5 // The minimum value for this trigger to be true. (Any value above "-5" will trigger this) } ], "PairsProperties": { - "DEFAULT_A_buy_value_OFFSETPERCENT": -10, - "DEFAULT_B_buy_value_OFFSETPERCENT": -10, - "DEFAULT_trailing_buy_OFFSETPERCENT": 10, + "max_trading_pairs_OFFSET": -2, + "DEFAULT_min_buy_volume_OFFSETPERCENT": 100, + //"DEFAULT_initial_cost_OFFSETPERCENT": -50, + //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": -50, + "DEFAULT_trailing_buy_OFFSETPERCENT": 25, "DEFAULT_trailing_profit_OFFSETPERCENT": -25 }, "DCAProperties": { - "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -25 + //"DEFAULT_DCA_rebuy_timeout_OFFSETPERCENT": 100, + "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": 25, + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -50 + }, + "IndicatorsProperties": { } }, + // ----------------------------- { "SettingName": "BearSighted", "TriggerConnection": "AND", "Triggers": [ { - "MarketTrendName": "Exchange1h", + "MarketTrendName": "1h", "MaxChange": 1 }, { - "MarketTrendName": "Exchange12h", + "MarketTrendName": "12h", "MaxChange": 0 }, { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "24h", "MaxChange": -1, "MinChange": -3 } ], "PairsProperties": { - "DEFAULT_A_buy_value_OFFSETPERCENT": -5, - "DEFAULT_B_buy_value_OFFSETPERCENT": -5, + "max_trading_pairs_OFFSET": -1, + //"DEFAULT_initial_cost_OFFSETPERCENT": -25, + //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": -25, + "DEFAULT_trailing_buy_OFFSETPERCENT": 10, "DEFAULT_trailing_profit_OFFSETPERCENT": -10 }, "DCAProperties": { - "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -10 + "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": 10, + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -10, + }, + "IndicatorsProperties": { } }, + // ----------------------------- { "SettingName": "ReadyForLiftOff", "TriggerConnection": "AND", "Triggers": [ { - "MarketTrendName": "Exchange1h", - "MinChange": -1 - }, - { - "MarketTrendName": "Exchange12h", + "MarketTrendName": "1h", "MinChange": 0 }, { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "12h", + "MinChange": 0 + }, + { + "MarketTrendName": "24h", "MinChange": 1, "MaxChange": 3 } ], "PairsProperties": { - "DEFAULT_trailing_buy_OFFSETPERCENT": -25, + "max_trading_pairs_OFFSET": 1, + //"DEFAULT_initial_cost_OFFSETPERCENT": 10, + //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": 10, + "DEFAULT_trailing_buy_OFFSETPERCENT": -10, "DEFAULT_A_sell_value_OFFSETPERCENT": 10 }, "DCAProperties": { - "DEFAULT_DCA_A_sell_value_OFFSETPERCENT": 10 + "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -10, + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 10, + }, + "IndicatorsProperties": { } }, + // ----------------------------- { "SettingName": "ToTheMoon", "TriggerConnection": "AND", "Triggers": [ { - "MarketTrendName": "Exchange1h", - "MinChange": -1 - }, - { - "MarketTrendName": "Exchange12h", + "MarketTrendName": "1h", "MinChange": 1 }, { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "12h", + "MinChange": 1 + }, + { + "MarketTrendName": "24h", "MinChange": 3 } ], "PairsProperties": { - "DEFAULT_trailing_buy_OFFSETPERCENT": -50, - "DEFAULT_A_sell_value_OFFSETPERCENT": 25 + "max_trading_pairs_OFFSET": 2, + //"DEFAULT_initial_cost_OFFSETPERCENT": 20, + //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": 20, + "DEFAULT_trailing_buy_OFFSETPERCENT": -10, + "DEFAULT_A_sell_value_OFFSETPERCENT": 20 }, "DCAProperties": { - "DEFAULT_DCA_A_sell_value_OFFSETPERCENT": 25 + "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -20, + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 20, + }, + "IndicatorsProperties": { } }, + // ----------------------------- { "SettingName": "Default", "PairsProperties": { - "File": "PAIRS.PROPERTIES" + "File": "PAIRS.properties" }, "DCAProperties": { - "File": "DCA.PROPERTIES" + "File": "DCA.properties" }, "IndicatorsProperties": { - "File": "INDICATORS.PROPERTIES" + "File": "INDICATORS.properties" } } ], + // ================================ COIN-SPECIFIC SETTINGS ================================ + // "SingleMarketSettings": [ // Single market/pair settings for Profit Trailer properties + // Any setting from https://wiki.profittrailer.com/doku.php?id=pairs.properties + // marked as CS (coin-specific) can be used here. + // Only coins that meet the triggered conditions will have the settings applied. { - "SettingName": "BlacklistNewCoins", + "SettingName": "BlacklistCoins", "StopProcessWhenTriggered": true, "Triggers": [ { - "AgeDaysLowerThan": 14 + "AgeDaysLowerThan": 21 } ], "PairsProperties": { @@ -193,25 +266,33 @@ "DEFAULT_DCA_enabled": false } }, + // ----------------------------- { "SettingName": "PumpNDumpProtection", "TriggerConnection": "OR", - "StopProcessWhenTriggered": true, "Triggers": [ { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "Relative", // The relation of the single market trend. Relative = Single market trend compared relative to the market trend / Absolute = Single market trend viewn on its own - "MinChange": 5 + "MarketTrendName": "1h", + "MarketTrendRelation": "Relative", // The relation of the single market trend. Relative = Single market + // trend compared relative to the market trend + // Absolute = Single market trend viewed on its own + "MinChange": 8 }, { - "MarketTrendName": "Exchange12h", + "MarketTrendName": "12h", "MarketTrendRelation": "Relative", - "MinChange": 5 + "MinChange": 10 }, { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "24h", "MarketTrendRelation": "Relative", - "MinChange": 5 + "MinChange": 12 + } + ], + "OffTriggers": [ + { + "HoursSinceTriggered": 3 // Any coin that triggers this setting, will remain under this setting + // for 3 hours, since the last time it triggered. } ], "PairsProperties": { @@ -219,20 +300,23 @@ "DEFAULT_DCA_enabled": false } }, + // ----------------------------- { "SettingName": "FreefallBlock", "TriggerConnection": "OR", - "StopProcessWhenTriggered": true, "Triggers": [ { - "MarketTrendName": "Exchange1h", + "MarketTrendName": "1h", "MarketTrendRelation": "Absolute", "MaxChange": -5 } ], - "PairsProperties": { - "DEFAULT_trailing_profit_OFFSETPERCENT": -10, - "DEFAULT_A_sell_value_OFFSETPERCENT": -30, + "OffTriggers": [ + { + "HoursSinceTriggered": 1 + } + ], + "PairsProperties": { "DEFAULT_sell_only_mode_enabled": true, "DEFAULT_DCA_enabled": false } diff --git a/_Development/DevSettings/settings.analyzer.json b/_Development/DevSettings/settings.analyzer.json index d23f823..124697b 100644 --- a/_Development/DevSettings/settings.analyzer.json +++ b/_Development/DevSettings/settings.analyzer.json @@ -1,79 +1,263 @@ +// +// The settings below offer a basic example of some of the options available when using PTMagic. +// You should take your time and adjust these settings according to your own personal preferences. +// Always test your PTMagic settings by running a Profit Trailer bot in TESTMODE, to make sure +// it is performing as you expect. +// +// For more information on these settings, see the wiki at: https://github.com/PTMagicians/PTMagic/wiki/settings.analyzer + + { "AnalyzerSettings": { "MarketAnalyzer": { - "StoreDataMaxHours": 24, // Number of hours to store market data - "IntervalMinutes": 1, // Interval in minutes for PTMagic to check market trends and triggers + "StoreDataMaxHours": 48, // Number of hours to store market data + "IntervalMinutes": 2, // Interval in minutes for PTMagic to check market trends and triggers "ExcludeMainCurrency": true, // Excludes the main currency (for example BTC) from market trend analysis "MarketTrends": [ { - "Name": "CMC24h", // UNIQUE market trend name (to be referenced by your triggers below) - "Platform": "CoinMarketCap", // Platform to grab prices from (Allowed values are: CoinMarketCap, Exchange) - "MaxMarkets": 50, // Number of markets/pairs to analyze sorted by 24h volume - "TrendMinutes": 1440 // Number of minutes to build a trend (1440 = 24h, 720 = 12h, 60 = 1h) + "Name": "15m", // UNIQUE market trend name (to be referenced by your triggers below) + "Platform": "Exchange", // Platform to grab prices from (Allowed values are: CoinMarketCap, Exchange) + "MaxMarkets": 50, // Number of markets/pairs to analyze sorted by 24h volume + "TrendMinutes": 15, // Number of minutes to build a trend (1440 = 24h, 720 = 12h, 60 = 1h) + "TrendCurrency": "Market", // Trend Currency to build the trend against. If set to "Fiat", the trend will + // take the USD value of your main currency into account to build the trend. + // (Allowed values are: Fiat, Market) + "DisplayGraph": false, // Use this trend in the graph on the PTM Monitor dashboard and market analyzer + "DisplayOnMarketAnalyzerList": false // Disply this trend for all coins on the PTM Monitor market analyzer }, { - "Name": "Exchange1h", + "Name": "1h", "Platform": "Exchange", "MaxMarkets": 50, "TrendMinutes": 60, - "TrendCurrency": "Market" // Trend Currency to build the trend against. If set to "Fiat", the trend will take the USD value of your main currency into account to build the trend. (Allowed values are: Fiat, Market) + "TrendCurrency": "Market", + "DisplayGraph": true, + "DisplayOnMarketAnalyzerList": true }, { - "Name": "Exchange12h", + "Name": "6h", + "Platform": "Exchange", + "MaxMarkets": 50, + "TrendMinutes": 360, + "TrendCurrency": "Market", + "DisplayGraph": true, + "DisplayOnMarketAnalyzerList": true + }, + { + "Name": "12h", "Platform": "Exchange", "MaxMarkets": 50, "TrendMinutes": 720, - "TrendCurrency": "Market" + "TrendCurrency": "Market", + "DisplayGraph": true, + "DisplayOnMarketAnalyzerList": true }, { - "Name": "Exchange24h", + "Name": "24h", "Platform": "Exchange", "MaxMarkets": 50, "TrendMinutes": 1440, - "TrendCurrency": "Market" + "TrendCurrency": "Market", + "DisplayGraph": true, + "DisplayOnMarketAnalyzerList": true } ] }, + // ================================ GLOBAL SETTINGS ================================ + // "GlobalSettings": [ // Global settings for Profit Trailer properties + // + // ----------------------------- { - "SettingName": "EndOfTheWorld", // UNIQUE name of your setting + "SettingName": "EndOfTheWorld", // ANY UNIQUE name of your setting "TriggerConnection": "AND", // Define if triggers will be connected by AND or OR - "Triggers": [ // Your triggers for this setting + "Triggers": [ // Your triggers for this setting. You can use any of your defined trends from above { - "MarketTrendName": "Exchange12h", // Reference to the market trend specified above - "MaxChange": -1 // Maximum trend change % for this setting to get triggered + "MarketTrendName": "1h", // Reference to the market trend specified above + "MaxChange": 0 // The maximum value for this trigger to be true. (Any value below "0" will trigger this) }, { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "12h", + "MaxChange": -2 + }, + { + "MarketTrendName": "24h", "MaxChange": -5 } ], "PairsProperties": { // Properties for PAIRS.PROPERTIES - "DEFAULT_sell_only_mode_enabled": true + // Any valid setting from https://wiki.profittrailer.com/doku.php?id=pairs.properties can be used here. + // You can use a specific value, or apply a discrete OFFSET or OFFSETPERCENT to the value in your default PAIRS setting. + "DEFAULT_sell_only_mode_enabled": true, + "DEFAULT_trailing_profit_OFFSETPERCENT": -50 }, "DCAProperties": { // Properties for DCA.PROPERTIES + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -75 } }, + // ----------------------------- + { + "SettingName": "TankingDown", + "TriggerConnection": "AND", + "Triggers": [ + { + "MarketTrendName": "15m", + "MaxChange": 0 + }, + { + "MarketTrendName": "1h", + "MaxChange": 0 + }, + { + "MarketTrendName": "12h", + "MaxChange": 0 + }, + { + "MarketTrendName": "24h", // Any value between -5 and -3 will make this trigger true. + "MaxChange": -3, + "MinChange": -5 // The minimum value for this trigger to be true. (Any value above "-5" will trigger this) + } + ], + "PairsProperties": { + "max_trading_pairs_OFFSET": -2, + "DEFAULT_min_buy_volume_OFFSETPERCENT": 100, + //"DEFAULT_initial_cost_OFFSETPERCENT": -50, + //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": -50, + "DEFAULT_trailing_buy_OFFSETPERCENT": 25, + "DEFAULT_trailing_profit_OFFSETPERCENT": -25 + }, + "DCAProperties": { + //"DEFAULT_DCA_rebuy_timeout_OFFSETPERCENT": 100, + "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": 25, + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -50 + }, + "IndicatorsProperties": { + } + }, + // ----------------------------- + { + "SettingName": "BearSighted", + "TriggerConnection": "AND", + "Triggers": [ + { + "MarketTrendName": "1h", + "MaxChange": 1 + }, + { + "MarketTrendName": "12h", + "MaxChange": 0 + }, + { + "MarketTrendName": "24h", + "MaxChange": -1, + "MinChange": -3 + } + ], + "PairsProperties": { + "max_trading_pairs_OFFSET": -1, + //"DEFAULT_initial_cost_OFFSETPERCENT": -25, + //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": -25, + "DEFAULT_trailing_buy_OFFSETPERCENT": 10, + "DEFAULT_trailing_profit_OFFSETPERCENT": -10 + }, + "DCAProperties": { + "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": 10, + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -10, + }, + "IndicatorsProperties": { + } + }, + // ----------------------------- + { + "SettingName": "ReadyForLiftOff", + "TriggerConnection": "AND", + "Triggers": [ + { + "MarketTrendName": "1h", + "MinChange": 0 + }, + { + "MarketTrendName": "12h", + "MinChange": 0 + }, + { + "MarketTrendName": "24h", + "MinChange": 1, + "MaxChange": 3 + } + ], + "PairsProperties": { + "max_trading_pairs_OFFSET": 1, + //"DEFAULT_initial_cost_OFFSETPERCENT": 10, + //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": 10, + "DEFAULT_trailing_buy_OFFSETPERCENT": -10, + "DEFAULT_A_sell_value_OFFSETPERCENT": 10 + }, + "DCAProperties": { + "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -10, + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 10, + }, + "IndicatorsProperties": { + } + }, + // ----------------------------- + { + "SettingName": "ToTheMoon", + "TriggerConnection": "AND", + "Triggers": [ + { + "MarketTrendName": "1h", + "MinChange": 1 + }, + { + "MarketTrendName": "12h", + "MinChange": 1 + }, + { + "MarketTrendName": "24h", + "MinChange": 3 + } + ], + "PairsProperties": { + "max_trading_pairs_OFFSET": 2, + //"DEFAULT_initial_cost_OFFSETPERCENT": 20, + //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": 20, + "DEFAULT_trailing_buy_OFFSETPERCENT": -10, + "DEFAULT_A_sell_value_OFFSETPERCENT": 20 + }, + "DCAProperties": { + "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -20, + "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 20, + }, + "IndicatorsProperties": { + } + }, + // ----------------------------- { "SettingName": "Default", "PairsProperties": { - "File": "PAIRS.PROPERTIES" + "File": "PAIRS.properties" }, "DCAProperties": { - "File": "DCA.PROPERTIES" + "File": "DCA.properties" }, "IndicatorsProperties": { - "File": "INDICATORS.PROPERTIES" + "File": "INDICATORS.properties" } } ], + // ================================ COIN-SPECIFIC SETTINGS ================================ + // "SingleMarketSettings": [ // Single market/pair settings for Profit Trailer properties + // Any setting from https://wiki.profittrailer.com/doku.php?id=pairs.properties + // marked as CS (coin-specific) can be used here. + // Only coins that meet the triggered conditions will have the settings applied. { - "SettingName": "BlacklistNewCoins", + "SettingName": "BlacklistCoins", "StopProcessWhenTriggered": true, "Triggers": [ { - "AgeDaysLowerThan": 14 + "AgeDaysLowerThan": 21 } ], "PairsProperties": { @@ -82,46 +266,33 @@ "DEFAULT_DCA_enabled": false } }, + // ----------------------------- { "SettingName": "PumpNDumpProtection", "TriggerConnection": "OR", - "OffTriggerConnection": "OR", - "StopProcessWhenTriggered": true, "Triggers": [ { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "Relative", // The relation of the single market trend. Relative = Single market trend compared relative to the market trend / Absolute = Single market trend viewn on its own - "MinChange": 5 + "MarketTrendName": "1h", + "MarketTrendRelation": "Relative", // The relation of the single market trend. Relative = Single market + // trend compared relative to the market trend + // Absolute = Single market trend viewed on its own + "MinChange": 8 }, { - "MarketTrendName": "Exchange12h", + "MarketTrendName": "12h", "MarketTrendRelation": "Relative", - "MinChange": 7 + "MinChange": 10 }, { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "24h", "MarketTrendRelation": "Relative", - "MinChange": 7 + "MinChange": 12 } ], - "OffTriggers": [ - { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "RelativeTrigger", - "MaxChange": -4 - }, - { - "MarketTrendName": "Exchange12h", - "MarketTrendRelation": "RelativeTrigger", - "MaxChange": -5 - }, - { - "MarketTrendName": "Exchange24h", - "MarketTrendRelation": "RelativeTrigger", - "MaxChange": -5 - }, - { - "HoursSinceTriggered": 48 + "OffTriggers": [ + { + "HoursSinceTriggered": 3 // Any coin that triggers this setting, will remain under this setting + // for 3 hours, since the last time it triggered. } ], "PairsProperties": { @@ -129,225 +300,25 @@ "DEFAULT_DCA_enabled": false } }, + // ----------------------------- { "SettingName": "FreefallBlock", "TriggerConnection": "OR", - "OffTriggerConnection": "OR", - "StopProcessWhenTriggered": true, "Triggers": [ { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "Absolute", - "MaxChange": -8 - } - ], - "OffTriggers": [ - { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "RelativeTrigger", - "MinChange": 6 - }, - { - "HoursSinceTriggered": 24 - } - ], - "PairsProperties": { - "DEFAULT_trailing_profit_OFFSETPERCENT": -10, - "DEFAULT_A_sell_value_OFFSETPERCENT": -50, - "DEFAULT_sell_only_mode_enabled": true, - "DEFAULT_DCA_enabled": false - } - }, - { - "SettingName": "StraightToHell", - "TriggerConnection": "AND", - "StopProcessWhenTriggered": true, - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "Absolute", - "MaxChange": -1 - }, - { - "MarketTrendName": "Exchange12h", - "MarketTrendRelation": "Absolute", - "MaxChange": -3 - }, - { - "MarketTrendName": "Exchange24h", + "MarketTrendName": "1h", "MarketTrendRelation": "Absolute", "MaxChange": -5 } ], - "PairsProperties": { + "OffTriggers": [ + { + "HoursSinceTriggered": 1 + } + ], + "PairsProperties": { "DEFAULT_sell_only_mode_enabled": true, - "DEFAULT_trailing_profit_OFFSETPERCENT": -75, - "DEFAULT_A_sell_value_OFFSETPERCENT": -50 - }, - "DCAProperties": { - "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -75, - "DEFAULT_DCA_A_sell_value_OFFSETPERCENT": -50 - } - }, - { - "SettingName": "StrongDownTrend", - "TriggerConnection": "AND", - "StopProcessWhenTriggered": true, - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "Absolute", - "MaxChange": 1 - }, - { - "MarketTrendName": "Exchange12h", - "MarketTrendRelation": "Absolute", - "MaxChange": 0 - }, - { - "MarketTrendName": "Exchange24h", - "MarketTrendRelation": "Absolute", - "MaxChange": -3, - "MinChange": -5 - } - ], - "PairsProperties": { - "DEFAULT_A_buy_value_OFFSETPERCENT": -100, - "DEFAULT_B_buy_value_OFFSETPERCENT": -15, - "DEFAULT_trailing_buy_OFFSETPERCENT": 20, - "DEFAULT_trailing_profit_OFFSETPERCENT": -75, - "DEFAULT_A_sell_value_OFFSETPERCENT": -30 - }, - "DCAProperties": { - "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -75, - "DEFAULT_DCA_A_sell_value_OFFSETPERCENT": -30 - } - }, - { - "SettingName": "WeakDownTrend", - "TriggerConnection": "AND", - "StopProcessWhenTriggered": true, - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "Absolute", - "MaxChange": 1 - }, - { - "MarketTrendName": "Exchange12h", - "MarketTrendRelation": "Absolute", - "MaxChange": 0 - }, - { - "MarketTrendName": "Exchange24h", - "MarketTrendRelation": "Absolute", - "MaxChange": -1, - "MinChange": -3 - } - ], - "PairsProperties": { - "DEFAULT_A_buy_value_OFFSETPERCENT": -50, - "DEFAULT_B_buy_value_OFFSETPERCENT": -7, - "DEFAULT_trailing_profit_OFFSETPERCENT": -50, - "DEFAULT_A_sell_value_OFFSETPERCENT": -20 - }, - "DCAProperties": { - "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -50, - "DEFAULT_DCA_A_sell_value_OFFSETPERCENT": -20 - } - }, - { - "SettingName": "WeakUpTrend", - "TriggerConnection": "AND", - "StopProcessWhenTriggered": true, - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "Absolute", - "MinChange": -1 - }, - { - "MarketTrendName": "Exchange12h", - "MarketTrendRelation": "Absolute", - "MinChange": 0 - }, - { - "MarketTrendName": "Exchange24h", - "MarketTrendRelation": "Absolute", - "MinChange": 1, - "MaxChange": 3 - } - ], - "PairsProperties": { - "DEFAULT_A_buy_value_OFFSETPERCENT": 10, - "DEFAULT_B_buy_value_OFFSETPERCENT": 10, - "DEFAULT_A_sell_value_OFFSETPERCENT": 20 - }, - "DCAProperties": { - "DEFAULT_DCA_A_sell_value_OFFSETPERCENT": 20 - } - }, - { - "SettingName": "StrongUpTrend", - "TriggerConnection": "AND", - "StopProcessWhenTriggered": true, - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "Absolute", - "MinChange": -1 - }, - { - "MarketTrendName": "Exchange12h", - "MarketTrendRelation": "Absolute", - "MinChange": 1 - }, - { - "MarketTrendName": "Exchange24h", - "MarketTrendRelation": "Absolute", - "MinChange": 3, - "MaxChange": 5 - } - ], - "PairsProperties": { - "DEFAULT_A_buy_value_OFFSETPERCENT": 30, - "DEFAULT_B_buy_value_OFFSETPERCENT": 30, - "DEFAULT_trailing_buy_OFFSETPERCENT": -50, - "DEFAULT_A_sell_value_OFFSETPERCENT": 30 - }, - "DCAProperties": { - "DEFAULT_DCA_A_sell_value_OFFSETPERCENT": 30 - } - }, - { - "SettingName": "ToTheMoon", - "TriggerConnection": "AND", - "StopProcessWhenTriggered": true, - "Triggers": [ - { - "MarketTrendName": "Exchange1h", - "MarketTrendRelation": "Absolute", - "MinChange": 0 - }, - { - "MarketTrendName": "Exchange12h", - "MarketTrendRelation": "Absolute", - "MinChange": 1 - }, - { - "MarketTrendName": "Exchange24h", - "MarketTrendRelation": "Absolute", - "MinChange": 5 - } - ], - "PairsProperties": { - "DEFAULT_A_buy_value_OFFSETPERCENT": 30, - "DEFAULT_B_buy_value_OFFSETPERCENT": 30, - "DEFAULT_trailing_buy_OFFSETPERCENT": -50, - "DEFAULT_A_sell_value_OFFSETPERCENT": 40 - }, - "DCAProperties": { - "DEFAULT_DCA_A_sell_value_OFFSETPERCENT": 40 + "DEFAULT_DCA_enabled": false } } ] diff --git a/_Development/DevSettings/settings.general.json b/_Development/DevSettings/settings.general.json index e5691a5..af1820b 100644 --- a/_Development/DevSettings/settings.general.json +++ b/_Development/DevSettings/settings.general.json @@ -2,7 +2,7 @@ "GeneralSettings": { "Application": { "IsEnabled": true, // Enables the PTMagic bot (needs restart to take effect) - "TestMode": true, // If TestMode is active, no properties files will be changed + "TestMode": false, // If TestMode is active, no properties files will be changed "ProfitTrailerMajorVersion": 2, // Major version of your Profit Trailer (If you are using 1.2.x the major version is "1", if you are using 2.x the major version is "2" and so on) "ProfitTrailerPath": "YOUR PROFIT TRAILER PATH", // Path to your Profit Trailer main directory (use double backslashes for windows like C:\\ProfitTrailer\\) "ProfitTrailerLicense": "YOUR PROFIT TRAILER LICENSE KEY", // Your Profit Trailer license key (needed to change your settings for PT 2.0 and above) @@ -14,12 +14,12 @@ "MainFiatCurrency": "USD", // Your main fiat currency that will be used in the monitor "AlwaysLoadDefaultBeforeSwitch": true, // If this is enabled, PTMagic will always load default settings before switching to another setting "FloodProtectionMinutes": 15, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute - "InstanceName": "PT Magic Development", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages. In case you are running more than one bot, you may set different names to separate them - "CoinMarketCapAPIKey": "" //CoinMarketCap ApiKey + "InstanceName": "PT Magic", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages. In case you are running more than one bot, you may set different names to separate them + "CoinMarketCapAPIKey": "" //CoinMarketCap Api }, "Monitor": { "IsPasswordProtected": true, // Defines if your monitor will be asking to setup a password on its first start - "OpenBrowserOnStart": true, // If active, a browser window will open as soon as you start the monitor + "OpenBrowserOnStart": false, // If active, a browser window will open as soon as you start the monitor "Port": 5000, // The port you want to run your monitor on "RootUrl": "/", // The root Url of your monitor "GraphIntervalMinutes": 60, // The interval for the monitor market trend graph to draw points diff --git a/_Development/PTMGitCommands.bat b/_Development/PTMGitCommands.bat new file mode 100644 index 0000000..2bd4304 --- /dev/null +++ b/_Development/PTMGitCommands.bat @@ -0,0 +1,23 @@ +@echo off +:start +cls +echo PTMagic Git Commands +echo The following Commands are available: +echo 1: Sync current Branch with Main Repo(This will remove all changes you made!) +SET /P Input= Please enter an input(1): + +IF "%Input%" == "1" GOTO :sync + +GOTO :start + +:sync +git remote add upstream https://github.com/PTMagicians/PTMagic.git +git fetch upstream +git checkout develop +git reset --hard upstream/develop +git push origin develop --force +git pull origin develop +GOTO :end + +:end +pause \ No newline at end of file diff --git a/omnisharp.json b/omnisharp.json index be5f904..437b981 100644 --- a/omnisharp.json +++ b/omnisharp.json @@ -3,17 +3,17 @@ "UseTabs": false, "TabSize": 2, "IndentationSize": 2, - "NewLinesForBracesInTypes": false, - "NewLinesForBracesInMethods": false, - "NewLinesForBracesInProperties": false, - "NewLinesForBracesInAccessors": false, - "NewLinesForBracesInAnonymousMethods": false, - "NewLinesForBracesInControlBlocks": false, - "NewLinesForBracesInAnonymousTypes": false, - "NewLinesForBracesInObjectCollectionArrayInitializers": false, - "NewLinesForBracesInLambdaExpressionBody": false, - "NewLineForElse": false, - "NewLineForCatch": false, - "NewLineForFinally": false + "NewLinesForBracesInTypes": true, + "NewLinesForBracesInMethods": true, + "NewLinesForBracesInProperties": true, + "NewLinesForBracesInAccessors": true, + "NewLinesForBracesInAnonymousMethods": true, + "NewLinesForBracesInControlBlocks": true, + "NewLinesForBracesInAnonymousTypes": true, + "NewLinesForBracesInObjectCollectionArrayInitializers": true, + "NewLinesForBracesInLambdaExpressionBody": true, + "NewLineForElse": true, + "NewLineForCatch": true, + "NewLineForFinally": true } } \ No newline at end of file