> > > To:
amibroker@xxxxxxxxxxxxxxx <amibroker%
40yahoogroups.com>
> > > Sent: Monday, September 21, 2009 1:40 PM
> > > Subject: [amibroker] Re: Support Issues
> > >
> > >
> > >
> > >
> > >
> > > Unfortunately, no. None of my questions are answered by this otherwise
> > excellent document (which I have already read many times).
> > >
> > > Perhaps my questions were not clearly expressed in the help request. But
> > basically I want to know:
> > >
> > > 1.. When should we use bo.UpdateStats(bar, 0)
> > > 2.. When should we use bo.UpdateStats(bar, 1)
> > > 3.. When should we use bo.UpdateStats(bar, 2)
> > > 4.. When should we use bo.HandleStops(bar)
> > > The author of the document that you pointed to seemed equally confused.
> > When describing usage of UpdateStats he says:
> > >
> > > "The AmiBroker help is a little vague on how the TimeInsideBar parameter
> > works"
> > >
> > > He then goes on to say:
> > >
> > > "why it would be called with the value set to zero or more than once, I'm
> > not sure."
> > >
> > > In other words, he had the exact same questions that I have now. Further,
> > after much experimentation, I must question his conclusions on when and
> > where to use HandleStops(), at least as it applies to more recent versions
> > of AmiBroker.
> > >
> > > I have put togeather a sample Optimization, below, for a "percent of
> > equity" position sizing that runs once for each of:
> > >
> > > 1.. default high level AmiBroker backtester
> > > 2.. low level backtester template described in the UKB document that you
> > pointed to
> > > 3.. low level backtester using my own best guess at how these calls
> > should be made.
> > > If you (or anyone else) run it, you will see that my own best guess
> > appears to match the default AmiBroker behavior. The UKB template (as I've
> > understood it) does not match at all. I would like to get confirmation (or
> > correction) that my conclusions are accurate. My conclusions are spelled out
> > as comments in the code.
> > >
> > > For the purposes of this example, I am running a Long only strategy,
> > using AmiBroker version 5.26.5 beta, Norgate Premium Data for the period Jan
> > 1/08 - Dec 31/08 on a watchlist containing:
> > >
> > > a.. AAPL
> > > b.. IBM
> > > c.. ORCL
> > > d.. MSFT
> >
> > > I am exagerating the position size to force scenarios where we run out of
> > cash. The script can also be Backtested with default optimization argument
> > set to 3 in order to see _TRACE statements in an AA Window "Detail Log"
> > style report of the sfclimbers trades.
> > >
> > > Here is the code:
> > >
> > > // Simplify the example by disabling most of the variables that effect
> > > // position size when expressed as a percentage of equity.
> > >
> > > SetOption("AccountMargin", 100);
> > > SetOption("AllowPositionShrinking", false);
> > > SetOption("CommissionMode", 2);
> > > SetOption("CommissionAmount", 0);
> > > SetOption("InterestRate", 0);
> > > SetOption("MinShares", 1);
> > > SetOption("MinPosValue", 0);
> > >
> > > // These two options have a direct impact on low level backtest,
> > > // far beyond what was covered in the otherwise excellent UKB document.
> > > // Run this optimization once for each of the 4 combinations of these two
> >
> > > // options using a watchlist of AAPL, IBM, ORCL, MSFT from 1/1/08 -
> > 12/31/08.
> > >
> > > SetOption("UsePrevBarEquityForPosSizing", true);
> > > SetOption("ActivateStopsImmediately", true);
> > >
> > > // Simple random entry followed by N-bar stop.
> > >
> > > SetOption("InitialEquity", 500000.00);
> > > SetTradeDelays(0, 0, 0, 0);
> > > SetPositionSize(33, spsPercentOfEquity);
> > >
> > > Buy = Random(13000) > 0.5;
> > > BuyPrice = Open;
> > > Sell = 0;
> > > ApplyStop(stopTypeNBar, stopModeBars, 3, 0);
> > >
> > > // Use optimizer to generate output for each of default AB, UKB and
> > > // sfclimbers speculation. At a minimum it will illustrate that the
> > > // UKB article is incomplete and/or out of date. Whether or not the
> > > // sfclimbers conclusions are correct is the subject of this help
> > request!
> > >
> > > custom = Optimize("Custom", 3, 1, 3, 1);
> > > SetCustomBacktestProc("");
> > >
> > > if (Status("action") == actionPortfolio) {
> > > maxPosition = 0.33; // Must match call to SetPositionSize above
> > > marginMultiplier = 100 / GetOption("AccountMargin");
> > > usePreviousBar = GetOption("UsePrevBarEquityForPosSizing");
> > > activateStopsImmediately = GetOption("ActivateStopsImmediately");
> > > dates = DateTime();
> > >
> > > bo = getBacktesterObject();
> > >
> > > switch (custom) {
> > > case 1: {
> > > // Default AmiBroker behavior
> > > bo.Backtest();
> > > break;
> > > }
> > >
> > > case 2: {
> > > // User knowledge base template behavior
> > > bo.PreProcess();
> > >
> > > for (bar = 0; bar < BarCount; bar++) {
> > > size = bo.Equity * maxPosition;
> > > quit = 0;
> > >
> > > for (sig = bo.GetFirstSignal(bar); sig; sig = bo.GetNextSignal(bar)) {
> > > if (sig.IsEntry()) {
> > > lotSize = int((size / sig.Price) / RoundLotSize) * RoundLotSize;
> > >
> > > if (bo.Cash >= ((lotSize * sig.Price) / marginMultiplier) && quit == 0) {
> >
> > > sig.PosSize = -2000 - lotSize;
> > > } else if (bo.Cash > 0 && quit == 0) {
> > > sig.PosSize = 0; // Insufficient funds. Cancel signal.
> > > quit = 1;
> > > } else {
> > > sig.PosSize = 0; // Already hit insufficient funds. Cancel remaining
> > signals.
> > > quit++;
> > > }
> > >
> > > if (sig.PosSize < 0) {
> > > bo.EnterTrade(bar, sig.Symbol, sig.IsLong(), sig.Price, sig.PosSize);
> > > }
> > > } else if (sig.IsExit()) {
> > > bo.ExitTrade(bar, sig.Symbol, sig.Price);
> > > }
> > > }
> > >
> > > bo.HandleStops(bar);
> > > bo.UpdateStats(bar, 1);
> > > bo.UpdateStats(bar, 2);
> > > }
> > >
> > > bo.PostProcess();
> > > }
> > >
> > > case 3: {
> > > // sfclimbers behavior
> > > bo.PreProcess();
> > >
> > > for (bar = 0; bar < BarCount; bar++) {
> > > _TRACE(DateTimeToStr(dates[bar]) + "\t" + bar);
> > >
> > > /*
> > > * Conclusion 1:
> > > * Must consult UsePrevBarEquityForPosSizingand call updateStats(bar, 0)
> > > * to get initial equity.
> > > */
> > > if (!usePreviousBar) {
> > > bo.updateStats(bar, 0); // Update to bar open.
> > > baseEquity = bo.Equity; // Then calculate equity
> > > } else {
> > > baseEquity = bo.Equity; // Calculate equity
> > > bo.updateStats(bar, 0); // Then update to bar open.
> > > }
> > >
> > > size = baseEquity * maxPosition;
> > > _TRACE("\tBase Equity: " + baseEquity + ", Opening Equity: " + bo.Equity
> > + ", Pct. of Base: " + size + ", Starting Cash: " + bo.Cash);
> > >
> > > /*
> > > * Conclusion 2:
> > > * Must process regular exit signals at start of each bar, before new
> > entries.
> > > *
> > > * Conclusion 3:
> > > * Must consult ActivateStopsImmediately to determine whether to
> > additionally
> > > * process stops at start of each bar, before new entries.
> > > */
> > > for (sig = bo.GetFirstSignal(bar); sig; sig = bo.GetNextSignal(bar)) {
> > > if (sig.IsExit() && (sig.Reason == 1 || !activateStopsImmediately)) {
> > > if (pos = bo.FindOpenPos(sig.Symbol)) {
> > > msg = "\tExit (" + sig.Reason + ") " + WriteIf(sig.IsLong(), "Long: ",
> > "Short: ") + sig.Symbol + ", Shares: " + pos.Shares;
> > >
> > > /*
> > > * Conclusion 4:
> > > * Must call updateStats(bar, 1) after each trade entry or exit in order
> > > * to have up to date Equity and Cash data.
> > > */
> > > bo.ExitTrade(bar, sig.Symbol, sig.Price);
> > > bo.UpdateStats(bar, 1);
> > > _TRACE(msg + ", Cash Balance: " + bo.Cash);
> > > }
> > > }
> > > }
> > >
> > > quit = 0;
> > >
> > > for (sig = bo.GetFirstSignal(bar); sig; sig = bo.GetNextSignal(bar)) {
> > > if (sig.IsEntry()) {
> > > lotSize = int((size / sig.Price) / RoundLotSize) * RoundLotSize;
> > >
> > > if (bo.Cash >= ((lotSize * sig.Price) / marginMultiplier) && quit == 0) {
> >
> > > sig.PosSize = -2000 - lotSize;
> > > } else if (bo.Cash > 0 && quit == 0) {
> > > sig.PosSize = 0; // Insufficient funds. Cancel signal.
> > > quit = 1;
> > > } else {
> > > sig.PosSize = 0; // Already hit insufficient funds. Cancel remaining
> > signals.
> > > quit++;
> > > }
> > >
> > > if (sig.PosSize < 0) {
> > > msg = "\tEnter " + WriteIf(sig.IsLong(), "Long: ", "Short: ") +
> > sig.Symbol + ", Price: " + sig.Price + ", Shares: " + abs(sig.PosSize +
> > 2000) + " Margin Loan: " + (abs(sig.PosSize + 2000) * sig.Price) * (1 - (1 /
> > marginMultiplier));
> > > result = bo.EnterTrade(bar, sig.Symbol, sig.IsLong(), sig.Price,
> > sig.PosSize);
> > > bo.UpdateStats(bar, 1);
> > > _TRACE(msg + ", Cash Balance: " + bo.Cash + " Result: " + result);
> > > } else if (quit == 1) {
> > > _TRACE("\t" + sig.Symbol + " not entered because of insufficient funds");
> >
> > > }
> > > }
> > > }
> > >
> > > /*
> > > * Conclusion 5:
> > > * Must consult ActivateStopsImmediately to determine whether to process
> > stops
> > > * at end of each bar, after new entries.
> > > */
> > > if (activateStopsImmediately) {
> > > for (sig = bo.GetFirstSignal(bar); sig; sig = bo.GetNextSignal(bar)) {
> > > if (sig.IsExit() && sig.Reason != 1) {
> > > if (pos = bo.FindOpenPos(sig.Symbol)) {
> > > msg = "\tExit (" + sig.Reason + ") " + WriteIf(sig.IsLong(), "Long: ",
> > "Short: ") + sig.Symbol + ", Shares: " + pos.Shares;
> > >
> > > bo.ExitTrade(bar, sig.Symbol, sig.Price);
> > > bo.UpdateStats(bar, 1); // Update running stats after each trade.
> > > _TRACE(msg + ", Cash Balance: " + bo.Cash);
> > > }
> > > }
> > > }
> > > }
> > >
> > > /*
> > > * Conclusion 6:
> > > * HandleStops is obsolete and should no longer be called.
> > > */
> > > msg = ", Ending Cash: " + bo.Cash;
> > > bo.UpdateStats(bar, 2);
> > > _TRACE("\tEnding Equity: " + bo.Equity + msg);
> > > }
> > >
> > > bo.PostProcess();
> > > break;
> > > }
> > > }
> > > }
> > >
> > > Try with each of the following combinations (make changes in the code).
> > The UKB template will always be wrong:
> > >
> > > UsePrevBarEquityForPosSizing: true, ActivateStopsImmediately: true
> > > UsePrevBarEquityForPosSizing: true, ActivateStopsImmediately: false
> > > UsePrevBarEquityForPosSizing: false, ActivateStopsImmediately: true
> > > UsePrevBarEquityForPosSizing: false, ActivateStopsImmediately: false
> > >
> > >
> > > Any attempt at low level custom code cannot be trusted until somebody can
> > actually write low level custom backtester code that can be verified against
> > AmiBroker built in code. I want to experiment with non trivial position
> > sizing. But, I can't do it without a better description of how the low level
> > backtester is supposed to be used.
> > >
> > > I will send this note to support also, if you or Marcin prefer to reply
> > privately. But, I suspect that any reply would be helpful to the whole
> > group.
> > >
> > > Mike
> > >
> > >