quick C++ STL questions..

Discussion in 'Trading Software' started by EliteInterest, Jan 26, 2006.

  1. cmaxb

    cmaxb

    That's pretty fancy there Elite. Wait till you have to chart and translate all coordinates from upper left (widget origin) to lower left.
     
    #41     Feb 20, 2006
  2. Thanks.

    Qt is amazing. Here are some status shots of the chart component.

    Load chart:
    <img src="http://i2.photobucket.com/albums/y12/e647g9i/1.png" border="0"><br>

    Drag in 'datetime' widget to change horizontal scale:
    <img src="http://i2.photobucket.com/albums/y12/e647g9i/2.png" border="0"><br>

    Drag in 'price' widget to change vertical scale (default is 1 tick spacing on top and bottom):
    <img src="http://i2.photobucket.com/albums/y12/e647g9i/3.png" border="0"><br>

    Dynamic panning by dragging in candle area:
    <img src="http://i2.photobucket.com/albums/y12/e647g9i/4.png" border="0"><br>

    Panning stops after there are no more bars (no more endless dragging back left if you drag too far right):
    <img src="http://i2.photobucket.com/albums/y12/e647g9i/5.png" border="0"><br><img src="http://i2.photobucket.com/albums/y12/e647g9i/6.png" border="0"><br>

    Change font/font size:
    <img src="http://i2.photobucket.com/albums/y12/e647g9i/8.png" border="0"><br>

    Add (non-existent as of yet) studies, unlimited number. Splitter handle can be resized. Will add ability to resize studies by dragging within their widget:
    <img src="http://i2.photobucket.com/albums/y12/e647g9i/9.png" border="0"><br>

    Resize chart, of course. There appears to be a bug in Qt 4.1.1 Windows version with the vertical distribution of the QLines onto the setWindow transform (Trolltech is very responsive to bug reports and will probably have it fixed soon):
    <img src="http://i2.photobucket.com/albums/y12/e647g9i/7.png" border="0"><br>

    No such bug on X11 version (source code is 100.0% identical!):
    <img src="http://i2.photobucket.com/albums/y12/e647g9i/10.png" border="0"><br>

    You can't imagine how much work it was to get it to behave EXACTLY right.. even something as simple as making sure the dynamic horizontal/vertical scaling works smoothly was a pain.... but the graphics performance is blazing fast when panning/scaling/resizing. Qt 4 takes care of double-buffering.. this, among other things, makes coding with Qt a total breeze. I still have to 'pin the position of the far right candle' when the horizontal scaling happens with extra space to the right (for now it just scales proportional to the amount of space, rather than holding the position of the right candle constant).

    It appears I need to look at even vs. odd rectangle widths and how that relates to the wick placements. Should be easy to fix.

    I'm tired now. Later.
     
    #42     Mar 6, 2006
  3. cmaxb

    cmaxb

    That blew me away. I've been trying to come up with something like this for months (I'm geometry challenged). I have it so that the coordinates are obtained with the following transform:

    QWMatrix chartWorldMatrix;
    chartWorldMatrix.rotate( -180 );
    chartWorldMatrix.scale( -1, (double)height/range );
    chartWorldMatrix.translate( 0, -range );

    and then a candle:

    // body
    chartWorldMatrix.map( 0, bodyHigh, &x, &adjBodyHigh);
    chartWorldMatrix.map( 0, bodyLow, &x, adjBodyLow);
    paint.setBrush( someColor );
    paint.drawRect( QRect( QPoint(chartX, lrint(adjBodyHigh)), QPoint(chartX+bodyWidth, lrint(adjBodyLow)) ) );
    // wicks
    xPos = chartX + (candWidth*0.5);
    chartWorldMatrix.map( 0, high, &x, &adjHigh);
    chartWorldMatrix.map( 0, low, &x, &adjLow);
    paint.drawLine( xPos, lrint(adjHigh), xPos, lrint(adjHigher) );
    paint.drawLine( xPos, lrint(adjLower), xPos, lrint(adjLow) );

    Somehow, though, I always get extra vertical space or some clipping. I am using a QScrollView and painting to the viewport(). Some problems I have encountered:

    1) I can't override the scrollbar action to pan the chart a set number of pixels horizontally (e.g. one candle width) at a time.
    2) How does one handle a really large number of data points, construct the adjacent pixmaps in the background and just blit them really quickly when scrolling?
    3) I haven't achieved the effect of vertical rescaling when horizontal panning.

    I have done a lot of programming, just not graphical type applications, and am really hurtin for the experience. Knowing that it can be done gives me a lot of inspiration.
     
    #43     Mar 7, 2006
  4. Just to let you know it's there: [code.] ... [/code.]
     
    #44     Mar 7, 2006
  5. you can't do anything w/this w/out the rest of the code, but maybe you can learn from it. (I don't claim to be any expert but I've been hacking away at the candle widget relentlessly for 24 hours now). things have improved greatly since the charts were posted a few hours ago. there's lots of other critical logic, particularly with the mouse events and how they relate to the indexing of the price data. :) never thought it would get this far.

    The .at(index) vector access methods are fast but pretty dangerous if index is out of range but I am 99% sure I have worked out the handling of spacing, scaling, running off the end of the chart, etc... so tough! :)

    the comments are nicely aligned in my IDE, they look like crap here..

    Code:
    
    void ChartAreaWidget:: paintEvent(QPaintEvent *event)
    {
    	QPainter painter(this);
    	QVector< QRect> redrectangles;
    	QVector< QRect> greenrectangles;
    	QVector< QLine> tails;
    	QVector< QLine> gridlines;
    	candlecount = (width()/spacing);			//# of candles = pixels / spacing
    	redrectangles.reserve(candlecount+1);
    	greenrectangles.reserve(candlecount+1);
    	tails.reserve(candlecount+1);
    	//if (candlecount < 0)						//can't remember why I put this in - width is always > 0, and spacing should always be > 0
    	//	candlecount=0;			
    	candleremainder = width()%spacing;			//the remainder is used to displace the origin of the transform so the candles always are right-justified
    	maxcandle = INT_MIN;						//initialize the max and min values to make comparison easy
    	mincandle = INT_MAX;
    	if (offset < 0)
    		adjustForPanning();
    	if (rightpixelspace - (spacing-1)/2 < 0)							//if necessary, add an extra bar to the left that will be partially displayed 
    		extrabar = 1;
    	else
    		extrabar = 0;
    	startbar = high.size() - candlecount - offset + begincompensate;	//find the first bar - this is used for the transform too
    	startindex = startbar-extrabar;										//extrabar not used by transform
    	if (startindex < 0)													//just in case user pans to far left..
    		startindex = 0;
    	rectanglestart = startbar-spacing*startbar;							//constant in the formula for the bottom left corner of the rectangles
    	if (spacingChanged) {												//wicks weren't centered on spacings such as 2,3,6,7,10,11.. 
    		if ( !((spacing-2)%4) || !((spacing-3)%4) ) {					//(I noticed the sequence and put those tests in to reduce
    			rectanglewidth = spacing/2-1;								//width by 1 pixel in those cases)
    		}
    		else {
    			rectanglewidth = spacing/2;									//otherwise, each rectangle takes up 1/2 of the allocated space
    		}																//candle width should be a user option but this will take some thought to figure out
    		spacingChanged = false;
    	}
    	wickx = rectanglestart+spacing/4;									//constant in the formula for wick x-placement
    	for (int i = startindex; i != high.size()-offset+endcompensate; ++i) {	
    		if (maxcandle < high.at(i))										//find the max and min while we're in here
    			maxcandle = high.at(i);										//if there are problems, use value() instead of at()
    		if (mincandle > low.at(i))
    			mincandle = low.at(i);
    		if (redgreen.at(i))												//start each rectangle at the total offset + i*spacing
    			greenrectangles.push_back(QRect(rectanglestart+i*spacing,openclose_low.at(i),rectanglewidth,(openclose_high.at(i)-openclose_low.at(i))));
    		else
    			redrectangles.push_back(QRect(rectanglestart+i*spacing,openclose_low.at(i),rectanglewidth,(openclose_high.at(i)-openclose_low.at(i))));
    		tails.push_back(QLine(wickx+i*spacing,openclose_high.at(i),wickx+i*spacing,high.at(i)));
    		tails.push_back(QLine(wickx+i*spacing,openclose_low.at(i),wickx+i*spacing,low.at(i)));
    	}
    	maxcandle = maxcandle + 1 + verticalscale;								//add 1 tick to top and bottom by default, and
    	mincandle = mincandle - 1 - verticalscale;	   							//factor in any change in scale by user
    	for (int i = startindex; i != high.size()-offset+endcompensate; ++i) {	
    		if (spacing == 1 && !(minute.at(i)%60)) {
    			gridlines.push_back(QLine(wickx+i*spacing,maxcandle,wickx+i*spacing,mincandle));
    		}
    		else if (spacing == 2 && !(minute.at(i)%30)) {
    			gridlines.push_back(QLine(wickx+i*spacing,maxcandle,wickx+i*spacing,mincandle));
    		}
    		else if (spacing >= 3 && !(minute.at(i)%15)) {
    			gridlines.push_back(QLine(wickx+i*spacing,maxcandle,wickx+i*spacing,mincandle));
    		}
    
    
    	}
    
    	painter.setWindow(QRect(startbar-candleremainder-(spacing-1)/2+rightpixelspace,maxcandle,width(),(mincandle-maxcandle)));  //**THE TRANSFORM**//
    	painter.setPen(gridpen);
    	painter.drawLines(gridlines);
    	painter.setPen(Qt::lightGray);
    	painter.drawLines(tails);
    	painter.setPen(Qt::red);
    	painter.setBrush(Qt::red);
    	painter.drawRects(redrectangles);
    	painter.setPen(Qt::green);
    	painter.setBrush(Qt::green);
    	painter.drawRects(greenrectangles);
    
    }
    
    <img src="http://i2.photobucket.com/albums/y12/e647g9i/11.png" border="0" ><br>

    I noticed that the following pixel spacings produced rectangles that had an even width, and therefore did not center the candle wick correctly:

    2, 3, 6, 7, 10, 11, 14, 15, .....

    So what's the pattern here? Look at this line of code:
    if ( !((spacing-2)%4) || !((spacing-3)%4) )

    I am pretty happy so far with how it is working, it is very close to commercial apps in speed and behavior (but obviously have a long way to go to put it all together).

    The 2nd loop that pushes the gridlines can be made more efficient - it is a waste to look at every datapoint (kind of just threw it together to see some gridlines) - better to have the index increase by 15, 30, 60 dependent on the spacing - save processing time.
     
    #45     Mar 7, 2006
  6. quick changes:

    Code:
    if (spacingChanged) {
      if ( !((spacing-2)%4) || !((spacing-3)%4) ) {
        rectanglewidth = spacing/2-1;
        rectanglewidthcompensate = 1;
      }
      else {
        rectanglewidth = spacing/2;
        rectanglewidthcompensate = 0;
      }
    }
    
    painter.setWindow(QRect(startbar-candleremainder-(spacing-1)/2+rightpixelspace-rectanglewidthcompensate,maxcandle,width(),(mincandle-maxcandle)));
    
    This way, if the rectangle is compensated from even-number-of-pixels width to one pixel less, the transform is displaced one pixel to the right. Now every scale factor from 1 on up shows exactly one pixel space (to be a user variable) at the right edge of the chart area widget.

    Forgot to show the updated CSV Import Wizard.. (not done yet but it does serialize the data into the proprietary 'Chart 0.001a' format using Qt serialization methods - that binary file is used to fill the chart.):

    <img src="http://i2.photobucket.com/albums/y12/e647g9i/12.png" border="0"><br>
     
    #46     Mar 7, 2006
  7. Hi EliteInterest,

    Unfortunately, displays using code blocks are broken in that they do not word wrap. Could you please be aware of this when posting and fold the lines yourself.

    thanks
    kt
     
    #47     Mar 7, 2006
  8. 1 - Since I am using the mouse event for panning (no scrollbar) it's easy to just factor in the scale factor:

    Code:
    void ChartAreaWidget::mousePressEvent(QMouseEvent *event)
    {
    	if ((event->buttons() & Qt::LeftButton)) {
    	mousexpos = (event->pos()).x();
    //store the intial position when first clicked..
    	setCursor(Qt::SizeHorCursor);
    	}
    }
    
    void ChartAreaWidget::mouseMoveEvent(QMouseEvent *event)
    {	
    	if ((event->buttons() & Qt::LeftButton)) {
    		candlecount =  (width()/spacing);
    //set the candle count at that exact moment
    		offset = storedoffset + (event->pos().x()-mousexpos) / spacing;
    //calculate offset based on delta-x for every spacing pixels, plus any offset.
    		if (offset > high.size() -1 || offset < -candlecount)
    //if there is only one bar on the screen..
    			mousexpos = event->pos().x();
    //then hold the position so when the user reverses, it will immediately turn around.
    		adjustForPanning();													
    		update();	
    //now that the offset has moved, repaint the chart.
    	}
    }
    
    void ChartAreaWidget::mouseReleaseEvent(QMouseEvent *event)
    {
    	storedoffset = offset;													//store the offset upon mouse release for further drags - might be unnecessary?
    	setCursor(Qt::ArrowCursor);												//can I just use offset since it persists in this Class Scope?
    }
    
    2. I have spent a good amount of time brainstorming how to make panning/scaling/etc. happen really fast. My code does it in realtime, any time there is a pan, scale, move, redraw, etc. The code is fast enough, with variables on the stack, try to minimize the code behind it. One idea I was kicking around would be to have some type of linked list that shows the highs and lows to the left and right for given number of datapoints, but this would create a huge amount of data. If you could figure out how to compact the data down due to redundancy of adjacent highs/lows overlapping, maybe it would be possible to make the extra data overhead worthwhile. It's pretty fast the way I am doing it, so at this point that is just a dream.

    3. See 2.
     
    #48     Mar 7, 2006
  9. missed it, sorry. 30 minute policy.
     
    #49     Mar 7, 2006
  10. <img src="http://i2.photobucket.com/albums/y12/e647g9i/14.png"</img><br>

    still plenty to do...arghhghgh.. i'm almost done with the price area algorithm (looks good for all font sizes and price configurations) but the griddots and time algorithms both need overhauls. for the price algorithm, i need to fix the displayed significant digits based upon contract type, plus consider using whole-number quantization where applicable - i.e. 704.0, 704.5, etc., instead of how it is showing in the attached pic, although that is kind of a luxury.

    basically i created one study widget, with a 'case-switch' statement to determine which block of code to run in the internal calculations and paint event - i.e. pass 1 into the constructor for volume, 2 for histogram, etc.

    the whole thing is so ripping fast, its unbelievable. never thought Qt would produce such good results.
     
    #50     Mar 11, 2006