#define Graph_And_Chart_PRO #define CandleChart using ChartAndGraph; using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; using UnityEngine.Events; namespace ChartAndGraph { [ExecuteInEditMode] public abstract class CandleChart : ScrollableAxisChart { /// /// a candle chart event /// [Serializable] public class CandleEvent : UnityEvent { } [SerializeField] [Tooltip("The height ratio of the chart")] protected float heightRatio = 300; [SerializeField] [Tooltip("The width ratio of the chart")] protected float widthRatio = 600; protected override float TotalHeightLink { get { return heightRatio; } } protected override float TotalWidthLink { get { return widthRatio; } } /// /// occures when a point is clicked /// public CandleEvent CandleClicked = new CandleEvent(); /// /// occurs when a point is hovered /// public CandleEvent CandleHovered = new CandleEvent(); /// /// occurs when no candle is hovered any longer /// public UnityEvent NonHovered = new UnityEvent(); public enum CandleThicknessMode { /// /// the candle size is detemined only by the candle duration paramenter /// Fill, /// /// the candle is of constant size , regardless of the view size /// Constant, /// /// the candle size is fixed , but proportional to the view size /// Proportional } /// /// format a candle value to the parameter strings /// /// /// /// /// /// /// public void FormatCandleValue(CandleChartData.CandleValue candleVal, int fractionDigits, out string open, out string high, out string low, out string close) { open = StringFromAxisFormat(new DoubleVector3(candleVal.Start,candleVal.Open,0.0), mVerticalAxis,false); close = StringFromAxisFormat(new DoubleVector3(candleVal.Start, candleVal.Close, 0.0), mVerticalAxis, false); high = StringFromAxisFormat(new DoubleVector3(candleVal.Start, candleVal.High, 0.0), mVerticalAxis, false); low = StringFromAxisFormat(new DoubleVector3(candleVal.Start, candleVal.Low, 0.0), mVerticalAxis, false); } /// /// format a candle value to the parameter strings /// /// /// /// /// public void FormatCandleValue(CandleChartData.CandleValue candleVal, int fractionDigits, out string start, out string duration) { start = StringFromAxisFormat(new DoubleVector3(candleVal.Start, candleVal.Open, 0.0), mHorizontalAxis, true); duration = StringFromAxisFormat(new DoubleVector3(candleVal.Duration, candleVal.Open, 0.0), mHorizontalAxis, true); } /// /// format a candle value to the parameter strings /// /// /// /// /// /// /// /// /// public void FormatCandleValue(CandleChartData.CandleValue candleVal, int fractionDigits, out string open, out string high, out string low, out string close, out string start, out string duration) { FormatCandleValue(candleVal, fractionDigits, out open, out high, out low, out close); FormatCandleValue(candleVal, fractionDigits, out start, out duration); } public class CandleEventArgs { int mType; public CandleEventArgs(int index, int type, Rect selectionRect, Vector3 position, CandleChartData.CandleValue value, string category) { mType = type; Position = position; SelectionRect = selectionRect; CandleValue = value; Category = category; } /// /// true if this event is triggered for the high portion of the candle /// public bool IsHighEvent { get { return mType == 0; } } /// /// true if this event is triggered for the low portion of the candle /// public bool IsLowEvent { get { return mType == 2; } } /// /// true if this event is triggerd for the body portion of the candle (open/close) /// public bool IsBodyEvent { get { return mType == 1; } } /// /// the rect on the canvas that represents the selected object /// public Rect SelectionRect { get; private set; } /// /// mouse position /// public Vector3 Position { get; private set; } /// /// the index of the candle in the data /// public int Index { get; private set; } /// /// the value of the candle /// public CandleChartData.CandleValue CandleValue { get; private set; } /// /// the category of the candle /// public string Category { get; private set; } } [SerializeField] [Tooltip("Thickness mode for the candle chart")] protected CandleThicknessMode thicknessMode = CandleThicknessMode.Constant; /// /// Thickness mode for the candle chart /// public CandleThicknessMode ThicknessMode { get { return thicknessMode; } set { thicknessMode = value; Invalidate(); } } protected override IChartData DataLink { get { return Data; } } [SerializeField] [Tooltip("Thickness contant for the candle chart , it's meaning depends on the thickness mode")] protected float thicknessConstant = 10f; /// /// Thickness contant for the candle chart , it's meaning depends on the thickness mode\ /// Fill - multiply the duration size of the candle by a constant , should be between 0f to 1f /// Constant - the pixel size of the candle /// Proportional - constant size of the candle in seconds /// public float ThicknessConstant { get { return thicknessConstant; } set { thicknessConstant = value; Invalidate(); } } protected override float GetScrollingRange(int axis) { float min = (float)((IInternalCandleData)Data).GetMinValue(axis, false); float max = (float)((IInternalCandleData)Data).GetMaxValue(axis, false); return max - min; } [SerializeField] [Tooltip("format with the following labels: , ,,,,")] private string itemFormat = "O:,H:,L:,C:"; /// /// format with the following labels: /// /// /// /// /// /// /// public string ItemFormat { get { return itemFormat; } set { itemFormat = value; Invalidate(); } } [SerializeField] [Tooltip("Used when using hoverItem component, and the mouse is hovering over the body of the candle,format with the following labels: , ,,,,")] private string bodyFormat = "O:,C:"; /// /// format with the following labels: /// /// /// /// /// /// /// public string BodyFormat { get { return bodyFormat; } set { bodyFormat = value; Invalidate(); } } [SerializeField] [Tooltip("Used when using hoverItem component, and the mouse is hovering over the high line of the candle,format with the following labels: , ,,,,")] private string highFormat = "H:"; public string HighFormat { get { return highFormat; } set { highFormat = value; Invalidate(); } } [SerializeField] [Tooltip("Used when using hoverItem component, and the mouse is hovering over the low line of the candle,format with the following labels: , ,,,,")] private string lowFormat = "L:"; public string LowFormat { get { return lowFormat; } set { lowFormat = value; Invalidate(); } } /// /// the candle chart data /// [HideInInspector] [SerializeField] protected CandleChartData Data = new CandleChartData(); /// /// Holds the candle chart data. including values and categories /// public CandleChartData DataSource { get { return Data; } } protected override void Start() { base.Start(); if (ChartCommon.IsInEditMode == false) { HookEvents(); } Invalidate(); } protected override void OnValidate() { base.OnValidate(); if (ChartCommon.IsInEditMode == false) { HookEvents(); } Data.RestoreDataValues(); Invalidate(); } /// /// hooks data source events. /// protected void HookEvents() { ((IInternalCandleData)Data).InternalDataChanged -= CandleChart_InternalDataChanged; ((IInternalCandleData)Data).InternalDataChanged += CandleChart_InternalDataChanged; ((IInternalCandleData)Data).InternalViewPortionChanged -= CandleChart_InternalViewPortionChanged; ((IInternalCandleData)Data).InternalViewPortionChanged += CandleChart_InternalViewPortionChanged; ((IInternalCandleData)Data).InternalRealTimeDataChanged -= CandleChart_InternalRealTimeDataChanged; ((IInternalCandleData)Data).InternalRealTimeDataChanged += CandleChart_InternalRealTimeDataChanged; } private void CandleChart_InternalViewPortionChanged(object sender, EventArgs e) { InvalidateRealtime(); } private void CandleChart_InternalRealTimeDataChanged(int index,string str) { InvalidateRealtime(); } private void CandleChart_InternalDataChanged(object sender, EventArgs e) { Invalidate(); } protected override void OnLabelSettingChanged() { base.OnLabelSettingChanged(); Invalidate(); } protected override void OnAxisValuesChanged() { base.OnAxisValuesChanged(); Invalidate(); } protected override void OnLabelSettingsSet() { base.OnLabelSettingsSet(); Invalidate(); } protected override LegenedData LegendInfo { get { LegenedData data = new LegenedData(); if (Data == null) return data; foreach (var cat in ((IInternalCandleData)Data).Categories) { for (int i = 0; i < 2; i++) { LegenedData.LegenedItem item = new LegenedData.LegenedItem(); CandleChartData.CandleSettings settings = cat.UpCandle; if (i == 0) { item.Name = cat.Name + " Increasing"; } else { item.Name = cat.Name + " Decreasing"; settings = cat.DownCandle; } if (settings.Fill != null) item.Material = settings.Fill; else { if (settings.Outline != null) item.Material = settings.Outline; else item.Material = settings.Line; } data.AddLegenedItem(item); } } return data; } } protected override bool SupportsCategoryLabels { get { return true; } } protected override bool SupportsGroupLables { get { return false; } } protected override bool SupportsItemLabels { get { return true; } } protected override bool HasValues(AxisBase axis) { if (axis == mVerticalAxis || axis == mHorizontalAxis) // has both horizontal and vertical axis return true; return false; } protected override double MaxValue(AxisBase axis) { if (axis == null) return 0.0; if (axis == mHorizontalAxis) return ((IInternalCandleData)Data).GetMaxValue(0, false); if (axis == mVerticalAxis) return ((IInternalCandleData)Data).GetMaxValue(1, false); return 0.0; } protected override double MinValue(AxisBase axis) { if (axis == null) return 0.0; if (axis == mHorizontalAxis) return ((IInternalCandleData)Data).GetMinValue(0, false); if (axis == mVerticalAxis) return ((IInternalCandleData)Data).GetMinValue(1, false); return 0.0; } void Deflate(ref double start, ref double duration, double amount) { double center = start + duration * 0.5; duration *= amount; start = center - duration * 0.5; } CandleChartData.CandleValue NormalizeCandle(CandleChartData.CandleValue candle, DoubleVector3 min, DoubleVector3 range) { CandleChartData.CandleValue res = new CandleChartData.CandleValue(); res.Open = ChartCommon.normalizeInRangeY(candle.Open, min, range); res.Close = ChartCommon.normalizeInRangeY(candle.Close, min, range); res.High = ChartCommon.normalizeInRangeY(candle.High, min, range); res.Low = ChartCommon.normalizeInRangeY(candle.Low, min, range); double duration = candle.Duration; double start = candle.Start; if (ThicknessMode == CandleThicknessMode.Fill) Deflate(ref start, ref duration, ThicknessConstant); else if (thicknessMode == CandleThicknessMode.Proportional) Deflate(ref start, ref duration, ThicknessConstant / duration); double candleEnd = start + duration; candleEnd = ChartCommon.normalizeInRangeX(candleEnd, min, range); res.Start = ChartCommon.normalizeInRangeX(start, min, range); res.Duration = candleEnd - res.Start; return res; } public CandleChartData.CandleValue InterpolateCandleInRect(CandleChartData.CandleValue candle, Rect viewRect) { CandleChartData.CandleValue res = new CandleChartData.CandleValue(); res.Open = ChartCommon.interpolateInRectY(viewRect, candle.Open); res.Close = ChartCommon.interpolateInRectY(viewRect, candle.Close); res.High = ChartCommon.interpolateInRectY(viewRect, candle.High); res.Low = ChartCommon.interpolateInRectY(viewRect, candle.Low); if (res.High < res.Low) { double tmp = res.High; res.High = res.Low; res.Low = tmp; tmp = res.Open; res.Open = res.Close; res.Close = tmp; } double candleEnd = candle.Start + candle.Duration; candleEnd = ChartCommon.interpolateInRectX(viewRect, candleEnd); double start = ChartCommon.interpolateInRectX(viewRect, candle.Start); double duration = candleEnd - start; if (start > candleEnd) { double tmp = start; start = candleEnd; candleEnd = tmp; } if (ThicknessMode == CandleThicknessMode.Constant) { Deflate(ref start, ref duration, ThicknessConstant / duration); } res.Start = start; res.Duration = duration; return res; } StringBuilder mTmpBuilder = new StringBuilder(); protected string FormatItemWithFormat(string format, string open, string close, string high, string low, string start, string duration) { FormatItemWithFormat(mTmpBuilder, format, open, close, high, low, start, duration); return mTmpBuilder.ToString(); } public string FormatItem(string open, string close, string high, string low, string start, string duration) { return FormatItemWithFormat(itemFormat, open, close, high, low, start, duration); } public string FormatLow(string open, string close, string high, string low, string start, string duration) { return FormatItemWithFormat(lowFormat, open, close, high, low, start, duration); } public string FormatHigh(string open, string close, string high, string low, string start, string duration) { return FormatItemWithFormat(highFormat, open, close, high, low, start, duration); } public string FormatBody(string open, string close, string high, string low, string start, string duration) { return FormatItemWithFormat(bodyFormat, open, close, high, low, start, duration); } protected void FormatItem(StringBuilder builder, string open, string close, string high, string low, string start, string duration) { FormatItemWithFormat(builder, itemFormat, open, close, high, low, start, duration); } protected void FormatItemWithFormat(StringBuilder builder, string format, string open, string close, string high, string low, string start, string duration) { builder.Length = 0; builder.Append(format); builder.Replace("", open).Replace("", close).Replace("", high).Replace("", low).Replace("", start).Replace("", duration); } protected void TransformCandles(IList candles, List output, Rect viewRect, DoubleVector3 min, DoubleVector3 max) { DoubleVector3 range = max - min; if (Math.Abs(range.x) <= 0.0001f || Math.Abs(range.y) < 0.0001f) return; output.Clear(); for (int i = 0; i < candles.Count; i++) { CandleChartData.CandleValue candle = candles[i]; candle = InterpolateCandleInRect(NormalizeCandle(candle, min, range), viewRect); output.Add(candle); } } protected int ClipCandles(IList candles, List result) { result.Clear(); double minX, minY, maxX, maxY, xScroll, yScroll, xSize, ySize, xOut; GetScrollParams(out minX, out minY, out maxX, out maxY, out xScroll, out yScroll, out xSize, out ySize, out xOut); double direction = 1.0; if (minX > maxX) direction = -1.0; bool prevOut = true; int refrenceIndex = 0; for (int i = 0; i < candles.Count; i++) { CandleChartData.CandleValue candle = candles[i]; double candleEnd = candle.Duration + candle.Start; if (candleEnd* direction >= xScroll* direction && candle.Start* direction <= xOut* direction) // if the candle is within range { if (prevOut) { refrenceIndex = i; prevOut = false; } result.Add(candle); } } return refrenceIndex; } protected override void OnNonHoverted() { base.OnNonHoverted(); if (NonHovered != null) NonHovered.Invoke(); } protected override void OnItemSelected(object userData) { base.OnItemSelected(userData); CandleEventArgs args = userData as CandleEventArgs; if (CandleClicked != null) CandleClicked.Invoke(args); } protected override void OnItemHoverted(object userData) { base.OnItemHoverted(userData); CandleEventArgs args = userData as CandleEventArgs; if (CandleHovered != null) CandleHovered.Invoke(args); } } }