Merge pull request #314 from PTMagicians/develop

PTM 2.5.12
This commit is contained in:
HojouFotytu 2021-09-04 11:25:40 +09:00 committed by GitHub
commit 824cfa99ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 599 additions and 688 deletions

View File

@ -36,8 +36,10 @@ namespace Core.Main.DataObjects.PTMagicData
public bool TestMode { get; set; } = true;
public bool EnableBetaFeatures { get; set; } = false;
public string ProfitTrailerLicense { get; set; } = "";
public string ProfitTrailerLicenseXtra { get; set; } = "";
public string ProfitTrailerServerAPIToken { get; set; }
public string ProfitTrailerMonitorURL { get; set; } = "http://localhost:8081/";
public string ProfitTrailerMonitorURL { get; set; } = "";
public string ProfitTrailerMonitorURLXtra { get; set; } = "";
public string ProfitTrailerDefaultSettingName { get; set; } = "default";
public int FloodProtectionMinutes { get; set; } = 15;
public string Exchange { get; set; }
@ -517,4 +519,4 @@ namespace Core.Main.DataObjects.PTMagicData
}
#endregion
}
}

View File

@ -108,7 +108,28 @@ namespace Core.Main.DataObjects
if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh))
{
_sellLog.Clear();
this.BuildSellLogData(GetDataFromProfitTrailer("/api/v2/data/sales"));
// Page through the sales data summarizing it.
bool exitLoop = false;
int pageIndex = 1;
while (!exitLoop)
{
var sellDataPage = GetDataFromProfitTrailer("/api/v2/data/sales?perPage=5000&sort=SOLDDATE&sortDirection=ASCENDING&page=" + pageIndex);
if (sellDataPage != null && sellDataPage.data.Count > 0)
{
// Add sales data page to collection
this.BuildSellLogData(sellDataPage);
pageIndex++;
}
else
{
// All data retrieved
exitLoop = true;
}
}
// Update sell log refresh time
_sellLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
}
}
@ -265,7 +286,10 @@ namespace Core.Main.DataObjects
private dynamic GetDataFromProfitTrailer(string callPath, bool arrayReturned = false)
{
string rawBody = "";
string url = string.Format("{0}{1}?token={2}", _systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL, callPath, _systemConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken);
string url = string.Format("{0}{1}{2}token={3}", _systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL,
callPath,
callPath.Contains("?") ? "&" : "?",
_systemConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken);
// Get the data from PT
Debug.WriteLine(String.Format("{0} - Calling '{1}'", DateTime.UtcNow, url));
@ -331,15 +355,9 @@ namespace Core.Main.DataObjects
sellLogData.ProfitPercent = rsld.profit;
sellLogData.SoldPrice = rsld.currentPrice;
sellLogData.AverageBuyPrice = rsld.avgPrice;
sellLogData.TotalCost = sellLogData.SoldAmount * sellLogData.AverageBuyPrice;
sellLogData.TotalCost = rsld.totalCost;
sellLogData.Profit = rsld.profitCurrency;
// check if bot is a shortbot via PT API. Losses on short bot currently showing as gains. Issue #195
// code removed
double soldValueRaw = (sellLogData.SoldAmount * sellLogData.SoldPrice);
double soldValueAfterFees = soldValueRaw - (soldValueRaw * ((double)rsld.fee / 100));
sellLogData.SoldValue = soldValueAfterFees;
sellLogData.Profit = Math.Round(sellLogData.SoldValue - sellLogData.TotalCost, 8);
//Convert Unix Timestamp to Datetime
System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc);

View File

@ -8,19 +8,15 @@ namespace Core.Helper
{
public static class TelegramHelper
{
public static void SendMessage(string botToken, Int64 chatId, string message, bool useSilentMode, LogHelper log)
public async 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)
{
log.DoLogDebug("Telegram message sent to ChatId " + chatId.ToString() + " on Bot Token '" + botToken + "'");
}
await botClient.SendTextMessageAsync(chatId: chatId, text: message, parseMode: ParseMode.Markdown,disableNotification: useSilentMode);
log.DoLogDebug("Telegram message sent to ChatId " + chatId.ToString() + " on Bot Token '" + botToken + "'");
}
catch (Exception ex)
{

View File

@ -1,5 +1,6 @@
using System;
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Threading;
using System.IO;
using System.Linq;
@ -52,7 +53,7 @@ namespace Core.Main
private List<string> _indicatorsLines = null;
private List<string> _exchangeMarketList = null;
private List<string> _marketList = new List<string>();
private Dictionary<string, MarketInfo> _marketInfos = new Dictionary<string, MarketInfo>();
private ConcurrentDictionary<string, MarketInfo> _marketInfos = new ConcurrentDictionary<string, MarketInfo>();
private Dictionary<string, double> _averageMarketTrendChanges = new Dictionary<string, double>();
private Dictionary<string, List<MarketTrendChange>> _singleMarketTrendChanges = new Dictionary<string, List<MarketTrendChange>>();
private Dictionary<string, List<MarketTrendChange>> _globalMarketTrendChanges = new Dictionary<string, List<MarketTrendChange>>();
@ -432,7 +433,7 @@ namespace Core.Main
}
}
public Dictionary<string, MarketInfo> MarketInfos
public ConcurrentDictionary<string, MarketInfo> MarketInfos
{
get
{
@ -701,13 +702,13 @@ namespace Core.Main
// Check if the program is enabled
if (this.PTMagicConfiguration.GeneralSettings.Application.IsEnabled)
{
result = RunProfitTrailerSettingsAPIChecks();
try
{
if (this.PTMagicConfiguration.GeneralSettings.Application.TestMode)
{
this.Log.DoLogInfo("TESTMODE ENABLED - No files will be changed!");
this.Log.DoLogWarn("TESTMODE ENABLED - No PT settings will be changed!");
}
result = RunProfitTrailerSettingsAPIChecks();
// Check for CoinMarketCap API Key
if (!String.IsNullOrEmpty(this.PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey))
@ -731,7 +732,7 @@ namespace Core.Main
}
catch (System.NullReferenceException)
{
this.Log.DoLogError("PTM failed to read the Config File. That means something in the File is either missing or incorrect. If this happend after an update please take a look at the release notes at: https://github.com/PTMagicians/PTMagic/releases");
this.Log.DoLogError("PTM failed to read the General Settings file. That means something in the file is either missing or incorrect. If this happend after an update please take a look at the release notes at: https://github.com/PTMagicians/PTMagic/releases");
Console.WriteLine("Press enter to close the Application...");
Console.ReadLine();
Environment.Exit(0);
@ -739,7 +740,7 @@ namespace Core.Main
}
else
{
this.Log.DoLogWarn("PTMagic disabled, shutting down...");
this.Log.DoLogWarn("PTMagic is disabled. The scheduled raid was skipped.");
result = false;
}
@ -952,7 +953,7 @@ namespace Core.Main
this.Log.DoLogInfo("+ Active setting: " + this.LastRuntimeSummary.CurrentGlobalSetting.SettingName);
this.Log.DoLogInfo("+ Global setting changed: " + ((this.LastRuntimeSummary.LastGlobalSettingSwitch == this.LastRuntimeSummary.LastRuntime) ? "Yes" : "No") + " " + ((this.LastRuntimeSummary.FloodProtectedSetting != null) ? "(Flood protection!)" : ""));
this.Log.DoLogInfo("+ Single Market Settings changed: " + (this.SingleMarketSettingChanged ? "Yes" : "No"));
this.Log.DoLogInfo("+ PT Config updated: " + (((this.GlobalSettingWritten || this.SingleMarketSettingChanged) && !this.PTMagicConfiguration.GeneralSettings.Application.TestMode) ? "Yes" : "No"));
this.Log.DoLogInfo("+ PT Config updated: " + (((this.GlobalSettingWritten || this.SingleMarketSettingChanged) && !this.PTMagicConfiguration.GeneralSettings.Application.TestMode) ? "Yes" : "No") + ((this.PTMagicConfiguration.GeneralSettings.Application.TestMode) ? " - TESTMODE active" : ""));
this.Log.DoLogInfo("+ Markets with active single market settings: " + this.TriggeredSingleMarketSettings.Count.ToString());
foreach (string activeSMS in this.SingleMarketSettingsCount.Keys)
{
@ -969,7 +970,7 @@ namespace Core.Main
else
{
this.State = Constants.PTMagicBotState_Idle;
Log.DoLogWarn("PTMagic disabled, shutting down until next raid...");
Log.DoLogWarn("PTMagic is disabled. The scheduled raid was skipped.");
}
}
catch (Exception ex)
@ -1538,7 +1539,7 @@ namespace Core.Main
market = market.Replace("-", "");
break;
case "poloniex":
market = market.Replace("-", "");
market = market.Replace("_", "");
break;
}
bool stopTriggers = false;
@ -2194,9 +2195,12 @@ namespace Core.Main
if (!this.PTMagicConfiguration.GeneralSettings.Application.TestMode)
{
SettingsAPI.SendPropertyLinesToAPI(this.PairsLines, this.DCALines, this.IndicatorsLines, this.PTMagicConfiguration, this.Log);
this.Log.DoLogInfo("Settings updates sent to PT!");
}
else
{
this.Log.DoLogWarn("TESTMODE enabled -- no updates sent to PT!");
}
this.Log.DoLogInfo("Properties saved!");
}
else
{

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Net;
@ -153,16 +154,16 @@ namespace Core.MarketAnalyzer
return result;
}
public static Dictionary<string, MarketInfo> GetMarketInfosFromFile(PTMagicConfiguration systemConfiguration, LogHelper log)
public static ConcurrentDictionary<string, MarketInfo> GetMarketInfosFromFile(PTMagicConfiguration systemConfiguration, LogHelper log)
{
Dictionary<string, MarketInfo> result = new Dictionary<string, MarketInfo>();
ConcurrentDictionary<string, MarketInfo> result = new ConcurrentDictionary<string, MarketInfo>();
string marketInfoFilePath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar + "MarketInfo.json";
if (File.Exists(marketInfoFilePath))
{
try
{
result = JsonConvert.DeserializeObject<Dictionary<string, MarketInfo>>(System.IO.File.ReadAllText(marketInfoFilePath));
result = JsonConvert.DeserializeObject<ConcurrentDictionary<string, MarketInfo>>(System.IO.File.ReadAllText(marketInfoFilePath));
}
catch (Exception ex)
{
@ -171,12 +172,12 @@ namespace Core.MarketAnalyzer
}
if (result == null)
{
result = new Dictionary<string, MarketInfo>();
result = new ConcurrentDictionary<string, MarketInfo>();
}
return result;
}
public static void SaveMarketInfosToFile(Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
public static void SaveMarketInfosToFile(ConcurrentDictionary<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));
}
@ -316,7 +317,7 @@ namespace Core.MarketAnalyzer
market = market.Replace("-", "");
break;
case "poloniex":
market = market.Replace("-", "");
market = market.Replace("_", "");
break;
}
if (recentMarkets.TryGetValue(recentMarketPair.Key, out recentMarket))

