How to properly use Name() to create file name?

I am amused at how I am able to produce two files with the below code while I want only one. The question is how to properly use Name() function to get only the symbol the formula is run for in the file name and thus only one file?

VarSetText("symbol", Name());
_TRACE("VarGetText symbol: " + VarGetText("symbol"));
symbol = VarGetText("symbol");
_TRACE("symbol: " + symbol);
strategyName = "DESCRIPTIVE_STRATEGY_NAME";
direction = "LongAndShort";
folder = "C:\\tmp\\Trading\\Systems\\";
filepath = folder + strategyName + "_" + symbol + "_" + direction + "_Trades.csv";
mode = "w";

fh = fopen(filepath, mode, True);

SetCustomBacktestProc("");
if (Status("action") == actionPortfolio) {
	bo = GetBacktesterObject();
	bo.Backtest();
}

Buy = Sell = Short = Cover = 0;
BuyPrice = SellPrice = ShortPrice = CoverPrice = 0;

fputs(symbol +  "\n", fh);

fclose(fh);

I get the following output:
image

I can of course notice that if I remove the custom backtester code my (almost all) "problems" go away. To my untrained newbie eye it looks like when I push "Backtest" formula/code is run twice...

Above is just a snippet of reproducible code. The aim is to use custom backtester to export the trades to csv file.

I know this is all with probability one documented on page 17256394 of AFL manual and in KB 9236572923 and I am to dumb and/or lazy to look it up, however, I still can not resist the urge to ask the experts for explanation of the above phenomenon and what to do to get what I want. Thank you.

It might become obvious to me in a couple of thousand years that if I would have learned how to properly use AFL and do the RTFM I would never feel the desire (which is, I agree, the root of all my suffering) to export trades into csv but would be able to do all the analysis in Amibroker. I am not at that stage yet, so this kind of advice is not helpful at this stage of the learning curve. Thanks for understanding.

When you run portfolio backtest WITH CUSTOM backtest code, your formula is executed at least twice - during FIRST PASS of backtesting - one time for every symbol included in the "Apply To" selection, and during SECOND PASS - actual portfolio backtesting / custom backtest that uses ~~~EQUITY as selected symbol.

You should NOT call your file creation functions when you are in portfolio backtest (actionPortfolio).

@kktrader, I think your example is not representative of your goal, as I understand you planned to write to the file "inside" the custom backtest block and not outside of it as you did in your example.

Here's something I quickly put together (I haven't tested extensively) that you can adapt to your needs.

Note that it creates a single file for each symbol in your trade list:

VarSetText( "symbol", Name() );
_TRACE( "VarGetText symbol: " + VarGetText( "symbol" ) );
symbol = VarGetText( "symbol" );
_TRACE( "symbol: " + symbol );
strategyName = "DESCRIPTIVE_STRATEGY_NAME";
direction = "LongAndShort";
folder = "C:\\tmp\\";

_TRACEF("Delete existing file for %s", Name()); // delete in the first pass
filepath = folder + strategyName + "_" + NAME() + "_" + direction + "_Trades.csv";
fdelete(filepath);

SetCustomBacktestProc( "" );
/* Custom-backtest procedure follows */
if( Status( "action" ) == actionPortfolio )
{
    bo = GetBacktesterObject();

    bo.Backtest(); // run default backtest procedure

    for( trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() )
    {
    
        _TRACEF("%s - Symbol = %s", Name(), trade.Symbol	); //  Use Trade object here
		filepath = folder + strategyName + "_" + trade.symbol + "_" + direction + "_Trades.csv";
		mode = "a";
		fh = fopen( filepath, mode, True );
        if (fh) {
            // adapt as needed
			fputs( trade.symbol + " In: " + DateTimeToStr(trade.EntryDateTime) + " - Out: " + DateTimeToStr(trade.ExitDateTime) + "\n", fh );
			fclose( fh );
		}
    }

    for( trade = bo.GetFirstOpenPos(); trade; trade = bo.GetNextOpenPos() )
    {
        _TRACEF("%s -Open Trade Symbol: %s = ", Name(), trade.Symbol	); //	//  Use Trade object here
		filepath = folder + strategyName + "_" + trade.symbol + "_" + direction + "_Trades.csv";
		mode = "a";
		fh = fopen( filepath, mode, True );
        if (fh) {
            // adapt as needed
			fputs( trade.symbol + " In: " + DateTimeToStr(trade.EntryDateTime) + " - Still Open\n", fh );
			fclose( fh );
		}
    }
}

// A fake trade system to test....
period = 20; // number of averaging periods
m = EMA( Close, period ); // exponential moving average
Buy = Cross( Close, m ); // buy when close crosses ABOVE moving average
Sell = Cross( m, Close ); // sell when closes crosses BELOW moving average

As said, the code that seems to work for me, may contains errors, so take it with a big grain of salt!

My example seems to go against what was recommended above by @Tomasz:

You should NOT call your file creation functions when you are in portfolio backtest

the reason escapes me but keep in mind that there may be side effects that I did not foresee.

1 Like

You do not need code as above to get just one trade list file.
Trades are available in HTML backtest report already.

Go to Reports folder of Amibroker main directory. There you find your reports with trades Html file which can be imported elsewhere.

Or even simpler.... Go to File - Export to HTML/CSV after analysis was run. And you can even use Batch to export.

But of course lets rather make it difficult with lots of code and words.

Thanks.

This is all fine if you want (and can) click on the GUI. I can not. I have to many systems/formulas to test across to many symbols.

