using System; using System.Collections.Generic; using System.Linq; using System.Xml; using TradeIdeas.ServerConnection; using TradeIdeas.TIProData.Interfaces; using TradeIdeas.XML; namespace TradeIdeas.TIProGUI.Charting { public delegate void ColumnLibraryReceivedHandler(); public class TiqColumnLibraryManager { public event ColumnLibraryReceivedHandler ColumnLibraryReceived; /// /// Flex command to use when retrieving indicator library from the server. /// private const string INDICATORS_FLEX_COMMAND = "chart_indicators"; /// /// The indicator cache maximum age seconds. If last retrieved longer ago than this, then re-retrieve. /// private readonly int _indicatorCacheMaxAgeSeconds; private readonly IConnectionMaster _connectionMaster; private TiqColumnLibraryManager() { _connectionMaster = GuiEnvironment.FindConnectionMaster(""); // TODO: Change the below to pull from GlobalSettings so we can override with Custom4.xml var prodCacheMaxAgeSeconds = GuiEnvironment.XmlConfig.Node("CHARTS").Node("INDICATOR_CACHE_MAX_AGE").Property("THRESHOLD", 300); var isProd = !GuiEnvironment.DevelopmentMode; _indicatorCacheMaxAgeSeconds = isProd ? prodCacheMaxAgeSeconds : 0; } private static TiqColumnLibraryManager _instance; public static TiqColumnLibraryManager Instance() { return _instance ?? (_instance = new TiqColumnLibraryManager()); } /// /// True = running in indicator admin mode. This mode turns on access to the ModifyIndicator form. /// public bool IsIndicatorAdmin() { return GuiEnvironment.DevelopmentMode; } private TalkWithServer.CancelToken _indicatorCancelToken; /// /// Initiates the request to load the indicator library from the server. Cancels any existing previous /// requests that might be still uncanceled. /// public void LoadIndicatorLibrary() { if (IsIndicatorAdmin() || !GuiEnvironment.LastRetrievedIndicatorLibrary.HasValue || (DateTime.Now - GuiEnvironment.LastRetrievedIndicatorLibrary.Value).TotalSeconds > _indicatorCacheMaxAgeSeconds || GuiEnvironment.TiqColumns.Count == 0) { if (null != _indicatorCancelToken) _connectionMaster.SendManager.CancelMessage(_indicatorCancelToken); var message = new object[] { "command", INDICATORS_FLEX_COMMAND, "subcommand", "me_get_config" }; _indicatorCancelToken = new TalkWithServer.CancelToken(); var messageToSend = TalkWithServer.CreateMessage(message); _connectionMaster.SendManager.SendMessage(messageToSend, IndicatorResponse, true, null, _indicatorCancelToken); } else if (null != ColumnLibraryReceived) { if (GuiEnvironment.TiqColumns.Count > 0) ColumnLibraryReceived(); } } /// /// Handles the response from the server which defines the indicator library. /// /// The body. /// The client identifier. private void IndicatorResponse(byte[] body, object clientId) { if (null == body) LoadIndicatorLibrary(); else { string unused = System.Text.Encoding.UTF8.GetString(body); XmlNode message = XmlHelper.Get(body).Node(0); string type = message.Property("type"); if (type == "columns") { GuiEnvironment.TiqColumns.Clear(); int columnCount = message.Property("COUNT", 0); for (int i = 1; i <= columnCount; i++) { var indicator = GetChartIndicator(message, i); if (null != indicator) GuiEnvironment.TiqColumns.Add(indicator); } GroupIndicatorSets(); } else if (type == "parameters") { int parameterCount = message.Property("COUNT", 0); for (int i = 1; i <= parameterCount; i++) { var parameter = GetIndicatorParameter(message, i); if (null != parameter) AssignIndicatorParameter(parameter); } } else if (type == "config_complete") { GuiEnvironment.LastRetrievedIndicatorLibrary = DateTime.Now; ColumnLibraryReceived?.Invoke(); if (null == _indicatorCancelToken) return; _connectionMaster.SendManager.CancelMessage(_indicatorCancelToken); _indicatorCancelToken = null; } } } /// /// When parameters come from the server they are in a separate list. This function assigns them to the /// appropriate indicator using the ColumnId. /// /// The parameter. private void AssignIndicatorParameter(IndicatorParameter parameter) { var matchingIndicators = GetIndicatorLibrary(true).Where(x => x.Id == parameter.ColumnId).ToList(); if (matchingIndicators.Count > 0) matchingIndicators[0].Parameters.Add(parameter); } /// /// Creates an Indicator Parameter object from the XML source. /// /// The source. /// The index. /// private IndicatorParameter GetIndicatorParameter(XmlNode source, int index) { if (null == source) return null; var parameter = new IndicatorParameter(); parameter.Restore(source, index); return parameter; } /// /// Creates a Chart Indicator object from the XML source from the server. /// /// The source. /// The index. /// private TiqColumn GetChartIndicator(XmlNode source, int index) { if (null == source) return null; var indicator = new TiqColumn(); indicator.RestoreFromServer(source, index); return indicator; } /// /// Goes through all received indicators and links together any indicator sets that should be grouped together. /// For example, the top and bottom of a channel or band. /// private void GroupIndicatorSets() { foreach (var indicator in GuiEnvironment.TiqColumns.ToList()) { LinkParentIndicator(indicator); } } /// /// Links indicator to its parent indicator if it has one. This can be applied to the master list /// or the locally stored copies that the user has selected to appear on the chart. When using /// the local list, set makeCopy to true. /// /// The indicator. /// if set to true [make copy]. private void LinkParentIndicator(TiqColumn indicator, bool makeCopy = false) { if (indicator.ParentId.HasValue) { var parentIndicators = GuiEnvironment.TiqColumns.Where(x => x.Id == indicator.ParentId.Value).ToList(); if (parentIndicators.Count > 0) { var parent = parentIndicators[0]; if (makeCopy) parent = parentIndicators[0].DeepCopy(); indicator.Parent = parent; if (!parent.ChildIndicators.Contains(indicator)) parent.ChildIndicators.Add(indicator); } } } /// /// Link this indicator with any child indicators that are associated with it. /// /// The indicator. public void LinkChildIndicators(TiqColumn indicator) { var masterCopies = GuiEnvironment.TiqColumns.Where(x => x.Id == indicator.Id).ToList(); if (masterCopies.Count > 0) { foreach (var childIndicator in masterCopies[0].ChildIndicators.ToList()) { var copy = childIndicator.DeepCopy(); copy.Parent = indicator; indicator.ChildIndicators.Add(copy); } } } /// /// Retrieves the indicator library. /// /// List of available indicators. public List GetIndicatorLibrary(bool chartable) { var toReturn = GuiEnvironment.TiqColumns.Where(x => x.Chartable && x.Category != "DEVELOPMENT").OrderBy(x => x.DisplayName).ToList(); if (GuiEnvironment.DevelopmentMode) toReturn = GuiEnvironment.TiqColumns.Where(x => x.Chartable).OrderBy(x => x.DisplayName).ToList(); if (!chartable) { toReturn = GuiEnvironment.TiqColumns.Where(x => x.Category != "DEVELOPMENT").OrderBy(x => x.DisplayName).ToList(); if (GuiEnvironment.DevelopmentMode) toReturn = GuiEnvironment.TiqColumns.OrderBy(x => x.DisplayName).ToList(); } return toReturn; } /// /// Gets the indicator code. /// /// The indicator. /// public string GetIndicatorCode(TiqColumn indicator) { string toReturn = ""; toReturn += " {{" + indicator.ResolvedReferenceName() + "}"; if (indicator.Code != "") { string indicatorCode = " {{" + indicator.ResolvedCode() + "}}"; toReturn += " " + indicatorCode; } else toReturn += " {{}}"; toReturn += "} "; return toReturn; } } }