Big symbol text in the background

Recently I heard the suggestion to add a security symbol written in big letters in the chart background. Well, actually it is pretty simple to do using low-level gfx. Just add this code sniplet anywhere in your chart formula.

GfxSetOverlayMode(1);
GfxSelectFont("Tahoma"Status("pxheight")/);
GfxSetTextAlign);// center alignment
GfxSetTextColorColorRGB200200200 ) );
GfxSetBkMode(1); // transparent
GfxTextOutName(), Status("pxwidth")/2Status("pxheight")/12 );

UPDATE: I have added transparent mode, so it works fine on non-white backgrounds too.

Getting started with automatic Walk-Forward optimization

Recently released AmiBroker 5.05 BETA features the automatic Walk-Forward Optimization mode.

The automatic Walk forward optimization is a system design and validation technique in which you optimize the parameter values on a past segment of market data (”in-sample”), then test the system forward in time on data following the optimization segment (”out-of-sample”). You evaluate the system based on how well it performs on the test data (”out-of-sample”), not the data it was optimized on.

To use Walk-Forward optimization please follow these steps:

  1. Goto Tools->Automatic Analysis
  2. Click Settings button, then switch to “Walk-Forward tab”
  3. Here you can see Walk forward settings for In-sample optimization, out-of-sample backtest
    “Start” and “End” dates mark initial period begin / end
    This period will be moved forward by “Step” until the “End” reaches the “Last” date.
    The “Start” date can move forward by “step” too, or can be anchored (constant) if “Anchored” check is on.
    If you mark “Use today” then “Last” date entered will be ignored and TODAY (current date) will be used instead

    By default an “EASY MODE” is selected which simplifies the process of setting up WF parameters.
    It assumes that:
    a) Out-of-sample segment immediatelly follows in-sample segment
    b) the length of out-of-sample segment equals to the walk-forward step

    Based on these two assumptions the “EASY” mode takes in-sample END date and sets
    out-of-sample START date to the following day. Then adds in-sample STEP and this becomes out-of-sample END date.
    In-sample and Out-of-sample step values are set to the same values.

    The “EASY” mode guarantees correctness of WF procedure settings.

    In the “ADVANCED” mode, the user has complete control over all values, to the extent that
    they may not constitute valid WF procedure.
    The interface allows to selectivelly disable in-sample and out-of-sample phases using checkboxes at top
    (for special things like runnign sequential backtests without optimization).

    All settings are immediatelly reflected in the PREVIEW list that shows all generated IS/OOS segments and their dates.

    The “Optimization target” field defines the optimization raport COLUMN NAME that
    will be used for sorting results and finding the BEST one. Any built-in column can be used
    (as appears in the optimization output), or you can use any custom metric that you define
    in custom backtester. The default is CAR/MDD, you can however select any other built-in metric from the combo.
    You can also TYPE-IN any custom metric that you have added via custom backtester interface.

  4. Once you defined Walk-Forward settings, please go to Automatic Analysis and
  5. press the dropdown ARROW on the Optimize button and select “Walk Forward Optimization”

This will run sequence of optimizaitons and backtest and the results will be displayed in the “Walk Forward” document that is open in the main application frame.
When optimization is running you can click “MINIMIZE” button on the Progress dialog to minimize it - this allows to see the Walk Forward output during the optimization steps.

IN-SAMPLE and OUT-OF-SAMPLE combined equity

Combined in-sample and out-sample equities are available by
~~~ISEQUITY and ~~~OSEQUITY composite tickers (consecutive periods of IS and OOS are concatenated and scaled to
maintain continuity of equity line - this approach assumes that you generally speaking are compounding profits)
To display IS and OOS equity you may use for example this:

PlotForeign("~~~ISEQUITY","In-Sample Equity"colorRedstyleLine); 
PlotForeign("~~~OSEQUITY","Out-Of-Sample Equity"colorGreenstyleLine); 
Title "{{NAME}} - {{INTERVAL}} {{DATE}} {{VALUES}}"

Low-level gfx example: Yearly/monthly profit chart