I was asking the other day how to properly organize .afl files and testing of many AFL formulas (representing long/short "systems") across many symbols so to see how they behave as a "portfolio".

I got 0 (zero) answers. So after 0 answers I am now trying to re-invent the wheel by myself. And being capable to automatically export all the output without touching the GUI using OLE is the only thing that looks promising to me(!).

Now, that is me, inexperienced in AFL and Amibroker. I am sure there is a proper, tested, proven way how to approach this. If you know how to approach this, please let me know or point me to the resource where I can read about this. Thanks.

Tomasz, thanks.

Now I am really confused.

How should I approach to export trade related data in a proper way then? When and how to properly construct the file name?

Here is my code attempt up to this point:

procedure writeTradesToDisk1(symbol, strategyName, direction, filepath, includeOpenTrades, includeHeader, deletFileBoferWriting, mode) {
	SetCustomBacktestProc("");
	if (Status("action") == actionPortfolio) {
		bo = GetBacktesterObject();
		bo.Backtest();
	   
	    if (deletFileBoferWriting) fdelete(filepath);
	    
	    // open file in "share-aware" append mode
        fh = fopen(filepath, mode, True);
        
        if(fh) {
			if (includeHeader) {
				fputs("Symbol,isLong,EntryDate,EntryTime,EntryPrice,ExitDate,ExitTime,ExitPrice,PnL,PnLPct,MAE,MFE,EntryValue,BarsInTrade,Shares,PointValue,TickSize,IsOpen\n", fh);
			}
			
			// iterate closes trades
			for (trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade()) {
				fputs(trade.Symbol + "," + 
					  StrFormat("%1.0f", IIf(trade.IsLong, 1, -1)) + "," + 
					  DateTimeFormat("%Y%m%d", trade.EntryDateTime) + "," + 
					  DateTimeFormat("%H%M%S", trade.EntryDateTime) + "," + 
					  StrFormat("%1.5f", trade.EntryPrice) + "," + 
					  DateTimeFormat("%Y%m%d", trade.ExitDateTime) + "," + 
					  DateTimeFormat("%H%M%S", trade.ExitDateTime) + "," + 
					  StrFormat("%1.5f", trade.ExitPrice) + "," + 
					  StrFormat("%1.2f", trade.GetProfit()) + "," +
					  StrFormat("%1.2f", trade.GetPercentProfit()) + "," +
					  StrFormat("%1.2f", trade.GetMAE()) + "," +
					  StrFormat("%1.2f", trade.GetMFE()) + "," +
					  StrFormat("%1.2f", trade.GetEntryValue()) + "," +
					  StrFormat("%1.2f", trade.BarsInTrade) + "," +
					  StrFormat("%1.2f", trade.Shares) + "," +
					  StrFormat("%1.2f", trade.PointValue) + "," +
					  StrFormat("%1.5f", trade.TickSize) + "," +
					  trade.IsOpen +  
					  "\n", 
					  fh);
			}

			if (includeOpenTrades) {
				// iterate open trades
				for (trade = bo.GetFirstOpenPos(); trade; trade = bo.GetNextOpenPos()) {
					 fputs(trade.Symbol + "," + 
					   	   StrFormat("%1.0f", IIf(trade.IsLong, 1, -1)) + "," + 
						   DateTimeFormat("%Y%m%d", trade.EntryDateTime) + "," + 
						   DateTimeFormat("%H%M%S", trade.EntryDateTime) + "," + 
						   StrFormat("%1.5f", trade.EntryPrice) + "," + 
						   DateTimeFormat("%Y%m%d", trade.ExitDateTime) + "," + 
						   DateTimeFormat("%H%M%S", trade.ExitDateTime) + "," + 
						   StrFormat("%1.5f", trade.ExitPrice) + "," + 
						   StrFormat("%1.2f", trade.GetProfit()) + "," +
						   StrFormat("%1.2f", trade.GetPercentProfit()) + "," +
						   StrFormat("%1.2f", trade.GetMAE()) + "," +
						   StrFormat("%1.2f", trade.GetMFE()) + "," +
						   StrFormat("%1.2f", trade.GetEntryValue()) + "," +
						   StrFormat("%1.2f", trade.BarsInTrade) + "," +
						   StrFormat("%1.2f", trade.Shares) + "," +
						   StrFormat("%1.2f", trade.PointValue) + "," +
						   StrFormat("%1.5f", trade.TickSize) + "," +
						   trade.IsOpen +    
						   "\n", 
						   fh);
				}
			}
			
			fclose(fh);
		} else {
            _TRACE("Failed to open the file ", filepath);
        }
	}
}

I can not simply change if (Status("action") == actionPortfolio) into if (Status("action") == actionBacktest) ...

As I said there are three options without code.

  1. File - Export to...
  2. Batch export
    or
  3. Reports folder.

As for latter one you can create custom Reports folder names including your analysed symbol name

At the top of your AFL code add just two lines

SetFormulaName("System1_"+ Name());
SetOption("GenerateReport", 1 ); 

Then run individual backtest.

And reports folders look like this for example

38

Each folder contains separate trade list named trades.html.

Why doing it complicated if it is simple?

2 Likes

@kktrader I was mainly answering your question why are you getting two files. That was only purpose of my answer.

And quite frankly @fxshrat solution is best (no code)

@kktrader,
You can use Excel VBA to parse the html reports in the report folders into Excel spreadsheet in a single Excel VBA run. If you are not familiar with Excel VBA script, you can use ChatGPT to guide you.