Merge pull request #216 from PTMagicians/develop

Develop
This commit is contained in:
HojouFotytu 2020-08-07 01:48:33 +09:00 committed by GitHub
commit d78fe1e7a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 6982 additions and 4316 deletions

View File

@ -361,6 +361,19 @@ namespace Core.Main.DataObjects.PTMagicData
}
#endregion
#region Properties Objects
public class Properties
{
public string Currency { get; set; } = "";
public bool Shorting { get; set; } = false;
public bool Margin { get; set; } = false;
public string UpTime { get; set; } = "";
public int Port { get; set; } = 0;
public bool IsLeverageExchange { get; set; } = false;
public string BaseUrl { get; set; } = "";
}
#endregion
#region Transaction Objects
public class Transaction
{
@ -456,6 +469,7 @@ namespace Core.Main.DataObjects.PTMagicData
public double AverageBuyPrice { get; set; }
public double TotalCost { get; set; }
public double CurrentValue { get; set; }
public double? TargetGainValue { get; set; }
public double Amount { get; set; }
public double CurrentPrice { get; set; }
public double SellTrigger { get; set; }
@ -463,6 +477,7 @@ namespace Core.Main.DataObjects.PTMagicData
public DateTime FirstBoughtDate { get; set; }
public string SellStrategy { get; set; }
public string BuyStrategy { get; set; }
public double Leverage { get; set; }
public List<Strategy> BuyStrategies { get; set; } = new List<Strategy>();
public List<Strategy> SellStrategies { get; set; } = new List<Strategy>();
}
@ -484,6 +499,7 @@ namespace Core.Main.DataObjects.PTMagicData
public double CurrentPrice { get; set; }
public int BoughtTimes { get; set; }
public double PercChange { get; set; }
public double Volume24h { get; set; }
public List<Strategy> BuyStrategies { get; set; } = new List<Strategy>();
}
@ -497,5 +513,6 @@ namespace Core.Main.DataObjects.PTMagicData
public double DustValue { get; set; }
public string Market { get; set; }
}
#endregion
}

View File

@ -15,14 +15,15 @@ namespace Core.Main.DataObjects
public class ProfitTrailerData
{
private SummaryData _summary = null;
private Properties _properties = null;
private List<SellLogData> _sellLog = new List<SellLogData>();
private List<DCALogData> _dcaLog = new List<DCALogData>();
private List<BuyLogData> _buyLog = new List<BuyLogData>();
private string _ptmBasePath = "";
private PTMagicConfiguration _systemConfiguration = null;
private TransactionData _transactionData = null;
private DateTime _buyLogRefresh = DateTime.UtcNow, _sellLogRefresh = DateTime.UtcNow, _dcaLogRefresh = DateTime.UtcNow, _summaryRefresh = DateTime.UtcNow;
private volatile object _buyLock = new object(), _sellLock = new object(), _dcaLock = new object(), _summaryLock = new object();
private DateTime _buyLogRefresh = DateTime.UtcNow, _sellLogRefresh = DateTime.UtcNow, _dcaLogRefresh = DateTime.UtcNow, _summaryRefresh = DateTime.UtcNow, _propertiesRefresh = DateTime.UtcNow;
private volatile object _buyLock = new object(), _sellLock = new object(), _dcaLock = new object(), _summaryLock = new object(), _propertiesLock = new object();
private TimeSpan? _offsetTimeSpan = null;
// Constructor
@ -75,6 +76,26 @@ namespace Core.Main.DataObjects
return _summary;
}
}
public Properties Properties
{
get
{
if (_properties == null || (DateTime.UtcNow > _propertiesRefresh))
{
lock (_propertiesLock)
{
// Thread double locking
if (_properties == null || (DateTime.UtcNow > _propertiesRefresh))
{
_properties = BuildProptertiesData(GetDataFromProfitTrailer("api/v2/data/properties"));
_propertiesRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
}
}
}
return _properties;
}
}
public List<SellLogData> SellLog
{
get
@ -205,11 +226,7 @@ namespace Core.Main.DataObjects
public double GetCurrentBalance()
{
return
(this.Summary.Balance +
this.Summary.PairsValue +
this.Summary.DCAValue +
this.Summary.PendingValue +
this.Summary.DustValue);
(this.Summary.Balance);
}
public double GetPairsBalance()
{
@ -290,7 +307,19 @@ namespace Core.Main.DataObjects
DustValue = PTData.totalDustCurrentValue
};
}
private Properties BuildProptertiesData(dynamic PTProperties)
{
return new Properties()
{
Currency = PTProperties.currency,
Shorting = PTProperties.shorting,
Margin = PTProperties.margin,
UpTime = PTProperties.upTime,
Port = PTProperties.port,
IsLeverageExchange = PTProperties.isLeverageExchange,
BaseUrl = PTProperties.baseUrl
};
}
private void BuildSellLogData(dynamic rawSellLogData)
{
foreach (var rsld in rawSellLogData.data)
@ -304,21 +333,13 @@ namespace Core.Main.DataObjects
sellLogData.AverageBuyPrice = rsld.avgPrice;
sellLogData.TotalCost = sellLogData.SoldAmount * sellLogData.AverageBuyPrice;
// check if sale was a short position
if ((sellLogData.ProfitPercent > 0) && (sellLogData.AverageBuyPrice > sellLogData.SoldPrice))
{
double soldValueRaw = (sellLogData.SoldAmount * sellLogData.SoldPrice);
double soldValueAfterFees = soldValueRaw + (soldValueRaw * ((double)rsld.fee / 100));
sellLogData.SoldValue = soldValueAfterFees;
sellLogData.Profit = Math.Abs(Math.Round(sellLogData.SoldValue - sellLogData.TotalCost, 8));
}
else
{
double soldValueRaw = (sellLogData.SoldAmount * sellLogData.SoldPrice);
double soldValueAfterFees = soldValueRaw - (soldValueRaw * ((double)rsld.fee / 100));
sellLogData.SoldValue = soldValueAfterFees;
sellLogData.Profit = Math.Round(sellLogData.SoldValue - sellLogData.TotalCost, 8);
}
// check if bot is a shortbot via PT API. Losses on short bot currently showing as gains. Issue #195
// code removed
double soldValueRaw = (sellLogData.SoldAmount * sellLogData.SoldPrice);
double soldValueAfterFees = soldValueRaw - (soldValueRaw * ((double)rsld.fee / 100));
sellLogData.SoldValue = soldValueAfterFees;
sellLogData.Profit = Math.Round(sellLogData.SoldValue - sellLogData.TotalCost, 8);
//Convert Unix Timestamp to Datetime
System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc);
@ -338,69 +359,92 @@ namespace Core.Main.DataObjects
private void BuildDCALogData(dynamic rawDCALogData, dynamic rawPairsLogData, dynamic rawPendingLogData, dynamic rawWatchModeLogData)
{
foreach (var rdld in rawDCALogData)
// Parse DCA data
_dcaLog.AddRange(ParsePairsData(rawDCALogData, true));
// Parse Pairs data
_dcaLog.AddRange(ParsePairsData(rawPairsLogData, false));
// Parse pending pairs data
_dcaLog.AddRange(ParsePairsData(rawPendingLogData, false));
// Parse watch only pairs data
_dcaLog.AddRange(ParsePairsData(rawWatchModeLogData, false));
}
// Parse the pairs data from PT to our own common data structure.
private List<DCALogData> ParsePairsData(dynamic pairsData, bool processBuyStrategies)
{
List<DCALogData> pairs = new List<DCALogData>();
foreach (var pair in pairsData)
{
DCALogData dcaLogData = new DCALogData();
dcaLogData.Amount = rdld.totalAmount;
dcaLogData.BoughtTimes = rdld.boughtTimes;
dcaLogData.Market = rdld.market;
dcaLogData.ProfitPercent = rdld.profit;
dcaLogData.AverageBuyPrice = rdld.avgPrice;
dcaLogData.TotalCost = rdld.totalCost;
dcaLogData.BuyTriggerPercent = rdld.buyProfit;
dcaLogData.CurrentLowBBValue = rdld.bbLow == null ? 0 : rdld.bbLow;
dcaLogData.CurrentHighBBValue = rdld.highBb == null ? 0 : rdld.highBb;
dcaLogData.BBTrigger = rdld.bbTrigger == null ? 0 : rdld.bbTrigger;
dcaLogData.CurrentPrice = rdld.currentPrice;
dcaLogData.SellTrigger = rdld.triggerValue == null ? 0 : rdld.triggerValue;
dcaLogData.PercChange = rdld.percChange;
dcaLogData.BuyStrategy = rdld.buyStrategy == null ? "" : rdld.buyStrategy;
dcaLogData.SellStrategy = rdld.sellStrategy == null ? "" : rdld.sellStrategy;
dcaLogData.Amount = pair.totalAmount;
dcaLogData.BoughtTimes = pair.boughtTimes;
dcaLogData.Market = pair.market;
dcaLogData.ProfitPercent = pair.profit;
dcaLogData.AverageBuyPrice = pair.avgPrice;
dcaLogData.TotalCost = pair.totalCost;
dcaLogData.BuyTriggerPercent = pair.buyProfit;
dcaLogData.CurrentLowBBValue = pair.bbLow == null ? 0 : pair.bbLow;
dcaLogData.CurrentHighBBValue = pair.highBb == null ? 0 : pair.highBb;
dcaLogData.BBTrigger = pair.bbTrigger == null ? 0 : pair.bbTrigger;
dcaLogData.CurrentPrice = pair.currentPrice;
dcaLogData.SellTrigger = pair.triggerValue == null ? 0 : pair.triggerValue;
dcaLogData.PercChange = pair.percChange;
dcaLogData.Leverage = pair.leverage == null ? 0 : pair.leverage;
dcaLogData.BuyStrategy = pair.buyStrategy == null ? "" : pair.buyStrategy;
dcaLogData.SellStrategy = pair.sellStrategy == null ? "" : pair.sellStrategy;
dcaLogData.IsTrailing = false;
if (rdld.positive != null)
if (pair.buyStrategies != null && processBuyStrategies)
{
dcaLogData.IsTrailing = ((string)rdld.positive).IndexOf("trailing", StringComparison.InvariantCultureIgnoreCase) > -1;
dcaLogData.IsTrue = ((string)rdld.positive).IndexOf("true", StringComparison.InvariantCultureIgnoreCase) > -1;
}
else
{
if (rdld.buyStrategies != null)
foreach (var bs in pair.buyStrategies)
{
foreach (var bs in rdld.buyStrategies)
{
Strategy buyStrategy = new Strategy();
buyStrategy.Type = bs.type;
buyStrategy.Name = bs.name;
buyStrategy.EntryValue = bs.entryValue;
buyStrategy.EntryValueLimit = bs.entryValueLimit;
buyStrategy.TriggerValue = bs.triggerValue;
buyStrategy.CurrentValue = bs.currentValue;
buyStrategy.CurrentValuePercentage = bs.currentValuePercentage;
buyStrategy.Decimals = bs.decimals;
buyStrategy.IsTrailing = ((string)bs.positive).IndexOf("trailing", StringComparison.InvariantCultureIgnoreCase) > -1;
buyStrategy.IsTrue = ((string)bs.positive).IndexOf("true", StringComparison.InvariantCultureIgnoreCase) > -1;
Strategy buyStrategy = new Strategy();
buyStrategy.Type = bs.type;
buyStrategy.Name = bs.name;
buyStrategy.EntryValue = bs.entryValue;
buyStrategy.EntryValueLimit = bs.entryValueLimit;
buyStrategy.TriggerValue = bs.triggerValue;
buyStrategy.CurrentValue = bs.currentValue;
buyStrategy.CurrentValuePercentage = bs.currentValuePercentage;
buyStrategy.Decimals = bs.decimals;
buyStrategy.IsTrailing = ((string)bs.positive).IndexOf("trailing", StringComparison.InvariantCultureIgnoreCase) > -1;
buyStrategy.IsTrue = ((string)bs.positive).IndexOf("true", StringComparison.InvariantCultureIgnoreCase) > -1;
dcaLogData.BuyStrategies.Add(buyStrategy);
}
dcaLogData.BuyStrategies.Add(buyStrategy);
}
}
if (rdld.sellStrategies != null)
if (pair.sellStrategies != null)
{
foreach (var ss in pair.sellStrategies)
{
foreach (var ss in rdld.sellStrategies)
{
Strategy sellStrategy = new Strategy();
sellStrategy.Type = ss.type;
sellStrategy.Name = ss.name;
sellStrategy.EntryValue = ss.entryValue;
sellStrategy.EntryValueLimit = ss.entryValueLimit;
sellStrategy.TriggerValue = ss.triggerValue;
sellStrategy.CurrentValue = ss.currentValue;
sellStrategy.CurrentValuePercentage = ss.currentValuePercentage;
sellStrategy.Decimals = ss.decimals;
sellStrategy.IsTrailing = ((string)ss.positive).IndexOf("trailing", StringComparison.InvariantCultureIgnoreCase) > -1;
sellStrategy.IsTrue = ((string)ss.positive).IndexOf("true", StringComparison.InvariantCultureIgnoreCase) > -1;
Strategy sellStrategy = new Strategy();
sellStrategy.Type = ss.type;
sellStrategy.Name = ss.name;
sellStrategy.EntryValue = ss.entryValue;
sellStrategy.EntryValueLimit = ss.entryValueLimit;
sellStrategy.TriggerValue = ss.triggerValue;
sellStrategy.CurrentValue = ss.currentValue;
sellStrategy.CurrentValuePercentage = ss.currentValuePercentage;
sellStrategy.Decimals = ss.decimals;
sellStrategy.IsTrailing = ((string)ss.positive).IndexOf("trailing", StringComparison.InvariantCultureIgnoreCase) > -1;
sellStrategy.IsTrue = ((string)ss.positive).IndexOf("true", StringComparison.InvariantCultureIgnoreCase) > -1;
dcaLogData.SellStrategies.Add(sellStrategy);
dcaLogData.SellStrategies.Add(sellStrategy);
// Find the target percentage gain to sell.
if (sellStrategy.Name.Contains("GAIN", StringComparison.InvariantCultureIgnoreCase))
{
if (!dcaLogData.TargetGainValue.HasValue || dcaLogData.TargetGainValue.Value > sellStrategy.EntryValue)
{
// Set the target sell percentage
dcaLogData.TargetGainValue = sellStrategy.EntryValue;
}
}
}
}
@ -408,12 +452,12 @@ namespace Core.Main.DataObjects
// Calculate current value
dcaLogData.CurrentValue = dcaLogData.CurrentPrice * dcaLogData.Amount;
//Convert Unix Timestamp to Datetime
// Convert Unix Timestamp to Datetime
System.DateTime rdldDateTime = new DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc);
rdldDateTime = rdldDateTime.AddSeconds((double)rdld.firstBoughtDate).ToUniversalTime();
rdldDateTime = rdldDateTime.AddSeconds((double)pair.firstBoughtDate).ToUniversalTime();
// Profit Trailer bought times are saved in UTC
if (rdld.firstBoughtDate > 0)
if (pair.firstBoughtDate > 0)
{
DateTimeOffset ptFirstBoughtDate = DateTimeOffset.Parse(rdldDateTime.Year.ToString() + "-" + rdldDateTime.Month.ToString("00") + "-" + rdldDateTime.Day.ToString("00") + "T" + rdldDateTime.Hour.ToString("00") + ":" + rdldDateTime.Minute.ToString("00") + ":" + rdldDateTime.Second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
@ -430,182 +474,7 @@ namespace Core.Main.DataObjects
_dcaLog.Add(dcaLogData);
}
foreach (var rpld in rawPairsLogData)
{
DCALogData dcaLogData = new DCALogData();
dcaLogData.Amount = rpld.totalAmount;
dcaLogData.BoughtTimes = 0;
dcaLogData.Market = rpld.market;
dcaLogData.ProfitPercent = rpld.profit;
dcaLogData.AverageBuyPrice = rpld.avgPrice;
dcaLogData.TotalCost = rpld.totalCost;
dcaLogData.BuyTriggerPercent = rpld.buyProfit;
dcaLogData.CurrentPrice = rpld.currentPrice;
dcaLogData.SellTrigger = rpld.triggerValue == null ? 0 : rpld.triggerValue;
dcaLogData.PercChange = rpld.percChange;
dcaLogData.BuyStrategy = rpld.buyStrategy == null ? "" : rpld.buyStrategy;
dcaLogData.SellStrategy = rpld.sellStrategy == null ? "" : rpld.sellStrategy;
dcaLogData.IsTrailing = false;
if (rpld.sellStrategies != null)
{
foreach (var ss in rpld.sellStrategies)
{
Strategy sellStrategy = new Strategy();
sellStrategy.Type = ss.type;
sellStrategy.Name = ss.name;
sellStrategy.EntryValue = ss.entryValue;
sellStrategy.EntryValueLimit = ss.entryValueLimit;
sellStrategy.TriggerValue = ss.triggerValue;
sellStrategy.CurrentValue = ss.currentValue;
sellStrategy.CurrentValuePercentage = ss.currentValuePercentage;
sellStrategy.Decimals = ss.decimals;
sellStrategy.IsTrailing = ((string)ss.positive).IndexOf("trailing", StringComparison.InvariantCultureIgnoreCase) > -1;
sellStrategy.IsTrue = ((string)ss.positive).IndexOf("true", StringComparison.InvariantCultureIgnoreCase) > -1;
dcaLogData.SellStrategies.Add(sellStrategy);
}
}
//Convert Unix Timestamp to Datetime
System.DateTime rpldDateTime = new DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc);
rpldDateTime = rpldDateTime.AddSeconds((double)rpld.firstBoughtDate).ToUniversalTime();
// Profit Trailer bought times are saved in UTC
if (rpld.firstBoughtDate > 0)
{
DateTimeOffset ptFirstBoughtDate = DateTimeOffset.Parse(rpldDateTime.Year.ToString() + "-" + rpldDateTime.Month.ToString("00") + "-" + rpldDateTime.Day.ToString("00") + "T" + rpldDateTime.Hour.ToString("00") + ":" + rpldDateTime.Minute.ToString("00") + ":" + rpldDateTime.Second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
// Convert UTC bought time to local offset time
ptFirstBoughtDate = ptFirstBoughtDate.ToOffset(OffsetTimeSpan);
dcaLogData.FirstBoughtDate = ptFirstBoughtDate.DateTime;
}
else
{
dcaLogData.FirstBoughtDate = Constants.confMinDate;
}
_dcaLog.Add(dcaLogData);
}
foreach (var rpld in rawPendingLogData)
{
DCALogData dcaLogData = new DCALogData();
dcaLogData.Amount = rpld.totalAmount;
dcaLogData.BoughtTimes = 0;
dcaLogData.Market = rpld.market;
dcaLogData.ProfitPercent = rpld.profit;
dcaLogData.AverageBuyPrice = rpld.avgPrice;
dcaLogData.TotalCost = rpld.totalCost;
dcaLogData.BuyTriggerPercent = rpld.buyProfit;
dcaLogData.CurrentPrice = rpld.currentPrice;
dcaLogData.SellTrigger = rpld.triggerValue == null ? 0 : rpld.triggerValue;
dcaLogData.PercChange = rpld.percChange;
dcaLogData.BuyStrategy = rpld.buyStrategy == null ? "" : rpld.buyStrategy;
dcaLogData.SellStrategy = rpld.sellStrategy == null ? "" : rpld.sellStrategy;
dcaLogData.IsTrailing = false;
if (rpld.sellStrategies != null)
{
foreach (var ss in rpld.sellStrategies)
{
Strategy sellStrategy = new Strategy();
sellStrategy.Type = ss.type;
sellStrategy.Name = ss.name;
sellStrategy.EntryValue = ss.entryValue;
sellStrategy.EntryValueLimit = ss.entryValueLimit;
sellStrategy.TriggerValue = ss.triggerValue;
sellStrategy.CurrentValue = ss.currentValue;
sellStrategy.CurrentValuePercentage = ss.currentValuePercentage;
sellStrategy.Decimals = ss.decimals;
sellStrategy.IsTrailing = ((string)ss.positive).IndexOf("trailing", StringComparison.InvariantCultureIgnoreCase) > -1;
sellStrategy.IsTrue = ((string)ss.positive).IndexOf("true", StringComparison.InvariantCultureIgnoreCase) > -1;
dcaLogData.SellStrategies.Add(sellStrategy);
}
}
//Convert Unix Timestamp to Datetime
System.DateTime rpldDateTime = new DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc);
rpldDateTime = rpldDateTime.AddSeconds((double)rpld.firstBoughtDate).ToUniversalTime();
// Profit Trailer bought times are saved in UTC
if (rpld.firstBoughtDate > 0)
{
DateTimeOffset ptFirstBoughtDate = DateTimeOffset.Parse(rpldDateTime.Year.ToString() + "-" + rpldDateTime.Month.ToString("00") + "-" + rpldDateTime.Day.ToString("00") + "T" + rpldDateTime.Hour.ToString("00") + ":" + rpldDateTime.Minute.ToString("00") + ":" + rpldDateTime.Second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
// Convert UTC bought time to local offset time
ptFirstBoughtDate = ptFirstBoughtDate.ToOffset(OffsetTimeSpan);
dcaLogData.FirstBoughtDate = ptFirstBoughtDate.DateTime;
}
else
{
dcaLogData.FirstBoughtDate = Constants.confMinDate;
}
_dcaLog.Add(dcaLogData);
}
foreach (var rpld in rawWatchModeLogData)
{
DCALogData dcaLogData = new DCALogData();
dcaLogData.Amount = rpld.totalAmount;
dcaLogData.BoughtTimes = 0;
dcaLogData.Market = rpld.market;
dcaLogData.ProfitPercent = rpld.profit;
dcaLogData.AverageBuyPrice = rpld.avgPrice;
dcaLogData.TotalCost = rpld.totalCost;
dcaLogData.BuyTriggerPercent = rpld.buyProfit;
dcaLogData.CurrentPrice = rpld.currentPrice;
dcaLogData.SellTrigger = rpld.triggerValue == null ? 0 : rpld.triggerValue;
dcaLogData.PercChange = rpld.percChange;
dcaLogData.BuyStrategy = rpld.buyStrategy == null ? "" : rpld.buyStrategy;
dcaLogData.SellStrategy = rpld.sellStrategy == null ? "" : rpld.sellStrategy;
dcaLogData.IsTrailing = false;
if (rpld.sellStrategies != null)
{
foreach (var ss in rpld.sellStrategies)
{
Strategy sellStrategy = new Strategy();
sellStrategy.Type = ss.type;
sellStrategy.Name = ss.name;
sellStrategy.EntryValue = ss.entryValue;
sellStrategy.EntryValueLimit = ss.entryValueLimit;
sellStrategy.TriggerValue = ss.triggerValue;
sellStrategy.CurrentValue = ss.currentValue;
sellStrategy.CurrentValuePercentage = ss.currentValuePercentage;
sellStrategy.Decimals = ss.decimals;
sellStrategy.IsTrailing = ((string)ss.positive).IndexOf("trailing", StringComparison.InvariantCultureIgnoreCase) > -1;
sellStrategy.IsTrue = ((string)ss.positive).IndexOf("true", StringComparison.InvariantCultureIgnoreCase) > -1;
dcaLogData.SellStrategies.Add(sellStrategy);
}
}
//Convert Unix Timestamp to Datetime
System.DateTime rpldDateTime = new DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc);
rpldDateTime = rpldDateTime.AddSeconds((double)rpld.firstBoughtDate).ToUniversalTime();
// Profit Trailer bought times are saved in UTC
if (rpld.firstBoughtDate > 0)
{
DateTimeOffset ptFirstBoughtDate = DateTimeOffset.Parse(rpldDateTime.Year.ToString() + "-" + rpldDateTime.Month.ToString("00") + "-" + rpldDateTime.Day.ToString("00") + "T" + rpldDateTime.Hour.ToString("00") + ":" + rpldDateTime.Minute.ToString("00") + ":" + rpldDateTime.Second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
// Convert UTC bought time to local offset time
ptFirstBoughtDate = ptFirstBoughtDate.ToOffset(OffsetTimeSpan);
dcaLogData.FirstBoughtDate = ptFirstBoughtDate.DateTime;
}
else
{
dcaLogData.FirstBoughtDate = Constants.confMinDate;
}
_dcaLog.Add(dcaLogData);
}
return pairs;
}
private void BuildBuyLogData(dynamic rawBuyLogData)
@ -617,6 +486,7 @@ namespace Core.Main.DataObjects
buyLogData.ProfitPercent = rbld.profit;
buyLogData.CurrentPrice = rbld.currentPrice;
buyLogData.PercChange = rbld.percChange;
buyLogData.Volume24h = rbld.volume;
if (rbld.positive != null)
{

View File

@ -547,6 +547,19 @@ namespace Core.Helper
result += pairName;
}
else if (platform.Equals("TradingViewFutures"))
{
result = "https://www.tradingview.com/chart/?symbol=";
string pairName = SystemHelper.StripBadCode(market, Constants.WhiteListMinimal);
if (pairName.StartsWith(mainMarket))
{
pairName = pairName.Replace(mainMarket, "") + mainMarket;
}
result += pairName + "PERP";
}
else
{
switch (exchange)

View File

@ -2564,7 +2564,7 @@ namespace Core.Main
}
}
this.LastRuntimeSummary.MarketSummary.Add(marketPair, mpSummary);
this.LastRuntimeSummary.MarketSummary.TryAdd(marketPair, mpSummary);
}
this.Log.DoLogInfo("Summary: Current single market properties saved.");

