CBT-metric-per-symbol-ProfitFactor

I would like to have a custom report with the PF for each symbol
Searching the forum here I found an interesting example
example found
I retouched it and I got this version

	function ProcessTrade( trade )
	{
		/// @link https://forum.amibroker.com/t/custom-backtest-metric-per-symbol-profit-loss-for-a-specific-date-range/11058
		global tradedSymbols;
		symbol = trade.Symbol;
		
		if( ! StrFind( tradedSymbols, "," + symbol + "," ) )
		{
			tradedSymbols += symbol + ",";
		}

		// HINT: you may replace it with GetPercentProfit if you wish
		profit = Nz( trade.GetProfit() );
		
		if( trade.IsLong() )
		{
			varname = "long_" + symbol;
			VarSet( varname, Nz( VarGet( varname ) ) + profit );

			GrossPr = "LngGrosP_" + symbol;
			GrossLs = "LngGrosL_" + symbol;
			if ( profit > 0)
				VarSet( GrossPr, Nz( VarGet( GrossPr ) ) + profit );
			else
				VarSet( GrossLs, Nz( VarGet( GrossLs ) ) + profit );
		}
		else
		{
			varname = "short_" + symbol;
			VarSet( varname, Nz( VarGet( varname ) ) + profit );
			
			GrossPr = "ShrGrosP_" + symbol;
			GrossLs = "ShrGrosL_" + symbol;
			if ( profit > 0 )
				VarSet( GrossPr, Nz( VarGet( GrossPr ) ) + profit );
			else
				VarSet( GrossLs, Nz( VarGet( GrossLs ) ) + profit );
		}
	}
	
	PosQty      = 45;
	InitialEquity = 1000000;
	SetOption("InitialEquity", InitialEquity);
	
	SetPositionSize(InitialEquity/PosQty, spsValue);		// XX mila / posizione
	
	SetOption( "Allowsamebarexit", False );
	SetOption( "UsePrevBarEquityForPosSizing", True );		//attivazione recente
	SetOption( "InterestRate"    , 0);						//attivazione recente
	SetOption( "MaxOpenPositions", PosQty );
	SetOption( "CommissionMode"  ,         3 );				//0 - use portfolio manager commission table 	1 - percent of trade 	2 - $ per trade 	3 - $ per share/contract
	SetOption( "CommissionAmount",  0*55/10000 );				//amount of commission in modes 1..3
	SetOption( "SeparateLongShortRank",True);
	SetOption( "AllowPositionShrinking",True);
	RoundLotSize = 1;										//amount minimum of stock
	TradeDelay = 1;											//set it to 0 for no delays
	SetTradeDelays( TradeDelay, TradeDelay, TradeDelay, TradeDelay );
	
	SetBacktestMode(backtestRegular);			//regular, signal-based backtest, redundant signals are removed as shown in this picture
	

	PositionScore = Ref( MA(Close*Volume, 10) , -1);
	
	// === SetPositionSize === 
	SetPositionSize(  (100-2)/PosQty                                      , spsPercentOfEquity);	// XX % / equity		se % of Equity
	
	// with this kind of signals work correctly
//	Buy   = Signal(12, 26, 9);
//	Cover = Signal(12, 26, 9);
//	Sell  = Signal(12, 26, 9);
//	Short = Signal(12, 26, 9);
	
	// with this kind of signals go on Runtime Error (on a folder with 1000 stocks)
	Buy = 		RSI(2) < 10;
	Short =		RSI(2) > 90;
	Sell =  	RSI(2) > 70;
	Cover =  	RSI(2) < 30;
	