View File

@ -1,17 +1,14 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.IO;
using System.Text;
using Core.Main;
using Core.Helper;
using Core.Main.DataObjects.PTMagicData;
using Newtonsoft.Json;
using Core.ProfitTrailer;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
namespace Core.MarketAnalyzer
{
@ -43,7 +40,7 @@ namespace Core.MarketAnalyzer
return result;
}
public static List<string> GetMarketData(string mainMarket, Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
public static List<string> GetMarketData(string mainMarket, ConcurrentDictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
{
List<string> result = new List<string>();
@ -163,7 +160,7 @@ 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 ConcurrentDictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
{
log.DoLogInfo("Binance - Checking first seen dates for " + markets.Count + " markets. This may take a while...");
@ -182,7 +179,7 @@ namespace Core.MarketAnalyzer
{
marketInfo = new MarketInfo();
marketInfo.Name = key;
marketInfos.Add(key, marketInfo);
marketInfos.TryAdd(key, marketInfo);
marketInfo.FirstSeen = Binance.GetFirstSeenDate(key, systemConfiguration, log);
}
else
@ -375,7 +372,7 @@ namespace Core.MarketAnalyzer
}
Parallel.ForEach(markets.Keys,
new ParallelOptions { MaxDegreeOfParallelism = ParallelThrottle},
new ParallelOptions { MaxDegreeOfParallelism = ParallelThrottle },
(key) =>
{
if (!marketTicks.TryAdd(key, GetMarketTicks(key, totalTicks, systemConfiguration, log)))
@ -413,26 +410,28 @@ namespace Core.MarketAnalyzer
}
}
Dictionary<string, Market> tickMarkets = new Dictionary<string, Market>();
foreach (string key in markets.Keys)
{
List<MarketTick> tickRange = marketTicks[key] != null ? marketTicks[key].FindAll(t => t.Time <= tickTime) : new List<MarketTick>();
ConcurrentDictionary<string, Market> tickMarkets = new ConcurrentDictionary<string, Market>();
if (tickRange.Count > 0)
{
MarketTick marketTick = tickRange.OrderByDescending(t => t.Time).First();
Parallel.ForEach(markets.Keys,
(key) =>
{
List<MarketTick> tickRange = marketTicks[key] != null ? marketTicks[key].FindAll(t => t.Time <= tickTime) : new List<MarketTick>();
Market market = new Market();
market.Position = markets.Count + 1;
market.Name = key;
market.Symbol = key;
market.Price = marketTick.Price;
//market.Volume24h = marketTick.Volume24h;
market.MainCurrencyPriceUSD = mainCurrencyPrice;
if (tickRange.Count > 0)
{
MarketTick marketTick = tickRange.OrderByDescending(t => t.Time).First();
tickMarkets.Add(market.Name, market);
}
}
Market market = new Market();
market.Position = markets.Count + 1;
market.Name = key;
market.Symbol = key;
market.Price = marketTick.Price;
//market.Volume24h = marketTick.Volume24h;
market.MainCurrencyPriceUSD = mainCurrencyPrice;
tickMarkets.TryAdd(market.Name, market);
}
});
DateTime fileDateTime = new DateTime(tickTime.ToLocalTime().Year, tickTime.ToLocalTime().Month, tickTime.ToLocalTime().Day, tickTime.ToLocalTime().Hour, tickTime.ToLocalTime().Minute, 0).ToUniversalTime();

View File

@ -1,17 +1,14 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.IO;
using System.Text;
using Core.Main;
using Core.Helper;
using Core.Main.DataObjects.PTMagicData;
using Newtonsoft.Json;
using Core.ProfitTrailer;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
namespace Core.MarketAnalyzer
{
@ -43,7 +40,7 @@ namespace Core.MarketAnalyzer
return result;
}
public static List<string> GetMarketData(string mainMarket, Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
public static List<string> GetMarketData(string mainMarket, ConcurrentDictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
{
List<string> result = new List<string>();
@ -160,7 +157,7 @@ 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 ConcurrentDictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
{
log.DoLogInfo("BinanceFutures - Checking first seen dates for " + markets.Count + " markets. This may take a while...");
@ -179,7 +176,7 @@ namespace Core.MarketAnalyzer
{
marketInfo = new MarketInfo();
marketInfo.Name = key;
marketInfos.Add(key, marketInfo);
marketInfos.TryAdd(key, marketInfo);
marketInfo.FirstSeen = BinanceFutures.GetFirstSeenDate(key, systemConfiguration, log);
}
else

View File

@ -2,14 +2,11 @@
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using Core.Main;
using Core.Helper;
using Core.Main.DataObjects.PTMagicData;
using Newtonsoft.Json;
using Core.ProfitTrailer;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
@ -48,7 +45,7 @@ namespace Core.MarketAnalyzer
}
}
public static List<string> GetMarketData(string mainMarket, Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
public static List<string> GetMarketData(string mainMarket, ConcurrentDictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
{
List<string> result = new List<string>();
@ -168,7 +165,7 @@ 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 ConcurrentDictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
{
log.DoLogInfo("BinanceUS - Checking first seen dates for " + markets.Count + " markets. This may take a while...");
@ -187,7 +184,7 @@ namespace Core.MarketAnalyzer
{
marketInfo = new MarketInfo();
marketInfo.Name = key;
marketInfos.Add(key, marketInfo);
marketInfos.TryAdd(key, marketInfo);
marketInfo.FirstSeen = BinanceUS.GetFirstSeenDate(key, systemConfiguration, log);
}
else

View File

@ -1,14 +1,12 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.IO;
using System.Text;
using Core.Main;
using Core.Helper;
using Core.Main.DataObjects.PTMagicData;
using Newtonsoft.Json;
using Core.ProfitTrailer;
using System.Threading;
using System.Threading.Tasks;
namespace Core.MarketAnalyzer
@ -44,7 +42,7 @@ namespace Core.MarketAnalyzer
return result;
}
public static List<string> GetMarketData(string mainMarket, Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
public static List<string> GetMarketData(string mainMarket, ConcurrentDictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
{
List<string> result = new List<string>();
@ -104,7 +102,7 @@ namespace Core.MarketAnalyzer
{
marketInfo = new MarketInfo();
marketInfo.Name = marketName;
marketInfos.Add(marketName, marketInfo);
marketInfos.TryAdd(marketName, marketInfo);
}
if (currencyTicker["Summary"]["Created"].Type == Newtonsoft.Json.Linq.JTokenType.Date) marketInfo.FirstSeen = (DateTime)currencyTicker["Summary"]["Created"];
marketInfo.LastSeen = DateTime.UtcNow;

View File

@ -1,14 +1,12 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.IO;
using System.Text;
using Core.Main;
using Core.Helper;
using Core.Main.DataObjects.PTMagicData;
using Newtonsoft.Json;
using Core.ProfitTrailer;
using System.Threading;
using System.Threading.Tasks;
namespace Core.MarketAnalyzer
@ -44,7 +42,7 @@ namespace Core.MarketAnalyzer
return result;
}
public static List<string> GetMarketData(string mainMarket, Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
public static List<string> GetMarketData(string mainMarket, ConcurrentDictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
{
List<string> result = new List<string>();
@ -125,7 +123,7 @@ 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 ConcurrentDictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
{
log.DoLogInfo("Poloniex - Checking first seen dates for " + markets.Count + " markets. This may take a while...");
@ -142,7 +140,7 @@ namespace Core.MarketAnalyzer
{
marketInfo = new MarketInfo();
marketInfo.Name = key;
marketInfos.Add(key, marketInfo);
marketInfos.TryAdd(key, marketInfo);
marketInfo.FirstSeen = Poloniex.GetFirstSeenDate(key, systemConfiguration, log);
}
else

View File

@ -19,95 +19,122 @@ namespace Core.ProfitTrailer
bool transferCompleted = false;
bool transferCanceled = false;
while (!transferCompleted && !transferCanceled)
// get PT license list
string licenses = systemConfiguration.GeneralSettings.Application.ProfitTrailerLicense;
if (systemConfiguration.GeneralSettings.Application.ProfitTrailerLicenseXtra != "")
{
try
licenses = licenses + ", " + systemConfiguration.GeneralSettings.Application.ProfitTrailerLicenseXtra;
}
List<string> licenseList = SystemHelper.ConvertTokenStringToList(licenses, ",");
int licenseCount = licenseList.Count;
// get URL list
string urls = systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL;
if (systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURLXtra != "")
{
urls = urls + ", " + systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURLXtra;
}
List<string> urlList = SystemHelper.ConvertTokenStringToList(urls, ",");
int urlCount = urlList.Count;
log.DoLogInfo("Found " + licenseCount + " licenses and " + urlCount + " URLs");
if (urlCount != licenseCount)
{
log.DoLogWarn("ERROR - urlCount must match licenseCount");
}
for (int i = 0; i < licenseCount; i++)
{
transferCompleted = false;
while (!transferCompleted && !transferCanceled)
{
ServicePointManager.Expect100Continue = true;
ServicePointManager.DefaultConnectionLimit = 9999;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(CertificateHelper.AllwaysGoodCertificate);
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL + "settingsapi/settings/saveAll");
httpWebRequest.ContentType = "application/x-www-form-urlencoded";
httpWebRequest.Method = "POST";
httpWebRequest.Proxy = null;
httpWebRequest.Timeout = 30000;
// PT is using ordinary POST data, not JSON
string query = "configName=" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "&license=" + systemConfiguration.GeneralSettings.Application.ProfitTrailerLicense;
string pairsPropertiesString = SystemHelper.ConvertListToTokenString(pairsLines, Environment.NewLine, false);
string dcaPropertiesString = SystemHelper.ConvertListToTokenString(dcaLines, Environment.NewLine, false);
string indicatorsPropertiesString = SystemHelper.ConvertListToTokenString(indicatorsLines, Environment.NewLine, false);
query += "&pairsData=" + WebUtility.UrlEncode(pairsPropertiesString) + "&dcaData=" + WebUtility.UrlEncode(dcaPropertiesString) + "&indicatorsData=" + WebUtility.UrlEncode(indicatorsPropertiesString);
byte[] formData = Encoding.ASCII.GetBytes(query);
httpWebRequest.ContentLength = formData.Length;
using (Stream stream = httpWebRequest.GetRequestStream())
try
{
stream.Write(formData, 0, formData.Length);
ServicePointManager.Expect100Continue = true;
ServicePointManager.DefaultConnectionLimit = 9999;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(CertificateHelper.AllwaysGoodCertificate);
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(urlList[i] + "settingsapi/settings/saveAll");
httpWebRequest.ContentType = "application/x-www-form-urlencoded";
httpWebRequest.Method = "POST";
httpWebRequest.Proxy = null;
httpWebRequest.Timeout = 30000;
// PT is using ordinary POST data, not JSON
string query = "configName=" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName;
query = query + "&license=" + licenseList[i];
string pairsPropertiesString = SystemHelper.ConvertListToTokenString(pairsLines, Environment.NewLine, false);
string dcaPropertiesString = SystemHelper.ConvertListToTokenString(dcaLines, Environment.NewLine, false);
string indicatorsPropertiesString = SystemHelper.ConvertListToTokenString(indicatorsLines, Environment.NewLine, false);
query += "&pairsData=" + WebUtility.UrlEncode(pairsPropertiesString) + "&dcaData=" + WebUtility.UrlEncode(dcaPropertiesString) + "&indicatorsData=" + WebUtility.UrlEncode(indicatorsPropertiesString);
byte[] formData = Encoding.ASCII.GetBytes(query);
httpWebRequest.ContentLength = formData.Length;
using (Stream stream = httpWebRequest.GetRequestStream())
{
stream.Write(formData, 0, formData.Length);
}
log.DoLogDebug("Built POST request for Properties");
int adjustedCount = i+1;
log.DoLogInfo("Sending Properties to license " + adjustedCount + " at " + urlList[i]);
using (HttpWebResponse httpResponse = (HttpWebResponse)httpWebRequest.GetResponse())
{
log.DoLogInfo("Properties sent!");
httpResponse.Close();
log.DoLogDebug("Properties response object closed.");
}
transferCompleted = true;
}
log.DoLogDebug("Built POST request for Properties");
catch (WebException ex)
{
// Manual error handling as PT doesn't seem to provide a proper error response...
if (ex.Message.IndexOf("401") > -1)
{
log.DoLogError("Saving 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)
{
// Handle timeout seperately
retryCount++;
if (retryCount <= maxRetries)
{
log.DoLogError("Saving Properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Timeout! Starting retry number " + retryCount + "/" + maxRetries.ToString() + "!");
}
else
{
transferCanceled = true;
log.DoLogError("Saving Properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Timeout! Canceling transfer after " + maxRetries.ToString() + " failed retries.");
}
}
else
{
log.DoLogCritical("Saving Properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': " + ex.Message, ex);
transferCanceled = true;
}
log.DoLogInfo("Sending Properties...");
using (HttpWebResponse httpResponse = (HttpWebResponse)httpWebRequest.GetResponse())
{
log.DoLogInfo("Properties sent!");
httpResponse.Close();
log.DoLogDebug("Properties response object closed.");
}
transferCompleted = true;
}
catch (WebException ex)
{
// Manual error handling as PT doesn't seem to provide a proper error response...
if (ex.Message.IndexOf("401") > -1)
catch (TimeoutException ex)
{
log.DoLogError("Saving 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)
{
// Handle timeout seperately
retryCount++;
if (retryCount <= maxRetries)
{
log.DoLogError("Saving Properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Timeout! Starting retry number " + retryCount + "/" + maxRetries.ToString() + "!");
log.DoLogError("Saving Properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Timeout (" + ex.Message + ")! Starting retry number " + retryCount + "/" + maxRetries.ToString() + "!");
}
else
{
transferCanceled = true;
log.DoLogError("Saving Properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Timeout! Canceling transfer after " + maxRetries.ToString() + " failed retries.");
log.DoLogError("Saving Properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Timeout (" + ex.Message + ")! Canceling transfer after " + maxRetries.ToString() + " failed retries.");
}
}
else
catch (Exception ex)
{
log.DoLogCritical("Saving Properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': " + ex.Message, ex);
transferCanceled = true;
}
}
catch (TimeoutException ex)
{
retryCount++;
if (retryCount <= maxRetries)
{
log.DoLogError("Saving Properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Timeout (" + ex.Message + ")! Starting retry number " + retryCount + "/" + maxRetries.ToString() + "!");
}
else
{
transferCanceled = true;
log.DoLogError("Saving Properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Timeout (" + ex.Message + ")! Canceling transfer after " + maxRetries.ToString() + " failed retries.");
}
}
catch (Exception ex)
{
log.DoLogCritical("Saving Properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': " + ex.Message, ex);
transferCanceled = true;
}
}
}
}
}

View File

@ -144,9 +144,8 @@ namespace Core.ProfitTrailer
foreach (string line in pairsLines)
{
if (line.Replace(" ", "").StartsWith("ALL_enabled_pairs", StringComparison.InvariantCultureIgnoreCase) || line.Replace(" ", "").StartsWith("enabled_pairs", StringComparison.InvariantCultureIgnoreCase))
if (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("#", "");
result = result.Replace("=", "").Trim();
@ -644,4 +643,4 @@ namespace Core.ProfitTrailer
}
#endregion
}
}
}

View File

@ -272,7 +272,7 @@ namespace Core.ProfitTrailer
if (!string.IsNullOrEmpty(strategyName))
{
// buy/sell strategies beginning with PT 2.3.3 contain the letter followed by a colon and space.
if (strategyName.Contains(":"))
if (strategyName.Contains(":") && !strategyName.Contains("FORMULA"))
{
result = true;
}

View File

@ -62,14 +62,7 @@
<h4 class="m-t-0 header-title">Sales Analysis</h4>
@{
double totalProfit = 0;
if (Model.PTData.Properties.Shorting)
{
totalProfit = Model.PTData.SellLog.Sum(s => s.Profit * (-1));
}
else
{
totalProfit = Model.PTData.SellLog.Sum(s => s.Profit);
}
totalProfit = Model.PTData.SellLog.Sum(s => s.Profit);
double totalProfitFiat = Math.Round(totalProfit * Model.Summary.MainMarketPrice, 2);
double percentGain = Math.Round(totalProfit / Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance * 100, 2);
double avgDailyGain = Model.DailyGains.Values.Average(dg => dg);
@ -205,14 +198,7 @@
@for (DateTime salesDate = Model.DateTimeNow.DateTime.Date; salesDate >= Model.DateTimeNow.DateTime.AddDays(-Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries) && salesDate >= Model.MinSellLogDate; salesDate = salesDate.AddDays(-1)) {
List<Core.Main.DataObjects.PTMagicData.SellLogData> salesDateSales = Model.PTData.SellLog.FindAll(sl => sl.SoldDate.Date == salesDate);
double salesDateProfit = 0;
if (Model.PTData.Properties.Shorting)
{
salesDateProfit = salesDateSales.Sum(sl => sl.Profit * (-1));
}
else
{
salesDateProfit = salesDateSales.Sum(sl => sl.Profit);
}
salesDateProfit = salesDateSales.Sum(sl => sl.Profit);
double salesDateProfitFiat = Math.Round(salesDateProfit * Model.Summary.MainMarketPrice, 2);
double salesDateStartBalance = Model.PTData.GetSnapshotBalance(salesDate);
double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2);
@ -252,14 +238,7 @@
@for (DateTime salesMonthDate = salesMonthStartDate.Date; salesMonthDate >= Model.DateTimeNow.DateTime.AddMonths(-Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries) && salesMonthDate >= minSellLogMonthDate; salesMonthDate = salesMonthDate.AddMonths(-1)) {
List<Core.Main.DataObjects.PTMagicData.SellLogData> salesMonthSales = Model.PTData.SellLog.FindAll(sl => sl.SoldDate.Date.Month == salesMonthDate.Month && sl.SoldDate.Date.Year == salesMonthDate.Year);
double salesDateProfit = 0;
if (Model.PTData.Properties.Shorting)
{
salesDateProfit = salesMonthSales.Sum(sl => sl.Profit * (-1));
}
else
{
salesDateProfit = salesMonthSales.Sum(sl => sl.Profit);
}
salesDateProfit = salesMonthSales.Sum(sl => sl.Profit);
double salesDateProfitFiat = Math.Round(salesDateProfit * Model.Summary.MainMarketPrice, 2);
double salesDateStartBalance = Model.PTData.GetSnapshotBalance(salesMonthDate);
double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2);
@ -272,14 +251,7 @@
days++;
List<Core.Main.DataObjects.PTMagicData.SellLogData> monthDaySales = Model.PTData.SellLog.FindAll(sl => sl.SoldDate.Date == monthDay.Date);
double monthDayProfit = 0;
if (Model.PTData.Properties.Shorting)
{
monthDayProfit = monthDaySales.Sum(sl => sl.Profit * (-1));
}
else
{
monthDayProfit = monthDaySales.Sum(sl => sl.Profit);
}
monthDayProfit = monthDaySales.Sum(sl => sl.Profit);
double monthDayStartBalance = Model.PTData.GetSnapshotBalance(monthDay);
monthDailyProfit += Math.Round(monthDayProfit / monthDayStartBalance * 100, 2);
}
@ -319,10 +291,6 @@
<tbody>
@{
var topMarkets = Model.PTData.SellLog.GroupBy(m => m.Market).Select(mg => mg.Sum(m => m.Profit));
if (Model.PTData.Properties.Shorting)
{
topMarkets = Model.PTData.SellLog.GroupBy(m => m.Market).Select(mg => mg.Sum(m => m.Profit) * (-1));
}
int marketRank = 0;
}
@foreach (KeyValuePair<string, double> marketData in Model.TopMarkets) {

View File

@ -46,14 +46,7 @@ namespace Monitor.Pages
foreach (var market in markets)
{
double totalProfit = 0;
if (PTData.Properties.Shorting)
{
totalProfit = PTData.SellLog.FindAll(m => m.Market == market.Key).Sum(m => m.Profit * (-1));
}
else
{
totalProfit = PTData.SellLog.FindAll(m => m.Market == market.Key).Sum(m => m.Profit);
}
totalProfit = PTData.SellLog.FindAll(m => m.Market == market.Key).Sum(m => m.Profit);
topMarketsDic.Add(market.Key, totalProfit);
}
TopMarkets = new SortedDictionary<string, double>(topMarketsDic).OrderByDescending(m => m.Value).Take(PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets);
@ -81,14 +74,7 @@ namespace Monitor.Pages
}
double profit = 0;
int trades = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate.Date).Count;
if (PTData.Properties.Shorting)
{
profit = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate.Date).Sum(t => t.Profit * (-1));
}
else
{
profit = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate.Date).Sum(t => t.Profit);
}
profit = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate.Date).Sum(t => t.Profit);
double profitFiat = Math.Round(profit * Summary.MainMarketPrice, 2);
balance += profitFiat;
tradesPerDayJSON += "{x: new Date('" + salesDate.Date.ToString("yyyy-MM-dd") + "'), y: " + trades + "}";
@ -124,14 +110,7 @@ namespace Monitor.Pages
{
List<SellLogData> salesDateSales = PTData.SellLog.FindAll(sl => sl.SoldDate.Date == salesDate);
double salesDateProfit;
if (PTData.Properties.Shorting)
{
salesDateProfit = salesDateSales.Sum(sl => sl.Profit * (-1));
}
else
{
salesDateProfit = salesDateSales.Sum(sl => sl.Profit);
}
salesDateProfit = salesDateSales.Sum(sl => sl.Profit);
double salesDateStartBalance = PTData.GetSnapshotBalance(salesDate);
double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2);
DailyGains.Add(salesDate, salesDateGain);
@ -142,14 +121,7 @@ namespace Monitor.Pages
{
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;
if (PTData.Properties.Shorting)
{
salesDateProfit = salesMonthSales.Sum(sl => sl.Profit * (-1));
}
else
{
salesDateProfit = salesMonthSales.Sum(sl => sl.Profit);
}
salesDateProfit = salesMonthSales.Sum(sl => sl.Profit);
double salesDateStartBalance = PTData.GetSnapshotBalance(salesMonthDate);
double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2);
MonthlyGains.Add(salesMonthDate, salesDateGain);

View File

@ -48,24 +48,30 @@
</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>
<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="If disabled PTM and the PTM monitor will continue to run, however all market analysis and PT settings changes are stopped."></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>
<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 market analysis will continue, however PT settings will not be updated."></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">Exchange <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The exchange PT Magic is using to get market data."></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GeneralSettings.Application.Exchange
</div>
<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 PT bot is running on."></i></label>
<div class="col-md-8">
<select name="Application_Exchange" class="form-control">
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex", StringComparison.InvariantCultureIgnoreCase))">Bittrex</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance", StringComparison.InvariantCultureIgnoreCase))">Binance</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("BinanceUS", StringComparison.InvariantCultureIgnoreCase))">BinanceUS</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("BinanceFutures", StringComparison.InvariantCultureIgnoreCase))">BinanceFutures</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex", StringComparison.InvariantCultureIgnoreCase))">Poloniex</option>
</select>
</div>
</div>
@ -77,16 +83,16 @@
</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>
<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 (if PT and PTM are running on the same machine, 'http://localhost:xxxx' should be used.)"></i></label>
<div class="col-md-8">
<a href="@Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL" target="_blank">@Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL</a>
<input type="text" class="form-control" name="Application_ProfitTrailerMonitorURL" value="@Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL">
</div>
</div>
<div class="form-group row">
<label class="col-md-4 col-form-label">Profit Trailer Server API Token <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The API token needed to communicate with Profit Trailer - set in Profit Trailer Server Settings"></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GetProfitTrailerServerAPITokenMasked()
<input type="text" class="form-control" name="Application_ProfitTrailerServerAPIToken" value="@Model.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken">
</div>
</div>
@ -170,28 +176,21 @@
</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>
<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 PTM 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>
<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 PTM monitor on"></i></label>
<div class="col-md-8">
@Model.PTMagicConfiguration.GeneralSettings.Monitor.Port
<input type="text" class="form-control" name="Monitor_Port" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.Port.ToString(new System.Globalization.CultureInfo("en-US"))">
</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">Market Analyzer Chart <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="By default the price chart on the analyzer page displays your base currency. You can select a different currency here"></i></label>
<label class="col-md-4 col-form-label">Market Analyzer Chart <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="By default the price chart on the analyzer page displays your base market currency against your fiat. You can change this to another pair."></i></label>
<div class="col-md-8">
<input type="text" class="form-control" name="Monitor_AnalyzerChart" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.AnalyzerChart.ToString(new System.Globalization.CultureInfo("en-US"))">
</div>

View File

@ -70,19 +70,19 @@ namespace Monitor.Pages
PTMagicConfiguration.GeneralSettings.Application.TestMode = HttpContext.Request.Form["Application_TestMode"].Equals("on");
PTMagicConfiguration.GeneralSettings.Application.StartBalance = SystemHelper.TextToDouble(HttpContext.Request.Form["Application_StartBalance"], PTMagicConfiguration.GeneralSettings.Application.StartBalance, "en-US");
PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName = HttpContext.Request.Form["Application_ProfitTrailerDefaultSettingName"];
PTMagicConfiguration.GeneralSettings.Application.Exchange = HttpContext.Request.Form["Application_Exchange"];
PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL = HttpContext.Request.Form["Application_ProfitTrailerMonitorURL"];
PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken = HttpContext.Request.Form["Application_ProfitTrailerServerAPIToken"];
PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset = HttpContext.Request.Form["Application_TimezoneOffset"];
PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency = HttpContext.Request.Form["Application_MainFiatCurrency"];
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.Application.FreeCurrencyConverterAPIKey = HttpContext.Request.Form["Application_FreeCurrencyConverterAPIKey"];
PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected = HttpContext.Request.Form["Monitor_IsPasswordProtected"].Equals("on");
PTMagicConfiguration.GeneralSettings.Monitor.OpenBrowserOnStart = HttpContext.Request.Form["Monitor_OpenBrowserOnStart"].Equals("on");
PTMagicConfiguration.GeneralSettings.Monitor.DefaultDCAMode = HttpContext.Request.Form["Monitor_AnalyzerChart"];
PTMagicConfiguration.GeneralSettings.Monitor.AnalyzerChart = HttpContext.Request.Form["Monitor_AnalyzerChart"];
PTMagicConfiguration.GeneralSettings.Monitor.Port = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_Port"], PTMagicConfiguration.GeneralSettings.Monitor.Port);
PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_GraphIntervalMinutes"], PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes);
PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_GraphMaxTimeframeHours"], PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours);
PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_RefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds);

View File

@ -103,7 +103,7 @@
<div class="tradingview-widget-container">
<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">
<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>

View File

@ -121,14 +121,7 @@
@{
double totalProfit = 0;
if (Model.PTData.Properties.Shorting)
{
totalProfit = Model.PTData.SellLog.Sum(s => s.Profit * (-1));
}
else
{
totalProfit = Model.PTData.SellLog.Sum(s => s.Profit);
}
totalProfit = Model.PTData.SellLog.Sum(s => s.Profit);
double totalProfitFiat = Math.Round(totalProfit * Model.Summary.MainMarketPrice, 2);
double percentGain = Math.Round(totalProfit / Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance * 100, 2);
string percentGainText = percentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%";
@ -138,53 +131,25 @@
}
double todaysProfit = 0;
if (Model.PTData.Properties.Shorting)
{
todaysProfit = Model.PTData.SellLogToday.Sum(s => s.Profit * (-1));
}
else
{
todaysProfit = Model.PTData.SellLogToday.Sum(s => s.Profit);
}
todaysProfit = Model.PTData.SellLogToday.Sum(s => s.Profit);
double todaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime);
double todaysProfitFiat = Math.Round(todaysProfit * Model.Summary.MainMarketPrice, 2);
double todaysPercentGain = Math.Round(todaysProfit / todaysStartBalance * 100, 2);
double yesterdaysProfit = 0;
if (Model.PTData.Properties.Shorting)
{
yesterdaysProfit = Model.PTData.SellLogYesterday.Sum(s => s.Profit * (-1));
}
else
{
yesterdaysProfit = Model.PTData.SellLogYesterday.Sum(s => s.Profit);
}
yesterdaysProfit = Model.PTData.SellLogYesterday.Sum(s => s.Profit);
double yesterdaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-1));
double yesterdaysProfitFiat = Math.Round(yesterdaysProfit * Model.Summary.MainMarketPrice, 2);
double yesterdaysPercentGain = Math.Round(yesterdaysProfit / yesterdaysStartBalance * 100, 2);
double last7DaysProfit = 0;
if (Model.PTData.Properties.Shorting)
{
last7DaysProfit = Model.PTData.SellLogLast7Days.Sum(s => s.Profit * (-1));
}
else
{
last7DaysProfit = Model.PTData.SellLogLast7Days.Sum(s => s.Profit);
}
last7DaysProfit = Model.PTData.SellLogLast7Days.Sum(s => s.Profit);
double last7DaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-7));
double last7DaysProfitFiat = Math.Round(last7DaysProfit * Model.Summary.MainMarketPrice, 2);
double last7DaysPercentGain = Math.Round(last7DaysProfit / last7DaysStartBalance * 100, 2);
double last30DaysProfit = 0;
if (Model.PTData.Properties.Shorting)
{
last30DaysProfit = Model.PTData.SellLogLast30Days.Sum(s => s.Profit * (-1));
}
else
{
last30DaysProfit = Model.PTData.SellLogLast30Days.Sum(s => s.Profit);
}
last30DaysProfit = Model.PTData.SellLogLast30Days.Sum(s => s.Profit);
double last30DaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-30));
double last30DaysProfitFiat = Math.Round(last30DaysProfit * Model.Summary.MainMarketPrice, 2);
double last30DaysPercentGain = Math.Round(last30DaysProfit / last30DaysStartBalance * 100, 2);

View File

@ -145,10 +145,6 @@ namespace Monitor.Pages
}
int trades = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate).Count;
double profit = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate).Sum(t => t.Profit);
if (PTData.Properties.Shorting)
{
profit = profit * (-1);
}
double profitFiat = Math.Round(profit * Summary.MainMarketPrice, 2);
profitPerDayJSON += "{x: new Date('" + salesDate.ToString("yyyy-MM-dd") + "'), y: " + profitFiat.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}";
tradeDayIndex++;