The code below is an little bit more complex example of Low Level Graphics functions (see http://www.amibroker.com/guide/a_lowlevelgfx.html)

It allows to display three kinds of charts:

  1. yearly/monthly profit table
  2. yearly profit bar chart
  3. average monthly profit bar chart

The type of chart is switchable from Parameters dialog.

It should be applied to ~~~EQUITY - portfolio equity symbol (so it only produces output if you run backtest before using it).

SetBarsRequired(1000000,1000000);
eq Foreign("~~~EQUITY""C" );

yr Year();
mo Month();

YearChange yr != Refyr, -);
MonChange mo != Refmo, -);

FirstYr 0;
LastYr 0;

startbar 0;

////////////////////////////
// SKIP non-trading bars
////////////////////////////
for( 0BarCounti++ )
{
  if( 
eq] )
  {
    
startbar i;
    break;
  } 
}

////////////////////////////
// collect yearly / monthly changes in equity
// into dynamic variables
////////////////////////////

LastYrValue eqstartbar  ];
LastMoValue eqstartbar  ];

MaxYrProfit MinYrProfit 0;
MaxMoProfit MinMoProfit 0;

for( startbar 1BarCounti++ )
{
  if( 
YearChange] || == BarCount )
  {
    
Chg 100 * ( -eq] / LastYrValue );
    
VarSet("ChgYear"yr], Chg );

    MaxYrProfit MaxMaxYrProfitChg );
    
MinYrProfit MinMinYrProfitChg );

    if( FirstYr == FirstYr yr];
    
LastYr yr];

    LastYrValue eq];
  }

  if( MonChange ] || == BarCount )
  {
    
mon mo];

    Chg 100 * ( -eq] / LastMoValue );

    VarSet("ChgMon" yr] + "-" monChg );
    
VarSet("SumChgMon"monChg NzVarGet("SumChgMon"mon ) ) );
    
VarSet("SumMon" monNzVarGet("SumMon"mon ) ) );
 
    
MaxMoProfit MaxMaxMoProfitChg );
    
MinMoProfit MinMinMoProfitChg );

    LastMoValue eq];
  }
}

/////////////////////////////////////////////////
// Drawing code & helper functions
////////////////////////////////////////////////

GfxSetOverlayMode);

CellHeight = (Status("pxheight")-1)/(LastYr FirstYr ); 
CellWidth = (Status("pxwidth")-1)/14
GfxSelectFont"Tahoma"8.5 ); 

GfxSetBkMode);

function PrintInCellstringrowCol 
{
 
Color =  ColorRGBIIfrow == || col == || col == 13220255 ), 255IIfrow 2255220 ) );
 
GfxSelectSolidBrushColor   );
 
GfxRectangleCol CellWidth
                    
row CellHeight, (Col ) * CellWidth 1
                    (
row ) * CellHeight  1); 
 
GfxDrawTextstringCol CellWidth 1
                    
row CellHeight 1
                    (
Col ) * CellWidth, (row ) * CellHeight32+); 

YOffset 25;
XOffset 15;

function DrawBartextbarnumbarsyMinyMaxy )
{
 
BarWidth = (Status("pxwidth") - XOffset )/( numbars ); 
 
BarHeight Status("pxheight") - YOffset;
 
relpos = ( Miny ) / (Maxy Miny );

 xp XOffset + ( bar 0.5 ) * BarWidth;
 
yp YOffset BarHeight * ( relpos );
 
xe XOffset + ( bar ) * BarWidth;
 
ye YOffset BarHeight * ( - ( -miny )/( maxy miny ) );
  
 if( 
)
 {
 
GfxGradientRectxpyp
                  
xe ye,
                  
ColorHSB70255 relpos255 ), ColorHSB7020255 ) ); 
 }
 else
 {
 
GfxGradientRectxpye
                  
xe yp,
                  
ColorHSB020255 ), ColorHSB0255 * ( relpos ), 255 ) ); 
 }
 
GfxTextOuttextxpye );
 
GfxTextOutStrFormat("%.2f"), xpyp );
}    