//	Buy  		= ExRem(Buy , Sell);							// remove ex. signals	
//	Sell 		= ExRem(Sell, Buy );							// remove ex. signals	
	BuyPrice 	= Open;
	SellPrice 	= Open;
	ShortPrice	= Open;
	CoverPrice	= Open;
	
	
	SetCustomBacktestProc( "" );
	if  (Status("action") == actionPortfolio)	{			// Mid_Level - Get backtester object  
		bo = GetBacktesterObject();    						// Mid_Level - Get backtester object  
	
		bo.Backtest(); 										// run default backtest procedure
		tradedSymbols = ",";

		//iterate through closed trades
		for( trade = bo.GetFirstTrade( ); trade; trade = bo.GetNextTrade( ) )
		{
			ProcessTrade( trade );
		}

		//iterate through open positions
		for( trade = bo.GetFirstOpenPos( ); trade; trade = bo.GetNextOpenPos( ) )
		{
			ProcessTrade( trade );
		}

		//iterate through the list of traded symbols and generate custom metrics
		for( i = 1; ( sym = StrExtract( tradedSymbols, i ) ) != ""; i++ )
		{
			longprofit  = VarGet( "long_"  + sym );
			shortprofit = VarGet( "short_" + sym );
			allprofit   = Nz( longprofit ) + Nz( shortprofit );
			
			// metric uses 2 decimal points and 3 (calculate sum) as a "combine method" for walk forward out-of-sample
			bo.AddCustomMetric( "Profit for " + sym, allprofit, longprofit, shortprofit, 2, 3 );
			
			Lng_Prof = Nz( VarGet( "LngGrosP_" + sym ) );
			Lng_Loss = Nz( VarGet( "LngGrosL_" + sym ) );
			Shr_Prof = Nz( VarGet( "ShrGrosP_" + sym ) );
			Shr_Loss = Nz( VarGet( "ShrGrosL_" + sym ) );
			All_Prof = Nz( Lng_Prof ) + Nz( Shr_Prof );
			All_Loss = Nz( Lng_Loss ) + Nz( Shr_Loss );
			
			All_Pf   = Nz( IIf( All_Loss != 0, -All_Prof/All_Loss, 100) );
			Lng_Pf   = Nz( IIf( Lng_Loss != 0, -Lng_Prof/Lng_Loss, 100) );
			Shr_Pf   = Nz( IIf( Shr_Loss != 0, -Shr_Prof/Shr_Loss, 100) );
			if ( All_Pf > 1)
			{
				bo.AddCustomMetric( "PF > 1.5 for " + sym, All_Pf, Lng_Pf, Shr_Pf, 2, 3 );
			}

		}
	}	
	
	

and under error return me this message :pensive:

Can someone kindly guide me on the direction to solve the problem?
It all seems correct to me
I also checked the data with "Tools Database Purify"

Here follow code error

AmiBroker version 6.21.0.6210
( 64-bit, cooltool.dll 6.21.0, mfc42.dll 6.21.0, msvcrt.dll 7.0.17763 )

Microsoft Windows 10 version 10.0 (Build 17763)
Service Pack 0.0, Common Controls: 6.16

Unhandled exception
Type: COleException
Parametro non corretto.

Additional information:

Multi-threaded charts - ENABLED

Number of stock loaded: 14982
Currently selected stock: SPY_NY
Number of quotes (current stock): 7565

Workspace:
Data source = MSTK, Data local mode = 1, NumBars = 10000

Preferences:
Data source = (local), Data local mode = 1, NumBars = 1000

Command history:
57616 - Open this file

Cache manager stats:
Number of list elements: 990
Number of map elements: 990
Hash table size: 5987

Memory status:
MemoryLoad: 52 %
TotalPhys: 6290996K AvailPhys: 2980860K
TotalPageFile: 7339572K AvailPageFile: 4296312K
TotalVirtual: 4294967168K AvailVirtual: 4289807304K

Last Windows message:
HWnd: 0x17e0b16
Msg: 0x0110
wParam: 0x012c0dea
lParam: 0x00000000

