Multi buy/sell system backtester discrepancy

Hi Everyone,

I have been stuck on one issue for quite some time and I am more than okay familiar with Amibroker as I have been working on AB for 7 years now.

A little help would be wonderful :blush:

My Scan Data, Exploration Data & Chart Data are in total sync but Backtest results are different even though I have tried to control all possible backtest input variables.

I will share the screenshot:-

Scan

SCAN

Exploration

EXPLORATION

It's a multi Entry-Exit strategy Scan/Exploration/Charts are behaving the way I want. Here is backtest screenshot:

Backtest

Somehow one sell signal is causing all positions to exit on 4-9-19 at 9:30:00. Even though trade entry is exactly the way it should be in all scenarios.

Could you please tell me what could be wrong? Or what's the way to correct it?

Thanks!!

seems you are using pyramiding Portfolio-level back testing

when using pyramiding in Amibroker your initial Buy is stored in the Buy array as a number 1. Additional buys are stored in the buy array using sigScaleIn. If you scale out of a positions the location of the scale out is stored in the Buy array using sigScaleOut. The last scale out is stored in the Sell array using 1.

If you use the sell array then it will sell the entire position at once. So you need to store the scaling outs in the Buy array using sigScaleOut

1 Like

Hey Empottasch,

Thanks for the quick reply.

I am not using Portfolio-level backtesting. I will share my structure

Part-1
{
Strategy1 Buy_Flag And Sell_Flag
Strategy2 Buy_Flag And Sell_Flag
''
''
''
StrategyN Buy_Flag And Sell_Flag
}

Part-2 //Signal Aggregator And Manager
{
Loop(Buy And Sell)
}

BackTest Options
SetPositionSize ( 100 , spsShares );
SetBacktestMode( backtestRegularRawMulti );
SetOption( "ReverseSignalForcesExit", False);

Kept everything else to get results from raw signals. Am I missing something?

not sure but if you integrate multiple systems that still means you are doing some form of pyramiding in my opinion. I just point out that if you use the sell array to sell it will sell all your positions. So if system1 is long and system2 goes long a few bars after that you need to use sigScaleIn to add that positions. If system2 then sells while system1 is still long then you need to use sigScaleOut to get out of the position of system2 while you keep the position of system1.

4 Likes

Hi,

Tried pyramiding it's giving the exact result as Backtest :frowning:

Hence no improvement.

Removed (backtestRegularRaw2Multi & ReverseSignalForcesExit)

is there any other way out?

hi,

i made a simple example. Seems to work for me. Made the code just now and been awhile since i worked with scaling but seems correct

SetTradeDelays( 0, 0, 0, 0 );
SetOption( "FuturesMode", True );
SetOption( "CommissionMode", 3 );
SetOption( "CommissionAmount", 2.37 );

SetChartBkColor( ColorRGB( 0, 0, 0 ) );
SetChartOptions( 0, chartShowArrows | chartShowDates );
Plot( C, "C", colorWhite, styleCandle, Null, Null, 0, 0, 0 );

// system 1
Buy1 = Cross( C, MA( C, 20 ) );
Sell1 = Cross( MA( C, 20 ), C );
BuyPrice1 = SellPrice1 = C;

// system 2
Buy2 = Cross( StochK(), StochD() ) AND StochD() < 20;
Sell2 = Cross( StochD(), StochK() ) AND StochD() > 80;
BuyPrice2 = SellPrice2 = C;

Buy = Sell = 0;
BuyPrice = SellPrice = C;
npos = 0;
maxpos = 6; // maximum number of positions
nposarray = 0;

for( i = 0; i < BarCount; i++ )
{
    if( ( Buy1[i] OR Buy2[i] ) AND npos == 0 )
    {
        Buy[i] = 1;
        npos = 1;
        nposarray[i] = npos;
    }
    else
        if( ( Buy1[i] OR Buy2[i] ) AND npos > 0 AND npos < maxpos )
        {
            Buy[i] = sigScaleIn;
            npos = npos + 1;
            nposarray[i] = npos;
        }

    if( ( Sell1[i] OR Sell2[i] ) AND npos == 1 )
    {
        Sell[i] = 1;
        npos = 0;
        nposarray[i] = npos;
    }
    else
        if( ( Sell1[i] OR Sell2[i] ) AND npos > 1 )
        {
            Buy[i] = sigScaleOut;
            npos = npos - 1;
            nposarray[i] = npos;
        }
}

