Date and Time to Number Conversions

Code developed and kindly donated by Murthy Suresh.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function Date_To_Num( aaaammdd )
{
    dd_ = StrToNum( StrRight( aaaammdd, 2 ) );	//printf(WriteVal(dd_) + " "  );
    mm_ = StrToNum( StrMid( aaaammdd, 4, 2 ) );	//printf(WriteVal(mm_) + " "  );
    aa_ = StrToNum( StrLeft( aaaammdd, 4 ) );	//printf(WriteVal(aa_) + " " + "\n" );
    Date_Num = ( 10000 * ( aa_ - 1900 ) ) + ( 100 * mm_ ) + dd_;
    RESULT = Date_Num;
    return RESULT;
}
 
function Time_To_Num( strTime ) // format for time is hh:mm:ss
{
    /*
    //do something to raise alert  if length does not match
    ????PopupWindow("Current time is: " + Now(),"Alert", 2,
    640*mtRandom(), 480*mtRandom());
    */
    hh_t = StrToNum( StrLeft( strTime, 2 ) );	//printf(WriteVal( hh_t ) + " "  );
    mm_t = StrToNum( StrMid( strTime, 3, 2 ) );	//printf(WriteVal( mm_t ) + " "  );
    ss_t = StrToNum( StrRight( strTime, 2 ) );	//printf(WriteVal( ss_t ) + " "  + "\n"  );
    Time_Num = 10000 * hh_t + 100 * mm_t + ss_t;
    RESULT = Time_Num;
    return RESULT;
}
1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Popup Window: Preventing pile-ups

By Dennis Brown

The popup window is a great tool in debugging and can help you to keep track of what your code is doing or it can be used to alert you to special situations in your normally running formula. A common problem is that if you call PopupWindow() from a loop or if one gets generated on every AFL pass, you can get hundreds or even thousands of pop-ups piling up on your screen. Tomasz posted a simple work-around on the AmiBroker Feedback Center that took care of the problem in some cases (Suggestion 1528):

The following is a more complete version of this solution that adds a popupID to keep track of each individual popup window and re-enable the popup after its specified timeout period, or if any of the displayed text changes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function GetSecondNum()
{
    Time = Now( 4 );
    Seconds = int( Time % 100 );
    Minutes = int( Time / 100 % 100 );
    Hours = int( Time / 10000 % 100 );
    SecondNum = int( Hours * 60 * 60 + Minutes * 60 + Seconds );
    return SecondNum;
}
 
function PopupWindowEx( popupID, bodytext, captiontext, timeout, left, top )
{
    displayText = bodytext + captiontext;
    if ( ( StaticVarGetText( "prevPopup" + popupID ) != displayText) OR ( StaticVarGet( "prevPopupTime" + popupID ) < GetSecondNum() ) )
    {
        StaticVarSetText( "prevPopup" + popupID, displayText);
        StaticVarSet( "prevPopupTime" + popupID, GetSecondNum() + timeout );
        PopupWindow( bodytext, Captiontext + popupID, timeout, Left, top );
    }
}
 
PopupWindowEx( "ID:1", "testing", "test alert ", 5, -1, -1 );
PopupWindowEx( "ID:2", "testing", "test alert ", 5, 0, 0 );
1 Star2 Stars3 Stars4 Stars5 Stars (3 votes, average: 5 out of 5)
Loading ... Loading ...

Using a GFX Include file

Important note: The AmiBroker 5.09.0 Beta introduced the following new GFX functions:

Status(”pxchartleft”) - returns x-coordinate of top-left corner of chart area
Status(”pxcharttop”) - returns y-coordinate of top-left corner of chart area
Status(”pxchartright”) - returns x-coordinate of bottom-right corner of chart area
Status(”pxchartbottom”) - returns y-coordinate of bottom-right corner of chart area
Status(”pxchartwidth”) - returns width chart area (right-left)
Status(”pxchartheight”) - returns width chart area (bottom-top)

Since this release appeared after this post was published these functions are not used in the examples below. This post has been left unchanged for educational purposes. For examples using the new functions please see the 5.09.0 Read Me file.

=====

While the post Creating GFX Chart-Overlays (v2) may have clarified a few of the more important aspects of using GFX functions, it doesn’t really give you a “quick Start” template to get started. Using a GFXInclude file can remove some of the burden of having to define pixel and charting parameters. The Include file at the bottom of this post contains most definitions as well as these common functions that you may want to call from your GFX application:

1
2
3
4
5
GetVisualBarIndex( ); // Returns array containing index for visible bars
gfxPlotHLine( YPixels, Color ); // Plots horizontal line at level YPixels
gfxPlotVLine( XPixels, Color ); // plots vertical line at level XPixels
GetYPixels( Y ); // Convert a vertical price number to the pixel equivalent
GetXPixels( X ); // Convert a horizontal DateTime number value to the pixel equivalent

Of course you can, and should, add additional functions of your own. Here is an example of how to call the above functions to draw a GFX cross-hair cursor (Red in the capture):