View File

@ -98,10 +98,9 @@
<th class="text-left" data-toggle="tooltip" data-placement="top" title="24 Hour Trend">24H</th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Total Buy Cost">Cost</th>
<th></th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Active buy strategies">DCA</th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Active sell strategies">Sell</th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Target profit to sell">Target</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="Active Buy Strategies">DCA</th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Active Sell Strategies">Sell</th>
<th class="text-left" data-toggle="tooltip" data-html="true" data-placement="top" title="Profit Target <br> Current Profit">Profit</th>
<th></th>
</tr>
</thead>
@ -202,42 +201,32 @@
<td>@Html.Raw(buyStrategyText)</td>
<!-- Sell Strategy -->
<td>@Html.Raw(sellStrategyText)</td>
<!-- Target -->
@if (!sellStrategyText.Contains("WATCHMODE"))
{
@if (sellStrategyText.Contains("CROSSED"))
// if leverage, recalculate profit target
{
string leverageText = sellStrategyText.Remove(0, sellStrategyText.IndexOf("CROSSED")+9);
leverage = leverageText.Remove(leverageText.IndexOf(".0)"), leverageText.Length - leverageText.IndexOf(".0)"));
leverageValue = double.Parse(leverage);
}
@if (sellStrategyText.Contains("ISOLATED"))
{
string leverageText = sellStrategyText.Remove(0, sellStrategyText.IndexOf("ISOLATED")+10);
leverage = leverageText.Remove(leverageText.IndexOf(".0)"), leverageText.Length - leverageText.IndexOf(".0)"));
leverageValue = double.Parse(leverage);
}
@if (leverageValue == 1)
{
<td class="@Html.Raw((dcaLogEntry.TargetGainValue.HasValue && (profitPercentage > dcaLogEntry.TargetGainValue.Value)) ? "text-success" : "text-danger")">@Html.Raw(dcaLogEntry.TargetGainValue.HasValue ? dcaLogEntry.TargetGainValue.Value.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%" : "&nbsp")</td>
}
else
{
double TargetGain = leverageValue * dcaLogEntry.TargetGainValue.Value;
<td class="@Html.Raw((dcaLogEntry.TargetGainValue.HasValue && (profitPercentage > dcaLogEntry.TargetGainValue.Value)) ? "text-success" : "text-danger")">@Html.Raw(dcaLogEntry.TargetGainValue.HasValue ? TargetGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%" : "&nbsp")</td>
}
}
else
{
<td class="text-left"></td>
}
<!-- Profit -->
<!-- Target/Profit -->
@if (!@lostValue)
{
profitPercentage = profitPercentage * leverageValue;
<td class="text-autocolor">@profitPercentage.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
}
{
@if (!sellStrategyText.Contains("WATCHMODE"))
{
@if (sellStrategyText.Contains("CROSSED"))
// if leverage, recalculate profit target
{
string leverageText = sellStrategyText.Remove(0, sellStrategyText.IndexOf("CROSSED")+9);
leverage = leverageText.Remove(leverageText.IndexOf(".0)"), leverageText.Length - leverageText.IndexOf(".0)"));
leverageValue = double.Parse(leverage);
}
@if (sellStrategyText.Contains("ISOLATED"))
{
string leverageText = sellStrategyText.Remove(0, sellStrategyText.IndexOf("ISOLATED")+10);
leverage = leverageText.Remove(leverageText.IndexOf(".0)"), leverageText.Length - leverageText.IndexOf(".0)"));
leverageValue = double.Parse(leverage);
}
profitPercentage = profitPercentage * leverageValue;
double TargetGain = leverageValue * dcaLogEntry.TargetGainValue.Value;
<td>@TargetGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%
<br>
<div class="text-autocolor">@profitPercentage.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</div>
</td>
}
}
else
{
<td class="text-left">No Value!</td>
@ -258,7 +247,6 @@
<td></td>
<td></td>
<td></td>
<td></td>
<td class="text-autocolor">@Html.Raw((((Model.TotalBagGain) / Model.TotalBagCost) * 100).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))%</td>
</tbody>
</table>

View File

@ -11,24 +11,29 @@
}
// Global setting tool tip
string globalSettingInfoIcon = "<i class=\"fa fa-info-circle text-muted\" data-toggle=\"tooltip\" data-placement=\"top\" data-html=\"true\" title=\"<b>Instance: </b>" + Model.PTMagicConfiguration.GeneralSettings.Application.InstanceName + "\" data-template=\"<div class='tooltip' role='tooltip'><div class='tooltip-arrow'></div><div class='tooltip-inner tooltip-100 text-left'></div></div>\"></i>";
string globalIconColor = "text-success";
string globalSettingInfoIcon = "<i class=\"fa fa-info-circle text-muted\" data-toggle=\"tooltip\" data-placement=\"top\" data-html=\"true\" title=\"<b>Instance: </b>" + Model.PTMagicConfiguration.GeneralSettings.Application.InstanceName + "\" data-template=\"<div class='tooltip' role='tooltip'><div class='tooltip-arrow'></div><div class='tooltip-inner tooltip-100 text-left'></div></div>\"></i>";
if (Model.PTData.Properties.Shorting)
{
globalIconColor = "text-danger";
}
// Health indicator
DateTime lastRuntime = Model.Summary.LastRuntime;
double elapsedSecondsSinceRuntime = DateTime.UtcNow.Subtract(lastRuntime).TotalSeconds;
double intervalSeconds = Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 60.0;
string iconColor = "text-success";
string healthIconColor = "text-success";
string ptMagicHealthIcon = "fa-heartbeat";
string ptMagicHealthTooltip = "PT Magic is alive and healthy! Time elapsed since last run: " + Math.Round(elapsedSecondsSinceRuntime / 60, 1) + " mins.";
if (elapsedSecondsSinceRuntime > (intervalSeconds * 2)) {
ptMagicHealthIcon = "fa-exclamation-triangle";
ptMagicHealthTooltip = "PT Magic seems to have problems, check the logs! Time elapsed since last run: " + Math.Round(elapsedSecondsSinceRuntime / 60, 1) + " mins.";
iconColor = "text-danger";
healthIconColor = "text-danger";
}
}
<div class="card-box card-box-mini card-box-ptmagic-outlined">
<div class="card-box card-box-mini card-box-ptmagic-outlined @globalIconColor">
<span data-toggle="tooltip" data-placement="bottom" title="Active global setting">
@Core.Helper.SystemHelper.SplitCamelCase(Model.Summary.CurrentGlobalSetting.SettingName)</span><span class = "header-title"><a href="StatusSummary">@Html.Raw(" " + globalSettingInfoIcon)</a></span>
</div>
@ -37,8 +42,8 @@
<span data-toggle="tooltip" data-placement="bottom" title="Active single market settings"><b>SMS: </b></span><span class = "header-title"><a href="ManageSMS">@Html.Raw(activeSingleSettings + " " + singleSettingInfoIcon)</a></span>
</div>
<div class="card-box card-box-mini card-box-ptmagic-status-outlined @iconColor" data-toggle="tooltip" data-placement="bottom" title="@ptMagicHealthTooltip">
<i class="fa @ptMagicHealthIcon @iconColor"></i>
<div class="card-box card-box-mini card-box-ptmagic-status-outlined @healthIconColor" data-toggle="tooltip" data-placement="bottom" title="@ptMagicHealthTooltip">
<i class="fa @ptMagicHealthIcon @healthIconColor"></i>
</div>
<script type="text/javascript">