View File

@ -20,27 +20,32 @@ namespace Core.MarketAnalyzer
public static double GetMainCurrencyPrice(string mainMarket, PTMagicConfiguration systemConfiguration, LogHelper log)
{
double result = 0;
try
if (mainMarket != "USD")
{
string baseUrl = "https://api.binance.us/api/v1/ticker/24hr?symbol=" + mainMarket + "USDT";
log.DoLogInfo("BinanceUS - Getting main market price...");
Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, null);
if (jsonObject != null)
try
{
log.DoLogInfo("BinanceUS - Market data received for " + mainMarket + "USDT");
string baseUrl = "https://api.binance.us/api/v1/ticker/24hr?symbol=" + mainMarket + "USDT";
result = (double)jsonObject.GetValue("lastPrice");
log.DoLogInfo("BinanceUS - Current price for " + mainMarket + "USDT: " + result.ToString("#,#0.00") + " USD");
log.DoLogInfo("BinanceUS - Getting main market price...");
Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, null);
if (jsonObject != null)
{
log.DoLogInfo("BinanceUS - Market data received for " + mainMarket + "USDT");
result = (double)jsonObject.GetValue("lastPrice");
log.DoLogInfo("BinanceUS - Current price for " + mainMarket + "USDT: " + result.ToString("#,#0.00") + " USD");
}
}
}
catch (Exception ex)
catch (Exception ex)
{
log.DoLogCritical(ex.Message, ex);
}
return result;
}
else
{
log.DoLogCritical(ex.Message, ex);
return 1.0;
}
return result;
}
public static List<string> GetMarketData(string mainMarket, Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
@ -58,7 +63,7 @@ namespace Core.MarketAnalyzer
if (jsonArray.Count > 0)
{
double mainCurrencyPrice = 1;
if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase))
if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase) && !mainMarket.Equals("USD", StringComparison.InvariantCultureIgnoreCase))
{
mainCurrencyPrice = BinanceUS.GetMainCurrencyPrice(mainMarket, systemConfiguration, log);
}
@ -238,40 +243,45 @@ namespace Core.MarketAnalyzer
}
bool go = true;
while (ticksFetched < ticksNeeded && go)
if (!(marketName == "USDUSDT"))
{
baseUrl = "https://api.binance.us/api/v1/klines?interval=1m&symbol=" + marketName + "&endTime=" + endTime.ToString() + "&limit=" + ticksLimit.ToString();
log.DoLogDebug("BinanceUS - Getting " + ticksLimit.ToString() + " ticks for '" + marketName + "'...");
Newtonsoft.Json.Linq.JArray jsonArray = GetSimpleJsonArrayFromURL(baseUrl, log);
if (jsonArray.Count > 0)
while (ticksFetched < ticksNeeded && go)
{
log.DoLogDebug("BinanceUS - " + jsonArray.Count.ToString() + " ticks received.");
baseUrl = "https://api.binance.us/api/v1/klines?interval=1m&symbol=" + marketName + "&endTime=" + endTime.ToString() + "&limit=" + ticksLimit.ToString();
foreach (Newtonsoft.Json.Linq.JArray marketTick in jsonArray)
log.DoLogDebug("BinanceUS - Getting " + ticksLimit.ToString() + " ticks for '" + marketName + "'...");
Newtonsoft.Json.Linq.JArray jsonArray = GetSimpleJsonArrayFromURL(baseUrl, log);
if (jsonArray.Count > 0)
{
log.DoLogDebug("BinanceUS - " + jsonArray.Count.ToString() + " ticks received.");
MarketTick tick = new MarketTick();
tick.Price = (double)marketTick[4];
tick.Volume24h = (double)marketTick[7];
tick.Time = Constants.Epoch.AddMilliseconds((Int64)marketTick[0]);
foreach (Newtonsoft.Json.Linq.JArray marketTick in jsonArray)
{
result.Add(tick);
MarketTick tick = new MarketTick();
tick.Price = (double)marketTick[4];
tick.Volume24h = (double)marketTick[7];
tick.Time = Constants.Epoch.AddMilliseconds((Int64)marketTick[0]);
result.Add(tick);
}
ticksFetched = ticksFetched + jsonArray.Count;
endTime = endTime - ticksLimit * 60 * 1000;
if (ticksNeeded - ticksFetched < ticksLimit)
{
ticksLimit = ticksNeeded - ticksFetched;
}
}
ticksFetched = ticksFetched + jsonArray.Count;
endTime = endTime - ticksLimit * 60 * 1000;
if (ticksNeeded - ticksFetched < ticksLimit)
else
{
ticksLimit = ticksNeeded - ticksFetched;
log.DoLogDebug("BinanceUS - No ticks received.");
go = false;
}
}
else
{
log.DoLogDebug("BinanceUS - No ticks received.");
go = false;
}
}
}
catch (WebException ex)
{

View File

@ -1,222 +1,212 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Core.Main;
using Core.Helper;
using Core.Main.DataObjects.PTMagicData;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
namespace Core.ProfitTrailer
{
public class OperandToken : Token
{
}
public class OrToken : OperandToken
{
}
public class AndToken : OperandToken
{
}
public class BooleanValueToken : Token
{
}
public class FalseToken : BooleanValueToken
{
}
public class TrueToken : BooleanValueToken
{
}
public class ParenthesisToken : Token
{
}
public class ClosedParenthesisToken : ParenthesisToken
{
}
public class OpenParenthesisToken : ParenthesisToken
{
}
public class NegationToken : Token
{
}
public abstract class Token
{
}
public class Tokenizer
{
private readonly StringReader _reader;
private string _text;
public Tokenizer(string text)
{
}
public class OrToken : OperandToken
{
_text = text;
_reader = new StringReader(text);
}
public class AndToken : OperandToken
public IEnumerable<Token> Tokenize()
{
}
public class BooleanValueToken : Token
{
}
public class FalseToken : BooleanValueToken
{
}
public class TrueToken : BooleanValueToken
{
}
public class ParenthesisToken : Token
{
}
public class ClosedParenthesisToken : ParenthesisToken
{
}
public class OpenParenthesisToken : ParenthesisToken
{
}
public class NegationToken : Token
{
}
public abstract class Token
{
}
public class Tokenizer
{
private readonly StringReader _reader;
private string _text;
public Tokenizer(string text)
var tokens = new List<Token>();
while (_reader.Peek() != -1)
{
while (Char.IsWhiteSpace((char)_reader.Peek()))
{
_text = text;
_reader = new StringReader(text);
_reader.Read();
}
public IEnumerable<Token> Tokenize()
if (_reader.Peek() == -1)
break;
var c = (char)_reader.Peek();
switch (c)
{
var tokens = new List<Token>();
while (_reader.Peek() != -1)
case '!':
tokens.Add(new NegationToken());
_reader.Read();
break;
case '(':
tokens.Add(new OpenParenthesisToken());
_reader.Read();
break;
case ')':
tokens.Add(new ClosedParenthesisToken());
_reader.Read();
break;
default:
if (Char.IsLetter(c))
{
while (Char.IsWhiteSpace((char) _reader.Peek()))
{
_reader.Read();
}
if (_reader.Peek() == -1)
break;
var c = (char) _reader.Peek();
switch (c)
{
case '!':
tokens.Add(new NegationToken());
_reader.Read();
break;
case '(':
tokens.Add(new OpenParenthesisToken());
_reader.Read();
break;
case ')':
tokens.Add(new ClosedParenthesisToken());
_reader.Read();
break;
default:
if (Char.IsLetter(c))
{
var token = ParseKeyword();
tokens.Add(token);
}
else
{
var remainingText = _reader.ReadToEnd() ?? string.Empty;
throw new Exception(string.Format("Unknown grammar found at position {0} : '{1}'", _text.Length - remainingText.Length, remainingText));
}
break;
}
}
return tokens;
}
private Token ParseKeyword()
{
var text = new StringBuilder();
while (Char.IsLetter((char) _reader.Peek()))
{
text.Append((char) _reader.Read());
}
var potentialKeyword = text.ToString().ToLower();
switch (potentialKeyword)
{
case "true":
return new TrueToken();
case "false":
return new FalseToken();
case "and":
return new AndToken();
case "or":
return new OrToken();
default:
throw new Exception("Expected keyword (True, False, and, or) but found "+ potentialKeyword);
var token = ParseKeyword();
tokens.Add(token);
}
else
{
var remainingText = _reader.ReadToEnd() ?? string.Empty;
throw new Exception(string.Format("Unknown grammar found at position {0} : '{1}'", _text.Length - remainingText.Length, remainingText));
}
break;
}
}
return tokens;
}
public class Parser
private Token ParseKeyword()
{
private readonly IEnumerator<Token> _tokens;
var text = new StringBuilder();
while (Char.IsLetter((char)_reader.Peek()))
{
text.Append((char)_reader.Read());
}
public Parser(IEnumerable<Token> tokens)
{
_tokens = tokens.GetEnumerator();
_tokens.MoveNext();
}
var potentialKeyword = text.ToString().ToLower();
public bool Parse()
{
while (_tokens.Current != null)
{
var isNegated = _tokens.Current is NegationToken;
if (isNegated)
_tokens.MoveNext();
var boolean = ParseBoolean();
if (isNegated)
boolean = !boolean;
while (_tokens.Current is OperandToken)
{
var operand = _tokens.Current;
if (!_tokens.MoveNext())
{
throw new Exception("Missing expression after operand");
}
var nextBoolean = ParseBoolean();
if (operand is AndToken)
boolean = boolean && nextBoolean;
else
boolean = boolean || nextBoolean;
}
return boolean;
}
throw new Exception("Empty expression");
}
private bool ParseBoolean()
{
if (_tokens.Current is BooleanValueToken)
{
var current = _tokens.Current;
_tokens.MoveNext();
if (current is TrueToken)
return true;
return false;
}
if (_tokens.Current is OpenParenthesisToken)
{
_tokens.MoveNext();
var expInPars = Parse();
if (!(_tokens.Current is ClosedParenthesisToken))
throw new Exception("Expecting Closing Parenthesis");
_tokens.MoveNext();
return expInPars;
}
if (_tokens.Current is ClosedParenthesisToken)
throw new Exception("Unexpected Closed Parenthesis");
// since its not a BooleanConstant or Expression in parenthesis, it must be a expression again
var val = Parse();
return val;
}
switch (potentialKeyword)
{
case "true":
return new TrueToken();
case "false":
return new FalseToken();
case "and":
return new AndToken();
case "or":
return new OrToken();
default:
throw new Exception("Expected keyword (True, False, and, or) but found " + potentialKeyword);
}
}
}
public class Parser
{
private readonly IEnumerator<Token> _tokens;
public Parser(IEnumerable<Token> tokens)
{
_tokens = tokens.GetEnumerator();
_tokens.MoveNext();
}
public bool Parse()
{
while (_tokens.Current != null)
{
var isNegated = _tokens.Current is NegationToken;
if (isNegated)
_tokens.MoveNext();
var boolean = ParseBoolean();
if (isNegated)
boolean = !boolean;
while (_tokens.Current is OperandToken)
{
var operand = _tokens.Current;
if (!_tokens.MoveNext())
{
throw new Exception("Missing expression after operand");
}
var nextBoolean = ParseBoolean();
if (operand is AndToken)
boolean = boolean && nextBoolean;
else
boolean = boolean || nextBoolean;
}
return boolean;
}
throw new Exception("Empty expression");
}
private bool ParseBoolean()
{
if (_tokens.Current is BooleanValueToken)
{
var current = _tokens.Current;
_tokens.MoveNext();
if (current is TrueToken)
return true;
return false;
}
if (_tokens.Current is OpenParenthesisToken)
{
_tokens.MoveNext();
var expInPars = Parse();
if (!(_tokens.Current is ClosedParenthesisToken))
throw new Exception("Expecting Closing Parenthesis");
_tokens.MoveNext();
return expInPars;
}
if (_tokens.Current is ClosedParenthesisToken)
throw new Exception("Unexpected Closed Parenthesis");
// since its not a BooleanConstant or Expression in parenthesis, it must be a expression again
var val = Parse();
return val;
}
}
public static class StrategyHelper
{
@ -234,29 +224,33 @@ namespace Core.ProfitTrailer
{
result = "";
}
// strategy labels that are variable, so can't be caught by the switch statement
// strategy labels that are variable value
if (result.Contains("REBUY"))
{
time = strategyName.Remove(0,14);
time = strategyName.Remove(0, 14);
result = "REBUY " + time;
}
if (result.Contains("CHANGE PERC"))
{
result = "CHANGE";
}
if (result.Contains("LEVERAGE"))
if (result.Contains("CROSSED"))
{
leverage = strategyName.Remove(0,10);
leverage = leverage.Remove(leverage.Length -1, 1);
result = leverage + " X";
leverage = strategyName.Remove(0, 9);
leverage = leverage.Remove(leverage.Length - 1, 1);
result = "CROSS " + leverage + "X";
}
// buy/sell strategies beginning with PT 2.3.3 contain the stragegy designation letter followed by a colon and space.
if (result.Contains("ISOLATED"))
{
leverage = strategyName.Remove(0, 10);
leverage = leverage.Remove(leverage.Length - 1, 1);
result = "ISOL " + leverage + "X";
}
// buy/sell strategies beginning with PT 2.3.3 contain the strategy designation letter followed by a colon and space.
// remove the letter and colon, change to shortcut, then reapply the letter and colon
if (strategyName.Contains(":"))
{
int strategyLength = strategyName.Length-3;
int strategyLength = strategyName.Length - 3;
strategyLetter = strategyName.Remove(3, strategyLength);
strategyNameOnly = strategyName.Remove(0, 3);
}
@ -275,40 +269,40 @@ namespace Core.ProfitTrailer
result = String.Concat(strategyLetter, "LOSS");
break;
case "smagain":
result = String.Concat(strategyLetter, "SMAG");
result = String.Concat(strategyLetter, "SMA-G");
break;
case "emagain":
result = String.Concat(strategyLetter, "EMAG");
result = String.Concat(strategyLetter, "EMA-G");
break;
case "hmagain":
result = String.Concat(strategyLetter, "HMAG");
result = String.Concat(strategyLetter, "HMA-G");
break;
case "dmagain":
result = String.Concat(strategyLetter, "DMAG");
result = String.Concat(strategyLetter, "DMA-G");
break;
case "smaspread":
result = String.Concat(strategyLetter, "SMAS");
result = String.Concat(strategyLetter, "SMA-S");
break;
case "emaspread":
result = String.Concat(strategyLetter, "EMAS");
result = String.Concat(strategyLetter, "EMA-S");
break;
case "hmaspread":
result = String.Concat(strategyLetter, "HMAS");
result = String.Concat(strategyLetter, "HMA-S");
break;
case "dmaspread":
result = String.Concat(strategyLetter, "DMAS");
result = String.Concat(strategyLetter, "DMA-S");
break;
case "smacross":
result = String.Concat(strategyLetter, "SMAC");
result = String.Concat(strategyLetter, "SMA-C");
break;
case "emacross":
result = String.Concat(strategyLetter, "EMAC");
result = String.Concat(strategyLetter, "EMA-C");
break;
case "hmacross":
result = String.Concat(strategyLetter, "HMAC");
result = String.Concat(strategyLetter, "HMA-C");
break;
case "dmacross":
result = String.Concat(strategyLetter, "DMAC");
result = String.Concat(strategyLetter, "DMA-C");
break;
case "rsi":
result = String.Concat(strategyLetter, "RSI");
@ -320,13 +314,13 @@ namespace Core.ProfitTrailer
result = String.Concat(strategyLetter, "SRSI");
break;
case "stochrsik":
result = String.Concat(strategyLetter, "SRSIK");
result = String.Concat(strategyLetter, "SRSI-K");
break;
case "stochrsid":
result = String.Concat(strategyLetter, "SRSID");
result = String.Concat(strategyLetter, "SRSI-D");
break;
case "stochrsicross":
result = String.Concat(strategyLetter, "SRSIC");
result = String.Concat(strategyLetter, "SRSI-C");
break;
case "macd":
result = String.Concat(strategyLetter, "MACD");
@ -365,19 +359,19 @@ namespace Core.ProfitTrailer
result = String.Concat(strategyLetter, "FIXED");
break;
case "lowatrband":
result = String.Concat(strategyLetter, "LATR");
result = String.Concat(strategyLetter, "L-ATR");
break;
case "highatrband":
result = String.Concat(strategyLetter, "HATR");
result = String.Concat(strategyLetter, "H-ATR");
break;
case "atrpercentage":
result = String.Concat(strategyLetter, "ATRPCT");
result = String.Concat(strategyLetter, "ATR-PCT");
break;
case "vwappercentage":
result = String.Concat(strategyLetter, "VWAP");
break;
case "mvwappercentage":
result = String.Concat(strategyLetter, "MVWAP");
result = String.Concat(strategyLetter, "M-VWAP");
break;
case "btcdominance":
result = String.Concat(strategyLetter, "BTCDOM");
@ -430,10 +424,21 @@ namespace Core.ProfitTrailer
case "no dca buy logic":
result = String.Concat(strategyLetter, "NODCA");
break;
case "combimagain":
result = String.Concat(strategyLetter, "COMBI-G");
break;
case "combimaspread":
result = String.Concat(strategyLetter, "COMBI-S");
break;
case "combimacross":
result = String.Concat(strategyLetter, "COMBI-C");
break;
case "macdpercentage":
result = String.Concat(strategyLetter, "MACDPERC");
break;
default:
break;
}
if (onlyValidStrategies)
{
if (strategyName.IndexOf("SOM") > -1 || strategyName.IndexOf("MAX") > -1 || strategyName.IndexOf("MIN") > -1 || strategyName.IndexOf("PRICE") > -1 || strategyName.IndexOf("BLACK") > -1 || strategyName.IndexOf("INSUFFICIENT") > -1 || strategyName.IndexOf("COST") > -1)
@ -441,7 +446,6 @@ namespace Core.ProfitTrailer
result = "";
}
}
return result;
}
@ -453,14 +457,11 @@ namespace Core.ProfitTrailer
public static bool IsValidStrategy(string strategyName, bool checkForAnyInvalid)
{
bool result = false;
// buy/sell strategies beginning with PT 2.3.3 contain the letter followed by a colon and space.
if (strategyName.Contains(":"))
{
result = true;
}
// Prior to PT 2.3.3
if (!checkForAnyInvalid)
{
switch (strategyName.ToLower())
@ -507,6 +508,10 @@ namespace Core.ProfitTrailer
case "vwappercentage":
case "mvwappercentage":
case "btcdominance":
case "combimagain":
case "combimaspread":
case "combimacross":
case "macdpercentage":
result = true;
break;
default:
@ -529,14 +534,11 @@ namespace Core.ProfitTrailer
result = true;
}
}
return result;
}
public static int GetStrategyValueDecimals(string strategyName)
{
int result = 0;
switch (strategyName.ToLower())
{
case "lowbb":
@ -575,45 +577,76 @@ namespace Core.ProfitTrailer
default:
break;
}
return result;
}
public static string GetStrategyText(Summary summary, List<Strategy> strategies, string strategyText, bool isTrue, bool isTrailingBuyActive)
{
{
bool isValidStrategy = false;
Regex regx = new Regex(@"[ABCDEFGHIJKLMNOPQRSTUVWXYZ]", RegexOptions.Compiled);
if (strategies.Count > 0)
{
foreach (Strategy strategy in strategies)
{
string textClass = (strategy.IsTrue) ? "label-success" : "label-danger";
isValidStrategy = StrategyHelper.IsValidStrategy(strategy.Name);
if (!isValidStrategy )
if (!isValidStrategy)
{
// Parse Formulas
if (strategy.Name.Contains("FORMULA") && !strategy.Name.Contains("STATS"))
if (strategy.Name.Contains("TRIGGERED"))
// remove levels already triggered, to show only currently waiting trigger
{
string expression = strategy.Name.Remove(0, 10);
expression = expression.Replace("<span class=\"tdgreen\">","true").Replace("<span class=\"red\">","false").Replace("</span>","").Replace("&&","and").Replace("||","or");
expression = Regex.Replace(expression, @"[ABCDEFGHIJKLMNOPQRSTUVWXYZ]", String.Empty);
var tokens = new Tokenizer(expression).Tokenize();
var parser = new Parser(tokens);
if (parser.Parse()) {
strategyText += "<span class=\"label label-success\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"CONDITIONAL FORMULA\">(FORM)</span> ";
strategyText += "";
}
else if (strategy.Name.Contains("STATS"))
// avoid parsing advanced buy stats
{
strategy.Name = "";
}
else if (strategy.Name.Contains("FORMULA"))
// Parse Various PT Formulas
{
if (strategy.Name.Contains("LEVEL"))
// level X
{
string level = strategy.Name.Substring(5, 2);
string expression = strategy.Name.Remove(0, 17);
expression = expression.Replace("<span class=\"tdgreen\">", "true").Replace("<span class=\"red\">", "false").Replace("</span>", "").Replace("&&", "and").Replace("||", "or");
expression = regx.Replace(expression, String.Empty);
var tokens = new Tokenizer(expression).Tokenize();
var parser = new Parser(tokens);
if (parser.Parse())
{
strategyText += "<span class=\"label label-success\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"LEVEL FORMULA\">L " + level + "</span> ";
}
else
{
strategyText += "<span class=\"label label-danger\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"LEVEL FORMULA\">L " + level + "</span> ";
}
}
else
// standard formula
{
strategyText += "<span class=\"label label-danger\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"CONDITIONAL FORMULA\">(FORM)</span> ";
string expression = strategy.Name.Remove(0, 10);
expression = expression.Replace("<span class=\"tdgreen\">", "true").Replace("<span class=\"red\">", "false").Replace("</span>", "").Replace("&&", "and").Replace("||", "or");
expression = regx.Replace(expression, String.Empty);
var tokens = new Tokenizer(expression).Tokenize();
var parser = new Parser(tokens);
if (parser.Parse())
{
strategyText += "<span class=\"label label-success\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"CONDITIONAL FORMULA\">FORM</span> ";
}
else
{
strategyText += "<span class=\"label label-danger\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"CONDITIONAL FORMULA\">FORM</span> ";
}
}
}
else
{
strategyText += "<span class=\"label label-warning\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"" + strategy.Name + "\">" + StrategyHelper.GetStrategyShortcut(strategy.Name, false) + "</span> ";
}
}
else
{
@ -639,14 +672,11 @@ namespace Core.ProfitTrailer
}
else
{
isValidStrategy = StrategyHelper.IsValidStrategy(strategyText);
if (isValidStrategy)
{
strategyText = "<span class=\"label label-danger\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"" + strategyText + "\">" + StrategyHelper.GetStrategyShortcut(strategyText, true) + "</span>";
}
else if (strategyText.Equals("") && isValidStrategy == false)
{
strategyText = "<span class=\"label label-muted\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"Not Applicable: Not using DCA!\"></span>";
@ -657,10 +687,8 @@ namespace Core.ProfitTrailer
}
}
}
return strategyText;
}
public static string GetCurrentValueText(List<Strategy> strategies, string strategyText, double bbValue, double simpleValue, bool includeShortcut)
{
string result = "";
@ -721,14 +749,12 @@ namespace Core.ProfitTrailer
result = simpleValue.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%";
}
}
return result;
}
public static string GetTriggerValueText(Summary summary, List<Strategy> strategies, string strategyText, double bbValue, double simpleValue, int buyLevel, bool includeShortcut)
{
string result = "";
if (strategies.Count > 0)
{
foreach (Strategy strategy in strategies)
@ -743,12 +769,10 @@ namespace Core.ProfitTrailer
{
decimalFormat += "0";
}
if (includeShortcut)
{
result += "<span class=\"text-muted\">" + StrategyHelper.GetStrategyShortcut(strategy.Name, true) + "</span> ";
}
if (StrategyHelper.GetStrategyShortcut(strategy.Name, true).IndexOf("and", StringComparison.InvariantCultureIgnoreCase) > -1)
{
result += strategy.TriggerValue.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"));
@ -792,7 +816,6 @@ namespace Core.ProfitTrailer
result = simpleValue.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%";
}
}
return result;
}
}

