TRADING SYSTEMS 2nd ed- book and video course by Urban Jaekle

Hi all

There are a few posts on this forum about Urban Jaekle's new book and course - both of which I have started on.

I thought we could use this thread to test some of the strategies and to discuss the AFL code and system development methodology he uses - the code is free on his site:

His Bollinger Band strategy for stocks is one of the few published BB coding examples for which I have replicated the backtest with reasonable accuracy, thanks to disclosure of the full code and since I use the same data source (Norgate NDU). The general BB system logic is familiar to many but I haven't been able to replicate the strategy results consistently before.

I am working through the video course and Beginning of Month strategy next.

I hope that Urban and keen students of his book or course will be able to interact here to develop a better understanding of the strategies, AFL coding, and the system development process.


1 Like

Hi Jay, many thanks!

The first important point to reproduce the results of the Bollinger system on a portfolio of stocks is to set the Norgate database settings to default:

Norgate Database Settings

(I had a discussion with another reader who had different results on the S&P500 current and past universe because he had different settings).

Any further questions feel free to ask- I will check from time to time here.


Thanks Urban!
I also noticed in the BB code comments that in the Analysis Settings ('General' tab) I should select 'pad and align' to the relevant reference symbols (eg. $SPX).

In order to follow the study of the chapters that work with the LUXOR system, tests are carried out with the GBPUSD pair.
I have a subscription with NORGATE, but it does not provide intraday data.
Any suggestion to get the GBPUSD 30 minutes history to be able to do the analysis simultaneously with the study of these chapters?
Thank you very much.

@svaztej, if you don't mind registering (no need to open an account) you can download for free multiple years of Forex 1-minute quotations from Dukascopy (using a maximum of 3 years of 1M data per batch).
Unfortunately their ASCII data is not in a format immediately importable using AmiBroker Import Wizard.

Their .csv layout is as follow:

Local time,Open,High,Low,Close,Volume
03.01.2011 00:00:00.000 GMT+0100,1.55334,1.55501,1.55334,1.55501,116.46
03.01.2011 00:01:00.000 GMT+0100,1.55501,1.55561,1.55501,1.55549,66.87
03.01.2011 00:02:00.000 GMT+0100,1.55549,1.55554,1.55544,1.55544,41.87
03.01.2011 00:03:00.000 GMT+0100,1.55544,1.55554,1.55484,1.55494,61.48
03.01.2011 00:04:00.000 GMT+0100,1.55486,1.55545,1.55486,1.55543,53.82
03.01.2011 00:05:00.000 GMT+0100,1.55548,1.55548,1.55531,1.55546,8.95

You will need to process this data to convert it to a format compatible with the ASCII importer.
For example, for this kind of task, I will use an R script exploiting the functionality of the "tidyverse" to convert the "Local time" to UTC, taking into account the time zone offset, separating the result into Date and Time fields, and multiply the volume * 1000000).

It would be nice if some future version of the ASCII importer will automatically detect additional kinds of timestamps, but for now, you need some extra preparatory work!

1 Like

Perfect. That's what I needed. I can process the data without any problem to feed the database.
If I encounter any difficulties I will let you know.
Thank you.

Your statement is incorrect.

Simply use separator comma or space and skip 3rd column.

Also there exists VolFactor command for ASCII Importer.



BTW, DC exporter has GMT button already?


It seems as if there is only data since 2003 and unfortunately the analysis in the book is from October 21, 2002 to July 4, 2008.
I am still looking for more sources...
Thank you very much for your help.

As the book does not contain the Luxor code in AFL, I share it with the adaptations that I have been doing as I have been studying, in case it is useful for anyone who requires it.
Any comments or corrections are appreciated.

// Downloaded From 
// Adapted by Santiago Vázquez
// including example code FX margin account by

SetChartOptions( 0, chartShowDates );

SetOption( "FuturesMode", True );
SetOption( "Allowsamebarexit", True );
SetOption( "ReverseSignalForcesExit", False );
SetOption( "PriceBoundChecking", True );