View File

@ -20,6 +20,7 @@ namespace Monitor.Pages {
}
private void BindData() {
PTData = this.PtDataObject;
// Get markets with active single settings
var MarketsWithSingleSettingsData = from x in Summary.MarketSummary
where x.Value.ActiveSingleSettings != null

View File

@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Threading;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Configuration;
@ -15,170 +16,186 @@ using Core.Main.DataObjects;
namespace Monitor._Internal
{
public class BasePageModel : PageModel
{
public string PTMagicBasePath = "";
public string PTMagicMonitorBasePath = "";
public PTMagicConfiguration PTMagicConfiguration = null;
public Summary Summary { get; set; } = new Summary();
public LogHelper Log = null;
public string LatestVersion = "";
public string CurrentBotVersion = "";
public string NotifyHeadline = "";
public string NotifyMessage = "";
public string NotifyType = "";
public string MainFiatCurrencySymbol = "$";
private volatile object _ptDataLock = new object();
private static ProfitTrailerData _ptData = null;
// Profit Trailer data accessor object
public ProfitTrailerData PtDataObject
public class BasePageModel : PageModel
{
get
{
if (_ptData == null)
public string PTMagicBasePath = "";
public string PTMagicMonitorBasePath = "";
public PTMagicConfiguration PTMagicConfiguration = null;
public Summary Summary { get; set; } = new Summary();
public LogHelper Log = null;
public string LatestVersion = "";
public string CurrentBotVersion = "";
public string NotifyHeadline = "";
public string NotifyMessage = "";
public string NotifyType = "";
public string MainFiatCurrencySymbol = "$";
private volatile object _ptDataLock = new object();
private static ProfitTrailerData _ptData = null;
// Profit Trailer data accessor object
public ProfitTrailerData PtDataObject
{
lock (_ptDataLock)
{
_ptData = new ProfitTrailerData(PTMagicConfiguration);
}
get
{
if (_ptData == null)
{
lock (_ptDataLock)
{
_ptData = new ProfitTrailerData(PTMagicConfiguration);
}
}
return _ptData;
}
}
return _ptData;
}
public void PreInit()
{
PTMagicMonitorBasePath = Directory.GetCurrentDirectory();
if (!System.IO.File.Exists(PTMagicMonitorBasePath + Path.DirectorySeparatorChar + "appsettings.json"))
{
PTMagicMonitorBasePath += Path.DirectorySeparatorChar + "Monitor";
}
if (!PTMagicMonitorBasePath.EndsWith(Path.DirectorySeparatorChar))
{
PTMagicMonitorBasePath += Path.DirectorySeparatorChar;
}
IConfiguration config = new ConfigurationBuilder()
.SetBasePath(PTMagicMonitorBasePath)
.AddJsonFile("appsettings.json", false)
.Build();
PTMagicBasePath = config.GetValue<string>("PTMagicBasePath");
if (!PTMagicBasePath.EndsWith(Path.DirectorySeparatorChar))
{
PTMagicBasePath += Path.DirectorySeparatorChar;
}
PTMagicConfiguration = new PTMagicConfiguration(PTMagicBasePath);
IServiceProvider logProvider = ServiceHelper.BuildLoggerService(PTMagicBasePath);
Log = logProvider.GetRequiredService<LogHelper>();
bool exitLoop = false;
while (!exitLoop)
{
try
{
// Try to read the current runtime summary, but may be being written, so retry if necessary.
Summary = JsonConvert.DeserializeObject<Summary>(System.IO.File.ReadAllText(PTMagicBasePath + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "LastRuntimeSummary.json"));
exitLoop = true;
}
catch (IOException e)
{
// Squash exception and try again, as file was locked.
Thread.Sleep(250);
}
}
if (Summary.CurrentGlobalSetting == null) Summary.CurrentGlobalSetting = PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(s => s.SettingName.IndexOf("default", StringComparison.InvariantCultureIgnoreCase) > -1);
MainFiatCurrencySymbol = SystemHelper.GetCurrencySymbol(Summary.MainFiatCurrency);
try
{
// Get latest release from GitHub
if (!String.IsNullOrEmpty(HttpContext.Session.GetString("LatestVersion")))
{
LatestVersion = HttpContext.Session.GetString("LatestVersion");
}
else
{
LatestVersion = BaseAnalyzer.GetLatestGitHubRelease(Log, Summary.Version);
HttpContext.Session.SetString("LatestVersion", LatestVersion);
}
}
catch { }
try
{
// Get current bot version
if (!String.IsNullOrEmpty(HttpContext.Session.GetString("CurrentBotVersion")))
{
CurrentBotVersion = HttpContext.Session.GetString("CurrentBotVersion");
}
else
{
string ptMagicBotDllPath = PTMagicBasePath + "PTMagic.dll";
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
{
CurrentBotVersion = Summary.Version;
}
}
}
catch
{
CurrentBotVersion = Summary.Version;
}
}
protected string GetStringParameter(string paramName, string defaultValue)
{
string result = defaultValue;
if (HttpContext.Request.Query.ContainsKey(paramName))
{
result = HttpContext.Request.Query[paramName];
}
else if (HttpContext.Request.Method.Equals("POST") && HttpContext.Request.Form.ContainsKey(paramName))
{
result = HttpContext.Request.Form[paramName];
}
return result;
}
/// <summary>
/// Holt einen Url-Parameter als Integer, wenn vorhanden.
/// </summary>
/// <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)
{
int result = defaultValue;
if (HttpContext.Request.Query.ContainsKey(paramName))
{
try
{
result = Int32.Parse(HttpContext.Request.Query[paramName]);
}
catch
{
result = defaultValue;
}
}
else if (HttpContext.Request.Method.Equals("POST") && HttpContext.Request.Form.ContainsKey(paramName))
{
try
{
result = Int32.Parse(HttpContext.Request.Form[paramName]);
}
catch
{
result = defaultValue;
}
}
return result;
}
}
public void PreInit()
{
PTMagicMonitorBasePath = Directory.GetCurrentDirectory();
if (!System.IO.File.Exists(PTMagicMonitorBasePath + Path.DirectorySeparatorChar + "appsettings.json"))
{
PTMagicMonitorBasePath += Path.DirectorySeparatorChar + "Monitor";
}
if (!PTMagicMonitorBasePath.EndsWith(Path.DirectorySeparatorChar))
{
PTMagicMonitorBasePath += Path.DirectorySeparatorChar;
}
IConfiguration config = new ConfigurationBuilder()
.SetBasePath(PTMagicMonitorBasePath)
.AddJsonFile("appsettings.json", false)
.Build();
PTMagicBasePath = config.GetValue<string>("PTMagicBasePath");
if (!PTMagicBasePath.EndsWith(Path.DirectorySeparatorChar))
{
PTMagicBasePath += Path.DirectorySeparatorChar;
}
PTMagicConfiguration = new PTMagicConfiguration(PTMagicBasePath);
IServiceProvider logProvider = ServiceHelper.BuildLoggerService(PTMagicBasePath);
Log = logProvider.GetRequiredService<LogHelper>();
Summary = JsonConvert.DeserializeObject<Summary>(System.IO.File.ReadAllText(PTMagicBasePath + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "LastRuntimeSummary.json"));
if (Summary.CurrentGlobalSetting == null) Summary.CurrentGlobalSetting = PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(s => s.SettingName.IndexOf("default", StringComparison.InvariantCultureIgnoreCase) > -1);
MainFiatCurrencySymbol = SystemHelper.GetCurrencySymbol(Summary.MainFiatCurrency);
try
{
// Get latest release from GitHub
if (!String.IsNullOrEmpty(HttpContext.Session.GetString("LatestVersion")))
{
LatestVersion = HttpContext.Session.GetString("LatestVersion");
}
else
{
LatestVersion = BaseAnalyzer.GetLatestGitHubRelease(Log, Summary.Version);
HttpContext.Session.SetString("LatestVersion", LatestVersion);
}
}
catch { }
try
{
// Get current bot version
if (!String.IsNullOrEmpty(HttpContext.Session.GetString("CurrentBotVersion")))
{
CurrentBotVersion = HttpContext.Session.GetString("CurrentBotVersion");
}
else
{
string ptMagicBotDllPath = PTMagicBasePath + "PTMagic.dll";
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
{
CurrentBotVersion = Summary.Version;
}
}
}
catch
{
CurrentBotVersion = Summary.Version;
}
}
protected string GetStringParameter(string paramName, string defaultValue)
{
string result = defaultValue;
if (HttpContext.Request.Query.ContainsKey(paramName))
{
result = HttpContext.Request.Query[paramName];
}
else if (HttpContext.Request.Method.Equals("POST") && HttpContext.Request.Form.ContainsKey(paramName))
{
result = HttpContext.Request.Form[paramName];
}
return result;
}
/// <summary>
/// Holt einen Url-Parameter als Integer, wenn vorhanden.
/// </summary>
/// <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)
{
int result = defaultValue;
if (HttpContext.Request.Query.ContainsKey(paramName))
{
try
{
result = Int32.Parse(HttpContext.Request.Query[paramName]);
}
catch
{
result = defaultValue;
}
}
else if (HttpContext.Request.Method.Equals("POST") && HttpContext.Request.Form.ContainsKey(paramName))
{
try
{
result = Int32.Parse(HttpContext.Request.Form[paramName]);
}
catch
{
result = defaultValue;
}
}
return result;
}
}
}

View File

@ -6,7 +6,7 @@ using Core.Helper;
using Microsoft.Extensions.DependencyInjection;
[assembly: AssemblyVersion("2.5.9")]
[assembly: AssemblyVersion("2.5.12")]
[assembly: AssemblyProduct("PT Magic")]
namespace PTMagic

View File

@ -1,6 +1,7 @@
//
// 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.
// You should take your time and adjust these settings according to your own personal preferences, and settings.
//
// Always test your PTMagic settings by running a Profit Trailer bot in TESTMODE, to make sure
// it is performing as you expect.
//
@ -10,21 +11,19 @@
{
"AnalyzerSettings": {
"MarketAnalyzer": {
"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
"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": "1h", // 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": 60, // 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.
// "Market" will build a trend against your base currency, such as BTC or USDT.
"TrendThreshold": 15, // Any coin that is above 15% or below -15% for this timeframe will not be used when calculating the market average.
"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": "1h", // 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": 60, // 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. "Market" will build a trend against your base currency, such as BTC or USDT.
"TrendThreshold": 15, // Any coin that is above 15% or below -15% for this timeframe will not be used when calculating the market average.
"DisplayGraph": false, // Use this trend in the graph on the PTM Monitor dashboard and market analyzer?
"DisplayOnMarketAnalyzerList": false // Disply this trend on the PTM Monitor market analyzer?
},
{
"Name": "6h",
@ -33,8 +32,8 @@
"TrendMinutes": 360,
"TrendCurrency": "Market",
"TrendThreshold": 30,
"DisplayGraph": true,
"DisplayOnMarketAnalyzerList": true
"DisplayGraph": true,
"DisplayOnMarketAnalyzerList": true
},
{
"Name": "12h",
@ -43,8 +42,8 @@
"TrendMinutes": 720,
"TrendCurrency": "Market",
"TrendThreshold": 50,
"DisplayGraph": true,
"DisplayOnMarketAnalyzerList": true
"DisplayGraph": true,
"DisplayOnMarketAnalyzerList": true
},
{
"Name": "24h",
@ -53,24 +52,24 @@
"TrendMinutes": 1440,
"TrendCurrency": "Market",
"TrendThreshold": 75,
"DisplayGraph": true,
"DisplayOnMarketAnalyzerList": true
"DisplayGraph": true,
"DisplayOnMarketAnalyzerList": true
}
]
},
// ================================ GLOBAL SETTINGS ================================
//
"GlobalSettings": [ // Global settings for Profit Trailer properties
//
// ===================================================================================
// -----------------------------
//
// ===================================================================================
// -----------------------------
{
"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. You can use any of your defined trends from above
"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. You can use any of your defined trends from above
{
"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": "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": "12h",
@ -81,17 +80,19 @@
"MaxChange": -5
}
],
"PairsProperties": { // Properties for PAIRS.PROPERTIES
// Any valid setting from https://wiki.profittrailer.com/en/config 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.
"PairsProperties": { // Changes you wish to make to your PAIRS.properties settings
// Any valid setting from https://wiki.profittrailer.com/en/config 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
"DCAProperties": { // Changes you wish to make to your DCA.properties settings
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -75
},
"IndicatorsProperties": { // Changes you wish to make to your INDICATORS.properties settings
}
},
// -----------------------------
// -----------------------------
{
"SettingName": "TankingDown",
"TriggerConnection": "AND",
@ -99,34 +100,25 @@
{
"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
"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
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -50
},
"IndicatorsProperties": {
"IndicatorsProperties": {
}
},
// -----------------------------
// -----------------------------
{
"SettingName": "BearSighted",
"TriggerConnection": "AND",
@ -147,16 +139,16 @@
],
"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
//"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,
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -10,
},
"IndicatorsProperties": {
"IndicatorsProperties": {
}
},
// -----------------------------
@ -180,19 +172,19 @@
],
"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
//"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,
"DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -10,
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 10,
},
"IndicatorsProperties": {
"IndicatorsProperties": {
}
},
// -----------------------------
// -----------------------------
{
"SettingName": "ToTheMoon",
"TriggerConnection": "AND",
@ -212,8 +204,8 @@
],
"PairsProperties": {
"max_trading_pairs_OFFSET": 2,
//"DEFAULT_initial_cost_OFFSETPERCENT": 20,
//"DEFAULT_initial_cost_percentage_OFFSETPERCENT": 20,
//"DEFAULT_initial_cost_OFFSETPERCENT": 20,
//"DEFAULT_initial_cost_percentage_OFFSETPERCENT": 20,
"DEFAULT_trailing_buy_OFFSETPERCENT": -10,
"DEFAULT_A_sell_value_OFFSETPERCENT": 20
},
@ -221,7 +213,7 @@
"DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -20,
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 20,
},
"IndicatorsProperties": {
"IndicatorsProperties": {
}
},
// -----------------------------
@ -238,36 +230,25 @@
}
}
],
//
// ================================ 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": "BlacklistCoins",
"StopProcessWhenTriggered": true,
"Triggers": [
{
"AgeDaysLowerThan": 21
}
],
"PairsProperties": {
"DEFAULT_trading_enabled": false,
"DEFAULT_sell_only_mode_enabled": true,
"DEFAULT_DCA_enabled": false
}
},
// -----------------------------
"SingleMarketSettings": [ // Single market/pair settings for Profit Trailer properties
// Any setting from https://wiki.profittrailer.com/en/config marked as COIN (coin-specific) can be used here.
// Only coins that meet the triggered conditions will have the settings applied.
// A variety of SMS can be employed to check for long-term down trends, sideways trends, over-extended uptrends, etc.
// If more than one SMS is true, the settings of the last applied SMS over-rides any prior SMS
{
"SettingName": "PumpNDumpProtection",
"TriggerConnection": "OR",
//"StopProcessWhenTriggered": true, // No SMS after this will be analyzed or applied if this SMS is true
//"AllowedGlobalSettings": "Default", // You can specify that this setting will only apply when a specific Global setting is active
//"IgnoredGlobalSettings": "Default", // You can specify that this setting will NOT apply when a specific Global setting is active
"Triggers": [
{
"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
"MarketTrendRelation": "Relative", // Relative = The single market trend is compared to the overall trend of the entire market
// Absolute = The Single market trend is considered on its own
"MinChange": 8
},
{
@ -281,18 +262,17 @@
"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.
"OffTriggers": [
{
"HoursSinceTriggered": 3 // Any coin that triggers this setting, will remain under this setting
// for 3 hours, since the last time it triggered.
}
],
"PairsProperties": {
"DEFAULT_sell_only_mode_enabled": true,
"DEFAULT_DCA_enabled": false
"DEFAULT_sell_only_mode_enabled": "true",
"DEFAULT_DCA_enabled": "false"
}
},
// -----------------------------
{
"SettingName": "FreefallBlock",
"TriggerConnection": "OR",
@ -303,14 +283,14 @@
"MaxChange": -5
}
],
"OffTriggers": [
{
"OffTriggers": [
{
"HoursSinceTriggered": 1
}
],
"PairsProperties": {
"DEFAULT_sell_only_mode_enabled": true,
"DEFAULT_DCA_enabled": false
"PairsProperties": {
"DEFAULT_sell_only_mode_enabled": "true",
"DEFAULT_DCA_enabled": "false"
}
}
]

View File

@ -3,10 +3,12 @@
"Application": {
"IsEnabled": true, // Enables the PTMagic bot (needs restart to take effect)
"TestMode": false, // If TestMode is active, no properties files will be changed
"ProfitTrailerLicense": "YOUR PROFIT TRAILER LICENSE KEY", // Your Profit Trailer license key (needed to change your settings for PT 2.0 and above)
"ProfitTrailerLicense": "ptlicense1asdf234fljlasdf014325ehm", // Your Profit Trailer license key (needed to change your settings)
"ProfitTrailerLicenseXtra": "", // Licenses for additional bots for PTM to update (optional - comma separated list)
"ProfitTrailerServerAPIToken": "", //Your Profit Trailer Server API Token
"ProfitTrailerMonitorURL": "http://localhost:8081/", // The URL to your profit trailer monitor (needed to change your settings for PT 2.0 and above)
"ProfitTrailerDefaultSettingName": "default", // Your Profit Trailer default setting name (needed to change your settings for PT 2.0 and above)
"ProfitTrailerMonitorURL": "http://localhost:8081/", // The URL to your profit trailer monitor (needed to change your settings)
"ProfitTrailerMonitorURLXtra": "", // URLs for additional bots you want PTM to update (optional - comma separated list)
"ProfitTrailerDefaultSettingName": "default", // Your Profit Trailer default setting name (needed to change your settings)
"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
@ -19,9 +21,9 @@
"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
"Port": 8080, // The port you want to run your monitor on
"RootUrl": "/", // The root Url of your monitor
"AnalyzerChart":"", // By default the chart on the market analyzer page displays your base currency against USD. You may change this if you like (eg., BTCEUR)
"AnalyzerChart": "", // By default the chart on the Market Analyzer page will use your default currency against USD. You can change that here. (eg., BTCEUR)
"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

View File

@ -1,6 +1,7 @@
//
// 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.
// You should take your time and adjust these settings according to your own personal preferences, and settings.
//
// Always test your PTMagic settings by running a Profit Trailer bot in TESTMODE, to make sure
// it is performing as you expect.
//
@ -10,21 +11,19 @@
{
"AnalyzerSettings": {
"MarketAnalyzer": {
"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
"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": "1h", // 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": 60, // 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.
// "Market" will build a trend against your base currency, such as BTC or USDT.
"TrendThreshold": 15, // Any coin that is above 15% or below -15% for this timeframe will not be used when calculating the market average.
"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": "1h", // 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": 60, // 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. "Market" will build a trend against your base currency, such as BTC or USDT.
"TrendThreshold": 15, // Any coin that is above 15% or below -15% for this timeframe will not be used when calculating the market average.
"DisplayGraph": false, // Use this trend in the graph on the PTM Monitor dashboard and market analyzer?
"DisplayOnMarketAnalyzerList": false // Disply this trend on the PTM Monitor market analyzer?
},
{
"Name": "6h",
@ -65,12 +64,12 @@
// ===================================================================================
// -----------------------------
{
"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. You can use any of your defined trends from above
"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. You can use any of your defined trends from above
{
"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": "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": "12h",
@ -81,14 +80,16 @@
"MaxChange": -5
}
],
"PairsProperties": { // Properties for PAIRS.PROPERTIES
// Any valid setting from https://wiki.profittrailer.com/en/config 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.
"PairsProperties": { // Changes you wish to make to your PAIRS.properties settings
// Any valid setting from https://wiki.profittrailer.com/en/config 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
"DCAProperties": { // Changes you wish to make to your DCA.properties settings
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -75
},
"IndicatorsProperties": { // Changes you wish to make to your INDICATORS.properties settings
}
},
// -----------------------------
@ -99,24 +100,15 @@
{
"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
"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,
@ -149,8 +141,8 @@
"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
"DEFAULT_trailing_buy_OFFSETPERCENT": 10,
"DEFAULT_trailing_profit_OFFSETPERCENT": -10
},
"DCAProperties": {
"DEFAULT_DCA_trailing_buy_OFFSETPERCENT": 10,
@ -180,13 +172,13 @@
],
"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
//"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_buy_OFFSETPERCENT": -10,
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 10,
},
"IndicatorsProperties": {
@ -219,7 +211,7 @@
},
"DCAProperties": {
"DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -20,
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 20,
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 20,
},
"IndicatorsProperties": {
}
@ -238,36 +230,25 @@
}
}
],
//
// ================================ COIN-SPECIFIC SETTINGS ================================
//
"SingleMarketSettings": [ // Single market/pair settings for Profit Trailer properties
// Any setting from https://wiki.profittrailer.com/en/config
// marked as CS (coin-specific) can be used here.
// Only coins that meet the triggered conditions will have the settings applied.
{
"SettingName": "BlacklistCoins",
"StopProcessWhenTriggered": true,
"Triggers": [
{
"AgeDaysLowerThan": 21
}
],
"PairsProperties": {
"DEFAULT_trading_enabled": false,
"DEFAULT_sell_only_mode_enabled": true,
"DEFAULT_DCA_enabled": false
}
},
// -----------------------------
"SingleMarketSettings": [ // Single market/pair settings for Profit Trailer properties
// Any setting from https://wiki.profittrailer.com/en/config marked as COIN (coin-specific) can be used here.
// Only coins that meet the triggered conditions will have the settings applied.
// A variety of SMS can be employed to check for long-term down trends, sideways trends, over-extended uptrends, etc.
// If more than one SMS is true, the settings of the last applied SMS over-rides any prior SMS
{
"SettingName": "PumpNDumpProtection",
"TriggerConnection": "OR",
//"StopProcessWhenTriggered": true, // No SMS after this will be analyzed or applied if this SMS is true
//"AllowedGlobalSettings": "Default", // You can specify that this setting will only apply when a specific Global setting is active
//"IgnoredGlobalSettings": "Default", // You can specify that this setting will NOT apply when a specific Global setting is active
"Triggers": [
{
"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
"MarketTrendRelation": "Relative", // Relative = The single market trend is compared to the overall trend of the entire market
// Absolute = The Single market trend is considered on its own
"MinChange": 8
},
{
@ -283,16 +264,15 @@
],
"OffTriggers": [
{
"HoursSinceTriggered": 3 // Any coin that triggers this setting, will remain under this setting
// for 3 hours, since the last time it triggered.
"HoursSinceTriggered": 3 // Any coin that triggers this setting, will remain under this setting
// for 3 hours, since the last time it triggered.
}
],
"PairsProperties": {
"DEFAULT_sell_only_mode_enabled": true,
"DEFAULT_DCA_enabled": false
"DEFAULT_sell_only_mode_enabled": "true",
"DEFAULT_DCA_enabled": "false"
}
},
// -----------------------------
{
"SettingName": "FreefallBlock",
"TriggerConnection": "OR",
@ -309,8 +289,8 @@
}
],
"PairsProperties": {
"DEFAULT_sell_only_mode_enabled": true,
"DEFAULT_DCA_enabled": false
"DEFAULT_sell_only_mode_enabled": "true",
"DEFAULT_DCA_enabled": "false"
}
}
]

View File

@ -3,10 +3,12 @@
"Application": {
"IsEnabled": true, // Enables the PTMagic bot (needs restart to take effect)
"TestMode": false, // If TestMode is active, no properties files will be changed
"ProfitTrailerLicense": "YOUR PROFIT TRAILER LICENSE KEY", // Your Profit Trailer license key (needed to change your settings for PT 2.0 and above)
"ProfitTrailerLicense": "ptlicense1asdf234fljlasdf014325ehm", // Your Profit Trailer license key (needed to change your settings)
"ProfitTrailerLicenseXtra": "", // Licenses for additional bots for PTM to update (optional - comma separated list)
"ProfitTrailerServerAPIToken": "", //Your Profit Trailer Server API Token
"ProfitTrailerMonitorURL": "http://localhost:8081/", // The URL to your profit trailer monitor (needed to change your settings for PT 2.0 and above)
"ProfitTrailerDefaultSettingName": "default", // Your Profit Trailer default setting name (needed to change your settings for PT 2.0 and above)
"ProfitTrailerMonitorURL": "http://localhost:8081/", // The URL to your profit trailer monitor (needed to change your settings)
"ProfitTrailerMonitorURLXtra": "", // URLs for additional bots you want PTM to update (optional - comma separated list)
"ProfitTrailerDefaultSettingName": "default", // Your Profit Trailer default setting name (needed to change your settings)
"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