gfxcrosshair.jpg

Here is the code that produced the above image:

1
2
3
4
5
6
7
8
9
10
11
12
13
GraphXSpace = 5; // See the AmiBroker help on how to init these variables
GfxSetBkMode( bkmode = 2 );
GfxSetOverlayMode( mode = 0 );
GfxSelectPen( colorRed );
 
Plot( C, "", colorBlack, styleLine ); // To define miny and maxy
#include <GFXInclude-001.afl> // Located in your default Include folder
 
// Example to draw cross-hair cursor
Yprice = GetCursorYPosition(0);
XIndex = SelectedValue(GetVisualBarIndex( ));
gfxPlotHLine( GetYPixels( YPrice ), colorRed );
gfxPlotVLine( GetXPixels( XIndex ), colorRed );

The include file listed below defines the following variables:

1
// pxwidth, pxheight, Miny, MinX, YRange, VisBarIndex, NumBarsVisible, pxPaneWidth, pxPaneheight, PixelsPerBar, PixelsPerPrice

You may want to copy the above comment line below the #include statement in your code to refresh your memory. You should copy the Include file to your default AmiBroker Include folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// GFXInclude-001.afl copy to default include folder
 
function gfxPlotHLine( YPixels, Color )
{
    global pxwidth;
    GfxSelectPen( Color ) ;
    GfxMoveTo( 0, YPixels );
    GfxLineTo( pxwidth, YPixels );
}
 
function gfxPlotVLine( XPixels, Color )
{
    global pxheight;
    GfxSelectPen( Color ) ;
    GfxMoveTo( XPixels, 0 );
    GfxLineTo( XPixels, pxheight );
}
 
function GetVisualBarIndex( )
{
    lvb = Status( "lastvisiblebar" );
    fvb = Status( "firstvisiblebar" );
    bi = BarIndex();
    StaticVarSet( "NumberbarsVisible", Lvb - fvb + 1 );
    return bi - bi[ 0 ] - fvb;
}
 
function GetYPixels( Y )
	{
	global PixelsPerPrice, pxTopArea, MaxY; 
	return (MaxY - Y) * PixelsPerPrice + pxTopArea;
	}
 
function GetXPixels( X )
	{
	global PixelsPerBar, pxLeftArea;
	return X * PixelsPerBar + pxLeftArea;
	}
 
_SECTION_BEGIN("GFX INITIALIZATION");
// These Parameters will change depending on screen layout/fonts
pxRightArea = Param( "Right Axis Area", 93, 0, 200, 1 ); // Depends on font
pxDateArea = Param( "Date Axis Area", 11, 0, 100, 1 ); // Depends on font
DateaxisOn = ParamToggle( "Date Axis", "HIDE|SHOW", 1 );
 
pxLeftArea = 5; 
pxTopArea = 5; 
pxBottomArea = 5; 
if ( DateaxisOn )
{
    pxBottomArea = pxDateArea + pxBottomArea;
    SetChartOptions( 2, chartShowDates );
}
else
    SetChartOptions( 3, chartShowDates );
 
pxwidth = Status( "pxwidth" );
pxheight = Status( "pxheight" );
 
// clalculate charting area width and height
Miny = Status( "axisminy" );
Maxy = Status( "axismaxy" );
YRange = MaxY - MinY;
VisBarIndex =  GetVisualBarIndex( );
NumBarsVisible = StaticVarGet( "NumberbarsVisible" );
 
// Calculate Pane width and height
pxPaneWidth = pxwidth - pxLeftArea - pxRightArea;
pxPaneHeight = pxHeight - pxTopArea - pxBottomArea;
 
// calculate conversion factors
PixelsPerBar 	= pxPaneWidth / NumBarsVisible;
PixelsPerPrice = pxPaneHeight / YRange;
_SECTION_END();

Edited by Al Venosa.

1 Star2 Stars3 Stars4 Stars5 Stars (3 votes, average: 5 out of 5)
Loading ... Loading ...

Creating GFX Chart-Overlays (v3)

Important note: The AmiBroker 5.09.0 Beta introduced the following new GFX functions:

Status(”pxchartleft”) - returns x-coordinate of top-left corner of chart area
Status(”pxcharttop”) - returns y-coordinate of top-left corner of chart area
Status(”pxchartright”) - returns x-coordinate of bottom-right corner of chart area
Status(”pxchartbottom”) - returns y-coordinate of bottom-right corner of chart area
Status(”pxchartwidth”) - returns width chart area (right-left)
Status(”pxchartheight”) - returns width chart area (bottom-top)

Since this release appeared after this post was published these functions are not used in the examples below. This post has been left unchanged for educational purposes. For examples using the new functions please see the 5.09.0 Read Me file.

=====