SetOption( "ExtraColumnsLocation", 12 );// defines location of CBT columns in AA

report = ParamToggle("Report", "no, yes", 0);
SetOption( "GenerateReport", report ); // force generation of full report true/false, slower if set to true
SetOption( "PortfolioReportMode", 0 );// 0-Trade list, 1- Detailed Log, 2- Summary, 3- No output
//Reports, (deactivate reports section during optimization,
//if you don't wanna have generated reports for every optimization process because it slows it down quite a bit!)

SetBacktestMode( backtestRegular );//

// ....
// add other setoptions according to your system requirements ...
// or go to "Backtester settings" of analysis window
// in general: setoptions of code level override settings of "Backtester settings" window
// don't forget about the fact that you can save an analysis project file (.apx) that saves your AFL and backtest settings.

basecurrency = ParamList("Base Currency", "USD,EUR");
Ini = Param( "Intial equity", 100000, 5000, 100000, 5000 );
SetOption( "InitialEquity", Ini ); // starting capital

maxpositions = Param( "Simultaneous positions", 1, 1, 100 );
SetOption( "MaxOpenPositions", maxpositions );

lot = ParamList( "Lot size", "Lot (100k)|Mini (10k)|Micro (1k)" );

if( lot == "Lot (100k)" )
    Lote = 100000;

if( lot == "Mini (10k)" )
    Lote = 10000;

if( lot == "Micro (1k)" )
    Lote = 1000;

SetOption( "UsePrevBarEquityForPosSizing", 0 );
SetPositionSize( lote, spsShares ); //i.e if trading standard lots
//SetPositionSize( 2, spsPercentOfEquity ); set percent of equity to use for position sizing
//This functions are used to determine the risk of each position.

//SetBacktestMode( backtestRegular );

Com = Param( "Commission and slipage", 0, 0, 150, 10 );
SetOption( "CommissionMode", 2 );
SetOption( "commissionamount", Com );

// FX Margin account settings ::::::::::::::::::::::::::::::::::
// these settings override settings of "Information" window
TickSize = IIf( StrFind( Name(), "JPY" ), 0.01, 0.0001 );
RoundLotSize = 1;
PointValue = 1;
Leverage = 40; // change to different leverage if using other broker i.e. IAB -> 40
MarginRate = 1 / Leverage; // dynamic calculation of margin
MarginDeposit = -100 * MarginRate; // here 100*0.025 = 2.5(%) 1:40

SmaPeriodFast = Param( "Fast Period ", 3, 1, 20, 2 );
SmaPeriodSlow = Param( "Slow Period", 30, 20, 200, 20 );

FastMA = MA( Close, SmaPeriodFast );
SlowMA = MA( Close, SmaPeriodSlow );

IsLong = FastMA > SlowMA;
IsShort = SlowMA > FastMA;
LongSignal  = Cross( FastMA, SlowMA );
ShortSignal = Cross( SlowMA, FastMA );

LongConfirmPrice = ValueWhen( LongSignal, H );
ShortConfirmPrice = ValueWhen( ShortSignal, L );

Buy = IsLong  AND H > LongConfirmPrice;
buyPrice = IIf(O > Ref(H, -1), O, LongConfirmPrice);//Avoid losing entries due to opening with a gap
Sell = IsShort AND L < ShortConfirmPrice;
SellPrice = ShortConfirmPrice;

Short = IsShort AND L < ShortConfirmPrice;
ShortPrice = IIf(O < Ref(L,-1), O, ShortConfirmPrice);//Avoid losing entries due to opening with a gap
Cover = IsLong  AND H > LongConfirmPrice;
CoverPrice = LongConfirmPrice;

Buy = ExRem( Buy, Sell );
Sell = ExRem( Sell, Buy );
Short = ExRem( Short, Cover );
Cover = ExRem( Cover, Short );

intradeLong = Flip(Buy, Sell OR Short);
intradeshort = Flip(Short, Cover OR Buy);

LongConfirmPrice = IIf(intradeLong, 0, LongConfirmPrice);
ShortConfirmPrice =  IIf(intradeshort, 0, ShortConfirmPrice);

