/// <summary>Populate the combo of BT radios</summary> private void PopulateRadios() { // Get the available radios var radios = Bluetooth.Radios().ToList(); if (radios.Count != 0) { radios.Insert(0, new Bluetooth.Radio(new SafeFileHandle(IntPtr.Zero, false))); } // Update the combo of radios if (radios.Count == 0) { m_cb_radio.DataSource = new[] { "Bluetooth Disabled" }; Radio = null; } else { // Compare to the existing list so the combo isn't changed unnecessarily var curr = m_cb_radio.DataSource as List <Bluetooth.Radio>; if (curr == null || !curr.SequenceEqual(radios, Cmp <Bluetooth.Radio> .From((l, r) => l.Name.CompareTo(r.Name)))) { m_cb_radio.DataSource = radios; // Select the same radio again var name = Radio?.Name; Radio = radios.FirstOrDefault(x => x.Name == name) ?? radios[0]; } } }
/// <summary>Populate the directory tree</summary> private void SetupTree() { // Sort directories alphabetically m_tree.TreeViewNodeSorter = Cmp <TreeNode> .From((l, r) => string.Compare(l.Name, r.Name)); // Dynamically add sub directory nodes as needed m_tree.NodesNeeded += (s, a) => { if (a.Node.Nodes.Count != 0) { return; } PopulateChildNodes(a.Node); }; // Populate the tree with the drives foreach (var dv in DriveInfo.GetDrives()) { if (!dv.IsReady) { continue; } var drive = dv.Name.TrimEnd('\\'); var node = new TreeNode(drive, TreeNode.ETriState.Unchecked); m_tree.Nodes.Add(node); PopulateChildNodes(node); } // Checking a node adds or removes that path from the list m_tree.AfterCheck += (s, a) => { if (m_updating_check_marks != null) { return; } // Add or remove the path var path = new Path(a.Node.FullPath); if (a.Node.Checked) { Paths.AddOrdered(path, Path.Compare); } else { // If 'path' is in the list, then the ListChanging handler will call HandlePathRemoved // If not in the list, then we need to call it explicitly if (!Paths.Remove(path)) { HandlePathRemoved(path); } } }; }
/// <summary>Populate the list view of bluetooth devices</summary> private void PopulateDevices() { var devices = Bluetooth.Devices(ShowDevices).ToList(); var curr = m_lb_devices.DataSource as List <Bluetooth.Device>; // Update the list of devices if (curr == null || !curr.SequenceEqual(devices, Cmp <Bluetooth.Device> .From((l, r) => l.Name.CompareTo(r.Name)))) { m_lb_devices.DataSource = devices; // Select the same device again var name = Device?.Name; Device = devices.FirstOrDefault(x => x.Name == name); } }
/// <summary>Identify the support and resistance levels</summary> private void CalculateLevels() { SnRLevels.Clear(); for (var loop = 2; loop-- != 0;) { var age_weight = 1.0; var last_bucket = -1; // Sort the stationary points into their nearest bucket foreach (var sp in StationaryPoints) { if (!sp.Price.Within(Price - Range, Price + Range)) { continue; } // Find the nearest level to 'sp' var idx = SnRLevels.BinarySearch(x => x.Price.CompareTo(sp.Price), find_insert_position: true); // Require the price to move away from the last bucket to ensure // periods of consolidation don't artificially strengthen an SnR level. if (idx == last_bucket) { continue; } last_bucket = idx; // Find the closest bucket // If the nearest bucket is further than the bucket size, insert a bucket var d0 = idx > 0 ? (double)(sp.Price - SnRLevels[idx - 1].Price) : BucketSize; var d1 = idx < SnRLevels.Count ? (double)(SnRLevels[idx].Price - sp.Price) : BucketSize; if (Math.Min(d0, d1) >= BucketSize) { SnRLevels.Insert(idx, new Level(sp.Price)); } else { idx = d0 < d1 ? idx - 1 : idx; } // Add 'sp' to the average for the bucket SnRLevels[idx].Average.Add(sp.Price); SnRLevels[idx].Strength = age_weight; age_weight *= 0.95; } // Adjust the prices to the mean for each bucket and remove empty buckets var done = true; var pip = Instrument.Symbol.PipSize; SnRLevels.RemoveIf(x => x.Average.Count == 0); foreach (var lvl in SnRLevels) { done &= Math.Abs(lvl.Price - lvl.Average.Mean) < pip; lvl.Price = lvl.Average.Mean; lvl.Strength *= lvl.Average.Count; lvl.Average.Reset(); } // All means are roughly unmoved if (done) { break; } } // Determine strength values for each SnR level if (SnRLevels.Count != 0) { // Level strength depends on the number of stationary points used to form the level // and on how recently the level was respected. Not the number of points relative to other levels. const int StrongCount = 2; foreach (var lvl in SnRLevels) { lvl.Strength = 0.5 * Maths.Sigmoid(lvl.Strength - StrongCount, StrongCount) + 0.5; } } var digits = Instrument.Symbol.Digits; // Round levels that are near 00 levels foreach (var lvl in SnRLevels) { var price00 = Math.Round(lvl.Price, digits - 3); var price0 = Math.Round(lvl.Price, digits - 2); if (Math.Abs(price00 - lvl.Price) < 2 * BucketSize) { lvl.Price = price00; } else if (Math.Abs(price0 - lvl.Price) < BucketSize) { lvl.Price = price0; } } // Insert levels for the 000 levels var pbeg = Math.Round(Price - Range, digits - 4); var pend = Math.Round(Price + Range, digits - 4); var pstep = Instrument.PipSize * 1000; for (var p = pbeg; p <= pend; p += pstep) { if (p < Price - Range || p > Price + Range) { continue; } var idx = SnRLevels.BinarySearch(x => x.Price.CompareTo(p), find_insert_position: true); if ((idx < SnRLevels.Count && !Maths.FEql(p, SnRLevels[idx].Price)) || (idx > 0 && !Maths.FEql(p, SnRLevels[idx - 1].Price)) || (idx == SnRLevels.Count)) { SnRLevels.Insert(idx, new Level(p) { Strength = 0.8 }); } } // Sort the levels by strength SnRLevels.Sort(Cmp <Level> .From((l, r) => - l.Strength.CompareTo(r.Strength))); }
/// <summary>Returns true if adjacent items in the collection satisfy 'pred(x[i], x[i+1])'</summary> public static bool IsOrdered <TSource>(this IEnumerable <TSource> source, Func <TSource, int> pred) { return(IsOrdered(source, ESequenceOrder.Increasing, Cmp <TSource> .From(pred))); }
/// <summary>Sort 'list' into a KD Tree</summary> /// <param name="list">The list to be sorted into a KD tree</param> /// <param name="dimensions">The number of dimensions to sort on (typically 2 or 3)</param> /// <param name="AxisValueGet">Callback that returns the value of the given element on the given dimension</param> /// <param name="SortAxisSet">Callback that sets the axis to sort on for the given element</param> public static void Build <T>(IList <T> list, int dimensions, Func <T, int, double> AxisValueGet, Func <T, int, T> SortAxisSet) { BuildTree(0, list.Count); // Helpers void BuildTree(int beg, int end) { if (end - beg <= 1) { return; } // Partition the range at the mid point of the longest axis var split_axis = LongestAxis(beg, end); var split_point = MedianSplit(beg, end, split_axis); // SortAxisSet must return the updated element because it may be a struct. list[split_point] = SortAxisSet(list[split_point], split_axis); // Recursively build each half of the remaining data BuildTree(beg, split_point); BuildTree(split_point + 1, end); } int LongestAxis(int beg, int end) { // Found the bounds on each axes of the range [beg, end) var lower = Array_.New(dimensions, i => double.MaxValue); var upper = Array_.New(dimensions, i => double.MinValue); for (var i = beg; i != end; ++i) { for (var a = 0; a != dimensions; ++a) { var value = AxisValueGet(list[i], a); lower[a] = Math.Min(lower[a], value); upper[a] = Math.Max(upper[a], value); } } // Return the axis with the greatest range var largest = 0; for (var a = 1; a != dimensions; ++a) { if (upper[a] - lower[a] < upper[largest] - lower[largest]) { continue; } largest = a; } return(largest); } int MedianSplit(int first, int last, int split_axis) { // Ensure that the element at the centre of the range has only values less than it on // the left and values greater or equal than it on the right, where the values are the // component of the axis to split on. var split_point = first + (last - first) / 2; var cmp = Cmp <T> .From((lhs, rhs) => AxisValueGet(lhs, split_axis) < AxisValueGet(rhs, split_axis)? -1 : 1); list.NthElement(split_point, first, last, cmp); return(split_point); } }