Creating an exact chart overlay using GFX functions can be a daunting task for the non-professional programmer. The solutions presented here were derived through experimentation; if there is a better way, please make a comment. Once the pixel layout is fully understood, GFX becomes an extremely powerful tool and may just give you an additional trading edge. The first and most important step in using GFX functions is to understand how pixels make up your display. In AmiBroker the width and height of your charting pane can be retrieved using the following two functions:

1
2
pxwidth = Status("pxwidth");
pxheight = Status("pxheight");

Horizontal pixels count left to right, 1 to pxwidth; vertical pixels count top to bottom, 1 to pxheight. The area covered by these two numbers is shown in Yellow below. For a high resolution monitor this area may cover about 2000 (H) x 1000 (V) pixels. This pixel area includes the areas used by the X and Y axis, and the blank top and bottom margins.

pixelarea.png

Next is the standard charting area, which is the area where your price charts are located. This area excludes the surrounding blank margins and the areas used for axis labeling. If you want to keep your overlay within the standard chart boundaries, you have to place your images within the above boundaries. This area is highlighted in Blue in the image below:

chartingarea.png

The boundaries for this area can be determined by running the example code listed in Finding Pixel Boundaries. Eight parameters must be known to create pixel overlays:

1
2
3
4
5
6
7
8
pxwidth = Status( "pxwidth" );
pxheight = Status( "pxheight" );
pxLeftArea = Param( "Left Blank Margin", 5, 0, 100, 1 ); // Constant
pxRightArea = Param( "Right Axis Area", 93, 0, 200, 1 ); // Depends on font
pxTopArea = Param( "Top Blank Margin", 5, 0, 100, 1 ); // Constant
pxDateArea = Param( "Date Axis Area", 11, 0, 100, 1 ); // Depends on font
pxBottomArea = Param( "Bottom Blank Margin", 5, 0, 100, 1 ); // Constant
DateaxisOn = ParamToggle( "Date Axis", "HIDE|SHOW", 1 );

Since adding date labels to your DateTime axis changes the size of your pixel plotting area, you need to compensate for this:

1
2
3
4
5
6
7
8
9
if( DateaxisOn ) 
	{
	pxBottomArea = pxDateArea + pxBottomArea;
	SetChartOptions(2,chartShowDates);
	}
else 
	{
	SetChartOptions(3,chartShowDates);
	}

The pixel plotting area’s width and height can now be calculated:

1
2
pxPaneWidth = pxwidth - pxLeftArea- pxRightArea;
pxPaneHeight = pxHeight - pxTopArea- pxBottomArea;

The dimension of the Blue area shown earlier changes when you resize AmiBroker, open additional windows or panes, change the fonts in your axis, or turn On/Off date labels. When this happens you will have to recalibrate the boundaries. To convert prices to pixels, so that you can create an exact overlay, you also need to define the width and height of your regular chart pane. These are expressed in DateTime and Price units. They will change when you zoom your chart. When you have at least one price plot displayed, so that the values for miny and maxy are defined, you can calculate these boundaries as follows:

1
2
3
4
5
Miny = Status("axisminy");
Maxy = Status("axismaxy");
YRange = MaxY - MinY
BarsVisible = Status("BarVisible");
NumBarsVisible = Cum(BarsVisible);

You now have all the information needed to calculate the Pixels/Price and Pixels/Bar conversion factors:

1
2
PixelsPerBar 	= pxPaneWidth / NumBarsVisible;
PixelsPerPrice = pxPaneHeight / YRange;

Putting it all together in a demo program (listed at the end of this post) produces the price-chart overlay shown below. The regular price plot is plotted using dots, so that the overlay is clearly visible. When you plot both traces in lines, you will see minor deviations that are probably due to rounding to the nearest pixel. The pixel price plot is shown in Red. The purpose of this exercise is to learn to work with pixels and be able to produce an exact overlay on the price chart. The Param window below the charts shows typical parameters; they will likely be different for your screen layout.

pricechartoverlay1.png

pricechartoverlayparam.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
function gfxPlotHLine( YPixels, Color )
{
    global pxwidth;
    GfxSelectPen( Color ) ;
    GfxMoveTo( 0, YPixels );
    GfxLineTo( pxwidth, YPixels );
}
 
function gfxPlotVLine( XPixels, Color )
{
    global pxheight;
    GfxSelectPen( Color ) ;
    GfxMoveTo( XPixels, 0 );
    GfxLineTo( XPixels, pxheight );
}
 
function GetVisualBarIndex( )
{
    lvb = Status( "lastvisiblebar" );
    fvb = Status( "firstvisiblebar" );
    bi = BarIndex();
    StaticVarSet( "NumberbarsVisible", Lvb - fvb + 1 );
    return bi - bi[ 0 ] - fvb;
}
 
function GetYPixels( Y )
	{
	global PixelsPerPrice, pxTopArea, MaxY; 
	return (MaxY - Y) * PixelsPerPrice + pxTopArea;
	}
 
function GetXPixels( X )
	{
	global PixelsPerBar, pxLeftArea;
	return X * PixelsPerBar + pxLeftArea;
	}
 