SetPositionSize( 1, spsShares );

PlotShapes( IIf( Buy == 1, shapeUpArrow, shapeNone ), colorDarkGreen, 0, L, -15 );
PlotShapes( IIf( Buy == 1, shapeSmallCircle, shapeNone ), colorWhite, 0, BuyPrice, 0 );
PlotShapes( IIf( Buy == sigScaleIn, shapeUpArrow, shapeNone ), colorlightblue, 0, L, -15 );
PlotShapes( IIf( Buy == sigScaleIn, shapeSmallCircle, shapeNone ), colorWhite, 0, BuyPrice, 0 );
PlotShapes( IIf( Buy == sigScaleOut, shapeDownArrow, shapeNone ), colorOrange, 0, H, -15 );
PlotShapes( IIf( Buy == sigScaleOut, shapeSmallCircle, shapeNone ), colorWhite, 0, BuyPrice, 0 );
PlotShapes( IIf( Sell, shapeDownArrow, shapeNone ), colorRed, 0, H, -15 );
PlotShapes( IIf( Sell, shapeSmallCircle, shapeNone ), colorWhite, 0, SellPrice, 0 );

bi = BarIndex();
fvb = FirstVisibleValue( bi );
lvb = LastVisibleValue( bi );
ft = "Arial Black";
sz = 10;

for( i = fvb; i <= lvb; i++ )
{
    if( Buy[i] == 1 )
    {
        PlotTextSetFont( "" + 1, ft, sz, i, L[i], colorGreen, colorWhite, -sz * 4 );
    }

    if( Buy[i] == sigScaleIn )
    {
        PlotTextSetFont( "" + nposarray[i], ft, sz, i, L[i], colorGreen, colorWhite, -sz * 4 );
    }

    if( Buy[i] == sigScaleOut )
    {
        PlotTextSetFont( "" + nposarray[i], ft, sz, i, H[i], colorOrange, colorWhite, sz * 3 );
    }

    if( Sell[i] == 1 )
    {
        PlotTextSetFont( "" + nposarray[i], ft, sz, i, H[i], colorRed, colorWhite, sz * 3 );
    }
}

Filter = Buy OR Sell;
AddColumn( Buy, "Buy" );
AddColumn( Sell, "Sell" );
AddColumn( nposarray, "Number Of Positions" );
1 Like

i see you use IQFeed data. Below is another example of scaling. I believe they call this method "The Ladder". You can display this for QGC#. I used 50$ as a separation. For other futures one would need other settings. For QGC# one would need deep pockets and a lot of strong beer :beer: to sit through such trades. But you could also trade QMGC#, QMES# or QMNQ#. The buy and sell levels I now calculate as a single price level but one could also maybe try variable levels like based on ATR.

SetTradeDelays( 0, 0, 0, 0 );
SetOption( "FuturesMode", True );
SetOption( "CommissionMode", 3 );
SetOption( "CommissionAmount", 2.37 );
SetPositionSize( 1, spsShares );

SetChartBkColor( ColorRGB( 0, 0, 0 ) );
SetChartOptions( 0, chartShowArrows | chartShowDates );
Plot( C, "C", colorWhite, styleCandle, Null, Null, 0, 0, 0 );

newBuySeries = ParamToggle( "New Buying Series", "Buy at any Level down|Use last Sell Level", 0 );

// separation between levels in points
separation = 50; // use 50$ for Gold futures from about 2016 - 2021 period

// determine price range and add 10%
priceRange = LastValue( Highest( H ) ) * 1.1;

// create buy and sell arrays trigger arrays
buyTrigger = sellTrigger = 0;
buyLevel = sellLevel = 0;

miny = Status( "axisminy" );
maxy = Status( "axismaxy" );