View File

@ -10,16 +10,12 @@
<None Remove="wwwroot\assets\**" />
</ItemGroup>
<ItemGroup>
<Content Include="wwwroot\assets\css\custom.css" CopyToOutputDirectory="Always" />
<Content Include="wwwroot\assets\img\qr-btc.png" CopyToOutputDirectory="Always" />
<Content Include="wwwroot\assets\img\qr-eth.png" CopyToOutputDirectory="Always" />
<Content Include="wwwroot\assets\img\qr-ltc.png" CopyToOutputDirectory="Always" />
<Content Include="wwwroot\assets\img\qr-neo.png" CopyToOutputDirectory="Always" />
<Content Include="wwwroot\assets\js\custom.js" CopyToOutputDirectory="Always" />
<Content Include="wwwroot\assets\js\analyzer-settings.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="wwwroot\assets\js\dca.js" CopyToOutputDirectory="Always" />
<Content Include="wwwroot\assets\img\**" CopyToOutputDirectory="Always" />
<Content Include="wwwroot\assets\css\**" CopyToOutputDirectory="Always" />
<Content Include="wwwroot\assets\fonts\**" CopyToOutputDirectory="Always" />
<Content Include="wwwroot\assets\pages\**" CopyToOutputDirectory="Always" />
<Content Include="wwwroot\assets\plugins\**" CopyToOutputDirectory="Always" />
<Content Include="wwwroot\assets\js\**" CopyToOutputDirectory="Always" />
<None Include="nlog.config" CopyToOutputDirectory="Always" />
</ItemGroup>
<ItemGroup>

