Merge pull request #150 from PTMagicians/develop
PT API v2 and Binance US
This commit is contained in:
commit
ed85dcdf4b
|
@ -4,12 +4,8 @@ using System.Collections.Generic;
|
|||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using System.Diagnostics;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Core.Main.DataObjects.PTMagicData;
|
||||
|
||||
|
@ -26,55 +22,13 @@ namespace Core.Main.DataObjects
|
|||
private PTMagicConfiguration _systemConfiguration = null;
|
||||
private TransactionData _transactionData = null;
|
||||
private DateTimeOffset _dateTimeNow = Constants.confMinDate;
|
||||
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();
|
||||
|
||||
public ProfitTrailerData(PTMagicConfiguration systemConfiguration)
|
||||
{
|
||||
_systemConfiguration = systemConfiguration;
|
||||
|
||||
string html = "";
|
||||
string url = systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL + "api/data?token=" + systemConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken;
|
||||
|
||||
// Get the data from PT
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||
request.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
request.KeepAlive = true;
|
||||
|
||||
WebResponse response = request.GetResponse();
|
||||
Stream dataStream = response.GetResponseStream();
|
||||
StreamReader reader = new StreamReader(dataStream);
|
||||
html = reader.ReadToEnd();
|
||||
reader.Close();
|
||||
response.Close();
|
||||
|
||||
// Parse the JSON and build the data sets
|
||||
dynamic rawPTData = JObject.Parse(html);
|
||||
|
||||
Parallel.Invoke(() =>
|
||||
{
|
||||
_summary = BuildSummaryData(rawPTData);
|
||||
},
|
||||
() =>
|
||||
{
|
||||
if (rawPTData.sellLogData != null)
|
||||
{
|
||||
this.BuildSellLogData(rawPTData.sellLogData, _systemConfiguration);
|
||||
}
|
||||
},
|
||||
() =>
|
||||
{
|
||||
if (rawPTData.bbBuyLogData != null)
|
||||
{
|
||||
this.BuildBuyLogData(rawPTData.bbBuyLogData);
|
||||
}
|
||||
},
|
||||
() =>
|
||||
{
|
||||
if (rawPTData.dcaLogData != null)
|
||||
{
|
||||
this.BuildDCALogData(rawPTData.dcaLogData, rawPTData.gainLogData, rawPTData.pendingLogData, rawPTData.watchModeLogData, _systemConfiguration);
|
||||
}
|
||||
});
|
||||
|
||||
// Convert local offset time to UTC
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
_dateTimeNow = DateTimeOffset.UtcNow.ToOffset(offsetTimeSpan);
|
||||
|
@ -84,6 +38,19 @@ namespace Core.Main.DataObjects
|
|||
{
|
||||
get
|
||||
{
|
||||
if (_summary == null || (DateTime.UtcNow > _summaryRefresh))
|
||||
{
|
||||
lock (_summaryLock)
|
||||
{
|
||||
// Thread double locking
|
||||
if (_summary == null || (DateTime.UtcNow > _summaryRefresh))
|
||||
{
|
||||
_summary = BuildSummaryData(GetDataFromProfitTrailer("api/v2/data/misc"));
|
||||
_summaryRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _summary;
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +58,20 @@ namespace Core.Main.DataObjects
|
|||
{
|
||||
get
|
||||
{
|
||||
if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh))
|
||||
{
|
||||
lock (_sellLock)
|
||||
{
|
||||
// Thread double locking
|
||||
if (_sellLog == null || (DateTime.UtcNow > _sellLogRefresh))
|
||||
{
|
||||
_sellLog.Clear();
|
||||
this.BuildSellLogData(GetDataFromProfitTrailer("/api/v2/data/sales"));
|
||||
_sellLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.RefreshSeconds - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _sellLog;
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +80,7 @@ namespace Core.Main.DataObjects
|
|||
{
|
||||
get
|
||||
{
|
||||
return _sellLog.FindAll(sl => sl.SoldDate.Date == _dateTimeNow.DateTime.Date);
|
||||
return SellLog.FindAll(sl => sl.SoldDate.Date == _dateTimeNow.DateTime.Date);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,7 +88,7 @@ namespace Core.Main.DataObjects
|
|||
{
|
||||
get
|
||||
{
|
||||
return _sellLog.FindAll(sl => sl.SoldDate.Date == _dateTimeNow.DateTime.AddDays(-1).Date);
|
||||
return SellLog.FindAll(sl => sl.SoldDate.Date == _dateTimeNow.DateTime.AddDays(-1).Date);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,7 +96,7 @@ namespace Core.Main.DataObjects
|
|||
{
|
||||
get
|
||||
{
|
||||
return _sellLog.FindAll(sl => sl.SoldDate.Date >= _dateTimeNow.DateTime.AddDays(-7).Date);
|
||||
return SellLog.FindAll(sl => sl.SoldDate.Date >= _dateTimeNow.DateTime.AddDays(-7).Date);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,7 +104,7 @@ namespace Core.Main.DataObjects
|
|||
{
|
||||
get
|
||||
{
|
||||
return _sellLog.FindAll(sl => sl.SoldDate.Date >= _dateTimeNow.DateTime.AddDays(-30).Date);
|
||||
return SellLog.FindAll(sl => sl.SoldDate.Date >= _dateTimeNow.DateTime.AddDays(-30).Date);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,6 +112,40 @@ namespace Core.Main.DataObjects
|
|||
{
|
||||
get
|
||||
{
|
||||
if (_dcaLog == null || (DateTime.UtcNow > _dcaLogRefresh))
|
||||
{
|
||||
lock (_dcaLock)
|
||||
{
|
||||
// Thread double locking
|
||||
if (_dcaLog == null || (DateTime.UtcNow > _dcaLogRefresh))
|
||||
{
|
||||
dynamic dcaData = null, pairsData = null, pendingData = null, watchData = null;
|
||||
_dcaLog.Clear();
|
||||
|
||||
Parallel.Invoke(() =>
|
||||
{
|
||||
dcaData = GetDataFromProfitTrailer("/api/v2/data/dca", true);
|
||||
},
|
||||
() =>
|
||||
{
|
||||
pairsData = GetDataFromProfitTrailer("/api/v2/data/pairs", true);
|
||||
},
|
||||
() =>
|
||||
{
|
||||
pendingData = GetDataFromProfitTrailer("/api/v2/data/pending", true);
|
||||
|
||||
},
|
||||
() =>
|
||||
{
|
||||
watchData = GetDataFromProfitTrailer("/api/v2/data/watchmode", true);
|
||||
});
|
||||
|
||||
this.BuildDCALogData(dcaData, pairsData, pendingData, watchData);
|
||||
_dcaLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.BagAnalyzerRefreshSeconds - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _dcaLog;
|
||||
}
|
||||
}
|
||||
|
@ -139,6 +154,20 @@ namespace Core.Main.DataObjects
|
|||
{
|
||||
get
|
||||
{
|
||||
if (_buyLog == null || (DateTime.UtcNow > _buyLogRefresh))
|
||||
{
|
||||
lock (_buyLock)
|
||||
{
|
||||
// Thread double locking
|
||||
if (_buyLog == null || (DateTime.UtcNow > _buyLogRefresh))
|
||||
{
|
||||
_buyLog.Clear();
|
||||
this.BuildBuyLogData(GetDataFromProfitTrailer("/api/v2/data/pbl", true));
|
||||
_buyLogRefresh = DateTime.UtcNow.AddSeconds(_systemConfiguration.GeneralSettings.Monitor.BuyAnalyzerRefreshSeconds - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _buyLog;
|
||||
}
|
||||
}
|
||||
|
@ -154,36 +183,34 @@ namespace Core.Main.DataObjects
|
|||
|
||||
public double GetCurrentBalance()
|
||||
{
|
||||
return
|
||||
return
|
||||
(this.Summary.Balance +
|
||||
this.Summary.PairsValue +
|
||||
this.Summary.DCAValue +
|
||||
this.Summary.PendingValue +
|
||||
this.Summary.DustValue);
|
||||
}
|
||||
public double GetPairsBalance()
|
||||
public double GetPairsBalance()
|
||||
{
|
||||
return
|
||||
return
|
||||
(this.Summary.PairsValue);
|
||||
}
|
||||
public double GetDCABalance()
|
||||
public double GetDCABalance()
|
||||
{
|
||||
return
|
||||
return
|
||||
(this.Summary.DCAValue);
|
||||
}
|
||||
public double GetPendingBalance()
|
||||
public double GetPendingBalance()
|
||||
{
|
||||
return
|
||||
return
|
||||
(this.Summary.PendingValue);
|
||||
}
|
||||
public double GetDustBalance()
|
||||
{
|
||||
return
|
||||
return
|
||||
(this.Summary.DustValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public double GetSnapshotBalance(DateTime snapshotDateTime)
|
||||
{
|
||||
double result = _systemConfiguration.GeneralSettings.Application.StartBalance;
|
||||
|
@ -197,6 +224,39 @@ namespace Core.Main.DataObjects
|
|||
return result;
|
||||
}
|
||||
|
||||
private dynamic GetDataFromProfitTrailer(string callPath, bool arrayReturned = false)
|
||||
{
|
||||
string rawBody = "";
|
||||
string url = string.Format("{0}{1}?token={2}", _systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL, callPath, _systemConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken);
|
||||
|
||||
// Get the data from PT
|
||||
Debug.WriteLine(String.Format("{0} - Calling '{1}'", DateTime.UtcNow, url));
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||
request.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
request.KeepAlive = true;
|
||||
|
||||
WebResponse response = request.GetResponse();
|
||||
|
||||
using (Stream dataStream = response.GetResponseStream())
|
||||
{
|
||||
StreamReader reader = new StreamReader(dataStream);
|
||||
rawBody = reader.ReadToEnd();
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
response.Close();
|
||||
|
||||
// Parse the JSON and build the data sets
|
||||
if (!arrayReturned)
|
||||
{
|
||||
return JObject.Parse(rawBody);
|
||||
}
|
||||
else
|
||||
{
|
||||
return JArray.Parse(rawBody);
|
||||
}
|
||||
}
|
||||
|
||||
private SummaryData BuildSummaryData(dynamic PTData)
|
||||
{
|
||||
return new SummaryData()
|
||||
|
@ -210,9 +270,9 @@ namespace Core.Main.DataObjects
|
|||
};
|
||||
}
|
||||
|
||||
private void BuildSellLogData(dynamic rawSellLogData, PTMagicConfiguration systemConfiguration)
|
||||
private void BuildSellLogData(dynamic rawSellLogData)
|
||||
{
|
||||
foreach (var rsld in rawSellLogData)
|
||||
foreach (var rsld in rawSellLogData.data)
|
||||
{
|
||||
SellLogData sellLogData = new SellLogData();
|
||||
sellLogData.SoldAmount = rsld.soldAmount;
|
||||
|
@ -236,7 +296,7 @@ namespace Core.Main.DataObjects
|
|||
DateTimeOffset ptSoldDate = DateTimeOffset.Parse(dtDateTime.Year.ToString() + "-" + dtDateTime.Month.ToString("00") + "-" + dtDateTime.Day.ToString("00") + "T" + dtDateTime.Hour.ToString("00") + ":" + dtDateTime.Minute.ToString("00") + ":" + dtDateTime.Second.ToString("00"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
|
||||
|
||||
// Convert UTC sales time to local offset time
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(_systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
ptSoldDate = ptSoldDate.ToOffset(offsetTimeSpan);
|
||||
|
||||
sellLogData.SoldDate = ptSoldDate.DateTime;
|
||||
|
@ -245,7 +305,7 @@ namespace Core.Main.DataObjects
|
|||
}
|
||||
}
|
||||
|
||||
private void BuildDCALogData(dynamic rawDCALogData, dynamic rawPairsLogData, dynamic rawPendingLogData, dynamic rawWatchModeLogData, PTMagicConfiguration systemConfiguration)
|
||||
private void BuildDCALogData(dynamic rawDCALogData, dynamic rawPairsLogData, dynamic rawPendingLogData, dynamic rawWatchModeLogData)
|
||||
{
|
||||
foreach (var rdld in rawDCALogData)
|
||||
{
|
||||
|
@ -327,7 +387,7 @@ namespace Core.Main.DataObjects
|
|||
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);
|
||||
|
||||
// Convert UTC bought time to local offset time
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(_systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
ptFirstBoughtDate = ptFirstBoughtDate.ToOffset(offsetTimeSpan);
|
||||
|
||||
dcaLogData.FirstBoughtDate = ptFirstBoughtDate.DateTime;
|
||||
|
@ -387,7 +447,7 @@ namespace Core.Main.DataObjects
|
|||
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
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(_systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
ptFirstBoughtDate = ptFirstBoughtDate.ToOffset(offsetTimeSpan);
|
||||
|
||||
dcaLogData.FirstBoughtDate = ptFirstBoughtDate.DateTime;
|
||||
|
@ -447,7 +507,7 @@ namespace Core.Main.DataObjects
|
|||
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
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(_systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
ptFirstBoughtDate = ptFirstBoughtDate.ToOffset(offsetTimeSpan);
|
||||
|
||||
dcaLogData.FirstBoughtDate = ptFirstBoughtDate.DateTime;
|
||||
|
@ -507,7 +567,7 @@ namespace Core.Main.DataObjects
|
|||
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
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(_systemConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
ptFirstBoughtDate = ptFirstBoughtDate.ToOffset(offsetTimeSpan);
|
||||
|
||||
dcaLogData.FirstBoughtDate = ptFirstBoughtDate.DateTime;
|
||||
|
@ -519,9 +579,6 @@ namespace Core.Main.DataObjects
|
|||
|
||||
_dcaLog.Add(dcaLogData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void BuildBuyLogData(dynamic rawBuyLogData)
|
||||
|
@ -558,7 +615,7 @@ namespace Core.Main.DataObjects
|
|||
buyStrategy.IsTrue = ((string)(bs.positive)).IndexOf("true", StringComparison.InvariantCultureIgnoreCase) > -1;
|
||||
|
||||
// Is SOM?
|
||||
buyLogData.IsSom = buyLogData.IsSom || buyStrategy.Name.Equals("som enabled", StringComparison.OrdinalIgnoreCase);
|
||||
buyLogData.IsSom = buyLogData.IsSom || buyStrategy.Name.Contains("som enabled", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// Is the pair trailing?
|
||||
buyLogData.IsTrailing = buyLogData.IsTrailing || buyStrategy.IsTrailing;
|
||||
|
|
|
@ -557,6 +557,9 @@ namespace Core.Helper
|
|||
case "Binance":
|
||||
result = "https://www.binance.com/trade.html?symbol=" + market;
|
||||
break;
|
||||
case "BinanceUS":
|
||||
result = "https://www.binance.us/trade.html?symbol=" + market;
|
||||
break;
|
||||
case "Poloniex":
|
||||
result = "https://poloniex.com/exchange#" + market.ToLower();
|
||||
break;
|
||||
|
@ -578,6 +581,9 @@ namespace Core.Helper
|
|||
case "Binance":
|
||||
result = market + mainMarket;
|
||||
break;
|
||||
case "BinanceUS":
|
||||
result = market + mainMarket;
|
||||
break;
|
||||
case "Poloniex":
|
||||
result = mainMarket + "_" + market;
|
||||
break;
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace Core.Main
|
|||
private Dictionary<string, List<MarketTrendChange>> _globalMarketTrendChanges = new Dictionary<string, List<MarketTrendChange>>();
|
||||
private Dictionary<string, int> _singleMarketSettingsCount = new Dictionary<string, int>();
|
||||
Dictionary<string, List<SingleMarketSetting>> _triggeredSingleMarketSettings = new Dictionary<string, List<SingleMarketSetting>>();
|
||||
private static readonly object _lockObj = new object();
|
||||
private static volatile object _lockObj = new object();
|
||||
|
||||
public LogHelper Log
|
||||
{
|
||||
|
@ -695,6 +695,7 @@ namespace Core.Main
|
|||
// Check if exchange is valid
|
||||
if (!this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance", StringComparison.InvariantCultureIgnoreCase)
|
||||
&& !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex", StringComparison.InvariantCultureIgnoreCase)
|
||||
&& !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("BinanceUS", StringComparison.InvariantCultureIgnoreCase)
|
||||
&& !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
this.Log.DoLogError("Exchange '" + this.PTMagicConfiguration.GeneralSettings.Application.Exchange + "' specified in settings.general.json is invalid! Terminating process...");
|
||||
|
@ -1091,7 +1092,7 @@ namespace Core.Main
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance", StringComparison.InvariantCultureIgnoreCase) && !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex", StringComparison.InvariantCultureIgnoreCase) && !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex", StringComparison.InvariantCultureIgnoreCase))
|
||||
if (!this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance", StringComparison.InvariantCultureIgnoreCase) && !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("BinanceUS", StringComparison.InvariantCultureIgnoreCase) && !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex", StringComparison.InvariantCultureIgnoreCase) && !this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
Log.DoLogError("Your setting for Application.Exchange in settings.general.json is invalid (" + this.PTMagicConfiguration.GeneralSettings.Application.Exchange + ")! Terminating process.");
|
||||
this.Timer.Stop();
|
||||
|
@ -1245,6 +1246,11 @@ namespace Core.Main
|
|||
// Get most recent market data from Binance
|
||||
this.ExchangeMarketList = Binance.GetMarketData(this.LastRuntimeSummary.MainMarket, this.MarketInfos, this.PTMagicConfiguration, this.Log);
|
||||
}
|
||||
else if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("BinanceUS", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
// Get most recent market data from BinanceUS
|
||||
this.ExchangeMarketList = BinanceUS.GetMarketData(this.LastRuntimeSummary.MainMarket, this.MarketInfos, this.PTMagicConfiguration, this.Log);
|
||||
}
|
||||
else if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Poloniex", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
|
||||
|
|
|
@ -26,16 +26,20 @@ namespace Core.MarketAnalyzer
|
|||
request.ContentType = "application/json";
|
||||
request.UserAgent = "PTMagic.Import";
|
||||
request.KeepAlive = true;
|
||||
request.Timeout = 60000;
|
||||
|
||||
HttpWebResponse httpResponse = null;
|
||||
string jsonString = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
httpResponse = (HttpWebResponse)request.GetResponse();
|
||||
|
||||
StreamReader jsonReader = new StreamReader(httpResponse.GetResponseStream());
|
||||
string jsonString = jsonReader.ReadToEnd();
|
||||
jsonReader.Close();
|
||||
using (StreamReader jsonReader = new StreamReader(httpResponse.GetResponseStream()))
|
||||
{
|
||||
jsonString = jsonReader.ReadToEnd();
|
||||
jsonReader.Close();
|
||||
}
|
||||
|
||||
jsonObject = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(jsonString);
|
||||
|
||||
|
@ -43,26 +47,32 @@ namespace Core.MarketAnalyzer
|
|||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
// Error calling the service but we got a response so dump it.
|
||||
string responseString = string.Empty;
|
||||
var encoding = httpResponse.CharacterSet == "" ? Encoding.UTF8 : Encoding.GetEncoding(httpResponse.CharacterSet);
|
||||
log.DoLogCritical(string.Format("Error whilst calling {0} \nError: {1}", url, ex.Message), ex);
|
||||
|
||||
using (var stream = httpResponse.GetResponseStream())
|
||||
if (ex.Response != null)
|
||||
{
|
||||
var reader = new StreamReader(stream, encoding);
|
||||
responseString = reader.ReadToEnd();
|
||||
// Error calling the service but we got a response so dump it.
|
||||
string responseString = string.Empty;
|
||||
var response = ((HttpWebResponse)ex.Response);
|
||||
var encoding = response.CharacterSet == "" ? Encoding.UTF8 : Encoding.GetEncoding(response.CharacterSet);
|
||||
|
||||
using (var stream = response.GetResponseStream())
|
||||
{
|
||||
var reader = new StreamReader(stream, encoding);
|
||||
responseString = reader.ReadToEnd();
|
||||
}
|
||||
|
||||
log.DoLogCritical(String.Format("{0} - Response: ({1}) {2} : {3}", ex.Message, response.StatusCode, response.StatusDescription, responseString), ex);
|
||||
}
|
||||
|
||||
log.DoLogCritical(String.Format("{0} - Response: ({1}) {2} : {3}", ex.Message, httpResponse.StatusCode, httpResponse.StatusDescription, responseString), ex);
|
||||
|
||||
throw ex;
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.DoLogCritical(ex.Message, ex);
|
||||
}
|
||||
|
||||
return jsonObject;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static Newtonsoft.Json.Linq.JObject GetSimpleJsonObjectFromURL(string url, LogHelper log, bool swallowException)
|
||||
|
@ -346,15 +356,15 @@ namespace Core.MarketAnalyzer
|
|||
}
|
||||
|
||||
public static List<MarketTrendChange> GetMarketTrendChanges(
|
||||
string platform,
|
||||
string mainMarket,
|
||||
MarketTrend marketTrend,
|
||||
List<string> marketList,
|
||||
Dictionary<string, Market> recentMarkets,
|
||||
Dictionary<string, Market> trendMarkets,
|
||||
string sortBy,
|
||||
bool isGlobal,
|
||||
PTMagicConfiguration systemConfiguration,
|
||||
string platform,
|
||||
string mainMarket,
|
||||
MarketTrend marketTrend,
|
||||
List<string> marketList,
|
||||
Dictionary<string, Market> recentMarkets,
|
||||
Dictionary<string, Market> trendMarkets,
|
||||
string sortBy,
|
||||
bool isGlobal,
|
||||
PTMagicConfiguration systemConfiguration,
|
||||
LogHelper log)
|
||||
{
|
||||
List<MarketTrendChange> result = new List<MarketTrendChange>();
|
||||
|
|
|
@ -416,7 +416,7 @@ namespace Core.MarketAnalyzer
|
|||
Dictionary<string, Market> tickMarkets = new Dictionary<string, Market>();
|
||||
foreach (string key in markets.Keys)
|
||||
{
|
||||
List<MarketTick> tickRange = marketTicks[key].FindAll(t => t.Time <= tickTime);
|
||||
List<MarketTick> tickRange = marketTicks[key] != null ? marketTicks[key].FindAll(t => t.Time <= tickTime) : new List<MarketTick>();
|
||||
|
||||
if (tickRange.Count > 0)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,455 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Core.Main;
|
||||
using Core.Helper;
|
||||
using Core.Main.DataObjects.PTMagicData;
|
||||
using Newtonsoft.Json;
|
||||
using Core.ProfitTrailer;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Core.MarketAnalyzer
|
||||
{
|
||||
public class BinanceUS : BaseAnalyzer
|
||||
{
|
||||
public static double GetMainCurrencyPrice(string mainMarket, PTMagicConfiguration systemConfiguration, LogHelper log)
|
||||
{
|
||||
double result = 0;
|
||||
|
||||
try
|
||||
{
|
||||
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, false);
|
||||
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)
|
||||
{
|
||||
log.DoLogCritical(ex.Message, ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<string> GetMarketData(string mainMarket, Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
|
||||
{
|
||||
List<string> result = new List<string>();
|
||||
|
||||
string lastMarket = "";
|
||||
Newtonsoft.Json.Linq.JObject lastTicker = null;
|
||||
try
|
||||
{
|
||||
string baseUrl = "https://api.binance.us/api/v1/ticker/24hr";
|
||||
|
||||
log.DoLogInfo("BinanceUS - Getting market data...");
|
||||
Newtonsoft.Json.Linq.JArray jsonArray = GetSimpleJsonArrayFromURL(baseUrl, log);
|
||||
if (jsonArray.Count > 0)
|
||||
{
|
||||
double mainCurrencyPrice = 1;
|
||||
if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
mainCurrencyPrice = BinanceUS.GetMainCurrencyPrice(mainMarket, systemConfiguration, log);
|
||||
}
|
||||
|
||||
log.DoLogInfo("BinanceUS - Market data received for " + jsonArray.Count.ToString() + " currencies");
|
||||
|
||||
if (mainCurrencyPrice > 0)
|
||||
{
|
||||
Dictionary<string, Market> markets = new Dictionary<string, Market>();
|
||||
foreach (Newtonsoft.Json.Linq.JObject currencyTicker in jsonArray)
|
||||
{
|
||||
string marketName = currencyTicker["symbol"].ToString();
|
||||
//New variables for filtering out bad markets
|
||||
float marketLastPrice = currencyTicker["lastPrice"].ToObject<float>();
|
||||
float marketVolume = currencyTicker["volume"].ToObject<float>();
|
||||
if (marketName.EndsWith(mainMarket, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
if (marketLastPrice > 0 && marketVolume > 0)
|
||||
{
|
||||
|
||||
// Set last values in case any error occurs
|
||||
lastMarket = marketName;
|
||||
lastTicker = currencyTicker;
|
||||
|
||||
Market market = new Market();
|
||||
market.Position = markets.Count + 1;
|
||||
market.Name = marketName;
|
||||
market.Symbol = currencyTicker["symbol"].ToString();
|
||||
market.Price = SystemHelper.TextToDouble(currencyTicker["lastPrice"].ToString(), 0, "en-US");
|
||||
market.Volume24h = SystemHelper.TextToDouble(currencyTicker["quoteVolume"].ToString(), 0, "en-US");
|
||||
market.MainCurrencyPriceUSD = mainCurrencyPrice;
|
||||
|
||||
markets.Add(market.Name, market);
|
||||
|
||||
result.Add(market.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Let the user know that the problem market was ignored.
|
||||
log.DoLogInfo("BinanceUS - Ignoring bad market data for " + marketName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BinanceUS.CheckFirstSeenDates(markets, ref marketInfos, systemConfiguration, log);
|
||||
|
||||
BaseAnalyzer.SaveMarketInfosToFile(marketInfos, systemConfiguration, log);
|
||||
|
||||
BinanceUS.CheckForMarketDataRecreation(mainMarket, markets, systemConfiguration, log);
|
||||
|
||||
DateTime fileDateTime = DateTime.UtcNow;
|
||||
|
||||
FileHelper.WriteTextToFile(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar, "MarketData_" + fileDateTime.ToString("yyyy-MM-dd_HH.mm") + ".json", JsonConvert.SerializeObject(markets), fileDateTime, fileDateTime);
|
||||
|
||||
log.DoLogInfo("BinanceUS - Market data saved for " + markets.Count.ToString() + " markets with " + mainMarket + ".");
|
||||
|
||||
FileHelper.CleanupFiles(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar, systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours);
|
||||
log.DoLogInfo("BinanceUS - Market data cleaned.");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.DoLogError("BinanceUS - Failed to get main market price for " + mainMarket + ".");
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
if (ex.Response != null)
|
||||
{
|
||||
using (HttpWebResponse errorResponse = (HttpWebResponse)ex.Response)
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(errorResponse.GetResponseStream()))
|
||||
{
|
||||
Dictionary<string, string> errorData = JsonConvert.DeserializeObject<Dictionary<string, string>>(reader.ReadToEnd());
|
||||
if (errorData != null)
|
||||
{
|
||||
string errorMessage = "Unable to get data from BinanceUS with URL '" + errorResponse.ResponseUri + "'!";
|
||||
if (errorData.ContainsKey("code"))
|
||||
{
|
||||
errorMessage += " - Code: " + errorData["code"];
|
||||
}
|
||||
|
||||
if (errorData.ContainsKey("msg"))
|
||||
{
|
||||
errorMessage += " - Message: " + errorData["msg"];
|
||||
}
|
||||
|
||||
log.DoLogError(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.DoLogCritical("Exception while getting data for '" + lastMarket + "': " + ex.Message, ex);
|
||||
result = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void CheckFirstSeenDates(Dictionary<string, Market> markets, ref Dictionary<string, MarketInfo> marketInfos, PTMagicConfiguration systemConfiguration, LogHelper log)
|
||||
{
|
||||
log.DoLogInfo("BinanceUS - Checking first seen dates for " + markets.Count + " markets. This may take a while...");
|
||||
|
||||
int marketsChecked = 0;
|
||||
|
||||
foreach (string key in markets.Keys)
|
||||
{
|
||||
// Save market info
|
||||
MarketInfo marketInfo = null;
|
||||
if (marketInfos.ContainsKey(key))
|
||||
{
|
||||
marketInfo = marketInfos[key];
|
||||
}
|
||||
|
||||
if (marketInfo == null)
|
||||
{
|
||||
marketInfo = new MarketInfo();
|
||||
marketInfo.Name = key;
|
||||
marketInfos.Add(key, marketInfo);
|
||||
marketInfo.FirstSeen = BinanceUS.GetFirstSeenDate(key, systemConfiguration, log);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (marketInfo.FirstSeen == Constants.confMinDate)
|
||||
{
|
||||
marketInfo.FirstSeen = BinanceUS.GetFirstSeenDate(key, systemConfiguration, log);
|
||||
}
|
||||
}
|
||||
marketInfo.LastSeen = DateTime.UtcNow;
|
||||
|
||||
marketsChecked++;
|
||||
|
||||
if ((marketsChecked % 20) == 0)
|
||||
{
|
||||
log.DoLogInfo("BinanceUS - Yes, I am still checking first seen dates... " + marketsChecked + "/" + markets.Count + " markets done...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static DateTime GetFirstSeenDate(string marketName, PTMagicConfiguration systemConfiguration, LogHelper log)
|
||||
{
|
||||
DateTime result = Constants.confMinDate;
|
||||
|
||||
string baseUrl = "https://api.binance.us/api/v1/klines?interval=1d&symbol=" + marketName + "&limit=100";
|
||||
|
||||
log.DoLogDebug("BinanceUS - Getting first seen date for '" + marketName + "'...");
|
||||
|
||||
Newtonsoft.Json.Linq.JArray jsonArray = GetSimpleJsonArrayFromURL(baseUrl, log);
|
||||
if (jsonArray.Count > 0)
|
||||
{
|
||||
result = Constants.Epoch.AddMilliseconds((Int64)jsonArray[0][0]);
|
||||
log.DoLogDebug("BinanceUS - First seen date for '" + marketName + "' set to " + result.ToString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<MarketTick> GetMarketTicks(string marketName, int ticksNeeded, PTMagicConfiguration systemConfiguration, LogHelper log)
|
||||
{
|
||||
List<MarketTick> result = new List<MarketTick>();
|
||||
|
||||
try
|
||||
{
|
||||
Int64 endTime = (Int64)Math.Ceiling(DateTime.UtcNow.Subtract(Constants.Epoch).TotalMilliseconds);
|
||||
int ticksLimit = 500;
|
||||
string baseUrl = "";
|
||||
int ticksFetched = 0;
|
||||
|
||||
if (ticksNeeded < ticksLimit)
|
||||
{
|
||||
ticksLimit = ticksNeeded;
|
||||
}
|
||||
|
||||
bool go = true;
|
||||
while (ticksFetched < ticksNeeded && go)
|
||||
{
|
||||
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)
|
||||
{
|
||||
log.DoLogDebug("BinanceUS - " + jsonArray.Count.ToString() + " ticks received.");
|
||||
|
||||
foreach (Newtonsoft.Json.Linq.JArray marketTick in jsonArray)
|
||||
{
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log.DoLogDebug("BinanceUS - No ticks received.");
|
||||
go = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
if (ex.Response != null)
|
||||
{
|
||||
using (HttpWebResponse errorResponse = (HttpWebResponse)ex.Response)
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(errorResponse.GetResponseStream()))
|
||||
{
|
||||
Dictionary<string, string> errorData = JsonConvert.DeserializeObject<Dictionary<string, string>>(reader.ReadToEnd());
|
||||
if (errorData != null)
|
||||
{
|
||||
string errorMessage = "Unable to get data from BinanceUS with URL '" + errorResponse.ResponseUri + "'!";
|
||||
if (errorData.ContainsKey("code"))
|
||||
{
|
||||
errorMessage += " - Code: " + errorData["code"];
|
||||
}
|
||||
|
||||
if (errorData.ContainsKey("msg"))
|
||||
{
|
||||
errorMessage += " - Message: " + errorData["msg"];
|
||||
}
|
||||
|
||||
log.DoLogError(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.DoLogCritical(ex.Message, ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void CheckForMarketDataRecreation(string mainMarket, Dictionary<string, Market> markets, PTMagicConfiguration systemConfiguration, LogHelper log)
|
||||
{
|
||||
string binanceUSDataDirectoryPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar;
|
||||
|
||||
if (!Directory.Exists(binanceUSDataDirectoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(binanceUSDataDirectoryPath);
|
||||
}
|
||||
|
||||
DirectoryInfo dataDirectory = new DirectoryInfo(binanceUSDataDirectoryPath);
|
||||
|
||||
// Check for existing market files
|
||||
DateTime latestMarketDataFileDateTime = Constants.confMinDate;
|
||||
List<FileInfo> marketFiles = dataDirectory.EnumerateFiles("MarketData*").ToList();
|
||||
FileInfo latestMarketDataFile = null;
|
||||
if (marketFiles.Count > 0)
|
||||
{
|
||||
latestMarketDataFile = marketFiles.OrderByDescending(mdf => mdf.LastWriteTimeUtc).First();
|
||||
latestMarketDataFileDateTime = latestMarketDataFile.LastWriteTimeUtc;
|
||||
}
|
||||
|
||||
if (latestMarketDataFileDateTime < DateTime.UtcNow.AddMinutes(-20))
|
||||
{
|
||||
int lastMarketDataAgeInSeconds = (int)Math.Ceiling(DateTime.UtcNow.Subtract(latestMarketDataFileDateTime).TotalSeconds);
|
||||
|
||||
// Go back in time and create market data
|
||||
DateTime startDateTime = DateTime.UtcNow;
|
||||
DateTime endDateTime = DateTime.UtcNow.AddHours(-systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours);
|
||||
if (latestMarketDataFileDateTime != Constants.confMinDate && latestMarketDataFileDateTime > endDateTime)
|
||||
{
|
||||
// Existing market files too old => Recreate market data for configured timeframe
|
||||
log.DoLogInfo("BinanceUS - Recreating market data for " + markets.Count + " markets over " + SystemHelper.GetProperDurationTime(lastMarketDataAgeInSeconds) + ". This may take a while...");
|
||||
endDateTime = latestMarketDataFileDateTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No existing market files found => Recreate market data for configured timeframe
|
||||
log.DoLogInfo("BinanceUS - Recreating market data for " + markets.Count + " markets over " + systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours + " hours. This may take a while...");
|
||||
}
|
||||
|
||||
int totalTicks = (int)Math.Ceiling(startDateTime.Subtract(endDateTime).TotalMinutes);
|
||||
|
||||
// Get Ticks for main market
|
||||
List<MarketTick> mainMarketTicks = new List<MarketTick>();
|
||||
if (!mainMarket.Equals("USDT", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
mainMarketTicks = BinanceUS.GetMarketTicks(mainMarket + "USDT", totalTicks, systemConfiguration, log);
|
||||
}
|
||||
|
||||
// Get Ticks for all markets
|
||||
log.DoLogDebug("BinanceUS - Getting ticks for '" + markets.Count + "' markets");
|
||||
ConcurrentDictionary<string, List<MarketTick>> marketTicks = new ConcurrentDictionary<string, List<MarketTick>>();
|
||||
|
||||
int ParallelThrottle = 4;
|
||||
if (systemConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours > 200)
|
||||
{
|
||||
ParallelThrottle = 2;
|
||||
log.DoLogInfo("----------------------------------------------------------------------------");
|
||||
log.DoLogInfo("StoreDataMaxHours is greater than 200. Historical data requests will be");
|
||||
log.DoLogInfo("throttled to avoid exceeding exchange data request limits. This initial ");
|
||||
log.DoLogInfo("run could take more than 30 minutes. Please go outside for a walk...");
|
||||
log.DoLogInfo("----------------------------------------------------------------------------");
|
||||
}
|
||||
|
||||
Parallel.ForEach(markets.Keys,
|
||||
new ParallelOptions { MaxDegreeOfParallelism = ParallelThrottle},
|
||||
(key) =>
|
||||
{
|
||||
if (!marketTicks.TryAdd(key, GetMarketTicks(key, totalTicks, systemConfiguration, log)))
|
||||
{
|
||||
// Failed to add ticks to dictionary
|
||||
throw new Exception("Failed to add ticks for " + key + " to the memory dictionary, results may be incorrectly calculated!");
|
||||
}
|
||||
|
||||
if ((marketTicks.Count % 10) == 0)
|
||||
{
|
||||
log.DoLogInfo("BinanceUS - No worries, I am still alive... " + marketTicks.Count + "/" + markets.Count + " markets done...");
|
||||
}
|
||||
});
|
||||
|
||||
log.DoLogInfo("BinanceUS - Ticks completed.");
|
||||
|
||||
log.DoLogInfo("BinanceUS - Creating initial market data ticks. This may take another while...");
|
||||
|
||||
// Go back in time and create market data
|
||||
int completedTicks = 0;
|
||||
if (marketTicks.Count > 0)
|
||||
{
|
||||
for (DateTime tickTime = startDateTime; tickTime >= endDateTime; tickTime = tickTime.AddMinutes(-1))
|
||||
{
|
||||
completedTicks++;
|
||||
|
||||
double mainCurrencyPrice = 1;
|
||||
if (mainMarketTicks.Count > 0)
|
||||
{
|
||||
List<MarketTick> mainCurrencyTickRange = mainMarketTicks.FindAll(t => t.Time <= tickTime);
|
||||
if (mainCurrencyTickRange.Count > 0)
|
||||
{
|
||||
MarketTick mainCurrencyTick = mainCurrencyTickRange.OrderByDescending(t => t.Time).First();
|
||||
mainCurrencyPrice = mainCurrencyTick.Price;
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<string, Market> tickMarkets = new Dictionary<string, Market>();
|
||||
foreach (string key in markets.Keys)
|
||||
{
|
||||
List<MarketTick> tickRange = marketTicks[key] != null ? marketTicks[key].FindAll(t => t.Time <= tickTime) : new List<MarketTick>();
|
||||
|
||||
if (tickRange.Count > 0)
|
||||
{
|
||||
MarketTick marketTick = tickRange.OrderByDescending(t => t.Time).First();
|
||||
|
||||
Market market = new Market();
|
||||
market.Position = markets.Count + 1;
|
||||
market.Name = key;
|
||||
market.Symbol = key;
|
||||
market.Price = marketTick.Price;
|
||||
//market.Volume24h = marketTick.Volume24h;
|
||||
market.MainCurrencyPriceUSD = mainCurrencyPrice;
|
||||
|
||||
tickMarkets.Add(market.Name, market);
|
||||
}
|
||||
}
|
||||
|
||||
DateTime fileDateTime = new DateTime(tickTime.ToLocalTime().Year, tickTime.ToLocalTime().Month, tickTime.ToLocalTime().Day, tickTime.ToLocalTime().Hour, tickTime.ToLocalTime().Minute, 0).ToUniversalTime();
|
||||
|
||||
FileHelper.WriteTextToFile(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + Constants.PTMagicPathExchange + Path.DirectorySeparatorChar, "MarketData_" + fileDateTime.ToString("yyyy-MM-dd_HH.mm") + ".json", JsonConvert.SerializeObject(tickMarkets), fileDateTime, fileDateTime);
|
||||
|
||||
log.DoLogDebug("BinanceUS - Market data saved for tick " + fileDateTime.ToString() + " - MainCurrencyPrice=" + mainCurrencyPrice.ToString("#,#0.00") + " USD.");
|
||||
|
||||
if ((completedTicks % 100) == 0)
|
||||
{
|
||||
log.DoLogInfo("BinanceUS - Our magicbots are still at work, hang on... " + completedTicks + "/" + totalTicks + " ticks done...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.DoLogInfo("BinanceUS - Initial market data created. Ready to go!");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,115 +1,17 @@
|
|||
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;
|
||||
|
||||
namespace Core.ProfitTrailer
|
||||
{
|
||||
public static class SettingsAPI
|
||||
{
|
||||
public static void GetInitialProfitTrailerSettings(PTMagicConfiguration systemConfiguration)
|
||||
{
|
||||
string html = "";
|
||||
string url = systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL + "api/data?token=" + systemConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken;
|
||||
|
||||
try
|
||||
{
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||
request.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
|
||||
WebResponse response = request.GetResponse();
|
||||
Stream dataStream = response.GetResponseStream();
|
||||
StreamReader reader = new StreamReader(dataStream);
|
||||
html = reader.ReadToEnd();
|
||||
reader.Close();
|
||||
response.Close();
|
||||
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
dynamic json = JsonConvert.DeserializeObject(html);
|
||||
|
||||
systemConfiguration.GeneralSettings.Application.Exchange = json.exchange;
|
||||
systemConfiguration.GeneralSettings.Application.TimezoneOffset = json.timeZoneOffset;
|
||||
systemConfiguration.GeneralSettings.Application.StartBalance = json.startBalance;
|
||||
systemConfiguration.GeneralSettings.Application.MainFiatCurrency = json.settings.currency;
|
||||
}
|
||||
public static List<string> GetPropertyLinesFromAPI(string ptFileName, PTMagicConfiguration systemConfiguration, LogHelper log)
|
||||
{
|
||||
List<string> result = null;
|
||||
|
||||
try
|
||||
{
|
||||
ServicePointManager.Expect100Continue = true;
|
||||
ServicePointManager.DefaultConnectionLimit = 9999;
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
|
||||
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(CertificateHelper.AllwaysGoodCertificate);
|
||||
|
||||
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(systemConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL + "settingsapi/settings/load");
|
||||
httpWebRequest.ContentType = "application/x-www-form-urlencoded";
|
||||
httpWebRequest.Method = "POST";
|
||||
|
||||
// PT is using ordinary POST data, not JSON
|
||||
string query = "fileName=" + ptFileName + "&configName=" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "&license=" + systemConfiguration.GeneralSettings.Application.ProfitTrailerLicense;
|
||||
byte[] formData = Encoding.ASCII.GetBytes(query);
|
||||
httpWebRequest.ContentLength = formData.Length;
|
||||
|
||||
using (Stream stream = httpWebRequest.GetRequestStream())
|
||||
{
|
||||
stream.Write(formData, 0, formData.Length);
|
||||
}
|
||||
|
||||
//using (StreamWriter streamWriter = new StreamWriter(httpWebRequest.GetRequestStream())) {
|
||||
// string json = JsonConvert.SerializeObject(new {
|
||||
// fileName = ptFileName,
|
||||
// configName = systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName,
|
||||
// license = systemConfiguration.GeneralSettings.Application.ProfitTrailerLicense
|
||||
// });
|
||||
|
||||
// streamWriter.Write(json);
|
||||
//}
|
||||
|
||||
HttpWebResponse httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
|
||||
using (StreamReader streamReader = new StreamReader(httpResponse.GetResponseStream()))
|
||||
{
|
||||
string jsonResult = streamReader.ReadToEnd();
|
||||
result = JsonConvert.DeserializeObject<List<string>>(jsonResult);
|
||||
}
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
// Manual error handling as PT doesn't seem to provide a proper error response...
|
||||
if (ex.Message.IndexOf("401") > -1)
|
||||
{
|
||||
log.DoLogError("Loading " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': Unauthorized! The specified Profit Trailer license key '" + systemConfiguration.GetProfitTrailerLicenseKeyMasked() + "' is invalid!");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.DoLogCritical("Loading " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': " + ex.Message, ex);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.DoLogCritical("Loading " + ptFileName + ".properties failed for setting '" + systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName + "': " + ex.Message, ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Save config back to Profit Trailer
|
||||
public static void SendPropertyLinesToAPI(List<string> pairsLines, List<string> dcaLines, List<string> indicatorsLines, PTMagicConfiguration systemConfiguration, LogHelper log)
|
||||
{
|
||||
int retryCount = 0;
|
||||
|
@ -148,17 +50,6 @@ namespace Core.ProfitTrailer
|
|||
}
|
||||
log.DoLogDebug("Built POST request for Properties");
|
||||
|
||||
//using (StreamWriter streamWriter = new StreamWriter(httpWebRequest.GetRequestStream())) {
|
||||
// string json = JsonConvert.SerializeObject(new {
|
||||
// fileName = ptFileName,
|
||||
// configName = systemConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName,
|
||||
// license = systemConfiguration.GeneralSettings.Application.ProfitTrailerLicense,
|
||||
// saveData = propertiesString
|
||||
// });
|
||||
|
||||
// streamWriter.Write(json);
|
||||
//}
|
||||
|
||||
log.DoLogInfo("Sending Properties...");
|
||||
HttpWebResponse httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
|
||||
log.DoLogInfo("Properties sent!");
|
||||
|
|
|
@ -2,17 +2,9 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Security.Permissions;
|
||||
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;
|
||||
|
||||
namespace Core.ProfitTrailer
|
||||
{
|
||||
|
|
|
@ -336,7 +336,7 @@ namespace Core.ProfitTrailer
|
|||
case "anderson":
|
||||
result = String.Concat(strategyLetter, "AND");
|
||||
break;
|
||||
case "som enabled":
|
||||
case "config som enabled":
|
||||
result = String.Concat(strategyLetter, "SOM");
|
||||
break;
|
||||
case "max buy times":
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<th data-fieldid="Market" data-tablesaw-sortable-col>Market</th>
|
||||
<th data-sortable-numeric="true" data-tablesaw-sortable-col data-fieldid="PercChange" data-toggle="tooltip" data-placement="top" title="24 hour market trend">Trend</th>
|
||||
<th data-sortable-numeric="true" data-fieldid="Amount" data-tablesaw-sortable-col>Amount</th>
|
||||
<th data-fieldid="TotalCost" data-tablesaw-sortable-col data-sortable-numeric="true" class="text-left" data-toggle="tooltip" data-placement="top" title="Spent total cost in @Model.Summary.MainMarket">Value</th>
|
||||
<th data-fieldid="TotalCost" data-tablesaw-sortable-col data-sortable-numeric="true" class="text-left" data-toggle="tooltip" data-placement="top" title="Spent total cost in @Model.Summary.MainMarket">Cost</th>
|
||||
<th data-fieldid="BoughtTimes" data-tablesaw-sortable-col data-sortable-numeric="true" class="text-right" data-toggle="tooltip" data-placement="top" title="Current DCA level">DCA</th>
|
||||
<th data-toggle="tooltip" data-placement="top" title="Active buy strategies">Buy Strats</th>
|
||||
<th class="text-right" data-toggle="tooltip" data-placement="top" title="Buy Strategy Value">BS Value</th>
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace Monitor.Pages
|
|||
|
||||
private void BindData()
|
||||
{
|
||||
PTData = new ProfitTrailerData(PTMagicConfiguration);
|
||||
PTData = this.PtDataObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace Monitor.Pages
|
|||
|
||||
private void BindData()
|
||||
{
|
||||
PTData = new ProfitTrailerData(PTMagicConfiguration);
|
||||
PTData = this.PtDataObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Monitor.Pages
|
|||
|
||||
private void BindData()
|
||||
{
|
||||
PTData = new ProfitTrailerData(PTMagicConfiguration);
|
||||
PTData = this.PtDataObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,29 @@
|
|||
}
|
||||
</script>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("BinanceUS", StringComparison.InvariantCultureIgnoreCase)) {
|
||||
string TvSymbol = "BINANCE:" + @Core.Helper.SystemHelper.GetMainCurrencySymbol(Model.Summary.MainMarket) + "USD";
|
||||
<div class="tradingview-widget-container">
|
||||
<div class="tradingview-widget-container__widget"></div>
|
||||
<script type="text/javascript" src="https://s3.tradingview.com/external-embedding/embed-widget-mini-symbol-overview.js" async>
|
||||
{
|
||||
"symbol": "@Core.Helper.SystemHelper.GetMainCurrencySymbol(@TvSymbol)",
|
||||
"width": "100%",
|
||||
"height": "100%",
|
||||
"locale": "en",
|
||||
"dateRange": "1d",
|
||||
"colorTheme": "dark",
|
||||
"trendLineColor": "#37a6ef",
|
||||
"underLineColor": "rgba(55, 166, 239, 0.15)",
|
||||
"isTransparent": true,
|
||||
"autosize": true,
|
||||
"largeChartUrl": ""
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
if (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex", StringComparison.InvariantCultureIgnoreCase)) {
|
||||
string TvSymbol = "BITTREX:" + @Core.Helper.SystemHelper.GetMainCurrencySymbol(Model.Summary.MainMarket) + "USD";
|
||||
|
@ -110,7 +132,7 @@
|
|||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card-box">
|
||||
<h4 class="m-t-0 m-b-20 header-title"><b>Market Trend Averages</b></h4>
|
||||
<h4 class="m-t-0 m-b-20 header-title"><b>Market Trends at @Model.PTMagicConfiguration.GeneralSettings.Application.Exchange</b></h4>
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace Monitor.Pages
|
|||
|
||||
private void BindData()
|
||||
{
|
||||
PTData = new ProfitTrailerData(PTMagicConfiguration);
|
||||
PTData = this.PtDataObject;
|
||||
|
||||
// Convert local offset time to UTC
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
|
|
|
@ -61,6 +61,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Exchange <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="The exchange PT Magic is using to get market data."></i></label>
|
||||
<div class="col-md-8">
|
||||
@Model.PTMagicConfiguration.GeneralSettings.Application.Exchange
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 col-form-label">Profit Trailer Path <i class="fa fa-info-circle text-muted" data-toggle="tooltip" data-placement="top" title="Path to your Profit Trailer main directory."></i></label>
|
||||
<div class="col-md-8">
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace Monitor.Pages {
|
|||
private void BindData() {
|
||||
DCAMarket = GetStringParameter("m", "");
|
||||
|
||||
PTData = new ProfitTrailerData(PTMagicConfiguration);
|
||||
PTData = this.PtDataObject;
|
||||
|
||||
DCALogData = PTData.DCALog.Find(d => d.Market == DCAMarket);
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace Monitor.Pages {
|
|||
SortFieldId = GetStringParameter("s", "ProfitPercent");
|
||||
SortDirection = GetStringParameter("d", "DESC");
|
||||
|
||||
PTData = new ProfitTrailerData(PTMagicConfiguration);
|
||||
PTData = this.PtDataObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace Monitor.Pages {
|
|||
SortFieldId = GetStringParameter("s", "ProfitPercent");
|
||||
SortDirection = GetStringParameter("d", "DESC");
|
||||
|
||||
PTData = new ProfitTrailerData(PTMagicConfiguration);
|
||||
PTData = this.PtDataObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
<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</b><small class="pull-right"><a href="@Html.Raw(Model.PTMagicConfiguration.GeneralSettings.Monitor.RootUrl)MarketAnalyzer">more</a></small></h4>
|
||||
<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>
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace Monitor.Pages {
|
|||
}
|
||||
|
||||
private void BindData() {
|
||||
PTData = new ProfitTrailerData(PTMagicConfiguration);
|
||||
PTData = this.PtDataObject;
|
||||
|
||||
// Cleanup temp files
|
||||
FileHelper.CleanupFilesMinutes(PTMagicMonitorBasePath + "wwwroot" + System.IO.Path.DirectorySeparatorChar + "assets" + System.IO.Path.DirectorySeparatorChar + "tmp" + System.IO.Path.DirectorySeparatorChar, 5);
|
||||
|
|
|
@ -51,9 +51,7 @@
|
|||
string buyStrategyText = Core.ProfitTrailer.StrategyHelper.GetStrategyText(Model.Summary, buyLogEntry.BuyStrategies, buyLogEntry.BuyStrategy, isBuyStrategyTrue, isTrailingBuyActive);
|
||||
if (!Core.ProfitTrailer.StrategyHelper.IsValidStrategy(buyStrategyText, true)) {
|
||||
buyDisabled = true;
|
||||
}
|
||||
|
||||
buyLogEntry.PercChange = @buyLogEntry.PercChange * 100;
|
||||
}
|
||||
|
||||
<tr>
|
||||
@if (mps == null || mps.ActiveSingleSettings == null || mps.ActiveSingleSettings.Count == 0) {
|
||||
|
@ -61,7 +59,7 @@
|
|||
} 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">@buyLogEntry.PercChange.ToString("#,#0.00")%</td>
|
||||
<td class="text-autocolor">@string.Format("{0}%", (buyLogEntry.PercChange * 100).ToString("#,#0.00"))</td>
|
||||
@if (buyDisabled) {
|
||||
<td>@Html.Raw(buyStrategyText)</td>
|
||||
} else {
|
||||
|
@ -96,7 +94,7 @@
|
|||
<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="Total Buy Value">Value</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>
|
||||
|
@ -149,6 +147,31 @@
|
|||
bool lostValue = false;
|
||||
lostValue = (dcaLogEntry.TotalCost == 0.0) || (dcaLogEntry.AverageBuyPrice == 0.0);
|
||||
|
||||
// Aggregate totals
|
||||
Model.TotalBagCost = Model.TotalBagCost + dcaLogEntry.TotalCost;
|
||||
double ExchangeFee = 0;
|
||||
|
||||
switch (Model.PTMagicConfiguration.GeneralSettings.Application.Exchange.ToLower())
|
||||
{
|
||||
case "binance":
|
||||
ExchangeFee = 0.002;
|
||||
break;
|
||||
case "binanceus":
|
||||
ExchangeFee = 0.002;
|
||||
break;
|
||||
case "bittrex":
|
||||
ExchangeFee = 0.0025;
|
||||
break;
|
||||
case "poloniex":
|
||||
ExchangeFee = 0.0025;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
double TradingFee = (dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) * ExchangeFee;
|
||||
Model.TotalBagValue = Model.TotalBagValue + ((dcaLogEntry.Amount * dcaLogEntry.CurrentPrice) - TradingFee);
|
||||
|
||||
// Render the row
|
||||
<tr @(lostValue ? "class=errorRow" : "") >
|
||||
@if (mps == null || mps.ActiveSingleSettings == null || mps.ActiveSingleSettings.Count == 0) {
|
||||
|
@ -187,6 +210,12 @@
|
|||
<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>@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>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace Monitor.Pages {
|
|||
public class DashboardTopModel : _Internal.BasePageModelSecureAJAX {
|
||||
public ProfitTrailerData PTData = null;
|
||||
public DateTimeOffset DateTimeNow = Constants.confMinDate;
|
||||
|
||||
|
||||
public void OnGet() {
|
||||
// Initialize Config
|
||||
base.Init();
|
||||
|
@ -19,8 +19,10 @@ namespace Monitor.Pages {
|
|||
BindData();
|
||||
}
|
||||
|
||||
public double TotalBagCost = 0;
|
||||
public double TotalBagValue = 0;
|
||||
private void BindData() {
|
||||
PTData = new ProfitTrailerData(PTMagicConfiguration);
|
||||
PTData = this.PtDataObject;
|
||||
|
||||
// Convert local offset time to UTC
|
||||
TimeSpan offsetTimeSpan = TimeSpan.Parse(PTMagicConfiguration.GeneralSettings.Application.TimezoneOffset.Replace("+", ""));
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace Monitor.Pages {
|
|||
salesDateString = GetStringParameter("d", "");
|
||||
salesMonthString = GetStringParameter("m", "");
|
||||
|
||||
PTData = new ProfitTrailerData(PTMagicConfiguration);
|
||||
PTData = this.PtDataObject;
|
||||
|
||||
if (!salesDateString.Equals("")) {
|
||||
SalesDate = SystemHelper.TextToDateTime(salesDateString, Constants.confMinDate);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
@ -10,9 +9,8 @@ using Core.Main;
|
|||
using Core.Helper;
|
||||
using Core.Main.DataObjects.PTMagicData;
|
||||
using Core.MarketAnalyzer;
|
||||
using Core.ProfitTrailer;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using System.Diagnostics;
|
||||
using Core.Main.DataObjects;
|
||||
|
||||
namespace Monitor._Internal
|
||||
{
|
||||
|
@ -22,7 +20,6 @@ namespace Monitor._Internal
|
|||
public string PTMagicBasePath = "";
|
||||
public string PTMagicMonitorBasePath = "";
|
||||
public PTMagicConfiguration PTMagicConfiguration = null;
|
||||
|
||||
public Summary Summary { get; set; } = new Summary();
|
||||
public LogHelper Log = null;
|
||||
public string LatestVersion = "";
|
||||
|
@ -32,6 +29,25 @@ namespace Monitor._Internal
|
|||
public string NotifyType = "";
|
||||
|
||||
public string MainFiatCurrencySymbol = "$";
|
||||
private volatile object _ptDataLock = new object();
|
||||
private static ProfitTrailerData _ptData = null;
|
||||
|
||||
// Profit Trailer data accessor object
|
||||
public ProfitTrailerData PtDataObject
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ptData == null)
|
||||
{
|
||||
lock (_ptDataLock)
|
||||
{
|
||||
_ptData = new ProfitTrailerData(PTMagicConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
return _ptData;
|
||||
}
|
||||
}
|
||||
|
||||
public void PreInit()
|
||||
{
|
||||
|
@ -58,14 +74,7 @@ namespace Monitor._Internal
|
|||
PTMagicBasePath += Path.DirectorySeparatorChar;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
PTMagicConfiguration = new PTMagicConfiguration(PTMagicBasePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
PTMagicConfiguration = new PTMagicConfiguration(PTMagicBasePath);
|
||||
|
||||
IServiceProvider logProvider = ServiceHelper.BuildLoggerService(PTMagicBasePath);
|
||||
Log = logProvider.GetRequiredService<LogHelper>();
|
||||
|
|
|
@ -1,21 +1,10 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Core.Main;
|
||||
using Core.Helper;
|
||||
using Core.Main.DataObjects.PTMagicData;
|
||||
using Core.MarketAnalyzer;
|
||||
using Core.ProfitTrailer;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Monitor._Internal
|
||||
{
|
||||
|
||||
public class BasePageModelSecure : BasePageModel
|
||||
{
|
||||
public void Init()
|
||||
|
|
|
@ -93,7 +93,7 @@ const PropertyTemplate = ({ settingType, settingName, propertyType, propertyKey,
|
|||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" placeholder="Profit Trailer setting" name="MarketAnalyzer_${settingType}_${settingName}|${propertyType}Property_${propertyKeySimple}" value="${propertyKeySimple}">
|
||||
<span class="help-block"><small>Any <a href="https://wiki.profittrailer.com/doku.php?id=${propertyType}.properties" target="_blank">variable from PT's settings</a> may be used!</small></span>
|
||||
<span class="help-block"><small>Any <a href="https://wiki.profittrailer.com/doku.php?id=${propertyType}_config" target="_blank">variable from PT's settings</a> may be used!</small></span>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<input type="text" class="form-control" placeholder="Value" name="MarketAnalyzer_${settingType}_${settingName}|${propertyType}Property_${propertyKeySimple}|Value" value="${value}">
|
||||
|
|
|
@ -7,7 +7,7 @@ using Core.Helper;
|
|||
using Core.Main.DataObjects.PTMagicData;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
[assembly: AssemblyVersion("2.2.6")]
|
||||
[assembly: AssemblyVersion("2.2.7")]
|
||||
[assembly: AssemblyProduct("PT Magic")]
|
||||
|
||||
namespace PTMagic
|
||||
|
|
Loading…
Reference in New Issue