commit
1298ff0c8a
|
@ -104,6 +104,13 @@
|
|||
"focus": true
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"type": "dotnet",
|
||||
"task": "build",
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"label": "dotnet: build"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="*" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.3.8" />
|
||||
<PackageReference Include="Telegram.Bot" Version="*" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace Core.Main.DataObjects.PTMagicData
|
|||
public int FloodProtectionMinutes { get; set; } = 15;
|
||||
public string Exchange { get; set; }
|
||||
public string InstanceName { get; set; } = "PT Magic";
|
||||
public string TimezoneOffset { get; set; } = "+0:00";
|
||||
//public string TimezoneOffset { get; set; } = "+0:00";
|
||||
public string CoinMarketCapAPIKey { get; set; }
|
||||
//public string FreeCurrencyConverterAPIKey { get; set; }
|
||||
}
|
||||
|
@ -55,13 +55,13 @@ namespace Core.Main.DataObjects.PTMagicData
|
|||
public bool OpenBrowserOnStart { get; set; } = false;
|
||||
public int Port { get; set; } = 5000;
|
||||
public string AnalyzerChart { get; set; } = "";
|
||||
public int LiveTCVTimeframeMinutes { get; set; } = 60;
|
||||
public int GraphIntervalMinutes { get; set; } = 60;
|
||||
public int GraphMaxTimeframeHours { get; set; } = 24;
|
||||
public int ProfitsMaxTimeframeDays { get; set; } = 60;
|
||||
public int RefreshSeconds { get; set; } = 30;
|
||||
public int DashboardChartsRefreshSeconds { get; set; } = 30;
|
||||
public int BagAnalyzerRefreshSeconds { get; set; } = 5;
|
||||
public int BuyAnalyzerRefreshSeconds { get; set; } = 5;
|
||||
//public int MaxSalesRecords { get; set; } = 99999;
|
||||
public int MaxTopMarkets { get; set; } = 20;
|
||||
public int MaxDailySummaries { get; set; } = 10;
|
||||
public int MaxMonthlySummaries { get; set; } = 10;
|
||||
|
@ -70,6 +70,7 @@ namespace Core.Main.DataObjects.PTMagicData
|
|||
public int MaxDCAPairs { get; set; } = 24;
|
||||
public int MaxSettingsLogEntries { get; set; } = 20;
|
||||
public string LinkPlatform { get; set; } = "TradingView";
|
||||
public string TVCustomLayout { get; set; } = "";
|
||||
public string DefaultDCAMode { get; set; } = "Simple";
|
||||
public string TvStudyA { get; set; } = "";
|
||||
public string TvStudyB { get; set; } = "";
|
||||
|
@ -164,6 +165,7 @@ namespace Core.Main.DataObjects.PTMagicData
|
|||
public class SingleMarketSetting
|
||||
{
|
||||
public string SettingName { get; set; }
|
||||
|
||||
public string TriggerConnection { get; set; } = "AND";
|
||||
|
||||
[DefaultValue("AND")]
|
||||
|
@ -195,6 +197,9 @@ namespace Core.Main.DataObjects.PTMagicData
|
|||
|
||||
public class Trigger
|
||||
{
|
||||
[DefaultValue("")]
|
||||
public string Tag { get; set; } = "";
|
||||
|
||||
[DefaultValue("")]
|
||||
public string MarketTrendName { get; set; } = "";
|
||||
|
||||
|
@ -219,6 +224,9 @@ namespace Core.Main.DataObjects.PTMagicData
|
|||
|
||||
public class OffTrigger
|
||||
{
|
||||
[DefaultValue("")]
|
||||
public string Tag { get; set; } = "";
|
||||
|
||||
[DefaultValue("")]
|
||||
public string MarketTrendName { get; set; } = "";
|
||||
|
||||
|
@ -399,21 +407,6 @@ namespace Core.Main.DataObjects.PTMagicData
|
|||
public double Last24hVolume { get; set; } = 0;
|
||||
}
|
||||
|
||||
// public class SellLogData
|
||||
// {
|
||||
// public double SoldAmount { get; set; }
|
||||
// public DateTime SoldDate { get; set; }
|
||||
// public int BoughtTimes { get; set; }
|
||||
// public string Market { get; set; }
|
||||
// public double ProfitPercent { get; set; }
|
||||
// public double Profit { get; set; }
|
||||
// public double AverageBuyPrice { get; set; }
|
||||
// public double TotalCost { get; set; }
|
||||
// public double SoldPrice { get; set; }
|
||||
// public double SoldValue { get; set; }
|
||||
// public double TotalSales { get; set; }
|
||||
// }
|
||||
|
||||
public class StatsData
|
||||
{
|
||||
public double SalesToday { get; set; }
|
||||
|
@ -572,5 +565,7 @@ namespace Core.Main.DataObjects.PTMagicData
|
|||
public string Market { get; set; }
|
||||
public string TotalCurrentValue { get; set; }
|
||||
public string TimeZoneOffset { get; set; }
|
||||
public string ExchangeURL { get; set; }
|
||||
public string PTVersion { get; set; }
|
||||
}
|
||||
}
|
|
@ -82,8 +82,11 @@ namespace Core.Main.DataObjects
|
|||
{
|
||||
if (!_offsetTimeSpan.HasValue)
|
||||
{
|
||||
// Get offset for settings.
|
||||
_offsetTimeSpan = TimeSpan.Parse(_systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
// Ensure Misc is populated
|
||||
var misc = this.Misc;
|
||||
|
||||
// Get offset from Misc
|
||||
_offsetTimeSpan = TimeSpan.Parse(misc.TimeZoneOffset);
|
||||
}
|
||||
|
||||
return _offsetTimeSpan.Value;
|
||||
|
@ -111,7 +114,7 @@ namespace Core.Main.DataObjects
|
|||
if (_misc == null || (DateTime.UtcNow > _miscRefresh))
|
||||
{
|
||||
_misc = BuildMiscData(GetDataFromProfitTrailer("api/v2/data/misc"));
|
||||
_miscRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
|
||||
_miscRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,6 +136,8 @@ namespace Core.Main.DataObjects
|
|||
StartBalance = PTData.startBalance,
|
||||
TotalCurrentValue = PTData.totalCurrentValue,
|
||||
TimeZoneOffset = PTData.timeZoneOffset,
|
||||
ExchangeURL = PTData.exchangeUrl,
|
||||
PTVersion = PTData.version,
|
||||
};
|
||||
}
|
||||
public List<DailyStatsData> DailyStats
|
||||
|
@ -180,7 +185,7 @@ namespace Core.Main.DataObjects
|
|||
{
|
||||
JArray dailyStatsSection = (JArray)extraSection["dailyStats"];
|
||||
_dailyStats = dailyStatsSection.Select(j => BuildDailyStatsData(j as JObject)).ToList();
|
||||
_dailyStatsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
|
||||
_dailyStatsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +220,7 @@ namespace Core.Main.DataObjects
|
|||
if (_properties == null || (DateTime.UtcNow > _propertiesRefresh))
|
||||
{
|
||||
_properties = BuildProptertiesData(GetDataFromProfitTrailer("api/v2/data/properties"));
|
||||
_propertiesRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
|
||||
_propertiesRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -258,7 +263,7 @@ namespace Core.Main.DataObjects
|
|||
jsonReader.Read(); // Move to the value of the "basic" property
|
||||
JObject basicSection = JObject.Load(jsonReader);
|
||||
_stats = BuildStatsData(basicSection);
|
||||
_statsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
|
||||
_statsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -355,7 +360,7 @@ namespace Core.Main.DataObjects
|
|||
{
|
||||
JArray dailyPNLSection = (JArray)extraSection["dailyPNLStats"];
|
||||
_dailyPNL = dailyPNLSection.Select(j => BuildDailyPNLData(j as JObject)).ToList();
|
||||
_dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
|
||||
_dailyPNLRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -429,7 +434,7 @@ namespace Core.Main.DataObjects
|
|||
_profitablePairs.Add(BuildProfitablePairs(profitablePair));
|
||||
counter++;
|
||||
}
|
||||
_profitablePairsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
|
||||
_profitablePairsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -495,7 +500,7 @@ namespace Core.Main.DataObjects
|
|||
{
|
||||
JArray dailyTCVSection = (JArray)extraSection["dailyTCVStats"];
|
||||
_dailyTCV = dailyTCVSection.Select(j => BuildDailyTCVData(j as JObject)).ToList();
|
||||
_dailyTCVRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
|
||||
_dailyTCVRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -563,7 +568,7 @@ namespace Core.Main.DataObjects
|
|||
{
|
||||
JArray monthlyStatsSection = (JArray)extraSection["monthlyStats"];
|
||||
_monthlyStats = monthlyStatsSection.Select(j => BuildMonthlyStatsData(j as JObject)).ToList();
|
||||
_monthlyStatsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
|
||||
_monthlyStatsRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -585,57 +590,6 @@ namespace Core.Main.DataObjects
|
|||
Order = monthlyStatsDataJson["order"],
|
||||
};
|
||||
}
|
||||
// public List<SellLogData> SellLog
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
|
||||
// if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh))
|
||||
// {
|
||||
// lock (_sellLock)
|
||||
// {
|
||||
// // Thread double locking
|
||||
// if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh))
|
||||
// {
|
||||
// _sellLog.Clear();
|
||||
|
||||
|
||||
// // Page through the sales data summarizing it.
|
||||
// bool exitLoop = false;
|
||||
// int pageIndex = 1;
|
||||
|
||||
// // 1 record per page to allow user to set max records to retrieve
|
||||
// int maxPages = _systemConfiguration.GeneralSettings.Monitor.MaxSalesRecords;
|
||||
// int requestedPages = 0;
|
||||
|
||||
// while (!exitLoop && requestedPages < maxPages)
|
||||
// {
|
||||
// var sellDataPage = GetDataFromProfitTrailer("/api/v2/data/sales?Page=1&perPage=1&sort=SOLDDATE&sortDirection=DESCENDING&page=" + pageIndex);
|
||||
// if (sellDataPage != null && sellDataPage.data.Count > 0)
|
||||
// {
|
||||
// // Add sales data page to collection
|
||||
// this.BuildSellLogData(sellDataPage);
|
||||
// pageIndex++;
|
||||
// requestedPages++;
|
||||
// Console.WriteLine($"Importing salesLog: {pageIndex}");
|
||||
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // All data retrieved
|
||||
// exitLoop = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Update sell log refresh time
|
||||
// _sellLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds -1);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return _sellLog;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
public List<DCALogData> DCALog
|
||||
{
|
||||
|
@ -682,11 +636,15 @@ namespace Core.Main.DataObjects
|
|||
{
|
||||
get
|
||||
{
|
||||
if (_systemConfiguration.GeneralSettings.Monitor.MaxDashboardBuyEntries == 0)
|
||||
{
|
||||
return _buyLog;
|
||||
}
|
||||
|
||||
if (_buyLog == null || (DateTime.UtcNow > _buyLogRefresh))
|
||||
{
|
||||
lock (_buyLock)
|
||||
{
|
||||
// Thread double locking
|
||||
if (_buyLog == null || (DateTime.UtcNow > _buyLogRefresh))
|
||||
{
|
||||
_buyLog.Clear();
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
@ -535,18 +533,22 @@ namespace Core.Helper
|
|||
}
|
||||
}
|
||||
|
||||
public static string GetMarketLink(string platform, string exchange, string market, string mainMarket)
|
||||
public static string GetMarketLink(string platform, string exchange, string market, string mainMarket, string tvCustomLayout)
|
||||
{
|
||||
string result = "#";
|
||||
if (tvCustomLayout.Length > 0)
|
||||
{
|
||||
tvCustomLayout += "/";
|
||||
}
|
||||
if (platform.Equals("TradingView"))
|
||||
{
|
||||
if (exchange.Equals("binancefutures", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
result = "https://uk.tradingview.com/chart/?symbol=BINANCE:" + market.ToUpper() + "PERP";
|
||||
result = "https://uk.tradingview.com/chart/"+tvCustomLayout+"?symbol=BINANCE:" + market.ToUpper() + ".P";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = "https://uk.tradingview.com/chart/?symbol=" + exchange.ToUpper() + ":" + market.ToUpper();
|
||||
result = "https://uk.tradingview.com/chart/"+tvCustomLayout+"?symbol=" + exchange.ToUpper() + ":" + market.ToUpper();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
using System;
|
||||
using System.Data;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Linq.Expressions;
|
||||
using Core.Helper;
|
||||
using Core.Main.DataObjects.PTMagicData;
|
||||
using Core.MarketAnalyzer;
|
||||
|
@ -59,6 +62,7 @@ namespace Core.Main
|
|||
private Dictionary<string, int> _singleMarketSettingsCount = new Dictionary<string, int>();
|
||||
Dictionary<string, List<SingleMarketSetting>> _triggeredSingleMarketSettings = new Dictionary<string, List<SingleMarketSetting>>();
|
||||
private static volatile object _lockObj = new object();
|
||||
private Mutex mutex = new Mutex(false, "analyzerStateMutex");
|
||||
|
||||
public LogHelper Log
|
||||
{
|
||||
|
@ -122,6 +126,27 @@ namespace Core.Main
|
|||
_state = value;
|
||||
}
|
||||
}
|
||||
public void WriteStateToFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
mutex.WaitOne(); // Acquire the mutex
|
||||
|
||||
string dirPath = "_data";
|
||||
string filePath = Path.Combine(dirPath, "AnalyzerState.");
|
||||
|
||||
// Ensure the directory exists
|
||||
Directory.CreateDirectory(dirPath);
|
||||
|
||||
File.WriteAllText(filePath, this.State.ToString());
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
mutex.ReleaseMutex(); // Release the mutex even if exceptions occur
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int RunCount
|
||||
{
|
||||
|
@ -828,6 +853,8 @@ namespace Core.Main
|
|||
|
||||
#region PTMagic Interval Methods
|
||||
|
||||
|
||||
|
||||
public void PTMagicIntervalTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
// Check if the bot is idle
|
||||
|
@ -840,6 +867,7 @@ namespace Core.Main
|
|||
{
|
||||
// Change state to "Running"
|
||||
this.State = Constants.PTMagicBotState_Running;
|
||||
this.WriteStateToFile();
|
||||
this.RunCount++;
|
||||
this.LastRuntime = DateTime.UtcNow;
|
||||
|
||||
|
@ -975,6 +1003,7 @@ namespace Core.Main
|
|||
|
||||
// Change state to Finished / Stopped
|
||||
this.State = Constants.PTMagicBotState_Idle;
|
||||
this.WriteStateToFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -991,6 +1020,7 @@ namespace Core.Main
|
|||
{
|
||||
Log.DoLogWarn("PTMagic raid " + this.RunCount.ToString() + " is taking longer than expected. Consider increasing your IntervalMinues setting, reducing other processes on your PC, or raising PTMagic's priority.");
|
||||
this.State = Constants.PTMagicBotState_Idle;
|
||||
this.WriteStateToFile();
|
||||
Log.DoLogInfo("PTMagic status reset, waiting for the next raid to be good to go again.");
|
||||
}
|
||||
}
|
||||
|
@ -998,6 +1028,7 @@ namespace Core.Main
|
|||
{
|
||||
Log.DoLogWarn("No LastRuntimeSummary.json found after raid " + this.RunCount.ToString() + ", trying to reset PT Magic status...");
|
||||
this.State = Constants.PTMagicBotState_Idle;
|
||||
this.WriteStateToFile();
|
||||
Log.DoLogInfo("PTMagic status reset, waiting for the next raid to be good to go again.");
|
||||
}
|
||||
}
|
||||
|
@ -1269,6 +1300,8 @@ namespace Core.Main
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void BuildGlobalMarketTrends()
|
||||
{
|
||||
this.Log.DoLogInfo("Build global market trends...");
|
||||
|
@ -1301,6 +1334,7 @@ namespace Core.Main
|
|||
private void CheckGlobalSettingsTriggers(ref GlobalSetting triggeredSetting, ref List<string> matchedTriggers)
|
||||
{
|
||||
this.Log.DoLogInfo("Checking global settings triggers...");
|
||||
|
||||
foreach (GlobalSetting globalSetting in this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings)
|
||||
{
|
||||
// Reset triggers for each setting
|
||||
|
@ -1309,20 +1343,21 @@ namespace Core.Main
|
|||
if (globalSetting.Triggers.Count > 0)
|
||||
{
|
||||
this.Log.DoLogInfo("Checking triggers for '" + globalSetting.SettingName + "'...");
|
||||
List<bool> triggerResults = new List<bool>();
|
||||
Dictionary<string, bool> triggerResults = new Dictionary<string, bool>();
|
||||
foreach (Trigger trigger in globalSetting.Triggers)
|
||||
{
|
||||
MarketTrend marketTrend = this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.Find(mt => mt.Name == trigger.MarketTrendName);
|
||||
if (marketTrend != null)
|
||||
{
|
||||
|
||||
// Get market trend change for trigger
|
||||
if (this.AverageMarketTrendChanges.ContainsKey(marketTrend.Name))
|
||||
{
|
||||
double averageMarketTrendChange = this.AverageMarketTrendChanges[marketTrend.Name];
|
||||
if (averageMarketTrendChange >= trigger.MinChange && averageMarketTrendChange < trigger.MaxChange)
|
||||
{
|
||||
bool isTriggered = averageMarketTrendChange >= trigger.MinChange && averageMarketTrendChange < trigger.MaxChange;
|
||||
triggerResults[trigger.Tag] = isTriggered;
|
||||
|
||||
if (isTriggered)
|
||||
{
|
||||
// Trigger met!
|
||||
this.Log.DoLogInfo("Trigger '" + trigger.MarketTrendName + "' triggered! TrendChange = " + averageMarketTrendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%");
|
||||
|
||||
|
@ -1338,37 +1373,38 @@ namespace Core.Main
|
|||
}
|
||||
|
||||
matchedTriggers.Add(triggerContent);
|
||||
|
||||
triggerResults.Add(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Log.DoLogDebug("Trigger '" + trigger.MarketTrendName + "' not triggered. TrendChange = " + averageMarketTrendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%");
|
||||
triggerResults.Add(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Log.DoLogError("Trigger '" + trigger.MarketTrendName + "' not found in this.AverageMarketTrendChanges[] (" + SystemHelper.ConvertListToTokenString(this.AverageMarketTrendChanges.Keys.ToList(), ",", true) + "). Unable to load recent trends?");
|
||||
triggerResults.Add(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Log.DoLogWarn("Market Trend '" + trigger.MarketTrendName + "' not found! Trigger ignored!");
|
||||
triggerResults.Add(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all triggers have to get triggered or just one
|
||||
// Check if the TriggerConnection field exists
|
||||
if (!string.IsNullOrEmpty(globalSetting.TriggerConnection))
|
||||
{
|
||||
// Check if TriggerConnection is using the old logic
|
||||
if (globalSetting.TriggerConnection.ToLower() == "and" || globalSetting.TriggerConnection.ToLower() == "or")
|
||||
{
|
||||
// Old logic
|
||||
bool settingTriggered = false;
|
||||
switch (globalSetting.TriggerConnection.ToLower())
|
||||
{
|
||||
case "and":
|
||||
settingTriggered = triggerResults.FindAll(tr => tr == false).Count == 0;
|
||||
settingTriggered = triggerResults.Values.All(tr => tr);
|
||||
break;
|
||||
case "or":
|
||||
settingTriggered = triggerResults.FindAll(tr => tr == true).Count > 0;
|
||||
settingTriggered = triggerResults.Values.Any(tr => tr);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1379,6 +1415,47 @@ namespace Core.Main
|
|||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// New logic
|
||||
string triggerConnection = globalSetting.TriggerConnection;
|
||||
foreach (var triggerResult in triggerResults)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(triggerResult.Key))
|
||||
{
|
||||
triggerConnection = triggerConnection.Replace(triggerResult.Key, triggerResult.Value.ToString().ToLower());
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Log.DoLogError($"ERROR: A required trigger Tag is missing for global setting {globalSetting.SettingName}. Program halted.");
|
||||
Environment.Exit(1); // Stop the program
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
bool settingTriggered = (bool)System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(System.Linq.Dynamic.Core.ParsingConfig.Default, new ParameterExpression[0], typeof(bool), triggerConnection).Compile().DynamicInvoke();
|
||||
|
||||
// Setting got triggered -> Activate it!
|
||||
if (settingTriggered)
|
||||
{
|
||||
triggeredSetting = globalSetting;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Log.DoLogError($"ERROR: Trigger Connection for global setting {globalSetting.SettingName} is invalid or missing. Program halted.");
|
||||
Environment.Exit(1); // Stop the program
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Log.DoLogError($"ERROR: Trigger Connection for global setting {globalSetting.SettingName} is missing. Program halted.");
|
||||
Environment.Exit(1); // Stop the program
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1751,6 +1828,14 @@ namespace Core.Main
|
|||
Dictionary<int, double> relevantTriggers = new Dictionary<int, double>();
|
||||
int triggerIndex = 0;
|
||||
|
||||
// Create a dictionary to store the tag and its corresponding result
|
||||
Dictionary<string, bool> triggerTagsResults = new Dictionary<string, bool>();
|
||||
// Initialize all tags with a value of false
|
||||
foreach (Trigger trigger in marketSetting.Triggers)
|
||||
{
|
||||
triggerTagsResults[trigger.Tag] = false;
|
||||
}
|
||||
|
||||
// Loop through SMS triggers
|
||||
foreach (Trigger trigger in marketSetting.Triggers)
|
||||
{
|
||||
|
@ -1786,6 +1871,7 @@ namespace Core.Main
|
|||
matchedSingleMarketTriggers.Add(marketSetting.SettingName + ": " + triggerContent + " - 24h volume = " + mtc.Volume24h.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket);
|
||||
|
||||
triggerResults.Add(true);
|
||||
triggerTagsResults[trigger.Tag] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1816,6 +1902,7 @@ namespace Core.Main
|
|||
|
||||
relevantTriggers.Add(triggerIndex, marketAge);
|
||||
triggerResults.Add(true);
|
||||
triggerTagsResults[trigger.Tag] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1897,6 +1984,7 @@ namespace Core.Main
|
|||
matchedSingleMarketTriggers.Add(marketSetting.SettingName + ": " + triggerContent + " - TrendChange (" + trigger.MarketTrendRelation + ") = " + trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%");
|
||||
|
||||
triggerResults.Add(true);
|
||||
triggerTagsResults[trigger.Tag] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1938,8 +2026,11 @@ namespace Core.Main
|
|||
triggerIndex++;
|
||||
} // End loop SMS triggers
|
||||
|
||||
|
||||
// Check if all triggers have to get triggered or just one
|
||||
bool settingTriggered = false;
|
||||
if (marketSetting.TriggerConnection.ToLower() == "and" || marketSetting.TriggerConnection.ToLower() == "or")
|
||||
{
|
||||
switch (marketSetting.TriggerConnection.ToLower())
|
||||
{
|
||||
case "and":
|
||||
|
@ -1949,6 +2040,37 @@ namespace Core.Main
|
|||
settingTriggered = triggerResults.FindAll(tr => tr == true).Count > 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Parse the TriggerConnection string into a logical expression
|
||||
string triggerConnection = marketSetting.TriggerConnection;
|
||||
foreach (var triggerResult in triggerTagsResults)
|
||||
{
|
||||
// Replace the tag in the expression with its corresponding value from triggerTagsResults
|
||||
if (!string.IsNullOrEmpty(triggerResult.Key))
|
||||
{
|
||||
triggerConnection = triggerConnection.Replace(triggerResult.Key, triggerResult.Value.ToString().ToLower());
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Log.DoLogError($"ERROR: A required trigger Tag is missing for global setting {marketSetting.SettingName}. Program halted.");
|
||||
Environment.Exit(1); // Stop the program
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
|
||||
// Evaluate the expression using ParseLambda
|
||||
settingTriggered = (bool)DynamicExpressionParser.ParseLambda(ParsingConfig.Default, new ParameterExpression[0], typeof(bool), triggerConnection).Compile().DynamicInvoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Log.DoLogError($"ERROR: Trigger Connection for global setting {marketSetting.SettingName} is invalid or missing. Program halted.");
|
||||
Environment.Exit(1); // Stop the program
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
bool isFreshTrigger = true;
|
||||
|
|
|
@ -26,9 +26,12 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
var errCountIndex = [];
|
||||
var counterIndex = []; // Add this line
|
||||
var intervalDashboardTop;
|
||||
var intervalDashboardBottom;
|
||||
|
||||
|
||||
|
||||
var loadDashboardTop = function () {
|
||||
$("#baglist-refresh-icon").html('<i class="fa fa-circle-o-notch fa-spin fa-fw" data-toggle="tooltip" data-placement="top" title="Loading fresh data..."></i>');
|
||||
$("#buylist-refresh-icon").html('<i class="fa fa-circle-o-notch fa-spin fa-fw" data-toggle="tooltip" data-placement="top" title="Loading fresh data..."></i>');
|
||||
|
@ -63,22 +66,26 @@
|
|||
};
|
||||
|
||||
var loadDashboardBottom = function () {
|
||||
//destroy all d3 svg graph to avoid memory leak
|
||||
setTimeout(function()
|
||||
{
|
||||
// Clear existing interval to stop multiple attempts to load at the same time.
|
||||
if (intervalDashboardBottom != null) {
|
||||
clearInterval(intervalDashboardBottom);
|
||||
}
|
||||
console.log(counterIndex["DashboardBottom"]);
|
||||
|
||||
// Destroy all d3 svg graph to avoid memory leak every 10 refreshes of Dashboard Bottom
|
||||
if (counterIndex["DashboardBottom"] >= 10) {
|
||||
$(".nvtooltip").remove();
|
||||
$("svg > *").remove();
|
||||
$("svg").remove();
|
||||
$("svg").off(); // Remove all event listeners from SVG elements
|
||||
nv.charts = {};
|
||||
nv.graphs = [];
|
||||
nv.logs = {};
|
||||
nv.tooltip = {};
|
||||
}, 10 * intervalDashboardBottom * 1000); // 10 times intervalDashboardBottom in milliseconds
|
||||
|
||||
// Clear exisitng interval to stop multiple attempts to load at the same time.
|
||||
if (intervalDashboardBottom != null)
|
||||
{
|
||||
clearInterval(intervalDashboardBottom);
|
||||
window.cleanupData();
|
||||
// Reset the counter
|
||||
counterIndex["DashboardBottom"] = 0;
|
||||
console.log("d3 svg graph destroyed");
|
||||
}
|
||||
|
||||
// Load dashboard
|
||||
|
@ -92,10 +99,15 @@
|
|||
window.location.replace("@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)Login");
|
||||
} else {
|
||||
errCountIndex["DashboardBottom"] = 0;
|
||||
|
||||
// Increment the counter
|
||||
counterIndex["DashboardBottom"] = (counterIndex["DashboardBottom"] || 0) + 1;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Reinstate the interval.
|
||||
intervalDashboardBottom = setInterval(function () { loadDashboardBottom(); }, @Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000);
|
||||
intervalDashboardBottom = setInterval(function () { loadDashboardBottom(); }, @Model.PTMagicConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds * 1000);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,11 @@
|
|||
}
|
||||
<td><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)SettingsAnalyzer#SingleMarketSetting_@sms">@sms</a>: @smsCount    </td>
|
||||
}
|
||||
@if (Model.PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected) {
|
||||
<a class="btn btn-danger btn-sm btn-custom btn-block text-uppercase btn-deleteall" style="margin-top: 10px;" data-filename="SingleMarketSettingSummary.json" href="#">Reset ALL Single MarketSettings</a>
|
||||
} else {
|
||||
<a class="btn btn-danger btn-custom btn-block text-uppercase" data-toggle="tooltip" data-placement="top" title="This is only accessible when you protect your monitor with a password!"><i class="fa fa-lock text-danger"></i> Delete File</a>
|
||||
}
|
||||
}
|
||||
</tr>
|
||||
</p>
|
||||
|
@ -69,7 +74,7 @@
|
|||
<tr>
|
||||
<th>
|
||||
@if (!lastMarket.Equals(smsSummary.Market)) {
|
||||
<a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform, Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, smsSummary.Market, Model.Summary.MainMarket)" target="_blank">@smsSummary.Market</a>
|
||||
<a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform, Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, smsSummary.Market, Model.Summary.MainMarket, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" target="_blank">@smsSummary.Market</a>
|
||||
}
|
||||
</th>
|
||||
<td>
|
||||
|
@ -197,7 +202,7 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Do you really want to reset this Single Market Setting during the next run of PT Magic?
|
||||
Do you really want to reset this Single Market Settings for the next Analyzer run?
|
||||
<p class="m-t-10"><span class="text-warning">Please note:</span> Even if you reset a setting, it may get triggered again on the next run depending on current market conditions.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
@ -208,7 +213,89 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="modalDeleteFile" tabindex="-1" role="dialog" aria-labelledby="modalDeleteFile" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="modalDeleteFile">Are you sure?</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Do you really want to reset ALL Single Market Settings for the next Analyzer run?
|
||||
<p class="m-t-10"><span class="text-warning">Please note:</span> Even if you reset your settings, they may get triggered again on the next run depending on current market conditions.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-ptmagic text-uppercase waves-effect waves-light btn-confirmdelete" data-datatarget="" data-setting="">Yes, do it!</button>
|
||||
<button type="button" class="btn btn-secondary text-uppercase" data-dismiss="modal">No...</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script type="text/javascript">
|
||||
$(document).on('click', '.btn-deleteall', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var filename = $(this).data("filename");
|
||||
|
||||
// Store the filename in the modal's data
|
||||
$('#modalDeleteFile').data('filename', filename);
|
||||
|
||||
// Show the modal
|
||||
$('#modalDeleteFile').modal('show');
|
||||
});
|
||||
|
||||
$(document).on('click', '.btn-confirmdelete', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Retrieve the filename from the modal's data
|
||||
var filename = $('#modalDeleteFile').data('filename');
|
||||
|
||||
var baseUrl = "@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)";
|
||||
var endpoint = "ManageSMS?handler=DeleteFile";
|
||||
var url = baseUrl + endpoint;
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'POST',
|
||||
beforeSend: function(xhr) {
|
||||
xhr.setRequestHeader("XSRF-TOKEN",
|
||||
$('input:hidden[name="__RequestVerificationToken"]').val());
|
||||
},
|
||||
data: { filename: filename },
|
||||
success: function(result) {
|
||||
if (result.success) {
|
||||
// If the server returned success, display the success message
|
||||
$.Notification.notify('success', 'top left', 'Success', result.message);
|
||||
// Wait for 5 seconds, then refresh the page
|
||||
setTimeout(function() {
|
||||
location.reload();
|
||||
}, 5000);
|
||||
} else {
|
||||
// If the server returned failure, display the error message
|
||||
$.Notification.notify('error', 'top left', 'Error', result.message);
|
||||
}
|
||||
|
||||
// Close the modal
|
||||
$('#modalDeleteFile').modal('hide');
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
var response = JSON.parse(xhr.responseText);
|
||||
// Show an alert for error
|
||||
alert(response.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#modalDeleteFile').on('hidden.bs.modal', function (e) {
|
||||
// Reset the modal's content
|
||||
$('#modalDeleteFile .modal-title').text('Are you sure?');
|
||||
$('#modalDeleteFile .modal-body').html('Do you really want to reset ALL Single Market Settings for the next Analyzer run? <p class="m-t-10"><span class="text-warning">Please note:</span> Even if you reset your settings, they may get triggered again on the next run depending on current market conditions.</p>');
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
|
|
|
@ -3,7 +3,8 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Core.Main;
|
||||
using Core.Main.DataObjects.PTMagicData;
|
||||
using Newtonsoft.Json;
|
||||
|
@ -13,7 +14,12 @@ namespace Monitor.Pages
|
|||
public class ManageSMSModel : _Internal.BasePageModelSecure
|
||||
{
|
||||
public List<SingleMarketSettingSummary> SingleMarketSettingSummaries = new List<SingleMarketSettingSummary>();
|
||||
private readonly IWebHostEnvironment _hostingEnvironment;
|
||||
|
||||
public ManageSMSModel(IWebHostEnvironment hostingEnvironment)
|
||||
{
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
}
|
||||
public void OnGet()
|
||||
{
|
||||
base.Init();
|
||||
|
@ -35,6 +41,38 @@ namespace Monitor.Pages
|
|||
}
|
||||
}
|
||||
|
||||
public IActionResult OnPostDeleteFile(string filename)
|
||||
{
|
||||
string webRootParent = Directory.GetParent(_hostingEnvironment.WebRootPath).FullName;
|
||||
string ptMagicRoot = Directory.GetParent(webRootParent).FullName;
|
||||
string analyzerStatePath = Path.Combine(ptMagicRoot, "_data", "AnalyzerState");
|
||||
|
||||
// Read the AnalyzerState file
|
||||
if (System.IO.File.Exists(analyzerStatePath))
|
||||
{
|
||||
string state = System.IO.File.ReadAllText(analyzerStatePath);
|
||||
if (state != "0")
|
||||
{
|
||||
return new JsonResult(new { success = false, message = "Tha Analyzer is in the middle of a run. Try again in a moment." });
|
||||
}
|
||||
}
|
||||
|
||||
// If state is "0", proceed to delete the file
|
||||
try
|
||||
{
|
||||
string path = Path.Combine(ptMagicRoot, "_data", filename);
|
||||
if (System.IO.File.Exists(path))
|
||||
{
|
||||
System.IO.File.Delete(path);
|
||||
}
|
||||
return new JsonResult(new { success = true, message = "All SMS settings reset!" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new JsonResult(new { success = false, message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
private void BindData()
|
||||
{
|
||||
if (System.IO.File.Exists(PTMagicBasePath + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "SingleMarketSettingSummary.json"))
|
||||
|
|
|
@ -162,7 +162,7 @@ else
|
|||
<th>Name</th>
|
||||
<th class="text-right">Markets</th>
|
||||
<th class="text-right">Timeframe</th>
|
||||
<th class="text-right">Threshold <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Pairs exceeding this threshold are excluded from the trend average."></i></th>
|
||||
<th class="text-right">Threshold <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Pairs exceeding this threshold are considered outliers that are excluded from the trend average."></i></th>
|
||||
<th class="text-right">Change</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -271,9 +271,9 @@ else
|
|||
}
|
||||
</th>
|
||||
@if (mps.ActiveSingleSettings == null || mps.ActiveSingleSettings.Count == 0) {
|
||||
<th><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, market, Model.Summary.MainMarket)" target="_blank">@market</a></th>
|
||||
<th><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, market, Model.Summary.MainMarket, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" target="_blank">@market</a></th>
|
||||
} else {
|
||||
<th><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, market, Model.Summary.MainMarket)" target="_blank">@market</a> <i class="fa fa-exclamation-triangle text-highlight" data-toggle="tooltip" data-placement="top" data-html="true" title="@await Component.InvokeAsync("PairIcon", mps)" data-template="<div class='tooltip' role='tooltip'><div class='tooltip-arrow'></div><div class='tooltip-inner pair-tooltip'></div></div>"></i></th>
|
||||
<th><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, market, Model.Summary.MainMarket, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" target="_blank">@market</a> <i class="fa fa-exclamation-triangle text-highlight" data-toggle="tooltip" data-placement="top" data-html="true" title="@await Component.InvokeAsync("PairIcon", mps)" data-template="<div class='tooltip' role='tooltip'><div class='tooltip-arrow'></div><div class='tooltip-inner pair-tooltip'></div></div>"></i></th>
|
||||
}
|
||||
<td class="text-right">@mps.LatestPrice.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US")) @Model.Summary.MainMarket</td>
|
||||
<td class="text-right">@Math.Round(mps.Latest24hVolume, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US")) @Model.Summary.MainMarket</td>
|
||||
|
@ -336,9 +336,9 @@ else
|
|||
}
|
||||
</th>
|
||||
@if (mps.ActiveSingleSettings == null || mps.ActiveSingleSettings.Count == 0) {
|
||||
<th><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, market, Model.Summary.MainMarket)" target="_blank">@market</a></th>
|
||||
<th><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, market, Model.Summary.MainMarket, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" target="_blank">@market</a></th>
|
||||
} else {
|
||||
<th><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, market, Model.Summary.MainMarket)" target="_blank">@market</a> <i class="fa fa-exclamation-triangle text-highlight" data-toggle="tooltip" data-placement="top" data-html="true" title="@await Component.InvokeAsync("PairIcon", mps)" data-template="<div class='tooltip' role='tooltip'><div class='tooltip-arrow'></div><div class='tooltip-inner pair-tooltip'></div></div>"></i></th>
|
||||
<th><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, market, Model.Summary.MainMarket, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" target="_blank">@market</a> <i class="fa fa-exclamation-triangle text-highlight" data-toggle="tooltip" data-placement="top" data-html="true" title="@await Component.InvokeAsync("PairIcon", mps)" data-template="<div class='tooltip' role='tooltip'><div class='tooltip-arrow'></div><div class='tooltip-inner pair-tooltip'></div></div>"></i></th>
|
||||
}
|
||||
<td class="text-right">@mps.LatestPrice.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
||||
<td class="text-right">@Math.Round(mps.Latest24hVolume, 0).ToString("#,#0", new System.Globalization.CultureInfo("en-US"))</td>
|
||||
|
@ -390,15 +390,55 @@ else
|
|||
@if (!Model.TrendChartDataJSON.Equals("")) {
|
||||
<text>
|
||||
nv.addGraph(function () {
|
||||
var lineChart = nv.models.lineChart();
|
||||
lineChart = nv.models.lineChart();
|
||||
var height = 300;
|
||||
var chartData = @Html.Raw(Model.TrendChartDataJSON);
|
||||
lineChart.useInteractiveGuideline(true);
|
||||
lineChart.xAxis.tickFormat(function (d) { return d3.time.format('%H:%M')(new Date(d)); });
|
||||
lineChart.yAxis.axisLabel('Trend %').tickFormat(d3.format(',.2f'));
|
||||
|
||||
d3.select('.trend-chart svg').attr('perserveAspectRatio', 'xMinYMid').datum(chartData).transition().duration(500).call(lineChart);
|
||||
//nv.utils.windowResize(lineChart.update); v1.3.0 => Removed this line to prevent memory leak
|
||||
|
||||
lineChart.dispatch.on('renderEnd', function() {
|
||||
// Get the chart's container
|
||||
var container = d3.select('.trend-chart .nv-wrap.nv-lineChart .nv-linesWrap');
|
||||
|
||||
// Remove any existing y=0 line
|
||||
container.selectAll('.zero-line').remove();
|
||||
|
||||
// Get the x-values of the first and last data points
|
||||
var xRange = lineChart.xAxis.scale().range();
|
||||
var xMin = xRange[0];
|
||||
var xMax = xRange[1];
|
||||
|
||||
|
||||
// Add a line at y=0
|
||||
container.insert('line', ':first-child')
|
||||
.attr('class', 'zero-line') // Add a class to the line for easy
|
||||
.attr('x1', xMin) // x position of the first end of the line
|
||||
.attr('y1', lineChart.yAxis.scale()(0)) // y position of the first end of the line
|
||||
.attr('x2', xMax) // x position of the second end of the line
|
||||
.attr('y2', lineChart.yAxis.scale()(0)) // y position of the second end of the line
|
||||
.attr('stroke', 'gray') // color of the line
|
||||
.attr('stroke-width', 2); // width of the line
|
||||
});
|
||||
|
||||
// Add mouseleave, and mousemove event listeners to hide tooltip
|
||||
d3.select('.trend-chart').on('mouseleave', function() {
|
||||
d3.select('.nvtooltip').remove();
|
||||
});
|
||||
|
||||
d3.select('.trend-chart').on('mousemove', function() {
|
||||
var chartBounds = d3.select('.trend-chart')[0][0].getBoundingClientRect();
|
||||
var mouseX = d3.event.clientX;
|
||||
var mouseY = d3.event.clientY;
|
||||
|
||||
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
|
||||
d3.select('.nvtooltip').remove();
|
||||
}
|
||||
});
|
||||
|
||||
return lineChart;
|
||||
});
|
||||
</text>
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using Core.Main;
|
||||
using Core.Helper;
|
||||
using Core.Main.DataObjects;
|
||||
using Core.Main.DataObjects.PTMagicData;
|
||||
using System.Globalization;
|
||||
|
||||
|
@ -11,6 +12,8 @@ namespace Monitor.Pages
|
|||
public class MarketAnalyzerModel : _Internal.BasePageModelSecure
|
||||
{
|
||||
public List<MarketTrend> MarketTrends { get; set; } = new List<MarketTrend>();
|
||||
public ProfitTrailerData PTData = null;
|
||||
public MiscData MiscData { get; set; }
|
||||
public string TrendChartDataJSON = "";
|
||||
public double DataHours { get; set; }
|
||||
|
||||
|
@ -22,7 +25,8 @@ namespace Monitor.Pages
|
|||
|
||||
private void BindData()
|
||||
{
|
||||
// Get market trends
|
||||
PTData = this.PtDataObject;
|
||||
MiscData = this.PTData.Misc;
|
||||
MarketTrends = PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.OrderBy(mt => mt.TrendMinutes).ThenByDescending(mt => mt.Platform).ToList();
|
||||
|
||||
BuildMarketTrendChartData();
|
||||
|
@ -56,8 +60,8 @@ namespace Monitor.Pages
|
|||
|
||||
// Get trend ticks for chart
|
||||
TimeSpan offset;
|
||||
bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-");
|
||||
string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-');
|
||||
bool isNegative = MiscData.TimeZoneOffset.StartsWith("-");
|
||||
string offsetWithoutSign = MiscData.TimeZoneOffset.TrimStart('+', '-');
|
||||
|
||||
if (!TimeSpan.TryParse(offsetWithoutSign, out offset))
|
||||
{
|
||||
|
|
|
@ -329,7 +329,7 @@
|
|||
double profitFiat = Math.Round(profit * Model.MiscData.FiatConversionRate, 0);
|
||||
<tr>
|
||||
<td>@rank</td>
|
||||
<td><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform, Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, coin, Model.PTData.Misc.Market)" target="_blank">@coin</a></td>
|
||||
<td><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform, Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, coin, Model.PTData.Misc.Market, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" target="_blank">@coin</a></td>
|
||||
<td class="text-right text-autocolor-saw">@profit</td>
|
||||
<td class="text-right">@sales</td>
|
||||
<td class="text-right text-autocolor-saw">@avg </td>
|
||||
|
@ -394,20 +394,7 @@
|
|||
.transition().duration(0)
|
||||
.call(salesChart);
|
||||
|
||||
// Add mouseleave, and mousemove event listeners to hide tooltip
|
||||
d3.select('.sales-chart').on('mouseleave', function() {
|
||||
d3.select('.nvtooltip').style('opacity', 0);
|
||||
});
|
||||
|
||||
d3.select('body').on('mousemove', function() {
|
||||
var chartBounds = d3.select('.sales-chart')[0][0].getBoundingClientRect();
|
||||
var mouseX = d3.event.clientX;
|
||||
var mouseY = d3.event.clientY;
|
||||
|
||||
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
|
||||
d3.select('.nvtooltip').style('opacity', 0);
|
||||
}
|
||||
});
|
||||
nv.utils.windowResize(salesChart.update);
|
||||
return salesChart;
|
||||
});
|
||||
|
@ -442,20 +429,7 @@
|
|||
.transition().duration(0)
|
||||
.call(cumulativeProfitChart);
|
||||
|
||||
// Add mouseleave, and mousemove event listeners to hide tooltip
|
||||
d3.select('.cumulative-profit-chart').on('mouseleave', function() {
|
||||
d3.select('.nvtooltip').style('opacity', 0);
|
||||
});
|
||||
|
||||
d3.select('body').on('mousemove', function() {
|
||||
var chartBounds = d3.select('.cumulative-profit-chart')[0][0].getBoundingClientRect();
|
||||
var mouseX = d3.event.clientX;
|
||||
var mouseY = d3.event.clientY;
|
||||
|
||||
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
|
||||
d3.select('.nvtooltip').style('opacity', 0);
|
||||
}
|
||||
});
|
||||
nv.utils.windowResize(cumulativeProfitChart.update);
|
||||
return cumulativeProfitChart;
|
||||
});
|
||||
|
@ -490,20 +464,6 @@
|
|||
.transition().duration(0)
|
||||
.call(TCVChart);
|
||||
|
||||
// Add mouseleave, and mousemove event listeners to hide tooltip
|
||||
d3.select('.TCV-chart').on('mouseleave', function() {
|
||||
d3.select('.nvtooltip').style('opacity', 0);
|
||||
});
|
||||
|
||||
d3.select('body').on('mousemove', function() {
|
||||
var chartBounds = d3.select('.TCV-chart')[0][0].getBoundingClientRect();
|
||||
var mouseX = d3.event.clientX;
|
||||
var mouseY = d3.event.clientY;
|
||||
|
||||
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
|
||||
d3.select('.nvtooltip').style('opacity', 0);
|
||||
}
|
||||
});
|
||||
nv.utils.windowResize(TCVChart.update);
|
||||
return TCVChart;
|
||||
});
|
||||
|
@ -538,20 +498,33 @@
|
|||
.transition().duration(0)
|
||||
.call(profitChart);
|
||||
|
||||
// Add mouseleave, and mousemove event listeners to hide tooltip
|
||||
d3.select('.profit-chart').on('mouseleave', function() {
|
||||
d3.select('.nvtooltip').style('opacity', 0);
|
||||
});
|
||||
|
||||
d3.select('body').on('mousemove', function() {
|
||||
var chartBounds = d3.select('.profit-chart')[0][0].getBoundingClientRect();
|
||||
var mouseX = d3.event.clientX;
|
||||
var mouseY = d3.event.clientY;
|
||||
profitChart.dispatch.on('renderEnd', function() {
|
||||
// Get the chart's container
|
||||
var container = d3.select('.profit-chart .nv-wrap.nv-lineChart .nv-linesWrap');
|
||||
|
||||
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
|
||||
d3.select('.nvtooltip').style('opacity', 0);
|
||||
// Remove any existing y=0 line
|
||||
container.selectAll('.zero-line').remove();
|
||||
|
||||
// Check if profitData[0].values is not empty
|
||||
if (profitData[0].values.length > 0) {
|
||||
// Get the x-values of the first and last data points
|
||||
var xMin = profitChart.xAxis.scale()(profitData[0].values[0].x);
|
||||
var xMax = profitChart.xAxis.scale()(profitData[0].values[profitData[0].values.length - 1].x);
|
||||
|
||||
// Add a line at y=0
|
||||
container.insert('line', ':first-child')
|
||||
.attr('class', 'zero-line') // Add a class to the line for easy selection
|
||||
.attr('x1', xMin) // x position of the first end of the line
|
||||
.attr('y1', profitChart.yAxis.scale()(0)) // y position of the first end of the line
|
||||
.attr('x2', xMax) // x position of the second end of the line
|
||||
.attr('y2', profitChart.yAxis.scale()(0)) // y position of the second end of the line
|
||||
.attr('stroke', 'gray') // color of the line
|
||||
.attr('stroke-width', 2); // width of the line
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
nv.utils.windowResize(profitChart.update);
|
||||
return profitChart;
|
||||
});
|
||||
|
@ -559,4 +532,34 @@
|
|||
}
|
||||
})(jQuery);
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
function attachTooltipRemoval(chartClass) {
|
||||
d3.selectAll(chartClass).on('mouseleave', function() {
|
||||
var tooltip = d3.select(this).select('.nvtooltip');
|
||||
if (!tooltip.empty()) {
|
||||
tooltip.style('opacity', 0);
|
||||
}
|
||||
});
|
||||
|
||||
d3.selectAll(chartClass).on('mouseenter', function() {
|
||||
var tooltip = d3.select(this).select('.nvtooltip');
|
||||
if (!tooltip.empty()) {
|
||||
tooltip.style('opacity', 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
$(document).ready(function () {
|
||||
// Hide all tooltips when the page is loaded
|
||||
d3.selectAll('.nvtooltip').style('opacity', 0);
|
||||
|
||||
// Call the function for each chart
|
||||
attachTooltipRemoval('.asset-distribution svg');
|
||||
attachTooltipRemoval('.trend-chart svg');
|
||||
attachTooltipRemoval('.profit-chart svg');
|
||||
attachTooltipRemoval('.TCVLive-chart svg');
|
||||
// Add other charts as needed
|
||||
});
|
||||
</script>
|
||||
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace Monitor.Pages
|
|||
DailyStats = this.PTData.DailyStats;
|
||||
|
||||
// Convert local offset time to UTC
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(MiscData.TimeZoneOffset.Replace("+", ""));
|
||||
DateTimeNow = DateTimeOffset.UtcNow.ToOffset(offsetTimeSpan);
|
||||
|
||||
BuildSalesChartData();
|
||||
|
@ -71,8 +71,8 @@ namespace Monitor.Pages
|
|||
{
|
||||
// Get timezone offset
|
||||
TimeSpan offset;
|
||||
bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-");
|
||||
string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-');
|
||||
bool isNegative = MiscData.TimeZoneOffset.StartsWith("-");
|
||||
string offsetWithoutSign = MiscData.TimeZoneOffset.TrimStart('+', '-');
|
||||
|
||||
if (!TimeSpan.TryParse(offsetWithoutSign, out offset))
|
||||
{
|
||||
|
@ -129,8 +129,8 @@ namespace Monitor.Pages
|
|||
{
|
||||
// Get timezone offset
|
||||
TimeSpan offset;
|
||||
bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-");
|
||||
string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-');
|
||||
bool isNegative = MiscData.TimeZoneOffset.StartsWith("-");
|
||||
string offsetWithoutSign = MiscData.TimeZoneOffset.TrimStart('+', '-');
|
||||
|
||||
if (!TimeSpan.TryParse(offsetWithoutSign, out offset))
|
||||
{
|
||||
|
@ -190,8 +190,8 @@ namespace Monitor.Pages
|
|||
{
|
||||
// Get timezone offset
|
||||
TimeSpan offset;
|
||||
bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-");
|
||||
string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-');
|
||||
bool isNegative = MiscData.TimeZoneOffset.StartsWith("-");
|
||||
string offsetWithoutSign = MiscData.TimeZoneOffset.TrimStart('+', '-');
|
||||
|
||||
if (!TimeSpan.TryParse(offsetWithoutSign, out offset))
|
||||
{
|
||||
|
@ -301,8 +301,8 @@ namespace Monitor.Pages
|
|||
{
|
||||
// Get timezone offset
|
||||
TimeSpan offset;
|
||||
bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-");
|
||||
string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-');
|
||||
bool isNegative = MiscData.TimeZoneOffset.StartsWith("-");
|
||||
string offsetWithoutSign = MiscData.TimeZoneOffset.TrimStart('+', '-');
|
||||
|
||||
if (!TimeSpan.TryParse(offsetWithoutSign, out offset))
|
||||
{
|
||||
|
|
|
@ -6,7 +6,6 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Core.Main;
|
||||
using Core.Helper;
|
||||
using Core.Main.DataObjects.PTMagicData;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Monitor.Pages
|
||||
{
|
||||
|
|
|
@ -117,12 +117,12 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
@* <div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Timezone Offset <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The time difference between your current location and UTC time. Examples: +7:00, -3:00"></i></label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" name="Application_TimezoneOffset" value="@Model.PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset">
|
||||
</div>
|
||||
</div>
|
||||
</div> *@
|
||||
|
||||
@* <div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Main Fiat Currency <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This is the local currency you want PTM to use when showing your sales and account value in fiat."></i></label>
|
||||
|
@ -196,42 +196,48 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Market Trend Graph Interval Minutes <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The time interval for the market trend graph (Dashboard and Sales Analyzer) between data points. Very small intervals on large timeframe graphs can significantly impact performance."></i></label>
|
||||
<label class="col-md-4 col-form-label">Live TCV Timeframe (Minutes) <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The total timeframe for the Live TCV chart on the Dashboard. Very large timeframes can significantly impact performance."></i></label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" name="Monitor_LiveTCVTimeframeMinutes" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.LiveTCVTimeframeMinutes.ToString(new System.Globalization.CultureInfo("en-US"))">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Trend Chart Interval (Minutes) <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The time interval for the market trend graph (Dashboard and Sales Analyzer) between data points. Very small intervals on large timeframe graphs can significantly impact performance."></i></label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" name="Monitor_GraphIntervalMinutes" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes.ToString(new System.Globalization.CultureInfo("en-US"))">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Market Trend Graph Max Timeframe <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This defines the total timeframe for the market trends graph (Dashboard and Sales Analyzer) in hours. Large timeframe graphs can significantly impact performance."></i></label>
|
||||
<label class="col-md-4 col-form-label">Trend Chart Timeframe (Hours) <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This defines the total timeframe for the market trends graph (Dashboard and Sales Analyzer) in hours. Large timeframe graphs can significantly impact performance."></i></label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" name="Monitor_GraphMaxTimeframeHours" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours.ToString(new System.Globalization.CultureInfo("en-US"))">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Daily Profit Graph Max Timeframe <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This defines the total timeframe for the daily profits graph on the dashboard in days. Large timeframes can significantly impact performance."></i></label>
|
||||
<label class="col-md-4 col-form-label">Daily Profit Chart Timeframe (Days) <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This defines the total timeframe for the daily profits graph on the dashboard in days. Large timeframes can significantly impact performance."></i></label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" name="Monitor_ProfitsMaxTimeframeDays" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays.ToString(new System.Globalization.CultureInfo("en-US"))">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Dashboard Bottom Refresh <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval in seconds, of the charts and graphs on then main page."></i></label>
|
||||
<label class="col-md-4 col-form-label">Dashboard Charts Refresh (Seconds) <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval in seconds, of the charts and graphs on then main page."></i></label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" name="Monitor_RefreshSeconds" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds.ToString(new System.Globalization.CultureInfo("en-US"))">
|
||||
<input type="text" class="form-control" name="Monitor_RefreshSeconds" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds.ToString(new System.Globalization.CultureInfo("en-US"))">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Bag AnalyzerRefresh Seconds <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval of your monitor bag analyzer page."></i></label>
|
||||
<label class="col-md-4 col-form-label">Bag Analyzer Refresh (Seconds) <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval of your monitor bag analyzer page."></i></label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" name="Monitor_BagAnalyzerRefreshSeconds" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.BagAnalyzerRefreshSeconds.ToString(new System.Globalization.CultureInfo("en-US"))">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Buy AnalyzerRefresh Seconds <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval of your monitor buy analyzer page."></i></label>
|
||||
<label class="col-md-4 col-form-label">Buy Analyzer Refresh (Seconds) <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The refresh interval of your monitor buy analyzer page."></i></label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" name="Monitor_BuyAnalyzerRefreshSeconds" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds.ToString(new System.Globalization.CultureInfo("en-US"))">
|
||||
</div>
|
||||
|
@ -247,12 +253,12 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
@* <div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Max Sales Records<i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The number of sales records PTMagic pulls from Profit Trailer. Changes require a Monitor Restart."></i></label>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">TV Custom Chart <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="If TradingView is selected above, you can use the custom layout code found in your TV URL - ex. AGrfHNiI."></i></label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" name="Monitor_MaxSalesRecords" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxSalesRecords.ToString(new System.Globalization.CultureInfo("en-US"))">
|
||||
<input type="text" class="form-control" name="Monitor_TVCustomLayout" value="@Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout">
|
||||
</div>
|
||||
</div>
|
||||
</div> *@
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Max Top Markets <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of top markets being show in your Sales Analyzer."></i></label>
|
||||
|
|
|
@ -27,30 +27,6 @@ namespace Monitor.Pages
|
|||
return result;
|
||||
}
|
||||
|
||||
public string GetTimezoneSelection()
|
||||
{
|
||||
string result = "";
|
||||
|
||||
List<string> tzOffsetList = new List<string>();
|
||||
foreach (TimeZoneInfo tzi in TimeZoneInfo.GetSystemTimeZones())
|
||||
{
|
||||
string offsetString = this.GetTimezoneOffsetString(tzi);
|
||||
if (!tzOffsetList.Contains(offsetString))
|
||||
{
|
||||
string selected = "";
|
||||
if (PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Equals(offsetString, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
selected = " selected=\"selected\"";
|
||||
}
|
||||
|
||||
result += "<option" + selected + ">" + offsetString + "</option>\n";
|
||||
tzOffsetList.Add(offsetString);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
base.Init();
|
||||
|
@ -72,30 +48,25 @@ namespace Monitor.Pages
|
|||
|
||||
// Read the new settings
|
||||
PTMagicConfiguration.GeneralSettings.Application.IsEnabled = HttpContext.Request.Form["Application_IsEnabled"].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.ProfitTrailerDefaultSettingName = HttpContext.Request.Form["Application_ProfitTrailerDefaultSettingName"];
|
||||
PTMagicConfiguration.GeneralSettings.Application.Exchange = HttpContext.Request.Form["Application_Exchange"];
|
||||
PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL = HttpContext.Request.Form["Application_ProfitTrailerMonitorURL"];
|
||||
PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken = HttpContext.Request.Form["Application_ProfitTrailerServerAPIToken"];
|
||||
PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset = HttpContext.Request.Form["Application_TimezoneOffset"];
|
||||
//PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency = HttpContext.Request.Form["Application_MainFiatCurrency"];
|
||||
PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes = SystemHelper.TextToInteger(HttpContext.Request.Form["Application_FloodProtectionMinutes"], PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes);
|
||||
PTMagicConfiguration.GeneralSettings.Application.InstanceName = HttpContext.Request.Form["Application_InstanceName"];
|
||||
PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey = HttpContext.Request.Form["Application_CoinMarketCapAPIKey"];
|
||||
//PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey = HttpContext.Request.Form["Application_FreeCurrencyConverterAPIKey"];
|
||||
PTMagicConfiguration.GeneralSettings.Monitor.IsPasswordProtected = HttpContext.Request.Form["Monitor_IsPasswordProtected"].Equals("on");
|
||||
PTMagicConfiguration.GeneralSettings.Monitor.OpenBrowserOnStart = HttpContext.Request.Form["Monitor_OpenBrowserOnStart"].Equals("on");
|
||||
PTMagicConfiguration.GeneralSettings.Monitor.AnalyzerChart = HttpContext.Request.Form["Monitor_AnalyzerChart"];
|
||||
PTMagicConfiguration.GeneralSettings.Monitor.Port = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_Port"], PTMagicConfiguration.GeneralSettings.Monitor.Port);
|
||||
PTMagicConfiguration.GeneralSettings.Monitor.LiveTCVTimeframeMinutes = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_LiveTCVTimeframeMinutes"], PTMagicConfiguration.GeneralSettings.Monitor.LiveTCVTimeframeMinutes);
|
||||
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.ProfitsMaxTimeframeDays = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_ProfitsMaxTimeframeDays"], PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays);
|
||||
PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_RefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds);
|
||||
PTMagicConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_RefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds);
|
||||
PTMagicConfiguration.GeneralSettings.Monitor.BagAnalyzerRefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_BagAnalyzerRefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.BagAnalyzerRefreshSeconds);
|
||||
PTMagicConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_BuyAnalyzerRefreshSeconds"], PTMagicConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds);
|
||||
PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform = HttpContext.Request.Form["Monitor_LinkPlatform"];
|
||||
//PTMagicConfiguration.GeneralSettings.Monitor.MaxSalesRecords = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxSalesRecords"], PTMagicConfiguration.GeneralSettings.Monitor.MaxSalesRecords);
|
||||
PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout = HttpContext.Request.Form["Monitor_TVCustomLayout"];
|
||||
PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxTopMarkets"], PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets);
|
||||
PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxDailySummaries"], PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries);
|
||||
PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries = SystemHelper.TextToInteger(HttpContext.Request.Form["Monitor_MaxMonthlySummaries"], PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries);
|
||||
|
|
|
@ -116,13 +116,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="card-box">
|
||||
<h4 class="m-t-0 header-title">Global Settings Log</h4>
|
||||
<table class="table table-striped table-sm">
|
||||
|
@ -136,7 +131,7 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
@foreach (Core.Main.DataObjects.PTMagicData.GlobalSettingSummary gss in Model.Summary.GlobalSettingSummary.OrderByDescending(g => g.SwitchDateTime).Take(Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxSettingsLogEntries)) {
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(Model.PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(Model.MiscData.TimeZoneOffset.Replace("+", ""));
|
||||
DateTimeOffset settingActivationTime = gss.SwitchDateTime;
|
||||
settingActivationTime = settingActivationTime.ToOffset(offsetTimeSpan);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Core.Main;
|
||||
using Core.Helper;
|
||||
using Core.Main.DataObjects;
|
||||
using Core.Main.DataObjects.PTMagicData;
|
||||
|
||||
namespace Monitor.Pages
|
||||
|
@ -14,6 +15,8 @@ namespace Monitor.Pages
|
|||
public string SettingsDistribution24hChartDataJSON = "";
|
||||
public string SettingsDistribution3dChartDataJSON = "";
|
||||
private Dictionary<string, string> settingsChartColors = new Dictionary<string, string>();
|
||||
public ProfitTrailerData PTData = null;
|
||||
public MiscData MiscData { get; set; }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
|
@ -24,6 +27,8 @@ namespace Monitor.Pages
|
|||
|
||||
private void BindData()
|
||||
{
|
||||
PTData = this.PtDataObject;
|
||||
MiscData = this.PTData.Misc;
|
||||
BuildMarketsWithSingleSettings();
|
||||
BuildChartColors();
|
||||
Build24hChartData();
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
@page
|
||||
@model TransactionsModel
|
||||
@{
|
||||
ViewData["Title"] = "";
|
||||
}
|
||||
|
||||
@section Styles {
|
||||
<link href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/tablesaw/css/tablesaw.css" rel="stylesheet" type="text/css" />
|
||||
<link href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/bootstrap-datepicker/css/bootstrap-datepicker.min.css" rel="stylesheet">
|
||||
<link href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/clockpicker/css/bootstrap-clockpicker.min.css" rel="stylesheet">
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card-box">
|
||||
<h4 class="m-t-0 header-title">Transactions</h4>
|
||||
|
||||
<p>
|
||||
In this area you may add manual transactions (like deposits and withdrawals) to your PT Magic data. Adding this kind of information will help PT Magic calculating your percentage gains and your balance more accurately.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="form-horizontal m-t-20" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card-box">
|
||||
<h4 class="m-t-0 header-title">New Transaction</h4>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Amount <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The amount of your transaction. Positive numbers for deposits and negative numbers for withdrawals."></i></label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" name="Transaction_Amount">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Date <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The date of your transaction."></i></label>
|
||||
<div class="col-md-8">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="mm/dd/yyyy" id="datepicker-autoclose" name="Transaction_Date">
|
||||
<span class="input-group-addon bg-custom b-0"><i class="md md-event-note text-dark"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Time <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The time of your transaction."></i></label>
|
||||
<div class="col-md-8">
|
||||
<div class="input-group clockpicker m-b-20" data-placement="top" data-align="top" data-autoclose="true">
|
||||
<input type="text" class="form-control" name="Transaction_Time">
|
||||
<span class="input-group-addon"> <span class="md md-access-time"></span> </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label"></label>
|
||||
<div class="col-md-8">
|
||||
<button class="btn btn-ptmagic btn-block text-uppercase waves-effect waves-light" type="submit">
|
||||
Save Transaction
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!Model.ValidationMessage.Equals("")) {
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card-box text-danger">
|
||||
@Model.ValidationMessage
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card-box">
|
||||
<h4 class="m-t-0 header-title">Your Transactions</h4>
|
||||
|
||||
@if (Model.TransactionData.Transactions.Count > 0) {
|
||||
<table class="tablesaw table m-b-0" data-tablesaw-sortable data-tablesaw-sortable-switch>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th scope="col" data-tablesaw-sortable-col data-tablesaw-sortable-default-col>Time</th>
|
||||
<th scope="col" data-tablesaw-sortable-col class="text-right">Amount</th>
|
||||
<th scope="col" data-tablesaw-sortable-col>Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (Core.Main.DataObjects.PTMagicData.Transaction transaction in Model.TransactionData.Transactions) {
|
||||
<tr>
|
||||
<td style="width:20px;"><a href="#" class="btn-remove" data-transactionguid="@transaction.GUID"><i class="fa fa-remove text-danger"></i></a></td>
|
||||
<td>@transaction.GetLocalDateTime(Model.PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset).ToShortDateString() @transaction.GetLocalDateTime(Model.PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset).ToShortTimeString()</td>
|
||||
<td class="text-right text-autocolor">@transaction.Amount.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
||||
@if (transaction.Amount > 0) {
|
||||
<td class="text-success">Deposit</td>
|
||||
} else {
|
||||
<td class="text-danger">Withdrawal</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
} else {
|
||||
<p>No transactions found.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@section Scripts {
|
||||
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/tablesaw/js/tablesaw.js"></script>
|
||||
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/tablesaw/js/tablesaw-init.js"></script>
|
||||
|
||||
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/bootstrap-datepicker/js/bootstrap-datepicker.min.js"></script>
|
||||
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/clockpicker/js/bootstrap-clockpicker.min.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
$('.text-autocolor').autocolor(false);
|
||||
|
||||
jQuery('#datepicker-autoclose').datepicker({
|
||||
weekStart: 1,
|
||||
autoclose: true,
|
||||
todayHighlight: true
|
||||
});
|
||||
|
||||
$('.clockpicker').clockpicker({
|
||||
donetext: 'Done'
|
||||
});
|
||||
|
||||
@if (!Model.NotifyType.Equals("") && !Model.NotifyHeadline.Equals("") && !Model.NotifyMessage.Equals("")) {
|
||||
<text>
|
||||
$.Notification.notify('@Model.NotifyType', 'top left', '@Model.NotifyHeadline', '@Model.NotifyMessage');
|
||||
</text>
|
||||
}
|
||||
|
||||
$('.btn-remove').click(function () {
|
||||
var tGuid = $(this).data('transactionguid');
|
||||
var postData = { Transaction_GUID: tGuid };
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: "@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)_post/RemoveTransaction",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
data: JSON.stringify(postData),
|
||||
beforeSend: function (xhr) {
|
||||
xhr.setRequestHeader("XSRF-TOKEN",
|
||||
$('input:hidden[name="__RequestVerificationToken"]').val());
|
||||
},
|
||||
success: function (data) {
|
||||
$.Notification.notify('success', 'top left', 'Transaction removed!', 'Transaction "' + tGuid + '" was successfully removed.');
|
||||
window.location = window.location.href;
|
||||
},
|
||||
error: function (jqxhr, errorText, thrownError) {
|
||||
$.Notification.notify('error', 'top left', 'Error removing transaction "' + tGuid + '"!', 'Error message: ' + errorText);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Core.Main;
|
||||
using Core.Helper;
|
||||
using Core.Main.DataObjects;
|
||||
using Core.Main.DataObjects.PTMagicData;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Monitor.Pages
|
||||
{
|
||||
public class TransactionsModel : _Internal.BasePageModelSecure
|
||||
{
|
||||
public TransactionData TransactionData = null;
|
||||
public string ValidationMessage = "";
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
base.Init();
|
||||
|
||||
BindData();
|
||||
}
|
||||
|
||||
private void BindData()
|
||||
{
|
||||
TransactionData = new TransactionData(PTMagicBasePath);
|
||||
}
|
||||
|
||||
public void OnPost()
|
||||
{
|
||||
base.Init();
|
||||
|
||||
BindData();
|
||||
|
||||
SaveTransaction();
|
||||
}
|
||||
|
||||
private void SaveTransaction()
|
||||
{
|
||||
double transactionAmount = 0;
|
||||
DateTimeOffset transactionDateTime = Constants.confMinDate;
|
||||
|
||||
try
|
||||
{
|
||||
transactionAmount = SystemHelper.TextToDouble(HttpContext.Request.Form["Transaction_Amount"], transactionAmount, "en-US");
|
||||
//transactionDateTime = DateTimeOffset.Parse(HttpContext.Request.Form["Transaction_Date"] + " " + HttpContext.Request.Form["Transaction_Time"], CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
|
||||
DateTime tmp = DateTime.Parse(HttpContext.Request.Form["Transaction_Date"] + " " + HttpContext.Request.Form["Transaction_Time"], CultureInfo.InvariantCulture, DateTimeStyles.None);
|
||||
|
||||
// Convert local offset time to UTC
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
transactionDateTime = new DateTimeOffset(tmp, offsetTimeSpan);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (transactionAmount == 0)
|
||||
{
|
||||
ValidationMessage = "Please enter a valid amount in the format 123.45!";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (transactionDateTime == Constants.confMinDate)
|
||||
{
|
||||
ValidationMessage = "Please select a valid date and time!";
|
||||
}
|
||||
else
|
||||
{
|
||||
TransactionData.Transactions.Add(new Transaction() { GUID = Guid.NewGuid().ToString(), Amount = transactionAmount, UTCDateTime = transactionDateTime.UtcDateTime });
|
||||
TransactionData.SaveTransactions(PTMagicBasePath);
|
||||
|
||||
NotifyHeadline = "Transaction saved!";
|
||||
NotifyMessage = "Transaction saved successfully to _data/Transactions.json.";
|
||||
NotifyType = "success";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -192,7 +192,7 @@
|
|||
}
|
||||
|
||||
// Reinstate the interval.
|
||||
interval = setInterval(function () { loadWidgets(); }, 5000);
|
||||
interval = setInterval(function () { loadWidgets(); }, 3000);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@
|
|||
<div class="tradingview-widget-container">
|
||||
<div id="tradingview_6aa22" style="height:600px;"></div>
|
||||
<div class="tradingview-widget-copyright">
|
||||
<a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform, Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, Model.DCAMarket, Model.Summary.MainMarket)" rel="noopener" target="_blank">
|
||||
<a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform, Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, Model.DCAMarket, Model.Summary.MainMarket, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" rel="noopener" target="_blank">
|
||||
<span class="blue-text">@Model.DCAMarket</span> <span class="blue-text">chart</span> by TradingView</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,6 +10,7 @@ using Core.MarketAnalyzer;
|
|||
namespace Monitor.Pages {
|
||||
public class BagDetailsModel : _Internal.BasePageModelSecure {
|
||||
public ProfitTrailerData PTData = null;
|
||||
public MiscData MiscData = null;
|
||||
public string DCAMarket = "";
|
||||
public DCALogData DCALogData = null;
|
||||
public DateTimeOffset DateTimeNow = Constants.confMinDate;
|
||||
|
@ -23,13 +24,12 @@ namespace Monitor.Pages {
|
|||
|
||||
private void BindData() {
|
||||
DCAMarket = GetStringParameter("m", "");
|
||||
|
||||
PTData = this.PtDataObject;
|
||||
|
||||
MiscData = this.PTData.Misc;
|
||||
DCALogData = PTData.DCALog.Find(d => d.Market == DCAMarket);
|
||||
|
||||
// Convert local offset time to UTC
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(MiscData.TimeZoneOffset.Replace("+", ""));
|
||||
DateTimeNow = DateTimeOffset.UtcNow.ToOffset(offsetTimeSpan);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,9 +84,9 @@
|
|||
<tr @(lostValue ? "class=errorRow" : "" ) >
|
||||
// Market
|
||||
@if (mps != null && (mps.ActiveSingleSettings == null || mps.ActiveSingleSettings.Count == 0)) {
|
||||
<th class="align-top"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, dcaLogEntry.Market, Model.Summary.MainMarket)" target="_blank">@dcaLogEntry.Market</a></th>
|
||||
<th class="align-top"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, dcaLogEntry.Market, Model.Summary.MainMarket, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" target="_blank">@dcaLogEntry.Market</a></th>
|
||||
} else {
|
||||
<th class="align-top"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, dcaLogEntry.Market, Model.Summary.MainMarket)" target="_blank">@dcaLogEntry.Market</a>
|
||||
<th class="align-top"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, dcaLogEntry.Market, Model.Summary.MainMarket, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" target="_blank">@dcaLogEntry.Market</a>
|
||||
<i class="fa fa-exclamation-triangle text-highlight" data-toggle="tooltip" data-placement="top" data-html="true" title="@await Component.InvokeAsync("PairIcon", mps)" data-template="<div class='tooltip' role='tooltip'><div class='tooltip-arrow'></div><div class='tooltip-inner pair-tooltip'></div></div>">
|
||||
</i>
|
||||
</th>
|
||||
|
|
|
@ -37,9 +37,9 @@
|
|||
string triggerValueText = Core.ProfitTrailer.StrategyHelper.GetTriggerValueText(Model.Summary, buyLogEntry.BuyStrategies, buyLogEntry.BuyStrategy, buyLogEntry.BBTrigger, buyLogEntry.TriggerValue, 0, true);
|
||||
<tr>
|
||||
@if (mps != null && (mps.ActiveSingleSettings == null || mps.ActiveSingleSettings.Count == 0)) {
|
||||
<th class="align-top"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, buyLogEntry.Market, Model.Summary.MainMarket)" target="_blank">@buyLogEntry.Market</a></th>
|
||||
<th class="align-top"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, buyLogEntry.Market, Model.Summary.MainMarket, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" target="_blank">@buyLogEntry.Market</a></th>
|
||||
} else {
|
||||
<th class="align-top"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, buyLogEntry.Market, Model.Summary.MainMarket)" target="_blank">@buyLogEntry.Market</a> <i class="fa fa-exclamation-triangle text-highlight" data-toggle="tooltip" data-placement="top" data-html="true" title="@await Component.InvokeAsync("PairIcon", mps)" data-template="<div class='tooltip' role='tooltip'><div class='tooltip-arrow'></div><div class='tooltip-inner pair-tooltip'></div></div>"></i></th>
|
||||
<th class="align-top"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, buyLogEntry.Market, Model.Summary.MainMarket, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" target="_blank">@buyLogEntry.Market</a> <i class="fa fa-exclamation-triangle text-highlight" data-toggle="tooltip" data-placement="top" data-html="true" title="@await Component.InvokeAsync("PairIcon", mps)" data-template="<div class='tooltip' role='tooltip'><div class='tooltip-arrow'></div><div class='tooltip-inner pair-tooltip'></div></div>"></i></th>
|
||||
}
|
||||
<td class="text-autocolor">@buyLogEntry.PercChange.ToString("#,#0.00")%</td>
|
||||
@if (buyDisabled) {
|
||||
|
|
|
@ -10,7 +10,48 @@
|
|||
}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-5 px-1">
|
||||
|
||||
<div class="col-md-3 px-1">
|
||||
<div class="card-box px-1" style="height:240px;">
|
||||
<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds * 1000)" data-color="#aaa,#414d59"
|
||||
title="All charts set to refresh every @Model.PTMagicConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds seconds in your general settings."></div>
|
||||
@{
|
||||
string totalCurrentValueString = Model.totalCurrentValue.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"));
|
||||
if (Model.totalCurrentValue > 100) {
|
||||
totalCurrentValueString = Math.Round(Model.totalCurrentValue, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"));
|
||||
}
|
||||
}
|
||||
<div class="asset-distribution container" style="height: 100%; width: 100%;">
|
||||
<div class="text-center">
|
||||
<span data-toggle="tooltip" data-placement="top" title="Total current account value">TCV: <text class="text-autocolor">@totalCurrentValueString @Model.Summary.MainMarket </text> </span>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<small>
|
||||
<span data-toggle="tooltip" data-placement="top" title="Starting balance from PTM settings">Start: <text class="text-autocolor">@Model.MiscData.StartBalance </text></span>
|
||||
<span data-toggle="tooltip" data-placement="top" title="TCV gain on starting balance">  Gain: <text class="text-autocolor">@Math.Round(((Model.totalCurrentValue - Model.MiscData.StartBalance) / Model.MiscData.StartBalance) * 100, 2)%</text></span>
|
||||
</small>
|
||||
</div>
|
||||
<svg style="height:100%;width:100%"></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-9">
|
||||
<div class="card-box px-2" style="height:240px;">
|
||||
<h4 class="m-t-0 header-title"><b>TCV Trend </b><i class="fa fa-info-circle text-muted" style="font-size x-small" data-toggle="tooltip" data-placement="top" title="Data is added while the monitor is running. Currently set to show the past @Model.PTMagicConfiguration.GeneralSettings.Monitor.LiveTCVTimeframeMinutes minutes in your monitor settings."></i>
|
||||
@if (!Model.TotalCurrentValueLiveChartDataJSON.Equals("")) {
|
||||
<div class="TCVLive-chart">
|
||||
<svg style="height:220px;width:100%"></svg>
|
||||
</div>
|
||||
} else {
|
||||
<p>Unable to load graph, no sales data found.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 px-1">
|
||||
<div class="card-box px-2" style="height:340px;">
|
||||
<h4 class="m-t-0 m-b-20 header-title" style="display: inline;"><b>Market Trend History </b><i class="fa fa-info-circle text-muted" style="font-size x-small" data-toggle="tooltip" data-placement="top" title="@Math.Round(Model.DataHours, 1) hours of data available. Currently set to show @Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours hours at @Model.PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes intervals, in general settings."></i></h4>
|
||||
@if (!Model.TrendChartDataJSON.Equals("")) {
|
||||
|
@ -23,37 +64,12 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 px-1">
|
||||
<div class="card-box px-3" style="height:340px;">
|
||||
<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"
|
||||
title="All charts set to refresh every @Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds seconds in your general settings."></div>
|
||||
@{
|
||||
string totalCurrentValueString = Model.totalCurrentValue.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"));
|
||||
if (Model.totalCurrentValue > 100) {
|
||||
totalCurrentValueString = Math.Round(Model.totalCurrentValue, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"));
|
||||
}
|
||||
}
|
||||
<div id="AssetDistribution" class="container">
|
||||
<div class="text-center">
|
||||
<small>
|
||||
<span data-toggle="tooltip" data-placement="top" title="Starting balance from PTM settings">Start: <text class="text-autocolor"> @Model.MiscData.StartBalance @Model.Summary.MainMarket </text></span>
|
||||
<span data-toggle="tooltip" data-placement="top" title="TCV gain on starting balance">     Gain: <text class="text-autocolor">@Math.Round(((Model.totalCurrentValue - Model.MiscData.StartBalance) / Model.MiscData.StartBalance) * 100, 2)%</text></span>
|
||||
</small>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<span data-toggle="tooltip" data-placement="top" title="Total current account value">TCV: <text class="text-autocolor"> @totalCurrentValueString @Model.Summary.MainMarket </text> </span>
|
||||
</div>
|
||||
<div class="row px1">
|
||||
<svg style="height:260px;width:100%"></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 px-1">
|
||||
|
||||
<div class="col-md-6 px-1">
|
||||
@*<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div>*@
|
||||
<div class="card-box px-2" style="height:340px;">
|
||||
<h4 class="m-t-0 m-b-20 header-title" style="display: inline;">Daily Profit <i class="fa fa-info-circle text-muted" style="font-size x-small" data-toggle="tooltip" data-placement="top" title="@Model.ProfitDays days of data available. Currently Set to @Model.PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays days in general settings."></i>
|
||||
<h4 class="m-t-0 m-b-20 header-title" style="display: inline;">Daily Profit <i class="fa fa-info-circle text-muted" style="font-size x-small" data-toggle="tooltip" data-placement="top" title="@Model.PTData.DailyPNL.Count days of data available. Currently Set to @Model.PTMagicConfiguration.GeneralSettings.Monitor.ProfitsMaxTimeframeDays days in general settings."></i>
|
||||
@if (!Model.ProfitChartDataJSON.Equals("")) {
|
||||
<div class="profit-chart">
|
||||
<svg style="height:300px;width:100%"></svg>
|
||||
|
@ -70,9 +86,9 @@
|
|||
<div class="card-box px-3">
|
||||
@* <div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeco;nds * 1000)" data-color="#aaa,#414d59"></div>
|
||||
<br> *@
|
||||
<h4 class="m-t-0 m-b-20 header-title">Live Trends
|
||||
<i class="fa fa-info-circle text-muted" style="font-size small" data-toggle="tooltip" data-placement="top" title="Set to refresh every @Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds seconds in general settings."></i>
|
||||
<small class="pull-right" style="font-size: x-small"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)MarketAnalyzer">ANALYZER</a></small>
|
||||
<h4 class="m-t-0 m-b-20 header-title">Live Market Trends
|
||||
<i class="fa fa-info-circle text-muted" style="font-size small" data-toggle="tooltip" data-placement="top" title="Set to refresh every @Model.PTMagicConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds seconds in general settings."></i>
|
||||
<small class="pull-right" style="font-size: small"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)MarketAnalyzer">ANALYZER</a></small>
|
||||
</h4>
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
|
@ -80,7 +96,7 @@
|
|||
<th>Name</th>
|
||||
<th class="text-right">Markets</th>
|
||||
<th class="text-right">Timeframe</th>
|
||||
<th class="text-right">Threshold <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Pairs exceeding this threshold are excluded from the trend average."></i>
|
||||
<th class="text-right">Threshold <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Pairs exceeding this threshold are considered outliers that are excluded from the trend average."></i>
|
||||
</th>
|
||||
<th class="text-right">Change</th>
|
||||
</tr>
|
||||
|
@ -283,13 +299,20 @@
|
|||
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/tablesaw/js/tablesaw.js"></script>
|
||||
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/tablesaw/js/tablesaw-init.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$(".cdev").circlos();
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
$('.text-autocolor').autocolor(false);
|
||||
|
||||
var assetDistributionChart; // Keep a reference to the chart
|
||||
var assetDistributionData; // Keep a reference to the data
|
||||
|
||||
@if (!Model.AssetDistributionData.Equals("")) {
|
||||
|
@ -302,29 +325,19 @@
|
|||
.labelThreshold(.1)
|
||||
.labelType("percent")
|
||||
.donut(true)
|
||||
.donutRatio(0.3);
|
||||
.donutRatio(0.3)
|
||||
.margin({top: 10, bottom: 10})
|
||||
.showLegend(false); // Hide the legend
|
||||
|
||||
assetDistributionData = @Html.Raw(Model.AssetDistributionData);
|
||||
|
||||
d3.select("#AssetDistribution svg")
|
||||
d3.select(".asset-distribution svg") // Change this line
|
||||
.style('height', '90%')
|
||||
.style('width', '100%')
|
||||
.datum(assetDistributionData)
|
||||
.transition().duration(0)
|
||||
.call(assetDistributionChart);
|
||||
|
||||
// Add mouseleave, and mousemove event listeners to hide tooltip
|
||||
d3.select('.profit-chart').on('mouseleave', function() {
|
||||
d3.select('.nvtooltip').style('opacity', 0);
|
||||
});
|
||||
|
||||
d3.select('body').on('mousemove', function() {
|
||||
var chartBounds = d3.select('.profit-chart')[0][0].getBoundingClientRect();
|
||||
var mouseX = d3.event.clientX;
|
||||
var mouseY = d3.event.clientY;
|
||||
|
||||
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
|
||||
d3.select('.nvtooltip').style('opacity', 0);
|
||||
}
|
||||
});
|
||||
nv.utils.windowResize(assetDistributionChart.update);
|
||||
return assetDistributionChart;
|
||||
});
|
||||
|
@ -336,6 +349,7 @@
|
|||
<script type="text/javascript">
|
||||
(function ($) {
|
||||
'use strict';
|
||||
// $('head').append('<style>.nv-point { stroke-width: 1px; }</style>');
|
||||
$('[role="tooltip"]').remove();
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
$('.text-autocolor').autocolor(false);
|
||||
|
@ -349,28 +363,39 @@
|
|||
trendChart = nv.models.lineChart();
|
||||
var height = 300;
|
||||
trendChart.useInteractiveGuideline(true);
|
||||
trendChart.pointSize(0.25); // Set the point size to a smaller value
|
||||
trendChart.xAxis.tickFormat(function (d) { return d3.time.format('%H:%M')(new Date(d)); });
|
||||
trendChart.yAxis.axisLabel('Trend %').tickFormat(d3.format(',.2f'));
|
||||
|
||||
trendData = @Html.Raw(Model.TrendChartDataJSON);
|
||||
|
||||
d3.select('.trend-chart svg')
|
||||
var svg = d3.select('.trend-chart svg').node();
|
||||
d3.select(svg)
|
||||
.datum(trendData)
|
||||
.transition().duration(0)
|
||||
.call(trendChart);
|
||||
// Add mouseleave, and mousemove event listeners to hide tooltip
|
||||
d3.select('.profit-chart').on('mouseleave', function() {
|
||||
d3.select('.nvtooltip').style('opacity', 0);
|
||||
});
|
||||
|
||||
d3.select('body').on('mousemove', function() {
|
||||
var chartBounds = d3.select('.profit-chart')[0][0].getBoundingClientRect();
|
||||
var mouseX = d3.event.clientX;
|
||||
var mouseY = d3.event.clientY;
|
||||
trendChart.dispatch.on('renderEnd', function() {
|
||||
// Get the chart's container
|
||||
var container = d3.select('.trend-chart .nv-wrap.nv-lineChart .nv-linesWrap');
|
||||
|
||||
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
|
||||
d3.select('.nvtooltip').style('opacity', 0);
|
||||
}
|
||||
// Remove any existing y=0 line
|
||||
container.selectAll('.zero-line').remove();
|
||||
|
||||
// Get the x-values of the first and last data points
|
||||
var xRange = trendChart.xAxis.scale().range();
|
||||
var xMin = xRange[0];
|
||||
var xMax = xRange[1];
|
||||
|
||||
// Add a line at y=0
|
||||
container.insert('line', ':first-child')
|
||||
.attr('class', 'zero-line') // Add a class to the line for easy selection
|
||||
.attr('x1', xMin) // x position of the first end of the line
|
||||
.attr('y1', trendChart.yAxis.scale()(0)) // y position of the first end of the line
|
||||
.attr('x2', xMax) // x position of the second end of the line
|
||||
.attr('y2', trendChart.yAxis.scale()(0)) // y position of the second end of the line
|
||||
.attr('stroke', 'gray') // color of the line
|
||||
.attr('stroke-width', 2); // width of the line
|
||||
});
|
||||
nv.utils.windowResize(trendChart.update);
|
||||
return trendChart;
|
||||
|
@ -390,6 +415,7 @@
|
|||
var profitChart; // Keep a reference to the chart
|
||||
var profitData; // Keep a reference to the data
|
||||
|
||||
|
||||
@if (!Model.ProfitChartDataJSON.Equals("")) {
|
||||
<text>
|
||||
nv.addGraph(function () {
|
||||
|
@ -406,20 +432,33 @@
|
|||
.transition().duration(0)
|
||||
.call(profitChart);
|
||||
|
||||
// Add mouseleave, and mousemove event listeners to hide tooltip
|
||||
d3.select('.profit-chart').on('mouseleave', function() {
|
||||
d3.select('.nvtooltip').style('opacity', 0);
|
||||
});
|
||||
profitChart.dispatch.on('renderEnd', function() {
|
||||
// Get the chart's container
|
||||
var container = d3.select('.profit-chart .nv-wrap.nv-lineChart .nv-linesWrap');
|
||||
|
||||
d3.select('body').on('mousemove', function() {
|
||||
var chartBounds = d3.select('.profit-chart')[0][0].getBoundingClientRect();
|
||||
var mouseX = d3.event.clientX;
|
||||
var mouseY = d3.event.clientY;
|
||||
// Remove any existing y=0 line
|
||||
container.selectAll('.zero-line').remove();
|
||||
|
||||
if (mouseX < chartBounds.left || mouseX > chartBounds.right || mouseY < chartBounds.top || mouseY > chartBounds.bottom) {
|
||||
d3.select('.nvtooltip').style('opacity', 0);
|
||||
// Check if profitData[0].values is not empty
|
||||
if (profitData[0].values.length > 0) {
|
||||
// Get the x-values of the first and last data points
|
||||
var xMin = profitChart.xAxis.scale()(profitData[0].values[0].x);
|
||||
var xMax = profitChart.xAxis.scale()(profitData[0].values[profitData[0].values.length - 1].x);
|
||||
|
||||
// Add a line at y=0
|
||||
container.insert('line', ':first-child')
|
||||
.attr('class', 'zero-line') // Add a class to the line for easy selection
|
||||
.attr('x1', xMin) // x position of the first end of the line
|
||||
.attr('y1', profitChart.yAxis.scale()(0)) // y position of the first end of the line
|
||||
.attr('x2', xMax) // x position of the second end of the line
|
||||
.attr('y2', profitChart.yAxis.scale()(0)) // y position of the second end of the line
|
||||
.attr('stroke', 'gray') // color of the line
|
||||
.attr('stroke-width', 2); // width of the line
|
||||
|
||||
window.profitChartUpdate = nv.utils.windowResize(function() { profitChart.update(); });
|
||||
}
|
||||
});
|
||||
|
||||
nv.utils.windowResize(profitChart.update);
|
||||
return profitChart;
|
||||
});
|
||||
|
@ -429,29 +468,78 @@
|
|||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
var originalLeave = $.fn.tooltip.Constructor.prototype.leave;
|
||||
$.fn.tooltip.Constructor.prototype.leave = function(obj){
|
||||
var self = obj instanceof this.constructor ?
|
||||
obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type);
|
||||
var container, timeout;
|
||||
(function ($) {
|
||||
'use strict';
|
||||
$('[role="tooltip"]').remove();
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
$('.text-autocolor').autocolor(false);
|
||||
|
||||
originalLeave.call(this, obj);
|
||||
var TCVLiveChart; // Keep a reference to the chart
|
||||
var TCVLiveData; // Keep a reference to the data
|
||||
|
||||
if(obj.currentTarget) {
|
||||
container = $(obj.currentTarget).siblings('.tooltip');
|
||||
timeout = self.timeout;
|
||||
container.one('mouseenter', function(){
|
||||
//We entered the actual tooltip – call off the dogs
|
||||
clearTimeout(timeout);
|
||||
//Let's monitor tooltip content instead
|
||||
container.one('mouseleave', function(){
|
||||
$.fn.tooltip.Constructor.prototype.leave.call(self, self);
|
||||
@if (!Model.TotalCurrentValueLiveChartDataJSON.Equals("")) {
|
||||
<text>
|
||||
nv.addGraph(function () {
|
||||
TCVLiveChart = nv.models.lineChart();
|
||||
TCVLiveChart.useInteractiveGuideline(true);
|
||||
TCVLiveChart.xAxis.tickFormat(function (d) { return d3.time.format('%H:%M:%S')(new Date(d)); });
|
||||
TCVLiveChart.yAxis.axisLabel('').tickFormat(d3.format(',.2f'));
|
||||
|
||||
TCVLiveData = @Html.Raw(Model.TotalCurrentValueLiveChartDataJSON);
|
||||
|
||||
d3.select('.TCVLive-chart svg')
|
||||
.style('width', '100%')
|
||||
.datum(TCVLiveData)
|
||||
.transition().duration(0)
|
||||
.call(TCVLiveChart);
|
||||
|
||||
|
||||
nv.utils.windowResize(TCVLiveChart.update);
|
||||
return TCVLiveChart;
|
||||
});
|
||||
</text>
|
||||
}
|
||||
})(jQuery);
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
function attachTooltipRemoval(chartClass) {
|
||||
d3.selectAll(chartClass).on('mouseleave', function() {
|
||||
var tooltip = d3.select(this).select('.nvtooltip');
|
||||
if (!tooltip.empty()) {
|
||||
tooltip.style('opacity', 0);
|
||||
}
|
||||
});
|
||||
|
||||
d3.selectAll(chartClass).on('mouseenter', function() {
|
||||
var tooltip = d3.select(this).select('.nvtooltip');
|
||||
if (!tooltip.empty()) {
|
||||
tooltip.style('opacity', 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
$(document).ready(function () {
|
||||
// Hide all tooltips when the page is loaded
|
||||
d3.selectAll('.nvtooltip').style('opacity', 0);
|
||||
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
// Call the function for each chart
|
||||
attachTooltipRemoval('.asset-distribution svg');
|
||||
attachTooltipRemoval('.trend-chart svg');
|
||||
attachTooltipRemoval('.profit-chart svg');
|
||||
attachTooltipRemoval('.TCVLive-chart svg');
|
||||
// Add other charts as needed
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.cleanupData = function() {
|
||||
TCVLiveChart = null;
|
||||
TCVLiveData = null;
|
||||
assetDistributionChart = null;
|
||||
assetDistributionData = null;
|
||||
trendChart = null;
|
||||
trendData = null;
|
||||
profitChart = null;
|
||||
profitData = null;
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace Monitor.Pages
|
|||
public DateTimeOffset DateTimeNow = Constants.confMinDate;
|
||||
public string AssetDistributionData = "";
|
||||
public double totalCurrentValue = 0;
|
||||
public string TotalCurrentValueLiveChartDataJSON { get; set; }
|
||||
public void OnGet()
|
||||
{
|
||||
// Initialize Config
|
||||
|
@ -49,7 +50,7 @@ namespace Monitor.Pages
|
|||
FileHelper.CleanupFilesMinutes(PTMagicMonitorBasePath + "wwwroot" + System.IO.Path.DirectorySeparatorChar + "assets" + System.IO.Path.DirectorySeparatorChar + "tmp" + System.IO.Path.DirectorySeparatorChar, 5);
|
||||
|
||||
// Convert local offset time to UTC
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(MiscData.TimeZoneOffset.Replace("+", ""));
|
||||
DateTimeNow = DateTimeOffset.UtcNow.ToOffset(offsetTimeSpan);
|
||||
|
||||
// Get last and current active setting
|
||||
|
@ -65,7 +66,108 @@ namespace Monitor.Pages
|
|||
BuildMarketTrendChartData();
|
||||
BuildAssetDistributionData();
|
||||
BuildProfitChartData();
|
||||
StartUpdatingTotalCurrentValueLive();
|
||||
UpdateTotalCurrentValueLive();
|
||||
BuildTotalCurrentValueLiveChartData();
|
||||
}
|
||||
private static System.Timers.Timer timer;
|
||||
private static List<(DateTime Timestamp, double TotalCurrentValue)> totalCurrentValueLiveList;
|
||||
|
||||
public void StartUpdatingTotalCurrentValueLive()
|
||||
{
|
||||
int liveTCVInterval = PTMagicConfiguration.GeneralSettings.Monitor.DashboardChartsRefreshSeconds;
|
||||
if (timer != null)
|
||||
{
|
||||
// Timer is already running
|
||||
return;
|
||||
}
|
||||
|
||||
totalCurrentValueLiveList = new List<(DateTime Timestamp, double TotalCurrentValueLive)>();
|
||||
|
||||
timer = new System.Timers.Timer(liveTCVInterval * 1000); // Set interval to liveTCVTimer seconds
|
||||
timer.Elapsed += (sender, e) => UpdateTotalCurrentValueLive();
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private void UpdateTotalCurrentValueLive()
|
||||
{
|
||||
double PairsBalance = 0.0;
|
||||
double DCABalance = 0.0;
|
||||
double PendingBalance = 0.0;
|
||||
double AvailableBalance = PTData.GetCurrentBalance();
|
||||
bool isSellStrategyTrue = false;
|
||||
bool isTrailingSellActive = false;
|
||||
|
||||
foreach (DCALogData dcaLogEntry in PTData.DCALog)
|
||||
{
|
||||
string sellStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, isSellStrategyTrue, isTrailingSellActive);
|
||||
// Aggregate totals
|
||||
double leverage = dcaLogEntry.Leverage;
|
||||
if (leverage == 0)
|
||||
{
|
||||
leverage = 1;
|
||||
}
|
||||
if (sellStrategyText.Contains("PENDING"))
|
||||
{
|
||||
PendingBalance = PendingBalance + (dcaLogEntry.Amount * dcaLogEntry.CurrentPrice / leverage);
|
||||
}
|
||||
else if (dcaLogEntry.BuyStrategies.Count > 0)
|
||||
{
|
||||
DCABalance = DCABalance + (dcaLogEntry.Amount * dcaLogEntry.CurrentPrice / leverage);
|
||||
}
|
||||
else
|
||||
{
|
||||
PairsBalance = PairsBalance + (dcaLogEntry.Amount * dcaLogEntry.CurrentPrice / leverage);
|
||||
}
|
||||
}
|
||||
double totalCurrentValueLive = PendingBalance + DCABalance + PairsBalance + AvailableBalance;
|
||||
|
||||
// Get the current time
|
||||
DateTime now = DateTime.UtcNow;
|
||||
totalCurrentValueLiveList.Add((now, totalCurrentValueLive));
|
||||
|
||||
// Get liveTCVTimeframe from PTMagicConfiguration.GeneralSettings.Monitor
|
||||
int liveTCVTimeframe = PTMagicConfiguration.GeneralSettings.Monitor.LiveTCVTimeframeMinutes;
|
||||
|
||||
// Calculate the timestamp that is liveTCVTimeframe minutes ago
|
||||
DateTime threshold = now.AddMinutes(-liveTCVTimeframe);
|
||||
|
||||
// Remove all data points that are older than the threshold
|
||||
while (totalCurrentValueLiveList.Count > 0 && totalCurrentValueLiveList[0].Item1 < threshold)
|
||||
{
|
||||
totalCurrentValueLiveList.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildTotalCurrentValueLiveChartData()
|
||||
{
|
||||
List<object> TotalCurrentValueLivePerIntervalList = new List<object>();
|
||||
|
||||
if (totalCurrentValueLiveList.Count > 0)
|
||||
{
|
||||
foreach (var dataPoint in totalCurrentValueLiveList)
|
||||
{
|
||||
DateTime timestamp = dataPoint.Timestamp;
|
||||
double totalCurrentValueLive = dataPoint.TotalCurrentValue;
|
||||
|
||||
// Convert the timestamp to a Unix timestamp
|
||||
long unixTimestamp = new DateTimeOffset(timestamp).ToUnixTimeMilliseconds();
|
||||
|
||||
// Add the data point to the list
|
||||
TotalCurrentValueLivePerIntervalList.Add(new { x = unixTimestamp, y = totalCurrentValueLive });
|
||||
}
|
||||
|
||||
// Convert the list to a JSON string using Newtonsoft.Json
|
||||
TotalCurrentValueLiveChartDataJSON = Newtonsoft.Json.JsonConvert.SerializeObject(new[] {
|
||||
new {
|
||||
key = "Total Current Value",
|
||||
color = Constants.ChartLineColors[1],
|
||||
values = TotalCurrentValueLivePerIntervalList
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void BuildMarketTrendChartData()
|
||||
{
|
||||
|
@ -95,8 +197,8 @@ namespace Monitor.Pages
|
|||
|
||||
// Get trend ticks for chart
|
||||
TimeSpan offset;
|
||||
bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-");
|
||||
string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-');
|
||||
bool isNegative = MiscData.TimeZoneOffset.StartsWith("-");
|
||||
string offsetWithoutSign = MiscData.TimeZoneOffset.TrimStart('+', '-');
|
||||
|
||||
if (!TimeSpan.TryParse(offsetWithoutSign, out offset))
|
||||
{
|
||||
|
@ -158,8 +260,8 @@ namespace Monitor.Pages
|
|||
{
|
||||
// Get timezone offset
|
||||
TimeSpan offset;
|
||||
bool isNegative = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.StartsWith("-");
|
||||
string offsetWithoutSign = PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.TrimStart('+', '-');
|
||||
bool isNegative = MiscData.TimeZoneOffset.StartsWith("-");
|
||||
string offsetWithoutSign = MiscData.TimeZoneOffset.TrimStart('+', '-');
|
||||
|
||||
if (!TimeSpan.TryParse(offsetWithoutSign, out offset))
|
||||
{
|
||||
|
|
|
@ -3,13 +3,15 @@
|
|||
@{
|
||||
Layout = null;
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
@{
|
||||
bool sideBySide = true;
|
||||
}
|
||||
<div class="@(sideBySide ? "row" : "col-md-12")">
|
||||
@if (Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDashboardBuyEntries>0)
|
||||
{
|
||||
<div class="col-md-6 px-1">
|
||||
<div class="col-md px-1">
|
||||
<div class="card-box px-2">
|
||||
<h4 class="m-t-0 m-b-20 header-title"><b>Possible Buys (@Model.PTData.BuyLog.Count)</b><small id="buylist-refresh-icon"></small><small class="pull-right"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)BuyAnalyzer">more</a></small></h4>
|
||||
<h4 class="m-t-0 m-b-20 header-title"><b>Possible Buys (@Model.PTData.BuyLog.Count)</b><small id="buylist-refresh-icon"><i class="fa fa-info-circle text-muted" style="font-size x-small" data-toggle="tooltip" data-placement="top" title="Set 'Max Dashboard Buy Entries' to zero in Monitor Settings, to hide this table."></i></small><small class="pull-right"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)BuyAnalyzer">more</a></small></h4>
|
||||
@if (Model.PTData.BuyLog.Count == 0)
|
||||
{
|
||||
<p>Your Profit Trailer did not find anything worth buying so far.</p>
|
||||
|
@ -19,11 +21,10 @@
|
|||
<table class="table table-sm m-b-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Market</th>
|
||||
<th class="text-left" data-toggle="tooltip" data-placement="top" title="24 Hour price trend">24H</th>
|
||||
<th class="text-left" data-toggle="tooltip" data-placement="top" title="24 Hour trading volume">Volume</th>
|
||||
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Current ask price for this market">Ask</th>
|
||||
<th>Buy Strategies</th>
|
||||
<th><strong>Market </strong><i class="fa fa-info-circle text-muted" style="font-size: x-small" data-toggle="tooltip" data-placement="top" title="Market and 24h CHANGE"></i></th>
|
||||
<th><strong>Volume </strong><i class="fa fa-info-circle text-muted" style="font-size: x-small" data-toggle="tooltip" data-placement="top" title="24h VOLUME"></i></th>
|
||||
<th><strong>Ask </strong></th>
|
||||
<th><strong>Buy Strategies </strong></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -57,13 +58,21 @@
|
|||
|
||||
<tr>
|
||||
@if (mps == null || mps.ActiveSingleSettings == null || mps.ActiveSingleSettings.Count == 0) {
|
||||
<th class="align-top"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, buyLogEntry.Market, Model.Summary.MainMarket)" target="_blank">@buyLogEntry.Market</a></th>
|
||||
<th>
|
||||
<span style="font-size: 1.3em;"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, buyLogEntry.Market, Model.Summary.MainMarket, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" target="_blank">@buyLogEntry.Market</span>
|
||||
<br><span class="text-autocolor">@Html.Raw((buyLogEntry.PercChange * 100).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))) % </span>
|
||||
</th>
|
||||
} else {
|
||||
<th class="align-top; text-nowrap"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, buyLogEntry.Market, Model.Summary.MainMarket)" target="_blank">@buyLogEntry.Market </a> <i class="fa fa-exclamation-triangle text-highlight" data-toggle="tooltip" data-placement="top" data-html="true" title="@await Component.InvokeAsync("PairIcon", mps)" data-template="<div class='tooltip' role='tooltip'><div class='tooltip-arrow'></div><div class='tooltip-inner pair-tooltip'></div></div>"></i></th>
|
||||
<th>
|
||||
<div style="white-space: nowrap;">
|
||||
<span style="font-size: 1.3em;"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, buyLogEntry.Market, Model.Summary.MainMarket, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" target="_blank">@buyLogEntry.Market</a></span>
|
||||
<i class="fa fa-exclamation-triangle text-highlight" data-toggle="tooltip" data-placement="top" data-html="true" title="@await Component.InvokeAsync("PairIcon", mps)" data-template="<div class='tooltip' role='tooltip'><div class='tooltip-arrow'></div><div class='tooltip-inner pair-tooltip'></div></div>"></i>
|
||||
</div>
|
||||
<span class="text-autocolor">@Html.Raw((buyLogEntry.PercChange * 100).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))) % </span>
|
||||
</th>
|
||||
}
|
||||
<td class="text-autocolor">@string.Format("{0}%", (buyLogEntry.PercChange * 100).ToString("#,#0.00"))</td>
|
||||
<td class="text">@string.Format("{0}", (buyLogEntry.Volume24h).ToString())</td>
|
||||
<td class="text-left">@buyLogEntry.CurrentPrice.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
||||
<td >@string.Format("{0}", (buyLogEntry.Volume24h).ToString())</td>
|
||||
<td >@buyLogEntry.CurrentPrice.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
|
||||
|
||||
@if (buyDisabled) {
|
||||
<td>@Html.Raw(buyStrategyText)</td>
|
||||
|
@ -85,7 +94,7 @@
|
|||
|
||||
<div class="col-md px-1">
|
||||
<div class="card-box px-2">
|
||||
<h4 class="m-t-0 m-b-20 header-title"><b>Pairs / DCA / Pending (@Model.PTData.DCALog.Count)</b><small id="baglist-refresh-icon"></small><small class="pull-right"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)BagAnalyzer">more</a></small></h4>
|
||||
<h4 class="m-t-0 m-b-20 header-title"><b>Positions (@Model.PTData.DCALog.Count)</b><small id="baglist-refresh-icon"></small><small class="pull-right"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)BagAnalyzer">more</a></small></h4>
|
||||
|
||||
@if (Model.PTData.DCALog.Count == 0)
|
||||
{
|
||||
|
@ -94,16 +103,15 @@
|
|||
else
|
||||
{
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm m-b-0">
|
||||
<table class="table table-sm m-b-0 table-auto-width">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Market</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><strong>Market </strong><i class="fa fa-info-circle text-muted" style="font-size: x-small" data-toggle="tooltip" data-placement="top" title="Market and 24h CHANGE"></i></th>
|
||||
<th><strong>Cost </strong><i class="fa fa-info-circle text-muted" style="font-size: x-small" data-toggle="tooltip" data-placement="top" title="Total COST and TIME HELD"></i></th>
|
||||
<th></th>
|
||||
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Active Buy Strategies">DCA</th>
|
||||
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Active Sell Strategies">Sell</th>
|
||||
<th class="text-left" data-toggle="tooltip" data-html="true" data-placement="top" title="Profit Target <br> Current Profit">Profit</th>
|
||||
<th><strong>DCA </strong></th>
|
||||
<th><strong>Sell </strong></th>
|
||||
<th><strong>Profit </strong><i class="fa fa-info-circle text-muted" style="font-size: x-small" data-toggle="tooltip" data-placement="top" title="Proft TARGET and CURRENT Profit"></i></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -142,7 +150,6 @@
|
|||
if (dcaLogEntry.SellStrategies.Count > 0) {
|
||||
isSellStrategyTrue = (dcaLogEntry.SellStrategies.FindAll(ss => !ss.IsTrue).Count == 0);
|
||||
}
|
||||
|
||||
string leverage = "";
|
||||
double leverageValue = 1;
|
||||
string buyStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Model.Summary, dcaLogEntry.BuyStrategies, dcaLogEntry.BuyStrategy, isBuyStrategyTrue, isTrailingBuyActive);
|
||||
|
@ -155,54 +162,37 @@
|
|||
// Profit percentage
|
||||
var profitPercentage = dcaLogEntry.ProfitPercent;
|
||||
|
||||
// if (dcaLogEntry.SellStrategies != null)
|
||||
// {
|
||||
// var gainStrategy = dcaLogEntry.SellStrategies.FirstOrDefault(x => x.Name.Contains(" GAIN", StringComparison.InvariantCultureIgnoreCase));
|
||||
// if (gainStrategy != null)
|
||||
// {
|
||||
// // Use the gain percentage value as it is accurate to what can be achieved with the order book!
|
||||
// profitPercentage = gainStrategy.CurrentValue;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Render the row
|
||||
|
||||
if (!sellStrategyText.Contains("PENDING-BUY"))
|
||||
{
|
||||
|
||||
<tr @(lostValue ? "class=errorRow" : "") >
|
||||
|
||||
<!-- Market -->
|
||||
<td class="align-top; text-nowrap">
|
||||
<b>
|
||||
@if (mps == null || mps.ActiveSingleSettings == null || mps.ActiveSingleSettings.Count == 0)
|
||||
{
|
||||
<a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, dcaLogEntry.Market, Model.Summary.MainMarket)" target="_blank">@dcaLogEntry.Market</a>
|
||||
<span style="font-size: 1.3em;"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, dcaLogEntry.Market, Model.Summary.MainMarket, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" target="_blank">@dcaLogEntry.Market</a></span>
|
||||
} else
|
||||
{
|
||||
<a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, dcaLogEntry.Market, Model.Summary.MainMarket)" target="_blank">@dcaLogEntry.Market </a><i class="fa fa-exclamation-triangle text-highlight" data-toggle="tooltip" data-placement="top" data-html="true" title="@await Component.InvokeAsync("PairIcon", mps)" data-template="<div class='tooltip' role='tooltip'><div class='tooltip-arrow'></div><div class='tooltip-inner pair-tooltip'></div></div>"></i>
|
||||
|
||||
<span style="font-size: 1.3em;"><a href="@Core.Helper.SystemHelper.GetMarketLink(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform,Model.PTMagicConfiguration.GeneralSettings.Application.Exchange, dcaLogEntry.Market, Model.Summary.MainMarket, Model.PTMagicConfiguration.GeneralSettings.Monitor.TVCustomLayout)" target="_blank">@dcaLogEntry.Market </a></span><i class="fa fa-exclamation-triangle text-highlight" data-toggle="tooltip" data-placement="top" data-html="true" title="@await Component.InvokeAsync("PairIcon", mps)" data-template="<div class='tooltip' role='tooltip'><div class='tooltip-arrow'></div><div class='tooltip-inner pair-tooltip'></div></div>"></i>
|
||||
}
|
||||
</b>
|
||||
<br>
|
||||
@bagAgeText
|
||||
</td>
|
||||
<br><span class="text-autocolor">@Html.Raw((dcaLogEntry.PercChange * 100).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))) %</span>
|
||||
|
||||
<!-- 24hr change -->
|
||||
<td class="text-autocolor">@Html.Raw((dcaLogEntry.PercChange * 100).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))%</td>
|
||||
</td>
|
||||
<!-- Cost -->
|
||||
<td class="text-left">@Html.Raw(dcaLogEntry.TotalCost.ToString("#,#0.000000", new System.Globalization.CultureInfo("en-US")))</td>
|
||||
<td class="text-left">@Html.Raw(dcaLogEntry.TotalCost.ToString("#,#0.000000", new System.Globalization.CultureInfo("en-US")))<br><span class="text-highlight">@bagAgeText</span></td>
|
||||
<!-- DCA Count -->
|
||||
<td class="text-right">
|
||||
@if (dcaEnabled)
|
||||
{
|
||||
@if (dcaLogEntry.BoughtTimes > 0)
|
||||
if (dcaLogEntry.BoughtTimes > 0)
|
||||
{
|
||||
@dcaLogEntry.BoughtTimes;
|
||||
}
|
||||
} else
|
||||
{
|
||||
<span data-toggle="tooltip" data-placement="top" title="DCA is disabled"><i class="fa fa-ban text-highlight"></i></span>
|
||||
<span data-toggle="tooltip" data-placement="top" title="DCA disabled"><i class="fa fa-ban text-highlight"></i></span>
|
||||
}
|
||||
</td>
|
||||
<!-- DCA Strategy -->
|
||||
|
@ -231,10 +221,10 @@
|
|||
|
||||
@if ( !(sellStrategyText.Contains("WATCHMODE")) && !(sellStrategyText.Contains("PENDING")))
|
||||
{
|
||||
double TargetGain = leverageValue * dcaLogEntry.TargetGainValue.Value;
|
||||
double TargetGain = leverageValue * dcaLogEntry.TargetGainValue.GetValueOrDefault();
|
||||
<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>
|
||||
<div class="text-autocolor" style="font-size: 1.2em; white-space: nowrap;">@profitPercentage.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) %</div>
|
||||
</td>
|
||||
}
|
||||
else
|
||||
|
@ -262,17 +252,15 @@
|
|||
Model.TotalBagCost = Model.TotalBagCost + dcaLogEntry.TotalCost;
|
||||
Model.TotalBagGain = Model.TotalBagGain + bagGain;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
<td>Totals:</td>
|
||||
<td></td>
|
||||
<td>@Html.Raw(Model.TotalBagCost.ToString("#,#0.000000", new System.Globalization.CultureInfo("en-US")))</td>
|
||||
<td style="font-size: 1.2em;"><strong>Totals:</strong></td>
|
||||
<td style="font-size: 1.2em;"><strong>@Html.Raw(Model.TotalBagCost.ToString("#,#0.000000", new System.Globalization.CultureInfo("en-US")))</strong></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; font-size: 1.2em;"><strong>@Html.Raw((((Model.TotalBagGain) / Model.TotalBagCost) * 100).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))) %</strong></td>
|
||||
<td></td>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
using System;
|
||||
using Core.Main;
|
||||
using Core.Main.DataObjects;
|
||||
using Core.Main.DataObjects.PTMagicData;
|
||||
|
||||
namespace Monitor.Pages {
|
||||
public class DashboardTopModel : _Internal.BasePageModelSecureAJAX {
|
||||
public ProfitTrailerData PTData = null;
|
||||
public MiscData MiscData = null;
|
||||
public DateTimeOffset DateTimeNow = Constants.confMinDate;
|
||||
public void OnGet() {
|
||||
// Initialize Config
|
||||
base.Init();
|
||||
|
||||
BindData();
|
||||
}
|
||||
public double TotalBagCost = 0;
|
||||
|
@ -17,9 +18,9 @@ namespace Monitor.Pages {
|
|||
public double TotalBagGain = 0;
|
||||
private void BindData() {
|
||||
PTData = this.PtDataObject;
|
||||
|
||||
MiscData = this.PTData.Misc;
|
||||
// Convert local offset time to UTC
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(MiscData.TimeZoneOffset.Replace("+", ""));
|
||||
DateTimeNow = DateTimeOffset.UtcNow.ToOffset(offsetTimeSpan);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,12 +36,7 @@
|
|||
@if (!Model.GlobalSetting.SettingName.StartsWith("Default", StringComparison.InvariantCultureIgnoreCase)) {
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Trigger Connection <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Define if triggers will be connected by AND or OR"></i></label>
|
||||
<div class="col-md-8">
|
||||
<select name="MarketAnalyzer_GlobalSetting_@(Model.SettingName)|TriggerConnection" class="form-control">
|
||||
<option selected="@(Model.GlobalSetting.TriggerConnection.Equals("AND", StringComparison.InvariantCultureIgnoreCase))">AND</option>
|
||||
<option selected="@(Model.GlobalSetting.TriggerConnection.Equals("OR", StringComparison.InvariantCultureIgnoreCase))">OR</option>
|
||||
</select>
|
||||
</div>
|
||||
<input type="text" name="MarketAnalyzer_GlobalSetting_@(Model.SettingName)|TriggerConnection" class="form-control" value="@Model.GlobalSetting.TriggerConnection" />
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Trend Threshold <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Exclude coins above/below this value when calculing market trend average."></i></label>
|
||||
<label class="col-md-4 col-form-label">Trend Threshold <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Exclude coins above/below this value as outliers, when calculing market trend average."></i></label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" name="MarketAnalyzer_MarketTrend_@(Model.MarketTrendName)|TrendThreshold" value="@Model.MarketTrend.TrendThreshold.ToString()">
|
||||
<span class="help-block"><small>Leave empty to exclude none</small></span>
|
||||
|
|
|
@ -29,20 +29,14 @@
|
|||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Trigger Connection <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Define if triggers will be connected by AND or OR"></i></label>
|
||||
<div class="col-md-8">
|
||||
<select name="MarketAnalyzer_SingleMarketSetting_@(Model.SettingName)|TriggerConnection" class="form-control">
|
||||
<option selected="@(Model.SingleMarketSetting.TriggerConnection.Equals("AND", StringComparison.InvariantCultureIgnoreCase))">AND</option>
|
||||
<option selected="@(Model.SingleMarketSetting.TriggerConnection.Equals("OR", StringComparison.InvariantCultureIgnoreCase))">OR</option>
|
||||
</select>
|
||||
<input type="text" name="MarketAnalyzer_SingleMarketSetting_@(Model.SettingName)|TriggerConnection" class="form-control" value="@Model.SingleMarketSetting.TriggerConnection" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Off Trigger Connection <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Define if off triggers will be connected by AND or OR"></i></label>
|
||||
<div class="col-md-8">
|
||||
<select name="MarketAnalyzer_SingleMarketSetting_@(Model.SettingName)|OffTriggerConnection" class="form-control">
|
||||
<option selected="@(Model.SingleMarketSetting.OffTriggerConnection.Equals("AND", StringComparison.InvariantCultureIgnoreCase))">AND</option>
|
||||
<option selected="@(Model.SingleMarketSetting.OffTriggerConnection.Equals("OR", StringComparison.InvariantCultureIgnoreCase))">OR</option>
|
||||
</select>
|
||||
<input type="text" name="MarketAnalyzer_SingleMarketSetting_@(Model.SettingName)|OffTriggerConnection" class="form-control" value="@Model.SingleMarketSetting.OffTriggerConnection" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -31,6 +31,9 @@
|
|||
ptMagicHealthTooltip = "PT Magic seems to have problems, check the logs! Time elapsed since last run: " + Math.Round(elapsedSecondsSinceRuntime / 60, 1) + " mins.";
|
||||
healthIconColor = "text-danger";
|
||||
}
|
||||
if (Model.IsAnalyzerRunning()) {
|
||||
ptMagicHealthIcon = "fa-cog fa-spin";
|
||||
}
|
||||
}
|
||||
|
||||
<div class="card-box card-box-mini card-box-ptmagic-outlined @globalIconColor">
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Core.Main;
|
||||
using Core.Main.DataObjects;
|
||||
using Core.Main.DataObjects.PTMagicData;
|
||||
using Core.MarketAnalyzer;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using System.Threading;
|
||||
|
||||
namespace Monitor.Pages {
|
||||
public class TickerWidgetsModel : _Internal.BasePageModelSecureAJAX {
|
||||
public ProfitTrailerData PTData = null;
|
||||
public List<string> MarketsWithSingleSettings = new List<string>();
|
||||
private readonly IWebHostEnvironment _hostingEnvironment;
|
||||
private Mutex mutex = new Mutex(false, "analyzerStateMutex");
|
||||
|
||||
public TickerWidgetsModel(IWebHostEnvironment hostingEnvironment) // Add this constructor
|
||||
{
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
}
|
||||
|
||||
public void OnGet() {
|
||||
// Initialize Config
|
||||
|
@ -18,6 +25,33 @@ namespace Monitor.Pages {
|
|||
|
||||
BindData();
|
||||
}
|
||||
public bool IsAnalyzerRunning()
|
||||
{
|
||||
bool ownsMutex = false;
|
||||
try
|
||||
{
|
||||
// Try to acquire the mutex.
|
||||
ownsMutex = mutex.WaitOne(0);
|
||||
|
||||
string webRootParent = Directory.GetParent(_hostingEnvironment.WebRootPath).FullName;
|
||||
string ptMagicRoot = Directory.GetParent(webRootParent).FullName;
|
||||
string analyzerStatePath = Path.Combine(ptMagicRoot, "_data", "AnalyzerState");
|
||||
if (System.IO.File.Exists(analyzerStatePath))
|
||||
{
|
||||
string state = System.IO.File.ReadAllText(analyzerStatePath);
|
||||
return state == "1";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Only release the mutex if this thread owns it.
|
||||
if (ownsMutex)
|
||||
{
|
||||
mutex.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BindData() {
|
||||
PTData = this.PtDataObject;
|
||||
|
|
|
@ -22,4 +22,8 @@
|
|||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.3.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -6,7 +6,7 @@ using Core.Helper;
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
|
||||
[assembly: AssemblyVersion("2.7.1")]
|
||||
[assembly: AssemblyVersion("2.8.4")]
|
||||
[assembly: AssemblyProduct("PT Magic")]
|
||||
|
||||
namespace PTMagic
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//
|
||||
// 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, and settings.
|
||||
//
|
||||
// You should take your time and adjust these settings according to your own personal preferences.
|
||||
// Always test your PTMagic settings by running a Profit Trailer bot in TESTMODE, to make sure
|
||||
// it is performing as you expect.
|
||||
//
|
||||
|
@ -13,17 +12,20 @@
|
|||
"MarketAnalyzer": {
|
||||
"StoreDataMaxHours": 48, // Number of hours to store market data
|
||||
"IntervalMinutes": 2, // Interval in minutes for PTMagic to check market trends and triggers
|
||||
"ExcludeMainCurrency": true, // Excludes the main currency (for example BTC) from market trend analysis
|
||||
"ExcludeMainCurrency": true, // Excludes the main currency (for example BTC, if you are trading against BTC) from market trend analysis
|
||||
"MarketTrends": [
|
||||
{
|
||||
"Name": "1h", // UNIQUE market trend name (to be referenced by your triggers below)
|
||||
"Platform": "Exchange", // Platform to grab prices from (Allowed values are: CoinMarketCap, Exchange)
|
||||
"MaxMarkets": 50, // Number of markets/pairs to analyze sorted by 24h volume
|
||||
"TrendMinutes": 60, // Number of minutes to build a trend (1440 = 24h, 720 = 12h, 60 = 1h)
|
||||
"TrendCurrency": "Market", // Trend Currency to build the trend against. If set to "Fiat", the trend will take the USD value of your main currency into account to build the trend. "Market" will build a trend against your base currency, such as BTC or USDT.
|
||||
"TrendThreshold": 15, // Any coin that is above 15% or below -15% for this timeframe will not be used when calculating the market average.
|
||||
"DisplayGraph": false, // Use this trend in the graph on the PTM Monitor dashboard and market analyzer?
|
||||
"DisplayOnMarketAnalyzerList": false // Disply this trend on the PTM Monitor market analyzer?
|
||||
"TrendCurrency": "Market", // Trend Currency to build the trend against. If set to "Fiat", the trend will
|
||||
// take the USD value of your main currency into account to build the trend.
|
||||
// "Market" will build a trend against your base currency, such as BTC or USDT.
|
||||
"TrendThreshold": 15, // Any coin that is above 15% or below -15% for this timeframe will be considered an outlier,
|
||||
// and not used when calculating the market average.
|
||||
"DisplayGraph": false, // Use this trend in the graph on the PTM Monitor dashboard and market analyzer
|
||||
"DisplayOnMarketAnalyzerList": false // Disply this trend for all coins on the PTM Monitor market analyzer
|
||||
},
|
||||
{
|
||||
"Name": "6h",
|
||||
|
@ -62,44 +64,60 @@
|
|||
"GlobalSettings": [ // Global settings for Profit Trailer properties
|
||||
//
|
||||
// ===================================================================================
|
||||
|
||||
// Each setting here is checked in order. If it is true, the analysis stops. If it is false, it moves on to check the next setting.
|
||||
// This way, you don't need to define ranges for each setting, just the minimums or maximums.
|
||||
|
||||
// -----------------------------
|
||||
{
|
||||
"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.
|
||||
//If you give each trigger a Tag, then you can use more robust boolean logic, such as: (A && B) || (B && C)
|
||||
"Triggers": [ // Your triggers for this setting. You can use any of your defined trends from above
|
||||
{
|
||||
"Tag": "A", // OPTIONAL: Give your triggers Tags, so you can use more robust boolean logic, such as: (A && B) || (C && D)
|
||||
"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. (Any value below "0" will trigger this)
|
||||
},
|
||||
{
|
||||
"Tag": "B",
|
||||
"MarketTrendName": "12h",
|
||||
"MaxChange": -2
|
||||
},
|
||||
{
|
||||
"Tag": "C",
|
||||
"MarketTrendName": "24h",
|
||||
"MaxChange": -5
|
||||
}
|
||||
],
|
||||
"PairsProperties": { // Changes you wish to make to your PAIRS.properties settings
|
||||
"PairsProperties": { // Properties for PAIRS.PROPERTIES
|
||||
// Any valid setting from https://wiki.profittrailer.com/en/config can be used here.
|
||||
// You can use a specific value, or apply a discrete OFFSET or OFFSETPERCENT to the value in your default PAIRS setting.
|
||||
"DEFAULT_sell_only_mode_enabled": true,
|
||||
"DEFAULT_trailing_profit_OFFSETPERCENT": -50
|
||||
},
|
||||
"DCAProperties": { // Changes you wish to make to your DCA.properties settings
|
||||
"DCAProperties": { // Properties for DCA.PROPERTIES
|
||||
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -75
|
||||
},
|
||||
"IndicatorsProperties": { // Changes you wish to make to your INDICATORS.properties settings
|
||||
}
|
||||
},
|
||||
// -----------------------------
|
||||
{
|
||||
"SettingName": "TankingDown",
|
||||
"TriggerConnection": "AND",
|
||||
"TriggerConnection": "AND", // You can use complex boolean logic for some settings, and not others
|
||||
"Triggers": [
|
||||
{
|
||||
"MarketTrendName": "1h",
|
||||
"MaxChange": 0,
|
||||
"MinChange": -5 // You can use Maxchange and Minchange together to create a range.
|
||||
},
|
||||
{
|
||||
"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": {
|
||||
|
@ -230,33 +248,50 @@
|
|||
}
|
||||
}
|
||||
],
|
||||
//
|
||||
// ================================ COIN-SPECIFIC SETTINGS ================================
|
||||
//
|
||||
"SingleMarketSettings": [ // Single market/pair settings for Profit Trailer properties
|
||||
// Any setting from https://wiki.profittrailer.com/en/config marked as COIN (coin-specific) can be used here.
|
||||
// Only coins that meet the triggered conditions will have the settings applied.
|
||||
// A variety of SMS can be employed to check for long-term down trends, sideways trends, over-extended uptrends, etc.
|
||||
// If more than one SMS is true, the settings of the last applied SMS over-rides any prior SMS
|
||||
// If StopProcessWhenTriggered is false, as the analyzer goes down the list multiple settings
|
||||
// can be applied to a single coin. This can allow for more complex logic and settings.
|
||||
// However, if two settings apply the same property, the property from the last setting
|
||||
// on the list will be the one that is used.
|
||||
{
|
||||
"SettingName": "PumpNDumpProtection",
|
||||
"TriggerConnection": "OR",
|
||||
//"StopProcessWhenTriggered": true, // No SMS after this will be analyzed or applied if this SMS is true
|
||||
//"AllowedGlobalSettings": "Default", // You can specify that this setting will only apply when a specific Global setting is active
|
||||
//"IgnoredGlobalSettings": "Default", // You can specify that this setting will NOT apply when a specific Global setting is active
|
||||
"SettingName": "BlacklistCoins",
|
||||
"StopProcessWhenTriggered": true,
|
||||
"TriggerConnection": "OR", // Just like Global Settings, you can use complex boolean logic for some settings, and not others
|
||||
"Triggers": [
|
||||
{
|
||||
"AgeDaysLowerThan": 21
|
||||
}
|
||||
],
|
||||
"PairsProperties": {
|
||||
"DEFAULT_trading_enabled": false, // Any setting from PT that begins with DEFAULT_ can be used here.
|
||||
"DEFAULT_sell_only_mode_enabled": true,
|
||||
"DEFAULT_DCA_enabled": false
|
||||
}
|
||||
},
|
||||
// -----------------------------
|
||||
{
|
||||
"SettingName": "PumpNDumpProtection",
|
||||
"TriggerConnection": "A || B || C",
|
||||
"Triggers": [
|
||||
{
|
||||
"Tag": "A",
|
||||
"MarketTrendName": "1h",
|
||||
"MarketTrendRelation": "Relative", // Relative = The single market trend is compared to the overall trend of the entire market
|
||||
// Absolute = The Single market trend is considered on its own
|
||||
"MarketTrendRelation": "Relative", // The relation of the single market trend. Relative = The trend of the coin market
|
||||
// is compared to the average trend of all other coins in the market market.
|
||||
// Absolute = Single market trend is considered on it's own, without reference to the market.
|
||||
"MinChange": 8
|
||||
},
|
||||
{
|
||||
"Tag": "B",
|
||||
"MarketTrendName": "12h",
|
||||
"MarketTrendRelation": "Relative",
|
||||
"MinChange": 10
|
||||
},
|
||||
{
|
||||
"Tag": "C",
|
||||
"MarketTrendName": "24h",
|
||||
"MarketTrendRelation": "Relative",
|
||||
"MinChange": 12
|
||||
|
@ -269,10 +304,11 @@
|
|||
}
|
||||
],
|
||||
"PairsProperties": {
|
||||
"DEFAULT_sell_only_mode_enabled": "true",
|
||||
"DEFAULT_DCA_enabled": "false"
|
||||
"DEFAULT_sell_only_mode_enabled": true,
|
||||
"DEFAULT_DCA_enabled": false
|
||||
}
|
||||
},
|
||||
// -----------------------------
|
||||
{
|
||||
"SettingName": "FreefallBlock",
|
||||
"TriggerConnection": "OR",
|
||||
|
@ -289,8 +325,8 @@
|
|||
}
|
||||
],
|
||||
"PairsProperties": {
|
||||
"DEFAULT_sell_only_mode_enabled": "true",
|
||||
"DEFAULT_DCA_enabled": "false"
|
||||
"DEFAULT_sell_only_mode_enabled": true,
|
||||
"DEFAULT_DCA_enabled": false
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -2,33 +2,44 @@
|
|||
"GeneralSettings": {
|
||||
"Application": {
|
||||
"IsEnabled": true, // Enables the PTMagic bot (needs restart to take effect)
|
||||
"TestMode": false, // If TestMode is active, no properties files will be changed
|
||||
"TestMode": true, // If TestMode is active, no properties files will be changed
|
||||
"ProfitTrailerLicense": "ptlicense1asdf234fljlasdf014325ehm", // Your Profit Trailer license key (needed to change your settings)
|
||||
"ProfitTrailerLicenseXtra": "", // Licenses for additional bots for PTM to update (optional - comma separated list)
|
||||
"ProfitTrailerServerAPIToken": "", //Your Profit Trailer Server API Token
|
||||
"ProfitTrailerMonitorURL": "http://localhost:8081/", // The URL to your profit trailer monitor (needed to change your settings)
|
||||
"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
|
||||
"TimezoneOffset": "+0:00", // Your timezone offset from UTC time
|
||||
"FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute
|
||||
"InstanceName": "PT Magic", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages. In case you are running more than one bot, you may set different names to separate them
|
||||
//"FreeCurrencyConverterAPIKey": "" // If "MainFiatCurrency" above is anything other than USD, you must obtain an API key from https://free.currencyconverterapi.com/free-api-key
|
||||
"Exchange": "BinanceFutures", // The exchange your are running Profit Trailer on
|
||||
"FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from
|
||||
// getting switched back and forth every minute
|
||||
"InstanceName": "MyBTCbot", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages.
|
||||
//In case you are running more than one bot, you may set different names to separate them
|
||||
"CoinMarketCapAPIKey": "", //CoinMarketCap Api
|
||||
},
|
||||
"Monitor": {
|
||||
"IsPasswordProtected": true, // Defines if your monitor will be asking to setup a password on its first start
|
||||
"OpenBrowserOnStart": false, // If active, a browser window will open as soon as you start the monitor
|
||||
"Port": 8080, // The port you want to run your monitor on
|
||||
"RootUrl": "/", // The root Url of your monitor
|
||||
"AnalyzerChart": "", // By default the chart on the Market Analyzer page will use your default currency against USD. You can change that here. (eg., BTCEUR)
|
||||
"OpenBrowserOnStart": true, // If active, a browser window will open as soon as you start the monitor
|
||||
"Port": 8080, // The port you want to run your PTMagic monitor on, to connect via browser. The url will be your IP:Port or localhost:Port
|
||||
"AnalyzerChart": "", // By default the chart on the Market Analyzer page will use your market currency against USD.
|
||||
//You can change that here. (eg., BTCEUR)
|
||||
"LiveTCVTimeframeMinutes": 10, // The timeframe for the live TCV chart on the dashboard
|
||||
"GraphIntervalMinutes": 60, // The interval for the monitor market trend graph to draw points in minutes
|
||||
"GraphMaxTimeframeHours": 24, // This will enable you to define the timeframe that your graph for market trends covers in hours
|
||||
"ProfitsMaxTimeframeDays": 30, // This will enable you to define the timeframe for your dashboard profits graph in days
|
||||
"RefreshSeconds": 30, // The refresh interval of your monitor main page
|
||||
"LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it
|
||||
"DashboardChartsRefreshSeconds": 30, // The refresh interval of your dashboard charts in seconds
|
||||
"BagAnalyzerRefreshSeconds": 60,
|
||||
"BuyAnalyzerRefreshSeconds": 60,
|
||||
"MaxDashboardBuyEntries": 5, // The number of coins in your Possible Buy List on the dashboard. Set to 0 to hide the list completely
|
||||
"MaxDashboardBagEntries": 9999, // The number of coins in your Positions List on the dashboard.
|
||||
"MaxTopMarkets": 20, // The amount of top markets being shown in your Sales Analyzer
|
||||
"MaxDailySummaries": 10, // The amount of "Last Days" being shown in your Sales Analyzer
|
||||
"MaxDCAPairs": 25, // For DCA calculations in the DCA Analyzer.
|
||||
"DefaultDCAMode": "Advanced", // The default DCA mode to use in the DCA Analyzer. Options are "Simple" or "Advanced"
|
||||
"MaxSettingsLogEntries": 500, // The number of entries in the Global Settings Log on the Status & Summary page
|
||||
"MaxMonthlySummaries": 10, // The amount of "Last Months" being shown in your Sales Analyzer
|
||||
"LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it
|
||||
"TVCustomLayout": "EbSR85R8", // A TradingView layout to use when clicking on a pair name while using TradingView as your platform
|
||||
// When saving a custom layout in TV, you will get a URL like this: https://www.tradingview.com/chart/EbSR85R8/
|
||||
"TvStudyA": "BB@tv-basicstudies", // See available STUDIES at https://www.tradingview.com/wiki/Widget:TradingView_Widget
|
||||
"TvStudyB": "",
|
||||
"TvStudyC": "",
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//
|
||||
// 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, and settings.
|
||||
//
|
||||
// You should take your time and adjust these settings according to your own personal preferences.
|
||||
// Always test your PTMagic settings by running a Profit Trailer bot in TESTMODE, to make sure
|
||||
// it is performing as you expect.
|
||||
//
|
||||
|
@ -13,17 +12,20 @@
|
|||
"MarketAnalyzer": {
|
||||
"StoreDataMaxHours": 48, // Number of hours to store market data
|
||||
"IntervalMinutes": 2, // Interval in minutes for PTMagic to check market trends and triggers
|
||||
"ExcludeMainCurrency": true, // Excludes the main currency (for example BTC) from market trend analysis
|
||||
"ExcludeMainCurrency": true, // Excludes the main currency (for example BTC, if you are trading against BTC) from market trend analysis
|
||||
"MarketTrends": [
|
||||
{
|
||||
"Name": "1h", // UNIQUE market trend name (to be referenced by your triggers below)
|
||||
"Platform": "Exchange", // Platform to grab prices from (Allowed values are: CoinMarketCap, Exchange)
|
||||
"MaxMarkets": 50, // Number of markets/pairs to analyze sorted by 24h volume
|
||||
"TrendMinutes": 60, // Number of minutes to build a trend (1440 = 24h, 720 = 12h, 60 = 1h)
|
||||
"TrendCurrency": "Market", // Trend Currency to build the trend against. If set to "Fiat", the trend will take the USD value of your main currency into account to build the trend. "Market" will build a trend against your base currency, such as BTC or USDT.
|
||||
"TrendThreshold": 15, // Any coin that is above 15% or below -15% for this timeframe will not be used when calculating the market average.
|
||||
"DisplayGraph": false, // Use this trend in the graph on the PTM Monitor dashboard and market analyzer?
|
||||
"DisplayOnMarketAnalyzerList": false // Disply this trend on the PTM Monitor market analyzer?
|
||||
"TrendCurrency": "Market", // Trend Currency to build the trend against. If set to "Fiat", the trend will
|
||||
// take the USD value of your main currency into account to build the trend.
|
||||
// "Market" will build a trend against your base currency, such as BTC or USDT.
|
||||
"TrendThreshold": 15, // Any coin that is above 15% or below -15% for this timeframe will be considered an outlier,
|
||||
// and not used when calculating the market average.
|
||||
"DisplayGraph": false, // Use this trend in the graph on the PTM Monitor dashboard and market analyzer
|
||||
"DisplayOnMarketAnalyzerList": false // Disply this trend for all coins on the PTM Monitor market analyzer
|
||||
},
|
||||
{
|
||||
"Name": "6h",
|
||||
|
@ -62,44 +64,60 @@
|
|||
"GlobalSettings": [ // Global settings for Profit Trailer properties
|
||||
//
|
||||
// ===================================================================================
|
||||
|
||||
// Each setting here is checked in order. If it is true, the analysis stops. If it is false, it moves on to check the next setting.
|
||||
// This way, you don't need to define ranges for each setting, just the minimums or maximums.
|
||||
|
||||
// -----------------------------
|
||||
{
|
||||
"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.
|
||||
//If you give each trigger a Tag, then you can use more robust boolean logic, such as: (A && B) || (B && C)
|
||||
"Triggers": [ // Your triggers for this setting. You can use any of your defined trends from above
|
||||
{
|
||||
"Tag": "A", // OPTIONAL: Give your triggers Tags, so you can use more robust boolean logic, such as: (A && B) || (C && D)
|
||||
"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. (Any value below "0" will trigger this)
|
||||
},
|
||||
{
|
||||
"Tag": "B",
|
||||
"MarketTrendName": "12h",
|
||||
"MaxChange": -2
|
||||
},
|
||||
{
|
||||
"Tag": "C",
|
||||
"MarketTrendName": "24h",
|
||||
"MaxChange": -5
|
||||
}
|
||||
],
|
||||
"PairsProperties": { // Changes you wish to make to your PAIRS.properties settings
|
||||
"PairsProperties": { // Properties for PAIRS.PROPERTIES
|
||||
// Any valid setting from https://wiki.profittrailer.com/en/config can be used here.
|
||||
// You can use a specific value, or apply a discrete OFFSET or OFFSETPERCENT to the value in your default PAIRS setting.
|
||||
"DEFAULT_sell_only_mode_enabled": true,
|
||||
"DEFAULT_trailing_profit_OFFSETPERCENT": -50
|
||||
},
|
||||
"DCAProperties": { // Changes you wish to make to your DCA.properties settings
|
||||
"DCAProperties": { // Properties for DCA.PROPERTIES
|
||||
"DEFAULT_DCA_trailing_profit_OFFSETPERCENT": -75
|
||||
},
|
||||
"IndicatorsProperties": { // Changes you wish to make to your INDICATORS.properties settings
|
||||
}
|
||||
},
|
||||
// -----------------------------
|
||||
{
|
||||
"SettingName": "TankingDown",
|
||||
"TriggerConnection": "AND",
|
||||
"TriggerConnection": "AND", // You can use complex boolean logic for some settings, and not others
|
||||
"Triggers": [
|
||||
{
|
||||
"MarketTrendName": "1h",
|
||||
"MaxChange": 0,
|
||||
"MinChange": -5 // You can use Maxchange and Minchange together to create a range.
|
||||
},
|
||||
{
|
||||
"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": {
|
||||
|
@ -230,33 +248,50 @@
|
|||
}
|
||||
}
|
||||
],
|
||||
//
|
||||
// ================================ COIN-SPECIFIC SETTINGS ================================
|
||||
//
|
||||
"SingleMarketSettings": [ // Single market/pair settings for Profit Trailer properties
|
||||
// Any setting from https://wiki.profittrailer.com/en/config marked as COIN (coin-specific) can be used here.
|
||||
// Only coins that meet the triggered conditions will have the settings applied.
|
||||
// A variety of SMS can be employed to check for long-term down trends, sideways trends, over-extended uptrends, etc.
|
||||
// If more than one SMS is true, the settings of the last applied SMS over-rides any prior SMS
|
||||
// If StopProcessWhenTriggered is false, as the analyzer goes down the list multiple settings
|
||||
// can be applied to a single coin. This can allow for more complex logic and settings.
|
||||
// However, if two settings apply the same property, the property from the last setting
|
||||
// on the list will be the one that is used.
|
||||
{
|
||||
"SettingName": "PumpNDumpProtection",
|
||||
"TriggerConnection": "OR",
|
||||
//"StopProcessWhenTriggered": true, // No SMS after this will be analyzed or applied if this SMS is true
|
||||
//"AllowedGlobalSettings": "Default", // You can specify that this setting will only apply when a specific Global setting is active
|
||||
//"IgnoredGlobalSettings": "Default", // You can specify that this setting will NOT apply when a specific Global setting is active
|
||||
"SettingName": "BlacklistCoins",
|
||||
"StopProcessWhenTriggered": true,
|
||||
"TriggerConnection": "OR", // Just like Global Settings, you can use complex boolean logic for some settings, and not others
|
||||
"Triggers": [
|
||||
{
|
||||
"AgeDaysLowerThan": 21
|
||||
}
|
||||
],
|
||||
"PairsProperties": {
|
||||
"DEFAULT_trading_enabled": false, // Any setting from PT that begins with DEFAULT_ can be used here.
|
||||
"DEFAULT_sell_only_mode_enabled": true,
|
||||
"DEFAULT_DCA_enabled": false
|
||||
}
|
||||
},
|
||||
// -----------------------------
|
||||
{
|
||||
"SettingName": "PumpNDumpProtection",
|
||||
"TriggerConnection": "A || B || C",
|
||||
"Triggers": [
|
||||
{
|
||||
"Tag": "A",
|
||||
"MarketTrendName": "1h",
|
||||
"MarketTrendRelation": "Relative", // Relative = The single market trend is compared to the overall trend of the entire market
|
||||
// Absolute = The Single market trend is considered on its own
|
||||
"MarketTrendRelation": "Relative", // The relation of the single market trend. Relative = The trend of the coin market
|
||||
// is compared to the average trend of all other coins in the market market.
|
||||
// Absolute = Single market trend is considered on it's own, without reference to the market.
|
||||
"MinChange": 8
|
||||
},
|
||||
{
|
||||
"Tag": "B",
|
||||
"MarketTrendName": "12h",
|
||||
"MarketTrendRelation": "Relative",
|
||||
"MinChange": 10
|
||||
},
|
||||
{
|
||||
"Tag": "C",
|
||||
"MarketTrendName": "24h",
|
||||
"MarketTrendRelation": "Relative",
|
||||
"MinChange": 12
|
||||
|
@ -269,10 +304,11 @@
|
|||
}
|
||||
],
|
||||
"PairsProperties": {
|
||||
"DEFAULT_sell_only_mode_enabled": "true",
|
||||
"DEFAULT_DCA_enabled": "false"
|
||||
"DEFAULT_sell_only_mode_enabled": true,
|
||||
"DEFAULT_DCA_enabled": false
|
||||
}
|
||||
},
|
||||
// -----------------------------
|
||||
{
|
||||
"SettingName": "FreefallBlock",
|
||||
"TriggerConnection": "OR",
|
||||
|
@ -289,8 +325,8 @@
|
|||
}
|
||||
],
|
||||
"PairsProperties": {
|
||||
"DEFAULT_sell_only_mode_enabled": "true",
|
||||
"DEFAULT_DCA_enabled": "false"
|
||||
"DEFAULT_sell_only_mode_enabled": true,
|
||||
"DEFAULT_DCA_enabled": false
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -2,33 +2,44 @@
|
|||
"GeneralSettings": {
|
||||
"Application": {
|
||||
"IsEnabled": true, // Enables the PTMagic bot (needs restart to take effect)
|
||||
"TestMode": false, // If TestMode is active, no properties files will be changed
|
||||
"TestMode": true, // If TestMode is active, no properties files will be changed
|
||||
"ProfitTrailerLicense": "ptlicense1asdf234fljlasdf014325ehm", // Your Profit Trailer license key (needed to change your settings)
|
||||
"ProfitTrailerLicenseXtra": "", // Licenses for additional bots for PTM to update (optional - comma separated list)
|
||||
"ProfitTrailerServerAPIToken": "", //Your Profit Trailer Server API Token
|
||||
"ProfitTrailerMonitorURL": "http://localhost:8081/", // The URL to your profit trailer monitor (needed to change your settings)
|
||||
"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
|
||||
"TimezoneOffset": "+0:00", // Your timezone offset from UTC time
|
||||
"FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from getting switched back and forth every minute
|
||||
"InstanceName": "PT Magic", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages. In case you are running more than one bot, you may set different names to separate them
|
||||
"Exchange": "BinanceFutures", // The exchange your are running Profit Trailer on
|
||||
"FloodProtectionMinutes": 0, // If a price trend is just zig-zagging around its trigger, you may want to protect your settings from
|
||||
// getting switched back and forth every minute
|
||||
"InstanceName": "MyBTCbot", // The name of the instance of this bot. This will be used in your monitor and your Telegram messages.
|
||||
//In case you are running more than one bot, you may set different names to separate them
|
||||
"CoinMarketCapAPIKey": "", //CoinMarketCap Api
|
||||
},
|
||||
"Monitor": {
|
||||
"IsPasswordProtected": true, // Defines if your monitor will be asking to setup a password on its first start
|
||||
"OpenBrowserOnStart": false, // If active, a browser window will open as soon as you start the monitor
|
||||
"Port": 8080, // The port you want to run your monitor on
|
||||
"RootUrl": "/", // The root Url of your monitor
|
||||
"AnalyzerChart": "", // By default the chart on the Market Analyzer page will use your default currency against USD. You can change that here. (eg., BTCEUR)
|
||||
"OpenBrowserOnStart": true, // If active, a browser window will open as soon as you start the monitor
|
||||
"Port": 8080, // The port you want to run your PTMagic monitor on, to connect via browser. The url will be your IP:Port or localhost:Port
|
||||
"AnalyzerChart": "", // By default the chart on the Market Analyzer page will use your market currency against USD.
|
||||
//You can change that here. (eg., BTCEUR)
|
||||
"LiveTCVTimeframeMinutes": 10, // The timeframe for the live TCV chart on the dashboard
|
||||
"GraphIntervalMinutes": 60, // The interval for the monitor market trend graph to draw points in minutes
|
||||
"GraphMaxTimeframeHours": 24, // This will enable you to define the timeframe that your graph for market trends covers in hours
|
||||
"ProfitsMaxTimeframeDays": 30, // This will enable you to define the timeframe for your dashboard profits graph in days
|
||||
"RefreshSeconds": 30, // The refresh interval of your monitor main page
|
||||
"LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it
|
||||
"DashboardChartsRefreshSeconds": 30, // The refresh interval of your dashboard charts in seconds
|
||||
"BagAnalyzerRefreshSeconds": 60,
|
||||
"BuyAnalyzerRefreshSeconds": 60,
|
||||
"MaxDashboardBuyEntries": 5, // The number of coins in your Possible Buy List on the dashboard. Set to 0 to hide the list completely
|
||||
"MaxDashboardBagEntries": 9999, // The number of coins in your Positions List on the dashboard.
|
||||
"MaxTopMarkets": 20, // The amount of top markets being shown in your Sales Analyzer
|
||||
"MaxDailySummaries": 10, // The amount of "Last Days" being shown in your Sales Analyzer
|
||||
"MaxDCAPairs": 25, // For DCA calculations in the DCA Analyzer.
|
||||
"DefaultDCAMode": "Advanced", // The default DCA mode to use in the DCA Analyzer. Options are "Simple" or "Advanced"
|
||||
"MaxSettingsLogEntries": 500, // The number of entries in the Global Settings Log on the Status & Summary page
|
||||
"MaxMonthlySummaries": 10, // The amount of "Last Months" being shown in your Sales Analyzer
|
||||
"LinkPlatform": "TradingView", // The platform to which the pair name will link if you click on it
|
||||
"TVCustomLayout": "EbSR85R8", // A TradingView layout to use when clicking on a pair name while using TradingView as your platform
|
||||
// When saving a custom layout in TV, you will get a URL like this: https://www.tradingview.com/chart/EbSR85R8/
|
||||
"TvStudyA": "BB@tv-basicstudies", // See available STUDIES at https://www.tradingview.com/wiki/Widget:TradingView_Widget
|
||||
"TvStudyB": "",
|
||||
"TvStudyC": "",
|
||||
|
|
Loading…
Reference in New Issue