View File

@ -8,8 +8,20 @@
<style type="text/css" media="screen">
#editor {
height: 500px;
width: 100%;
height: 500px;
width: 100%;
}
@@media (min-height: 1000px) {
#editor {
height: 750px;
}
}
@@media (min-height: 1250px) {
#editor {
height: 1000px;
}
}
#preset-file {
@ -58,7 +70,7 @@
<form class="form-horizontal" method="post">
<!-- Modal -->
<div class="modal fade" id="modalEditPresetFile" tabindex="-1" role="dialog" aria-labelledby="modalEditPresetFileTitle" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalEditPresetFileTitle"></h5>
@ -84,10 +96,12 @@
<script type="text/javascript">
$(document).ready(function () {
// Setup UI
$('[data-toggle="tooltip"]').tooltip();
$('.text-autocolor').autocolor(false);
$('.btn-edit-presetfile').click(function () {
$('.btn-edit-presetfile').click(function () {
// Setup UI
var settingName = $(this).data('settingname');
var dataTarget = $(this).data('datatarget');
@ -103,6 +117,11 @@
} else if (responseText == 'returntologin') {
window.location.replace("@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)Login");
}
else
{
// Set ACE editor language mode.
window.editor.session.setMode("ace/mode/yaml");
}
});
return false;

View File

@ -1,11 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;
using Core.Main;
using Core.Helper;
using Core.Main.DataObjects.PTMagicData;
using System.Globalization;
namespace Monitor.Pages
{

View File

@ -19,13 +19,13 @@
<thead>
<tr>
@{
double currentBalance = Model.PTData.GetCurrentBalance();
string currentBalanceString = currentBalance.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"));
if (currentBalance > 100) {
currentBalanceString = Math.Round(currentBalance, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"));
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"));
}
}
<th class="m-t-0 header-title text-left">Account Value: &nbsp; <text class="text-autocolor"> @currentBalanceString &nbsp; @Model.Summary.MainMarket </text> <small> <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This is based on your sales history, entries on the Transactions page and any currently held positions."></i></small></th>
<th class="m-t-0 header-title text-left">Total Account Value: &nbsp; <text class="text-autocolor"> @totalCurrentValueString @Model.Summary.MainMarket </text> <small> <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This is based on your sales history, entries on the Transactions page and any currently held positions."></i></small></th>
<th class="text-right">Starting Value: &nbsp; <text class="text-autocolor"> @Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance &nbsp; @Model.Summary.MainMarket </text> <small> <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="This is the starting value found in your settings file"></i></small></th>
</tr>
</thead>
@ -59,16 +59,21 @@
<div class="row">
<div class="col-md-6">
<div class="card-box">
<h4 class="m-t-0 header-title">Sales Analysis</h4>
@{
double totalProfit = Model.PTData.SellLog.Sum(s => s.Profit);
double totalProfit = 0;
if (Model.PTData.Properties.Shorting)
{
totalProfit = Model.PTData.SellLog.Sum(s => s.Profit * (-1));
}
else
{
totalProfit = Model.PTData.SellLog.Sum(s => s.Profit);
}
double totalProfitFiat = Math.Round(totalProfit * Model.Summary.MainMarketPrice, 2);
double percentGain = Math.Round(totalProfit / Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance * 100, 2);
double avgDailyGain = Model.DailyGains.Values.Average(dg => dg);
double avgMonthlyGain = Model.MonthlyGains.Values.Average(dg => dg);
string percentGainText = percentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%";
if (Model.PTData.TransactionData.Transactions.Count > 0) {
percentGainText = "<i class=\"fa fa-info-circle text-muted\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"You have added at least one manual transaction, so the total gain percentage cannot be calculated.\"></i>";
@ -123,9 +128,7 @@
double estimatedBalance6Months = Math.Round(currentTotalBalance * Math.Pow((1 + (avgDailyGain / 100)), 180.0), 8);
double estimatedBalance1Year = Math.Round(currentTotalBalance * Math.Pow((1 + (avgDailyGain / 100)), 365.0), 8);
}
<h4 class="m-t-0 header-title">Balance Prediction <small><i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The balance prediction is based on your daily average gain of @avgDailyGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))% and your current approximate balance of @currentTotalBalance.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))"></i></small></h4>
<table class="table table-striped table-sm">
<thead>
<tr>
@ -188,7 +191,6 @@
<div class="col-md-6">
<div class="card-box">
<h4 class="m-t-0 header-title">Last @Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries days</h4>
<table class="table table-sm">
<thead>
<tr>
@ -202,7 +204,15 @@
<tbody>
@for (DateTime salesDate = Model.DateTimeNow.DateTime.Date; salesDate >= Model.DateTimeNow.DateTime.AddDays(-Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDailySummaries) && salesDate >= Model.MinSellLogDate; salesDate = salesDate.AddDays(-1)) {
List<Core.Main.DataObjects.PTMagicData.SellLogData> salesDateSales = Model.PTData.SellLog.FindAll(sl => sl.SoldDate.Date == salesDate);
double salesDateProfit = salesDateSales.Sum(sl => sl.Profit);
double salesDateProfit = 0;
if (Model.PTData.Properties.Shorting)
{
salesDateProfit = salesDateSales.Sum(sl => sl.Profit * (-1));
}
else
{
salesDateProfit = salesDateSales.Sum(sl => sl.Profit);
}
double salesDateProfitFiat = Math.Round(salesDateProfit * Model.Summary.MainMarketPrice, 2);
double salesDateStartBalance = Model.PTData.GetSnapshotBalance(salesDate);
double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2);
@ -241,11 +251,18 @@
}
@for (DateTime salesMonthDate = salesMonthStartDate.Date; salesMonthDate >= Model.DateTimeNow.DateTime.AddMonths(-Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxMonthlySummaries) && salesMonthDate >= minSellLogMonthDate; salesMonthDate = salesMonthDate.AddMonths(-1)) {
List<Core.Main.DataObjects.PTMagicData.SellLogData> salesMonthSales = Model.PTData.SellLog.FindAll(sl => sl.SoldDate.Date.Month == salesMonthDate.Month && sl.SoldDate.Date.Year == salesMonthDate.Year);
double salesDateProfit = salesMonthSales.Sum(sl => sl.Profit);
double salesDateProfit = 0;
if (Model.PTData.Properties.Shorting)
{
salesDateProfit = salesMonthSales.Sum(sl => sl.Profit * (-1));
}
else
{
salesDateProfit = salesMonthSales.Sum(sl => sl.Profit);
}
double salesDateProfitFiat = Math.Round(salesDateProfit * Model.Summary.MainMarketPrice, 2);
double salesDateStartBalance = Model.PTData.GetSnapshotBalance(salesMonthDate);
double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2);
double salesDateAVGDailyGain = 0;
double monthDailyProfit = 0;
int days = 0;
@ -254,7 +271,15 @@
if (monthDay <= Model.DateTimeNow) {
days++;
List<Core.Main.DataObjects.PTMagicData.SellLogData> monthDaySales = Model.PTData.SellLog.FindAll(sl => sl.SoldDate.Date == monthDay.Date);
double monthDayProfit = monthDaySales.Sum(sl => sl.Profit);
double monthDayProfit = 0;
if (Model.PTData.Properties.Shorting)
{
monthDayProfit = monthDaySales.Sum(sl => sl.Profit * (-1));
}
else
{
monthDayProfit = monthDaySales.Sum(sl => sl.Profit);
}
double monthDayStartBalance = Model.PTData.GetSnapshotBalance(monthDay);
monthDailyProfit += Math.Round(monthDayProfit / monthDayStartBalance * 100, 2);
}
@ -278,9 +303,7 @@
<div class="row">
<div class="col-sm-12">
<div class="card-box">
<h4 class="m-t-0 header-title"><b>Top @Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets Sales Market Analysis</b></h4>
<table class="tablesaw table m-b-0" data-tablesaw-sortable data-tablesaw-sortable-switch>
<thead>
<tr>
@ -296,6 +319,10 @@
<tbody>
@{
var topMarkets = Model.PTData.SellLog.GroupBy(m => m.Market).Select(mg => mg.Sum(m => m.Profit));
if (Model.PTData.Properties.Shorting)
{
topMarkets = Model.PTData.SellLog.GroupBy(m => m.Market).Select(mg => mg.Sum(m => m.Profit) * (-1));
}
int marketRank = 0;
}
@foreach (KeyValuePair<string, double> marketData in Model.TopMarkets) {
@ -362,7 +389,6 @@
lineChart.yAxis.axisLabel('Daily Sales').tickFormat(d3.format(','));
d3.select('.trades-chart svg').attr('perserveAspectRatio', 'xMinYMid').datum(chartData).transition().duration(500).call(lineChart);
nv.utils.windowResize(lineChart.update);
return lineChart;
});
@ -377,7 +403,6 @@
lineChart.yAxis.axisLabel('Daily Profit').tickFormat(d3.format(',.2f'));
d3.select('.profit-chart svg').attr('perserveAspectRatio', 'xMinYMid').datum(chartData).transition().duration(500).call(lineChart);
nv.utils.windowResize(lineChart.update);
return lineChart;
});
@ -392,7 +417,6 @@
lineChart.yAxis.axisLabel('Profit').tickFormat(d3.format(',.2f'));
d3.select('.balance-chart svg').attr('perserveAspectRatio', 'xMinYMid').datum(chartData).transition().duration(500).call(lineChart);
nv.utils.windowResize(lineChart.update);
return lineChart;
});