Plot( C, "Close", colorBlack, styleBar );
Plot( FastMA, "Fast MA", colorRed, styleThick );
Plot( SlowMA, "Slow MA", colorBlue, styleThick );

distancia = 2.5 * ATR( 14 );

for( i = 0; i < BarCount; i++ )
    if( Buy[i] ) PlotText( "buy\n" + BuyPrice[i], i, H[i] - distancia[i], colorGreen );

    if( Sell[i] AND !Short[i] ) PlotText( "sell\n" + SellPrice[i], i, L[i] + distancia[i], colorRed );

    if( Short[i] ) PlotText( "short\n" + ShortPrice[i], i,  L[i] + distancia[i], colorRed );

    if( Cover[i] AND !Buy[i] ) PlotText( "cover\n" + CoverPrice[i], i, H[i] - distancia[i], colorGreen );

PlotShapes( IIf( LongSignal, shapeSmallCircle, shapeNone ), colorGreen, 0, LongConfirmPrice, 0 );
PlotShapes( IIf( ShortSignal, shapeSmallCircle , shapeNone ), colorRed, 0,  ShortConfirmPrice, 0 );

PlotShapes( IIf( Buy, shapeUpArrow, shapeNone ), colorGreen, 0, L, -15 );
PlotShapes( IIf( Buy, shapeHollowCircle, shapeNone ), colorGreen, 0, BuyPrice, 0 );
PlotShapes( IIf( Sell AND !Short, shapeDownArrow, shapeNone ), colorRed, 0, H, -15 );
PlotShapes( IIf( Sell AND !Short , shapeHollowCircle, shapeNone ), colorRed, 0, SellPrice, 0 );
PlotShapes( IIf( Short, shapeDownArrow, shapeNone ), colorRed, 0, H, -15 );
PlotShapes( IIf( Short, shapeHollowCircle, shapeNone ), colorRed, 0, ShortPrice, 0 );
PlotShapes( IIf( Cover AND !Buy, shapeUpArrow, shapeNone ), colorGreen, 0, L, -15 );
PlotShapes( IIf( Cover AND !Buy, shapeHollowCircle, shapeNone ), colorGreen, 0, CoverPrice, 0 );

// 2nd phase - Custom backtest start, here in example used to output a few additional columns//

// store currency of Information window for CBT code below
StaticVarSetText( "Curr" + Name(), GetFnData( "Currency" ) );

SetCustomBacktestProc( "" );

if( Status( "action" ) == actionPortfolio )
    bo = GetBacktesterObject();
    bo.Backtest( 1 ); //high-level method performs default portfolio backtest to obtain custom metrics

    st = bo.GetPerformanceStats( 0 ); // get stats for all trades
    initialcap = st.GetValue( "InitialCapital" );

    Sumprof = 0;
    Sumpip = 0;

// iterate through closed trades and output some columns ////////////////////////////////
    for( trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() )
        // equity at exit column
        SumProf += trade.GetProfit();// cum. profit
        EquityAtExit = initialcap + SumProf;
        trade.AddCustomMetric( "Equity@Exit", EquityAtExit );

		// base currency of tools-preferences-currencies
        trade.AddCustomMetric( "BaseCurr", basecurrency );
		// currency of information window
        trade.AddCustomMetric( "Currency", StaticVarGetText( "Curr" + trade.Symbol ) );
		// columns of conversion currency
        trade.AddCustomMetric( "FXRate@Entry", trade.EntryFxRate, 5 );
        trade.AddCustomMetric( "FXRate@Exit", trade.ExitFxRate, 5 );

		// P/L in pips column
        PLPips = IIf( trade.IsLong(),
                      /*Long*/ ( trade.ExitPrice - trade.EntryPrice ) / trade.TickSize,
                      /*Short*/ ( trade.EntryPrice - trade.ExitPrice ) / trade.TickSize );
        trade.AddCustomMetric( "P/L Pips", PLPips, 1 );
		// cummulated P/L pips column
        Sumpip += PLPips;
        trade.AddCustomMetric( "Cum.Pips", Sumpip, 1 );