GraphXSpace = 5;
SetChartOptions( 0, chartHideQuoteMarker );
 
pxwidth = Status( "pxwidth" );
pxheight = Status( "pxheight" );
 
// These Parameters will change depending on screen layout/fonts
pxRightArea = Param( "Right Axis Area", 93, 0, 200, 1 ); // Depends on font
pxDateArea = Param( "Date Axis Area", 11, 0, 100, 1 ); // Depends on font
DateaxisOn = ParamToggle( "Date Axis", "HIDE|SHOW", 1 );
 
// These Parameters appear constant and can probably be hardcoded
pxLeftArea = Param( "Left Blank Margin", 5, 0, 100, 1 ); // Constant
pxTopArea = Param( "Top Blank Margin", 5, 0, 100, 1 ); // Constant
pxBottomArea = Param( "Bottom Blank Margin", 5, 0, 100, 1 ); // Constant
 
if ( DateaxisOn ) // Size of bottom boundary depends on whether dates are shown
{
    pxBottomArea = pxDateArea + pxBottomArea;
    SetChartOptions( 2, chartShowDates );
}
else
    SetChartOptions( 3, chartShowDates );
 
// Test Plots to help line up boundary lines
Color = colorWhite;
gfxPlotVLine( pxLeftArea, color );
gfxPlotVLine( pxwidth - pxRightArea, color );
gfxPlotHLine( pxTopArea, color );
gfxPlotHLine( pxHeight - pxBottomArea, color );
 
// Calculate Pane width and height
pxPaneWidth = pxwidth - pxLeftArea - pxRightArea;
pxPaneHeight = pxHeight - pxTopArea - pxBottomArea;
 
// clalculate charting area width and height
Plot( C, "", 1, styleDots );
Miny = Status( "axisminy" );
Maxy = Status( "axismaxy" );
YRange = MaxY - MinY;
VisBarIndex =  GetVisualBarIndex( );
NumBarsVisible = StaticVarGet( "NumberbarsVisible" );
 
// calculate conversion factors
PixelsPerBar 	= pxPaneWidth / NumBarsVisible;
PixelsPerPrice = pxPaneHeight / YRange;
 
// For verification: Overlay pixel on price plot
FVB = Status( "firstvisiblebar" );
LVB = Status( "lastvisiblebar" );
GfxSelectPen( colorRed );
for ( b = FVB + 1; b <= LVB AND b < BarCount; b++ )
{
    PrevPixelY = GetYPixels( C[b-1] );
    PixelY = GetYPixels( C[b] );
 
    PrevPixelX = GetXPixels( VisBarIndex[b-1] );
    PixelX = GetXPixels( VisBarIndex[b] );
 
    GfxMoveTo( PrevPixelX, PrevPixelY );
    GfxLineTo( PixelX, PixelY );
}

Edited by Al Venosa.

1 Star2 Stars3 Stars4 Stars5 Stars (5 votes, average: 5 out of 5)
Loading ... Loading ...

AFL Shapes Cheat Sheet

The plotshapes() can be used to plot shapes on your chart to indicate signals, stops, and other events and conditions. Figure 1 below gives a quick overview of the shapes that are available and includes a few undocumented ones. A PDF version suitable for printing is here: AFL Shapes Cheat Sheet

afl-shapes.png

Figure 1

Figure 2 shows the small AFL program that was used to explore all the built-in shapes and their numerical values.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for ( i = 0; i < BarCount; i++ )
{
    O[i] = C[i] = i;
    H[i] = i + 5;
    L[i] = i - 5;
 
    if ( i % 2 == 0 )  {PlotText( NumToStr( i, 1.0, False ), i, L[i]-2, colorDarkGrey );}
    else  {PlotText( NumToStr( i, 1.0, False ), i, H[i]+.5, colorDarkGrey );}
}
PlotShapes( C, colorRed, 0, C, -10 );
Plot( C, "", colorLightGrey, styleBar );
 
Title = "Hollow = " + NumToStr( shapeHollowCircle - shapeCircle, 0, 0 ) + "\n" + 
	"Small = " + NumToStr( shapeSmallCircle - shapeCircle, 0, 0 );

aflshapesf2.png

Figure 2

With additions by Herman

1 Star2 Stars3 Stars4 Stars5 Stars (5 votes, average: 4.8 out of 5)
Loading ... Loading ...

Finding Pixel Boundaries

Important note: The AmiBroker 5.09.0 Beta introduced the following new GFX functions:

Status(”pxchartleft”) - returns x-coordinate of top-left corner of chart area
Status(”pxcharttop”) - returns y-coordinate of top-left corner of chart area
Status(”pxchartright”) - returns x-coordinate of bottom-right corner of chart area
Status(”pxchartbottom”) - returns y-coordinate of bottom-right corner of chart area
Status(”pxchartwidth”) - returns width chart area (right-left)
Status(”pxchartheight”) - returns width chart area (bottom-top)