View File

@ -20,14 +20,14 @@ namespace Monitor.Pages
public Dictionary<DateTime, double> DailyGains = new Dictionary<DateTime, double>();
public Dictionary<DateTime, double> MonthlyGains = new Dictionary<DateTime, double>();
public DateTimeOffset DateTimeNow = Constants.confMinDate;
public double totalCurrentValue = 0;
public void OnGet()
{
base.Init();
BindData();
BuildTCV();
}
private void BindData()
{
PTData = this.PtDataObject;
@ -39,20 +39,25 @@ namespace Monitor.Pages
BuildTopMarkets();
BuildSalesChartData();
}
private void BuildTopMarkets()
{
var markets = PTData.SellLog.GroupBy(m => m.Market);
Dictionary<string, double> topMarketsDic = new Dictionary<string, double>();
foreach (var market in markets)
{
double totalProfit = PTData.SellLog.FindAll(m => m.Market == market.Key).Sum(m => m.Profit);
double totalProfit = 0;
if (PTData.Properties.Shorting)
{
totalProfit = PTData.SellLog.FindAll(m => m.Market == market.Key).Sum(m => m.Profit * (-1));
}
else
{
totalProfit = PTData.SellLog.FindAll(m => m.Market == market.Key).Sum(m => m.Profit);
}
topMarketsDic.Add(market.Key, totalProfit);
}
TopMarkets = new SortedDictionary<string, double>(topMarketsDic).OrderByDescending(m => m.Value).Take(PTMagicConfiguration.GeneralSettings.Monitor.MaxTopMarkets);
}
private void BuildSalesChartData()
{
if (PTData.SellLog.Count > 0)
@ -68,26 +73,29 @@ namespace Monitor.Pages
double balance = 0.0;
for (DateTime salesDate = graphStartDate; salesDate <= DateTimeNow.DateTime.Date; salesDate = salesDate.AddDays(1))
{
if (tradeDayIndex > 0)
{
tradesPerDayJSON += ",\n";
profitPerDayJSON += ",\n";
balancePerDayJSON += ",\n";
}
double profit = 0;
int trades = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate.Date).Count;
double profit = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate.Date).Sum(t => t.Profit);
if (PTData.Properties.Shorting)
{
profit = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate.Date).Sum(t => t.Profit * (-1));
}
else
{
profit = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate.Date).Sum(t => t.Profit);
}
double profitFiat = Math.Round(profit * Summary.MainMarketPrice, 2);
balance += profitFiat;
tradesPerDayJSON += "{x: new Date('" + salesDate.Date.ToString("yyyy-MM-dd") + "'), y: " + trades + "}";
profitPerDayJSON += "{x: new Date('" + salesDate.Date.ToString("yyyy-MM-dd") + "'), y: " + profitFiat.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}";
balancePerDayJSON += "{x: new Date('" + salesDate.Date.ToString("yyyy-MM-dd") + "'), y: " + balance.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}";
tradeDayIndex++;
}
TradesChartDataJSON = "[";
TradesChartDataJSON += "{";
TradesChartDataJSON += "key: 'Sales',";
@ -111,29 +119,51 @@ namespace Monitor.Pages
BalanceChartDataJSON += "values: [" + balancePerDayJSON + "]";
BalanceChartDataJSON += "}";
BalanceChartDataJSON += "]";
for (DateTime salesDate = DateTimeNow.DateTime.Date; salesDate >= MinSellLogDate; salesDate = salesDate.AddDays(-1))
{
List<SellLogData> salesDateSales = PTData.SellLog.FindAll(sl => sl.SoldDate.Date == salesDate);
double salesDateProfit = salesDateSales.Sum(sl => sl.Profit);
double salesDateProfit;
if (PTData.Properties.Shorting)
{
salesDateProfit = salesDateSales.Sum(sl => sl.Profit * (-1));
}
else
{
salesDateProfit = salesDateSales.Sum(sl => sl.Profit);
}
double salesDateStartBalance = PTData.GetSnapshotBalance(salesDate);
double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2);
DailyGains.Add(salesDate, salesDateGain);
}
DateTime minSellLogMonthDate = new DateTime(MinSellLogDate.Year, MinSellLogDate.Month, 1).Date;
DateTime salesMonthStartDate = new DateTime(DateTimeNow.DateTime.Year, DateTimeNow.DateTime.Month, 1).Date;
for (DateTime salesMonthDate = salesMonthStartDate.Date; salesMonthDate >= minSellLogMonthDate; salesMonthDate = salesMonthDate.AddMonths(-1))
{
List<Core.Main.DataObjects.PTMagicData.SellLogData> salesMonthSales = PTData.SellLog.FindAll(sl => sl.SoldDate.Date.Month == salesMonthDate.Month && sl.SoldDate.Date.Year == salesMonthDate.Year);
double salesDateProfit = salesMonthSales.Sum(sl => sl.Profit);
double salesDateProfit;
if (PTData.Properties.Shorting)
{
salesDateProfit = salesMonthSales.Sum(sl => sl.Profit * (-1));
}
else
{
salesDateProfit = salesMonthSales.Sum(sl => sl.Profit);
}
double salesDateStartBalance = PTData.GetSnapshotBalance(salesMonthDate);
double salesDateGain = Math.Round(salesDateProfit / salesDateStartBalance * 100, 2);
MonthlyGains.Add(salesMonthDate, salesDateGain);
}
}
}
private void BuildTCV()
{
double AvailableBalance = PTData.GetCurrentBalance();
foreach (Core.Main.DataObjects.PTMagicData.DCALogData dcaLogEntry in PTData.DCALog)
{
totalCurrentValue = totalCurrentValue + ((dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) / dcaLogEntry.Leverage);
}
totalCurrentValue = totalCurrentValue + AvailableBalance;
}
}
}

View File

@ -241,8 +241,9 @@
<label class="col-md-4 col-form-label">Link Platform <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The platform to which the pair name will link if you click on it."></i></label>
<div class="col-md-8">
<select name="Monitor_LinkPlatform" class="form-control">
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform.Equals("TradingView", StringComparison.InvariantCultureIgnoreCase))">TradingView</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase))">Exchange</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform.Equals("TradingView", StringComparison.InvariantCultureIgnoreCase))">TradingView</option>
<option selected="@(Model.PTMagicConfiguration.GeneralSettings.Monitor.LinkPlatform.Equals("TradingViewFutures", StringComparison.InvariantCultureIgnoreCase))">TradingViewFutures</option>
</select>
</div>
</div>

View File

@ -209,5 +209,6 @@
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/js/jquery.core.js?v=@Html.Raw(Model.CurrentBotVersion)"></script>
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/js/jquery.app.js?v=@Html.Raw(Model.CurrentBotVersion)"></script>
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/js/circlos.js"></script>
</body>
</html>

View File

@ -125,9 +125,9 @@
<tr>
<td>DCA Buy Strategies</td>
@if (buyDisabled) {
<td class="text-right text-nowrap">@Html.Raw(buyStrategyText)</td>
<td class="text-right text">@Html.Raw(buyStrategyText)</td>
} else {
<td class="text-right text-nowrap">@Html.Raw(buyStrategyText)</td>
<td class="text-right text">@Html.Raw(buyStrategyText)</td>
}
</tr>
<tr>

View File

@ -110,9 +110,9 @@
// Buy strats
@if (buyDisabled) {
<td class="text-nowrap">@Html.Raw(buyStrategyText)</td>
<td class="text">@Html.Raw(buyStrategyText)</td>
} else {
<td class="text-nowrap">@Html.Raw(buyStrategyText)</td>
<td class="text">@Html.Raw(buyStrategyText)</td>
}
// BS Value
@ -144,7 +144,7 @@
}
// Sell Strats
<td class="text-nowrap">@Html.Raw(sellStrategyText)</td>
<td class="text">@Html.Raw(sellStrategyText)</td>
// SSV
<td class="text-right text-nowrap">@Html.Raw(currentSellValueText)</td>

View File

