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 TestMode { get; set; } = true;
public bool EnableBetaFeatures { get; set; } = false; public bool EnableBetaFeatures { get; set; } = false;
public string ProfitTrailerLicense { get; set; } = ""; public string ProfitTrailerLicense { get; set; } = "";
public string ProfitTrailerLicenseXtra { get; set; } = "";
public string ProfitTrailerServerAPIToken { 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 string ProfitTrailerDefaultSettingName { get; set; } = "default";
public int FloodProtectionMinutes { get; set; } = 15; public int FloodProtectionMinutes { get; set; } = 15;
public string Exchange { get; set; } public string Exchange { get; set; }
@ -517,4 +519,4 @@ namespace Core.Main.DataObjects.PTMagicData
} }
#endregion #endregion
} }

View File

@ -108,7 +108,28 @@ namespace Core.Main.DataObjects
if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh)) if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh))
{ {
_sellLog.Clear(); _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); _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) private dynamic GetDataFromProfitTrailer(string callPath, bool arrayReturned = false)
{ {
string rawBody = ""; 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 // Get the data from PT
Debug.WriteLine(String.Format("{0} - Calling '{1}'", DateTime.UtcNow, url)); Debug.WriteLine(String.Format("{0} - Calling '{1}'", DateTime.UtcNow, url));
@ -331,15 +355,9 @@ namespace Core.Main.DataObjects
sellLogData.ProfitPercent = rsld.profit; sellLogData.ProfitPercent = rsld.profit;
sellLogData.SoldPrice = rsld.currentPrice; sellLogData.SoldPrice = rsld.currentPrice;
sellLogData.AverageBuyPrice = rsld.avgPrice; 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 //Convert Unix Timestamp to Datetime
System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc); 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 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) if (!botToken.Equals("") && chatId != 0)
{ {
try try
{ {
TelegramBotClient botClient = new TelegramBotClient(botToken); TelegramBotClient botClient = new TelegramBotClient(botToken);
System.Threading.Tasks.Task<Message> sentMessage = botClient.SendTextMessageAsync(chatId, message, ParseMode.Markdown, false, useSilentMode); 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 + "'");
if (sentMessage.IsCompleted)
{
log.DoLogDebug("Telegram message sent to ChatId " + chatId.ToString() + " on Bot Token '" + botToken + "'");
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Threading; using System.Threading;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -52,7 +53,7 @@ namespace Core.Main
private List<string> _indicatorsLines = null; private List<string> _indicatorsLines = null;
private List<string> _exchangeMarketList = null; private List<string> _exchangeMarketList = null;
private List<string> _marketList = new List<string>(); 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, double> _averageMarketTrendChanges = new Dictionary<string, double>();
private Dictionary<string, List<MarketTrendChange>> _singleMarketTrendChanges = new Dictionary<string, List<MarketTrendChange>>(); private Dictionary<string, List<MarketTrendChange>> _singleMarketTrendChanges = new Dictionary<string, List<MarketTrendChange>>();
private Dictionary<string, List<MarketTrendChange>> _globalMarketTrendChanges = 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 get
{ {
@ -701,13 +702,13 @@ namespace Core.Main
// Check if the program is enabled // Check if the program is enabled
if (this.PTMagicConfiguration.GeneralSettings.Application.IsEnabled) if (this.PTMagicConfiguration.GeneralSettings.Application.IsEnabled)
{ {
result = RunProfitTrailerSettingsAPIChecks();
try try
{ {
if (this.PTMagicConfiguration.GeneralSettings.Application.TestMode) 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 // Check for CoinMarketCap API Key
if (!String.IsNullOrEmpty(this.PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey)) if (!String.IsNullOrEmpty(this.PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey))
@ -731,7 +732,7 @@ namespace Core.Main
} }
catch (System.NullReferenceException) 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.WriteLine("Press enter to close the Application...");
Console.ReadLine(); Console.ReadLine();
Environment.Exit(0); Environment.Exit(0);
@ -739,7 +740,7 @@ namespace Core.Main
} }
else else
{ {
this.Log.DoLogWarn("PTMagic disabled, shutting down..."); this.Log.DoLogWarn("PTMagic is disabled. The scheduled raid was skipped.");
result = false; result = false;
} }
@ -952,7 +953,7 @@ namespace Core.Main
this.Log.DoLogInfo("+ Active setting: " + this.LastRuntimeSummary.CurrentGlobalSetting.SettingName); 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("+ 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("+ 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()); this.Log.DoLogInfo("+ Markets with active single market settings: " + this.TriggeredSingleMarketSettings.Count.ToString());
foreach (string activeSMS in this.SingleMarketSettingsCount.Keys) foreach (string activeSMS in this.SingleMarketSettingsCount.Keys)
{ {
@ -969,7 +970,7 @@ namespace Core.Main
else else
{ {
this.State = Constants.PTMagicBotState_Idle; 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) catch (Exception ex)
@ -1538,7 +1539,7 @@ namespace Core.Main
market = market.Replace("-", ""); market = market.Replace("-", "");
break; break;
case "poloniex": case "poloniex":
market = market.Replace("-", ""); market = market.Replace("_", "");
break; break;
} }
bool stopTriggers = false; bool stopTriggers = false;
@ -2194,9 +2195,12 @@ namespace Core.Main
if (!this.PTMagicConfiguration.GeneralSettings.Application.TestMode) if (!this.PTMagicConfiguration.GeneralSettings.Application.TestMode)
{ {
SettingsAPI.SendPropertyLinesToAPI(this.PairsLines, this.DCALines, this.IndicatorsLines, this.PTMagicConfiguration, this.Log); 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 else
{ {

View File

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

View File

@ -1,17 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.IO; using System.IO;
using System.Text;
using Core.Main; using Core.Main;
using Core.Helper; using Core.Helper;
using Core.Main.DataObjects.PTMagicData; using Core.Main.DataObjects.PTMagicData;
using Newtonsoft.Json; using Newtonsoft.Json;
using Core.ProfitTrailer;
using System.Net; using System.Net;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Concurrent;
namespace Core.MarketAnalyzer namespace Core.MarketAnalyzer
{ {
@ -43,7 +40,7 @@ namespace Core.MarketAnalyzer
return result; 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>(); List<string> result = new List<string>();
@ -163,7 +160,7 @@ namespace Core.MarketAnalyzer
return result; 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..."); 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 = new MarketInfo();
marketInfo.Name = key; marketInfo.Name = key;
marketInfos.Add(key, marketInfo); marketInfos.TryAdd(key, marketInfo);
marketInfo.FirstSeen = Binance.GetFirstSeenDate(key, systemConfiguration, log); marketInfo.FirstSeen = Binance.GetFirstSeenDate(key, systemConfiguration, log);
} }
else else
@ -375,7 +372,7 @@ namespace Core.MarketAnalyzer
} }
Parallel.ForEach(markets.Keys, Parallel.ForEach(markets.Keys,
new ParallelOptions { MaxDegreeOfParallelism = ParallelThrottle}, new ParallelOptions { MaxDegreeOfParallelism = ParallelThrottle },
(key) => (key) =>
{ {
if (!marketTicks.TryAdd(key, GetMarketTicks(key, totalTicks, systemConfiguration, log))) if (!marketTicks.TryAdd(key, GetMarketTicks(key, totalTicks, systemConfiguration, log)))
@ -413,26 +410,28 @@ namespace Core.MarketAnalyzer
} }
} }
Dictionary<string, Market> tickMarkets = new Dictionary<string, Market>(); ConcurrentDictionary<string, Market> tickMarkets = new ConcurrentDictionary<string, Market>();
foreach (string key in markets.Keys)
{
List<MarketTick> tickRange = marketTicks[key] != null ? marketTicks[key].FindAll(t => t.Time <= tickTime) : new List<MarketTick>();
if (tickRange.Count > 0) Parallel.ForEach(markets.Keys,
{ (key) =>
MarketTick marketTick = tickRange.OrderByDescending(t => t.Time).First(); {
List<MarketTick> tickRange = marketTicks[key] != null ? marketTicks[key].FindAll(t => t.Time <= tickTime) : new List<MarketTick>();
Market market = new Market(); if (tickRange.Count > 0)
market.Position = markets.Count + 1; {
market.Name = key; MarketTick marketTick = tickRange.OrderByDescending(t => t.Time).First();
market.Symbol = key;
market.Price = marketTick.Price;
//market.Volume24h = marketTick.Volume24h;
market.MainCurrencyPriceUSD = mainCurrencyPrice;
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(); 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.IO; using System.IO;
using System.Text;
using Core.Main; using Core.Main;
using Core.Helper; using Core.Helper;
using Core.Main.DataObjects.PTMagicData; using Core.Main.DataObjects.PTMagicData;
using Newtonsoft.Json; using Newtonsoft.Json;
using Core.ProfitTrailer;
using System.Net; using System.Net;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Concurrent;
namespace Core.MarketAnalyzer namespace Core.MarketAnalyzer
{ {
@ -43,7 +40,7 @@ namespace Core.MarketAnalyzer
return result; 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>(); List<string> result = new List<string>();
@ -160,7 +157,7 @@ namespace Core.MarketAnalyzer
return result; 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..."); 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 = new MarketInfo();
marketInfo.Name = key; marketInfo.Name = key;
marketInfos.Add(key, marketInfo); marketInfos.TryAdd(key, marketInfo);
marketInfo.FirstSeen = BinanceFutures.GetFirstSeenDate(key, systemConfiguration, log); marketInfo.FirstSeen = BinanceFutures.GetFirstSeenDate(key, systemConfiguration, log);
} }
else else

View File

@ -2,14 +2,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.IO; using System.IO;
using System.Text;
using Core.Main; using Core.Main;
using Core.Helper; using Core.Helper;
using Core.Main.DataObjects.PTMagicData; using Core.Main.DataObjects.PTMagicData;
using Newtonsoft.Json; using Newtonsoft.Json;
using Core.ProfitTrailer;
using System.Net; using System.Net;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Concurrent; 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>(); List<string> result = new List<string>();
@ -168,7 +165,7 @@ namespace Core.MarketAnalyzer
return result; 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..."); 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 = new MarketInfo();
marketInfo.Name = key; marketInfo.Name = key;
marketInfos.Add(key, marketInfo); marketInfos.TryAdd(key, marketInfo);
marketInfo.FirstSeen = BinanceUS.GetFirstSeenDate(key, systemConfiguration, log); marketInfo.FirstSeen = BinanceUS.GetFirstSeenDate(key, systemConfiguration, log);
} }
else else

View File

@ -1,14 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.IO; using System.IO;
using System.Text;
using Core.Main; using Core.Main;
using Core.Helper; using Core.Helper;
using Core.Main.DataObjects.PTMagicData; using Core.Main.DataObjects.PTMagicData;
using Newtonsoft.Json; using Newtonsoft.Json;
using Core.ProfitTrailer;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Core.MarketAnalyzer namespace Core.MarketAnalyzer
@ -44,7 +42,7 @@ namespace Core.MarketAnalyzer
return result; 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>(); List<string> result = new List<string>();
@ -104,7 +102,7 @@ namespace Core.MarketAnalyzer
{ {
marketInfo = new MarketInfo(); marketInfo = new MarketInfo();
marketInfo.Name = marketName; 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"]; if (currencyTicker["Summary"]["Created"].Type == Newtonsoft.Json.Linq.JTokenType.Date) marketInfo.FirstSeen = (DateTime)currencyTicker["Summary"]["Created"];
marketInfo.LastSeen = DateTime.UtcNow; marketInfo.LastSeen = DateTime.UtcNow;

View File

@ -1,14 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.IO; using System.IO;
using System.Text;
using Core.Main; using Core.Main;
using Core.Helper; using Core.Helper;
using Core.Main.DataObjects.PTMagicData; using Core.Main.DataObjects.PTMagicData;
using Newtonsoft.Json; using Newtonsoft.Json;
using Core.ProfitTrailer;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Core.MarketAnalyzer namespace Core.MarketAnalyzer
@ -44,7 +42,7 @@ namespace Core.MarketAnalyzer
return result; 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>(); List<string> result = new List<string>();
@ -125,7 +123,7 @@ namespace Core.MarketAnalyzer
return result; 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..."); 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 = new MarketInfo();
marketInfo.Name = key; marketInfo.Name = key;
marketInfos.Add(key, marketInfo); marketInfos.TryAdd(key, marketInfo);
marketInfo.FirstSeen = Poloniex.GetFirstSeenDate(key, systemConfiguration, log); marketInfo.FirstSeen = Poloniex.GetFirstSeenDate(key, systemConfiguration, log);
} }
else else

View File

@ -19,95 +19,122 @@ namespace Core.ProfitTrailer
bool transferCompleted = false; bool transferCompleted = false;
bool transferCanceled = 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; try
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())
{ {
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.");
} }
catch (TimeoutException ex)
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)
{ {
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++; retryCount++;
if (retryCount <= maxRetries) 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 else
{ {
transferCanceled = true; 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); log.DoLogCritical("Saving Properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': " + ex.Message, ex);
transferCanceled = true; 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) 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("enabled_pairs", "", StringComparison.InvariantCultureIgnoreCase);
result = result.Replace("#", ""); result = result.Replace("#", "");
result = result.Replace("=", "").Trim(); result = result.Replace("=", "").Trim();
@ -644,4 +643,4 @@ namespace Core.ProfitTrailer
} }
#endregion #endregion
} }
} }

View File

@ -272,7 +272,7 @@ namespace Core.ProfitTrailer
if (!string.IsNullOrEmpty(strategyName)) if (!string.IsNullOrEmpty(strategyName))
{ {
// buy/sell strategies beginning with PT 2.3.3 contain the letter followed by a colon and space. // 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; result = true;
} }

View File

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

View File

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

View File

@ -48,24 +48,30 @@
</div> </div>
<div id="collapse1" class="panel-collapse collapse"> <div id="collapse1" class="panel-collapse collapse">
<div class="form-group row"> <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"> <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" /> <input type="checkbox" name="Application_IsEnabled" checked="@(Model.PTMagicConfiguration.GeneralSettings.Application.IsEnabled)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div> </div>
</div> </div>
<div class="form-group row"> <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"> <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" /> <input type="checkbox" name="Application_TestMode" checked="@(Model.PTMagicConfiguration.GeneralSettings.Application.TestMode)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div> </div>
</div> </div>
<div class="form-group row"> <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> <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"> <div class="col-md-8">
@Model.PTMagicConfiguration.GeneralSettings.Application.Exchange <select name="Application_Exchange" class="form-control">
</div> <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> </div>
@ -77,16 +83,16 @@
</div> </div>
<div class="form-group row"> <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"> <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> </div>
<div class="form-group row"> <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> <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"> <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>
</div> </div>
@ -170,28 +176,21 @@
</div> </div>
<div class="form-group row"> <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"> <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" /> <input type="checkbox" name="Monitor_OpenBrowserOnStart" checked="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.OpenBrowserOnStart)" data-plugin="switchery" data-color="#81c868" data-size="small" />
</div> </div>
</div> </div>
<div class="form-group row"> <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"> <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> </div>
<div class="form-group row"> <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> <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">
@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>
<div class="col-md-8"> <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"))"> <input type="text" class="form-control" name="Monitor_AnalyzerChart" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.AnalyzerChart.ToString(new System.Globalization.CultureInfo("en-US"))">
</div> </div>

View File

@ -70,19 +70,19 @@ namespace Monitor.Pages
PTMagicConfiguration.GeneralSettings.Application.TestMode = HttpContext.Request.Form["Application_TestMode"].Equals("on"); 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.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.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.TimezoneOffset = HttpContext.Request.Form["Application_TimezoneOffset"];
PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency = HttpContext.Request.Form["Application_MainFiatCurrency"]; 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.FloodProtectionMinutes = SystemHelper.TextToInteger(HttpContext.Request.Form["Application_FloodProtectionMinutes"], PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes);
PTMagicConfiguration.GeneralSettings.Application.InstanceName = HttpContext.Request.Form["Application_InstanceName"]; PTMagicConfiguration.GeneralSettings.Application.InstanceName = HttpContext.Request.Form["Application_InstanceName"];
PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey = HttpContext.Request.Form["Application_CoinMarketCapAPIKey"]; PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey = HttpContext.Request.Form["Application_CoinMarketCapAPIKey"];
PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey = HttpContext.Request.Form["Application_FreeCurrencyConverterAPIKey"]; PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey = HttpContext.Request.Form["Application_FreeCurrencyConverterAPIKey"];
PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected = HttpContext.Request.Form["Monitor_IsPasswordProtected"].Equals("on"); 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.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.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.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); 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 class="tradingview-widget-container">
<div id="tradingview_6aa22" style="height:600px;"></div> <div id="tradingview_6aa22" style="height:600px;"></div>
<div class="tradingview-widget-copyright"> <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> <span class="blue-text">@Model.DCAMarket</span> <span class="blue-text">chart</span> by TradingView</a>
</div> </div>
</div> </div>

View File

@ -121,14 +121,7 @@
@{ @{
double totalProfit = 0; double totalProfit = 0;
if (Model.PTData.Properties.Shorting) totalProfit = Model.PTData.SellLog.Sum(s => s.Profit);
{
totalProfit = Model.PTData.SellLog.Sum(s => s.Profit * (-1));
}
else
{
totalProfit = Model.PTData.SellLog.Sum(s => s.Profit);
}
double totalProfitFiat = Math.Round(totalProfit * Model.Summary.MainMarketPrice, 2); double totalProfitFiat = Math.Round(totalProfit * Model.Summary.MainMarketPrice, 2);
double percentGain = Math.Round(totalProfit / Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance * 100, 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")) + "%"; string percentGainText = percentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%";
@ -138,53 +131,25 @@
} }
double todaysProfit = 0; double todaysProfit = 0;
if (Model.PTData.Properties.Shorting) todaysProfit = Model.PTData.SellLogToday.Sum(s => s.Profit);
{
todaysProfit = Model.PTData.SellLogToday.Sum(s => s.Profit * (-1));
}
else
{
todaysProfit = Model.PTData.SellLogToday.Sum(s => s.Profit);
}
double todaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime); double todaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime);
double todaysProfitFiat = Math.Round(todaysProfit * Model.Summary.MainMarketPrice, 2); double todaysProfitFiat = Math.Round(todaysProfit * Model.Summary.MainMarketPrice, 2);
double todaysPercentGain = Math.Round(todaysProfit / todaysStartBalance * 100, 2); double todaysPercentGain = Math.Round(todaysProfit / todaysStartBalance * 100, 2);
double yesterdaysProfit = 0; double yesterdaysProfit = 0;
if (Model.PTData.Properties.Shorting) yesterdaysProfit = Model.PTData.SellLogYesterday.Sum(s => s.Profit);
{
yesterdaysProfit = Model.PTData.SellLogYesterday.Sum(s => s.Profit * (-1));
}
else
{
yesterdaysProfit = Model.PTData.SellLogYesterday.Sum(s => s.Profit);
}
double yesterdaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-1)); double yesterdaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-1));
double yesterdaysProfitFiat = Math.Round(yesterdaysProfit * Model.Summary.MainMarketPrice, 2); double yesterdaysProfitFiat = Math.Round(yesterdaysProfit * Model.Summary.MainMarketPrice, 2);
double yesterdaysPercentGain = Math.Round(yesterdaysProfit / yesterdaysStartBalance * 100, 2); double yesterdaysPercentGain = Math.Round(yesterdaysProfit / yesterdaysStartBalance * 100, 2);
double last7DaysProfit = 0; double last7DaysProfit = 0;
if (Model.PTData.Properties.Shorting) last7DaysProfit = Model.PTData.SellLogLast7Days.Sum(s => s.Profit);
{
last7DaysProfit = Model.PTData.SellLogLast7Days.Sum(s => s.Profit * (-1));
}
else
{
last7DaysProfit = Model.PTData.SellLogLast7Days.Sum(s => s.Profit);
}
double last7DaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-7)); double last7DaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-7));
double last7DaysProfitFiat = Math.Round(last7DaysProfit * Model.Summary.MainMarketPrice, 2); double last7DaysProfitFiat = Math.Round(last7DaysProfit * Model.Summary.MainMarketPrice, 2);
double last7DaysPercentGain = Math.Round(last7DaysProfit / last7DaysStartBalance * 100, 2); double last7DaysPercentGain = Math.Round(last7DaysProfit / last7DaysStartBalance * 100, 2);
double last30DaysProfit = 0; double last30DaysProfit = 0;
if (Model.PTData.Properties.Shorting) last30DaysProfit = Model.PTData.SellLogLast30Days.Sum(s => s.Profit);
{
last30DaysProfit = Model.PTData.SellLogLast30Days.Sum(s => s.Profit * (-1));
}
else
{
last30DaysProfit = Model.PTData.SellLogLast30Days.Sum(s => s.Profit);
}
double last30DaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-30)); double last30DaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-30));
double last30DaysProfitFiat = Math.Round(last30DaysProfit * Model.Summary.MainMarketPrice, 2); double last30DaysProfitFiat = Math.Round(last30DaysProfit * Model.Summary.MainMarketPrice, 2);
double last30DaysPercentGain = Math.Round(last30DaysProfit / last30DaysStartBalance * 100, 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; 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); 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); 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")) + "}"; profitPerDayJSON += "{x: new Date('" + salesDate.ToString("yyyy-MM-dd") + "'), y: " + profitFiat.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}";
tradeDayIndex++; 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="24 Hour Trend">24H</th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Total Buy Cost">Cost</th> <th class="text-left" data-toggle="tooltip" data-placement="top" title="Total Buy Cost">Cost</th>
<th></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 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="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-html="true" data-placement="top" title="Profit Target <br> Current Profit">Profit</th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Current Profit">Profit</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@ -202,42 +201,32 @@
<td>@Html.Raw(buyStrategyText)</td> <td>@Html.Raw(buyStrategyText)</td>
<!-- Sell Strategy --> <!-- Sell Strategy -->
<td>@Html.Raw(sellStrategyText)</td> <td>@Html.Raw(sellStrategyText)</td>
<!-- Target --> <!-- Target/Profit -->
@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 -->
@if (!@lostValue) @if (!@lostValue)
{ {
profitPercentage = profitPercentage * leverageValue; @if (!sellStrategyText.Contains("WATCHMODE"))
<td class="text-autocolor">@profitPercentage.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td> {
} @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 else
{ {
<td class="text-left">No Value!</td> <td class="text-left">No Value!</td>
@ -258,7 +247,6 @@
<td></td> <td></td>
<td></td> <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> <td class="text-autocolor">@Html.Raw((((Model.TotalBagGain) / Model.TotalBagCost) * 100).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))%</td>
</tbody> </tbody>
</table> </table>

View File

@ -11,24 +11,29 @@
} }
// Global setting tool tip // 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 // Health indicator
DateTime lastRuntime = Model.Summary.LastRuntime; DateTime lastRuntime = Model.Summary.LastRuntime;
double elapsedSecondsSinceRuntime = DateTime.UtcNow.Subtract(lastRuntime).TotalSeconds; double elapsedSecondsSinceRuntime = DateTime.UtcNow.Subtract(lastRuntime).TotalSeconds;
double intervalSeconds = Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 60.0; double intervalSeconds = Model.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 60.0;
string iconColor = "text-success"; string healthIconColor = "text-success";
string ptMagicHealthIcon = "fa-heartbeat"; string ptMagicHealthIcon = "fa-heartbeat";
string ptMagicHealthTooltip = "PT Magic is alive and healthy! Time elapsed since last run: " + Math.Round(elapsedSecondsSinceRuntime / 60, 1) + " mins."; string ptMagicHealthTooltip = "PT Magic is alive and healthy! Time elapsed since last run: " + Math.Round(elapsedSecondsSinceRuntime / 60, 1) + " mins.";
if (elapsedSecondsSinceRuntime > (intervalSeconds * 2)) { if (elapsedSecondsSinceRuntime > (intervalSeconds * 2)) {
ptMagicHealthIcon = "fa-exclamation-triangle"; 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."; 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"> <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> @Core.Helper.SystemHelper.SplitCamelCase(Model.Summary.CurrentGlobalSetting.SettingName)</span><span class = "header-title"><a href="StatusSummary">@Html.Raw(" " + globalSettingInfoIcon)</a></span>
</div> </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> <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>
<div class="card-box card-box-mini card-box-ptmagic-status-outlined @iconColor" data-toggle="tooltip" data-placement="bottom" title="@ptMagicHealthTooltip"> <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 @iconColor"></i> <i class="fa @ptMagicHealthIcon @healthIconColor"></i>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">

View File

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

View File

@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -15,170 +16,186 @@ using Core.Main.DataObjects;
namespace Monitor._Internal namespace Monitor._Internal
{ {
public class BasePageModel : PageModel 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
{ {
get public string PTMagicBasePath = "";
{ public string PTMagicMonitorBasePath = "";
if (_ptData == null) 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) get
{ {
_ptData = new ProfitTrailerData(PTMagicConfiguration); 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; using Microsoft.Extensions.DependencyInjection;
[assembly: AssemblyVersion("2.5.9")] [assembly: AssemblyVersion("2.5.12")]
[assembly: AssemblyProduct("PT Magic")] [assembly: AssemblyProduct("PT Magic")]
namespace PTMagic namespace PTMagic

View File

@ -1,6 +1,7 @@
// //
// The settings below offer a basic example of some of the options available when using PTMagic. // 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 // Always test your PTMagic settings by running a Profit Trailer bot in TESTMODE, to make sure
// it is performing as you expect. // it is performing as you expect.
// //
@ -10,21 +11,19 @@
{ {
"AnalyzerSettings": { "AnalyzerSettings": {
"MarketAnalyzer": { "MarketAnalyzer": {
"StoreDataMaxHours": 48, // Number of hours to store market data "StoreDataMaxHours": 48, // Number of hours to store market data
"IntervalMinutes": 2, // Interval in minutes for PTMagic to check market trends and triggers "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 "ExcludeMainCurrency": true, // Excludes the main currency (for example BTC) from market trend analysis
"MarketTrends": [ "MarketTrends": [
{ {
"Name": "1h", // UNIQUE market trend name (to be referenced by your triggers below) "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) "Platform": "Exchange", // Platform to grab prices from (Allowed values are: CoinMarketCap, Exchange)
"MaxMarkets": 50, // Number of markets/pairs to analyze sorted by 24h volume "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) "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 "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.
// take the USD value of your main currency into account to build the trend. "TrendThreshold": 15, // Any coin that is above 15% or below -15% for this timeframe will not be used when calculating the market average.
// "Market" will build a trend against your base currency, such as BTC or USDT. "DisplayGraph": false, // Use this trend in the graph on the PTM Monitor dashboard and market analyzer?
"TrendThreshold": 15, // Any coin that is above 15% or below -15% for this timeframe will not be used when calculating the market average. "DisplayOnMarketAnalyzerList": false // Disply this trend on the PTM Monitor market analyzer?
"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": "6h", "Name": "6h",
@ -33,8 +32,8 @@
"TrendMinutes": 360, "TrendMinutes": 360,
"TrendCurrency": "Market", "TrendCurrency": "Market",
"TrendThreshold": 30, "TrendThreshold": 30,
"DisplayGraph": true, "DisplayGraph": true,
"DisplayOnMarketAnalyzerList": true "DisplayOnMarketAnalyzerList": true
}, },
{ {
"Name": "12h", "Name": "12h",
@ -43,8 +42,8 @@
"TrendMinutes": 720, "TrendMinutes": 720,
"TrendCurrency": "Market", "TrendCurrency": "Market",
"TrendThreshold": 50, "TrendThreshold": 50,
"DisplayGraph": true, "DisplayGraph": true,
"DisplayOnMarketAnalyzerList": true "DisplayOnMarketAnalyzerList": true
}, },
{ {
"Name": "24h", "Name": "24h",
@ -53,24 +52,24 @@
"TrendMinutes": 1440, "TrendMinutes": 1440,
"TrendCurrency": "Market", "TrendCurrency": "Market",
"TrendThreshold": 75, "TrendThreshold": 75,
"DisplayGraph": true, "DisplayGraph": true,
"DisplayOnMarketAnalyzerList": true "DisplayOnMarketAnalyzerList": true
} }
] ]
}, },
// ================================ GLOBAL SETTINGS ================================ // ================================ GLOBAL SETTINGS ================================
// //
"GlobalSettings": [ // Global settings for Profit Trailer properties "GlobalSettings": [ // Global settings for Profit Trailer properties
// //
// =================================================================================== // ===================================================================================
// ----------------------------- // -----------------------------
{ {
"SettingName": "EndOfTheWorld", // ANY UNIQUE name of your setting "SettingName": "EndOfTheWorld", // ANY UNIQUE name of your setting
"TriggerConnection": "AND", // Define if triggers will be connected by AND or OR "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 "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 "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) "MaxChange": 0 // The maximum value for this trigger to be true. (Any value below "0" will trigger this)
}, },
{ {
"MarketTrendName": "12h", "MarketTrendName": "12h",
@ -81,17 +80,19 @@
"MaxChange": -5 "MaxChange": -5
} }
], ],
"PairsProperties": { // Properties for PAIRS.PROPERTIES "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. // 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. // 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_sell_only_mode_enabled": true,
"DEFAULT_trailing_profit_OFFSETPERCENT": -50 "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 "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -75
},
"IndicatorsProperties": { // Changes you wish to make to your INDICATORS.properties settings
} }
}, },
// ----------------------------- // -----------------------------
{ {
"SettingName": "TankingDown", "SettingName": "TankingDown",
"TriggerConnection": "AND", "TriggerConnection": "AND",
@ -99,34 +100,25 @@
{ {
"MarketTrendName": "1h", "MarketTrendName": "1h",
"MaxChange": 0 "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": { "PairsProperties": {
"max_trading_pairs_OFFSET": -2, "max_trading_pairs_OFFSET": -2,
"DEFAULT_min_buy_volume_OFFSETPERCENT": 100, "DEFAULT_min_buy_volume_OFFSETPERCENT": 100,
//"DEFAULT_initial_cost_OFFSETPERCENT": -50, //"DEFAULT_initial_cost_OFFSETPERCENT": -50,
//"DEFAULT_initial_cost_percentage_OFFSETPERCENT": -50, //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": -50,
"DEFAULT_trailing_buy_OFFSETPERCENT": 25, "DEFAULT_trailing_buy_OFFSETPERCENT": 25,
"DEFAULT_trailing_profit_OFFSETPERCENT": -25 "DEFAULT_trailing_profit_OFFSETPERCENT": -25
}, },
"DCAProperties": { "DCAProperties": {
//"DEFAULT_DCA_rebuy_timeout_OFFSETPERCENT": 100, //"DEFAULT_DCA_rebuy_timeout_OFFSETPERCENT": 100,
"DEFAULT_DCA_trailing_buy_OFFSETPERCENT": 25, "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": 25,
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -50 "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -50
}, },
"IndicatorsProperties": { "IndicatorsProperties": {
} }
}, },
// ----------------------------- // -----------------------------
{ {
"SettingName": "BearSighted", "SettingName": "BearSighted",
"TriggerConnection": "AND", "TriggerConnection": "AND",
@ -147,16 +139,16 @@
], ],
"PairsProperties": { "PairsProperties": {
"max_trading_pairs_OFFSET": -1, "max_trading_pairs_OFFSET": -1,
//"DEFAULT_initial_cost_OFFSETPERCENT": -25, //"DEFAULT_initial_cost_OFFSETPERCENT": -25,
//"DEFAULT_initial_cost_percentage_OFFSETPERCENT": -25, //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": -25,
"DEFAULT_trailing_buy_OFFSETPERCENT": 10, "DEFAULT_trailing_buy_OFFSETPERCENT": 10,
"DEFAULT_trailing_profit_OFFSETPERCENT": -10 "DEFAULT_trailing_profit_OFFSETPERCENT": -10
}, },
"DCAProperties": { "DCAProperties": {
"DEFAULT_DCA_trailing_buy_OFFSETPERCENT": 10, "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": { "PairsProperties": {
"max_trading_pairs_OFFSET": 1, "max_trading_pairs_OFFSET": 1,
//"DEFAULT_initial_cost_OFFSETPERCENT": 10, //"DEFAULT_initial_cost_OFFSETPERCENT": 10,
//"DEFAULT_initial_cost_percentage_OFFSETPERCENT": 10, //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": 10,
"DEFAULT_trailing_buy_OFFSETPERCENT": -10, "DEFAULT_trailing_buy_OFFSETPERCENT": -10,
"DEFAULT_A_sell_value_OFFSETPERCENT": 10 "DEFAULT_A_sell_value_OFFSETPERCENT": 10
}, },
"DCAProperties": { "DCAProperties": {
"DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -10, "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -10,
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 10, "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 10,
}, },
"IndicatorsProperties": { "IndicatorsProperties": {
} }
}, },
// ----------------------------- // -----------------------------
{ {
"SettingName": "ToTheMoon", "SettingName": "ToTheMoon",
"TriggerConnection": "AND", "TriggerConnection": "AND",
@ -212,8 +204,8 @@
], ],
"PairsProperties": { "PairsProperties": {
"max_trading_pairs_OFFSET": 2, "max_trading_pairs_OFFSET": 2,
//"DEFAULT_initial_cost_OFFSETPERCENT": 20, //"DEFAULT_initial_cost_OFFSETPERCENT": 20,
//"DEFAULT_initial_cost_percentage_OFFSETPERCENT": 20, //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": 20,
"DEFAULT_trailing_buy_OFFSETPERCENT": -10, "DEFAULT_trailing_buy_OFFSETPERCENT": -10,
"DEFAULT_A_sell_value_OFFSETPERCENT": 20 "DEFAULT_A_sell_value_OFFSETPERCENT": 20
}, },
@ -221,7 +213,7 @@
"DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -20, "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -20,
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 20, "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 20,
}, },
"IndicatorsProperties": { "IndicatorsProperties": {
} }
}, },
// ----------------------------- // -----------------------------
@ -238,36 +230,25 @@
} }
} }
], ],
//
// ================================ COIN-SPECIFIC SETTINGS ================================ // ================================ COIN-SPECIFIC SETTINGS ================================
// //
"SingleMarketSettings": [ // Single market/pair settings for Profit Trailer properties "SingleMarketSettings": [ // Single market/pair settings for Profit Trailer properties
// Any setting from https://wiki.profittrailer.com/doku.php?id=pairs.properties // Any setting from https://wiki.profittrailer.com/en/config marked as COIN (coin-specific) can be used here.
// marked as CS (coin-specific) can be used here. // Only coins that meet the triggered conditions will have the settings applied.
// 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": "BlacklistCoins",
"StopProcessWhenTriggered": true,
"Triggers": [
{
"AgeDaysLowerThan": 21
}
],
"PairsProperties": {
"DEFAULT_trading_enabled": false,
"DEFAULT_sell_only_mode_enabled": true,
"DEFAULT_DCA_enabled": false
}
},
// -----------------------------
{ {
"SettingName": "PumpNDumpProtection", "SettingName": "PumpNDumpProtection",
"TriggerConnection": "OR", "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": [ "Triggers": [
{ {
"MarketTrendName": "1h", "MarketTrendName": "1h",
"MarketTrendRelation": "Relative", // The relation of the single market trend. Relative = Single market "MarketTrendRelation": "Relative", // Relative = The single market trend is compared to the overall trend of the entire market
// trend compared relative to the market trend // Absolute = The Single market trend is considered on its own
// Absolute = Single market trend viewed on its own
"MinChange": 8 "MinChange": 8
}, },
{ {
@ -281,18 +262,17 @@
"MinChange": 12 "MinChange": 12
} }
], ],
"OffTriggers": [ "OffTriggers": [
{ {
"HoursSinceTriggered": 3 // Any coin that triggers this setting, will remain under this setting "HoursSinceTriggered": 3 // Any coin that triggers this setting, will remain under this setting
// for 3 hours, since the last time it triggered. // for 3 hours, since the last time it triggered.
} }
], ],
"PairsProperties": { "PairsProperties": {
"DEFAULT_sell_only_mode_enabled": true, "DEFAULT_sell_only_mode_enabled": "true",
"DEFAULT_DCA_enabled": false "DEFAULT_DCA_enabled": "false"
} }
}, },
// -----------------------------
{ {
"SettingName": "FreefallBlock", "SettingName": "FreefallBlock",
"TriggerConnection": "OR", "TriggerConnection": "OR",
@ -303,14 +283,14 @@
"MaxChange": -5 "MaxChange": -5
} }
], ],
"OffTriggers": [ "OffTriggers": [
{ {
"HoursSinceTriggered": 1 "HoursSinceTriggered": 1
} }
], ],
"PairsProperties": { "PairsProperties": {
"DEFAULT_sell_only_mode_enabled": true, "DEFAULT_sell_only_mode_enabled": "true",
"DEFAULT_DCA_enabled": false "DEFAULT_DCA_enabled": "false"
} }
} }
] ]