// iterate through open trades and output some columns /////////////////////////////////
    for( openpos = bo.GetFirstOpenPos(); openpos; openpos = bo.GetNextOpenPos() )
        // equity at "exit" column
        SumProf += openpos.GetProfit();// cum. profit
        EquityAtExit = initialcap + SumProf;
        openpos.AddCustomMetric( "Equity@Exit", EquityAtExit );

        // base currency of tools-preferences-currencies
        openpos.AddCustomMetric( "BaseCurr", basecurrency );
        // currency of information window
        openpos.AddCustomMetric( "Currency", StaticVarGetText( "Curr" + openpos.Symbol ) );
        // columns of conversion currency
        openpos.AddCustomMetric( "FXRate@Entry", openpos.EntryFxRate, 5 );
        openpos.AddCustomMetric( "FXRate@Exit ", openpos.ExitFxRate, 5 );

        // P/L in pips column
        PLPips = IIf( openpos.IsLong(),
                      /*Long*/ ( openpos.ExitPrice - openpos.EntryPrice ) / openpos.TickSize,
                      /*Short*/ ( openpos.EntryPrice - openpos.ExitPrice ) / openpos.TickSize );
        openpos.AddCustomMetric( "P/L Pips", PLPips, 1 );

        // cummulated P/L pips column
        Sumpip += PLPips;
        openpos.AddCustomMetric( "Cum.Pips", Sumpip, 1 );



It does not allow me to include the original source code, so I have deleted it.


Thanks for the correction.

You are right about the possibility to use the Wizard in such a way. My main concern was about the GMT+0100 offset.

Anyway, I wondered about how to specify "Comma or Space" for the ASCII Importer.

I found this KB article that explains it:
Importing data using multiple column separators

I should have spent more time searching! :pleading_face:

1 Like

GMT.. part is just ignored by the importer. You can apply the offset if you want using
$TIMESHIFT command of ASCII importer.


Hi Jay, just wondering what you think of Urban’s Amibroker course? I’m tossing up between this and the Marwood / Trading Tuitions Academy course (bundle)?

Hi bouralex

I found Urban's course to be clear and easy to follow. It may have a limited number of systems and content compared to the Marwood course and that is the biggest difference I see, but that may change over time.

Also, I was recently trying to replicate the BB system results in the book and course again but somehow failed to get the same results as in the book / course so will need to revise and get some help from Urban. I notice he is active on youtube so it might be worth having a look at his updates there too.

Great, thanks Jay. Many of the Urban’s videos are in German so not that helpful. However, I did find the Amibroker Canadian Usergroup session he did last year to be quite useful. Regarding replicating the results from the book, a number of others have referenced or pointed to the Database settings that aren’t fully aligned with how these authors conduct the back tests, so that might be one avenue to explore if you haven’t already. Regards,

I completed Urbans course few days ago. IMO it is a very good starting point to lear Amibroker. What I was missing a little was some more detailed explanation of the code.

All in all it is worth the money

Thanks bouralex for the tip about the database settings. Urban provide some basic pointers in the second post in this thread and I will check other settings too. I assume an APX file with the BB system code (that's publicly available on his site) might be a good working file to update by sharing in this thread. I'll see if I can post it here and we could see if our results are similar - note this is the free code I referred to in the first post.

Also about the German youtube videos, I can get a rough idea of what he is communicating because the slides/content is quite simple and clear, but I'm probably missing about 50% of the detail! Shame there are no English captions.

Thanks Jay, never realised there was an APX file he posted - I only have the AFL file he sent me 6 months or so ago, which is simply the code with a handful of back-test settings included inside (but certainly not exhaustive). The APX file would contain the exact replica of the settings, code and timeframes if I am not mistaken - then you would have a better chance of replicating his results. Regards,

I should have been clearer bouralex- I meant to refer to the code on his site only. What I was proposing was to share an APX file with the code to address some of the issues with replication of results, for the reasons you noted. The NDU database settings may need to be aligned manually though to match the settings discussed previously in this thread.