@ -10,33 +10,46 @@
}
<div class="row">
<div class="col-md-5 px-1">
<div class="col-md-4 px-1">
<div class="card-box px-2" style="height:305px;">
<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div>
@if (!Model.TrendChartDataJSON.Equals("")) {
<div class="trend-chart">
<svg style="height:300px;width:100%"></svg>
<svg style="height: 300px;width: 100%;"></svg>
</div>
} else {
<p>Unable to load graph, no market trend data found.</p>
}
</div>
</div>
<div class="col-md-2 px-1">
<div class="card-box px-2" style="height:305px;">
<div class="col-md-3 px-1">
<div class="card-box px-3" style="height:305px;">
<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div>
@{
double currentBalance = Model.PTData.GetCurrentBalance();
string currentBalanceString = currentBalance.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"));
if (currentBalance > 100) {
currentBalanceString = Math.Round(currentBalance, 2).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"));
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="text-center"><small>TCV: &nbsp; <text class="text-autocolor"> @currentBalanceString &nbsp; @Model.Summary.MainMarket &nbsp; </text> </small></div>
<div id="AssetDistribution">
<svg style="height:290px;width:100%"></svg>
}
<div id="AssetDistribution" class="container">
<div class="text-center">
<small>
<span data-toggle="tooltip" data-placement="top" title="Starting balance from PTM settings">Start: &nbsp; <text class="text-autocolor"> @Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance @Model.Summary.MainMarket </text></span>
<span data-toggle="tooltip" data-placement="top" title="TCV gain on starting balance"> &emsp; &emsp; Gain:&nbsp;<text class="text-autocolor">@Math.Round(((Model.totalCurrentValue - Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance) / Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance) * 100, 2)%</text></span>
</small>
</div>
<div class="text-center">
<span data-toggle="tooltip" data-placement="top" title="Total current account value">TCV: &nbsp; <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-5 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:305px;">
@if (!Model.ProfitChartDataJSON.Equals("")) {
<div class="profit-chart">
@ -48,13 +61,13 @@
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 px-1">
<div class="card-box px-2">
<h4 class="m-t-0 m-b-20 header-title"><b>Market Trends at @Model.PTMagicConfiguration.GeneralSettings.Application.Exchange</b><small class="pull-right"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)MarketAnalyzer">more</a></small></h4>
<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div>
<br>
<h4 class="m-t-0 m-b-20 header-title"><b>Market Trends at @Model.PTMagicConfiguration.GeneralSettings.Application.Exchange</b>
<small class="pull-right"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)MarketAnalyzer">more</a></small></h4>
<table class="table table-sm">
<thead>
<tr>
@ -93,33 +106,77 @@
<div class="col-md-6 px-1">
<div class="card-box px-2">
<div class="cdev" data-percent="100" data-duration="@Html.Raw(@Model.PTMagicConfiguration.GeneralSettings.Monitor.RefreshSeconds * 1000)" data-color="#aaa,#414d59"></div>
<br>
<h4 class="m-t-0 m-b-20 header-title"><b>Sales Overview</b><small class="pull-right"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)SalesAnalyzer">more</a></small></h4>
@{
double totalProfit = Model.PTData.SellLog.Sum(s => s.Profit);
double totalProfit = 0;
if (Model.PTData.Properties.Shorting)
{
totalProfit = Model.PTData.SellLog.Sum(s => s.Profit * (-1));
}
else
{
totalProfit = Model.PTData.SellLog.Sum(s => s.Profit);
}
double totalProfitFiat = Math.Round(totalProfit * Model.Summary.MainMarketPrice, 2);
double percentGain = Math.Round(totalProfit / Model.PTMagicConfiguration.GeneralSettings.Application.StartBalance * 100, 2);
string percentGainText = percentGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%";
if (Model.PTData.TransactionData.Transactions.Count > 0) {
if (Model.PTData.TransactionData.Transactions.Count > 0)
{
percentGainText = "<i class=\"fa fa-info-circle text-muted\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"You have added at least one manual transaction, so the total gain percentage cannot be calculated.\"></i>";
}
double todaysProfit = 0;
if (Model.PTData.Properties.Shorting)
{
todaysProfit = Model.PTData.SellLogToday.Sum(s => s.Profit * (-1));
}
else
{
todaysProfit = Model.PTData.SellLogToday.Sum(s => s.Profit);
}
double todaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime);
double todaysProfit = Model.PTData.SellLogToday.Sum(s => s.Profit);
double todaysProfitFiat = Math.Round(todaysProfit * Model.Summary.MainMarketPrice, 2);
double todaysPercentGain = Math.Round(todaysProfit / todaysStartBalance * 100, 2);
double yesterdaysProfit = 0;
if (Model.PTData.Properties.Shorting)
{
yesterdaysProfit = Model.PTData.SellLogYesterday.Sum(s => s.Profit * (-1));
}
else
{
yesterdaysProfit = Model.PTData.SellLogYesterday.Sum(s => s.Profit);
}
double yesterdaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-1));
double yesterdaysProfit = Model.PTData.SellLogYesterday.Sum(s => s.Profit);
double yesterdaysProfitFiat = Math.Round(yesterdaysProfit * Model.Summary.MainMarketPrice, 2);
double yesterdaysPercentGain = Math.Round(yesterdaysProfit / yesterdaysStartBalance * 100, 2);
double last7DaysProfit = 0;
if (Model.PTData.Properties.Shorting)
{
last7DaysProfit = Model.PTData.SellLogLast7Days.Sum(s => s.Profit * (-1));
}
else
{
last7DaysProfit = Model.PTData.SellLogLast7Days.Sum(s => s.Profit);
}
double last7DaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-7));
double last7DaysProfit = Model.PTData.SellLogLast7Days.Sum(s => s.Profit);
double last7DaysProfitFiat = Math.Round(last7DaysProfit * Model.Summary.MainMarketPrice, 2);
double last7DaysPercentGain = Math.Round(last7DaysProfit / last7DaysStartBalance * 100, 2);
double last30DaysProfit = 0;
if (Model.PTData.Properties.Shorting)
{
last30DaysProfit = Model.PTData.SellLogLast30Days.Sum(s => s.Profit * (-1));
}
else
{
last30DaysProfit = Model.PTData.SellLogLast30Days.Sum(s => s.Profit);
}
double last30DaysStartBalance = Model.PTData.GetSnapshotBalance(Model.DateTimeNow.DateTime.AddDays(-30));
double last30DaysProfit = Model.PTData.SellLogLast30Days.Sum(s => s.Profit);
double last30DaysProfitFiat = Math.Round(last30DaysProfit * Model.Summary.MainMarketPrice, 2);
double last30DaysPercentGain = Math.Round(last30DaysProfit / last30DaysStartBalance * 100, 2);
}
@ -181,6 +238,7 @@
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/plugins/tablesaw/js/tablesaw-init.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$(".cdev").circlos();
$('[data-toggle="tooltip"]').tooltip();
$('.text-autocolor').autocolor(false);
@ -191,17 +249,15 @@
.x(function(d) { return d.label })
.y(function(d) { return d.value })
.showLabels(true) //Display pie labels
.labelThreshold(.1) //Configure the minimum slice size for labels to show up
.labelThreshold(.1) //Configure the minimum slice size for labels to show up
.labelType("percent") //Configure what type of data to show in the label. Can be "key", "value" or "percent"
.donut(true) //Turn on Donut mode. Makes pie chart look tasty!
.donutRatio(0.3) //Configure how big you want the donut hole size to be.
;
d3.select("#AssetDistribution svg")
.datum(@Html.Raw(Model.AssetDistributionData))
.transition().duration(350)
.call(chart);
return chart;
});
</text>
@ -219,7 +275,6 @@
$.Notification.notify('success', 'top left', '@Core.Helper.SystemHelper.SplitCamelCase(Model.Summary.CurrentGlobalSetting.SettingName) now active!', 'PTMagic switched Profit Trailer settings to "@Core.Helper.SystemHelper.SplitCamelCase(Model.Summary.CurrentGlobalSetting.SettingName)".');
</text>
}
@if (!Model.TrendChartDataJSON.Equals("")) {
<text>
nv.addGraph(function () {
@ -235,7 +290,6 @@
});
</text>
}
@if (!Model.ProfitChartDataJSON.Equals("")) {
<text>
nv.addGraph(function () {
@ -247,7 +301,6 @@
lineChart.yAxis.axisLabel('Daily Profit').tickFormat(d3.format(',.2f'));
d3.select('.profit-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
return lineChart;
});
</text>

View File

@ -8,8 +8,10 @@ using Core.Main.DataObjects;
using Core.Main.DataObjects.PTMagicData;
using Core.MarketAnalyzer;
namespace Monitor.Pages {
public class DashboardBottomModel : _Internal.BasePageModelSecureAJAX {
namespace Monitor.Pages
{
public class DashboardBottomModel : _Internal.BasePageModelSecureAJAX
{
public ProfitTrailerData PTData = null;
public List<MarketTrend> MarketTrends { get; set; } = new List<MarketTrend>();
public string TrendChartDataJSON = "";
@ -17,9 +19,9 @@ namespace Monitor.Pages {
public string LastGlobalSetting = "Default";
public DateTimeOffset DateTimeNow = Constants.confMinDate;
public string AssetDistributionData = "";
public double currentBalance = 0;
public string currentBalanceString = "";
public void OnGet() {
public double totalCurrentValue = 0;
public void OnGet()
{
// Initialize Config
base.Init();
@ -27,7 +29,8 @@ namespace Monitor.Pages {
BuildAssetDistributionData();
}
private void BindData() {
private void BindData()
{
PTData = this.PtDataObject;
// Cleanup temp files
@ -38,7 +41,8 @@ namespace Monitor.Pages {
DateTimeNow = DateTimeOffset.UtcNow.ToOffset(offsetTimeSpan);
// Get last and current active setting
if (!String.IsNullOrEmpty(HttpContext.Session.GetString("LastGlobalSetting"))) {
if (!String.IsNullOrEmpty(HttpContext.Session.GetString("LastGlobalSetting")))
{
LastGlobalSetting = HttpContext.Session.GetString("LastGlobalSetting");
}
HttpContext.Session.SetString("LastGlobalSetting", Summary.CurrentGlobalSetting.SettingName);
@ -49,24 +53,32 @@ namespace Monitor.Pages {
BuildMarketTrendChartData();
BuildProfitChartData();
}
private void BuildMarketTrendChartData() {
if (MarketTrends.Count > 0) {
private void BuildMarketTrendChartData()
{
if (MarketTrends.Count > 0)
{
TrendChartDataJSON = "[";
int mtIndex = 0;
foreach (MarketTrend mt in MarketTrends) {
if (mt.DisplayGraph) {
foreach (MarketTrend mt in MarketTrends)
{
if (mt.DisplayGraph)
{
string lineColor = "";
if (mtIndex < Constants.ChartLineColors.Length) {
if (mtIndex < Constants.ChartLineColors.Length)
{
lineColor = Constants.ChartLineColors[mtIndex];
} else {
}
else
{
lineColor = Constants.ChartLineColors[mtIndex - 20];
}
if (Summary.MarketTrendChanges.ContainsKey(mt.Name)) {
if (Summary.MarketTrendChanges.ContainsKey(mt.Name))
{
List<MarketTrendChange> marketTrendChangeSummaries = Summary.MarketTrendChanges[mt.Name];
if (marketTrendChangeSummaries.Count > 0) {
if (marketTrendChangeSummaries.Count > 0)
{
if (!TrendChartDataJSON.Equals("[")) TrendChartDataJSON += ",";
TrendChartDataJSON += "{";
@ -79,9 +91,11 @@ namespace Monitor.Pages {
DateTime startDateTime = currentDateTime.AddHours(-PTMagicConfiguration.GeneralSettings.Monitor.GraphMaxTimeframeHours);
DateTime endDateTime = currentDateTime;
int trendChartTicks = 0;
for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes)) {
for (DateTime tickTime = startDateTime; tickTime <= endDateTime; tickTime = tickTime.AddMinutes(PTMagicConfiguration.GeneralSettings.Monitor.GraphIntervalMinutes))
{
List<MarketTrendChange> tickRange = marketTrendChangeSummaries.FindAll(m => m.TrendDateTime >= tickTime).OrderBy(m => m.TrendDateTime).ToList();
if (tickRange.Count > 0) {
if (tickRange.Count > 0)
{
MarketTrendChange mtc = tickRange.First();
if (tickTime != startDateTime) TrendChartDataJSON += ",\n";
if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0;
@ -92,17 +106,15 @@ namespace Monitor.Pages {
}
// Add most recent tick
List<MarketTrendChange> latestTickRange = marketTrendChangeSummaries.OrderByDescending(m => m.TrendDateTime).ToList();
if (latestTickRange.Count > 0) {
if (latestTickRange.Count > 0)
{
MarketTrendChange mtc = latestTickRange.First();
if (trendChartTicks > 0) TrendChartDataJSON += ",\n";
if (Double.IsInfinity(mtc.TrendChange)) mtc.TrendChange = 0;
TrendChartDataJSON += "{ x: new Date('" + mtc.TrendDateTime.ToString("yyyy-MM-ddTHH:mm:ss").Replace(".", ":") + "'), y: " + mtc.TrendChange.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}";
}
TrendChartDataJSON += "]";
TrendChartDataJSON += "}";
mtIndex++;
}
}
@ -112,19 +124,31 @@ namespace Monitor.Pages {
}
}
private void BuildProfitChartData() {
private void BuildProfitChartData()
{
int tradeDayIndex = 0;
string profitPerDayJSON = "";
if (PTData.SellLog.Count > 0) {
if (PTData.SellLog.Count > 0)
{
DateTime minSellLogDate = PTData.SellLog.OrderBy(sl => sl.SoldDate).First().SoldDate.Date;
DateTime graphStartDate = DateTime.UtcNow.Date.AddDays(-30);
if (minSellLogDate > graphStartDate) graphStartDate = minSellLogDate;
for (DateTime salesDate = graphStartDate; salesDate <= DateTime.UtcNow.Date; salesDate = salesDate.AddDays(1)) {
if (tradeDayIndex > 0) {
if (minSellLogDate > graphStartDate)
{
graphStartDate = minSellLogDate;
}
for (DateTime salesDate = graphStartDate; salesDate <= DateTime.UtcNow.Date; salesDate = salesDate.AddDays(1))
{
if (tradeDayIndex > 0)
{
profitPerDayJSON += ",\n";
}
int trades = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate).Count;
double profit = PTData.SellLog.FindAll(t => t.SoldDate.Date == salesDate).Sum(t => t.Profit);
if (PTData.Properties.Shorting)
{
profit = profit * (-1);
}
double profitFiat = Math.Round(profit * Summary.MainMarketPrice, 2);
profitPerDayJSON += "{x: new Date('" + salesDate.ToString("yyyy-MM-dd") + "'), y: " + profitFiat.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "}";
tradeDayIndex++;
@ -140,13 +164,51 @@ namespace Monitor.Pages {
}
private void BuildAssetDistributionData()
{
double PairsBalance = PTData.GetPairsBalance();
double DCABalance = PTData.GetDCABalance();
double PendingBalance = PTData.GetPendingBalance();
double DustBalance = PTData.GetDustBalance();
double TotalValue = PTData.GetCurrentBalance();
double AvailableBalance = (TotalValue - PairsBalance - DCABalance - PendingBalance - DustBalance);
// the per PT-Eelroy, the PT API doesn't provide these values when using leverage, so they are calculated here to cover either case.
double PairsBalance = 0.0;
double DCABalance = 0.0;
double PendingBalance = 0.0;
double AvailableBalance = PTData.GetCurrentBalance();
bool isSellStrategyTrue = false;
bool isTrailingSellActive = false;
foreach (Core.Main.DataObjects.PTMagicData.DCALogData dcaLogEntry in PTData.DCALog)
{
string sellStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, isSellStrategyTrue, isTrailingSellActive);
// Aggregate totals
if (dcaLogEntry.Leverage == 0)
{
if (sellStrategyText.Contains("PENDING"))
{
PendingBalance = PendingBalance + (dcaLogEntry.Amount * dcaLogEntry.CurrentPrice);
}
else if (dcaLogEntry.BuyStrategies.Count > 0)
{
DCABalance = DCABalance + (dcaLogEntry.Amount * dcaLogEntry.CurrentPrice);
}
else
{
PairsBalance = PairsBalance + (dcaLogEntry.Amount * dcaLogEntry.CurrentPrice);
}
}
else
{
if (sellStrategyText.Contains("PENDING"))
{
PendingBalance = PendingBalance + ((dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) / dcaLogEntry.Leverage);
}
else if (dcaLogEntry.BuyStrategies.Count > 0)
{
DCABalance = DCABalance + ((dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) / dcaLogEntry.Leverage);
}
else
{
PairsBalance = PairsBalance + ((dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) / dcaLogEntry.Leverage);
}
}
}
totalCurrentValue = PendingBalance + DCABalance + PairsBalance + AvailableBalance;
AssetDistributionData = "[";
AssetDistributionData += "{label: 'Pairs',color: '#82E0AA',value: '" + PairsBalance.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "'},";
AssetDistributionData += "{label: 'DCA',color: '#D98880',value: '" + DCABalance.ToString("0.00", new System.Globalization.CultureInfo("en-US")) + "'},";

View File

@ -8,66 +8,68 @@
<div class="col-md-5 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>
@if (Model.PTData.BuyLog.Count == 0) {
@if (Model.PTData.BuyLog.Count == 0)
{
<p>Your Profit Trailer did not find anything worth buying so far.</p>
} else {
}
else
{
<table class="table table-sm m-b-0">
<thead>
<tr>
<th>Market</th>
<th>24H Trend</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 class="text-right" data-toggle="tooltip" data-placement="top" title="Current ask price for this market">Ask Price</th>
</tr>
</thead>
<tbody>
@foreach (Core.Main.DataObjects.PTMagicData.BuyLogData buyLogEntry in Model.PTData.BuyLog.OrderBy(b => b.IsSom).
ThenByDescending(b => b.IsTrailing).
ThenByDescending(b => b.IsTrue).
ThenByDescending(b => b.TrueStrategyCount).
ThenByDescending(b => b.PercChange).
Take(Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDashboardBuyEntries)) {
Core.Main.DataObjects.PTMagicData.MarketPairSummary mps = null;
if (Model.Summary.MarketSummary.ContainsKey(buyLogEntry.Market)) {
mps = Model.Summary.MarketSummary[buyLogEntry.Market];
}
bool isTrailingBuyActive = buyLogEntry.IsTrailing;
if (buyLogEntry.BuyStrategies.Count > 0) {
isTrailingBuyActive = (buyLogEntry.BuyStrategies.FindAll(bs => bs.IsTrailing).Count > 0);
}
bool isBuyStrategyTrue = buyLogEntry.IsTrue;
if (buyLogEntry.BuyStrategies.Count > 0) {
isBuyStrategyTrue = (buyLogEntry.BuyStrategies.FindAll(bs => !bs.IsTrue).Count == 0);
}
bool buyDisabled = false;
string buyStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Model.Summary, buyLogEntry.BuyStrategies, buyLogEntry.BuyStrategy, isBuyStrategyTrue, isTrailingBuyActive);
if (!Core.ProfitTrailer.StrategyHelper.IsValidStrategy(buyStrategyText, true)) {
buyDisabled = 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>
} 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>
}
<td class="text-autocolor">@string.Format("{0}%", (buyLogEntry.PercChange * 100).ToString("#,#0.00"))</td>
@if (buyDisabled) {
<td>@Html.Raw(buyStrategyText)</td>
} else {
<td>@Html.Raw(buyStrategyText)</td>
}
<td class="text-right">@buyLogEntry.CurrentPrice.ToString("#,#0.00000000", new System.Globalization.CultureInfo("en-US"))</td>
</tr>
@foreach (Core.Main.DataObjects.PTMagicData.BuyLogData buyLogEntry in Model.PTData.BuyLog.OrderBy(b => b.IsSom).
ThenByDescending(b => b.IsTrailing).
ThenByDescending(b => b.IsTrue).
ThenByDescending(b => b.TrueStrategyCount).
ThenByDescending(b => b.PercChange).
Take(Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDashboardBuyEntries)) {
Core.Main.DataObjects.PTMagicData.MarketPairSummary mps = null;
if (Model.Summary.MarketSummary.ContainsKey(buyLogEntry.Market))
{
mps = Model.Summary.MarketSummary[buyLogEntry.Market];
}
bool isTrailingBuyActive = buyLogEntry.IsTrailing;
if (buyLogEntry.BuyStrategies.Count > 0) {
isTrailingBuyActive = (buyLogEntry.BuyStrategies.FindAll(bs => bs.IsTrailing).Count > 0);
}
bool isBuyStrategyTrue = buyLogEntry.IsTrue;
if (buyLogEntry.BuyStrategies.Count > 0) {
isBuyStrategyTrue = (buyLogEntry.BuyStrategies.FindAll(bs => !bs.IsTrue).Count == 0);
}
bool buyDisabled = false;
string buyStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Model.Summary, buyLogEntry.BuyStrategies, buyLogEntry.BuyStrategy, isBuyStrategyTrue, isTrailingBuyActive);
if (!Core.ProfitTrailer.StrategyHelper.IsValidStrategy(buyStrategyText, true)) {
buyDisabled = 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>
} 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>
}
<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>
@if (buyDisabled) {
<td>@Html.Raw(buyStrategyText)</td>
} else {
<td>@Html.Raw(buyStrategyText)</td>
}
</tr>
}
</tbody>
</table>
@ -82,22 +84,23 @@
<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>
@if (Model.PTData.DCALog.Count == 0) {
@if (Model.PTData.DCALog.Count == 0)
{
<p>Profit Trailer is not reporting any holdings on your exchange.</p>
} else {
}
else
{
<div class="table-responsive">
<table class="table table-sm m-b-0">
<thead>
<tr>
<th>Market</th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Market trend last 24 hours">24H Trend</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></th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Active buy strategies">DCA Buy Strats</th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Active sell strategies">Sell Strats</th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Active buy strategies">DCA</th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Active sell strategies">Sell</th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Target profit to sell">Target</th>
<th class="text-left" data-toggle="tooltip" data-placement="top" title="Current Profit">Profit</th>
<th></th>
</tr>
@ -131,77 +134,101 @@
}
bool isSellStrategyTrue = false;
if (dcaLogEntry.BuyStrategies.Count > 0) {
if (dcaLogEntry.SellStrategies.Count > 0) {
isSellStrategyTrue = (dcaLogEntry.SellStrategies.FindAll(ss => !ss.IsTrue).Count == 0);
}
bool buyDisabled = false;
string leverage = "";
double leverageValue = 1;
string buyStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Model.Summary, dcaLogEntry.BuyStrategies, dcaLogEntry.BuyStrategy, isBuyStrategyTrue, isTrailingBuyActive);
if (!Core.ProfitTrailer.StrategyHelper.IsValidStrategy(buyStrategyText, true)) {
if (!Core.ProfitTrailer.StrategyHelper.IsValidStrategy(buyStrategyText, true))
{
buyDisabled = true;
}
string sellStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Model.Summary, dcaLogEntry.SellStrategies, dcaLogEntry.SellStrategy, isSellStrategyTrue, isTrailingSellActive);
// Check for when PT loses the value of a pair
bool lostValue = false;
lostValue = (dcaLogEntry.TotalCost == 0.0) || (dcaLogEntry.AverageBuyPrice == 0.0);
// Aggregate totals
Model.TotalBagCost = Model.TotalBagCost + dcaLogEntry.TotalCost;
double ExchangeFee = 0;
double exchangeFee = 0;
switch (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.ToLower())
{
case "binance":
ExchangeFee = 0.002;
exchangeFee = 0.002;
break;
case "binanceus":
ExchangeFee = 0.002;
exchangeFee = 0.002;
break;
case "binancefutures":
ExchangeFee = 0.002;
exchangeFee = 0.002;
break;
case "bittrex":
ExchangeFee = 0.0025;
exchangeFee = 0.0025;
break;
case "poloniex":
ExchangeFee = 0.0025;
exchangeFee = 0.0025;
break;
default:
break;
}
double TradingFee = (dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) * ExchangeFee;
Model.TotalBagValue = Model.TotalBagValue + ((dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) - TradingFee);
// Aggregate totals
double tradingFee = (exchangeFee * dcaLogEntry.TotalCost) * 2;
double bagGain = (dcaLogEntry.ProfitPercent / 100) * dcaLogEntry.TotalCost * leverageValue;
Model.TotalBagCost = Model.TotalBagCost + dcaLogEntry.TotalCost;
Model.TotalBagGain = Model.TotalBagGain + bagGain;
// Render the row
<tr @(lostValue ? "class=errorRow" : "") >
@if (mps == null || mps.ActiveSingleSettings == null || mps.ActiveSingleSettings.Count == 0) {
@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>
} else {
} 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> <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">@Html.Raw((dcaLogEntry.PercChange * 100).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))%</td>
<td class="text-left">@Html.Raw(dcaLogEntry.TotalCost.ToString("#,#0.000000", new System.Globalization.CultureInfo("en-US")))</td>
<td class="text-right">
@if (dcaEnabled) {
@if (dcaLogEntry.BoughtTimes > 0) {
@if (dcaEnabled)
{
@if (dcaLogEntry.BoughtTimes > 0)
{
@dcaLogEntry.BoughtTimes;
}
} else {
} else
{
<span data-toggle="tooltip" data-placement="top" title="DCA is disabled"><i class="fa fa-ban text-highlight"></i></span>
}
</td>
<td>@Html.Raw(buyStrategyText)</td>
<td>@Html.Raw(sellStrategyText)</td>
@if(!@lostValue)
@if (sellStrategyText.Contains("CROSSED"))
// if leverage, recalculate profit target
{
string leverageText = sellStrategyText.Remove(0, sellStrategyText.IndexOf("CROSSED")+9);
leverage = leverageText.Remove(leverageText.IndexOf(".0)"), leverageText.Length - leverageText.IndexOf(".0)"));
leverageValue = double.Parse(leverage);
}
@if (sellStrategyText.Contains("ISOLATED"))
{
string leverageText = sellStrategyText.Remove(0, sellStrategyText.IndexOf("ISOLATED")+10);
leverage = leverageText.Remove(leverageText.IndexOf(".0)"), leverageText.Length - leverageText.IndexOf(".0)"));
leverageValue = double.Parse(leverage);
}
@if (leverageValue == 1)
{
<td class="@Html.Raw((dcaLogEntry.TargetGainValue.HasValue && (dcaLogEntry.ProfitPercent > dcaLogEntry.TargetGainValue.Value)) ? "text-success" : "text-danger")">@Html.Raw(dcaLogEntry.TargetGainValue.HasValue ? dcaLogEntry.TargetGainValue.Value.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%" : "&nbsp")</td>
}
else
{
double leverageTargetGain = leverageValue * dcaLogEntry.TargetGainValue.Value;
<td class="@Html.Raw((dcaLogEntry.TargetGainValue.HasValue && (dcaLogEntry.ProfitPercent > dcaLogEntry.TargetGainValue.Value)) ? "text-success" : "text-danger")">@Html.Raw(dcaLogEntry.TargetGainValue.HasValue ? leverageTargetGain.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%" : "&nbsp")</td>
}
@if (!@lostValue)
{
<td class="text-autocolor">@dcaLogEntry.ProfitPercent.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US"))%</td>
}
@ -209,20 +236,20 @@
{
<td class="text-left">No Value!</td>
}
<td class="text-right"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)_get/BagDetails/?m=@dcaLogEntry.Market" data-remote="false" data-toggle="modal" data-target="#dca-chart"><i class="fa fa-plus-circle"></i></a></td>
</tr>
}
<td>Totals:</td><td></td>
<td>Totals:</td>
<td></td>
<td>@Html.Raw(Model.TotalBagCost.ToString("#,#0.000000", new System.Globalization.CultureInfo("en-US")))</td>
<td></td><td></td><td></td>
<td class="text-autocolor">@Html.Raw((((Model.TotalBagValue - Model.TotalBagCost) / Model.TotalBagCost) * 100).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))%</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td class="text-autocolor">@Html.Raw((((Model.TotalBagGain) / Model.TotalBagCost) * 100).ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")))%</td>
</tbody>
</table>
</div>
@if (Model.PTData.DCALog.Count > Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDashboardBagEntries) {
<p class="text-right"><small><i class="fa fa-info-circle"></i> @Model.PTMagicConfiguration.GeneralSettings.Monitor.MaxDashboardBagEntries of @Model.PTData.DCALog.Count items listed - <a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)BagAnalyzer">View all items</a></small></p>
}
@ -232,11 +259,9 @@
</div>
<script src="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)assets/js/jquery.nicescroll.js"></script>
<script type="text/javascript">
(function ($) {
'use strict';
$("#dca-chart").on("show.bs.modal", function (e) {
$(this).find(".modal-content").html('<i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i>');
var link = $(e.relatedTarget);
@ -245,7 +270,5 @@
$('[data-toggle="tooltip"]').tooltip();
});
});
})(jQuery);
</script>

View File

@ -6,16 +6,15 @@ namespace Monitor.Pages {
public class DashboardTopModel : _Internal.BasePageModelSecureAJAX {
public ProfitTrailerData PTData = null;
public DateTimeOffset DateTimeNow = Constants.confMinDate;
public void OnGet() {
// Initialize Config
base.Init();
BindData();
}
public double TotalBagCost = 0;
public double TotalBagValue = 0;
public double TotalBagGain = 0;
private void BindData() {
PTData = this.PtDataObject;

View File

@ -13,7 +13,7 @@
indent_size: 2
};
editor.setTheme("ace/theme/ptmagic");
editor.getSession().setMode("ace/mode/java");
editor.getSession().setMode("ace/mode/yaml");
editor.getSession().setValue(txtArea.val());
editor.getSession().on('change', function () {
txtArea.val(editor.getSession().getValue());

View File

@ -173,6 +173,10 @@ a:active {
stroke-opacity: 1 !important;
}
.trend-chart
{
clear: none;
}
/* Custom menu styles */
#topnav .navigation-menu {
@ -211,4 +215,69 @@ a:active {
#topnav .has-submenu.active>a {
color: #50cefc;
}
}
/* Circolus */
.cdev {
position: absolute;
height : 30px;
width : 30px;
top: 5px;
left: 0;
margin : 0 0 0 10px;
display: inline;
}
.cdev div {
position : absolute;
height : 30px;
width : 30px;
border-radius: 50%;
}
.cdev div span {
position : absolute;
font-family : Arial;
font-size : 5px;
line-height : 7px;
height : 20px;
width : 20px;
left : 5px;
top : 5px;
text-align : center;
border-radius : 50%;
background-color: #36404a;
color: #36404a;
}
.cdev .background {
background-color: #b3cef6;
}
.cdev .rotate {
clip : rect(0 15px 30px 0);
background-color: #4b86db;
}
.cdev .left {
clip : rect(0 15px 30px 0);
opacity : 1;
background-color: #b3cef6;
}
.cdev .right {
clip : rect(0 15px 30px 0);
transform : rotate(180deg);
opacity : 0;
background-color: #4b86db;
}
@keyframes toggle {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,62 @@
/*
jQuery-Circle-Plugin v0.1 by Jamal hassouni
https://github.com/jamalhassouni
*/
(function ($) {
// circle animation pluqins by jamal hassouni
$.fn.circlos = function () {
// deafualt options
var DEFAULTS = {
backgroundColor: '#b3cef6', // default background color
progressColor: '#4b86db', // default progress color
percent: 75, // default percent value
duration: 2000 // default duration value
};
$(this).each(function () {
var $target = $(this);
// options of circle
var options = {
// if isset value of background if not use the default value of background color
backgroundColor: $target.data('color') ? $target.data('color').split(',')[0] : DEFAULTS.backgroundColor,
// if isset value of progress if not use the default value of progress color
progressColor: $target.data('color') ? $target.data('color').split(',')[1] : DEFAULTS.progressColor,
// if isset value of percent if not use the default value of percent
percent: $target.data('percent') ? $target.data('percent') : DEFAULTS.percent,
// if isset value of duration if not use the default value of duration
duration: $target.data('duration') ? $target.data('duration') : DEFAULTS.duration
};
console.log(options);
// add divs for structure
$target.append('<div class="background"></div><div class="rotate"></div><div class="left"></div><div class="right"></div><div class=""><span>' + options.percent + '%</span></div>');
// change style of the circle with the options values
$target.find('.background').css('background-color', options.backgroundColor);
$target.find('.left').css('background-color', options.backgroundColor);
$target.find('.rotate').css('background-color', options.progressColor);
$target.find('.right').css('background-color', options.progressColor);
var $rotate = $target.find('.rotate');
setTimeout(function () {
$rotate.css({
'transition': 'transform ' + options.duration + 'ms linear',
'transform': 'rotate(' + options.percent * 3.6 + 'deg)'
});
},1);
if (options.percent > 50) {
// add animation for the right class and left class
var animationRight = 'toggle ' + (options.duration / options.percent * 50) + 'ms step-end';
var animationLeft = 'toggle ' + (options.duration / options.percent * 50) + 'ms step-start';
$target.find('.right').css({
animation: animationRight,
opacity: 1
});
$target.find('.left').css({
animation: animationLeft,
opacity: 0
});
}
});
}
})(jQuery);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,9 @@
define("ace/mode/yaml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"list.markup",regex:/^(?:-{3}|\.{3})\s*(?=#|$)/},{token:"list.markup",regex:/^\s*[\-?](?:$|\s)/},{token:"constant",regex:"!![\\w//]+"},{token:"constant.language",regex:"[&\\*][a-zA-Z0-9-_]+"},{token:["meta.tag","keyword"],regex:/^(\s*\w.*?)(:(?=\s|$))/},{token:["meta.tag","keyword"],regex:/(\w+?)(\s*:(?=\s|$))/},{token:"keyword.operator",regex:"<<\\w*:\\w*"},{token:"keyword.operator",regex:"-\\s*(?=[{])"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:/[|>][-+\d\s]*$/,onMatch:function(e,t,n,r){var i=/^\s*/.exec(r)[0];return n.length<1?n.push(this.next):n[0]="mlString",n.length<2?n.push(i.length):n[1]=i.length,this.token},next:"mlString"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:/(\b|[+\-\.])[\d_]+(?:(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)(?=[^\d-\w]|$)/},{token:"constant.numeric",regex:/[+\-]?\.inf\b|NaN\b|0x[\dA-Fa-f_]+|0b[10_]+/},{token:"constant.language.boolean",regex:"\\b(?:true|false|TRUE|FALSE|True|False|yes|no)\\b"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:/[^\s,:\[\]\{\}]+/}],mlString:[{token:"indent",regex:/^\s*$/},{token:"indent",regex:/^\s*/,onMatch:function(e,t,n){var r=n[1];return r>=e.length?(this.next="start",n.splice(0)):this.next="mlString",this.token},next:"mlString"},{token:"string",regex:".+"}]},this.normalizeRules()};r.inherits(s,i),t.YamlHighlightRules=s}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++n<f){o=e.getLine(n);var h=o.search(i);if(h==-1)continue;if(o[h]!="#")break;c=n}if(c>l){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u<a?"start":"","";if(u==-1){if(i==a&&r[i]=="#"&&s[i]=="#")return e.foldWidgets[n-1]="",e.foldWidgets[n+1]="","start"}else if(u==i&&r[i]=="#"&&o[i]=="#"&&e.getLine(n-2).search(/\S/)==-1)return e.foldWidgets[n-1]="start",e.foldWidgets[n+1]="","";return u!=-1&&u<i?e.foldWidgets[n-1]="start":e.foldWidgets[n-1]="",i<a?"start":""}}.call(o.prototype)}),define("ace/mode/yaml",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/yaml_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/folding/coffee"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./yaml_highlight_rules").YamlHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./folding/coffee").FoldMode,a=function(){this.HighlightRules=s,this.$outdent=new o,this.foldingRules=new u,this.$behaviour=this.$defaultBehaviour};r.inherits(a,i),function(){this.lineCommentStart=["#","//"],this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t);if(e=="start"){var i=t.match(/^.*[\{\(\[]\s*$/);i&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/yaml"}.call(a.prototype),t.Mode=a});
(function() {
window.require(["ace/mode/yaml"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();

View File

@ -1,9 +1,43 @@
define("ace/theme/ptmagic",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!0,t.cssClass="ace-ptmagic",t.cssText=".ace-ptmagic .ace_gutter {background: #414d59;color: #8F938F}.ace-ptmagic .ace_constant.ace_language.ace_boolean {color: #CCCCCC}.ace-ptmagic .ace_print-margin {width: 1px;background: #414d59}.ace-ptmagic {background-color: #414d59;color: #8F938F}.ace-ptmagic .ace_cursor {color: #A7A7A7}.ace-ptmagic .ace_marker-layer .ace_selection {background: rgba(221, 240, 255, 0.20)}.ace-ptmagic.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #414d59;}.ace-ptmagic .ace_marker-layer .ace_step {background: rgb(102, 82, 0)}.ace-ptmagic .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgba(255, 255, 255, 0.25)}.ace-ptmagic .ace_marker-layer .ace_active-line {background: rgba(255, 255, 255, 0.031)}.ace-ptmagic .ace_gutter-active-line {background-color: rgba(255, 255, 255, 0.031)}.ace-ptmagic .ace_marker-layer .ace_selected-word {border: 1px solid rgba(221, 240, 255, 0.20)}.ace-ptmagic .ace_invisible {color: rgba(255, 255, 255, 0.25)}.ace-ptmagic .ace_keyword,.ace-ptmagic .ace_meta {color: #757aD8}.ace-ptmagic .ace_constant,.ace-ptmagic .ace_constant.ace_character,.ace-ptmagic .ace_constant.ace_character.ace_escape,.ace-ptmagic .ace_constant.ace_other {color: #4FB7C5}.ace-ptmagic .ace_keyword.ace_operator {color: #797878}.ace-ptmagic .ace_constant.ace_character {color: #AFA472}.ace-ptmagic .ace_constant.ace_language {color: #DE8E30}.ace-ptmagic .ace_constant.ace_numeric {color: #CCCCCC}.ace-ptmagic .ace_invalid,.ace-ptmagic .ace_invalid.ace_illegal {color: #F8F8F8;background-color: rgba(86, 45, 86, 0.75)}.ace-ptmagic .ace_invalid.ace_deprecated {text-decoration: underline;font-style: italic;color: #D2A8A1}.ace-ptmagic .ace_fold {background-color: #757aD8;border-color: #8F938F}.ace-ptmagic .ace_support.ace_function {color: #AEB2F8}.ace-ptmagic .ace_string {color: #66A968}.ace-ptmagic .ace_string.ace_regexp {color: #E9C062}.ace-ptmagic .ace_comment {color: #A6C6FF}.ace-ptmagic .ace_variable {color: #BEBF55}.ace-ptmagic .ace_variable.ace_language {color: #C1C144}.ace-ptmagic .ace_xml-pe {color: #494949}.ace-ptmagic .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYIiPj/8PAARgAh2NTMh8AAAAAElFTkSuQmCC) right repeat-y}";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)});
(function() {
window.require(["ace/theme/ptmagic"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();
define("ace/theme/ptmagic", ["require", "exports", "module", "ace/lib/dom"],
function (e, t, n) {
t.isDark = !0,
t.cssClass = "ace-ptmagic",
t.cssText = "\
.ace-ptmagic .ace_gutter {background: #566776;color: #dddddd}\
.ace-ptmagic .ace_constant.ace_language.ace_boolean {color: #CCCCCC}\
.ace-ptmagic .ace_print-margin {width: 1px;background: #414d59}\
.ace-ptmagic {background-color: #414d59;color: #8F938F}\
.ace-ptmagic .ace_cursor {color: #A7A7A7}\
.ace-ptmagic .ace_marker-layer .ace_selection {background: rgba(221, 240, 255, 0.20)}\
.ace-ptmagic.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #414d59;}\
.ace-ptmagic .ace_marker-layer .ace_step {background: rgb(102, 82, 0)}\
.ace-ptmagic .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgba(255, 255, 255, 0.25)}\
.ace-ptmagic .ace_marker-layer .ace_active-line {background: rgba(255, 255, 255, 0.031)}\
.ace-ptmagic .ace_gutter-active-line {background-color: rgba(255, 255, 255, 0.031)}\
.ace-ptmagic .ace_marker-layer .ace_selected-word {border: 1px solid rgba(221, 240, 255, 0.20)}\
.ace-ptmagic .ace_invisible {color: rgba(255, 255, 255, 0.25)}\
.ace-ptmagic .ace_keyword,.ace-ptmagic .ace_meta {color: #757aD8}\
.ace-ptmagic .ace_constant,.ace-ptmagic .ace_constant.ace_character,.ace-ptmagic .ace_constant.ace_character.ace_escape,.ace-ptmagic .ace_constant.ace_other {color: #4FB7C5}\
.ace-ptmagic .ace_keyword.ace_operator {color: #797878}\.ace-ptmagic .ace_constant.ace_character {color: #AFA472}\
.ace-ptmagic .ace_constant.ace_language {color: #DE8E30}\.ace-ptmagic .ace_constant.ace_numeric {color: #CCCCCC}\
.ace-ptmagic .ace_invalid,.ace-ptmagic .ace_invalid.ace_illegal {color: #F8F8F8;background-color: rgba(86, 45, 86, 0.75)}\
.ace-ptmagic .ace_invalid.ace_deprecated {text-decoration: underline;font-style: italic;color: #D2A8A1}\
.ace-ptmagic .ace_fold {background-color: #757aD8;border-color: #8F938F}\
.ace-ptmagic .ace_support.ace_function {color: #AEB2F8}\
.ace-ptmagic .ace_string {color: #66A968}\
.ace-ptmagic .ace_string.ace_regexp {color: #E9C062}\
.ace-ptmagic .ace_comment {color: #33b5e5}\
.ace-ptmagic .ace_variable {color: #BEBF55}\
.ace-ptmagic .ace_variable.ace_language {color: #C1C144}\
.ace-ptmagic .ace_xml-pe {color: #494949}\
.ace-ptmagic .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYIiPj/8PAARgAh2NTMh8AAAAAElFTkSuQmCC) right repeat-y}";
var r = e("../lib/dom"); r.importCssString(t.cssText, t.cssClass)
});
(function () {
window.require(["ace/theme/ptmagic"], function (m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();

View File

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

View File

@ -0,0 +1,61 @@
PT API properties (unavailable via swagger UI)
--------------------------------------------
{"sellOnlyMode":false
"role":"A"
"enableConfig":true
"enableShutdown":true
"passwordSet":true
"language":"ENUS"
"currency":"USD"
"skin":"dark"
"testMode":false
"shorting":true
"margin":false
"testnet":false
"activeConfig":"MyConfig1"
"availableConfigs":"[\"MyConfig1\"
\"MyConfig2\"
\"MyConfig3\"
\"MyConfig4\"
\"MyConfig5\"]"
"availableSignalProviders":"[]"
"publicConfigs":"[\"public-ProfitTrailer_Three_Amigos-V4\"
\"public-ProfitTrailer_Conners2RSI-V2\"
\"public-ProfitTrailer_Fibonacci_Day_Trader-V1\"
\"public-ProfitTrailer_Signals_Template-V2\"
\"public-AwesomeSignals_Starter-V1\"
\"public-ProfitTrailer_Nifty_Fifty-V1\"
\"public-CryptosetSignal_ADVconfig-V5\"
\"public-ProfitTrailer_Trade_Panther-V1\"
\"public-Collective_Signals_Template-V4\"
\"public-ProfitTrailer_Fibonacci_Swing_Trader-V2\"
\"public-ProfitTrailer_RocketMan-V2\"
\"public-ProfitTrailer_OldFaithful-V6\"
\"public-ProfitTrailer_FrankenStrategy-V8\"
\"public-ProfitTrailer_Jobbing_The_Market-V5\"
\"public-ProfitTrailer_Golden_Cross-V3\"
\"public-ProfitTrailer_Spanish_Cross-V5\"
\"public-ProfitTrailer_Monte_Carlo-V8\"
\"public-ProfitTrailer_Bride_of_FrankenStrategy-V5\"
\"public-ProfitTrailer_Base_Settings-V3\"
\"public-ProfitTrailer_DoubleCross-V8\"
\"public-ProfitTrailer_ElToro-V7\"
\"public-ProfitTrailer_ElDorado-V8\"]"
"myPublicConfigs":"[]"
"myProducts":"[]"
"isAllowedToPublish":false
"maxConfigsAllowed":13
"licenseType":"PRO"
"upTime":"2020-07-17T15:30:44.140"
"port":8095
"sslEnabled":false
"displayAdvancedStats":true
"displayFullVersion":true
"displayButtons":true
"isMarketOrderSupported":true
"isLeverageExchange":true
"hasTestNet":false
"isPTFeederEnabled":false
"baseUrl":"http://x.x.x.x:xxxx"}