using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Xml; using System.IO; using System.Web; using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; using TradeIdeas.TIProData; using TradeIdeas.XML; namespace TradeIdeas.TIProGUI.CompareCount { public partial class CompareCount : Form, ISaveLayout { private string _fileNameSaved; private readonly ConnectionMaster _connectionMaster; private const int MAXCOUNT = 2000; //At the 5 second interval this is about 2.7 hours. At 120 seconds interval this is about 2.7 days. public CompareCount(ConnectionMaster connectionMaster) { _connectionMaster = connectionMaster; InitializeComponent(); if (!IsHandleCreated) CreateHandle(); WindowIconCache.SetIcon(this); // TODO: Add to Common.xml the title name this.Text = "Compare Count"; _bullishSeries = new ConfigManager(this, connectionMaster); _bearishSeries = new ConfigManager(this, connectionMaster); Settings = Settings.DecentSample(); //Update legend UpdateLegend(); //Allow scrolling and zooming of the x-axis chart1.ChartAreas[0].CursorX.IsUserSelectionEnabled = true; chart1.ChartAreas[0].CursorX.IsUserEnabled = true; // Set scrollbar colors chart1.ChartAreas[0].AxisX.ScrollBar.BackColor = Color.White; chart1.ChartAreas[0].AxisX.ScrollBar.ButtonColor = Color.FromArgb(224, 224, 224); chart1.ChartAreas[0].AxisX.ScrollBar.LineColor = Color.Black; } private Settings _settings; public Settings Settings { get { _settings.BearConfigString = _bearishSeries.Settings; _settings.BullConfigString = _bullishSeries.Settings; return _settings.Clone(); } set { _settings = value.Clone(); _bullishSeries.Settings = _settings.BullConfigString; _bullishSeries.ResetSeries(); _bullishSeries.Add(chart1.Series[0], _settings.FastRecentWeight); _bullishSeries.Add(chart1.Series[1], _settings.SlowRecentWeight); _bearishSeries.Settings = _settings.BearConfigString; _bearishSeries.ResetSeries(); _bearishSeries.Add(chart1.Series[2], _settings.FastRecentWeight); _bearishSeries.Add(chart1.Series[3], _settings.SlowRecentWeight); timer1.Interval = _settings.SecondsPerDataPoint * 1000; } } private ConfigManager _bullishSeries; private ConfigManager _bearishSeries; /// /// Standard cleanup. Stop requesting data! /// /// Unused. /// Unused. private void CompareCount_FormClosed(object sender, FormClosedEventArgs e) { _bullishSeries.Settings = null; _bearishSeries.Settings = null; } private class ConfigManager { private readonly Control _invoker; private readonly ConnectionMaster _connectionMaster; private StreamingAlerts _dataProvider; private int _recentCount; private String _settings; private class SeriesDescription { public Series Series { get; private set; } public double RecentWeight { get; private set; } private double? _previousCount; private bool _recentHasBeenCreated; public SeriesDescription(Series series, double recentWeight) { Series = series; RecentWeight = recentWeight; } public void UpdateLastPoint(int recentCount, bool freeze) { double currentValue; if (_previousCount.HasValue) currentValue = recentCount * RecentWeight + _previousCount.Value * (1 - RecentWeight); else currentValue = recentCount; double [] valueList = { currentValue }; if (_recentHasBeenCreated) { // TODO we are not updating the scale! It should automatically update. /*When we've cleared data points with the clear function, this method UpdateLastPoint will still be called during the timer_Tick event. As a result, there will be a time where the point will be 0, which will cause an exception...*/ if (Series.Points.Count != 0) { Series.Points[Series.Points.Count - 1].YValues = valueList; } else { return; } } else { //Remove oldest data once we reach MAXCOUNT if (Series.Points.Count == MAXCOUNT) Series.Points.RemoveAt(0); Series.Points.Add(valueList); if (Series.Points.Count == 1) // A line chart with only one data point seems to be inivisble. So we switch // to this mode to make it visible. Series.ChartType = SeriesChartType.Point; else Series.ChartType = SeriesChartType.Line; } if (freeze) { // Freeze this data point as is. Start a new data point the next time we get data. _recentHasBeenCreated = false; _previousCount = currentValue; } else // Overwrite this data point next time. _recentHasBeenCreated = true; } } public ConfigManager(Control invoker, ConnectionMaster connectionMaster) { _invoker = invoker; _connectionMaster = connectionMaster; } void dataProvider_StreamingAlertsData(List data, StreamingAlerts source) { if (_invoker.InvokeRequired) _invoker.Invoke((MethodInvoker)delegate { dataProvider_StreamingAlertsData(data, source); }); else if (source == _dataProvider) { int count = 0; foreach (var rowData in data) if (rowData.GetAsString("HISTORICAL") != "1") count++; if (count > 0) { _recentCount += data.Count; foreach (var series in _allSeries) series.UpdateLastPoint(_recentCount, false); } } } public void StartNewDataPoint() { foreach (var series in _allSeries) series.UpdateLastPoint(_recentCount, true); _recentCount = 0; } void dataProvider_StreamingAlertsConfig(StreamingAlerts source) { if (_invoker.InvokeRequired) _invoker.Invoke((MethodInvoker)delegate { dataProvider_StreamingAlertsConfig(source); }); else if (source == _dataProvider) { // This might be a good place to update the legend. _settings = source.Config; } } public string Settings { get { return _settings; } set { _settings = value; if (null != _dataProvider) _dataProvider.Stop(); if (null == value) _dataProvider = null; else { _dataProvider = _connectionMaster.StreamingAlertsManager.GetAlerts(_settings); _dataProvider.StreamingAlertsConfig += new StreamingAlertsConfig(dataProvider_StreamingAlertsConfig); _dataProvider.StreamingAlertsData += new StreamingAlertsData(dataProvider_StreamingAlertsData); _dataProvider.Start(); } } } private readonly List _allSeries = new List(); public void ResetSeries() { _allSeries.Clear(); _recentCount = 0; } private static readonly double[] STARTING_POINT = { 0.0 }; public void Add(Series series, double recentWeight) { System.Diagnostics.Debug.Assert(null != series); _allSeries.Add(new SeriesDescription(series, recentWeight)); series.Points.Clear(); } } private void timer1_Tick(object sender, EventArgs e) { _bullishSeries.StartNewDataPoint(); _bearishSeries.StartNewDataPoint(); } private const string FORM_TYPE = "COMPARE_COUNT_WINDOW"; static public void RegisterLayout() { LayoutManager.Instance().AddRestoreRule(FORM_TYPE, (RestoreLayout)delegate(XmlNode description, bool ignorePosition, bool cascadePosition) { ConnectionMaster connectionMaster = GuiEnvironment.FindConnectionMaster(description.Property("CONNECTION")); if (null == connectionMaster) { // We could report an error here, but it's simpler just to do nothing. Any error message we tried to // report would probably be confusing at best to the user. } else { CompareCount form = new CompareCount(connectionMaster); LayoutManager.RestoreBase(description, form, ignorePosition, cascadePosition); form._settings.Load(description); form.Settings = form._settings; // Update legend form.UpdateLegend(); } }); } public static readonly WindowIconCache WindowIconCache = new WindowIconCache("COMPARE_COUNT"); void ISaveLayout.SaveLayout(XmlNode parent) { XmlNode description = LayoutManager.SaveBase(parent, this, FORM_TYPE); _settings.Save(description); } private void toolStripMenuItemDuplicate_Click(object sender, EventArgs e) { LayoutManager.Instance().Duplicate(this); } private void toolStripMenuItemSaveAs_Click(object sender, EventArgs e) { LayoutManager layoutManager = LayoutManager.Instance(); SaveFileDialog dialog = new SaveFileDialog(); if (null != layoutManager.Directory) dialog.InitialDirectory = layoutManager.Directory; dialog.Filter = GuiEnvironment.XmlConfig.Node("COMPARE_COUNT").Node("PHRASES").Node("WINDOW_FILTER").PropertyForCulture("TEXT", "***"); dialog.DefaultExt = "*.WTI"; string fileName = ""; if (_fileNameSaved == null) { fileName = GuiEnvironment.XmlConfig.Node("COMPARE_COUNT").Node("PHRASES").Node("FILE_PREFIX").PropertyForCulture("TEXT", "***") + "Test"; // +_shortWindowName; } else { fileName = _fileNameSaved; } dialog.FileName = FileNameMethod.QuoteFileName(fileName); if (dialog.ShowDialog() == DialogResult.OK) { layoutManager.SaveOne(this, dialog.FileName); _fileNameSaved = Path.GetFileName(dialog.FileName); } dialog.Dispose(); } private void toolStripMenuItemConfigure_Click(object sender, EventArgs e) { Settings clonedSettings = _settings.Clone(); CompareCountConfig form = new CompareCountConfig(_connectionMaster, clonedSettings); form.ShowDialog(); if (form.DialogResult == System.Windows.Forms.DialogResult.OK) { clearAllPoints(); _settings = clonedSettings; //Update legend. UpdateLegend(); } form.Dispose(); } /// /// Updates the legend by obtaining the window name from the config string. If the name is empty do not show that entry in the legend. /// private void UpdateLegend() { chart1.Series[0].LegendText = getThatWindowName(_settings.BullConfigString); chart1.Series[2].LegendText = getThatWindowName(_settings.BearConfigString); // Hide legend entry of window name is empty chart1.Series[0].IsVisibleInLegend = chart1.Series[0].LegendText != ""; chart1.Series[2].IsVisibleInLegend = chart1.Series[2].LegendText != ""; } /// /// Returns the windowname embedded in the config string. /// /// /// /// The window name as a string. /// private string getThatWindowName(string config) { string[] firstPass = config.Split(new string[] { "&WN=" }, StringSplitOptions.RemoveEmptyEntries); //we'll want segment [1] if (firstPass.Length == 1) { //This scenario happens when the user enters //nothing for the window name (e.g. "" ), thus no //window name identifier would appear in the //the config string. return ""; } string[] lastPass = firstPass[1].Split('&'); return HttpUtility.UrlDecode(lastPass[0]); } WindowIconCache ISaveLayout.WindowIconCache { get { return WindowIconCache; } } private void toolStripMenuItemClear_Click(object sender, EventArgs e) { clearAllPoints(); } private void clearAllPoints() //clears all points for series..restarts again { chart1.Series[0].Points.Clear(); chart1.Series[1].Points.Clear(); chart1.Series[2].Points.Clear(); chart1.Series[3].Points.Clear(); Settings = _settings; } } public class Settings { public String BullConfigString; public String BearConfigString; public int SecondsPerDataPoint; public double FastRecentWeight; public double SlowRecentWeight; // public String BullConfigString { get; set; } /// /// This is mostly aimed at testing, before the config window works. /// public static Settings DecentSample() { Settings result = new Settings(); result.BullConfigString = "O=a000000000000000000000000000001_7dd_0&MinPrice=1&MaxPrice=250&MinVol5D=10000&col_ver=1&show0=D_Type"; //fed to config window when you click on "Config" for Green Data /save layout result.BearConfigString = "O=14000000000000000000000000000002_7dd_0&MinPrice=1&MaxPrice=250&MinVol5D=10000&col_ver=1&show0=D_Type";//fed to config window when you click on "Config" for Red Data /save layout result.SecondsPerDataPoint = 10; result.FastRecentWeight = 0.9; result.SlowRecentWeight = 0.1; return result; } public Settings Clone() { return (Settings)MemberwiseClone(); } /// /// Load saved settings. Use values defined in DecentSample as defaults /// /// public void Load(XmlNode source) { DecentSample(); XmlNode description = source.Node("SETTINGS"); BullConfigString = description.Property("BULLISH", BullConfigString); BearConfigString = description.Property("BEARISH", BearConfigString); try { SecondsPerDataPoint = Convert.ToInt32(description.Property("SECONDS", SecondsPerDataPoint)); FastRecentWeight = Convert.ToDouble(description.Property("FAST_WEIGHT", FastRecentWeight)); SlowRecentWeight = Convert.ToDouble(description.Property("SLOW_WEIGHT", SlowRecentWeight)); } catch { } } /// /// Save all settings /// /// public void Save(XmlNode destination) { XmlNode settings = destination.NewNode("SETTINGS"); settings.SetProperty("BULLISH", BullConfigString); settings.SetProperty("BEARISH", BearConfigString); settings.SetProperty("SECONDS", SecondsPerDataPoint.ToString()); settings.SetProperty("FAST_WEIGHT", FastRecentWeight.ToString()); settings.SetProperty("SLOW_WEIGHT", SlowRecentWeight.ToString()); } } }