function DrawLevelsMinyMaxy )
{
  
range Maxy Miny;

  grid 100;
  if( 
range 10 grid 1;
  else 
  if( 
range 20 grid 2;
  else 
  if( 
range 50 grid 5;
  else 
  if( 
range 100 grid 10;
  else 
  if( 
range 200 grid 20;
  else 
  if( 
range 500 grid 50;

  _TRACE("grid = "+grid +" range "+range );
  
  
width Status("pxwidth") - XOffset;
  
height Status("pxheight") - YOffset;

  GfxSelectPencolorBlack1);
  for( 
grid ceilMiny grid ); <= grid floorMaxy grid ); += grid )
  {
    
yp =  YOffset Height * ( -  ( Miny ) / (Maxy Miny ) );

    GfxMoveToXOffsetyp );
    
GfxLineToXOffset width yp );
    
GfxTextOut""yXOffset widthyp );
  }

  GfxSelectPencolorBlack1);
  
GfxMoveToXOffsetYOffset );
  
GfxLineToXOffset widthYOffset );
  
GfxLineToXOffset widthYOffset Height );
  
GfxLineToXOffset YOffset Height );
  
GfxLineToXOffset YOffset );
}

MonthNames "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec";

function DisplayProfitTable( )
{
 
Header "Year,"+MonthNames+",Yr Profit%";
 for( 
Col 0; (Colname StrExtractHeaderCol ) ) != ""Col++ )
 {
  
PrintInCellColName0Col );
 }

 Row 1;
 for( 
FirstYr<= LastYry++ )
 {
  
PrintInCellStrFormat("%g"), Row); 
  
PrintInCellStrFormat("%.1f%%"VarGet("ChgYear" ) ), Row13 ); 
  for( 
1<= 12m++ )
  { 
   
Chg VarGet("ChgMon" "-" m);
   if( 
Chg 
     
PrintInCellStrFormat("%.1f%%"Chg ), Row);
   else
     
PrintInCell"N/A"Row);
  }
  
Row++;
 } 

 PrintInCell("Mon. Avg"Row);
 for( 
1<= 12m++ )
 { 
   
PrintInCellStrFormat("%.1f%%",  NzVarGet("SumChgMon" m)/VarGet("SumMon" ) ) ), Row);
 }

}

function DisplayYearlyProfits()
{
 
Bar 0;
 for( 
FirstYr<= LastYry++ )
 {
   
Chg VarGet("ChgYear" );
   
DrawBar""+yBar++, ( LastYr FirstYr ), ChgMinYrProfitMaxYrProfit );
 }
 
GfxTextOut("Yearly % Profit chart"1010 );

 DrawLevelsMinYrProfitMaxYrProfit ); 
}

function DisplayMonthlyProfits()
{
 
Bar 0;
 
 
MinAvgProf MaxAvgProf 0;
 for( 
1<= 12y++ )
 {
   
Chg VarGet("SumChgMon" ) / VarGet("SumMon" );
   
MinAvgProf MinMinAvgProfChg );
   
MaxAvgProf MaxMaxAvgProfChg );
 }

 for( 1<= 12y++ )
 {
   
Chg VarGet("SumChgMon" ) / VarGet("SumMon" );
   
DrawBarStrExtract(MonthNamesy-), Bar++, 13ChgMinAvgProf MaxAvgProf );
 }
 
GfxTextOut("Avg. Monthly % Profit chart"1010 );

 DrawLevelsMinAvgProf MaxAvgProf ); 
}

///////////////////////////
// This function checks if currently selected symbol
// is portfolio equity
//////////////////////////
function CheckSymbol()
{
 if( 
Name() != "~~~EQUITY" )
 {
  
GfxSelectFont"Tahoma"20 ); 
  
GfxSetBkMode);
  
GfxTextOut("For accurate results switch to ~~~EQUITY symbol"1010 );
 }
}

////////////////////////////
// Main program - chart type switch
////////////////////////////
type ParamList("Chart Type""Profit Table|Yearly Profits|Avg. Monthly Profits");