View File

@ -3,10 +3,12 @@
"Application": { "Application": {
"IsEnabled": true, // Enables the PTMagic bot (needs restart to take effect) "IsEnabled": true, // Enables the PTMagic bot (needs restart to take effect)
"TestMode": false, // If TestMode is active, no properties files will be changed "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 "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) "ProfitTrailerMonitorURL": "http://localhost:8081/", // The URL to your profit trailer monitor (needed to change your settings)
"ProfitTrailerDefaultSettingName": "default", // Your Profit Trailer default setting name (needed to change your settings for PT 2.0 and above) "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 "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 "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 "TimezoneOffset": "+0:00", // Your timezone offset from UTC time
@ -19,9 +21,9 @@
"Monitor": { "Monitor": {
"IsPasswordProtected": true, // Defines if your monitor will be asking to setup a password on its first start "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 "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 "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 "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 "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 "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. // 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 // Always test your PTMagic settings by running a Profit Trailer bot in TESTMODE, to make sure
// it is performing as you expect. // it is performing as you expect.
// //
@ -10,21 +11,19 @@
{ {
"AnalyzerSettings": { "AnalyzerSettings": {
"MarketAnalyzer": { "MarketAnalyzer": {
"StoreDataMaxHours": 48, // Number of hours to store market data "StoreDataMaxHours": 48, // Number of hours to store market data
"IntervalMinutes": 2, // Interval in minutes for PTMagic to check market trends and triggers "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 "ExcludeMainCurrency": true, // Excludes the main currency (for example BTC) from market trend analysis
"MarketTrends": [ "MarketTrends": [
{ {
"Name": "1h", // UNIQUE market trend name (to be referenced by your triggers below) "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) "Platform": "Exchange", // Platform to grab prices from (Allowed values are: CoinMarketCap, Exchange)
"MaxMarkets": 50, // Number of markets/pairs to analyze sorted by 24h volume "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) "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 "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.
// take the USD value of your main currency into account to build the trend. "TrendThreshold": 15, // Any coin that is above 15% or below -15% for this timeframe will not be used when calculating the market average.
// "Market" will build a trend against your base currency, such as BTC or USDT. "DisplayGraph": false, // Use this trend in the graph on the PTM Monitor dashboard and market analyzer?
"TrendThreshold": 15, // Any coin that is above 15% or below -15% for this timeframe will not be used when calculating the market average. "DisplayOnMarketAnalyzerList": false // Disply this trend on the PTM Monitor market analyzer?
"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": "6h", "Name": "6h",
@ -65,12 +64,12 @@
// =================================================================================== // ===================================================================================
// ----------------------------- // -----------------------------
{ {
"SettingName": "EndOfTheWorld", // ANY UNIQUE name of your setting "SettingName": "EndOfTheWorld", // ANY UNIQUE name of your setting
"TriggerConnection": "AND", // Define if triggers will be connected by AND or OR "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 "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 "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) "MaxChange": 0 // The maximum value for this trigger to be true. (Any value below "0" will trigger this)
}, },
{ {
"MarketTrendName": "12h", "MarketTrendName": "12h",
@ -81,14 +80,16 @@
"MaxChange": -5 "MaxChange": -5
} }
], ],
"PairsProperties": { // Properties for PAIRS.PROPERTIES "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. // 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. // 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_sell_only_mode_enabled": true,
"DEFAULT_trailing_profit_OFFSETPERCENT": -50 "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 "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -75
},
"IndicatorsProperties": { // Changes you wish to make to your INDICATORS.properties settings
} }
}, },
// ----------------------------- // -----------------------------
@ -99,24 +100,15 @@
{ {
"MarketTrendName": "1h", "MarketTrendName": "1h",
"MaxChange": 0 "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": { "PairsProperties": {
"max_trading_pairs_OFFSET": -2, "max_trading_pairs_OFFSET": -2,
"DEFAULT_min_buy_volume_OFFSETPERCENT": 100, "DEFAULT_min_buy_volume_OFFSETPERCENT": 100,
//"DEFAULT_initial_cost_OFFSETPERCENT": -50, //"DEFAULT_initial_cost_OFFSETPERCENT": -50,
//"DEFAULT_initial_cost_percentage_OFFSETPERCENT": -50, //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": -50,
"DEFAULT_trailing_buy_OFFSETPERCENT": 25, "DEFAULT_trailing_buy_OFFSETPERCENT": 25,
"DEFAULT_trailing_profit_OFFSETPERCENT": -25 "DEFAULT_trailing_profit_OFFSETPERCENT": -25
}, },
"DCAProperties": { "DCAProperties": {
//"DEFAULT_DCA_rebuy_timeout_OFFSETPERCENT": 100, //"DEFAULT_DCA_rebuy_timeout_OFFSETPERCENT": 100,
@ -149,8 +141,8 @@
"max_trading_pairs_OFFSET": -1, "max_trading_pairs_OFFSET": -1,
//"DEFAULT_initial_cost_OFFSETPERCENT": -25, //"DEFAULT_initial_cost_OFFSETPERCENT": -25,
//"DEFAULT_initial_cost_percentage_OFFSETPERCENT": -25, //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": -25,
"DEFAULT_trailing_buy_OFFSETPERCENT": 10, "DEFAULT_trailing_buy_OFFSETPERCENT": 10,
"DEFAULT_trailing_profit_OFFSETPERCENT": -10 "DEFAULT_trailing_profit_OFFSETPERCENT": -10
}, },
"DCAProperties": { "DCAProperties": {
"DEFAULT_DCA_trailing_buy_OFFSETPERCENT": 10, "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": 10,
@ -180,13 +172,13 @@
], ],
"PairsProperties": { "PairsProperties": {
"max_trading_pairs_OFFSET": 1, "max_trading_pairs_OFFSET": 1,
//"DEFAULT_initial_cost_OFFSETPERCENT": 10, //"DEFAULT_initial_cost_OFFSETPERCENT": 10,
//"DEFAULT_initial_cost_percentage_OFFSETPERCENT": 10, //"DEFAULT_initial_cost_percentage_OFFSETPERCENT": 10,
"DEFAULT_trailing_buy_OFFSETPERCENT": -10, "DEFAULT_trailing_buy_OFFSETPERCENT": -10,
"DEFAULT_A_sell_value_OFFSETPERCENT": 10 "DEFAULT_A_sell_value_OFFSETPERCENT": 10
}, },
"DCAProperties": { "DCAProperties": {
"DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -10, "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -10,
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 10, "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 10,
}, },
"IndicatorsProperties": { "IndicatorsProperties": {
@ -219,7 +211,7 @@
}, },
"DCAProperties": { "DCAProperties": {
"DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -20, "DEFAULT_DCA_trailing_buy_OFFSETPERCENT": -20,
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 20, "DEFAULT_DCA_trailing_profit_OFFSETPERCENT": 20,
}, },
"IndicatorsProperties": { "IndicatorsProperties": {
} }
@ -238,36 +230,25 @@
} }
} }
], ],
//
// ================================ COIN-SPECIFIC SETTINGS ================================ // ================================ COIN-SPECIFIC SETTINGS ================================
// //
"SingleMarketSettings": [ // Single market/pair settings for Profit Trailer properties "SingleMarketSettings": [ // Single market/pair settings for Profit Trailer properties
// Any setting from https://wiki.profittrailer.com/en/config // Any setting from https://wiki.profittrailer.com/en/config marked as COIN (coin-specific) can be used here.
// marked as CS (coin-specific) can be used here. // Only coins that meet the triggered conditions will have the settings applied.
// 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": "BlacklistCoins",
"StopProcessWhenTriggered": true,
"Triggers": [
{
"AgeDaysLowerThan": 21
}
],
"PairsProperties": {
"DEFAULT_trading_enabled": false,
"DEFAULT_sell_only_mode_enabled": true,
"DEFAULT_DCA_enabled": false
}
},
// -----------------------------
{ {
"SettingName": "PumpNDumpProtection", "SettingName": "PumpNDumpProtection",
"TriggerConnection": "OR", "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": [ "Triggers": [
{ {
"MarketTrendName": "1h", "MarketTrendName": "1h",
"MarketTrendRelation": "Relative", // The relation of the single market trend. Relative = Single market "MarketTrendRelation": "Relative", // Relative = The single market trend is compared to the overall trend of the entire market
// trend compared relative to the market trend // Absolute = The Single market trend is considered on its own
// Absolute = Single market trend viewed on its own
"MinChange": 8 "MinChange": 8
}, },
{ {
@ -283,16 +264,15 @@
], ],
"OffTriggers": [ "OffTriggers": [
{ {
"HoursSinceTriggered": 3 // Any coin that triggers this setting, will remain under this setting "HoursSinceTriggered": 3 // Any coin that triggers this setting, will remain under this setting
// for 3 hours, since the last time it triggered. // for 3 hours, since the last time it triggered.
} }
], ],
"PairsProperties": { "PairsProperties": {
"DEFAULT_sell_only_mode_enabled": true, "DEFAULT_sell_only_mode_enabled": "true",
"DEFAULT_DCA_enabled": false "DEFAULT_DCA_enabled": "false"
} }
}, },
// -----------------------------
{ {
"SettingName": "FreefallBlock", "SettingName": "FreefallBlock",
"TriggerConnection": "OR", "TriggerConnection": "OR",
@ -309,8 +289,8 @@
} }
], ],
"PairsProperties": { "PairsProperties": {
"DEFAULT_sell_only_mode_enabled": true, "DEFAULT_sell_only_mode_enabled": "true",
"DEFAULT_DCA_enabled": false "DEFAULT_DCA_enabled": "false"
} }
} }
] ]

View File

@ -3,10 +3,12 @@
"Application": { "Application": {
"IsEnabled": true, // Enables the PTMagic bot (needs restart to take effect) "IsEnabled": true, // Enables the PTMagic bot (needs restart to take effect)
"TestMode": false, // If TestMode is active, no properties files will be changed "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 "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) "ProfitTrailerMonitorURL": "http://localhost:8081/", // The URL to your profit trailer monitor (needed to change your settings)
"ProfitTrailerDefaultSettingName": "default", // Your Profit Trailer default setting name (needed to change your settings for PT 2.0 and above) "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 "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 "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 "TimezoneOffset": "+0:00", // Your timezone offset from UTC time