2624 lines
110 KiB
C#
2624 lines
110 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using Core.Helper;
|
|
using Core.Main.DataObjects.PTMagicData;
|
|
using Core.MarketAnalyzer;
|
|
using Core.ProfitTrailer;
|
|
using Newtonsoft.Json;
|
|
|
|
namespace Core.Main
|
|
{
|
|
public class PTMagic
|
|
{
|
|
|
|
public PTMagic(LogHelper log)
|
|
{
|
|
this.Log = log;
|
|
}
|
|
|
|
#region Properties
|
|
private LogHelper _log;
|
|
private PTMagicConfiguration _systemConfiguration;
|
|
private System.Timers.Timer _timer;
|
|
private Summary _lastRuntimeSummary = null;
|
|
private int _state = 0;
|
|
private int _runCount = 0;
|
|
private int _totalElapsedSeconds = 0;
|
|
private bool _globalSettingWritten = false;
|
|
private bool _singleMarketSettingChanged = false;
|
|
private List<KeyValuePair<string, string>> _lastActiveSingleMarketSettings = null;
|
|
private bool _enforceSettingsReapply = false;
|
|
private DateTime _lastRuntime = Constants.confMinDate;
|
|
private DateTime _lastSettingsChange = Constants.confMinDate;
|
|
private DateTime _lastSettingFileCheck = Constants.confMinDate;
|
|
private DateTime _lastVersionCheck = Constants.confMinDate;
|
|
private DateTime _lastFiatCurrencyCheck = Constants.confMinDate;
|
|
private string _activeSettingName = "";
|
|
private GlobalSetting _activeSetting = null;
|
|
private string _defaultSettingName = "";
|
|
private string _pairsFileName = "PAIRS.PROPERTIES";
|
|
private string _dcaFileName = "DCA.PROPERTIES";
|
|
private string _indicatorsFileName = "INDICATORS.PROPERTIES";
|
|
private Version _currentVersion = null;
|
|
private string _latestVersion = "";
|
|
private string _lastMainFiatCurrency = "USD";
|
|
private double _lastMainFiatCurrencyExchangeRate = 1;
|
|
private List<SingleMarketSettingSummary> _singleMarketSettingSummaries = new List<SingleMarketSettingSummary>();
|
|
private List<string> _pairsLines = null;
|
|
private List<string> _dcaLines = null;
|
|
private List<string> _indicatorsLines = null;
|
|
private List<string> _exchangeMarketList = null;
|
|
private List<string> _marketList = new List<string>();
|
|
private Dictionary<string, MarketInfo> _marketInfos = new Dictionary<string, MarketInfo>();
|
|
private Dictionary<string, double> _averageMarketTrendChanges = new Dictionary<string, double>();
|
|
private Dictionary<string, List<MarketTrendChange>> _singleMarketTrendChanges = new Dictionary<string, List<MarketTrendChange>>();
|
|
private Dictionary<string, List<MarketTrendChange>> _globalMarketTrendChanges = new Dictionary<string, List<MarketTrendChange>>();
|
|
private Dictionary<string, int> _singleMarketSettingsCount = new Dictionary<string, int>();
|
|
Dictionary<string, List<SingleMarketSetting>> _triggeredSingleMarketSettings = new Dictionary<string, List<SingleMarketSetting>>();
|
|
private static volatile object _lockObj = new object();
|
|
|
|
public LogHelper Log
|
|
{
|
|
get
|
|
{
|
|
return _log;
|
|
}
|
|
set
|
|
{
|
|
_log = value;
|
|
}
|
|
}
|
|
|
|
public PTMagicConfiguration PTMagicConfiguration
|
|
{
|
|
get
|
|
{
|
|
return _systemConfiguration;
|
|
}
|
|
set
|
|
{
|
|
_systemConfiguration = value;
|
|
}
|
|
}
|
|
|
|
public System.Timers.Timer Timer
|
|
{
|
|
get
|
|
{
|
|
return _timer;
|
|
}
|
|
set
|
|
{
|
|
_timer = value;
|
|
}
|
|
}
|
|
|
|
public Summary LastRuntimeSummary
|
|
{
|
|
get
|
|
{
|
|
return _lastRuntimeSummary;
|
|
}
|
|
set
|
|
{
|
|
_lastRuntimeSummary = value;
|
|
}
|
|
}
|
|
|
|
public int State
|
|
{
|
|
get
|
|
{
|
|
return _state;
|
|
}
|
|
set
|
|
{
|
|
_state = value;
|
|
}
|
|
}
|
|
|
|
public int RunCount
|
|
{
|
|
get
|
|
{
|
|
return _runCount;
|
|
}
|
|
set
|
|
{
|
|
_runCount = value;
|
|
}
|
|
}
|
|
|
|
public int TotalElapsedSeconds
|
|
{
|
|
get
|
|
{
|
|
return _totalElapsedSeconds;
|
|
}
|
|
set
|
|
{
|
|
_totalElapsedSeconds = value;
|
|
}
|
|
}
|
|
|
|
public bool GlobalSettingWritten
|
|
{
|
|
get
|
|
{
|
|
return _globalSettingWritten;
|
|
}
|
|
set
|
|
{
|
|
_globalSettingWritten = value;
|
|
}
|
|
}
|
|
|
|
public bool SingleMarketSettingChanged
|
|
{
|
|
get
|
|
{
|
|
return _singleMarketSettingChanged;
|
|
}
|
|
set
|
|
{
|
|
_singleMarketSettingChanged = value;
|
|
}
|
|
}
|
|
|
|
public bool EnforceSettingsReapply
|
|
{
|
|
get
|
|
{
|
|
return _enforceSettingsReapply;
|
|
}
|
|
set
|
|
{
|
|
_enforceSettingsReapply = value;
|
|
}
|
|
}
|
|
|
|
public DateTime LastSettingsChange
|
|
{
|
|
get
|
|
{
|
|
return _lastSettingsChange;
|
|
}
|
|
set
|
|
{
|
|
_lastSettingsChange = value;
|
|
}
|
|
}
|
|
|
|
public DateTime LastVersionCheck
|
|
{
|
|
get
|
|
{
|
|
return _lastVersionCheck;
|
|
}
|
|
set
|
|
{
|
|
_lastVersionCheck = value;
|
|
}
|
|
}
|
|
|
|
public DateTime LastFiatCurrencyCheck
|
|
{
|
|
get
|
|
{
|
|
return _lastFiatCurrencyCheck;
|
|
}
|
|
set
|
|
{
|
|
_lastFiatCurrencyCheck = value;
|
|
}
|
|
}
|
|
|
|
public DateTime LastSettingFileCheck
|
|
{
|
|
get
|
|
{
|
|
return _lastSettingFileCheck;
|
|
}
|
|
set
|
|
{
|
|
_lastSettingFileCheck = value;
|
|
}
|
|
}
|
|
|
|
public DateTime LastRuntime
|
|
{
|
|
get
|
|
{
|
|
return _lastRuntime;
|
|
}
|
|
set
|
|
{
|
|
_lastRuntime = value;
|
|
}
|
|
}
|
|
|
|
public string DefaultSettingName
|
|
{
|
|
get
|
|
{
|
|
return _defaultSettingName;
|
|
}
|
|
set
|
|
{
|
|
_defaultSettingName = value;
|
|
}
|
|
}
|
|
|
|
public GlobalSetting ActiveSetting
|
|
{
|
|
get
|
|
{
|
|
return _activeSetting;
|
|
}
|
|
set
|
|
{
|
|
_activeSetting = value;
|
|
}
|
|
}
|
|
|
|
public string ActiveSettingName
|
|
{
|
|
get
|
|
{
|
|
return _activeSettingName;
|
|
}
|
|
set
|
|
{
|
|
_activeSettingName = value;
|
|
}
|
|
}
|
|
|
|
public string PairsFileName
|
|
{
|
|
get
|
|
{
|
|
return _pairsFileName;
|
|
}
|
|
set
|
|
{
|
|
_pairsFileName = value;
|
|
}
|
|
}
|
|
|
|
public string DCAFileName
|
|
{
|
|
get
|
|
{
|
|
return _dcaFileName;
|
|
}
|
|
set
|
|
{
|
|
_dcaFileName = value;
|
|
}
|
|
}
|
|
|
|
public string IndicatorsFileName
|
|
{
|
|
get
|
|
{
|
|
return _indicatorsFileName;
|
|
}
|
|
set
|
|
{
|
|
_indicatorsFileName = value;
|
|
}
|
|
}
|
|
|
|
public Version CurrentVersion
|
|
{
|
|
get
|
|
{
|
|
return _currentVersion;
|
|
}
|
|
set
|
|
{
|
|
_currentVersion = value;
|
|
}
|
|
}
|
|
|
|
public string LatestVersion
|
|
{
|
|
get
|
|
{
|
|
return _latestVersion;
|
|
}
|
|
set
|
|
{
|
|
_latestVersion = value;
|
|
}
|
|
}
|
|
|
|
public string LastMainFiatCurrency
|
|
{
|
|
get
|
|
{
|
|
return _lastMainFiatCurrency;
|
|
}
|
|
set
|
|
{
|
|
_lastMainFiatCurrency = value;
|
|
}
|
|
}
|
|
|
|
public double LastMainFiatCurrencyExchangeRate
|
|
{
|
|
get
|
|
{
|
|
return _lastMainFiatCurrencyExchangeRate;
|
|
}
|
|
set
|
|
{
|
|
_lastMainFiatCurrencyExchangeRate = value;
|
|
}
|
|
}
|
|
|
|
public List<SingleMarketSettingSummary> SingleMarketSettingSummaries
|
|
{
|
|
get
|
|
{
|
|
return _singleMarketSettingSummaries;
|
|
}
|
|
set
|
|
{
|
|
_singleMarketSettingSummaries = value;
|
|
}
|
|
}
|
|
|
|
public List<string> PairsLines
|
|
{
|
|
get
|
|
{
|
|
return _pairsLines;
|
|
}
|
|
set
|
|
{
|
|
_pairsLines = value;
|
|
}
|
|
}
|
|
|
|
public List<string> DCALines
|
|
{
|
|
get
|
|
{
|
|
return _dcaLines;
|
|
}
|
|
set
|
|
{
|
|
_dcaLines = value;
|
|
}
|
|
}
|
|
|
|
public List<string> IndicatorsLines
|
|
{
|
|
get
|
|
{
|
|
return _indicatorsLines;
|
|
}
|
|
set
|
|
{
|
|
_indicatorsLines = value;
|
|
}
|
|
}
|
|
|
|
public List<string> ExchangeMarketList
|
|
{
|
|
get
|
|
{
|
|
return _exchangeMarketList;
|
|
}
|
|
set
|
|
{
|
|
_exchangeMarketList = value;
|
|
}
|
|
}
|
|
|
|
public List<string> MarketList
|
|
{
|
|
get
|
|
{
|
|
return _marketList;
|
|
}
|
|
set
|
|
{
|
|
_marketList = value;
|
|
}
|
|
}
|
|
|
|
public Dictionary<string, MarketInfo> MarketInfos
|
|
{
|
|
get
|
|
{
|
|
return _marketInfos;
|
|
}
|
|
set
|
|
{
|
|
_marketInfos = value;
|
|
}
|
|
}
|
|
|
|
public Dictionary<string, List<MarketTrendChange>> SingleMarketTrendChanges
|
|
{
|
|
get
|
|
{
|
|
return _singleMarketTrendChanges;
|
|
}
|
|
set
|
|
{
|
|
_singleMarketTrendChanges = value;
|
|
}
|
|
}
|
|
|
|
public Dictionary<string, List<MarketTrendChange>> GlobalMarketTrendChanges
|
|
{
|
|
get
|
|
{
|
|
return _globalMarketTrendChanges;
|
|
}
|
|
set
|
|
{
|
|
_globalMarketTrendChanges = value;
|
|
}
|
|
}
|
|
|
|
public Dictionary<string, double> AverageMarketTrendChanges
|
|
{
|
|
get
|
|
{
|
|
return _averageMarketTrendChanges;
|
|
}
|
|
set
|
|
{
|
|
_averageMarketTrendChanges = value;
|
|
}
|
|
}
|
|
|
|
public Dictionary<string, int> SingleMarketSettingsCount
|
|
{
|
|
get
|
|
{
|
|
return _singleMarketSettingsCount;
|
|
}
|
|
set
|
|
{
|
|
_singleMarketSettingsCount = value;
|
|
}
|
|
}
|
|
|
|
public Dictionary<string, List<SingleMarketSetting>> TriggeredSingleMarketSettings
|
|
{
|
|
get
|
|
{
|
|
return _triggeredSingleMarketSettings;
|
|
}
|
|
set
|
|
{
|
|
_triggeredSingleMarketSettings = value;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region PTMagic Startup Methods
|
|
|
|
private static int ExponentialDelay(int failedAttempts, int maxDelayInSeconds = 900)
|
|
{
|
|
//Attempt 1 0s 0s
|
|
//Attempt 2 2s 2s
|
|
//Attempt 3 4s 4s
|
|
//Attempt 4 8s 8s
|
|
//Attempt 5 16s 16s
|
|
//Attempt 6 32s 32s
|
|
|
|
//Attempt 7 64s 1m 4s
|
|
//Attempt 8 128s 2m 8s
|
|
//Attempt 9 256s 4m 16s
|
|
//Attempt 10 512 8m 32s
|
|
//Attempt 11 1024 17m 4s
|
|
|
|
var delayInSeconds = ((1d / 2d) * (Math.Pow(2d, failedAttempts) - 1d));
|
|
|
|
return maxDelayInSeconds < delayInSeconds
|
|
? maxDelayInSeconds
|
|
: (int)delayInSeconds;
|
|
}
|
|
|
|
public bool StartProcess()
|
|
{
|
|
bool result = true;
|
|
|
|
this.Log.DoLogInfo("");
|
|
this.Log.DoLogInfo(" ██████╗ ████████╗ ███╗ ███╗ █████╗ ██████╗ ██╗ ██████╗");
|
|
this.Log.DoLogInfo(" ██╔══██╗╚══██╔══╝ ████╗ ████║██╔══██╗██╔════╝ ██║██╔════╝");
|
|
this.Log.DoLogInfo(" ██████╔╝ ██║ ██╔████╔██║███████║██║ ███╗██║██║ ");
|
|
this.Log.DoLogInfo(" ██╔═══╝ ██║ ██║╚██╔╝██║██╔══██║██║ ██║██║██║ ");
|
|
this.Log.DoLogInfo(" ██║ ██║ ██║ ╚═╝ ██║██║ ██║╚██████╔╝██║╚██████╗");
|
|
this.Log.DoLogInfo(" ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝");
|
|
this.Log.DoLogInfo(" Version " + this.CurrentVersion.Major + "." + this.CurrentVersion.Minor + "." + this.CurrentVersion.Build);
|
|
this.Log.DoLogInfo("");
|
|
this.Log.DoLogInfo("Starting PTMagic in " + Directory.GetCurrentDirectory());
|
|
this.Log.DoLogInfo("with .NET Core: " + Path.GetDirectoryName(typeof(object).Assembly.Location));
|
|
|
|
if (!this.RunStartupChecks())
|
|
{
|
|
return false;
|
|
}
|
|
if (!this.InitializeConfiguration())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool configCheckResult = this.RunConfigurationChecks();
|
|
|
|
if (!configCheckResult)
|
|
{
|
|
// Config check failed so retry using an exponential back off until it passes; max retry time 15 mins.
|
|
int configRetryCount = 1;
|
|
int delaySeconds;
|
|
|
|
while (!configCheckResult)
|
|
{
|
|
delaySeconds = ExponentialDelay(configRetryCount);
|
|
this.Log.DoLogError("Configuration check retry " + configRetryCount + " failed, starting next retry in " + delaySeconds + " seconds...");
|
|
Thread.Sleep(delaySeconds * 1000);
|
|
|
|
// Reinit config in case the user changed something
|
|
this.InitializeConfiguration();
|
|
configCheckResult = this.RunConfigurationChecks();
|
|
configRetryCount++;
|
|
}
|
|
}
|
|
|
|
this.LastSettingFileCheck = DateTime.UtcNow;
|
|
|
|
SettingsFiles.CheckPresets(this.PTMagicConfiguration, this.Log, true);
|
|
|
|
// Start the _preset folder file watcher.
|
|
SettingsFiles.PresetFileWatcher.Changed += PresetFileWatcher_OnChanged;
|
|
SettingsFiles.PresetFileWatcher.EnableRaisingEvents = true;
|
|
|
|
// Force settings refresh first time
|
|
EnforceSettingsReapply = true;
|
|
|
|
// Set the Active config
|
|
this.ActiveSetting = this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(s => s.SettingName.Equals(this.DefaultSettingName, StringComparison.InvariantCultureIgnoreCase));
|
|
this.ActiveSettingName = ActiveSetting.SettingName;
|
|
this.LastSettingsChange = DateTime.UtcNow;
|
|
|
|
// Start polling
|
|
this.StartPTMagicIntervalTimer();
|
|
|
|
return result;
|
|
}
|
|
|
|
// File watcher event handlers
|
|
private void PresetFileWatcher_OnChanged(object source, FileSystemEventArgs e)
|
|
{
|
|
// Disable the file watcher whilst we deal with the event
|
|
SettingsFiles.PresetFileWatcher.EnableRaisingEvents = false;
|
|
|
|
this.Log.DoLogInfo("Detected a '" + e.ChangeType.ToString() + "' change in the following preset file: " + e.FullPath);
|
|
|
|
// Reprocess now
|
|
this.EnforceSettingsReapply = true;
|
|
PTMagicIntervalTimer_Elapsed(new object(), null);
|
|
|
|
// Enable the file watcher again
|
|
SettingsFiles.PresetFileWatcher.EnableRaisingEvents = true;
|
|
}
|
|
|
|
public bool RunStartupChecks()
|
|
{
|
|
bool result = true;
|
|
|
|
// Startup checks
|
|
if (!File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "settings.general.json"))
|
|
{
|
|
this.Log.DoLogError("File 'settings.general.json' not found! Please review the setup steps on the wiki and double check every step that involves copying files!");
|
|
result = false;
|
|
}
|
|
|
|
if (!File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "settings.analyzer.json"))
|
|
{
|
|
this.Log.DoLogError("File 'settings.analyzer.json' not found! Please review the setup steps on the wiki and double check every step that involves copying files!");
|
|
result = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public bool InitializeConfiguration()
|
|
{
|
|
bool result = true;
|
|
|
|
try
|
|
{
|
|
this.PTMagicConfiguration = new PTMagicConfiguration();
|
|
|
|
this.Log.DoLogInfo("Configuration loaded. Found " +
|
|
this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends != null ? this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.Count.ToString() : "0" +
|
|
" Market Trends, " +
|
|
this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings != null ? this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Count.ToString() : "0" +
|
|
" Global Settings and " +
|
|
this.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings != null ? this.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings.Count.ToString() : "0" +
|
|
" Single Market Settings.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result = false;
|
|
this.Log.DoLogCritical("Error loading configuration!", ex);
|
|
throw (ex);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public bool RunConfigurationChecks()
|
|
{
|
|
bool result = true;
|
|
|
|
//Import Initial ProfitTrailer Information(Deactivated for now)
|
|
//SettingsAPI.GetInitialProfitTrailerSettings(this.PTMagicConfiguration);
|
|
|
|
// Check for valid default setting
|
|
GlobalSetting defaultSetting = this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(s => s.SettingName.Equals("default", StringComparison.InvariantCultureIgnoreCase));
|
|
if (defaultSetting == null)
|
|
{
|
|
defaultSetting = this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(s => s.SettingName.IndexOf("default", StringComparison.InvariantCultureIgnoreCase) > -1);
|
|
if (defaultSetting != null)
|
|
{
|
|
this.Log.DoLogDebug("No setting named 'default' found, taking '" + defaultSetting.SettingName + "' as default.");
|
|
this.DefaultSettingName = defaultSetting.SettingName;
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogError("No 'default' setting found! Terminating process...");
|
|
result = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.DefaultSettingName = defaultSetting.SettingName;
|
|
}
|
|
|
|
// 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...");
|
|
result = false;
|
|
}
|
|
|
|
// Check if the program is enabled
|
|
if (this.PTMagicConfiguration.GeneralSettings.Application.IsEnabled)
|
|
{
|
|
try
|
|
{
|
|
if (this.PTMagicConfiguration.GeneralSettings.Application.TestMode) this.Log.DoLogInfo("TESTMODE ENABLED - No files will be changed!");
|
|
|
|
// Check for PT Directory
|
|
DirectoryInfo ptRoot = new DirectoryInfo(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath);
|
|
if (ptRoot.Exists)
|
|
{
|
|
this.Log.DoLogInfo("Profit Trailer directory found");
|
|
result = RunProfitTrailerSettingsAPIChecks();
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogError("Profit Trailer directory not found (" + this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + ")");
|
|
result = false;
|
|
}
|
|
|
|
// Check for CoinMarketCap API Key
|
|
if (!String.IsNullOrEmpty(this.PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey))
|
|
{
|
|
this.Log.DoLogInfo("CoinMarketCap API KEY found");
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogInfo("No CoinMarketCap API KEY specified! You can't use CoinMarketCap in your settings.analyzer.json");
|
|
}
|
|
|
|
// Check for CurrencyConverterApi Key
|
|
if (!this.PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey.Equals(""))
|
|
{
|
|
this.Log.DoLogInfo("FreeCurrencyConverterApi KEY found");
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogInfo("No FreeCurrencyConverterApi KEY specified, you can only use USD; apply for a key at: https://freecurrencyrates.com/en");
|
|
}
|
|
}
|
|
catch (System.NullReferenceException)
|
|
{
|
|
this.Log.DoLogError("PTM failed to read the Config File. That means something in the File is either missing or incorrect. If this happend after an update please take a look at the release notes at: https://github.com/PTMagicians/PTMagic/releases");
|
|
Console.WriteLine("Press enter to close the Application...");
|
|
Console.ReadLine();
|
|
Environment.Exit(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogWarn("PTMagic disabled, shutting down...");
|
|
result = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private bool RunProfitTrailerSettingsAPIChecks()
|
|
{
|
|
bool result = true;
|
|
|
|
this.Log.DoLogInfo("========== STARTING CHECKS FOR Profit Trailer ==========");
|
|
|
|
// Check for PT license key
|
|
if (!String.IsNullOrEmpty(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerLicense))
|
|
{
|
|
this.Log.DoLogInfo("Profit Trailer check: Profit Trailer license found");
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogError("Profit Trailer check: No Profit Trailer license key specified! The license key is necessary to adjust your Profit Trailer settings");
|
|
result = false;
|
|
}
|
|
|
|
//Check for ptServerAPIToken
|
|
if (!String.IsNullOrEmpty(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerServerAPIToken))
|
|
{
|
|
this.Log.DoLogInfo("Profit Trailer check: Profit Trailer Server API Token Specified");
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogError("Profit Trailer check: No Server API Token specified. Please configure ProfitTrailerServerAPIToken in settings.general.json , ensuring it has to be the same Token as in the Profit Trailer Config File!");
|
|
result = false;
|
|
}
|
|
|
|
// Check for PT default setting key
|
|
if (!String.IsNullOrEmpty(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerDefaultSettingName))
|
|
{
|
|
this.Log.DoLogInfo("Profit Trailer check: Profit Trailer default setting name specified");
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogError("Profit Trailer check: No Profit Trailer default setting name specified! The default setting name is necessary to adjust your Profit Trailer settings since 2.0");
|
|
result = false;
|
|
}
|
|
|
|
// Check for PT monitor
|
|
if (!String.IsNullOrEmpty(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL))
|
|
{
|
|
this.Log.DoLogInfo("Profit Trailer check: Profit Trailer monitor URL found");
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogError("Profit Trailer check: No Profit Trailer monitor URL specified! The monitor URL is necessary to adjust your Profit Trailer settings since 2.0");
|
|
result = false;
|
|
}
|
|
|
|
// Check if PT monitor is reachable
|
|
if (SystemHelper.UrlIsReachable(this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL))
|
|
{
|
|
this.Log.DoLogInfo("Profit Trailer check: Profit Trailer monitor connection test succeeded");
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogError("Profit Trailer check: Your Profit Trailer monitor (" + this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerMonitorURL + ") is not available! Make sure your Profit Trailer bot is up and running and your monitor is accessible.");
|
|
result = false;
|
|
}
|
|
|
|
if (result)
|
|
{
|
|
this.Log.DoLogInfo("========== CHECKS FOR Profit Trailer COMPLETED! ==========");
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogInfo("========== CHECKS FOR Profit Trailer FAILED! ==========");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public void StartPTMagicIntervalTimer()
|
|
{
|
|
this.Timer = new System.Timers.Timer(this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 60 * 1000);
|
|
this.Timer.Enabled = true;
|
|
this.Timer.Elapsed += new System.Timers.ElapsedEventHandler(this.PTMagicIntervalTimer_Elapsed);
|
|
|
|
this.Log.DoLogInfo("Checking market trends every " + this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes.ToString() + " minutes...");
|
|
|
|
// Fire the first start immediately
|
|
this.PTMagicIntervalTimer_Elapsed(null, null);
|
|
}
|
|
#endregion
|
|
|
|
#region PTMagic Interval Methods
|
|
|
|
public void PTMagicIntervalTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
|
{
|
|
// Check if the bot is idle
|
|
if (this.State == Constants.PTMagicBotState_Idle)
|
|
{
|
|
// Only let one thread change the settings at once
|
|
lock (_lockObj)
|
|
{
|
|
try
|
|
{
|
|
// Change state to "Running"
|
|
this.State = Constants.PTMagicBotState_Running;
|
|
this.RunCount++;
|
|
this.LastRuntime = DateTime.UtcNow;
|
|
|
|
this.EnforceSettingsReapply = this.HaveSettingsChanged() || this.EnforceSettingsReapply;
|
|
|
|
if (PTMagicConfiguration.GeneralSettings.Application.IsEnabled)
|
|
{
|
|
// Validate settings
|
|
this.ValidateSettings();
|
|
|
|
// Start the process
|
|
this.Log.DoLogInfo("");
|
|
this.Log.DoLogInfo("##########################################################");
|
|
this.Log.DoLogInfo("#********************************************************#");
|
|
this.Log.DoLogInfo("Starting market trend check with Version " + this.CurrentVersion.Major + "." + this.CurrentVersion.Minor + "." + this.CurrentVersion.Build);
|
|
|
|
// Initialise the last runtime summary
|
|
this.LastRuntimeSummary = new Summary();
|
|
this.LastRuntimeSummary.LastRuntime = this.LastRuntime;
|
|
this.LastRuntimeSummary.Version = this.CurrentVersion.Major.ToString() + "." + this.CurrentVersion.Minor.ToString() + "." + this.CurrentVersion.Build.ToString();
|
|
|
|
// Check for latest GitHub version
|
|
this.CheckLatestGitHubVersion(this.LastRuntimeSummary.Version);
|
|
|
|
// Get latest main fiat currency exchange rate
|
|
this.GetMainFiatCurrencyDetails();
|
|
|
|
// Load current PT files
|
|
this.LoadCurrentProfitTrailerProperties();
|
|
|
|
// Loading SMS Summaries
|
|
this.LoadSMSSummaries();
|
|
|
|
// Get saved market info
|
|
this.MarketInfos = BaseAnalyzer.GetMarketInfosFromFile(this.PTMagicConfiguration, this.Log);
|
|
|
|
// Build exchange market data
|
|
this.BuildMarketData();
|
|
|
|
// Get markets from PT properties
|
|
this.BuildMarketList();
|
|
this.ValidateMarketList();
|
|
|
|
// Build global market trends configured in settings
|
|
this.BuildGlobalMarketTrends();
|
|
|
|
// Check for global settings triggers
|
|
GlobalSetting triggeredSetting = this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(s => s.SettingName.Equals(this.DefaultSettingName, StringComparison.InvariantCultureIgnoreCase));
|
|
List<string> matchedTriggers = new List<string>();
|
|
this.CheckGlobalSettingsTriggers(ref triggeredSetting, ref matchedTriggers);
|
|
|
|
// Activate global setting
|
|
this.ActivateSetting(ref triggeredSetting, ref matchedTriggers);
|
|
|
|
// Check for single market trend triggers
|
|
this.ApplySingleMarketSettings();
|
|
|
|
// Save new properties to Profit Trailer
|
|
this.SaveProfitTrailerProperties();
|
|
|
|
// Save Single Market Settings Summary
|
|
this.SaveSingleMarketSettingsSummary();
|
|
|
|
// Claculate raid time
|
|
DateTime endTime = DateTime.UtcNow;
|
|
int elapsedSeconds = (int)Math.Round(endTime.Subtract(this.LastRuntime).TotalSeconds, 0);
|
|
this.TotalElapsedSeconds += elapsedSeconds;
|
|
|
|
// Save Runtime Summary
|
|
this.SaveRuntimeSummary(elapsedSeconds);
|
|
|
|
// Summarise raid
|
|
this.Log.DoLogInfo("##########################################################");
|
|
this.Log.DoLogInfo("#******************* RAID SUMMARY ********************#");
|
|
this.Log.DoLogInfo("+ PT Magic Version: " + this.LastRuntimeSummary.Version);
|
|
if (!SystemHelper.IsRecentVersion(this.LastRuntimeSummary.Version, this.LatestVersion))
|
|
{
|
|
this.Log.DoLogWarn("+ Your version is out of date! The most recent version is " + this.LatestVersion);
|
|
}
|
|
this.Log.DoLogInfo("+ Instance name: " + PTMagicConfiguration.GeneralSettings.Application.InstanceName);
|
|
this.Log.DoLogInfo("+ Time spent: " + SystemHelper.GetProperDurationTime(elapsedSeconds));
|
|
this.Log.DoLogInfo("+ Active setting: " + this.LastRuntimeSummary.CurrentGlobalSetting.SettingName);
|
|
this.Log.DoLogInfo("+ Global setting changed: " + ((this.LastRuntimeSummary.LastGlobalSettingSwitch == this.LastRuntimeSummary.LastRuntime) ? "Yes" : "No") + " " + ((this.LastRuntimeSummary.FloodProtectedSetting != null) ? "(Flood protection!)" : ""));
|
|
this.Log.DoLogInfo("+ Single Market Settings changed: " + (this.SingleMarketSettingChanged ? "Yes" : "No"));
|
|
this.Log.DoLogInfo("+ PT Config updated: " + (((this.GlobalSettingWritten || this.SingleMarketSettingChanged) && !this.PTMagicConfiguration.GeneralSettings.Application.TestMode) ? "Yes" : "No"));
|
|
this.Log.DoLogInfo("+ Markets with active single market settings: " + this.TriggeredSingleMarketSettings.Count.ToString());
|
|
foreach (string activeSMS in this.SingleMarketSettingsCount.Keys)
|
|
{
|
|
this.Log.DoLogInfo("+ " + activeSMS + ": " + this.SingleMarketSettingsCount[activeSMS].ToString());
|
|
}
|
|
this.Log.DoLogInfo("+ " + this.TotalElapsedSeconds.ToString() + " Magicbots killed in " + this.RunCount.ToString() + " raids on Cryptodragon's Lair " + this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes.ToString() + ".");
|
|
this.Log.DoLogInfo("");
|
|
this.Log.DoLogInfo("DO NOT CLOSE THIS WINDOW! THIS IS THE BOT THAT ANALYZES TRENDS AND CHANGES SETTINGS!");
|
|
this.Log.DoLogInfo("");
|
|
this.Log.DoLogInfo("#********************************************************#");
|
|
this.Log.DoLogInfo("##########################################################");
|
|
this.Log.DoLogInfo("");
|
|
}
|
|
else
|
|
{
|
|
this.State = Constants.PTMagicBotState_Idle;
|
|
Log.DoLogWarn("PTMagic disabled, shutting down until next raid...");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Error
|
|
this.Log.DoLogCritical("A error occurred during the raid, the raid did not complete, but will try again next interval!", ex);
|
|
}
|
|
finally
|
|
{
|
|
// Cleanup to free memory in between intervals
|
|
this.Cleanup();
|
|
|
|
// Change state to Finished / Stopped
|
|
this.State = Constants.PTMagicBotState_Idle;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (this.RunCount > 1)
|
|
{
|
|
Log.DoLogWarn("PTMagic has been raiding since " + this.LastRuntime.ToLocalTime().ToString() + ". Checking things...");
|
|
|
|
if (File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "LastRuntimeSummary.json"))
|
|
{
|
|
FileInfo fiLastSummary = new FileInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "LastRuntimeSummary.json");
|
|
if (fiLastSummary.LastWriteTimeUtc < DateTime.UtcNow.AddMinutes(-(this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 2)))
|
|
{
|
|
Log.DoLogWarn("PTMagic raid " + this.RunCount.ToString() + " is taking longer than expected. Consider increasing your IntervalMinues setting, reducing other processes on your PC, or raising PTMagic's priority.");
|
|
this.State = Constants.PTMagicBotState_Idle;
|
|
Log.DoLogInfo("PTMagic status reset, waiting for the next raid to be good to go again.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log.DoLogWarn("No LastRuntimeSummary.json found after raid " + this.RunCount.ToString() + ", trying to reset PT Magic status...");
|
|
this.State = Constants.PTMagicBotState_Idle;
|
|
Log.DoLogInfo("PTMagic status reset, waiting for the next raid to be good to go again.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool HaveSettingsChanged()
|
|
{
|
|
bool result = false;
|
|
|
|
FileInfo generalSettingsFile = new FileInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "settings.general.json");
|
|
FileInfo analyzerSettingsFile = new FileInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "settings.analyzer.json");
|
|
if (generalSettingsFile.LastWriteTimeUtc > this.LastSettingFileCheck || analyzerSettingsFile.LastWriteTimeUtc > this.LastSettingFileCheck)
|
|
{
|
|
Log.DoLogInfo("Detected configuration changes. Reloading settings...");
|
|
|
|
try
|
|
{
|
|
PTMagicConfiguration = new PTMagicConfiguration();
|
|
|
|
GlobalSetting defaultSetting = this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(s => s.SettingName.Equals("default", StringComparison.InvariantCultureIgnoreCase));
|
|
if (defaultSetting == null)
|
|
{
|
|
defaultSetting = this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(s => s.SettingName.IndexOf("default", StringComparison.InvariantCultureIgnoreCase) > -1);
|
|
if (defaultSetting != null)
|
|
{
|
|
Log.DoLogDebug("No setting named 'default' found, taking '" + defaultSetting.SettingName + "' as default.");
|
|
this.DefaultSettingName = defaultSetting.SettingName;
|
|
}
|
|
else
|
|
{
|
|
Log.DoLogError("No 'default' setting found! Terminating process...");
|
|
this.Timer.Stop();
|
|
Exception ex = new Exception("No 'default' setting found!Terminating process...");
|
|
throw ex;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.DefaultSettingName = defaultSetting.SettingName;
|
|
}
|
|
|
|
Log.DoLogInfo("New configuration reloaded.");
|
|
this.LastSettingFileCheck = DateTime.UtcNow;
|
|
result = true;
|
|
|
|
if (this.Timer.Interval != this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 60 * 1000)
|
|
{
|
|
Log.DoLogInfo("Setting for 'IntervalMinutes' changed in MarketAnalyzer, setting new timer...");
|
|
this.Timer.Stop();
|
|
this.Timer.Interval = this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes * 60 * 1000;
|
|
this.Timer.Start();
|
|
Log.DoLogInfo("New timer set to " + this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes.ToString() + " minutes.");
|
|
}
|
|
|
|
SettingsFiles.CheckPresets(this.PTMagicConfiguration, this.Log, true);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.DoLogCritical("Error loading new configuration!", ex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = SettingsFiles.CheckPresets(this.PTMagicConfiguration, this.Log, false);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private void ValidateSettings()
|
|
{
|
|
//Reimport Initial ProfitTrailer Information(Deactivated for now)
|
|
//SettingsAPI.GetInitialProfitTrailerSettings(this.PTMagicConfiguration);
|
|
|
|
// Check for a valid exchange
|
|
if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange == null)
|
|
{
|
|
Log.DoLogError("Your setting for Application.Exchange in settings.general.json is invalid (null)! Terminating process.");
|
|
this.Timer.Stop();
|
|
Exception ex = new Exception("Your setting for Application.Exchange in settings.general.json is invalid (null)! Terminating process.");
|
|
throw ex;
|
|
}
|
|
else
|
|
{
|
|
if (String.IsNullOrEmpty(this.PTMagicConfiguration.GeneralSettings.Application.Exchange))
|
|
{
|
|
Log.DoLogError("Your setting for Application.Exchange in settings.general.json is invalid (empty)! Terminating process.");
|
|
this.Timer.Stop();
|
|
Exception ex = new Exception("Your setting for Application.Exchange in settings.general.json is invalid (empty)! Terminating process.");
|
|
throw ex;
|
|
}
|
|
else
|
|
{
|
|
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();
|
|
Exception ex = new Exception("Your setting for Application.Exchange in settings.general.json is invalid (" + this.PTMagicConfiguration.GeneralSettings.Application.Exchange + ")! Terminating process.");
|
|
throw ex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CheckLatestGitHubVersion(string currentVersion)
|
|
{
|
|
// Get latest version number
|
|
if (this.LastVersionCheck < DateTime.UtcNow.AddMinutes(-30))
|
|
{
|
|
this.LatestVersion = BaseAnalyzer.GetLatestGitHubRelease(this.Log, currentVersion);
|
|
this.LastVersionCheck = DateTime.UtcNow;
|
|
if (!SystemHelper.IsRecentVersion(currentVersion, this.LatestVersion))
|
|
{
|
|
this.Log.DoLogWarn("Your bot is out of date! The most recent version of PTMagic is " + this.LatestVersion);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void GetMainFiatCurrencyDetails()
|
|
{
|
|
this.LastRuntimeSummary.MainFiatCurrency = this.LastMainFiatCurrency;
|
|
this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = this.LastMainFiatCurrencyExchangeRate;
|
|
|
|
if (this.LastFiatCurrencyCheck < DateTime.UtcNow.AddHours(-12) && !this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency.Equals("USD", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
try
|
|
{
|
|
this.LastRuntimeSummary.MainFiatCurrency = this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency;
|
|
this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = BaseAnalyzer.GetMainFiatCurrencyRate(this.PTMagicConfiguration.GeneralSettings.Application.MainFiatCurrency, this.PTMagicConfiguration.GeneralSettings.Application.FreeCurrencyConverterAPIKey, this.Log);
|
|
this.LastMainFiatCurrency = this.LastRuntimeSummary.MainFiatCurrency;
|
|
this.LastMainFiatCurrencyExchangeRate = this.LastRuntimeSummary.MainFiatCurrencyExchangeRate;
|
|
|
|
this.LastFiatCurrencyCheck = DateTime.UtcNow;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
|
|
// Fallback to USD in case something went wrong
|
|
this.Log.DoLogError("Fixer.io exchange rate check error: " + ex.Message);
|
|
this.LastRuntimeSummary.MainFiatCurrency = "USD";
|
|
this.LastRuntimeSummary.MainFiatCurrencyExchangeRate = 1;
|
|
this.LastMainFiatCurrency = "USD";
|
|
this.LastMainFiatCurrencyExchangeRate = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void GetProfitTrailerPropertiesPaths(out string pairsPropertiesPath, out string dcaPropertiesPath, out string indicatorsPropertiesPath)
|
|
{
|
|
// Get current PT properties
|
|
pairsPropertiesPath = this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.PairsFileName;
|
|
dcaPropertiesPath = this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.DCAFileName;
|
|
indicatorsPropertiesPath = this.PTMagicConfiguration.GeneralSettings.Application.ProfitTrailerPath + Constants.PTPathTrading + Path.DirectorySeparatorChar + this.IndicatorsFileName;
|
|
}
|
|
|
|
private void LoadCurrentProfitTrailerProperties()
|
|
{
|
|
// Load current PT properties from API (Valid for PT 2.x and above)
|
|
this.Log.DoLogInfo("Loading current Profit Trailer properties from preset files...");
|
|
|
|
// Get current preset file PT properties
|
|
SettingsHandler.CompileProperties(this, this.ActiveSetting, this.LastSettingsChange.ToLocalTime());
|
|
|
|
if (this.PairsLines != null && this.DCALines != null && this.IndicatorsLines != null)
|
|
{
|
|
this.Log.DoLogInfo("Properties loaded - P (" + this.PairsLines.Count.ToString() + " lines) - D (" + this.DCALines.Count.ToString() + " lines) - I (" + this.IndicatorsLines.Count.ToString() + " lines).");
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogError("Unable to load all Profit Trailer properties! Waiting for the next interval to retry...");
|
|
Exception ex = new Exception("Unable to load all Profit Trailer properties! Waiting for the next interval to retry...");
|
|
this.State = 0;
|
|
throw ex;
|
|
}
|
|
|
|
// Get market from PT properties
|
|
this.LastRuntimeSummary.MainMarket = SettingsHandler.GetMainMarket(this.PTMagicConfiguration, this.PairsLines, this.Log);
|
|
}
|
|
|
|
private void LoadSMSSummaries()
|
|
{
|
|
this.Log.DoLogInfo("Loading Single Market Setting Summaries...");
|
|
this.SingleMarketSettingSummaries = new List<SingleMarketSettingSummary>();
|
|
if (File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "SingleMarketSettingSummary.json"))
|
|
{
|
|
try
|
|
{
|
|
Dictionary<string, bool> smsVerificationResult = new Dictionary<string, bool>();
|
|
// Cleanup SMS Summaries in case a SMS got removed
|
|
foreach (SingleMarketSettingSummary smsSummary in JsonConvert.DeserializeObject<List<SingleMarketSettingSummary>>(System.IO.File.ReadAllText(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "SingleMarketSettingSummary.json")))
|
|
{
|
|
string smsName = smsSummary.SingleMarketSetting.SettingName;
|
|
bool smsIsValid = false;
|
|
if (smsVerificationResult.ContainsKey(smsName))
|
|
{
|
|
smsIsValid = smsVerificationResult[smsName];
|
|
}
|
|
else
|
|
{
|
|
SingleMarketSetting sms = this.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings.Find(s => s.SettingName.Equals(smsName));
|
|
if (sms != null)
|
|
{
|
|
smsIsValid = true;
|
|
smsVerificationResult.Add(smsName, true);
|
|
}
|
|
else
|
|
{
|
|
smsVerificationResult.Add(smsName, false);
|
|
}
|
|
}
|
|
|
|
if (smsIsValid)
|
|
{
|
|
this.SingleMarketSettingSummaries.Add(smsSummary);
|
|
}
|
|
}
|
|
|
|
this.Log.DoLogInfo("Single Market Setting Summaries loaded.");
|
|
}
|
|
catch { }
|
|
}
|
|
}
|
|
|
|
private void BuildMarketData()
|
|
{
|
|
|
|
if (!String.IsNullOrEmpty(this.PTMagicConfiguration.GeneralSettings.Application.CoinMarketCapAPIKey))
|
|
{
|
|
// Get most recent market data from CMC
|
|
string cmcMarketDataResult = CoinMarketCap.GetMarketData(this.PTMagicConfiguration, this.Log);
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogInfo("No CMC API-Key specified. No CMC Data will be pulled");
|
|
}
|
|
|
|
if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Bittrex", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
|
|
// Get most recent market data from Bittrex
|
|
this.ExchangeMarketList = Bittrex.GetMarketData(this.LastRuntimeSummary.MainMarket, this.MarketInfos, this.PTMagicConfiguration, this.Log);
|
|
}
|
|
else if (this.PTMagicConfiguration.GeneralSettings.Application.Exchange.Equals("Binance", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
// 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))
|
|
{
|
|
|
|
// Get most recent market data from Poloniex
|
|
this.ExchangeMarketList = Poloniex.GetMarketData(this.LastRuntimeSummary.MainMarket, this.MarketInfos, this.PTMagicConfiguration, this.Log);
|
|
}
|
|
|
|
// Check if problems occured during the Exchange contact
|
|
if (this.ExchangeMarketList == null)
|
|
{
|
|
Exception ex = new Exception("Unable to contact " + this.PTMagicConfiguration.GeneralSettings.Application.Exchange + " for fresh market data. Trying again in " + this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.IntervalMinutes + " minute(s).");
|
|
Log.DoLogError(ex.Message);
|
|
this.State = Constants.PTMagicBotState_Idle;
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
private void BuildMarketList()
|
|
{
|
|
string marketPairs = SettingsHandler.GetMarketPairs(this.PTMagicConfiguration, this.PairsLines, this.Log);
|
|
if (marketPairs.ToLower().Equals("all") || marketPairs.ToLower().Equals("false") || marketPairs.ToLower().Equals("true") || String.IsNullOrEmpty(marketPairs))
|
|
{
|
|
this.MarketList = this.ExchangeMarketList;
|
|
}
|
|
else
|
|
{
|
|
// Since PT 2.0 the main market is no longer included in the market list so we need to rebuild the list
|
|
List<string> originalMarketList = SystemHelper.ConvertTokenStringToList(marketPairs, ",");
|
|
foreach (string market in originalMarketList)
|
|
{
|
|
this.MarketList.Add(SystemHelper.GetFullMarketName(this.LastRuntimeSummary.MainMarket, market, this.PTMagicConfiguration.GeneralSettings.Application.Exchange));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ValidateMarketList()
|
|
{
|
|
// Check if markets are valid for the selected main market
|
|
List<string> validMarkets = this.MarketList.FindAll(m => m.IndexOf(this.LastRuntimeSummary.MainMarket, StringComparison.InvariantCultureIgnoreCase) > -1);
|
|
if (validMarkets.Count == 0)
|
|
{
|
|
Exception ex = new Exception("No valid pairs found for main market '" + this.LastRuntimeSummary.MainMarket + "' in configured pars list (" + SystemHelper.ConvertListToTokenString(this.MarketList, ",", true) + ")! Terminating process...");
|
|
Log.DoLogError(ex.Message);
|
|
this.State = Constants.PTMagicBotState_Idle;
|
|
this.Timer.Stop();
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
private void BuildGlobalMarketTrends()
|
|
{
|
|
this.Log.DoLogInfo("Build global market trends...");
|
|
this.SingleMarketTrendChanges = BaseAnalyzer.BuildMarketTrends("Exchange", this.LastRuntimeSummary.MainMarket, this.MarketList, "Volume", false, new Dictionary<string, List<MarketTrendChange>>(), this.PTMagicConfiguration, this.Log);
|
|
this.GlobalMarketTrendChanges = new Dictionary<string, List<MarketTrendChange>>();
|
|
|
|
// CoinMarketCap
|
|
this.GlobalMarketTrendChanges = BaseAnalyzer.BuildMarketTrends("CoinMarketCap", this.LastRuntimeSummary.MainMarket, new List<string>(), "", true, this.GlobalMarketTrendChanges, this.PTMagicConfiguration, this.Log);
|
|
|
|
// Bittrex
|
|
foreach (MarketTrend marketTrend in this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.FindAll(mt => mt.Platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase)))
|
|
{
|
|
if (this.SingleMarketTrendChanges.ContainsKey(marketTrend.Name))
|
|
{
|
|
int maxMarkets = this.SingleMarketTrendChanges[marketTrend.Name].Count;
|
|
if (marketTrend.MaxMarkets > 0 && marketTrend.MaxMarkets <= this.SingleMarketTrendChanges[marketTrend.Name].Count)
|
|
{
|
|
maxMarkets = marketTrend.MaxMarkets;
|
|
}
|
|
|
|
this.GlobalMarketTrendChanges.Add(marketTrend.Name, this.SingleMarketTrendChanges[marketTrend.Name].Take(maxMarkets).ToList());
|
|
}
|
|
}
|
|
|
|
this.AverageMarketTrendChanges = BaseAnalyzer.BuildGlobalMarketTrends(this.GlobalMarketTrendChanges, this.PTMagicConfiguration, this.Log);
|
|
|
|
this.Log.DoLogInfo("Global market trends built.");
|
|
}
|
|
|
|
private void CheckGlobalSettingsTriggers(ref GlobalSetting triggeredSetting, ref List<string> matchedTriggers)
|
|
{
|
|
this.Log.DoLogInfo("Checking global settings triggers...");
|
|
foreach (GlobalSetting globalSetting in this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings)
|
|
{
|
|
// Reset triggers for each setting
|
|
matchedTriggers = new List<string>();
|
|
|
|
if (globalSetting.Triggers.Count > 0)
|
|
{
|
|
this.Log.DoLogInfo("Checking triggers for '" + globalSetting.SettingName + "'...");
|
|
List<bool> triggerResults = new List<bool>();
|
|
foreach (Trigger trigger in globalSetting.Triggers)
|
|
{
|
|
MarketTrend marketTrend = this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.Find(mt => mt.Name == trigger.MarketTrendName);
|
|
if (marketTrend != null)
|
|
{
|
|
|
|
// Get market trend change for trigger
|
|
if (this.AverageMarketTrendChanges.ContainsKey(marketTrend.Name))
|
|
{
|
|
double averageMarketTrendChange = this.AverageMarketTrendChanges[marketTrend.Name];
|
|
if (averageMarketTrendChange >= trigger.MinChange && averageMarketTrendChange < trigger.MaxChange)
|
|
{
|
|
|
|
// Trigger met!
|
|
this.Log.DoLogInfo("Trigger '" + trigger.MarketTrendName + "' triggered! TrendChange = " + averageMarketTrendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%");
|
|
|
|
string triggerContent = trigger.MarketTrendName + " - ";
|
|
if (trigger.MinChange != Constants.MinTrendChange)
|
|
{
|
|
triggerContent += " - Min: " + trigger.MinChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%";
|
|
}
|
|
|
|
if (trigger.MaxChange != Constants.MaxTrendChange)
|
|
{
|
|
triggerContent += " - Max: " + trigger.MaxChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%";
|
|
}
|
|
|
|
matchedTriggers.Add(triggerContent);
|
|
|
|
triggerResults.Add(true);
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogDebug("Trigger '" + trigger.MarketTrendName + "' not triggered. TrendChange = " + averageMarketTrendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%");
|
|
triggerResults.Add(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogError("Trigger '" + trigger.MarketTrendName + "' not found in this.AverageMarketTrendChanges[] (" + SystemHelper.ConvertListToTokenString(this.AverageMarketTrendChanges.Keys.ToList(), ",", true) + "). Unable to load recent trends?");
|
|
triggerResults.Add(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogWarn("Market Trend '" + trigger.MarketTrendName + "' not found! Trigger ignored!");
|
|
triggerResults.Add(false);
|
|
}
|
|
}
|
|
|
|
// Check if all triggers have to get triggered or just one
|
|
bool settingTriggered = false;
|
|
switch (globalSetting.TriggerConnection.ToLower())
|
|
{
|
|
case "and":
|
|
settingTriggered = triggerResults.FindAll(tr => tr == false).Count == 0;
|
|
break;
|
|
case "or":
|
|
settingTriggered = triggerResults.FindAll(tr => tr == true).Count > 0;
|
|
break;
|
|
}
|
|
|
|
// Setting got triggered -> Activate it!
|
|
if (settingTriggered)
|
|
{
|
|
triggeredSetting = globalSetting;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ActivateSetting(ref GlobalSetting triggeredSetting, ref List<string> matchedTriggers)
|
|
{
|
|
// Do we need to write the settings?
|
|
if (this.EnforceSettingsReapply || !this.ActiveSettingName.Equals(triggeredSetting.SettingName, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
// Check if we need to force a refresh of the settings
|
|
this.Log.DoLogInfo("Setting '" + this.ActiveSettingName + "' currently active. Checking for flood protection...");
|
|
|
|
// If the setting we are about to activate is the default one, do not list matched triggers
|
|
if (triggeredSetting.SettingName.Equals(this.DefaultSettingName, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
matchedTriggers = new List<string>();
|
|
}
|
|
|
|
// Check if flood protection is active
|
|
if (this.EnforceSettingsReapply || this.LastSettingsChange <= DateTime.UtcNow.AddMinutes(-PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes))
|
|
{
|
|
// Setting not set => Change setting
|
|
if (!this.ActiveSettingName.Equals(triggeredSetting.SettingName, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
this.Log.DoLogInfo("Switching global settings to '" + triggeredSetting.SettingName + "'...");
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogInfo("Applying '" + triggeredSetting.SettingName + "' as the settings.analyzer.json or a preset file got changed.");
|
|
}
|
|
|
|
// Get file lines from the preset files
|
|
SettingsHandler.CompileProperties(this, triggeredSetting, DateTime.Now);
|
|
this.GlobalSettingWritten = true;
|
|
|
|
// Record the switch in the runtime summary
|
|
this.LastRuntimeSummary.LastGlobalSettingSwitch = this.LastRuntimeSummary.LastRuntime;
|
|
this.LastRuntimeSummary.CurrentGlobalSetting = triggeredSetting;
|
|
|
|
// Record last settings run
|
|
this.LastSettingsChange = DateTime.UtcNow;
|
|
|
|
// Build Telegram message
|
|
try
|
|
{
|
|
string telegramMessage;
|
|
telegramMessage = this.PTMagicConfiguration.GeneralSettings.Application.InstanceName + ": Setting switched to '*" + SystemHelper.SplitCamelCase(triggeredSetting.SettingName) + "*'.";
|
|
|
|
if (matchedTriggers.Count > 0)
|
|
{
|
|
telegramMessage += "\n\n*Matching Triggers:*";
|
|
foreach (string triggerResult in matchedTriggers)
|
|
{
|
|
telegramMessage += "\n" + triggerResult;
|
|
}
|
|
}
|
|
|
|
if (this.AverageMarketTrendChanges.Keys.Count > 0)
|
|
{
|
|
telegramMessage += "\n\n*Market Trends:*";
|
|
foreach (string key in this.AverageMarketTrendChanges.Keys)
|
|
{
|
|
telegramMessage += "\n" + key + ": " + this.AverageMarketTrendChanges[key].ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%";
|
|
}
|
|
}
|
|
|
|
// Send Telegram message
|
|
if (this.PTMagicConfiguration.GeneralSettings.Telegram.IsEnabled)
|
|
{
|
|
TelegramHelper.SendMessage(this.PTMagicConfiguration.GeneralSettings.Telegram.BotToken, this.PTMagicConfiguration.GeneralSettings.Telegram.ChatId, telegramMessage, this.PTMagicConfiguration.GeneralSettings.Telegram.SilentMode, this.Log);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
this.Log.DoLogCritical("Failed to send Telegram message", ex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Flood protection
|
|
this.Log.DoLogInfo("Flood protection active until " + this.LastSettingsChange.AddMinutes(PTMagicConfiguration.GeneralSettings.Application.FloodProtectionMinutes).ToString() + " (UTC). Not switching settings to '" + triggeredSetting.SettingName + "'!");
|
|
|
|
this.LastRuntimeSummary.FloodProtectedSetting = triggeredSetting;
|
|
this.LastRuntimeSummary.CurrentGlobalSetting = this.ActiveSetting;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
matchedTriggers = new List<string>();
|
|
|
|
// Setting already set => Do nothing
|
|
this.Log.DoLogInfo("Setting '" + triggeredSetting.SettingName + "' already active. No action taken.");
|
|
|
|
this.LastRuntimeSummary.CurrentGlobalSetting = triggeredSetting;
|
|
}
|
|
|
|
// Set Active settings
|
|
this.ActiveSetting = this.LastRuntimeSummary.CurrentGlobalSetting;
|
|
this.ActiveSettingName = this.ActiveSetting.SettingName;
|
|
}
|
|
|
|
private void ApplySingleMarketSettings()
|
|
{
|
|
if (this.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings.Count > 0)
|
|
{
|
|
this.Log.DoLogInfo("Checking single market settings triggers for " + this.MarketList.Count.ToString() + " markets...");
|
|
|
|
int marketPairProcess = 1;
|
|
Dictionary<string, List<string>> matchedMarketTriggers = new Dictionary<string, List<string>>();
|
|
foreach (string marketPair in this.MarketList)
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - Checking triggers (" + marketPairProcess.ToString() + "/" + this.MarketList.Count.ToString() + ")...");
|
|
|
|
bool stopTriggers = false;
|
|
foreach (SingleMarketSetting marketSetting in this.PTMagicConfiguration.AnalyzerSettings.SingleMarketSettings)
|
|
{
|
|
List<string> matchedSingleMarketTriggers = new List<string>();
|
|
|
|
// Check ignore markets
|
|
List<string> ignoredMarkets = SystemHelper.ConvertTokenStringToList(marketSetting.IgnoredMarkets, ",");
|
|
if (ignoredMarkets.Contains(marketPair))
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - Is ignored in '" + marketSetting.SettingName + "'.");
|
|
continue;
|
|
}
|
|
|
|
// Check allowed markets
|
|
List<string> allowedMarkets = SystemHelper.ConvertTokenStringToList(marketSetting.AllowedMarkets, ",");
|
|
if (allowedMarkets.Count > 0 && !allowedMarkets.Contains(marketPair))
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - Is not allowed in '" + marketSetting.SettingName + "'.");
|
|
continue;
|
|
}
|
|
|
|
// Check ignore global settings
|
|
List<string> ignoredGlobalSettings = SystemHelper.ConvertTokenStringToList(marketSetting.IgnoredGlobalSettings, ",");
|
|
if (ignoredGlobalSettings.Contains(this.ActiveSettingName))
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - '" + this.ActiveSettingName + "' - Is ignored in '" + marketSetting.SettingName + "'.");
|
|
continue;
|
|
}
|
|
|
|
// Check allowed global settings
|
|
List<string> allowedGlobalSettings = SystemHelper.ConvertTokenStringToList(marketSetting.AllowedGlobalSettings, ",");
|
|
if (allowedGlobalSettings.Count > 0 && !allowedGlobalSettings.Contains(this.ActiveSettingName))
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - '" + this.ActiveSettingName + "' - Is not allowed in '" + marketSetting.SettingName + "'.");
|
|
continue;
|
|
}
|
|
|
|
#region Checking Off Triggers
|
|
SingleMarketSettingSummary smss = this.SingleMarketSettingSummaries.Find(s => s.Market.Equals(marketPair, StringComparison.InvariantCultureIgnoreCase) && s.SingleMarketSetting.SettingName.Equals(marketSetting.SettingName, StringComparison.InvariantCultureIgnoreCase));
|
|
if (smss != null)
|
|
{
|
|
if (marketSetting.OffTriggers != null)
|
|
{
|
|
if (marketSetting.OffTriggers.Count > 0)
|
|
{
|
|
|
|
this.Log.DoLogDebug("'" + marketPair + "' - Checking off triggers '" + marketSetting.SettingName + "'...");
|
|
|
|
List<bool> offTriggerResults = new List<bool>();
|
|
foreach (OffTrigger offTrigger in marketSetting.OffTriggers)
|
|
{
|
|
if (offTrigger.HoursSinceTriggered > 0)
|
|
{
|
|
#region Check for Activation time period trigger
|
|
int smsActiveHours = (int)Math.Floor(DateTime.UtcNow.Subtract(smss.ActivationDateTimeUTC).TotalHours);
|
|
if (smsActiveHours >= offTrigger.HoursSinceTriggered)
|
|
{
|
|
|
|
// Trigger met!
|
|
this.Log.DoLogDebug("'" + marketPair + "' - SMS already active for " + smsActiveHours.ToString() + " hours. Trigger matched!");
|
|
|
|
offTriggerResults.Add(true);
|
|
}
|
|
else
|
|
{
|
|
|
|
// Trigger not met!
|
|
this.Log.DoLogDebug("'" + marketPair + "' - SMS only active for " + smsActiveHours.ToString() + " hours. Trigger not matched!");
|
|
|
|
offTriggerResults.Add(false);
|
|
}
|
|
#endregion
|
|
}
|
|
else if (offTrigger.Min24hVolume > 0 || offTrigger.Max24hVolume < Constants.Max24hVolume)
|
|
{
|
|
#region Check for 24h volume trigger
|
|
List<MarketTrendChange> marketTrendChanges = this.SingleMarketTrendChanges[this.SingleMarketTrendChanges.Keys.Last()];
|
|
if (marketTrendChanges.Count > 0)
|
|
{
|
|
MarketTrendChange mtc = marketTrendChanges.Find(m => m.Market.Equals(marketPair, StringComparison.InvariantCultureIgnoreCase));
|
|
if (mtc != null)
|
|
{
|
|
if (mtc.Volume24h >= offTrigger.Min24hVolume && mtc.Volume24h <= offTrigger.Max24hVolume)
|
|
{
|
|
|
|
// Trigger met!
|
|
this.Log.DoLogDebug("'" + marketPair + "' - 24h volume off trigger matched! 24h volume = " + mtc.Volume24h.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket);
|
|
|
|
offTriggerResults.Add(true);
|
|
}
|
|
else
|
|
{
|
|
|
|
// Trigger not met!
|
|
this.Log.DoLogDebug("'" + marketPair + "' - 24h volume off trigger not matched! 24h volume = " + mtc.Volume24h.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket);
|
|
|
|
offTriggerResults.Add(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
else
|
|
{
|
|
#region Check for market trend triggers
|
|
if (this.SingleMarketTrendChanges.ContainsKey(offTrigger.MarketTrendName))
|
|
{
|
|
|
|
List<MarketTrendChange> marketTrendChanges = this.SingleMarketTrendChanges[offTrigger.MarketTrendName];
|
|
if (marketTrendChanges.Count > 0)
|
|
{
|
|
double averageMarketTrendChange = marketTrendChanges.Average(m => m.TrendChange);
|
|
|
|
MarketTrendChange mtc = marketTrendChanges.Find(m => m.Market.Equals(marketPair, StringComparison.InvariantCultureIgnoreCase));
|
|
if (mtc != null)
|
|
{
|
|
// Get trend change according to configured relation
|
|
double trendChange = mtc.TrendChange;
|
|
|
|
if (offTrigger.MarketTrendRelation.Equals(Constants.MarketTrendRelationRelative))
|
|
{
|
|
|
|
// Build pair trend change relative to the global market trend
|
|
trendChange = trendChange - averageMarketTrendChange;
|
|
}
|
|
else if (offTrigger.MarketTrendRelation.Equals(Constants.MarketTrendRelationRelativeTrigger))
|
|
{
|
|
|
|
// Build pair trend change relative to the trigger price
|
|
double currentPrice = mtc.LastPrice;
|
|
double triggerPrice = smss.TriggerSnapshot.LastPrice;
|
|
double triggerTrend = (currentPrice - triggerPrice) / triggerPrice * 100;
|
|
trendChange = triggerTrend;
|
|
}
|
|
|
|
// Get market trend change for trigger
|
|
if (trendChange >= offTrigger.MinChange && trendChange < offTrigger.MaxChange)
|
|
{
|
|
|
|
// Trigger met!
|
|
this.Log.DoLogDebug("'" + marketPair + "' - Off Trigger '" + offTrigger.MarketTrendName + "' triggered! TrendChange (" + offTrigger.MarketTrendRelation + ") = " + trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%");
|
|
|
|
offTriggerResults.Add(true);
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - Off Trigger '" + offTrigger.MarketTrendName + "' not triggered. TrendChange (" + offTrigger.MarketTrendRelation + ") = " + trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%");
|
|
offTriggerResults.Add(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
offTriggerResults.Add(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
offTriggerResults.Add(false);
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|
|
|
|
// Check if all off triggers have to get triggered or just one
|
|
bool settingOffTriggered = false;
|
|
switch (marketSetting.OffTriggerConnection.ToLower())
|
|
{
|
|
case "and":
|
|
settingOffTriggered = offTriggerResults.FindAll(tr => tr == false).Count == 0;
|
|
break;
|
|
case "or":
|
|
settingOffTriggered = offTriggerResults.FindAll(tr => tr == true).Count > 0;
|
|
break;
|
|
}
|
|
|
|
// Setting got off triggered, remove it from the summary
|
|
if (settingOffTriggered)
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - '" + marketSetting.SettingName + "' off triggered!");
|
|
this.SingleMarketSettingSummaries.Remove(smss);
|
|
smss = null;
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - '" + marketSetting.SettingName + "' not off triggered!");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - '" + marketSetting.SettingName + "' has no off triggers -> triggering off!");
|
|
this.SingleMarketSettingSummaries.Remove(smss);
|
|
smss = null;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
if (marketSetting.Triggers.Count > 0 && !stopTriggers)
|
|
{
|
|
#region Checking Triggers
|
|
this.Log.DoLogDebug("'" + marketPair + "' - Checking triggers for '" + marketSetting.SettingName + "'...");
|
|
|
|
List<bool> triggerResults = new List<bool>();
|
|
Dictionary<int, double> relevantTriggers = new Dictionary<int, double>();
|
|
int triggerIndex = 0;
|
|
foreach (Trigger trigger in marketSetting.Triggers)
|
|
{
|
|
|
|
if (trigger.Min24hVolume > 0 || trigger.Max24hVolume < Constants.Max24hVolume)
|
|
{
|
|
#region Check for 24h volume trigger
|
|
List<MarketTrendChange> marketTrendChanges = this.SingleMarketTrendChanges[this.SingleMarketTrendChanges.Keys.Last()];
|
|
if (marketTrendChanges.Count > 0)
|
|
{
|
|
MarketTrendChange mtc = marketTrendChanges.Find(m => m.Market.Equals(marketPair, StringComparison.InvariantCultureIgnoreCase));
|
|
if (mtc != null)
|
|
{
|
|
|
|
if (mtc.Volume24h >= trigger.Min24hVolume && mtc.Volume24h <= trigger.Max24hVolume)
|
|
{
|
|
// Trigger met!
|
|
this.Log.DoLogDebug("'" + marketPair + "' - 24h volume trigger matched! 24h volume = " + mtc.Volume24h.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket);
|
|
|
|
relevantTriggers.Add(triggerIndex, mtc.Volume24h);
|
|
|
|
string triggerContent = "24h Volume";
|
|
if (trigger.Min24hVolume > 0)
|
|
{
|
|
triggerContent += " - Min: " + trigger.Min24hVolume.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket;
|
|
}
|
|
|
|
if (trigger.Max24hVolume < Constants.Max24hVolume)
|
|
{
|
|
triggerContent += " - Max: " + trigger.Max24hVolume.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket;
|
|
}
|
|
|
|
matchedSingleMarketTriggers.Add(marketSetting.SettingName + ": " + triggerContent + " - 24h volume = " + mtc.Volume24h.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket);
|
|
|
|
triggerResults.Add(true);
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - 24h volume trigger not matched. 24h volume = " + mtc.Volume24h.ToString(new System.Globalization.CultureInfo("en-US")) + " " + this.LastRuntimeSummary.MainMarket);
|
|
triggerResults.Add(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
else if (trigger.AgeDaysLowerThan > 0)
|
|
{
|
|
#region Check for age trigger
|
|
MarketInfo marketInfo = null;
|
|
if (this.MarketInfos.ContainsKey(marketPair))
|
|
{
|
|
marketInfo = this.MarketInfos[marketPair];
|
|
}
|
|
|
|
if (marketInfo != null)
|
|
{
|
|
int marketAge = (int)Math.Floor(DateTime.UtcNow.Subtract(marketInfo.FirstSeen).TotalDays);
|
|
if (marketAge < trigger.AgeDaysLowerThan)
|
|
{
|
|
matchedSingleMarketTriggers.Add(marketSetting.SettingName + ": '" + marketPair + "' is only " + marketAge.ToString() + " days old on this exchange. Trigger matched!");
|
|
this.Log.DoLogDebug("'" + marketPair + "' - Is only " + marketAge.ToString() + " days old on this exchange. Trigger matched!");
|
|
|
|
relevantTriggers.Add(triggerIndex, marketAge);
|
|
triggerResults.Add(true);
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - Age Trigger not triggered. Is already " + marketAge.ToString() + " days old on this exchange.");
|
|
triggerResults.Add(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
matchedSingleMarketTriggers.Add("Age for '" + marketPair + "' not found, trigger matched just to be safe!");
|
|
this.Log.DoLogDebug("'" + marketPair + "' - Age not found, trigger matched just to be safe!");
|
|
triggerResults.Add(true);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
else
|
|
{
|
|
#region Check for market trend triggers
|
|
if (this.SingleMarketTrendChanges.ContainsKey(trigger.MarketTrendName))
|
|
{
|
|
|
|
List<MarketTrendChange> marketTrendChanges = this.SingleMarketTrendChanges[trigger.MarketTrendName];
|
|
if (marketTrendChanges.Count > 0)
|
|
{
|
|
double averageMarketTrendChange = marketTrendChanges.Average(m => m.TrendChange);
|
|
|
|
MarketTrendChange mtc = marketTrendChanges.Find(m => m.Market.Equals(marketPair, StringComparison.InvariantCultureIgnoreCase));
|
|
if (mtc != null)
|
|
{
|
|
// Get trend change according to configured relation
|
|
double trendChange = mtc.TrendChange;
|
|
|
|
if (trigger.MarketTrendRelation.Equals(Constants.MarketTrendRelationRelative))
|
|
{
|
|
// Build pair trend change relative to the global market trend
|
|
trendChange = trendChange - averageMarketTrendChange;
|
|
}
|
|
|
|
// Get market trend change for trigger
|
|
if (trendChange >= trigger.MinChange && trendChange < trigger.MaxChange)
|
|
{
|
|
|
|
// Trigger met!
|
|
this.Log.DoLogDebug("'" + marketPair + "' - Trigger '" + trigger.MarketTrendName + "' triggered! TrendChange (" + trigger.MarketTrendRelation + ") = " + trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%");
|
|
|
|
relevantTriggers.Add(triggerIndex, trendChange);
|
|
|
|
string triggerContent = trigger.MarketTrendName + " - ";
|
|
if (trigger.MinChange != Constants.MinTrendChange)
|
|
{
|
|
triggerContent += " - Min: " + trigger.MinChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%";
|
|
}
|
|
|
|
if (trigger.MaxChange != Constants.MaxTrendChange)
|
|
{
|
|
triggerContent += " - Max: " + trigger.MaxChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%";
|
|
}
|
|
|
|
matchedSingleMarketTriggers.Add(marketSetting.SettingName + ": " + triggerContent + " - TrendChange (" + trigger.MarketTrendRelation + ") = " + trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%");
|
|
|
|
triggerResults.Add(true);
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - Trigger '" + trigger.MarketTrendName + "' not triggered. TrendChange (" + trigger.MarketTrendRelation + ") = " + trendChange.ToString("#,#0.00", new System.Globalization.CultureInfo("en-US")) + "%");
|
|
triggerResults.Add(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - No market trend change found for '" + trigger.MarketTrendName + "'! Coin just got released? Trigger ignored!");
|
|
triggerResults.Add(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogWarn("'" + marketPair + "' - No market trend changes found for '" + trigger.MarketTrendName + "'! Trigger ignored!");
|
|
triggerResults.Add(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MarketTrend marketTrend = this.PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.MarketTrends.Find(mt => mt.Name.Equals(trigger.MarketTrendName, StringComparison.InvariantCultureIgnoreCase));
|
|
if (marketTrend != null)
|
|
{
|
|
if (!marketTrend.Platform.Equals("Exchange", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
this.Log.DoLogWarn("Market Trend '" + trigger.MarketTrendName + "' is invalid for single market settings! Only trends using the platform 'Exchange' are valid for single market settings.");
|
|
triggerResults.Add(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogWarn("Market Trend '" + trigger.MarketTrendName + "' not found! Trigger ignored!");
|
|
triggerResults.Add(false);
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
triggerIndex++;
|
|
}
|
|
|
|
// Check if all triggers have to get triggered or just one
|
|
bool settingTriggered = false;
|
|
switch (marketSetting.TriggerConnection.ToLower())
|
|
{
|
|
case "and":
|
|
settingTriggered = triggerResults.FindAll(tr => tr == false).Count == 0;
|
|
break;
|
|
case "or":
|
|
settingTriggered = triggerResults.FindAll(tr => tr == true).Count > 0;
|
|
break;
|
|
}
|
|
#endregion
|
|
|
|
bool isFreshTrigger = true;
|
|
|
|
// Setting not triggered -> Check if it is already active as a long term SMS using Off Triggers
|
|
if (!settingTriggered)
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - SMS '" + marketSetting.SettingName + "' not triggered, checking for long term activation.");
|
|
if (smss != null)
|
|
{
|
|
if (marketSetting.OffTriggers != null)
|
|
{
|
|
if (marketSetting.OffTriggers.Count > 0)
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - SMS '" + marketSetting.SettingName + "' has off triggers, starting special trigger...");
|
|
// Setting already active and using off triggers -> set as triggered
|
|
settingTriggered = true;
|
|
isFreshTrigger = false;
|
|
|
|
matchedSingleMarketTriggers = new List<string>();
|
|
foreach (string matchedTriggerContent in smss.TriggerSnapshot.MatchedTriggersContent)
|
|
{
|
|
if (matchedTriggerContent.StartsWith(marketSetting.SettingName + ":"))
|
|
{
|
|
matchedSingleMarketTriggers.Add(matchedTriggerContent);
|
|
}
|
|
}
|
|
|
|
int removalLength = matchedSingleMarketTriggers.Count - marketSetting.Triggers.Count;
|
|
if (removalLength > 0)
|
|
{
|
|
matchedSingleMarketTriggers.RemoveRange(0, removalLength);
|
|
}
|
|
|
|
this.Log.DoLogDebug("'" + marketPair + "' - Activating SMS '" + marketSetting.SettingName + "' as off triggers are not met.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Setting got triggered -> Activate it!
|
|
if (settingTriggered)
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - '" + marketSetting.SettingName + "' triggered!");
|
|
|
|
// Save matched triggers to get displayed in the comment lines
|
|
if (!matchedMarketTriggers.ContainsKey(marketPair))
|
|
{
|
|
matchedMarketTriggers.Add(marketPair, matchedSingleMarketTriggers);
|
|
}
|
|
else
|
|
{
|
|
matchedMarketTriggers[marketPair].AddRange(matchedSingleMarketTriggers);
|
|
}
|
|
|
|
if (!this.TriggeredSingleMarketSettings.ContainsKey(marketPair))
|
|
{
|
|
List<SingleMarketSetting> smsList = new List<SingleMarketSetting>();
|
|
smsList.Add(marketSetting);
|
|
this.TriggeredSingleMarketSettings.Add(marketPair, smsList);
|
|
}
|
|
else
|
|
{
|
|
this.TriggeredSingleMarketSettings[marketPair].Add(marketSetting);
|
|
}
|
|
|
|
// Counting triggered setting
|
|
if (!this.SingleMarketSettingsCount.ContainsKey(marketSetting.SettingName))
|
|
{
|
|
this.SingleMarketSettingsCount.Add(marketSetting.SettingName, 1);
|
|
}
|
|
else
|
|
{
|
|
this.SingleMarketSettingsCount[marketSetting.SettingName]++;
|
|
}
|
|
|
|
if (isFreshTrigger)
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - SMS '" + marketSetting.SettingName + "' saving summary data...");
|
|
|
|
// Check if this setting is already active for this market
|
|
if (smss == null || marketSetting.RefreshOffTriggers)
|
|
{
|
|
if (smss == null)
|
|
{
|
|
smss = new SingleMarketSettingSummary();
|
|
}
|
|
else
|
|
{
|
|
this.SingleMarketSettingSummaries.Remove(smss);
|
|
}
|
|
|
|
smss.ActivationDateTimeUTC = DateTime.UtcNow;
|
|
smss.Market = marketPair;
|
|
smss.SingleMarketSetting = marketSetting;
|
|
smss.TriggerSnapshot = new TriggerSnapshot();
|
|
smss.TriggerSnapshot.Last24hVolume = 0;
|
|
smss.TriggerSnapshot.LastPrice = 0;
|
|
smss.TriggerSnapshot.RelevantTriggers = relevantTriggers;
|
|
smss.TriggerSnapshot.MatchedTriggersContent = matchedSingleMarketTriggers;
|
|
|
|
List<MarketTrendChange> marketTrendChanges = this.SingleMarketTrendChanges[this.SingleMarketTrendChanges.Keys.Last()];
|
|
if (marketTrendChanges.Count > 0)
|
|
{
|
|
MarketTrendChange mtc = marketTrendChanges.Find(m => m.Market.Equals(marketPair, StringComparison.InvariantCultureIgnoreCase));
|
|
if (mtc != null)
|
|
{
|
|
smss.TriggerSnapshot.Last24hVolume = mtc.Volume24h;
|
|
smss.TriggerSnapshot.LastPrice = mtc.LastPrice;
|
|
}
|
|
}
|
|
|
|
this.SingleMarketSettingSummaries.Add(smss);
|
|
|
|
this.Log.DoLogDebug("'" + marketPair + "' - SMS '" + marketSetting.SettingName + "' summary data saved.");
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - SMS '" + marketSetting.SettingName + "' already active for this market and no refresh allowed.");
|
|
}
|
|
}
|
|
|
|
// Stop processing other settings if configured
|
|
if (marketSetting.StopProcessWhenTriggered)
|
|
{
|
|
stopTriggers = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogDebug("'" + marketPair + "' - '" + marketSetting.SettingName + "' not triggered!");
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((marketPairProcess % 10) == 0)
|
|
{
|
|
this.Log.DoLogInfo("What are you looking at? " + marketPairProcess + "/" + this.MarketList.Count + " markets done...");
|
|
}
|
|
|
|
marketPairProcess++;
|
|
}
|
|
|
|
if (this.TriggeredSingleMarketSettings.Count > 0)
|
|
{
|
|
this.Log.DoLogInfo("Building single market settings for '" + this.TriggeredSingleMarketSettings.Count.ToString() + "' markets...");
|
|
|
|
// Write single market settings
|
|
var newSingleMarketSettings = SettingsHandler.CompileSingleMarketProperties(this, matchedMarketTriggers);
|
|
|
|
// Compare against last run to see if they have changed or not
|
|
if (_lastActiveSingleMarketSettings == null || haveSingleMarketSettingsHaveChanged(_lastActiveSingleMarketSettings, newSingleMarketSettings))
|
|
{
|
|
// Single market settings differ from the last raid, so update
|
|
this.SingleMarketSettingChanged = true;
|
|
}
|
|
|
|
// Buffer for next raid
|
|
_lastActiveSingleMarketSettings = newSingleMarketSettings;
|
|
|
|
this.Log.DoLogInfo("Building single market settings completed.");
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogInfo("No settings triggered for single markets.");
|
|
|
|
// Remove single market settings if no triggers are met - if necessary
|
|
this.SingleMarketSettingChanged = SettingsHandler.RemoveSingleMarketSettings(this);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogInfo("No single market settings found.");
|
|
}
|
|
}
|
|
|
|
private bool haveSingleMarketSettingsHaveChanged(List<KeyValuePair<string, string>> oldMarketTriggers, List<KeyValuePair<string, string>> newMarketTriggers)
|
|
{
|
|
// Check if the SMS settings have changed between raids
|
|
string oldSms = "", newSms = "";
|
|
|
|
foreach (var entry in oldMarketTriggers)
|
|
{
|
|
oldSms += string.Format("{0}: {1}|", entry.Key, entry.Value);
|
|
}
|
|
|
|
foreach (var entry in newMarketTriggers)
|
|
{
|
|
newSms += string.Format("{0}: {1}|", entry.Key, entry.Value);
|
|
}
|
|
|
|
return !string.Equals(oldSms, newSms, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private void SaveProfitTrailerProperties()
|
|
{
|
|
// Get current PT properties
|
|
string pairsPropertiesPath, dcaPropertiesPath, indicatorsPropertiesPath;
|
|
GetProfitTrailerPropertiesPaths(out pairsPropertiesPath, out dcaPropertiesPath, out indicatorsPropertiesPath);
|
|
|
|
if (this.GlobalSettingWritten || this.SingleMarketSettingChanged)
|
|
{
|
|
// Save current PT properties to API (Valid for PT 2.x and above)
|
|
this.Log.DoLogInfo("Saving properties using API...");
|
|
|
|
// Send all Properties
|
|
if (!this.PTMagicConfiguration.GeneralSettings.Application.TestMode)
|
|
{
|
|
SettingsAPI.SendPropertyLinesToAPI(this.PairsLines, this.DCALines, this.IndicatorsLines, this.PTMagicConfiguration, this.Log);
|
|
}
|
|
|
|
this.Log.DoLogInfo("Properties saved!");
|
|
}
|
|
else
|
|
{
|
|
this.Log.DoLogInfo("Nothing changed, no config written!");
|
|
}
|
|
}
|
|
|
|
private void SaveSingleMarketSettingsSummary()
|
|
{
|
|
JsonSerializerSettings smsSummaryJsonSettings = new JsonSerializerSettings();
|
|
smsSummaryJsonSettings.NullValueHandling = NullValueHandling.Ignore;
|
|
smsSummaryJsonSettings.DefaultValueHandling = DefaultValueHandling.Ignore;
|
|
|
|
FileHelper.WriteTextToFile(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar, "SingleMarketSettingSummary.json", JsonConvert.SerializeObject(this.SingleMarketSettingSummaries, Formatting.None, smsSummaryJsonSettings));
|
|
|
|
this.Log.DoLogInfo("Single Market Settings Summary saved.");
|
|
}
|
|
|
|
private void SaveRuntimeSummary(int elapsedSeconds)
|
|
{
|
|
this.Log.DoLogInfo("Building LastRuntimeSummary.json for your monitor...");
|
|
|
|
this.LastRuntimeSummary.LastRuntimeSeconds = elapsedSeconds;
|
|
|
|
// Load existing runtime summary and read ongoing data
|
|
if (File.Exists(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "LastRuntimeSummary.json"))
|
|
{
|
|
try
|
|
{
|
|
Summary summary = JsonConvert.DeserializeObject<Summary>(System.IO.File.ReadAllText(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar + "LastRuntimeSummary.json"));
|
|
if (summary != null)
|
|
{
|
|
|
|
// Last setting switch in case the app got restarted and has no history
|
|
if (this.LastRuntimeSummary.LastGlobalSettingSwitch == Constants.confMinDate)
|
|
{
|
|
this.LastRuntimeSummary.LastGlobalSettingSwitch = summary.LastGlobalSettingSwitch;
|
|
}
|
|
|
|
// Market trend changes history for graph data
|
|
foreach (string key in summary.MarketTrendChanges.Keys)
|
|
{
|
|
this.LastRuntimeSummary.MarketTrendChanges.Add(key, summary.MarketTrendChanges[key].FindAll(mtc => mtc.TrendDateTime >= DateTime.UtcNow.AddHours(-PTMagicConfiguration.AnalyzerSettings.MarketAnalyzer.StoreDataMaxHours)));
|
|
}
|
|
|
|
// Global setting summary to be kept
|
|
this.LastRuntimeSummary.GlobalSettingSummary.AddRange(summary.GlobalSettingSummary.FindAll(gss => gss.SwitchDateTime >= DateTime.UtcNow.AddHours(-96)));
|
|
|
|
this.Log.DoLogInfo("Summary: Loaded old LastRuntimeSummary.json to keep data.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
this.Log.DoLogCritical("Summary: Error loading old summary (" + ex.Message + "). Creating new one.", ex);
|
|
}
|
|
}
|
|
|
|
this.Log.DoLogInfo("Summary: Building global settings summary...");
|
|
|
|
// Change setting summary
|
|
GlobalSettingSummary lastSettingSummary = null;
|
|
if (this.LastRuntimeSummary.LastGlobalSettingSwitch == this.LastRuntimeSummary.LastRuntime || this.LastRuntimeSummary.GlobalSettingSummary.Count == 0)
|
|
{
|
|
|
|
// Setting got switched this run, add a new setting summary
|
|
GlobalSettingSummary gss = new GlobalSettingSummary();
|
|
gss.SettingName = this.LastRuntimeSummary.CurrentGlobalSetting.SettingName;
|
|
gss.SwitchDateTime = this.LastRuntimeSummary.LastRuntime.ToUniversalTime();
|
|
|
|
if (this.LastRuntimeSummary.GlobalSettingSummary.Count > 0)
|
|
{
|
|
lastSettingSummary = this.LastRuntimeSummary.GlobalSettingSummary.OrderByDescending(lss => lss.SwitchDateTime).First();
|
|
lastSettingSummary.ActiveSeconds = (int)Math.Ceiling(DateTime.UtcNow.Subtract(lastSettingSummary.SwitchDateTime).TotalSeconds);
|
|
}
|
|
|
|
this.LastRuntimeSummary.GlobalSettingSummary.Add(gss);
|
|
|
|
lastSettingSummary = this.LastRuntimeSummary.GlobalSettingSummary.OrderByDescending(lss => lss.SwitchDateTime).First();
|
|
}
|
|
else
|
|
{
|
|
|
|
// Setting did not get switched, update data
|
|
if (this.LastRuntimeSummary.GlobalSettingSummary.Count > 0)
|
|
{
|
|
lastSettingSummary = this.LastRuntimeSummary.GlobalSettingSummary.OrderByDescending(lss => lss.SwitchDateTime).First();
|
|
lastSettingSummary.ActiveSeconds = (int)Math.Ceiling(DateTime.UtcNow.Subtract(lastSettingSummary.SwitchDateTime).TotalSeconds);
|
|
}
|
|
}
|
|
|
|
this.Log.DoLogInfo("Summary: Built global settings summary.");
|
|
|
|
this.Log.DoLogInfo("Summary: Save market trend changes for summary.");
|
|
// Save market trend changes for the summary
|
|
foreach (string key in this.AverageMarketTrendChanges.Keys)
|
|
{
|
|
List<MarketTrendChange> mtChanges = new List<MarketTrendChange>();
|
|
if (this.LastRuntimeSummary.MarketTrendChanges.ContainsKey(key))
|
|
{
|
|
mtChanges = this.LastRuntimeSummary.MarketTrendChanges[key];
|
|
}
|
|
|
|
MarketTrendChange newChange = new MarketTrendChange();
|
|
newChange.MarketTrendName = key;
|
|
newChange.TrendChange = this.AverageMarketTrendChanges[key];
|
|
newChange.TrendDateTime = this.LastRuntimeSummary.LastRuntime;
|
|
|
|
mtChanges.Add(newChange);
|
|
if (lastSettingSummary != null)
|
|
{
|
|
if (!lastSettingSummary.MarketTrendChanges.ContainsKey(key))
|
|
{
|
|
GlobalSetting gs = this.PTMagicConfiguration.AnalyzerSettings.GlobalSettings.Find(g => g.SettingName.Equals(lastSettingSummary.SettingName));
|
|
if (gs != null)
|
|
{
|
|
if (gs.SettingName.Equals("Default", StringComparison.InvariantCultureIgnoreCase) || gs.Triggers.Find(t => t.MarketTrendName.Equals(key)) != null)
|
|
{
|
|
lastSettingSummary.MarketTrendChanges.Add(key, newChange);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.LastRuntimeSummary.MarketTrendChanges[key] = mtChanges;
|
|
}
|
|
this.Log.DoLogInfo("Summary: Market trends saved.");
|
|
|
|
this.Log.DoLogInfo("Summary: Getting current global properties...");
|
|
|
|
// Get current global settings from PAIRS.PROPERTIES
|
|
Dictionary<string, string> pairsProperties = SettingsHandler.GetPropertiesAsDictionary(this.PairsLines);
|
|
Dictionary<string, string> dcaProperties = SettingsHandler.GetPropertiesAsDictionary(this.DCALines);
|
|
|
|
string defaultBuyValueString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "ALL_buy_value", "DEFAULT_A_buy_value");
|
|
double defaultBuyValue = SystemHelper.TextToDouble(defaultBuyValueString, 0, "en-US");
|
|
|
|
string defaultTrailingBuyString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "ALL_trailing_buy", "DEFAULT_trailing_buy");
|
|
double defaultTrailingBuy = SystemHelper.TextToDouble(defaultTrailingBuyString, 0, "en-US");
|
|
|
|
string defaultSellValueString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "ALL_sell_value", "DEFAULT_A_sell_value");
|
|
double defaultSellValue = SystemHelper.TextToDouble(defaultSellValueString, 0, "en-US");
|
|
|
|
string defaultTrailingProfitString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "ALL_trailing_profit", "DEFAULT_trailing_profit");
|
|
double defaultTrailingProfit = SystemHelper.TextToDouble(defaultTrailingProfitString, 0, "en-US");
|
|
|
|
string defaultMaxTradingPairsString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "ALL_max_trading_pairs", "max_trading_pairs");
|
|
double defaultMaxTradingPairs = SystemHelper.TextToDouble(defaultMaxTradingPairsString, 0, "en-US");
|
|
|
|
string defaultMaxCostString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "ALL_max_cost", "DEFAULT_initial_cost");
|
|
double defaultMaxCost = SystemHelper.TextToDouble(defaultMaxCostString, 0, "en-US");
|
|
|
|
string defaultMaxCostPercentageString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "ALL_max_cost_percentage", "DEFAULT_initial_cost_percentage");
|
|
double defaultMaxCostPercentage = SystemHelper.TextToDouble(defaultMaxCostPercentageString, 0, "en-US");
|
|
|
|
string defaultMinBuyVolumeString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "ALL_min_buy_volume", "DEFAULT_min_buy_volume");
|
|
double defaultMinBuyVolume = SystemHelper.TextToDouble(defaultMinBuyVolumeString, 0, "en-US");
|
|
|
|
string defaultDCALevelString = SettingsHandler.GetCurrentPropertyValue(dcaProperties, "max_buy_times", "DEFAULT_DCA_max_buy_times");
|
|
double defaultDCALevel = SystemHelper.TextToDouble(defaultDCALevelString, 0, "en-US");
|
|
|
|
string defaultBuyStrategy = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "ALL_buy_strategy", "DEFAULT_A_buy_strategy");
|
|
|
|
string defaultSellStrategy = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "ALL_sell_strategy", "DEFAULT_A_sell_strategy");
|
|
|
|
string defaultDCAEnabledString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "ALL_DCA_enabled", "DEFAULT_DCA_enabled");
|
|
bool defaultDCAEnabled = (defaultDCAEnabledString.Equals("false", StringComparison.InvariantCultureIgnoreCase)) ? false : true;
|
|
|
|
string defaultSOMActiveString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "ALL_sell_only_mode", "DEFAULT_sell_only_mode_enabled");
|
|
bool defaultSOMActive = (defaultSOMActiveString.Equals("false", StringComparison.InvariantCultureIgnoreCase)) ? false : true;
|
|
this.LastRuntimeSummary.IsSOMActive = defaultSOMActive;
|
|
|
|
string dcaBuyStrategyString = SettingsHandler.GetCurrentPropertyValue(dcaProperties, "buy_strategy", "DEFAULT_DCA_A_buy_strategy");
|
|
this.LastRuntimeSummary.DCABuyStrategy = dcaBuyStrategyString;
|
|
|
|
this.LastRuntimeSummary.BuyValue = defaultBuyValue;
|
|
this.LastRuntimeSummary.TrailingBuy = defaultTrailingBuy;
|
|
this.LastRuntimeSummary.SellValue = defaultSellValue;
|
|
this.LastRuntimeSummary.TrailingProfit = defaultTrailingProfit;
|
|
this.LastRuntimeSummary.MaxTradingPairs = defaultMaxTradingPairs;
|
|
this.LastRuntimeSummary.MaxCost = defaultMaxCost;
|
|
this.LastRuntimeSummary.MaxCostPercentage = defaultMaxCostPercentage;
|
|
this.LastRuntimeSummary.MinBuyVolume = defaultMinBuyVolume;
|
|
this.LastRuntimeSummary.BuyStrategy = defaultBuyStrategy;
|
|
this.LastRuntimeSummary.SellStrategy = defaultSellStrategy;
|
|
this.LastRuntimeSummary.DCALevels = defaultDCALevel;
|
|
|
|
double maxDCALevel = this.LastRuntimeSummary.DCALevels;
|
|
if (maxDCALevel == 0) maxDCALevel = 1000;
|
|
|
|
string dcaDefaultTriggerString = SettingsHandler.GetCurrentPropertyValue(dcaProperties, "buy_trigger", "DEFAULT_DCA_buy_trigger");
|
|
double dcaDefaultTrigger = SystemHelper.TextToDouble(dcaDefaultTriggerString, 0, "en-US");
|
|
|
|
this.LastRuntimeSummary.DCATrigger = dcaDefaultTrigger;
|
|
|
|
|
|
// Get PROFITPERCENTAGE strategy label
|
|
string ProfitPercentageLabel = "";
|
|
for (char c = 'A'; c <= 'Z'; c++)
|
|
{
|
|
|
|
string buyStrategyName = SettingsHandler.GetCurrentPropertyValue(dcaProperties, "DEFAULT_DCA_" + c + "_buy_strategy", "");
|
|
if (buyStrategyName.Contains("PROFITPERCENTAGE"))
|
|
{
|
|
|
|
ProfitPercentageLabel = "" + c;
|
|
}
|
|
}
|
|
|
|
// Get configured DCA triggers
|
|
for (int dca = 1; dca <= maxDCALevel; dca++)
|
|
{
|
|
string dcaTriggerString = SettingsHandler.GetCurrentPropertyValue(dcaProperties, ProfitPercentageLabel + "buy_value_" + dca.ToString(), "DEFAULT_DCA_" + ProfitPercentageLabel + "_buy_value_" + dca.ToString());
|
|
if (!String.IsNullOrEmpty(dcaTriggerString))
|
|
{
|
|
double dcaTrigger = SystemHelper.TextToDouble(dcaTriggerString, 0, "en-US");
|
|
|
|
this.LastRuntimeSummary.DCATriggers.Add(dca, dcaTrigger);
|
|
}
|
|
else
|
|
{
|
|
if (this.LastRuntimeSummary.DCALevels == 0) this.LastRuntimeSummary.DCALevels = dca - 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Get configured DCA percentages
|
|
string dcaDefaultPercentageString = SettingsHandler.GetCurrentPropertyValue(dcaProperties, "DEFAULT_DCA_buy_percentage", "");
|
|
double dcaDefaultPercentage = SystemHelper.TextToDouble(dcaDefaultPercentageString, 0, "en-US");
|
|
|
|
this.LastRuntimeSummary.DCAPercentage = dcaDefaultPercentage;
|
|
|
|
for (int dca = 1; dca <= maxDCALevel; dca++)
|
|
{
|
|
string dcaPercentageString = SettingsHandler.GetCurrentPropertyValue(dcaProperties, "DEFAULT_DCA_buy_percentage_" + dca.ToString(), "");
|
|
if (!String.IsNullOrEmpty(dcaPercentageString))
|
|
{
|
|
double dcaPercentage = SystemHelper.TextToDouble(dcaPercentageString, 0, "en-US");
|
|
|
|
this.LastRuntimeSummary.DCAPercentages.Add(dca, dcaPercentage);
|
|
}
|
|
else
|
|
{
|
|
if (this.LastRuntimeSummary.DCALevels == 0) this.LastRuntimeSummary.DCALevels = dca - 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Get configured Buy Strategies
|
|
for (char c = 'A'; c <= 'Z'; c++)
|
|
{
|
|
string buyStrategyName = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "DEFAULT_" + c + "_buy_strategy", "");
|
|
if (!String.IsNullOrEmpty(buyStrategyName))
|
|
{
|
|
StrategySummary buyStrategy = new StrategySummary();
|
|
buyStrategy.Name = buyStrategyName;
|
|
buyStrategy.Value = SystemHelper.TextToDouble(SettingsHandler.GetCurrentPropertyValue(pairsProperties, "DEFAULT_" + c + "_buy_value", ""), 0, "en-US");
|
|
|
|
this.LastRuntimeSummary.BuyStrategies.Add(buyStrategy);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Get configured Sell Strategies
|
|
for (char c = 'A'; c <= 'Z'; c++)
|
|
{
|
|
string sellStrategyName = SettingsHandler.GetCurrentPropertyValue(pairsProperties, "DEFAULT_" + c + "_sell_strategy", "");
|
|
if (!String.IsNullOrEmpty(sellStrategyName))
|
|
{
|
|
StrategySummary sellStrategy = new StrategySummary();
|
|
sellStrategy.Name = sellStrategyName;
|
|
sellStrategy.Value = SystemHelper.TextToDouble(SettingsHandler.GetCurrentPropertyValue(pairsProperties, "DEFAULT_" + c + "_sell_value", ""), 0, "en-US");
|
|
|
|
this.LastRuntimeSummary.SellStrategies.Add(sellStrategy);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Get configured DCA Buy Strategies
|
|
for (char c = 'A'; c <= 'Z'; c++)
|
|
{
|
|
string buyStrategyName = SettingsHandler.GetCurrentPropertyValue(dcaProperties, "DEFAULT_DCA_" + c + "_buy_strategy", "");
|
|
if (!String.IsNullOrEmpty(buyStrategyName))
|
|
{
|
|
StrategySummary buyStrategy = new StrategySummary();
|
|
buyStrategy.Name = buyStrategyName;
|
|
buyStrategy.Value = SystemHelper.TextToDouble(SettingsHandler.GetCurrentPropertyValue(dcaProperties, "DEFAULT_DCA_" + c + "_buy_value", ""), 0, "en-US");
|
|
|
|
this.LastRuntimeSummary.DCABuyStrategies.Add(buyStrategy);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Get configured DCA Sell Strategies
|
|
for (char c = 'A'; c <= 'Z'; c++)
|
|
{
|
|
string sellStrategyName = SettingsHandler.GetCurrentPropertyValue(dcaProperties, "DEFAULT_DCA_" + c + "_sell_strategy", "");
|
|
if (!String.IsNullOrEmpty(sellStrategyName))
|
|
{
|
|
StrategySummary sellStrategy = new StrategySummary();
|
|
sellStrategy.Name = sellStrategyName;
|
|
sellStrategy.Value = SystemHelper.TextToDouble(SettingsHandler.GetCurrentPropertyValue(dcaProperties, "DEFAULT_DCA_" + c + "_sell_value", ""), 0, "en-US");
|
|
|
|
this.LastRuntimeSummary.DCASellStrategies.Add(sellStrategy);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Get current main currency price
|
|
Dictionary<string, Market> recentMarkets = BaseAnalyzer.GetMarketDataFromFile(this.PTMagicConfiguration, this.Log, "Exchange", DateTime.UtcNow, "Recent");
|
|
if (recentMarkets.Keys.Count > 0)
|
|
{
|
|
this.LastRuntimeSummary.MainMarketPrice = recentMarkets.First().Value.MainCurrencyPriceUSD;
|
|
|
|
if (!this.LastRuntimeSummary.MainFiatCurrency.Equals("USD", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
this.LastRuntimeSummary.MainMarketPrice = this.LastRuntimeSummary.MainMarketPrice * this.LastRuntimeSummary.MainFiatCurrencyExchangeRate;
|
|
}
|
|
}
|
|
|
|
this.Log.DoLogInfo("Summary: Current global properties saved.");
|
|
|
|
// Get current single market settings from PAIRS.PROPERTIES for each configured market
|
|
this.Log.DoLogInfo("Summary: Getting current single market properties...");
|
|
|
|
foreach (string marketPair in this.MarketList)
|
|
{
|
|
MarketPairSummary mpSummary = new MarketPairSummary();
|
|
mpSummary.CurrentBuyValue = defaultBuyValue;
|
|
mpSummary.CurrentTrailingBuy = defaultTrailingBuy;
|
|
mpSummary.CurrentSellValue = defaultSellValue;
|
|
mpSummary.CurrentTrailingProfit = defaultTrailingProfit;
|
|
mpSummary.IsDCAEnabled = defaultDCAEnabled;
|
|
mpSummary.IsSOMActive = defaultSOMActive;
|
|
mpSummary.ActiveSingleSettings = new List<SingleMarketSetting>();
|
|
|
|
if (this.MarketList.Contains(marketPair))
|
|
{
|
|
|
|
// Pair is allowed for trading, check for individual values
|
|
mpSummary.IsTradingEnabled = true;
|
|
|
|
if (this.TriggeredSingleMarketSettings.Count > 0)
|
|
{
|
|
if (this.TriggeredSingleMarketSettings.ContainsKey(marketPair))
|
|
{
|
|
mpSummary.ActiveSingleSettings = this.TriggeredSingleMarketSettings[marketPair];
|
|
}
|
|
}
|
|
|
|
string marketPairSimple = marketPair.Replace(this.LastRuntimeSummary.MainMarket, "").Replace("_", "").Replace("-", "");
|
|
|
|
// Get configured Buy Strategies
|
|
for (char c = 'A'; c <= 'Z'; c++)
|
|
{
|
|
string buyStrategyName = SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPairSimple + "_" + c + "_buy_strategy", "");
|
|
if (!String.IsNullOrEmpty(buyStrategyName))
|
|
{
|
|
StrategySummary buyStrategy = new StrategySummary();
|
|
buyStrategy.Name = buyStrategyName;
|
|
buyStrategy.Value = SystemHelper.TextToDouble(SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPair + "_" + c + "_buy_value", ""), 0, "en-US");
|
|
|
|
mpSummary.BuyStrategies.Add(buyStrategy);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (mpSummary.BuyStrategies.Count == 0) mpSummary.BuyStrategies = this.LastRuntimeSummary.BuyStrategies;
|
|
|
|
// Get configured Sell Strategies
|
|
for (char c = 'A'; c <= 'Z'; c++)
|
|
{
|
|
string sellStrategyName = SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPairSimple + "_" + c + "_sell_strategy", "");
|
|
if (!String.IsNullOrEmpty(sellStrategyName))
|
|
{
|
|
StrategySummary sellStrategy = new StrategySummary();
|
|
sellStrategy.Name = sellStrategyName;
|
|
sellStrategy.Value = SystemHelper.TextToDouble(SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPairSimple + "_" + c + "_sell_value", ""), 0, "en-US");
|
|
|
|
mpSummary.SellStrategies.Add(sellStrategy);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (mpSummary.SellStrategies.Count == 0) mpSummary.SellStrategies = this.LastRuntimeSummary.SellStrategies;
|
|
|
|
string pairBuyValueString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPairSimple + "_buy_value", marketPairSimple + "_A_buy_value");
|
|
double pairBuyValue = SystemHelper.TextToDouble(pairBuyValueString, 100, "en-US");
|
|
if (pairBuyValue < 100) mpSummary.CurrentBuyValue = pairBuyValue;
|
|
|
|
string pairTrailingBuyString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPair + "_trailing_buy", marketPairSimple + "_trailing_buy");
|
|
double pairTrailingBuy = SystemHelper.TextToDouble(pairTrailingBuyString, -1, "en-US");
|
|
if (pairTrailingBuy > -1) mpSummary.CurrentTrailingBuy = pairTrailingBuy;
|
|
|
|
string pairSellValueString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPair + "_sell_value", marketPairSimple + "_A_sell_value");
|
|
double pairSellValue = SystemHelper.TextToDouble(pairSellValueString, 0, "en-US");
|
|
if (pairSellValue > -1) mpSummary.CurrentSellValue = pairSellValue;
|
|
|
|
string pairTrailingProfitString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPair + "_trailing_profit", marketPairSimple + "_trailing_profit");
|
|
double pairTrailingProfit = SystemHelper.TextToDouble(pairTrailingProfitString, 0, "en-US");
|
|
if (pairTrailingProfit > -1) mpSummary.CurrentTrailingProfit = pairTrailingProfit;
|
|
|
|
string pairTradingEnabledString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPair + "_trading_enabled", marketPairSimple + "_trading_enabled");
|
|
mpSummary.IsTradingEnabled = (pairTradingEnabledString.Equals("false", StringComparison.InvariantCultureIgnoreCase)) ? false : true;
|
|
|
|
string pairDCAEnabledString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPair + "_DCA_enabled", marketPairSimple + "_DCA_enabled");
|
|
mpSummary.IsDCAEnabled = (pairDCAEnabledString.Equals("false", StringComparison.InvariantCultureIgnoreCase)) ? false : defaultDCAEnabled;
|
|
|
|
string pairSOMActiveString = SettingsHandler.GetCurrentPropertyValue(pairsProperties, marketPair + "_sell_only_mode", marketPairSimple + "_sell_only_mode_enabled");
|
|
mpSummary.IsSOMActive = (pairSOMActiveString.Equals("true", StringComparison.InvariantCultureIgnoreCase)) ? true : false;
|
|
}
|
|
|
|
// Get market trend values for each market pair
|
|
foreach (string marketTrendName in this.SingleMarketTrendChanges.Keys)
|
|
{
|
|
if (this.SingleMarketTrendChanges.ContainsKey(marketTrendName))
|
|
{
|
|
MarketTrendChange mtc = this.SingleMarketTrendChanges[marketTrendName].Find(m => m.Market == marketPair);
|
|
if (mtc != null)
|
|
{
|
|
double marketTrendChange = mtc.TrendChange;
|
|
mpSummary.MarketTrendChanges.Add(marketTrendName, marketTrendChange);
|
|
|
|
mpSummary.LatestPrice = mtc.LastPrice;
|
|
mpSummary.Latest24hVolume = mtc.Volume24h;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.LastRuntimeSummary.MarketSummary.Add(marketPair, mpSummary);
|
|
}
|
|
|
|
this.Log.DoLogInfo("Summary: Current single market properties saved.");
|
|
|
|
string serialziedJson = JsonConvert.SerializeObject(this.LastRuntimeSummary);
|
|
|
|
// Save the summary JSON file
|
|
try
|
|
{
|
|
FileHelper.WriteTextToFile(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar, "LastRuntimeSummary.json", serialziedJson);
|
|
|
|
this.Log.DoLogInfo("Summary: LastRuntimeSummary.json saved.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
this.Log.DoLogCritical("Exception while writing LastRuntimeSummary.json", ex);
|
|
|
|
try
|
|
{
|
|
this.Log.DoLogInfo("Summary: Retrying one more time to save LastRuntimeSummary.json.");
|
|
FileHelper.WriteTextToFile(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathData + Path.DirectorySeparatorChar, "LastRuntimeSummary.json", serialziedJson);
|
|
|
|
this.Log.DoLogInfo("Summary: LastRuntimeSummary.json saved.");
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
this.Log.DoLogCritical("Nope, another Exception while writing LastRuntimeSummary.json", ex2);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Cleanup()
|
|
{
|
|
// Clear down memory
|
|
this.GlobalSettingWritten = false;
|
|
this.SingleMarketSettingChanged = false;
|
|
this.EnforceSettingsReapply = false;
|
|
|
|
this.PairsLines = null;
|
|
this.DCALines = null;
|
|
this.IndicatorsLines = null;
|
|
|
|
this.SingleMarketTrendChanges = new Dictionary<string, List<MarketTrendChange>>();
|
|
this.GlobalMarketTrendChanges = new Dictionary<string, List<MarketTrendChange>>();
|
|
this.AverageMarketTrendChanges = new Dictionary<string, double>();
|
|
this.SingleMarketSettingsCount = new Dictionary<string, int>();
|
|
this.TriggeredSingleMarketSettings = new Dictionary<string, List<SingleMarketSetting>>();
|
|
this.ExchangeMarketList = null;
|
|
this.MarketList = new List<string>();
|
|
this.LastRuntimeSummary = null;
|
|
|
|
// Cleanup log files
|
|
string logsPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + Constants.PTMagicPathLogs + Path.DirectorySeparatorChar;
|
|
if (Directory.Exists(logsPath))
|
|
{
|
|
FileHelper.CleanupFiles(logsPath, 24 * 3);
|
|
this.Log.DoLogInfo("Cleaned up logfiles.");
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|