00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052 using System;
00053 using System.Drawing;
00054 using System.Data;
00055
00056 namespace NPlot
00057 {
00058
00064 public class PointOLHC
00065 {
00066
00075 public PointOLHC( double x, double open, double low, double high, double close )
00076 {
00077 this.x_ = x;
00078 this.open_ = open;
00079 this.close_ = close;
00080 this.low_ = low;
00081 this.high_ = high;
00082 }
00083
00084
00088 public double X
00089 {
00090 get
00091 {
00092 return x_;
00093 }
00094 set
00095 {
00096 this.x_ = value;
00097 }
00098 }
00099 private double x_;
00100
00101
00105 public double Open
00106 {
00107 get
00108 {
00109 return open_;
00110 }
00111 set
00112 {
00113 open_ = value;
00114 }
00115 }
00116 private double open_;
00117
00118
00122 public double Close
00123 {
00124 get
00125 {
00126 return close_;
00127 }
00128 set
00129 {
00130 close_ = value;
00131 }
00132 }
00133 private double close_;
00134
00135
00139 public double Low
00140 {
00141 get
00142 {
00143 return low_;
00144 }
00145 set
00146 {
00147 low_ = value;
00148 }
00149 }
00150 private double low_;
00151
00152
00156 public double High
00157 {
00158 get
00159 {
00160 return high_;
00161 }
00162 set
00163 {
00164 high_ = value;
00165 }
00166 }
00167 private double high_;
00168
00169 }
00170
00171
00175 public class CandlePlot : BasePlot, IPlot
00176 {
00177
00181 public abstract class CandleStyle
00182 {
00188 public abstract CandleStyle Create(CandleDataAdapter d);
00189 }
00190
00194 public class Stick : CandleStyle
00195 {
00196 private Stick() { }
00197
00203 public override CandleStyle Create(CandleDataAdapter d)
00204 {
00205 return new Stick();
00206 }
00207 }
00208
00209
00214 public class CandleDataAdapter
00215 {
00216 private object openData_;
00217 private object lowData_;
00218 private object highData_;
00219 private object closeData_;
00220 private object abscissaData_;
00221
00222 private object dataSource_;
00223 private string dataMember_;
00224 DataRowCollection rows_ = null;
00225
00226
00237 public CandleDataAdapter(
00238 object dataSource, string dataMember, object abscissaData,
00239 object openData, object lowData, object highData, object closeData )
00240 {
00241 this.openData_ = openData;
00242 this.lowData_ = lowData;
00243 this.highData_ = highData;
00244 this.closeData_ = closeData;
00245 this.abscissaData_ = abscissaData;
00246
00247 this.dataSource_ = dataSource;
00248 this.dataMember_ = dataMember;
00249
00250 if (dataSource_ != null)
00251 {
00252 if ( dataSource_ is DataSet )
00253 {
00254 if (dataMember_ != null)
00255 {
00256 rows_ = ((DataTable)((DataSet)dataSource_).Tables[dataMember_]).Rows;
00257 }
00258 else
00259 {
00260 rows_ = ((DataTable)((DataSet)dataSource_).Tables[0]).Rows;
00261 }
00262 }
00263
00264 else if (dataSource_ is DataTable )
00265 {
00266 rows_ = ((DataTable)dataSource_).Rows;
00267 }
00268
00269 else
00270 {
00271 throw new NPlotException ( "not implemented yet" );
00272 }
00273 }
00274 }
00275
00276
00282 public PointOLHC this[int i]
00283 {
00284 get
00285 {
00286
00287 if (rows_ != null)
00288 {
00289 double x = Utils.ToDouble( ((DataRow)(rows_[i]))[(string)abscissaData_] );
00290 double open = Utils.ToDouble( ((DataRow)(rows_[i]))[(string)openData_] );
00291 double low = Utils.ToDouble( ((DataRow)(rows_[i]))[(string)lowData_] );
00292 double high = Utils.ToDouble( ((DataRow)(rows_[i]))[(string)highData_] );
00293 double close = Utils.ToDouble( ((DataRow)(rows_[i]))[(string)closeData_] );
00294
00295 return new PointOLHC( x, open, low, high, close );
00296 }
00297
00298
00299 else if (abscissaData_ is Array && openData_ is Array && lowData_ is Array && highData_ is Array && closeData_ is Array)
00300 {
00301 double x = Utils.ToDouble(((Array)abscissaData_).GetValue(i));
00302 double open = Utils.ToDouble(((Array)openData_).GetValue(i));
00303 double low = Utils.ToDouble(((Array)lowData_).GetValue(i));
00304 double high = Utils.ToDouble(((Array)highData_).GetValue(i));
00305 double close = Utils.ToDouble(((Array)closeData_).GetValue(i));
00306
00307 return new PointOLHC(x, open, low, high, close);
00308 }
00309
00310 else
00311 {
00312 throw new NPlotException("not implemented yet");
00313 }
00314
00315 }
00316 }
00317
00318
00323 public int Count
00324 {
00325 get
00326 {
00327
00328
00329 if (openData_ == null)
00330 {
00331 return 0;
00332 }
00333
00334 if (rows_ != null)
00335 {
00336 return rows_.Count;
00337 }
00338
00339 if (openData_ is Array)
00340 {
00341 int size = ((Array)openData_).Length;
00342 if (size != ((Array)closeData_).Length)
00343 throw new NPlotException("open and close arrays are not of same length");
00344 if (size != ((Array)lowData_).Length)
00345 throw new NPlotException("open and close arrays are not of same length");
00346 if (size != ((Array)highData_).Length)
00347 throw new NPlotException("open and close arrays are not of same length");
00348 return size;
00349 }
00350
00351 throw new NPlotException( "data not in correct format" );
00352 }
00353 }
00354
00355
00360 public Axis SuggestXAxis()
00361 {
00362 double min;
00363 double max;
00364 double minStep = 0.0;
00365
00366 if (this.rows_ == null)
00367 {
00368 Utils.ArrayMinMax((System.Collections.IList)this.abscissaData_, out min, out max);
00369
00370 if (((System.Collections.IList)abscissaData_).Count > 1)
00371 {
00372 double first = Utils.ToDouble(((Array)abscissaData_).GetValue(0));
00373 double second = Utils.ToDouble(((Array)abscissaData_).GetValue(1));
00374 minStep = Math.Abs(second - first);
00375 }
00376 if (((System.Collections.IList)abscissaData_).Count > 2)
00377 {
00378 double first = Utils.ToDouble(((Array)abscissaData_).GetValue(1));
00379 double second = Utils.ToDouble(((Array)abscissaData_).GetValue(2));
00380 if (Math.Abs(second - first) < minStep)
00381 minStep = Math.Abs(second - first);
00382 }
00383 if (((System.Collections.IList)abscissaData_)[0] is DateTime)
00384 {
00385 return new DateTimeAxis(min - minStep / 2.0, max + minStep / 2.0);
00386 }
00387 else
00388 {
00389 return new LinearAxis(min - minStep / 2.0, max + minStep / 2.0);
00390 }
00391 }
00392 else
00393 {
00394
00395 Utils.RowArrayMinMax(this.rows_, out min, out max, (string)this.abscissaData_);
00396
00397 if (rows_.Count > 1)
00398 {
00399 double first = Utils.ToDouble(rows_[0][(string)abscissaData_]);
00400 double second = Utils.ToDouble(rows_[1][(string)abscissaData_]);
00401 minStep = Math.Abs(second - first);
00402 }
00403 if (rows_.Count > 2)
00404 {
00405 double first = Utils.ToDouble(rows_[1][(string)abscissaData_]);
00406 double second = Utils.ToDouble(rows_[2][(string)abscissaData_]);
00407 if (Math.Abs(second - first) < minStep)
00408 minStep = Math.Abs(second - first);
00409 }
00410
00411 if ((rows_[0])[(string)abscissaData_] is DateTime)
00412 {
00413 return new DateTimeAxis(min - minStep / 2.0, max + minStep / 2.0);
00414 }
00415 else
00416 {
00417 return new LinearAxis(min - minStep / 2.0, max + minStep / 2.0);
00418 }
00419 }
00420
00421 }
00422
00423
00428 public Axis SuggestYAxis()
00429 {
00430
00431 double min_l;
00432 double max_l;
00433 double min_h;
00434 double max_h;
00435
00436 if (this.rows_ == null)
00437 {
00438 Utils.ArrayMinMax((System.Collections.IList)lowData_, out min_l, out max_l);
00439 Utils.ArrayMinMax((System.Collections.IList)highData_, out min_h, out max_h);
00440 }
00441 else
00442 {
00443 Utils.RowArrayMinMax(this.rows_, out min_l, out max_l, (string)this.lowData_);
00444 Utils.RowArrayMinMax(this.rows_, out min_h, out max_h, (string)this.highData_);
00445 }
00446
00447 Axis a = new LinearAxis( min_l, max_h );
00448 a.IncreaseRange( 0.08 );
00449 return a;
00450 }
00451
00452 }
00453
00454
00458 public CandlePlot()
00459 {
00460 }
00461
00462
00469 private static float CalculatePhysicalSeparation( CandleDataAdapter cd, PhysicalAxis xAxis )
00470 {
00471 if (cd.Count > 2)
00472 {
00473
00474 int xPos1 = (int)(xAxis.WorldToPhysical( ((PointOLHC)cd[0]).X, false )).X;
00475 int xPos2 = (int)(xAxis.WorldToPhysical( ((PointOLHC)cd[1]).X, false )).X;
00476 int xPos3 = (int)(xAxis.WorldToPhysical( ((PointOLHC)cd[2]).X, false )).X;
00477 int xPos4 = (int)(xAxis.WorldToPhysical( ((PointOLHC)cd[3]).X, false )).X;
00478
00479 int minDist = xPos2 - xPos1;
00480 if ( xPos3 - xPos2 < minDist ) minDist = xPos3 - xPos2;
00481 if ( xPos4 - xPos3 < minDist ) minDist = xPos4 - xPos3;
00482
00483 return minDist;
00484 }
00485
00486 return 0.0f;
00487 }
00488
00489
00496 public void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
00497 {
00498 CandleDataAdapter cd = new CandleDataAdapter( this.DataSource, this.DataMember,
00499 this.AbscissaData, this.OpenData, this.LowData, this.HighData, this.CloseData );
00500
00501 Brush bearishBrush = new SolidBrush( BearishColor );
00502 Brush bullishBrush = new SolidBrush( BullishColor );
00503
00504 uint offset = 0;
00505 if (this.centered_)
00506 {
00507 offset = (uint)(CalculatePhysicalSeparation(cd,xAxis) / 2.0f);
00508 }
00509
00510 uint addAmount = (uint)StickWidth/2;
00511 uint stickWidth = (uint)StickWidth;
00512
00513 if (StickWidth == AutoScaleStickWidth)
00514 {
00515
00516 addAmount = 2;
00517 stickWidth = 4;
00518
00519 float minDist = CalculatePhysicalSeparation( cd, xAxis );
00520 if (minDist != 0.0f)
00521 {
00522 addAmount = (uint)(minDist / 3);
00523 stickWidth = addAmount * 2;
00524 }
00525 }
00526
00527 Pen p = new Pen(this.color_);
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538 for (int i=0; i<cd.Count; ++i)
00539 {
00540
00541 PointOLHC point = (PointOLHC)cd[i];
00542 if ( (!double.IsNaN (point.Open)) && (!double.IsNaN(point.High)) && (!double.IsNaN (point.Low)) && (!double.IsNaN(point.Close)) )
00543 {
00544
00545 int xPos = (int)(xAxis.WorldToPhysical( point.X, false )).X;
00546 int yPos1 = (int)(yAxis.WorldToPhysical( point.Low, false )).Y;
00547 int yPos2 = (int)(yAxis.WorldToPhysical( point.High, false )).Y;
00548 int yPos3 = (int)(yAxis.WorldToPhysical( point.Open, false )).Y;
00549 int yPos4 = (int)(yAxis.WorldToPhysical( point.Close, false )).Y;
00550
00551 if (this.Style == Styles.Stick)
00552 {
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569 g.DrawLine( p, xPos+offset, yPos1, xPos+offset, yPos2 );
00570 g.DrawLine( p, xPos-addAmount+offset, yPos3, xPos+offset, yPos3 );
00571 g.DrawLine( p, xPos+offset, yPos4, xPos+addAmount+offset, yPos4 );
00572 }
00573
00574 else if (this.Style == Styles.Filled)
00575 {
00576 g.DrawLine( p, xPos+offset, yPos1, xPos+offset, yPos2 );
00577 if (yPos3 > yPos4)
00578 {
00579 g.FillRectangle( bullishBrush, xPos-addAmount+offset, yPos4, stickWidth, yPos3 - yPos4 );
00580 g.DrawRectangle( p, xPos-addAmount+offset, yPos4, stickWidth, yPos3 - yPos4 );
00581 }
00582 else if (yPos3 < yPos4)
00583 {
00584 g.FillRectangle( bearishBrush, xPos-addAmount+offset, yPos3, stickWidth, yPos4 - yPos3 );
00585 g.DrawRectangle( p, xPos-addAmount+offset, yPos3, stickWidth, yPos4 - yPos3 );
00586 }
00587 else
00588 {
00589 g.DrawLine( p, xPos-addAmount+offset, yPos3, xPos-addAmount+stickWidth+offset, yPos3 );
00590 }
00591
00592 }
00593
00594 }
00595 }
00596
00597 }
00598
00599
00604 public Axis SuggestXAxis()
00605 {
00606 CandleDataAdapter candleData = new CandleDataAdapter( this.DataSource, this.DataMember,
00607 this.AbscissaData, this.OpenData, this.LowData, this.HighData, this.CloseData );
00608
00609 return candleData.SuggestXAxis();
00610 }
00611
00612
00617 public Axis SuggestYAxis()
00618 {
00619 CandleDataAdapter candleData = new CandleDataAdapter( this.DataSource, this.DataMember,
00620 this.AbscissaData, this.OpenData, this.LowData, this.HighData, this.CloseData );
00621
00622 return candleData.SuggestYAxis();
00623 }
00624
00625
00629 public object OpenData
00630 {
00631 get
00632 {
00633 return openData_;
00634 }
00635 set
00636 {
00637 openData_ = value;
00638 }
00639 }
00640 private object openData_ = null;
00641
00642
00646 public object LowData
00647 {
00648 get
00649 {
00650 return lowData_;
00651 }
00652 set
00653 {
00654 lowData_ = value;
00655 }
00656 }
00657 private object lowData_ = null;
00658
00659
00663 public object HighData
00664 {
00665 get
00666 {
00667 return highData_;
00668 }
00669 set
00670 {
00671 highData_ = value;
00672 }
00673 }
00674 private object highData_ = null;
00675
00676
00680 public object CloseData
00681 {
00682 get
00683 {
00684 return closeData_;
00685 }
00686 set
00687 {
00688 closeData_ = value;
00689 }
00690 }
00691 private object closeData_ = null;
00692
00693
00697 public object AbscissaData
00698 {
00699 get
00700 {
00701 return abscissaData_;
00702 }
00703 set
00704 {
00705 abscissaData_ = value;
00706 }
00707 }
00708 private object abscissaData_ = null;
00709
00710
00716 public virtual void DrawInLegend( Graphics g, Rectangle startEnd )
00717 {
00718 Pen p = new Pen(this.color_);
00719
00720 g.DrawLine( p, startEnd.Left, (startEnd.Top + startEnd.Bottom)/2,
00721 startEnd.Right, (startEnd.Top + startEnd.Bottom)/2 );
00722
00723 }
00724
00725
00731 public System.Drawing.Color Color
00732 {
00733 get
00734 {
00735 return color_;
00736 }
00737 set
00738 {
00739 color_ = value;
00740 }
00741 }
00742 Color color_ = Color.Black;
00743
00744
00748 public enum Styles
00749 {
00750
00754 Stick,
00755
00761 Filled
00762
00763 }
00764
00765
00769 public Styles Style = Styles.Filled;
00770
00771
00775 public Color BullishColor = Color.White;
00776
00777
00781 public Color BearishColor = Color.Black;
00782
00783
00787 public int StickWidth
00788 {
00789 get
00790 {
00791 return stickWidth_;
00792 }
00793 set
00794 {
00795 if (value < 1)
00796 {
00797 throw new NPlotException( "Stick width must be greater than 0." );
00798 }
00799 stickWidth_ = value;
00800 }
00801 }
00802 private int stickWidth_ = AutoScaleStickWidth;
00803
00804
00809 public const int AutoScaleStickWidth = 0;
00810
00817 public bool Centered
00818 {
00819 get
00820 {
00821 return centered_;
00822 }
00823 set
00824 {
00825 centered_ = value;
00826 }
00827 }
00828 private bool centered_ = true;
00829
00830
00838 public void WriteData( System.Text.StringBuilder sb, RectangleD region, bool onlyInRegion )
00839 {
00840 }
00841
00842 }
00843 }