Since this release appeared after this post was published these functions are not used in the examples below. This post has been left unchanged for educational purposes. For examples using the new functions please see the 5.09.0 Read Me file.

=====

The first requirement when designing chart overlays and/or control panels is to know the exact dimensions of your charting pane. These are expressed in pixels. The numerical values where the margins (the four edges of your charting pane) are located depends on the resolution of your monitor, the size of the chart pane, and the fonts used to label your axis.

Currently the only way to obtain the values is, as far as I know, to determine the pane boundaries manually. The code below plots four lines that you can position using sliders in the Param window. When the lines fall exactly on the edges of your charting pane you can read the pixel value in the Param window and in the chart Title. See the image below and note how the blue lines hide the regular black chart frame:

clip_image002

Try changing the size of your charting pane, the overall AmiBroker window, and the font for the axis, to see how the pixel values change. Be aware that opening multiple chart panes and/or windows will also change pixel values. Also, probably to ensure that the first bar shows, the left edge of your charting pane is located at the third pixel, while actual bars start plotting at the fifth pixel. This means that trying to overlay the Blue line to determine the left boundary will be offset to the left by two pixels. To use the code below copy it to a new Indicator formula window and click Apply.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
function gfxPlotHLine( YPixels, Color )
{
    global pxwidth;
    GfxSelectPen( Color ) ;
    GfxMoveTo( 0, YPixels );
    GfxLineTo( pxwidth, YPixels );
}
 
function gfxPlotVLine( XPixels, Color )
{
    global pxheight;
    GfxSelectPen( Color ) ;
    GfxMoveTo( XPixels, 0 );
    GfxLineTo( XPixels, pxheight );
}
 
GraphXSpace = 0;
SetChartOptions( 2, chartHideQuoteMarker );
 
pxwidth = Status( "pxwidth" );
pxheight = Status( "pxheight" );
 
BottomMargin = Param( "Bottom Margin", 14, 0, 100 );
gfxPlotHLine( pxheight - BottomMargin, 6 );
 
RightMargin = Param( "Right Margin", 77, 0, 100 );
gfxPlotVLine( pxwidth - RightMargin, 6 );
 
LeftMargin = Param( "Left margin", 3, 0, 100 );
gfxPlotVLine( LeftMargin, 6 );
 
TopMargin = Param( "Top Margin", 5, 0, 100 );
gfxPlotHLine( TopMargin, 6 );
 
Title="\n"+
"   Pixel width: "+NumToStr(pxwidth,1.0,False)+"\n"+
"  Pixel height: "+NumToStr(pxheight,1.0,False)+"\n"+
"   Top marging: "+NumToStr(TopMargin,1.0,False)+"\n"+
" Right marging: "+NumToStr(RightMargin,1.0,False)+"\n"+
"Bottom marging: "+NumToStr(BottomMargin,1.0,False)+"\n"+
"  Left marging: "+NumToStr(LeftMargin,1.0,False);
1 Star2 Stars3 Stars4 Stars5 Stars (4 votes, average: 4.5 out of 5)
Loading ... Loading ...

Weekly High or Low Days

DRAFT

This code reports the probability of the high or low, for the week, occurring on any given day.

In its current form it is designed for running as an exploration and then exporting the results to a spreadsheet for analysis.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//P_WeeklyHL   
// Day of week numerical descriptor is from 0 == Sunday to 6 == Saturday    
// Identifies which day had the low and the high for the week     
// Export to Excel for counting     
// Note Excel has an upper limit of approx 65000 rows   
Filter = 1;   
WeeklyHigh = IIf(DayOfWeek() == 5, HHV(H,5),0);   
Plot(WeeklyHigh,"HighOfWeek(Value)",1,1);   
HSB = HighestSinceBars(DayOfWeek() == 1,H,1);   
HighOfWeekArray = 5 - HSB;   
HighOfWeek = IIf(DayOfWeek() == 5, HighOfWeekArray, 0);   
Plot(HighOfWeek,"HighOfWeek(Day)",2,1);   
AddColumn(HighOfWeek,"HighDayOfWeek",1,1);   
///////////////////////////////////////////   
//WeeklyLow = IIf(DayOfWeek() == 5, LLV(L,5),0);   
//Plot(WeeklyLow,"LowOfWeek(Value)",1,1);   
//LSB = LowestSinceBars(DayOfWeek() == 1,L,1);   
//LowOfWeekArray = 5 - LSB;   
//LowOfWeek = IIf(DayOfWeek() == 5, LowOfWeekArray, 0);   
//Plot(LowOfWeek,"LowOfWeek(Day)",2,1);   
//AddColumn(LowOfWeek,"LowDayOfWeek",1,1);

It can also be inserted into an indicator pane to allow users to cross-check the code output against expected values.

WHLD001

1 Star2 Stars3 Stars4 Stars5 Stars (3 votes, average: 2.67 out of 5)
Loading ... Loading ...

Moving Low Level Graphics (GFX) Objects on your Charts

