CandlePlot.cs

Go to the documentation of this file.
00001 /*
00002 NPlot - A charting library for .NET
00003 
00004 CandlePlot.cs
00005 Copyright (C) 2003
00006 Matt Howlett
00007 
00008 Redistribution and use of NPlot or parts there-of in source and
00009 binary forms, with or without modification, are permitted provided
00010 that the following conditions are met:
00011 
00012 1. Re-distributions in source form must retain at the head of each
00013    source file the above copyright notice, this list of conditions
00014    and the following disclaimer.
00015 
00016 2. Any product ("the product") that makes use NPlot or parts 
00017    there-of must either:
00018   
00019     (a) allow any user of the product to obtain a complete machine-
00020         readable copy of the corresponding source code for the 
00021         product and the version of NPlot used for a charge no more
00022         than your cost of physically performing source distribution,
00023         on a medium customarily used for software interchange, or:
00024 
00025     (b) reproduce the following text in the documentation, about 
00026         box or other materials intended to be read by human users
00027         of the product that is provided to every human user of the
00028         product: 
00029    
00030               "This product includes software developed as 
00031               part of the NPlot library project available 
00032               from: http://www.nplot.com/" 
00033 
00034         The words "This product" may optionally be replace with 
00035         the actual name of the product.
00036 
00037 ------------------------------------------------------------------------
00038 
00039 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00040 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00041 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00042 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00043 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00044 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00045 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00046 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00047 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00048 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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                                         // is the data coming from a data source? 
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                                         // the data is coming from individual arrays.
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                                         // this is inefficient [could set up delegates in constructor].
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                                 // to be pretty sure we get the smallest gap.
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                                 // default
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                         // brant hyatt proposed.
00531                         if (this.Style == Styles.Stick)
00532                         {
00533                                 p.Width = stickWidth;
00534                                 addAmount = stickWidth + 2;
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                                                 // brant hyatt proposed.
00556                                                 if (i > 0) 
00557                                                 {
00558                                                         if ( ((PointOLHC)cd[i]).Close > ((PointOLHC)cd[i-1]).Close)
00559                                                         {
00560                                                                 p.Color = BullishColor;
00561                                                         }
00562                                                         else
00563                                                         {
00564                                                                 p.Color = BearishColor;
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 }

Generated on Sat Nov 5 01:04:06 2005 for NPlot by  doxygen 1.4.5