As you can see below, the detail log shows a Scale-Out at 19/11/2024
and in the log windows it says that the bar where it did the bo.scaleout was 20/11/2024
I can not find what is making my bo.scaleout not being executed in at 20/11/2024.
Anyone ?
Using AB 64 7.00, with EOD database. In Analysis/Backtest I used current having SPY set and last 300 recent bars , but the problem arises with any symbol
SetBacktestMode( backtestRegularRaw );
SetTradeDelays(0, 0, 0, 0);
SetOption("FuturesMode",false);
SetOption("AllowPositionShrinking",false);//
SetOption("InitialEquity",1000000);
SetOption("CommissionMode",1); // 1- Percent of trade
Comission = 0.037;
SetOption("CommissionAmount",0.037); //0.37%
SetOption("maxopenpositions",1);//
SetOption("MinShares",1);//
PercentInitial = 4;
bir = Status("barinrange");
Buy = Cum(bir) == 1;
Buy = Ref(Buy, -1);
BuyPrice = Close;
StaticVarSet("xPrice" + Name(), Close);
Sell = Short = Cover = 0;
PositionSize = SetPositionSize(100, spsPercentOfEquity); // will be adjusted in CBT
//CBT code begins here
SetCustomBacktestProc( "" );
debugPrefix = "#AB: ";
dbMaxLines = 10; // debug log lines
if( Status( "action" ) == actionPortfolio )
{
DateNumberArray = DateTimeConvert(2,DateNum());
Withdraw = Day() == 20;
TotalWithdraw = 0;
Lines = 0;
bo = GetBacktesterObject();
bo.PreProcess();
yearChange = Year() != Ref(Year(), -1);
MonthWithdrawAmmount = bo.InitialEquity * (PercentInitial / 100) / 12;
xSymbol = "";
for( i = 0; i < BarCount; i++ )
{
if( xSymbol == "" )
for( openpos = bo.GetFirstOpenPos(); openpos; openpos = bo.GetNextOpenPos() )
{
xSymbol = openpos.Symbol;
if( dbMaxLines > 0 )
_TRACE(debugPrefix + DateTimeToStr( DateNumberArray [0] ) + ": openpos.Symbol : " + xSymbol);
xPrice_a = StaticVarGet("xPrice" + xSymbol);
break;
}
if( xSymbol == "" )
for( trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() )
{
xSymbol = trade.Symbol;
if( dbMaxLines > 0 )
_TRACE(debugPrefix + DateTimeToStr( DateNumberArray [0] ) + ": trade.Symbol : " + xSymbol);
xPrice_a = StaticVarGet("xPrice" + xSymbol);
break;
}
if( yearChange[i] )
{
CurEquity = bo.Equity;
MonthWithdrawAmmount = CurEquity * (PercentInitial / 100) / 12;
}
if( Withdraw[i] AND xSymbol != "")
{
CurEquity = bo.Equity;
xPrice = xPrice_a[i];
PosSize = MonthWithdrawAmmount + (MonthWithdrawAmmount * (Comission + 0.003));
qty = ceil(SafeDivide(PosSize, xPrice));
PosSize = qty * xPrice;
if( CurEquity > 0 AND PosSize < CurEquity )
{
//long ScaleTrade(long Bar, string Symbol, bool bIncrease, float Price, float PosSize, [optional] variant Deposit)
sc_res = bo.ScaleTrade(i, xSymbol, false, xPrice, PosSize);
if( sc_res != 1 )
_TRACE(debugPrefix + DateTimeToStr( DateNumberArray [i] ) + ": ##### sc_res : " + sc_res);
Lines++;
if( Lines < dbMaxLines )
_TRACE(debugPrefix + DateTimeToStr( DateNumberArray [i] )
+ ": MonthWithdrawAmmount : " + MonthWithdrawAmmount
+ ": bo.Cash : " + bo.Cash
+ ": CurEquity : " + CurEquity
+ ": xSymbol : " + xSymbol
+ ": xPrice : " + xPrice
+ ": qty : " + qty
+ ": PosSize : " + PosSize);
bo.UpdateStats(i, 1); // update regular stats
}
} //if( Withdraw[i] )
//bo.HandleStops( i );
bo.ProcessTradeSignals( i );
// Update equity after withdrawal
bo.UpdateStats(i, 2); // update regular stats
} //for
bo.PostProcess();
} //if( xSymbol != "" )
It looks like your CBT logic will only print those _TRACE lines and scale out when Withdraw[i] is true, which only happens on the 20th of the month. The other scale-out that occurs as part of the bo.ProcessTradeSignals() call does not have any associated output, so you wouldn’t see it in your log. Or am I missing something?
1 Like
Well, as you can see the only way the backtest can know about a scale-out is from CBT
nowhere eslse there is scale-out command
And in CBT the only place there is a scale-out command has also a _TRACE
In detail log the scale out is done day 19
Oh, I see now that there are no actual scaleout signals generated in Phase 1. The detailed log can get a bit screwy with a CBT… are you sure that the date is being printed before the output, and not after it? In other words, maybe (probably?) your scale-out trace output really belongs on the 20th.
1 Like
all information that appears in detail log is correct,
the symbol is correct, the price used to scaleout belongs to day 20, the only thing wrong is that in detail log it happens day 19
Tomasz
January 13, 2026, 8:19pm
6
Note that ProcessTradeSignals calls UpdateStats internally. Since you are calling it yourself with timeinbar=2 that should occur only once, it may lead to unpredictable behaviour.
1 Like
Thank you for your attention and pointing that out, unfortunatelly after commenting out line with UpdateSTats (2) the result is stil the same
SetBacktestMode( backtestRegularRaw );
SetTradeDelays(0, 0, 0, 0);
SetOption("FuturesMode",false);
SetOption("AllowPositionShrinking",false);//
SetOption("InitialEquity",1000000);
SetOption("CommissionMode",1); // 1- Percent of trade
Comission = 0.037;
SetOption("CommissionAmount",0.037); //0.37%
SetOption("maxopenpositions",1);//
SetOption("MinShares",1);//
PercentInitial = 4;
bir = Status("barinrange");
Buy = Cum(bir) == 1;
Buy = Ref(Buy, -1);
BuyPrice = Close;
StaticVarSet("xPrice" + Name(), Close);
Sell = Short = Cover = 0;
PositionSize = SetPositionSize(100, spsPercentOfEquity); // will be adjusted in CBT
//CBT code begins here
SetCustomBacktestProc( "" );
debugPrefix = "#AB: ";
dbMaxLines = 10; // debug log lines
if( Status( "action" ) == actionPortfolio )
{
DateNumberArray = DateTimeConvert(2,DateNum());
Withdraw = Day() == 20;
TotalWithdraw = 0;
Lines = 0;
bo = GetBacktesterObject();
bo.PreProcess();
yearChange = Year() != Ref(Year(), -1);
MonthWithdrawAmmount = bo.InitialEquity * (PercentInitial / 100) / 12;
xSymbol = "";
for( i = 0; i < BarCount; i++ )
{
if( xSymbol == "" )
for( openpos = bo.GetFirstOpenPos(); openpos; openpos = bo.GetNextOpenPos() )
{
xSymbol = openpos.Symbol;
if( dbMaxLines > 0 )
_TRACE(debugPrefix + DateTimeToStr( DateNumberArray [0] ) + ": openpos.Symbol : " + xSymbol);
xPrice_a = StaticVarGet("xPrice" + xSymbol);
break;
}
if( xSymbol == "" )
for( trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() )
{
xSymbol = trade.Symbol;
if( dbMaxLines > 0 )
_TRACE(debugPrefix + DateTimeToStr( DateNumberArray [0] ) + ": trade.Symbol : " + xSymbol);
xPrice_a = StaticVarGet("xPrice" + xSymbol);
break;
}
if( yearChange[i] )
{
CurEquity = bo.Equity;
MonthWithdrawAmmount = CurEquity * (PercentInitial / 100) / 12;
}
if( Withdraw[i] AND xSymbol != "")
{
CurEquity = bo.Equity;
xPrice = xPrice_a[i];
PosSize = MonthWithdrawAmmount + (MonthWithdrawAmmount * (Comission + 0.003));
qty = ceil(SafeDivide(PosSize, xPrice));
PosSize = qty * xPrice;
if( CurEquity > 0 AND PosSize < CurEquity )
{
//long ScaleTrade(long Bar, string Symbol, bool bIncrease, float Price, float PosSize, [optional] variant Deposit)
sc_res = bo.ScaleTrade(i, xSymbol, false, xPrice, PosSize);
if( sc_res != 1 )
_TRACE(debugPrefix + DateTimeToStr( DateNumberArray [i] ) + ": ##### sc_res : " + sc_res);
Lines++;
if( Lines < dbMaxLines )
_TRACE(debugPrefix + DateTimeToStr( DateNumberArray [i] )
+ ": MonthWithdrawAmmount : " + MonthWithdrawAmmount
+ ": bo.Cash : " + bo.Cash
+ ": CurEquity : " + CurEquity
+ ": xSymbol : " + xSymbol
+ ": xPrice : " + xPrice
+ ": qty : " + qty
+ ": PosSize : " + PosSize);
bo.UpdateStats(i, 1); // update regular stats
}
} //if( Withdraw[i] )
//bo.HandleStops( i );
bo.ProcessTradeSignals( i );
// Update equity after withdrawal
//bo.UpdateStats(i, 2); // update regular stats
} //for
bo.PostProcess();
} //if( xSymbol != "" )
You should keep bo.ProcessTradeSignals(i) at the Top of the Loop.
1 Like
awilson
January 14, 2026, 11:44am
9
Thank you. In fact moving bo.ProcessTradeSignals(i) to the begining of the loop did the trick.
I thought it had to be executed as the last command of each bar
beppe
January 14, 2026, 2:35pm
10
I thought it had to be executed as the last command of each bar
Exactly. This is how it is typically used in the documentation examples and even in the predefined "snippets".
I would really appreciate it if @Tomasz could clarify how this different behavior arises in your specific case.
Tomasz
January 14, 2026, 3:48pm
11
Proper sample codes are included in the Knowledge Base, the one with scaling is here:
2 Likes
awilson
January 14, 2026, 5:10pm
12
Again thank you very much for your time and patience.
Now scale out is on day 20,
but the first bo.scale is not respecting PosSize parameter.
It scaleout less money then I expected.
I pass 3490.13 and got 2941.64 although SPY have a lot more
The second scaleout seams ok and the others too.
Any idea ?
// Parameters - adjust these
PercentInitial = 4;
SetBacktestMode( backtestRegularRaw );
SetTradeDelays(0, 0, 0, 0);
SetOption("FuturesMode",false);
SetOption("AllowPositionShrinking",false);//
SetOption("InitialEquity",1000000);
SetOption("CommissionMode",1); // 1- Percent of trade
Comission = 0.037;
SetOption("CommissionAmount",0.037); //0.37%
SetOption("maxopenpositions",1);//
SetOption("MinShares",1);//
bir = Status("barinrange");
Buy = Cum(bir) == 1;
Buy = Ref(Buy, -1); // buy 100% one time only
BuyPrice = Close;
Sell = Short = Cover = 0;
PositionSize = SetPositionSize(100, spsPercentOfEquity); // will be adjusted in CBT
SetCustomBacktestProc( "" );
debugPrefix = "#AB: ";
dbMaxLines = 100;
if( Status( "action" ) == actionPortfolio )
{
DateNumberArray = DateTimeConvert(2,DateNum());
Withdraw = Day() == 20;
TotalWithdraw = 0;
Lines = 0;
bo = GetBacktesterObject();
bo.PreProcess();
yearChange = Year() != Ref(Year(), -1);
MonthWithDraw = bo.InitialEquity * (PercentInitial / 100) / 12;
for( i = 0; i < BarCount; i++ )
{
bo.ProcessTradeSignals( i );
CurEquity = bo.Equity;
if( yearChange[i] )
{
MonthWithDraw = CurEquity * (PercentInitial / 100) / 12;
if( dbMaxLines > 0 )
_TRACE(debugPrefix + DateTimeToStr( DateNumberArray [i] ) + ": YEAR : "
+ ": TotalWithdraw : " + TotalWithdraw
+ ": CurEquity : " + CurEquity
+ ": MonthWithDraw : " + MonthWithDraw);
}
if( Withdraw[i] )
{
for( pos = bo.GetFirstOpenPos(); pos; pos = bo.GetNextOpenPos() )
{
posval = pos.GetPositionValue();
xPrice = pos.GetPrice( i, "C" );
xSymbol = pos.Symbol;
PosSize = MonthWithDraw + (MonthWithDraw * (Comission + 0.001));
qty = ceil(SafeDivide(PosSize, xPrice));
PosSize = qty * xPrice;
if( PosSize >= posval )
{
//Lines++;
_TRACE(debugPrefix + DateTimeToStr( DateNumberArray [i] ) + ": PosSize >= posval : "
+ ": qty : " + qty
+ ": PosSize : " + PosSize
+ ": bo.Cash : " + bo.Cash
+ ": CurEquity : " + CurEquity);
}
else
{
//long ScaleTrade(long Bar, string Symbol, bool bIncrease, float Price, float PosSize, [optional] variant Deposit)
sc_res = bo.ScaleTrade(i, xSymbol, false, xPrice, PosSize);
if( sc_res != 1 ) _TRACE(debugPrefix + DateTimeToStr( DateNumberArray [i] ) + ": ScaleTrade : " + ": sc_res : " + sc_res);
if( Lines < dbMaxLines )
{
Lines++;
_TRACE( debugPrefix + DateTimeToStr( DateNumberArray [i] ) + ": ScaleOut : "
+ ": MonthWithDraw : " + MonthWithDraw
+ ": bo.Cash : " + bo.Cash
+ ": CurEquity : " + CurEquity
+ ": xSymbol : " + xSymbol
+ ": xPrice : " + xPrice
+ ": qty : " + qty
+ ": PosSize : " + PosSize);
}
barsSinceWD = 0;
str = debugPrefix + DateTimeToStr( DateNumberArray [i] ) + "\t: ScaleOut : "
+ ": MonthWithDraw : " + MonthWithDraw
+ ": bo.Cash : " + bo.Cash
+ ": CurEquity : " + CurEquity
+ ": xSymbol : " + xSymbol
+ ": xPrice : " + xPrice
+ ": qty : " + qty
+ ": PosSize : " + PosSize;
bo.RawTextOutput( str );
}
break;
} // for( pos = bo.GetFirstOpenPos()
} //if( Withdraw[i] )
} //for Barcount
bo.PostProcess();
_TRACE(debugPrefix + ": TotalWithdraw : " + TotalWithdraw);
} //if( Status( "action" ) == actionPortfolio )
mradtke
January 14, 2026, 5:36pm
13
If your Round Lot Size is set to 1, then AmiBroker will scale out as many whole shares as possible without exceeding your scaleout value. One more share at $589 would exceed your requested value of $3490.
1 Like
awilson
January 14, 2026, 5:47pm
14
Perfect you nail it.
Thank you everybody
Your code is also working correctly.
The issue is only a misunderstanding caused by the order in which logs are printed.
The scale-out shown in your detailed log does happen on 20-11-2024, exactly as expected.
What happens in your code is this:
The position is scaled out first.
Then bo.ProcessTradeSignals(i) is called
Because of this order, the detailed log prints messages out of chronological sequence.
This creates the impression that the scale-out happened on a different date, even though it did not.
You can clearly verify this by printing the datetime directly in the detailed log before scale-out :
bo.RawTextOutput( "SCALE-OUT DateTime: " + DateTimeToStr( DateNumberArray [i] ) );
sc_res = bo.ScaleTrade(i, xSymbol, false, xPrice, PosSize);
When you do this, you will observe the following:
Logs initially show 19-11-2024
Then the position is scaled out on 20-11-2024
After that, bo.ProcessTradeSignals(i) runs and prints 20-11-2024
So the confusion comes from when the log lines are printed, not from when the trade action actually occurs.
3 Likes
Tomasz
January 15, 2026, 2:03pm
16
Exactly. As I wrote low-level is tricky because you can place your code anywhere and do ANYTHING and be surprised.
2 Likes
awilson
January 16, 2026, 3:18pm
17
Apreciatte the attention, one more question about bo.ScaleTrade
Base currency of my database is USD
and I want to scaleout a Symbol that uses another currency, let say EUR.
long ScaleTrade (long Bar, string Symbol, bool bIncrease, float Price, float PosSize, [optional] variant Deposit)
parameters price and possize, should be in USD or an EURO ?
Results are not what I expected , so to find my error I would like to clarify the point.
Thnaks again