When drawing gfx objects on your charts, you can move them around by applying an X and Y offset to the coordinates of the object. The following code shows a simple example of how you can move a square object around your chart.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function DrawButton( Text, x1, y1, x2, y2, BackColor )
{
    GfxSetOverlayMode( 0 );
    GfxSelectFont( "Tahoma", 12, 800 );
    GfxSelectPen( colorBlack );
    GfxSetBkMode( 1 );
    GfxSelectSolidBrush( BackColor );
    GfxSetBkColor( BackColor );
    GfxSetTextColor( 1 );
    GfxRectangle( x1, y1, x2, y2 );
    GfxDrawText( Text, x1, y1, x2, y2, 32 | 1 | 4 );
}
 
ChartPixelheight	= Status( "pxheight" );
ChartPixelWidth	= Status( "pxwidth" );
Yoffset = Param( "Button Row Offset (px)", 50, 0, ChartPixelheight, 5 );
Xoffset	= Param( "Button Column Offset (px)", 50, 0, ChartPixelWidth, 5 );
CellHeight = Param("Cell Height",20,5,200,5);   
CellWidth = Param("Cell Width",120,5,200,5);  
DrawButton( "TEST", Xoffset, yoffset, Xoffset + CellWidth, yOffset + CellHeight, colorBlue );

If you only need to adjust the position of your object once in awhile, this simple method may serve best. However, when you want to move an object on the fly, without having to open the Param window each time, you can do this by registering the coordinates of your first click on the object and then move the object to the location of your next mouse click. The program below shows how to do this. It also shows you how to detect whether your click is within the object area (see the CursorInField variable).

To facilitate readability, the code has not been optimized. You can see in the captures below the code only adds about 4 microseconds to your execution time. It is doubtful that code optimization will have much effect on the overall execution time of your code. To test this code you Apply it to an indicator. At First Apply, it will display the Yellow Square with the word TEST in it:

clip_image002

When you click within the yellow square with your Left mouse button, it will turn Red:

clip_image004

The red color indicates that a move is in progress and that the next click will determine the location to which the object will be moved. After the move is completed, the object will return to its default yellow color:

clip_image006

In a real application, you may want to add some protective code that disables other mouse click activated actions while a move is in progress. Such code would be disabled while the object is Red and the variable MoveInProgress is true.

While the code could be (and initially was) written to move the object by holding down the Left Mouse button and dragging the object in the conventional way, the one-second minimum chart refresh rate makes this extremely awkward and slow to work with. For diagnostic purposes, a Reset button in provided in the Param window. Clicking this button will move the yellow square to its default coordinates.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
function DrawButton( Text, x1, y1, x2, y2, BackColor )
{
    GfxSetOverlayMode( 0 );
    GfxSelectFont( "Tahoma", 12, 800 );
    GfxSelectPen( colorBlack );
    GfxSetBkMode( 1 );
    GfxSelectSolidBrush( BackColor );
    GfxSetBkColor( BackColor );
    GfxSetTextColor( 1 );
    GfxRectangle( x1, y1, x2, y2 );
    GfxDrawText( Text, x1, y1, x2, y2, 32 | 1 | 4 );
}
 
 
ParamSqrSize	= 100;
Reset = ParamTrigger( "Reset Coordinates", "RESET" );
xOffset  = StaticVarGet( "xOffset" );
YOffset  = StaticVarGet( "YOffset" );
 
if ( IsNull( xOffset ) OR IsNull( yOffset ) OR Reset )
{
    StaticVarSet( "xOffset", 20 );
    StaticVarSet( "yOffset", 20 );
    X1 = XOffset;
    Y1 = YOffset;
    X2 = XOffset + ParamSqrSize;
    Y2 = YOffset + ParamSqrSize;
    StaticVarSet( "X1", X1 );
    StaticVarSet( "X2", X2 );
    StaticVarSet( "Y1", Y1 );
    StaticVarSet( "Y2", Y2 );
    StaticVarSet( "MoveinProgress", False );
}
 
X1 = Nz( StaticVarGet( "X1" ) );
 
X2 = Nz( StaticVarGet( "X2" ) );
Y1 = Nz( StaticVarGet( "Y1" ) );
Y2 = Nz( StaticVarGet( "Y2" ) );
LButtonTrigger	= GetCursorMouseButtons() == 9;
MousePx  = Nz( GetCursorXPosition( 1 ) );
MousePy  = Nz( GetCursorYPosition( 1 ) );
 
CursorInField = MousePx > X1 AND MousePx < X2 AND MousePy > Y1 AND MousePy < Y2;
 
MoveInProgress = Nz( StaticVarGet( "MoveInProgress" ) );
 
BackColor = colorYellow;
 
