//このファイル↓ //OmegaChart Fibonacciリトレースメント Fibonacci.cs 新規追加 ※差し替え その3 //https://osdn.net/users/omegachartuser/pastebin/5775 //との差分追加修正ポイントには//20180731 using System; using System.Collections; using System.Drawing; using System.Text; using Zanetti.Data; using Travis.Storage; namespace Zanetti.UI { /// /// Fibonacci の概要の説明です。 /// internal class Fibonacci { private PointF _pivot; private PointF _destination; private int _id; //SolidFibonacciからの作成時のみセット、それ以外は-1 private LineDrawMode _mode; public enum LineDrawMode { Normal, Hilight } public Fibonacci(PointF p) { _pivot = p; _destination = p; _id = -1; } public Fibonacci(PointF p1, PointF p2) { _pivot = p1; _destination = p2; _id = -1; } //リサイズしたときなどの再計算 public Fibonacci(DataFarm farm, int firstdateindex, SolidFibonacci fl, Trans value_to_y) { int p = Env.Layout.DatePitch; _pivot = new PointF((farm.DateToIndex(fl._date1) - firstdateindex) * p + p / 2, (float)value_to_y.TransValue(fl._value1)); _destination = new PointF((farm.DateToIndex(fl._date2) - firstdateindex) * p + p / 2, (float)value_to_y.TransValue(fl._value2)); _id = fl._id; } public PointF Pivot { get { return _pivot; } set { _pivot = value; } } public PointF Destination { get { return _destination; } set { _destination = value; } } public LineDrawMode DrawMode { get { return _mode; } set { _mode = value; } } public int ID { get { return _id; } } public void SetHighPriceAndLowPrice(DataFarm farm, Trans pricetrans, float x1, float x2, int FirstDateIndex) { if (x1 > Env.Layout.ChartAreaWidth || x2 > Env.Layout.ChartAreaWidth) return; int ind1 = FirstDateIndex + (int)Math.Floor((double)Math.Min(x1, x2) / Env.Layout.DatePitch); int ind2 = FirstDateIndex + (int)Math.Floor((double)Math.Max(x1, x2) / Env.Layout.DatePitch); if (ind1 < 0) ind1 = 0; if (ind2 < 0) ind2 = 0; if (ind1 >= farm.TotalLength) ind1 = farm.FilledLength - 1; if (ind2 >= farm.TotalLength) ind2 = farm.FilledLength - 1; //最高値と最安値の株価 //double range_max = new IndicatorTimeSeries(farm, Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.High), ind1, ind2).Max; //double range_min = new IndicatorTimeSeries(farm, Env.CurrentIndicators.GetPrimitive(PrimitiveIndicator.Low), ind1, ind2).Min; double range_max = Max(farm, ind1, ind2); double range_min = Min(farm, ind1, ind2); //最高値と最安値の画面上の位置 //double top = pricetrans.TransValue(range_max);//20180731 コメントアウト //double bot = pricetrans.TransValue(range_min);//20180731 コメントアウト var y1 = pricetrans.TransValue(_pivot.Y < _destination.Y ? range_max : range_min);//20180731 追加 var y2 = pricetrans.TransValue(_pivot.Y < _destination.Y ? range_min : range_max);//20180731 追加 //_pivot = new PointF(x1, (float)bot);//20180731 コメントアウト //_destination = new PointF(x2, (float)top);//20180731 コメントアウト _pivot = new PointF(x1, (float)y1);//20180731 追加 _destination = new PointF(x2, (float)y2);//20180731 追加 } private double Max(DataFarm farm, int ind1, int ind2) { var pos = ind1; var max = double.MinValue; var last = ind2 > farm.FilledLength ? farm.FilledLength - 1 : ind2; while (pos <= last) { var a = BitConverter.ToInt32(farm.RawDataImage, pos * 32 + 4 * 2) * farm.Brand.PriceScale / (Env.Preference.AdjustSplit ? farm.CalcSplitRatio(BitConverter.ToInt32(farm.RawDataImage, pos * 32)) : 1); if (max < a) max = a; pos++; } return max; } private double Min(DataFarm farm, int ind1, int ind2) { var pos = ind1; var min = double.MaxValue; var last = ind2 > farm.FilledLength ? farm.FilledLength - 1 : ind2; while (pos <= last) { var a = BitConverter.ToInt32(farm.RawDataImage, pos * 32 + 4 * 3) * farm.Brand.PriceScale / (Env.Preference.AdjustSplit ? farm.CalcSplitRatio(BitConverter.ToInt32(farm.RawDataImage, pos * 32)) : 1); if (min > a) min = a; pos++; } return min; } public Rectangle GetInclusion(Rectangle rect) { if (_pivot == _destination) return new Rectangle(new Point((int)_pivot.X, (int)_pivot.Y), new Size(0, 0)); Point[] es = GetEdge(rect, new Point((int)_pivot.X, (int)_pivot.Y), new Point((int)_destination.X, (int)_destination.Y)); return new Rectangle(Math.Min(es[0].X, es[1].X), Math.Min(es[0].Y, es[1].Y), Math.Abs(es[0].X - es[1].X), Math.Abs(es[0].Y - es[1].Y)); } //十分に離した位置でないと線を確定させないようにする public bool PivotHasEnoughDistanceTo(PointF pt) { return Math.Abs(_pivot.X - pt.X) > 10 || Math.Abs(_pivot.Y - pt.Y) > 10; } public void Draw(Trans pricetrans, Rectangle rect, IntPtr hdc) { Win32.POINT pt = new Win32.POINT(); Win32.SelectObject(hdc, _mode == LineDrawMode.Normal ? Env.Preference.FibonacciPen.Handle : Env.Preference.FibonacciBoldPen.Handle); PointF p1 = _pivot; PointF p2 = _destination; var m = Math.Min(p1.X, p2.X); var l = Math.Max(p1.X, p2.X); var y1 = 0F; var y2 = 0F; var y3 = 0F; var y4 = 0F; var y5 = 0F; var k1 = .0; var k2 = .0; var k3 = .0; var k4 = .0; var k5 = .0; if (Env.Preference.LogScale) { k1 = pricetrans.Inverse(Math.Min(p1.Y, p2.Y)); k2 = pricetrans.Inverse(Math.Abs(p1.Y - p2.Y) * 0.382 + Math.Min(p1.Y, p2.Y)); k3 = pricetrans.Inverse(Math.Abs(p1.Y - p2.Y) * 0.500 + Math.Min(p1.Y, p2.Y)); k4 = pricetrans.Inverse(Math.Abs(p1.Y - p2.Y) * 0.618 + Math.Min(p1.Y, p2.Y)); k5 = pricetrans.Inverse(Math.Max(p1.Y, p2.Y)); y1 = Math.Min(p1.Y, p2.Y); y2 = (float)(Math.Abs(p1.Y - p2.Y) * 0.382 + Math.Min(p1.Y, p2.Y)); y3 = (p1.Y + p2.Y) / 2; y4 = (float)(Math.Abs(p1.Y - p2.Y) * 0.618 + Math.Min(p1.Y, p2.Y)); y5 = Math.Max(p1.Y, p2.Y); } else { k1 = pricetrans.Inverse(Math.Min(p1.Y, p2.Y)); k5 = pricetrans.Inverse(Math.Max(p1.Y, p2.Y)); k2 = Math.Abs(k1 - k5) * 0.618 + k5; k3 = (k5 + k1) / 2; k4 = Math.Abs(k1 - k5) * 0.382 + k5; y1 = Math.Min(p1.Y, p2.Y); y2 = (float)pricetrans.TransValue(k2); y3 = (float)pricetrans.TransValue(k3); y4 = (float)pricetrans.TransValue(k4); y5 = Math.Max(p1.Y, p2.Y); } //DrawTheLine(hdc, m, l, y1, k1, out pt);//20180731 コメントアウト //DrawTheLine(hdc, m, l, y2, k2, out pt);//20180731 コメントアウト //DrawTheLine(hdc, m, l, y3, k3, out pt);//20180731 コメントアウト //DrawTheLine(hdc, m, l, y4, k4, out pt);//20180731 コメントアウト //DrawTheLine(hdc, m, l, y5, k5, out pt);//20180731 コメントアウト //20180731 追加 var s = new string[] { "0%", "38.2%", "50%", "61.8%", "100%" }; if(_pivot.Y>_destination.Y) { s = new string[] { "100%", "61.8%", "50%", "38.2%", "0%" }; } DrawTheLine(hdc, m, l, y1, k1, s[0], out pt); DrawTheLine(hdc, m, l, y2, k2, s[1], out pt); DrawTheLine(hdc, m, l, y3, k3, s[2], out pt); DrawTheLine(hdc, m, l, y4, k4, s[3], out pt); DrawTheLine(hdc, m, l, y5, k5, s[4], out pt); //追加ここまで } //20180731 追加 private void DrawTheLine(IntPtr hdc, float px1, float px2, float py, double kabuka, string text, out Win32.POINT pt) { Win32.MoveToEx(hdc, (int)px1, (int)py, out pt); Win32.LineTo(hdc, (int)px2, (int)py); DrawText(hdc, (int)px2, (int)py, kabuka); DrawText(hdc, (int)px1-30, (int)py, text); } private void DrawText(IntPtr hdc, int x, int y, string s) { Win32.SetTextColor(hdc, Util.ToCOLORREF(Color.White)); ChartUtil.DrawText(hdc, x + 3, y - 6, s); } //追加ここまで private void DrawTheLine(IntPtr hdc, float px1, float px2, float py, double kabuka, out Win32.POINT pt) { Win32.MoveToEx(hdc, (int)px1, (int)py, out pt); Win32.LineTo(hdc, (int)px2, (int)py); DrawText(hdc, (int)px2, (int)py, kabuka); } private void DrawText(IntPtr hdc, int x, int y, double d) { Win32.SetTextColor(hdc, Util.ToCOLORREF(Color.White)); ChartUtil.DrawText(hdc, x + 3, y - 6, string.Format("{0:N}", d)); } //p1,p2がrectに入っているとき、p1からp2に伸ばした線とp2からp1に伸ばした線がそれぞれrectと交差するところを返す private static Point[] GetEdge(Rectangle rect, Point p1, Point p2) { Point[] result = new Point[2]; ArrayList ar = GetAllEdges(rect, p1, p2); if (ar.Count == 2) return (Point[])ar.ToArray(typeof(Point)); result[0] = (Point)(p1.X > p2.X ? ar[1] : ar[2]); result[1] = (Point)(p1.X > p2.X ? ar[2] : ar[1]); return result; } //p1とp2を結ぶ線がrectを構成する4直線と交差する点を返す。水平・垂直のときは2個になる private static ArrayList GetAllEdges(Rectangle rect, Point p1, Point p2) { ArrayList ar = new ArrayList(); if (p1.X == p2.X) { ar.Add(new Point(p1.X, p1.Y < p2.Y ? rect.Bottom : rect.Top)); ar.Add(new Point(p1.X, p1.Y > p2.Y ? rect.Bottom : rect.Top)); return ar; } else if (p1.Y == p2.Y) { ar.Add(new Point(p1.X < p2.X ? rect.Right : rect.Left, p1.Y)); ar.Add(new Point(p1.X > p2.X ? rect.Right : rect.Left, p1.Y)); return ar; } LinearTrans lt = LinearTrans.Solve(p1.X, p1.Y, p2.X, p2.Y); ar.Add(new Point(rect.Left, (int)lt.TransValue(rect.Left))); ar.Add(new Point(rect.Right, (int)lt.TransValue(rect.Right))); ar.Add(new Point((int)lt.Inverse(rect.Top), rect.Top)); ar.Add(new Point((int)lt.Inverse(rect.Bottom), rect.Bottom)); //この4点をX座標の順に並べ、2番目と3番目が答え ar.Sort(new PointComparer()); return ar; } private class PointComparer : IComparer { public int Compare(object x, object y) { return ((Point)x).X - ((Point)y).X; } } public SolidFibonacci ToSolid(DataFarm farm, int firstdateindex, Trans value_to_y) { SolidFibonacci fl = new SolidFibonacci(); int ind1 = firstdateindex + (int)Math.Floor((double)_pivot.X / Env.Layout.DatePitch); int ind2 = firstdateindex + (int)Math.Floor((double)_destination.X / Env.Layout.DatePitch); LinearTrans tr = LinearTrans.Solve(ind1, _pivot.Y, ind2, _destination.Y); double y1 = _pivot.Y; if (ind1 >= farm.FilledLength) { ind1 = farm.FilledLength - 1; y1 = tr.TransValue(ind1); } else if (ind1 < 0) { ind1 = 0; y1 = tr.TransValue(ind1); } fl._date1 = farm.GetByIndex(ind1).Date; fl._value1 = value_to_y.Inverse(y1); double y2 = _destination.Y; if (ind2 >= farm.FilledLength) { ind2 = farm.FilledLength - 1; y2 = tr.TransValue(ind2); } else if (ind2 < 0) { ind2 = 0; y2 = tr.TransValue(0); } fl._date2 = farm.GetByIndex(ind2).Date; //fl._value2 = value_to_y.Inverse(y2); fl._value2 = value_to_y.Inverse(_destination.Y); if (_id == -1) { fl._id = SolidFibonacci.NextID++; _id = fl._id; } else { fl._id = _id; } return fl; } //p1,p2を結ぶ直線とpの距離を返す public double GetDistance(Point p) { double[] par = GetNormalizedParam(); return Math.Abs(par[0] * p.X + par[1] * p.Y + par[2]); } //両端を通る直線を ax+by+c=0, a^2+b^2=1 となる形式でa,b,cの配列で返す private double[] GetNormalizedParam() { double a, b, c; if (_pivot.X == _destination.X) { a = 1; b = 0; } else { double d = -(_pivot.Y - _destination.Y) / (double)(_pivot.X - _destination.X); b = Math.Sqrt(1 / (1 + d * d)); a = b * d; } c = -(a * _pivot.X + b * _pivot.Y); return new double[] { a, b, c }; } } internal class SolidFibonacci { public int _id; public int _code; public ChartFormat _targetFormat; public bool _logScale; public int _date1; public double _value1; public int _date2; public double _value2; private static int _nextID; public static int NextID { get { return _nextID; } set { _nextID = value; } } } internal class FibonacciCollection { private ArrayList _data; public FibonacciCollection() { _data = new ArrayList(); } public int Count { get { return _data.Count; } } public void Add(AbstractBrand br, ChartFormat format, bool logScale, SolidFibonacci fl) { fl._code = br.Code; fl._targetFormat = format; fl._logScale = logScale; _data.Add(fl); } public SolidFibonacci[] Find(AbstractBrand br, ChartFormat format, bool logScale) { ArrayList t = new ArrayList(); foreach (SolidFibonacci fl in _data) { if (fl._code == br.Code && fl._targetFormat == format && fl._logScale == logScale) t.Add(fl); } return (SolidFibonacci[])t.ToArray(typeof(SolidFibonacci)); } public void ClearAll() { _data.Clear(); } public void Clear(AbstractBrand br, ChartFormat format, bool logScale) { ArrayList temp = new ArrayList(); IEnumerator ie = _data.GetEnumerator(); while (ie.MoveNext()) { SolidFibonacci fl = (SolidFibonacci)ie.Current; if (!(fl._code == br.Code && fl._targetFormat == format && fl._logScale == logScale)) temp.Add(fl); } _data = temp; } public void Remove(int id) { for (int i = 0; i < _data.Count; i++) { SolidFibonacci l = (SolidFibonacci)_data[i]; if (l._id == id) { _data.RemoveAt(i); break; } } } public void Load(StorageNode parent) { string t = parent["fibonacci-lines"]; if (t == null) return; foreach (string ee in t.Split(',')) { if (ee == null) continue; if (ee.Trim() == string.Empty) continue; SolidFibonacci fl = new SolidFibonacci(); string[] e = ee.Split(':'); fl._code = Int32.Parse(e[0]); switch (e[1]) { case "D": fl._targetFormat = ChartFormat.Daily; fl._logScale = false; break; case "W": fl._targetFormat = ChartFormat.Weekly; fl._logScale = false; break; case "M": fl._targetFormat = ChartFormat.Monthly; fl._logScale = false; break; case "Y": fl._targetFormat = ChartFormat.Yearly; fl._logScale = false; break; case "DL": fl._targetFormat = ChartFormat.Daily; fl._logScale = true; break; case "WL": fl._targetFormat = ChartFormat.Weekly; fl._logScale = true; break; case "ML": fl._targetFormat = ChartFormat.Monthly; fl._logScale = true; break; case "YL": fl._targetFormat = ChartFormat.Yearly; fl._logScale = true; break; } fl._date1 = Int32.Parse(e[2]); fl._value1 = Double.Parse(e[3]); fl._date2 = Int32.Parse(e[4]); fl._value2 = Double.Parse(e[5]); fl._id = SolidFibonacci.NextID++; _data.Add(fl); } } public void SaveTo(StorageNode parent) { StringBuilder bld = new StringBuilder(); foreach (SolidFibonacci sl in _data) { if (bld.Length > 0) bld.Append(","); String format; switch (sl._targetFormat) { case ChartFormat.Daily: default: format = "D"; break; case ChartFormat.Weekly: format = "W"; break; case ChartFormat.Monthly: format = "M"; break; case ChartFormat.Yearly: format = "Y"; break; } if (sl._logScale) { format += "L"; } bld.Append(String.Format("{0}:{1}:{2}:{3:F2}:{4}:{5:F2}", sl._code, format, sl._date1, sl._value1, sl._date2, sl._value2)); } parent["fibonacci-lines"] = bld.ToString(); } } }