switch( type )
{
 case 
"Profit Table"
         
DisplayProfitTable();  
         break;
 case 
"Yearly Profits"
         
DisplayYearlyProfits();
         break;
 case 
"Avg. Monthly Profits"
         
DisplayMonthlyProfits();
         break;
}

CheckSymbol();

Figure 1. Profit chart in table mode

Profit chart example 2

Figure 2. Profit chart in yearly mode

Profit chart example 3

Figure 3. Profit chart in monthly mode

Profit chart example 4

How to detect the divergences

There are many different ways to check for divergences. One of the simplest is to use Rate of change indicator and EXPLORATION feature of Automatic Analysis window:

- Analysis -> Formula Editor
- enter:
 
// 5 day rate of change of close
PriceUp ROCC) > 
// 5 day rate of change of MACD histogram
MacdUP ROCMACD() - Signal(), ) > 0
BullishDiv NOT PriceUP AND MACDUp;
BearishDiv PriceUP AND NOT MACDUp;
Filter BullishDiv OR BearishDiv;
AddColumnBullishDiv"Bullish Divergence"1.0
       
colorDefaultIIf(BullishDivcolorGreencolorDefault ) ); 
AddColumn
BearishDiv "Bearish Divergence"1.0
       
colorDefaultIIf(BearishDiv colorRedcolorDefault) );

- Tools -> Send to Auto-analysis
- Apply to: All Symbols, N last quotations = 1
- press EXPLORE

Tools -> Send to Auto-analysis- Apply to: All Symbols, N last quotations = 1- press EXPLORE

A different approach can use linear regression instead:
 
// 10 day linear regression slope of close
PriceUp LinRegSlopeC10 ) > 
// 10 day linear regression slope of MACD histogram
MacdUP LinRegSlopeMACD() - Signal(), 10 ); 

 

How to detect the study crossover for multiple symbols with use of SCAN

It’s possible to use Automatic Analysis window to search for trendline (or other study) crossovers for multiple symbols at once. It’s necessary to do the following:

1. Draw trendlines on the chart and assidn them a STUDY ID - two letter code that allows to recognise the particular study. To do this, go to study properties (Alt+Enter) after you draw the line (in this example - StudyID = “RE”).

study1.gif

2. Repeat the process for other symbols (remember to draw the trendlines in the same chart pane).

3. Check the CHART ID (in order to call this particular chart pane from the SCAN). To check the ChartID - click on the chart with right mouse button, go to: PARAMETERS -> Axes&Grid (in this example - CHARTID = 1023).

study2.gif

4. Now we can write the formula:
- Analysis -> Formula Editor
- enter:

Buy = Cross( Close, Study(”RE”, 1023) );

(note that we use the same StudyID and ChartID in the formula)
- Tools -> Send to analysis.
- Apply To: All Symbols, All Quotations
- press SCAN

New keywords in AFL and possible conflict with user-defined variables

AmiBroker 4.91.0 BETA introduced the following new keywords:

switch, case, break, continue, default

You have to make sure that your formulas do not use them as variable names. The above words are now reserved AFL keywords and if you use them for your own variables you need to replace this identifiers with names that do not conflict with the reserved keywords.

This article shows how to perform multiple-file text replace very quickly. (more…)

How to chart spreads?

To create a spread chart (and other multi-security indicators / statistics etc.) one can use FOREIGN function which allows to refer to other symbols than currently selected:

It’s necessary to do the following:
- Analysis -> Formula Editor
- enter the formula:

spread Foreign"ticker1""C") - Foreign"ticker2""C");
Plotspread"spread"colorRed); 

- Tools -> Apply Indicator
(replace ticker1, ticker2 with actual symbol names)

How to plot a trailing stop in the Price chart

In this short article we will show how to calculate and plot trailing stop using two different methods. (more…)

How to fill the area between two lines with a solid color

This example shows how to fill the area between Bollinger Bands with a solid color. (more…)

Calendar day index

Someone asked me recently how to count calendar days (not bars) that passed since first available quote. Here is a sample formula that shows how to do that. (more…)

Next Page »