if ( NOT MoveInProgress )
{
    if ( LButtonTrigger AND CursorInField )
    {
        StaticVarSet( "BackColor", colorRed );
        StaticVarSet( "DownPx1", MousePx );
        StaticVarSet( "DownPy1", MousePy );
        StaticVarSet( "MoveinProgress", True );
 
    }
}
else
    if ( LButtonTrigger )
    {
        StaticVarSet( "BackColor", colorYellow );
        DownPx1 = StaticVarGet( "DownPx1" );
        DownPy1 = StaticVarGet( "DownPy1" );
        xMove = MousePx - DownPx1;
        yMove = MousePy - downPy1;
        PrevxOffset = StaticVarGet( "xOffset" );
        PrevYOffset = StaticVarGet( "YOffset" );
        xOffset = PrevxOffset + xMove;
        yOffset = PrevYOffset + yMove;
        StaticVarSet( "xOffset", xOffset );
        StaticVarSet( "yOffset", yOffset );
        StaticVarSet( "MoveinProgress", False );
    }
 
 
BackColor = Nz( StaticVarGet( "BackColor" ), colorYellow );
 
xOffset = StaticVarGet( "xOffset" );
YOffset = StaticVarGet( "YOffset" );
X1 = XOffset;
Y1 = YOffset;
X2 = XOffset + ParamSqrSize;
Y2 = YOffset + ParamSqrSize;
StaticVarSet( "X1", X1 );
StaticVarSet( "X2", X2 );
StaticVarSet( "Y1", Y1 );
StaticVarSet( "Y2", Y2 );
DrawButton( "TEST", x1, y1, x2, y2, BackColor );

Edited by Al Venosa.

1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 5 out of 5)
Loading ... Loading ...

Restore Last Used Range

When starting up AmiBroker the chart range defaults to the last number of bars set in Preferences. This means that in virtually all cases you have to manually zoom and restore the chart range you were previously working with. This post presents a function that will restore the last used range before you shut down AmiBroker. If you like to use this function routinely, you can copy the functions below to your default #Include file and place this call at the top of your code:

1
RestoreLastUsedRange();

This function checks the contents of a static variable named “StartUp”+ChartIDStr. This static variable will return a NULL the first time it is called. When this happens, the ZoomToIndex() is called. This function retrieves the last used range for the current chartID from your hard disk and zooms to this range. The Plot() and Title statements are added to help you verify proper operation. You can also uncomment the two _TRACE() commands and start up DebugView for further diagnostic purposes.

If you do not have it yet, be sure to create a folder for the PersistentVariables, as shown at the top of the code. This is where the First and Last Indexes, which point to the start and end of the range being restored, are saved in a small file. You can open these with Notepad to help debugging operations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
\\Pay attention to the double slashes in the path line. They need to be there.
PersistentPath = "C:\\Program Files\\AmiBroker\\PersistentVariables\\";
 
function PersistentVarGet( VarName )
{
    global PersistentPath;
    fh = fopen( PersistentPath + VarName + ".pva", "r" );
 
    if ( fh )
    {
        String = fgets( fh );
        fclose( fh );
    }
    else
        string = "";
 
    Number = StrToNum( String );
 
    return Number;
}
 
function PersistentVarSet( VarName, Number )
{
    global PersistentPath;
    String = NumToStr( Number, 1.3, False );
    fh = fopen( PersistentPath + VarName + ".pva", "w" );
 
    if ( fh )
    {
        fputs( String, fh );
        fclose( fh );
    }
 
    return fh;
}
 
procedure ZoomToIndex( FirstBarIndex, LastBarIndex )
{
    StaticVarSet( "FirstBarIndex", FirstBarIndex );
    StaticVarSet( "LastBarIndex", LastBarIndex );
    DT = DateTime();
    BI = BarIndex();
    LastDateTime = LastValue( ValueWhen( LastBarIndex == BI, DT ) );
    FirstDateTime = LastValue( ValueWhen( FirstBarIndex == BI, DT ) );
    LastDateTimestr = DateTimeToStr( LastDateTime );
    FirstDateTimestr = DateTimeToStr( FirstDateTime );
    AB = CreateObject( "Broker.Application" );
    AW = AB.ActiveWindow;
    AW.ZoomToRange( FirstDateTimestr, LastDateTimestr );
}
 
function RestoreLastUsedRange()
{
    if ( Status( "Action" ) == 1 )
    {
        ChartIDStr = NumToStr( GetChartID(), 1.0, False );
        PrevFirstBarIndex = PersistentVarGet( "FirstBarIndex" + ChartIDStr );
        PrevLastBarIndex = PersistentVarGet( "LastBarIndex" + ChartIDStr );
 
        if ( IsNull( StaticVarGet( "StartUp" + ChartIDStr ) ) )
        {
            ZoomToIndex( PrevFirstBarIndex, PrevLastBarIndex );
            StaticVarSet( "StartUp" + ChartIDStr, True );
            //_TRACE( "# StartUp: CID: " + ChartIDStr + ", PFBI: " + PrevFirstBarIndex + ", PLBI: " + PrevLastBarIndex );
        }
 
        FirstBarIndex = Status( "firstvisiblebarindex" );
 
        LastBarIndex = Status( "lastvisiblebarindex" );
 
        if ( PrevFirstBarIndex != FirstBarIndex OR PrevLastBarIndex != LastBarIndex )
        {
            PersistentVarSet( "FirstBarIndex" + ChartIDStr, FirstBarIndex );
            PersistentVarSet( "LastBarIndex" + ChartIDStr, LastBarIndex );
            //_TRACE( "# UpdateRange: CID: " + ChartIDStr + ", FBI: " + FirstBarIndex + ", LBI: " + LastBarIndex );
        }
    }
}

