2.0.6 Dev (#24)

* Reformat Code (#2)

* Multiple Changes (#13)

* Add "No CMC Key" Log Info

* Larger Tradingview Window

* Update json and NLog to latest version

* Update Remaining Packages

* Include CMC API Key in Settings Web Frontend

* Settings.General are now Collapsible

* Make Settings Analyzer Collapsible

* More Fixup

* Multiple Changes

* Even more fixups

* Add files via upload

* Binance API bad market fix

* Add Git Sync Command

* updated default settings

* Default Settings Fix
This commit is contained in:
HojouFotytu 2018-12-16 06:07:29 +09:00 committed by GitHub
parent 24d5c9d37a
commit 45181915bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
65 changed files with 4095 additions and 2816 deletions

3
.gitignore vendored
View File

@ -8,8 +8,9 @@ _[Dd]ata
_[Rr]eleases
_backups
LocalProfitTrailer
PTMagic/settings.*.json
PTMagic/settings.*
Monitor/appsettings.json
Monitor/Monitor
Release/

View File

@ -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<GlobalSetting> GlobalSettings { get; set; }
public List<SingleMarketSetting> 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<MarketTrend> 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<Trigger> Triggers { get; set; } = new List<Trigger>();
@ -139,7 +155,8 @@ namespace Core.Main.DataObjects.PTMagicData {
public Dictionary<string, object> IndicatorsProperties { get; set; } = new Dictionary<string, object>();
}
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<string, object> IndicatorsProperties { get; set; } = new Dictionary<string, object>();
}
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<StrategySummary> DCASellStrategies { get; set; } = new List<StrategySummary>();
}
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<string, MarketTrendChange> MarketTrendChanges { get; set; } = new Dictionary<string, MarketTrendChange>();
}
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<int, double> RelevantTriggers { get; set; } = new Dictionary<int, double>();
public List<string> MatchedTriggersContent { get; set; } = new List<string>();
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> SellLogData { get; set; } = new List<sellLogData>();
public List<dcaLogData> DCALogData { get; set; } = new List<dcaLogData>();
public List<dcaLogData> GainLogData { get; set; } = new List<dcaLogData>();
public List<buyLogData> bbBuyLogData { get; set; } = new List<buyLogData>();
}
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<PTStrategy> 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<Strategy> SellStrategies { get; set; } = new List<Strategy>();
}
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<PTStrategy> buyStrategies { get; set; }
}
public class BuyLogData {
public class BuyLogData
{
public double CurrentLowBBValue { get; set; }
public double CurrentHighBBValue { get; set; }
public double BBTrigger { get; set; }

View File

@ -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<SellLogData> _sellLog = new List<SellLogData>();
private List<DCALogData> _dcaLog = new List<DCALogData>();
private List<BuyLogData> _buyLog = new List<BuyLogData>();
@ -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;
@ -38,15 +41,18 @@ namespace Core.Main.DataObjects {
}
PTData rawPTData = JsonConvert.DeserializeObject<PTData>(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<SellLogData> SellLog {
get {
public List<SellLogData> SellLog
{
get
{
return _sellLog;
}
}
public List<SellLogData> SellLogToday {
get {
public List<SellLogData> SellLogToday
{
get
{
return _sellLog.FindAll(sl => sl.SoldDate.Date == _dateTimeNow.DateTime.Date);
}
}
public List<SellLogData> SellLogYesterday {
get {
public List<SellLogData> SellLogYesterday
{
get
{
return _sellLog.FindAll(sl => sl.SoldDate.Date == _dateTimeNow.DateTime.AddDays(-1).Date);
}
}
public List<SellLogData> SellLogLast7Days {
get {
public List<SellLogData> SellLogLast7Days
{
get
{
return _sellLog.FindAll(sl => sl.SoldDate.Date >= _dateTimeNow.DateTime.AddDays(-7).Date);
}
}
public List<DCALogData> DCALog {
get {
public List<DCALogData> DCALog
{
get
{
return _dcaLog;
}
}
public List<BuyLogData> BuyLog {
get {
public List<BuyLogData> 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<sellLogData> rawSellLogData, PTMagicConfiguration systemConfiguration) {
foreach (sellLogData rsld in rawSellLogData) {
private void BuildSellLogData(List<sellLogData> 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<dcaLogData> rawDCALogData, List<dcaLogData> rawPairsLogData, PTMagicConfiguration systemConfiguration) {
foreach (dcaLogData rdld in rawDCALogData) {
private void BuildDCALogData(List<dcaLogData> rawDCALogData, List<dcaLogData> 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<buyLogData> rawBuyLogData, PTMagicConfiguration systemConfiguration) {
foreach (buyLogData rbld in rawBuyLogData) {
private void BuildBuyLogData(List<buyLogData> rawBuyLogData, PTMagicConfiguration systemConfiguration)
{
foreach (buyLogData rbld in rawBuyLogData)
{
BuyLogData buyLogData = new BuyLogData();
buyLogData.Market = rbld.market;
buyLogData.ProfitPercent = rbld.profit;
@ -288,12 +332,17 @@ namespace Core.Main.DataObjects {
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;

View File

@ -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<Transaction> _transactions = new List<Transaction>();
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<List<Transaction>>(File.ReadAllText(transactionsFilePath));
}
}
public List<Transaction> Transactions {
get {
public List<Transaction> 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));
}
}

View File

@ -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;
}
}

View File

@ -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.~L<R 8NH-p=Ze<smbpb*]dP,%d9d{P{DC)R$xf]s|6UC-d)X[y_kDR^EsL";
}
}
public static string CryptoSaltValue {
get {
public static string CryptoSaltValue
{
get
{
return "/-T:_~Z|j~0%@~|?7,L~]:us9-=VO[.0V[nZDYTjnUeHcka#hdQ{U^YHv:0sJlfk";
}
}
public static string CryptoInitVector {
get {
public static string CryptoInitVector
{
get
{
return "qWEE:ADg)}6b;V{B";
}
}
public static string CryptoPassPhrase {
get {
public static string CryptoPassPhrase
{
get
{
return "KUBD`o.]*#CCL n9m}tZN4B4~>2EK>((/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 "";
}
}

View File

@ -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);
}
}

View File

@ -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<LogHelper> log;
public LogHelper(ILogger<LogHelper> logger) {
public LogHelper(ILogger<LogHelper> 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);
}
}

View File

@ -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<LogHelper>();

View File

@ -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 {
/// </summary>
/// <param name="s">The string to check.</param>
/// <returns>True, if the string is numeric.</returns>
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 {
/// </summary>
/// <param name="s">The string to check.</param>
/// <returns>True, if the string is a double value.</returns>
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 {
/// </summary>
/// <param name="s">The string to check.</param>
/// <returns>True, if the string is a DateTime value.</returns>
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 {
/// <param name="text">Zu konvertierender Text.</param>
/// <param name="defaultValue">Der Vorgabewert für den Fall, dass keine gültige Zahl eingegeben wurde.</param>
/// <returns>Den Text als Integer. Wenn die Konvertierung fehlschlägt, dann wird der Defaultwert zurückgegeben.</returns>
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 {
/// <param name="text">Zu konvertierender Text.</param>
/// <param name="defaultValue">Der Vorgabewert für den Fall, dass keine gültige Zahl eingegeben wurde.</param>
/// <returns>Den Text als Integer64. Wenn die Konvertierung fehlschlägt, dann wird der Defaultwert zurückgegeben.</returns>
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 {
/// <param name="text">Zu konvertierender Text.</param>
/// <param name="defaultValue">Der Vorgabewert für den Fall, dass keine gültige DateTime eingegeben wurde.</param>
/// <returns>Den Text als DateTime. Wenn die Konvertierung fehlschlägt, dann wird der Defaultwert zurückgegeben.</returns>
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 {
/// <param name="text">Zu konvertierender Text.</param>
/// <param name="defaultValue">Der Vorgabewert für den Fall, dass keine gültige Boolean eingegeben wurde.</param>
/// <returns>Den Text als Boolean. Wenn die Konvertierung fehlschlägt, dann wird der Defaultwert zurückgegeben.</returns>
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 {
/// <param name="text">Text to clear.</param>
/// <param name="allowedCharacters">Allowed characters.</param>
/// <returns>The cleared text.</returns>
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 {
/// <param name="text">Der Text, der gekürzt werden soll.</param>
/// <param name="maxLength">Die maximale Länge, auf die der Text gekürzt werden soll.</param>
/// <returns>Der gekürzte Text.</returns>
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 {
/// <summary>
/// Ermittelt den Teilstring eines Zeitstring, der die Stunden darstellt.
/// </summary>
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 {
/// <summary>
/// Ermittelt den Teilstring eines Zeitstring, der die Minuten darstellt.
/// </summary>
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<string> ConvertTokenStringToList(string tokenizedString, string separator) {
public static List<string> ConvertTokenStringToList(string tokenizedString, string separator)
{
List<string> result = new List<string>();
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<int> ConvertTokenStringToListInt(string tokenizedString, string separator) {
public static List<int> ConvertTokenStringToListInt(string tokenizedString, string separator)
{
List<int> result = new List<int>();
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<string> tokenList, string separator, bool cropDoubleSeparators) {
public static string ConvertListToTokenString(List<string> 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<int> tokenList, string separator) {
public static string ConvertListToTokenString(List<int> 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<object> ConvertToObjectList<T>(List<T> inputList) {
public static List<object> ConvertToObjectList<T>(List<T> inputList)
{
List<object> result = new List<object>();
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<int> currentVersionInfo = SystemHelper.ConvertTokenStringToListInt(currentVersion, ".");
List<int> 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;
}

View File

@ -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<Message> 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);
}
}

View File

@ -6,20 +6,25 @@ using System.IO;
using System.Text;
using ICSharpCode.SharpZipLib.Zip;
namespace Core.Helper {
namespace Core.Helper
{
public class ZIPHelper {
public class ZIPHelper
{
public static bool CreateZipFile(ArrayList filePaths, string outputPath) {
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);
}
}

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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<AnalyzerSettingsWrapper>(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<SecureSettingsWrapper>(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;

View File

@ -9,13 +9,17 @@ using Core.Main;
using Core.Helper;
using Core.Main.DataObjects.PTMagicData;
namespace Core.MarketAnalyzer {
public class BaseAnalyzer {
public static Dictionary<string, dynamic> GetJsonFromURL(string url, LogHelper log, string api) {
namespace Core.MarketAnalyzer
{
public class BaseAnalyzer
{
public static Dictionary<string, dynamic> GetJsonFromURL(string url, LogHelper log, string api)
{
Dictionary<string, dynamic> 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<Dictionary<string, dynamic>>(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<Newtonsoft.Json.Linq.JObject>(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<dynamic> GetSimpleJsonListFromURL(string url, LogHelper log) {
public static List<dynamic> GetSimpleJsonListFromURL(string url, LogHelper log)
{
List<dynamic> 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<List<dynamic>>(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<Newtonsoft.Json.Linq.JArray>(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<string, MarketInfo> GetMarketInfosFromFile(PTMagicConfiguration systemConfiguration, LogHelper log) {
public static Dictionary<string, MarketInfo> GetMarketInfosFromFile(PTMagicConfiguration systemConfiguration, LogHelper log)
{
Dictionary<string, MarketInfo> result = new Dictionary<string, MarketInfo>();
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<Dictionary<string, MarketInfo>>(System.IO.File.ReadAllText(marketInfoFilePath));
} catch (Exception ex) {
}
catch (Exception ex)
{
log.DoLogDebug(ex.Message);
}
}
if (result == null) {
if (result == null)
{
result = new Dictionary<string, MarketInfo>();
}
return result;
}
public static void SaveMarketInfosToFile(Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static void SaveMarketInfosToFile(Dictionary<string, MarketInfo> 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<string, Market> GetMarketDataFromFile(PTMagicConfiguration systemConfiguration, LogHelper log, string platform, DateTime maxDateTime, string marketCaption) {
public static Dictionary<string, Market> GetMarketDataFromFile(PTMagicConfiguration systemConfiguration, LogHelper log, string platform, DateTime maxDateTime, string marketCaption)
{
Dictionary<string, Market> result = new Dictionary<string, Market>();
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<Dictionary<string, Market>>(marketFile.OpenText().ReadToEnd());
} catch (Exception ex) {
}
catch (Exception ex)
{
log.DoLogCritical(ex.Message, ex);
}
return result;
}
public static Dictionary<string, List<MarketTrendChange>> BuildMarketTrends(string platform, string mainMarket, List<string> marketList, string sortBy, bool isGlobal, Dictionary<string, List<MarketTrendChange>> output, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static Dictionary<string, List<MarketTrendChange>> BuildMarketTrends(string platform, string mainMarket, List<string> marketList, string sortBy, bool isGlobal, Dictionary<string, List<MarketTrendChange>> output, PTMagicConfiguration systemConfiguration, LogHelper log)
{
try {
try
{
List<MarketTrend> marketTrends = systemConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.FindAll(mt => mt.Platform.Equals(platform, StringComparison.InvariantCultureIgnoreCase));
if (marketTrends.Count > 0) {
if (marketTrends.Count > 0)
{
Dictionary<string, Market> 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<MarketTrendChange> GetMarketTrendChanges(string platform, string mainMarket, MarketTrend marketTrend, List<string> marketList, Dictionary<string, Market> recentMarkets, Dictionary<string, Market> trendMarkets, string sortBy, bool isGlobal, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static List<MarketTrendChange> GetMarketTrendChanges(string platform, string mainMarket, MarketTrend marketTrend, List<string> marketList, Dictionary<string, Market> recentMarkets, Dictionary<string, Market> trendMarkets, string sortBy, bool isGlobal, PTMagicConfiguration systemConfiguration, LogHelper log)
{
List<MarketTrendChange> result = new List<MarketTrendChange>();
var sortedMarkets = new SortedDictionary<string, Market>(recentMarkets).OrderBy(m => m.Value.Position);
if (sortBy.Equals("Volume")) {
if (sortBy.Equals("Volume"))
{
sortedMarkets = new SortedDictionary<string, Market>(recentMarkets).OrderByDescending(m => m.Value.Volume24h);
}
int marketCount = 1;
foreach (KeyValuePair<string, Market> recentMarketPair in sortedMarkets) {
if (marketList.Count == 0 || marketList.Contains(recentMarketPair.Key)) {
foreach (KeyValuePair<string, Market> 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<string> 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<string> 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<string, double> BuildGlobalMarketTrends(Dictionary<string, List<MarketTrendChange>> globalMarketTrendChanges, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static Dictionary<string, double> BuildGlobalMarketTrends(Dictionary<string, List<MarketTrendChange>> globalMarketTrendChanges, PTMagicConfiguration systemConfiguration, LogHelper log)
{
Dictionary<string, double> result = new Dictionary<string, double>();
List<MarketTrend> 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<MarketTrendChange> 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%");

View File

@ -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<string> GetMarketData(string mainMarket, Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static List<string> GetMarketData(string mainMarket, Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
{
List<string> result = new List<string>();
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<string, Market> markets = new Dictionary<string, Market>();
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>();
float marketVolume = currencyTicker["volume"].ToObject<float>();
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<string, string> errorData = JsonConvert.DeserializeObject<Dictionary<string, string>>(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<string, Market> markets, ref Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static void CheckFirstSeenDates(Dictionary<string, Market> markets, ref Dictionary<string, MarketInfo> 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<MarketTick> GetMarketTicks(string marketName, int ticksNeeded, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static List<MarketTick> GetMarketTicks(string marketName, int ticksNeeded, PTMagicConfiguration systemConfiguration, LogHelper log)
{
List<MarketTick> result = new List<MarketTick>();
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<string, string> errorData = JsonConvert.DeserializeObject<Dictionary<string, string>>(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<string, Market> markets, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static void CheckForMarketDataRecreation(string mainMarket, Dictionary<string, Market> 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<FileInfo> 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<MarketTick> mainMarketTicks = new List<MarketTick>();
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<string, List<MarketTick>> marketTicks = new Dictionary<string, List<MarketTick>>();
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<MarketTick> 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<string, Market> tickMarkets = new Dictionary<string, Market>();
foreach (string key in markets.Keys) {
foreach (string key in markets.Keys)
{
List<MarketTick> 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...");
}
}

View File

@ -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<string, dynamic> 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<string> GetMarketData(string mainMarket, Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static List<string> GetMarketData(string mainMarket, Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
{
List<string> result = new List<string>();
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<string, dynamic> 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<string, Market> markets = new Dictionary<string, Market>();
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<MarketTick> GetMarketTicks(string marketName, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static List<MarketTick> GetMarketTicks(string marketName, PTMagicConfiguration systemConfiguration, LogHelper log)
{
List<MarketTick> result = new List<MarketTick>();
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<string, dynamic> 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<string, Market> markets, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static void CheckForMarketDataRecreation(string mainMarket, Dictionary<string, Market> 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<FileInfo> 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<MarketTick> mainMarketTicks = new List<MarketTick>();
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<string, List<MarketTick>> marketTicks = new Dictionary<string, List<MarketTick>>();
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<MarketTick> 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<string, Market> tickMarkets = new Dictionary<string, Market>();
foreach (string key in markets.Keys) {
foreach (string key in markets.Keys)
{
List<MarketTick> 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...");
}
}

View File

@ -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,26 +24,31 @@ namespace Core.MarketAnalyzer {
Dictionary<string, dynamic> 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<string, Market> markets = new Dictionary<string, Market>();
for (int i = 0; i < jsonObject["data"].Count; i++){
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"];
}
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);
markets.Add(market.Name, market);
}
}
@ -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<string, Market> markets, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static void CheckForMarketDataRecreation(Dictionary<string, Market> 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<string, Market> markets24h = new Dictionary<string, Market>();
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;

View File

@ -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<string, dynamic> 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<string> GetMarketData(string mainMarket, Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static List<string> GetMarketData(string mainMarket, Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
{
List<string> result = new List<string>();
string lastMarket = "";
KeyValuePair<string, dynamic> lastTicker = new KeyValuePair<string, dynamic>();
try {
try
{
string baseUrl = "https://poloniex.com/public?command=returnTicker";
log.DoLogInfo("Poloniex - Getting market data...");
Dictionary<string, dynamic> 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<string, Market> markets = new Dictionary<string, Market>();
foreach (KeyValuePair<string, dynamic> currencyTicker in jsonObject) {
foreach (KeyValuePair<string, dynamic> 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<string, Market> markets, ref Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static void CheckFirstSeenDates(Dictionary<string, Market> markets, ref Dictionary<string, MarketInfo> 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<dynamic> 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<MarketTick> GetMarketTicks(string marketName, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static List<MarketTick> GetMarketTicks(string marketName, PTMagicConfiguration systemConfiguration, LogHelper log)
{
List<MarketTick> result = new List<MarketTick>();
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&currencyPair=" + marketName;
log.DoLogDebug("Poloniex - Getting ticks for '" + marketName + "'...");
List<dynamic> 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<string, Market> markets, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static void CheckForMarketDataRecreation(string mainMarket, Dictionary<string, Market> 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<FileInfo> 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<MarketTick> mainMarketTicks = new List<MarketTick>();
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<string, List<MarketTick>> marketTicks = new Dictionary<string, List<MarketTick>>();
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<MarketTick> 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<string, Market> tickMarkets = new Dictionary<string, Market>();
foreach (string key in markets.Keys) {
foreach (string key in markets.Keys)
{
List<MarketTick> 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...");
}
}

View File

@ -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<string> GetPropertyLinesFromAPI(string ptFileName, PTMagicConfiguration systemConfiguration, LogHelper log) {
namespace Core.ProfitTrailer
{
public static class SettingsAPI
{
public static List<string> GetPropertyLinesFromAPI(string ptFileName, PTMagicConfiguration systemConfiguration, LogHelper log)
{
List<string> 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<List<string>>(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<string> lines, PTMagicConfiguration systemConfiguration, LogHelper log) {
public static void SendPropertyLinesToAPI(string ptFileName, List<string> 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;
}

View File

@ -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<string> 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<string> GetPresetFileLinesAsList(string settingName, string fileName, PTMagicConfiguration systemConfiguration) {
public static List<string> GetPresetFileLinesAsList(string settingName, string fileName, PTMagicConfiguration systemConfiguration)
{
return SettingsFiles.GetPresetFileLinesAsList(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar, settingName, fileName, systemConfiguration);
}
public static List<string> GetPresetFileLinesAsList(string baseFolderPath, string settingName, string fileName, PTMagicConfiguration systemConfiguration) {
public static List<string> 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<string> result = new List<string>();
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;

View File

@ -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<string> pairsLines, LogHelper log) {
namespace Core.ProfitTrailer
{
public static class SettingsHandler
{
public static string GetMainMarket(PTMagicConfiguration systemConfiguration, List<string> 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<string> pairsLines, LogHelper log) {
public static string GetMarketPairs(PTMagicConfiguration systemConfiguration, List<string> 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<string> fileLines = (List<string>)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<string, string> GetPropertiesAsDictionary(List<string> propertyLines) {
public static Dictionary<string, string> GetPropertiesAsDictionary(List<string> propertyLines)
{
Dictionary<string, string> result = new Dictionary<string, string>();
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<string, string> properties, string propertyKey, string fallbackPropertyKey) {
public static string GetCurrentPropertyValue(Dictionary<string, string> 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<string> result = new List<string>();
List<string> fileLines = (List<string>)ptmagicInstance.GetType().GetProperty(fileType + "Lines").GetValue(ptmagicInstance, null);
Dictionary<string, object> properties = (Dictionary<string, object>)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<string, object> defaultProperties = new Dictionary<string, object>();
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<string> BuildPropertyLine(List<string> result, string settingName, string line, Dictionary<string, object> properties, string settingProperty) {
public static List<string> BuildPropertyLine(List<string> result, string settingName, string line, Dictionary<string, object> 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<string, List<string>> matchedTriggers) {
try {
public static void CompileSingleMarketProperties(PTMagic ptmagicInstance, Dictionary<string, List<string>> matchedTriggers)
{
try
{
List<string> globalPairsLines = new List<string>();
List<string> globalDCALines = new List<string>();
List<string> globalIndicatorsLines = new List<string>();
@ -264,12 +322,16 @@ namespace Core.ProfitTrailer {
List<string> newDCALines = new List<string>();
List<string> newIndicatorsLines = new List<string>();
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<string, object> pairsPropertiesToApply = new Dictionary<string, object>();
Dictionary<string, object> dcaPropertiesToApply = new Dictionary<string, object>();
Dictionary<string, object> indicatorsPropertiesToApply = new Dictionary<string, object>();
// 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<string> BuildPropertyLinesForSingleMarketSetting(int ptMajorVersion, string mainMarket, string marketPair, List<SingleMarketSetting> appliedSettings, Dictionary<string, object> properties, Dictionary<string, List<string>> matchedTriggers, Dictionary<string, string> fullProperties, List<string> newPropertyLines, PTMagicConfiguration systemConfiguration, LogHelper log) {
if (properties.Keys.Count > 0) {
public static List<string> BuildPropertyLinesForSingleMarketSetting(int ptMajorVersion, string mainMarket, string marketPair, List<SingleMarketSetting> appliedSettings, Dictionary<string, object> properties, Dictionary<string, List<string>> matchedTriggers, Dictionary<string, string> fullProperties, List<string> 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<string> cleanedUpPairsLines = new List<string>();
List<string> cleanedUpDCALines = new List<string>();
List<string> cleanedUpIndicatorsLines = new List<string>();
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,7 +628,9 @@ 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);
}

View File

@ -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<Strategy> strategies, string strategyText, bool isTrue, bool isTrailingBuyActive) {
if (strategies.Count > 0) {
foreach (Strategy strategy in strategies) {
public static string GetStrategyText(Summary summary, List<Strategy> 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 += "<span class=\"label label-warning\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"" + strategy.Name + "\">" + StrategyHelper.GetStrategyShortcut(strategy.Name, false) + "</span> ";
} else {
}
else
{
strategyText += "<span class=\"label " + textClass + "\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"" + strategy.Name + "\">" + StrategyHelper.GetStrategyShortcut(strategy.Name, false) + "</span> ";
}
}
if (isTrailingBuyActive) {
if (isTrailingBuyActive)
{
strategyText += " <i class=\"fa fa-flag text-success\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"Trailing active!\"></i>";
}
} else {
if (isTrue) {
}
else
{
if (isTrue)
{
strategyText = "<span class=\"label label-success\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"" + strategyText + "\">" + StrategyHelper.GetStrategyShortcut(strategyText, true) + "</span>";
if (isTrailingBuyActive) {
if (isTrailingBuyActive)
{
strategyText += " <i class=\"fa fa-flag text-success\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"Trailing active!\"></i>";
}
} else {
if (StrategyHelper.IsValidStrategy(strategyText)) {
}
else
{
if (StrategyHelper.IsValidStrategy(strategyText))
{
strategyText = "<span class=\"label label-danger\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"" + strategyText + "\">" + StrategyHelper.GetStrategyShortcut(strategyText, true) + "</span>";
} else if (strategyText.Equals("")) {
}
else if (strategyText.Equals(""))
{
strategyText = summary.DCABuyStrategy;
strategyText = "<span class=\"label label-danger\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"" + strategyText + "\">" + StrategyHelper.GetStrategyShortcut(strategyText, true) + "</span>";
} else {
}
else
{
strategyText = "<span class=\"label label-warning\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"" + strategyText + "\">" + StrategyHelper.GetStrategyShortcut(strategyText, false) + "</span> ";
}
}
@ -256,43 +289,63 @@ namespace Core.ProfitTrailer {
return strategyText;
}
public static string GetCurrentValueText(List<Strategy> strategies, string strategyText, double bbValue, double simpleValue, bool includeShortcut) {
public static string GetCurrentValueText(List<Strategy> 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 += "<br />";
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 += "<span class=\"text-muted\">" + StrategyHelper.GetStrategyShortcut(strategy.Name, true) + "</span> ";
}
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<Strategy> strategies, string strategyText, double bbValue, double simpleValue, int buyLevel, bool includeShortcut) {
public static string GetTriggerValueText(Summary summary, List<Strategy> 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 += "<br />";
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 += "<span class=\"text-muted\">" + StrategyHelper.GetStrategyShortcut(strategy.Name, true) + "</span> ";
}
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];
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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<IExceptionHandlerFeature>();
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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<SingleMarketSettingSummary> SingleMarketSettingSummaries = new List<SingleMarketSettingSummary>();
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<List<SingleMarketSettingSummary>>(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;

View File

@ -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<MarketTrend> MarketTrends { get; set; } = new List<MarketTrend>();
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<MarketTrendChange> 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<MarketTrendChange> 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<MarketTrendChange> 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;

View File

@ -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<GlobalSetting> GlobalSettingsWithPresets = new List<GlobalSetting>();
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);
}
}

View File

@ -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<DateTime, double> MonthlyGains = new Dictionary<DateTime, double>();
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<string, double> topMarketsDic = new Dictionary<string, double>();
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<string, double>(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<SellLogData> 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<Core.Main.DataObjects.PTMagicData.SellLogData> 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);

View File

@ -68,7 +68,6 @@
<div class="row">
<div class="col-md-4">
<h4 class="m-t-0 header-title">Market Trends (@Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.Count)</h4>
<ul>
@foreach (Core.Main.DataObjects.PTMagicData.MarketTrend mt in Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends) {
<li><a href="#MarketTrend_@Core.Helper.SystemHelper.StripBadCode(mt.Name, Core.Main.Constants.WhiteListNames)">@Core.Helper.SystemHelper.SplitCamelCase(mt.Name)</a></li>
@ -102,32 +101,14 @@
</button>
</div>
<div class="col-md-4">
<div class="btn-group btn-block">
<button class="btn btn-ptmagic btn-custom btn-block text-uppercase waves-effect waves-light dropdown-toggle" data-toggle="dropdown">
Add Global Setting
</button>
<div class="dropdown-menu">
@foreach (Core.Main.DataObjects.PTMagicData.GlobalSetting gs in Model.PTMagicConfiguration.AnalyzerSettings.GlobalSettings) {
<a class="dropdown-item btn-new" data-datatype="GlobalSetting" data-datatarget="@Core.Helper.SystemHelper.StripBadCode(gs.SettingName, Core.Main.Constants.WhiteListNames)">Add before <i>@Core.Helper.SystemHelper.SplitCamelCase(gs.SettingName)</i></a>
}
</div>
</div>
<button class="btn btn-ptmagic btn-custom btn-block text-uppercase waves-effect waves-light btn-new" data-datatype="GlobalSetting">
Add Global Setting
</button>
</div>
<div class="col-md-4">
<div class="btn-group btn-block">
<button class="btn btn-ptmagic btn-custom btn-block text-uppercase waves-effect waves-light dropdown-toggle" data-toggle="dropdown">
Add Single Market Setting
</button>
<div class="dropdown-menu">
@foreach (Core.Main.DataObjects.PTMagicData.SingleMarketSetting sms in Model.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings) {
<a class="dropdown-item btn-new" data-datatype="SingleMarketSetting" data-datadirection="before" data-datatarget="@Core.Helper.SystemHelper.StripBadCode(sms.SettingName, Core.Main.Constants.WhiteListNames)">Add before <i>@Core.Helper.SystemHelper.SplitCamelCase(sms.SettingName)</i></a>
}
<div class="dropdown-divider"></div>
@foreach (Core.Main.DataObjects.PTMagicData.SingleMarketSetting sms in Model.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings) {
<a class="dropdown-item btn-new" data-datatype="SingleMarketSetting" data-datadirection="after" data-datatarget="@Core.Helper.SystemHelper.StripBadCode(sms.SettingName, Core.Main.Constants.WhiteListNames)">Add after <i>@Core.Helper.SystemHelper.SplitCamelCase(sms.SettingName)</i></a>
}
</div>
</div>
<button class="btn btn-ptmagic btn-custom btn-block text-uppercase waves-effect waves-light btn-new" data-datatype="SingleMarketSetting">
Add Single Market Setting
</button>
</div>
</div>
</div>
@ -138,26 +119,33 @@
<div class="row">
<div class="col-md-12">
<div class="card-box">
<h4 class="m-t-0 header-title">Market Analyzer</h4>
<div class="panel-group" style="">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><strong><a href="#collapse0" data-toggle="collapse">MARKET ANALYZER</a></strong></h3>
</div>
<div id="collapse0" class="panel-collapse collapse">
<div class="form-group row">
<label class="col-md-4 col-form-label">Store Data Max Hours <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Number of hours to store market data."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="MarketAnalyzer_StoreDataMaxHours" value="@Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours.ToString()">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Store Data Max Hours <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Number of hours to store market data."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="MarketAnalyzer_StoreDataMaxHours" value="@Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours.ToString()">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Interval Minutes <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Interval in minutes for PTMagic to check market trends and triggers."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="MarketAnalyzer_IntervalMinutes" value="@Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes.ToString()">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Interval Minutes <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Interval in minutes for PTMagic to check market trends and triggers."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="MarketAnalyzer_IntervalMinutes" value="@Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes.ToString()">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Exclude Main Currency <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Excludes the main currency (for example BTC) from market trend analysis."></i></label>
<div class="col-md-8">
<input type="checkbox" name="MarketAnalyzer_ExcludeMainCurrency" checked="@(Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.ExcludeMainCurrency)" data-plugin="switchery" data-color="#81c868" data-size="small" />
<div class="form-group row">
<label class="col-md-4 col-form-label">Exclude Main Currency <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Excludes the main currency (for example BTC) from market trend analysis."></i></label>
<div class="col-md-8">
<input type="checkbox" name="MarketAnalyzer_ExcludeMainCurrency" checked="@(Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.ExcludeMainCurrency)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
</div>
</div>
</div>
</div>
@ -167,7 +155,22 @@
<div id="MarketAnalyzer_MarketTrends">
@if (Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.Count > 0) {
@foreach (Core.Main.DataObjects.PTMagicData.MarketTrend mt in Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends) {
<div class="settings-markettrend" data-trendname="@Core.Helper.SystemHelper.StripBadCode(mt.Name, Core.Main.Constants.WhiteListNames)" data-rooturl="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></div>
<div class="row">
<div class="col-md-12">
<div class="card-box">
<div class="panel-group" style="">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><strong><a href="#@Core.Helper.SystemHelper.StripBadCode(mt.Name, Core.Main.Constants.WhiteListNames)" data-toggle="collapse">MARKET TREND: @Core.Helper.SystemHelper.StripBadCode(mt.Name, Core.Main.Constants.WhiteListNames)</a></strong></h3>
</div>
<div id="@Core.Helper.SystemHelper.StripBadCode(mt.Name, Core.Main.Constants.WhiteListNames)" class="panel-collapse collapse">
<div class="settings-markettrend" data-trendname="@Core.Helper.SystemHelper.StripBadCode(mt.Name, Core.Main.Constants.WhiteListNames)" data-rooturl="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></div>
</div>
</div>
</div>
</div>
</div>
</div>
}
} else {
<div class="row">
@ -183,7 +186,22 @@
<div id="MarketAnalyzer_GlobalSettings">
@if (Model.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Count > 0) {
@foreach (Core.Main.DataObjects.PTMagicData.GlobalSetting gs in Model.PTMagicConfiguration.AnalyzerSettings.GlobalSettings) {
<div class="settings-globalsetting" data-settingname="@Core.Helper.SystemHelper.StripBadCode(gs.SettingName, Core.Main.Constants.WhiteListNames)" data-rooturl="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></div>
<div class="row">
<div class="col-md-12">
<div class="card-box">
<div class="panel-group" style="">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><strong><a href="#@Core.Helper.SystemHelper.StripBadCode(gs.SettingName, Core.Main.Constants.WhiteListNames)" data-toggle="collapse">GLOBAL SETTING: @Core.Helper.SystemHelper.StripBadCode(gs.SettingName, Core.Main.Constants.WhiteListNames)</a></strong></h3>
</div>
<div id="@Core.Helper.SystemHelper.StripBadCode(gs.SettingName, Core.Main.Constants.WhiteListNames)" class="panel-collapse collapse">
<div class="settings-globalsetting" data-settingname="@Core.Helper.SystemHelper.StripBadCode(gs.SettingName, Core.Main.Constants.WhiteListNames)" data-rooturl="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></div>
</div>
</div>
</div>
</div>
</div>
</div>
}
} else {
<div class="row">
@ -199,7 +217,22 @@
<div id="MarketAnalyzer_SingleMarketSettings">
@if (Model.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings.Count > 0) {
@foreach (Core.Main.DataObjects.PTMagicData.SingleMarketSetting sms in Model.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings) {
<div class="settings-singlemarketsetting" data-settingname="@Core.Helper.SystemHelper.StripBadCode(sms.SettingName, Core.Main.Constants.WhiteListNames)" data-rooturl="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></div>
<div class="row">
<div class="col-md-12">
<div class="card-box">
<div class="panel-group" style="">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><strong><a href="#@Core.Helper.SystemHelper.StripBadCode(sms.SettingName, Core.Main.Constants.WhiteListNames)" data-toggle="collapse">SINGLE MARKET SETTING: @Core.Helper.SystemHelper.StripBadCode(sms.SettingName, Core.Main.Constants.WhiteListNames)</a></strong></h3>
</div>
<div id="@Core.Helper.SystemHelper.StripBadCode(sms.SettingName, Core.Main.Constants.WhiteListNames)" class="panel-collapse collapse">
<div class="settings-singlemarketsetting" data-settingname="@Core.Helper.SystemHelper.StripBadCode(sms.SettingName, Core.Main.Constants.WhiteListNames)" data-rooturl="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></div>
</div>
</div>
</div>
</div>
</div>
</div>
}
} else {
<div class="row">
@ -211,7 +244,6 @@
</div>
}
</div>
<div id="div-loading-settings" class="row">
<div class="col-md-12">
<div class="card-box">
@ -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('<div class="settings-globalsetting new" data-settingname="" data-rooturl="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></div>');
$('#MarketAnalyzer_GlobalSettings').append('<div class="settings-globalsetting new" data-settingname="" data-rooturl="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></div>');
$('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('<div class="settings-singlemarketsetting new" data-settingname="" data-rooturl="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></div>');
} else {
$('html, body').scrollTop($('#MarketAnalyzer_SingleMarketSettings > [data-settingname="' + dataTarget + '"]').offset().top + $('#MarketAnalyzer_SingleMarketSettings > [data-settingname="' + dataTarget + '"]').height() - 100);
$('#MarketAnalyzer_SingleMarketSettings > [data-settingname="' + dataTarget + '"]').after('<div class="settings-singlemarketsetting new" data-settingname="" data-rooturl="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></div>');
}
$('#MarketAnalyzer_SingleMarketSettings').append('<div class="settings-singlemarketsetting new" data-settingname="" data-rooturl="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></div>');
$('html, body').scrollTop($('#MarketAnalyzer_SingleMarketSettings').offset().top + $('#MarketAnalyzer_SingleMarketSettings').height() - 100);
$('.settings-singlemarketsetting.new').buildSingleMarketSettings();
break;
}

View File

@ -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<string> formKeys) {
private void SaveMarketTrends(List<string> formKeys)
{
List<MarketTrend> newMarketTrends = new List<MarketTrend>();
List<string> 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<string> formKeys) {
private void SaveGlobalSettings(List<string> formKeys)
{
List<GlobalSetting> newGlobalMarketSettings = new List<GlobalSetting>();
List<string> 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<Trigger> newTriggers = new List<Trigger>();
List<string> 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<string> formKeys) {
private void SaveSingleMarketSettings(List<string> formKeys)
{
List<SingleMarketSetting> newSingleMarketMarketSettings = new List<SingleMarketSetting>();
List<string> 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<Trigger> newTriggers = new List<Trigger>();
List<string> 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<string> 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<string> 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<OffTrigger> newOffTriggers = new List<OffTrigger>();
List<string> 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<string> 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<string> 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<string, object> GetProfitTrailerProperties(List<string> formKeys, string sFormKey, string propertyType) {
private Dictionary<string, object> GetProfitTrailerProperties(List<string> formKeys, string sFormKey, string propertyType)
{
Dictionary<string, object> result = new Dictionary<string, object>();
List<string> 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;
}
}

View File

@ -41,102 +41,116 @@
<div class="row">
<div class="col-md-12">
<div class="card-box">
<h4 class="m-t-0 header-title">Application</h4>
<div class="panel-group" style="">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><strong><a href="#collapse1" data-toggle="collapse">APPLICATION</a></strong></h3>
</div>
<div id="collapse1" class="panel-collapse collapse">
<div class="form-group row">
<label class="col-md-4 col-form-label">Is Enabled <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Enables the PTMagic bot."></i></label>
<div class="col-md-8">
<input type="checkbox" name="Application_IsEnabled" checked="@(Model.PTMagicConfiguration.GeneralSettings.Application.IsEnabled)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Is Enabled <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Enables the PTMagic bot."></i></label>
<div class="col-md-8">
<input type="checkbox" name="Application_IsEnabled" checked="@(Model.PTMagicConfiguration.GeneralSettings.Application.IsEnabled)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Test Mode <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="If TestMode is active, no properties files will be changed"></i></label>
<div class="col-md-8">
<input type="checkbox" name="Application_TestMode" checked="@(Model.PTMagicConfiguration.GeneralSettings.Application.TestMode)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Test Mode <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="If TestMode is active, no properties files will be changed"></i></label>
<div class="col-md-8">
<input type="checkbox" name="Application_TestMode" checked="@(Model.PTMagicConfiguration.GeneralSettings.Application.TestMode)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Profit Trailer Major Version <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Major version of your Profit Trailer."></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMajorVersion
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Profit Trailer Major Version <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Major version of your Profit Trailer."></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMajorVersion
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Profit Trailer Path <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Path to your Profit Trailer main directory."></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Profit Trailer Path <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Path to your Profit Trailer main directory."></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Profit Trailer License <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Your Profit Trailer license key (needed to change your settings for PT 2.0 and above)"></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GetProfitTrailerLicenseKeyMasked()
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Profit Trailer License <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Your Profit Trailer license key (needed to change your settings for PT 2.0 and above)"></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GetProfitTrailerLicenseKeyMasked()
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Profit Trailer Monitor URL <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The URL to your profit trailer monitor (needed to change your settings for PT 2.0 and above)"></i></label>
<div class="col-md-8">
<a href="@Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL" target="_blank">@Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL</a>
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Profit Trailer Monitor URL <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The URL to your profit trailer monitor (needed to change your settings for PT 2.0 and above)"></i></label>
<div class="col-md-8">
<a href="@Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL" target="_blank">@Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL</a>
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Profit Trailer Default Setting Name <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Your Profit Trailer default setting name (needed to change your settings for PT 2.0 and above)"></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Profit Trailer Default Setting Name <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Your Profit Trailer default setting name (needed to change your settings for PT 2.0 and above)"></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Exchange <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The exchange your are running Profit Trailer on."></i></label>
<div class="col-md-8">
<select name="Application_Exchange" class="form-control">
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance", StringComparison.InvariantCultureIgnoreCase))">Binance</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex", StringComparison.InvariantCultureIgnoreCase))">Bittrex</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex", StringComparison.InvariantCultureIgnoreCase))">Poloniex</option>
</select>
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Exchange <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The exchange your are running Profit Trailer on."></i></label>
<div class="col-md-8">
<select name="Application_Exchange" class="form-control">
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance", StringComparison.InvariantCultureIgnoreCase))">Binance</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex", StringComparison.InvariantCultureIgnoreCase))">Bittrex</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex", StringComparison.InvariantCultureIgnoreCase))">Poloniex</option>
</select>
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Start Balance <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The balance you had in your wallet when you started working with Profit Trailer."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Application_StartBalance" value="@Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Start Balance <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The balance you had in your wallet when you started working with Profit Trailer."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Application_StartBalance" value="@Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Timezone Offset <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Your timezone offset from GMT."></i></label>
<div class="col-md-8">
<select name="Application_TimezoneOffset" class="form-control">
@Html.Raw(Model.GetTimezoneSelection())
</select>
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Timezone Offset <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Your timezone offset from GMT."></i></label>
<div class="col-md-8">
<select name="Application_TimezoneOffset" class="form-control">
@Html.Raw(Model.GetTimezoneSelection())
</select>
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Always Load Default Before Switch <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="If this is enabled, PTMagic will always load default settings before switching to another setting."></i></label>
<div class="col-md-8">
<input type="checkbox" name="Application_AlwaysLoadDefaultBeforeSwitch" checked="@(Model.PTMagicConfiguration.GeneralSettings.Application.AlwaysLoadDefaultBeforeSwitch)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Always Load Default Before Switch <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="If this is enabled, PTMagic will always load default settings before switching to another setting."></i></label>
<div class="col-md-8">
<input type="checkbox" name="Application_AlwaysLoadDefaultBeforeSwitch" checked="@(Model.PTMagicConfiguration.GeneralSettings.Application.AlwaysLoadDefaultBeforeSwitch)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Flood Protection Minutes <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="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."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Application_FloodProtectionMinutes" value="@Model.PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes.ToString()">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Flood Protection Minutes <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="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."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Application_FloodProtectionMinutes" value="@Model.PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes.ToString()">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Instance Name <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="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."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Application_InstanceName" value="@Model.PTMagicConfiguration.GeneralSettings.Application.InstanceName">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Instance Name <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="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."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Application_InstanceName" value="@Model.PTMagicConfiguration.GeneralSettings.Application.InstanceName">
<div class="form-group row">
<label class="col-md-4 col-form-label">CoinMarketCap API Key <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The API Key that will be used to get Coin Data from CoinMarketCap."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Application_CoinMarketCapAPIKey" value="@Model.PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey">
</div>
</div>
</div>
</div>
</div>
</div>
@ -146,130 +160,137 @@
<div class="row">
<div class="col-md-12">
<div class="card-box">
<h4 class="m-t-0 header-title">Monitor</h4>
<div class="panel-group" style="">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><strong><a href="#collapse2" data-toggle="collapse">MONITOR</a></strong></h3>
</div>
<div id="collapse2" class="panel-collapse collapse">
<div class="form-group row">
<label class="col-md-4 col-form-label">Is Password Protected <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Defines if your monitor will be asking to setup a password on its first start."></i></label>
<div class="col-md-8">
<input type="checkbox" name="Monitor_IsPasswordProtected" checked="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Is Password Protected <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Defines if your monitor will be asking to setup a password on its first start."></i></label>
<div class="col-md-8">
<input type="checkbox" name="Monitor_IsPasswordProtected" checked="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Open Browser On Start <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="If active, a browser window will open as soon as you start the monitor."></i></label>
<div class="col-md-8">
<input type="checkbox" name="Monitor_OpenBrowserOnStart" checked="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.OpenBrowserOnStart)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Open Browser On Start <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="If active, a browser window will open as soon as you start the monitor."></i></label>
<div class="col-md-8">
<input type="checkbox" name="Monitor_OpenBrowserOnStart" checked="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.OpenBrowserOnStart)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Port <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The port you want to run your monitor on"></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GeneralSettings.Monitor.Port
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Port <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The port you want to run your monitor on"></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GeneralSettings.Monitor.Port
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">RootUrl <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The root URL of your monitor website"></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">RootUrl <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The root URL of your monitor website"></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Graph Interval Minutes <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The interval for the monitor market trend graph to draw points."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_GraphIntervalMinutes" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Graph Interval Minutes <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The interval for the monitor market trend graph to draw points."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_GraphIntervalMinutes" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Graph Max Timeframe Hours <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This defines the timeframe that your graph for market trends covers."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_GraphMaxTimeframeHours" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Graph Max Timeframe Hours <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This defines the timeframe that your graph for market trends covers."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_GraphMaxTimeframeHours" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Refresh Seconds <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval of your monitor main page."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_RefreshSeconds" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Refresh Seconds <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval of your monitor main page."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_RefreshSeconds" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Bag AnalyzerRefresh Seconds <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval of your monitor bag analyzer page."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_BagAnalyzerRefreshSeconds" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.BagAnalyzerRefreshSeconds.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Bag AnalyzerRefresh Seconds <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval of your monitor bag analyzer page."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_BagAnalyzerRefreshSeconds" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.BagAnalyzerRefreshSeconds.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Buy AnalyzerRefresh Seconds <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval of your monitor buy analyzer page."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_BuyAnalyzerRefreshSeconds" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Buy AnalyzerRefresh Seconds <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval of your monitor buy analyzer page."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_BuyAnalyzerRefreshSeconds" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Link Platform <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The platform to which the pair name will link if you click on it."></i></label>
<div class="col-md-8">
<select name="Monitor_LinkPlatform" class="form-control">
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform.Equals("TradingView", StringComparison.InvariantCultureIgnoreCase))">TradingView</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase))">Exchange</option>
</select>
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Link Platform <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The platform to which the pair name will link if you click on it."></i></label>
<div class="col-md-8">
<select name="Monitor_LinkPlatform" class="form-control">
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform.Equals("TradingView", StringComparison.InvariantCultureIgnoreCase))">TradingView</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase))">Exchange</option>
</select>
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Max Top Markets <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of top markets being show in your Sales Analyzer."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_MaxTopMarkets" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Max Top Markets <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of top markets being show in your Sales Analyzer."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_MaxTopMarkets" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Max Daily Summaries <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of Last Days being shown in your Sales Analyzer."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_MaxDailySummaries" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Max Daily Summaries <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of Last Days being shown in your Sales Analyzer."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_MaxDailySummaries" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Max Monthly Summaries <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of Last Months being shown in your Sales Analyzer."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_MaxMonthlySummaries" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Max Monthly Summaries <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of Last Months being shown in your Sales Analyzer."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_MaxMonthlySummaries" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Max Dashboard Buy Entries <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of entries being shown in your dashboard for possible buys."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_MaxDashboardBuyEntries" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDashboardBuyEntries.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Max Dashboard Buy Entries <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of entries being shown in your dashboard for possible buys."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_MaxDashboardBuyEntries" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDashboardBuyEntries.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Max Dashboard Bag Entries <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of top markets being shown in your dashboard for pairs and dca."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_MaxDashboardBagEntries" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDashboardBagEntries.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Max Dashboard Bag Entries <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of top markets being shown in your dashboard for pairs and dca."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_MaxDashboardBagEntries" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDashboardBagEntries.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Max DCA Pairs <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The number of pairs (rows) to be shown in your DCA calculator."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_MaxDCAPairs" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDCAPairs.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Max DCA Pairs <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The number of pairs (rows) to be shown in your DCA calculator."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_MaxDCAPairs" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDCAPairs.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Default DCA Mode <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Set the default mode of your DCA calculator.."></i></label>
<div class="col-md-8">
<select name="Monitor_DefaultDCAMode" class="form-control">
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.DefaultDCAMode.Equals("Simple", StringComparison.InvariantCultureIgnoreCase))">Simple</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.DefaultDCAMode.Equals("Advanced", StringComparison.InvariantCultureIgnoreCase))">Advanced</option>
</select>
<div class="form-group row">
<label class="col-md-4 col-form-label">Default DCA Mode <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Set the default mode of your DCA calculator.."></i></label>
<div class="col-md-8">
<select name="Monitor_DefaultDCAMode" class="form-control">
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.DefaultDCAMode.Equals("Simple", StringComparison.InvariantCultureIgnoreCase))">Simple</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.DefaultDCAMode.Equals("Advanced", StringComparison.InvariantCultureIgnoreCase))">Advanced</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
@ -279,19 +300,26 @@
<div class="row">
<div class="col-md-12">
<div class="card-box">
<h4 class="m-t-0 header-title">Backup</h4>
<div class="panel-group" style="">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><strong><a href="#collapse3" data-toggle="collapse">BACKUP</a></strong></h3>
</div>
<div id="collapse3" class="panel-collapse collapse">
<div class="form-group row">
<label class="col-md-4 col-form-label">Is Enabled <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Enables a backup procedure for your properties files. Before every switch PTMagic will backup the current properties."></i></label>
<div class="col-md-8">
<input type="checkbox" name="Backup_IsEnabled" checked="@(Model.PTMagicConfiguration.GeneralSettings.Backup.IsEnabled)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Is Enabled <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Enables a backup procedure for your properties files. Before every switch PTMagic will backup the current properties."></i></label>
<div class="col-md-8">
<input type="checkbox" name="Backup_IsEnabled" checked="@(Model.PTMagicConfiguration.GeneralSettings.Backup.IsEnabled)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Max Hours <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Max number of hours to keep backup files."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Backup_MaxHours" value="@Model.PTMagicConfiguration.GeneralSettings.Backup.MaxHours.ToString(new System.Globalization.CultureInfo("en-US"))">
<div class="form-group row">
<label class="col-md-4 col-form-label">Max Hours <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Max number of hours to keep backup files."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Backup_MaxHours" value="@Model.PTMagicConfiguration.GeneralSettings.Backup.MaxHours.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>
</div>
</div>
</div>
</div>
</div>
@ -301,42 +329,49 @@
<div class="row">
<div class="col-md-12">
<div class="card-box">
<h4 class="m-t-0 header-title">Telegram</h4>
<div class="panel-group" style="">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><strong><a href="#collapse4" data-toggle="collapse">TELEGRAM</a></strong></h3>
</div>
<div id="collapse4" class="panel-collapse collapse">
<div class="form-group row">
<label class="col-md-4 col-form-label">Is Enabled <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Enables PT Magic to send Telegram messages."></i></label>
<div class="col-md-8">
<input type="checkbox" name="Telegram_IsEnabled" checked="@(Model.PTMagicConfiguration.GeneralSettings.Telegram.IsEnabled)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Is Enabled <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Enables PT Magic to send Telegram messages."></i></label>
<div class="col-md-8">
<input type="checkbox" name="Telegram_IsEnabled" checked="@(Model.PTMagicConfiguration.GeneralSettings.Telegram.IsEnabled)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Bot Token <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Your Telegram bot token."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Telegram_BotToken" id="Telegram_BotToken" value="@Model.PTMagicConfiguration.GeneralSettings.Telegram.BotToken">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Bot Token <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Your Telegram bot token."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Telegram_BotToken" id="Telegram_BotToken" value="@Model.PTMagicConfiguration.GeneralSettings.Telegram.BotToken">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Chat Id <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Your Telegram Chat ID."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Telegram_ChatId" id="Telegram_ChatId" value="@Model.PTMagicConfiguration.GeneralSettings.Telegram.ChatId">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Chat Id <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Your Telegram Chat ID."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Telegram_ChatId" id="Telegram_ChatId" value="@Model.PTMagicConfiguration.GeneralSettings.Telegram.ChatId">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Silent Mode <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="If SilentMode is active, no notification sound or vibration will happen when the bot sends a Telegram message."></i></label>
<div class="col-md-8">
<input type="checkbox" name="Telegram_SilentMode" id="Telegram_SilentMode" checked="@(Model.PTMagicConfiguration.GeneralSettings.Telegram.SilentMode)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Silent Mode <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="If SilentMode is active, no notification sound or vibration will happen when the bot sends a Telegram message."></i></label>
<div class="col-md-8">
<input type="checkbox" name="Telegram_SilentMode" id="Telegram_SilentMode" checked="@(Model.PTMagicConfiguration.GeneralSettings.Telegram.SilentMode)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label"></label>
<div class="col-md-8">
<button id="btn-test-telegram" class="btn btn-ptmagic btn-block text-uppercase waves-effect waves-light">
Send Telegram Test Message
</button>
<div class="form-group row">
<label class="col-md-4 col-form-label"></label>
<div class="col-md-8">
<button id="btn-test-telegram" class="btn btn-ptmagic btn-block text-uppercase waves-effect waves-light">
Send Telegram Test Message
</button>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -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<string> tzOffsetList = new List<string>();
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");

View File

@ -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);

View File

@ -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<string> MarketsWithSingleSettings = new List<string>();
public string SettingsDistribution24hChartDataJSON = "";
public string SettingsDistribution3dChartDataJSON = "";
private Dictionary<string, string> settingsChartColors = new Dictionary<string, string>();
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<GlobalSettingSummary> gsSummaries24h = Summary.GlobalSettingSummary.FindAll(gss => gss.SwitchDateTime >= dateTime24hAgo);
IEnumerable<GlobalSettingSummary> 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<GlobalSettingSummary> gsSummaries3d = Summary.GlobalSettingSummary.FindAll(gss => gss.SwitchDateTime >= dateTime3dAgo);
IEnumerable<GlobalSettingSummary> 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];
}
}

View File

@ -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);

View File

@ -101,7 +101,7 @@
<div class="col-md-12">
<!-- TradingView Widget BEGIN -->
<div class="tradingview-widget-container">
<div id="tradingview_6aa22" style="height:300px;"></div>
<div id="tradingview_6aa22" style="height:600px;"></div>
<div class="tradingview-widget-copyright"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, Model.DCAMarket, Model.Summary.MainMarket)" rel="noopener" target="_blank"><span class="blue-text">@Model.DCAMarket</span> <span class="blue-text">chart</span> by TradingView</a></div>
</div>
<!-- TradingView Widget END -->

View File

@ -73,6 +73,7 @@
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);
<tr>
@if (mps != null && (mps.ActiveSingleSettings == null || mps.ActiveSingleSettings.Count == 0)) {
@ -111,7 +112,7 @@
</td>
<td class="text-right">@dcaLogEntry.CurrentPrice.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right">@dcaLogEntry.AverageBuyPrice.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right">@dcaLogEntry.TotalCost.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
<td class="text-right">@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")) + ")")</td>
<td><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)_get/BagDetails/?m=@dcaLogEntry.Market" data-remote="false" data-toggle="modal" data-target="#dca-chart" class="btn btn-sm btn-ptmagic">Details</a></td>
</tr>
}

View File

@ -85,7 +85,9 @@
<th class="text-right" data-toggle="tooltip" data-placement="top" title="Current DCA level">DCA</th>
<th data-toggle="tooltip" data-placement="top" title="Active buy strategies">Buy Strats</th>
<th data-toggle="tooltip" data-placement="top" title="Active sell strategies">Sell Strats</th>
<th class="text-right" data-toggle="tooltip" data-placement="top" title="Current profit percentage">Profit</th>
<th class="text-right" data-toggle="tooltip" data-placement="top" title=""></th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Current Profit">Profit</th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Current Total Cost">Cost</th>
<th></th>
</tr>
</thead>
@ -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);
<tr>
@ -154,8 +158,10 @@
@if (isTrailingSellActive) {
<i class="fa fa-flag-checkered text-success" data-toggle="tooltip" data-placement="top" title="Trailing sell active!"></i>
}
<span class="text-autocolor">@dcaLogEntry.ProfitPercent.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</span>
</td>
<td class="text-autocolor">@dcaLogEntry.ProfitPercent.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
<td class="text-left">@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")) + ")")</td>
<td class="text-right"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)_get/BagDetails/?m=@dcaLogEntry.Market" data-remote="false" data-toggle="modal" data-target="#dca-chart" class="btn btn-mini btn-ptmagic"><i class="fa fa-plus"></i></a></td>
</tr>
}

View File

@ -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<IViewComponentResult> InvokeAsync(Core.Main.DataObjects.PTMagicData.MarketPairSummary mps) {
namespace Monitor.ViewComponents
{
public class PairIconViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(Core.Main.DataObjects.PTMagicData.MarketPairSummary mps)
{
IViewComponentResult result = null;
await Task.Run(() => {
await Task.Run(() =>
{
result = View(mps);
});
return result;

View File

@ -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<string>("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 { }
}
catch { }
try {
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 {
/// <param name="paramName">Name des Parameters</param>
/// <param name="defaultValue">Defaultvalue, wenn Parameter nicht vorhanden ist.</param>
/// <returns>Der Wert des Parameters als Integer.</returns>
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;
}
}

View File

@ -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");
}
}

View File

@ -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");
}
}

File diff suppressed because one or more lines are too long

View File

@ -12,14 +12,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="NLog" Version="4.5.0-rc04" />
<PackageReference Include="NLog.Extensions.Logging" Version="1.0.0-rtm-rc6" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="NLog" Version="4.5.11" />
<PackageReference Include="NLog.Extensions.Logging" Version="1.3.0" />
</ItemGroup>
<ItemGroup>

View File

@ -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<LogHelper>());
ptMagic.CurrentVersion = Assembly.GetExecutingAssembly().GetName().Version;
@ -21,7 +24,8 @@ namespace PTMagic {
ptMagic.StartProcess();
// Keep the app running
for (; ; ) {
for (; ; )
{
Thread.Sleep(100);
}
}

View File

@ -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"
}
}
}

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- XSD manual extracted from package NLog.Schema: https://www.nuget.org/packages/NLog.Schema-->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Info" >
<!-- the targets to write to -->
<targets>
<!-- write logs to file -->
<target xsi:type="File" name="File" fileName="${basedir}/_logs/${shortdate}.txt"
layout="${date} ${level:uppercase=true} - ${message} ${exception:format=type,message,stacktrace}"
archiveEvery="Day"
archiveNumbering="Rolling"
maxArchiveFiles="7" />
<target xsi:type="ColoredConsole" name="Console"
layout="${date} ${level:uppercase=true} - ${message} ${exception:format=type,message,stacktrace}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<logger name="*" minlevel="Info" writeTo="Console" />
<logger name="*" minlevel="Info" writeTo="File" />
</rules>
</nlog>

View File

@ -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
}
}
]
}
}

View File

@ -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
}
}
}

View File

@ -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"
}
}
}

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- XSD manual extracted from package NLog.Schema: https://www.nuget.org/packages/NLog.Schema-->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Info" >
<!-- the targets to write to -->
<targets>
<!-- write logs to file -->
<target xsi:type="File" name="File" fileName="${basedir}/_logs/${shortdate}.txt"
layout="${date} ${level:uppercase=true} - ${message} ${exception:format=type,message,stacktrace}"
archiveEvery="Day"
archiveNumbering="Rolling"
maxArchiveFiles="7" />
<target xsi:type="ColoredConsole" name="Console"
layout="${date} ${level:uppercase=true} - ${message} ${exception:format=type,message,stacktrace}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<logger name="*" minlevel="Info" writeTo="Console" />
<logger name="*" minlevel="Info" writeTo="File" />
</rules>
</nlog>

View File

@ -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
}
}
]
}
}

View File

@ -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
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}
}
]

View File

@ -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

View File

@ -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

View File

@ -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
}
}