for( i = separation; i < priceRange; i = i + separation )
{
    //_TRACE( "i: " + i );
    level = i;

    if( LastValue( level ) > miny AND LastValue( level ) < maxy )
    {
        Plot( level, "", colorWhite, styleLine | styleNoRescale | styleNoLabel, Null, Null, 0, 0, 1 );
    }

    buyTrigger = IIf( Cross( level, C ), 1, buyTrigger );
    buyLevel = IIf( Cross( level, C ), level, buyLevel );

    sellTrigger = IIf( Cross( C, level ), 1, sellTrigger );
    sellLevel = IIf( Cross( C, level ), level, sellLevel );
}

Buy = Sell = 0;
BuyPrice = SellPrice = C;
npos = 0;
maxpos = 50; // maximum number of positions
nposarray = 0;
lev = 1e12;

for( i = 0; i < BarCount; i++ )
{
    if( ( BuyTrigger[i] ) AND npos == 0 AND buyLevel[i] < lev )
    {
        Buy[i] = 1;
        npos = 1;
        nposarray[i] = npos;
        lev = buyLevel[i];
    }
    else
        if( ( BuyTrigger[i] ) AND npos > 0 AND npos < maxpos AND buyLevel[i] < lev )
        {
            Buy[i] = sigScaleIn;
            npos = npos + 1;
            nposarray[i] = npos;
            lev = buyLevel[i];
        }

    if( ( SellTrigger[i] ) AND npos == 1 AND sellLevel[i] > lev )
    {
        Sell[i] = 1;
        npos = 0;
        nposarray[i] = npos;

        // final sell, new round of buy can start now
        if( NewBuySeries )
        {
            lev = sellLevel[i]; // use last sell level when starting the first new buy
        }
        else
        {
            lev = 1e12; // or reset level and restart buying at any cross down.
        }
    }
    else
        if( ( SellTrigger[i] ) AND npos > 1 AND sellLevel[i] > lev )
        {
            Buy[i] = sigScaleOut;
            npos = npos - 1;
            nposarray[i] = npos;
            lev = sellLevel[i];
        }
}

PlotShapes( IIf( Buy == 1, shapeUpArrow, shapeNone ), colorDarkGreen, 0, L, -15 );
PlotShapes( IIf( Buy == 1, shapeSmallCircle, shapeNone ), colorWhite, 0, BuyPrice, 0 );
PlotShapes( IIf( Buy == sigScaleIn, shapeUpArrow, shapeNone ), colorlightblue, 0, L, -15 );
PlotShapes( IIf( Buy == sigScaleIn, shapeSmallCircle, shapeNone ), colorWhite, 0, BuyPrice, 0 );
PlotShapes( IIf( Buy == sigScaleOut, shapeDownArrow, shapeNone ), colorOrange, 0, H, -15 );
PlotShapes( IIf( Buy == sigScaleOut, shapeSmallCircle, shapeNone ), colorWhite, 0, BuyPrice, 0 );
PlotShapes( IIf( Sell, shapeDownArrow, shapeNone ), colorRed, 0, H, -15 );
PlotShapes( IIf( Sell, shapeSmallCircle, shapeNone ), colorWhite, 0, SellPrice, 0 );

bi = BarIndex();
fvb = FirstVisibleValue( bi );
lvb = LastVisibleValue( bi );
ft = "Arial Black";
sz = 10;

for( i = fvb; i <= lvb; i++ )
{
    if( Buy[i] == 1 )
    {
        PlotTextSetFont( "" + 1, ft, sz, i, L[i], colorGreen, colorWhite, -sz * 4 );
    }

    if( Buy[i] == sigScaleIn )
    {
        PlotTextSetFont( "" + nposarray[i], ft, sz, i, L[i], colorGreen, colorWhite, -sz * 4 );
    }

    if( Buy[i] == sigScaleOut )
    {
        PlotTextSetFont( "" + nposarray[i], ft, sz, i, H[i], colorOrange, colorWhite, sz * 3 );
    }

    if( Sell[i] == 1 )
    {
        PlotTextSetFont( "" + nposarray[i], ft, sz, i, H[i], colorRed, colorWhite, sz * 3 );
    }
}

Filter = Buy OR Sell;
AddColumn( Buy, "Buy" );
AddColumn( Sell, "Sell" );
AddColumn( nposarray, "Number Of Positions" );

qmgc

3 Likes

Help idea #1 - Use code tags.

How to ask a good question

1 Like

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.