///// All code above this line can be placed in an Include file /////

1
2
3
4
5
6
7
Plot( C, "", 1, 128 );
RestoreLastUsedRange();
 
Title = "\n" +
        "ChartIDStr: " + NumToStr( GetChartID(), 1.0, False ) + "\n" +
        "FirstIndex: " + Status( "firstvisiblebarindex" ) + "\n" +
        " LastIndex: " + Status( "Lastvisiblebarindex" );

Edited by Al Venosa.

1 Star2 Stars3 Stars4 Stars5 Stars (4 votes, average: 4.75 out of 5)
Loading ... Loading ...

Deleting Tickers and Composites

Creating composites is a lot of fun and can provide useful information about market and sector movements. However, when creating many composites, you will sooner or later find that your database is bloated with obsolete composites. The first requirement is to create a function to delete composites. Dingo kindly provided the following example code, which will be used to provide a variety of other useful functions. Note that the functions below can be used to delete both tickers and composites; the procedure is the same. The formula is designed to be run as an indicator but can be adapted to run in a scan, etc.

Deleting a list of Tickers or Composites

1
2
3
4
5
6
7
8
9
10
11
12
DeleteTickers = ParamTrigger("Delete Tickers", "Click Here To Delete Tickers"); 
TickerList = ParamStr("Tickers to Delete","ALY,CALM,FCX,GLF,PNCL,RS,SCHN,STLD,WNR,X"); 
if ( DeleteTickers ) 
{ 
oAB = CreateObject( "Broker.Application" ); 
oStocks = oAB.Stocks(); 
for( n=0; (Ticker=StrExtract( TickerList, n))!= ""; n++) 
{ 
oStocks.Remove( Ticker ); 
} 
oAB.RefreshAll(); 
}

When testing this code be sure to use a test database to prevent deleting important tickers. It is always best to develop code that creates and deletes tickers on a test database. To test the above code create a new test database and copy-n-paste the TickerList into a Watchlist using Symbol -> Watchlist -> Type in Symbols. There is no need to backfill the tickers with data. Next open your workspace and display the Watchlist. Next, if you click Click Here To Delete Tickers, you’ll see the ticker disappearing from your Symbol Workspace.

Deleting a Single Ticker or Composite

The above code can easily be trimmed to provide a function to delete a single ticker:

1
2
3
4
5
6
7
8
9
10
11
function DeleteComposite( CompositeName ) 
{ 
oAB = CreateObject( "Broker.Application" ); 
oStocks = oAB.Stocks(); 
oStocks.Remove( CompositeName ); 
oAB.RefreshAll(); 
} 
 
DeleteTicker = ParamTrigger("Delete Ticker", "Click Here To Delete Ticker"); 
Ticker = ParamStr("Ticker to Delete","ALY"); 
if( DeleteTicker ) DeleteComposite( Ticker );

Deleting All Tickers in a Watchlist

A slight modification of the above code will let you delete all tickers in a Watchlist:

1
2
3
4
5
6
7
8
9
10
11
12
13
WatchListNum = Param("Watchlist Number",0,0,64,1); 
DeleteTickers = ParamTrigger("Delete Tickers in Watchlist", "Delete Tickers"); 
TickerList = GetCategorySymbols( categoryWatchlist, WatchListNum ); 
if ( DeleteTickers ) 
{ 
oAB = CreateObject( "Broker.Application" ); 
oStocks = oAB.Stocks(); 
for( n=0; (Ticker=StrExtract( TickerList, n))!= ""; n++) 
{ 
oStocks.Remove( Ticker ); 
} 
oAB.RefreshAll(); 
}

Deleting all Composites in Group 253

Composites are normally stored in Group 253. To delete all composites in this group replace the categorywatchlist with categorygroup:

1
2
3
4
5
6
7
8
9
10
11
12
13
GroupNum = Param("Group Nbr",253,0,255,1); 
DeleteTickers = ParamTrigger("Delete Composites", "Click Here To Delete Composites"); 
TickerList = GetCategorySymbols( categoryGroup, GroupNum ); 
if ( DeleteTickers ) 
{ 
oAB = CreateObject( "Broker.Application" ); 
oStocks = oAB.Stocks(); 
for( n=0; (Ticker=StrExtract( TickerList, n))!= ""; n++) 
{ 
oStocks.Remove( Ticker ); 
} 
oAB.RefreshAll(); 
}

OLE Code written by dingo.

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...
Next Page »