Log:
Logging started 2023-02-14 17:24:32
8.57 ms : Enabling low frag heap (0.05 ms)
8.62 ms : Launching splash screen (5.33 ms)
13.95 ms : Waiting for splash screen (8.90 ms)
22.85 ms : Waiting for splash screen (7.97 ms)
30.82 ms : Waiting for splash screen (9.04 ms)
39.86 ms : Waiting for splash screen (7.99 ms)
47.86 ms : Waiting for splash screen (8.97 ms)
56.83 ms : Waiting for splash screen (8.00 ms)
64.83 ms : Waiting for splash screen (9.52 ms)
74.36 ms : Alloc other stuff (5.08 ms)
79.44 ms : Loading amisci.dll (28.41 ms)
107.85 ms : Setting up SEH translator (1227.83 ms)
1335.68 ms : Initializing OLE (8.61 ms)
1344.30 ms : Initializing RichEdit (76.98 ms)
1421.28 ms : Checking current working directory (2.18 ms)
1423.45 ms : Loading persistent variables (376.13 ms)
1799.59 ms : Loading MRU lists (77.40 ms)
1876.99 ms : Loading commisison table (0.23 ms)
1877.21 ms : Loading preferences (46.52 ms)
1923.74 ms : Initializing display settings (0.00 ms)
1923.74 ms : Loading old groups and markets (0.00 ms)
1923.74 ms : Loading GICS and ICB (0.00 ms)
1923.74 ms : Loading plugins (1639.35 ms)
3563.09 ms : Loading AT interfaces (0.22 ms)
3563.31 ms : Loading AFL function table (1705.28 ms)
5268.59 ms : Init chart infos (4.12 ms)
5272.70 ms : Loading parameters (27.34 ms)
5300.04 ms : Loading chart infos (29.24 ms)
5329.28 ms : Loading custom tools (3.46 ms)
5332.74 ms : Allocating lists (0.55 ms)
5333.29 ms : Loading layers and alerts (27.03 ms)
5360.32 ms : Loading miscellaneous data (0.15 ms)
5360.47 ms : Adding MDI templates (17.25 ms)
5377.71 ms : Register OLE server (6.53 ms)
5384.25 ms : Creating main frame object (87.43 ms)
5471.68 ms : Loading main frame (938.96 ms)
6410.64 ms : Parsing command line (0.05 ms)
6410.69 ms : Checking Broker.Document object registration (3.05 ms)
6413.74 ms : Dispatch commands via ProcessShellCommand (0.00 ms)
6413.74 ms : Showing main frame window (259.38 ms)
6673.12 ms : Setting up accelerators (0.45 ms)
6673.56 ms : Loading database (LoadMarketData) (2194.02 ms)
8867.58 ms : Setting active symbol (737.21 ms)
9604.80 ms : Opening default chart (375.38 ms)
9980.18 ms : Loading default workspace/layout (338.14 ms)
10318.32 ms : Closing startup splash screen (async in 1 second) (1.70 ms)
10320.02 ms : Starting up schedule (0.72 ms)
10320.74 ms : *InitInstance finished

image

Typically it means that somewhere you have passed incorrect date time, either in string form, or your input data MS contain invalid dates.

Instead of reinventing the wheel why don’t you run individual back test instead. It will produce individual PF for each symbol under test automatically without need to write any code.

I also advise using current version, 6.40+ because it would give more detailed exception report

@flaviog2005, FWIW, I tested the posted formula, on multiple symbols, watchlists, entire databases, using AB 6.20.1 32-bit and 6.30.5 64-bit and was not able to reproduce the error you reported.
(The only issue I saw during the CBT phase was about an Error 15. Endless loop detected in FOR loop that was quickly fixed increasing the value in the Preferences dialog - AFL tab - endless loop detection threshold; judging by the text reported in the error window it occurred during ProcessTrade).

To better understand if your issue is specifically linked to your database/tickers, have you tested the code using the default database provided by Amibroker during installation?

As I wrote - most likely is bad data in MS format.

Tomasz, hit the mark!

I haven't found the data yet but I have detected that it only does this with a folder

Thank you for guiding me in the search for the solution !!

If you find incorrect data file/folder, you might consider sending it to me (to support email), for checking so I can try to find out what exact record was the culprit and how to handle it more gracefully.

1 Like

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