Final AmiBroker Code Template

เพื่อสนับสนุนวงการลงทุนด้วยระบบเทรดในเมืองไทยให้ก้าวไปข้างหน้าในระดับสากล ด้วยการให้ความรู้แก่ นลท สายควอนทฺ (Quants) ในการเขียน AmiBroker AFL CODE ให้โค้ดถูกต้อง โค้ดครบถ้วน และโค้ดเป็นระเบียบ (ลำดับโค้ดสำคัญมาก) ซึ่งจะช่วยให้สามารถพัฒนากลยุทธ์/ระบบเทรด (Strategies และ Trading System) อย่างมีประสิทธิภาพและต่อยอดได้ง่ายขึ้น อีกทั้งยังทำให้ เรา นลท สายควอนทฺ พูดจาภาษาเดียวกัน ใช้โครงสร้างโค้ดคล้ายๆกัน ด้วยเทมเพลต (template) ที่ได้ถูกออกแบบสำหรับการสร้าง Trading Platform โดยเฉพาะ

หมายเหตุ: No Copyright or Any Rights Reserved ไม่สงวนลิขสิทธิ์ใดๆทั้งสิ้น ผู้ที่สนใจสามารถนำไปใช้ได้เลย ไม่ต้องขออนุญาติ ไม่ต้องอ้างอิงถึงก็ได้ ถ้าไม่สะดวก

 

Template จะแบ่งออกได้เป็น 7 ส่วน (สีเข้ม คือ ส่วนที่ต้องใช้แน่นอน)

  1. OPTIONS ตั้งค่าสำคัญต่างๆที่ต้องใช้ในการทดสอบ Backtest ซึ่งส่วนใหญ่อยู่ในรูปแบบของตัวเลข
  2. MARKET ANALYSIS ใช้สำหรับวิเคราะห์หาสภาพตลาดที่เอื้ออำนวนต่อการลงทุน
  3. SIGNALS สัญญาณซื้อขาย Buy และ Sell ซึ่งจะถูกเขียนในรูปแบบเงื่อนไข Conditions
  4. POSITIONS เป็นส่วนของการบริหารหน้าตัก รวมถึงเรียงลำดับความสำคัญของสัญญาณเข้าซื้อ
  5. STOPS เป็นส่วนของการขายหุ้นในพอร์ต ที่ไม่ได้มาจาก Sell Signals เช่น การคัทลอส และการขายล๊อคกำไร
  6. SIMULATION & RISK เป็นการผนวกความผันผวน ความเสี่ยงเข้าไปในการทดสอบ และ/หรือจำลองการเทรด
  7. CUSTOM BACKTEST เป็นการเขียนโค้ด AmiBroker ขั้นกลาง-สูง เพื่อให้ได้การวิเคราะห์ที่ละเอียดขึ้น

และใน template ยังมีโค้ดจิ๋วสั้นๆ (code snippets) ที่มีหน้าที่เฉพาะเจาะจง มักจำเป็นต้องใช้ในทุกกลยุทธ์

  • โค้ดจิ๋วเลี่ยงหุ้นแตกพาร์ Avoid stock split
  • โค้ดจิ๋วเลี่ยงหุ้นเพิกถอนและหุ้นที่ถูกเปลี่ยนชื่อ Avoid stock delisted and renamed
  • โค้ดจิ๋วปรับ tick size ตามช่วงระดับราคาหุ้น
  • โค้ดจิ๋วจำลองการพลาดการเข้าซื้อหุ้น (Missing Trades)
  • โค้ดจิ๋วจำลองการ slippage ด้วย Random ฟังก์ชั่น

 

CONCISE TEMPLATE แบบกระชับ

เหมาะสำหรับผู้เริ่มต้น และ/หรือผู้ที่ได้เรียน ABEC ซึ่ง template ตัวนี้จะละเว้นในส่วนของ Market Analysis และ Simulation รวมถึงโค้ดจิ๋วปรับ tick size (ที่มีผลกระทบเพียงเล็กน้อย)

/** CONCISE TEMPLATE with #required info
	No Copyright or Any Rights Reserved
	@link https://bit.ly/tq-template
*/

{//1. OPTIONS
	MOP = #required;
	SetOption("MaxOpenPositions", MOP);
	SetOption("InitialEquity", #required);
	SetOption("CommissionMode", 1);
	SetOption("CommissionAmount", 0.2);
	SetOption("AllowPositionShrinking", 1);
	RoundLotSize = 100;
	
	BuyPrice = SellPrice = Open;

	signalDelay = #required;
	SetTradeDelays(signalDelay, signalDelay, 0, 0);
}

{//2. MARKET ANALYSIS (empty)
}

{//3. SIGNALS
	buyCon1 = #required;
	buyCon2 = #required;
	Buy = buyCon1 AND buyCon2;
	
	sellCon1 = #required;
	sellCon2 = #required;
	Sell = sellCon1 AND sellCon2;

	Short = 0; Cover = 0;
	
	{//Avoid stock split (Don't change)	
		C0 = Ref(C, 0); C1 = Ref(C, 1); C2 = Ref(C, 2);
		difC1C0 = abs( (C1 - C0)/C0 ) * 100;
		difC2C1 = abs( (C2 - C1)/C1 ) * 100;
		detectSplit = 0.35 * 100;
		buyConAvoidSplit = !(difC1C0 > detectSplit) AND !(difC2C1 > detectSplit);
		sellConAvoidSplit = (difC1C0 > detectSplit) OR   (difC2C1 > detectSplit);   
	
		Buy &= buyConAvoidSplit;
		Sell |= sellConAvoidSplit;
	}

	{//Avoid stock delisted and renamed	(Don't change)	
		buyConAvoidDelisted = BarIndex() < (LastValue(BarIndex()) - signalDelay - 1);
		sellConAvoidDelisted = BarIndex() >= (LastValue(BarIndex()) - signalDelay - 1);
		Buy &= buyConAvoidDelisted;
		Sell |= sellConAvoidDelisted;
	}

}

{//4. POSITIONS
	PositionScore = #required;
	SetPositionSize(100/MOP, spsPercentOfEquity);
}

{//5. STOPS
	stopLoss = #required;
	ApplyStop(stopTypeLoss, stopModePercent, stopLoss);
}

{//6. SIMULATION AND RISK
	slippage = #required;
	BuyPrice = BuyPrice*(1 + slippage/100); 
	SellPrice = SellPrice*(1 - slippage/100);
}

{//7. CUSTOM BACKTEST (empty)
}

 

FULL TEMPLATE แบบเต็ม สำหรับคนทั่วไป

เหมาะสำหรับผู้ที่มีประสบการณ์ในระดับหนึ่ง หรือผู้ที่ได้เรียน ABQC ซึ่งมีความรู้ในการทำ Market Analysis และการทำ Simulation ใน AmiBroker แล้ววิเคราะห์ผลลัพธ์ที่ได้ด้วย Excel เพื่อหาค่าสำคัญที่ Confidence Level ระดับต่างๆ

/** FULL TEMPLATE with #required info
	No Copyright or Any Rights Reserved
	@link https://bit.ly/tq-template
*/

{//1. OPTIONS
	MOP = #required;
	SetOption("MaxOpenPositions", MOP);
	SetOption("InitialEquity", #required);
	SetOption("CommissionMode", 1);
	SetOption("CommissionAmount", 0.2);
	SetOption("AllowPositionShrinking", 1);
	//SetOption("UsePrevBarEquityForPosSizing", 1);
	//SetOption("MinPosValue", 10000);
	//SetOption("MinShares", 100);
	RoundLotSize = 100;
	
	BuyPrice = SellPrice = Open;

	signalDelay = #required;
	SetTradeDelays(signalDelay, signalDelay, 0, 0);
}

{//2. MARKET ANALYSIS
	SetForeign("SET");
		buyConMK = #required;
	RestorePriceArrays();
}

{//3. SIGNALS
	buyCon1 = #required;
	buyCon2 = #required;
	Buy = buyConMK AND buyCon1 AND buyCon2;
	
	sellCon1 = #required;
	sellCon2 = #required;
	Sell = sellCon1 AND sellCon2;

	Short = 0; Cover = 0;
	
	{//Avoid stock split (Don't change)	
		C0 = Ref(C, 0); C1 = Ref(C, 1); C2 = Ref(C, 2);
		difC1C0 = abs( (C1 - C0)/C0 ) * 100;
		difC2C1 = abs( (C2 - C1)/C1 ) * 100;
		detectSplit = 0.35 * 100;
		buyConAvoidSplit = !(difC1C0 > detectSplit) AND !(difC2C1 > detectSplit);
		sellConAvoidSplit = (difC1C0 > detectSplit) OR   (difC2C1 > detectSplit);   
	
		Buy &= buyConAvoidSplit;
		Sell |= sellConAvoidSplit;
	}

	{//Avoid stock delisted and renamed	(Don't change)	
		buyConAvoidDelisted = BarIndex() < (LastValue(BarIndex()) - signalDelay - 1);
		sellConAvoidDelisted = BarIndex() >= (LastValue(BarIndex()) - signalDelay - 1);
		Buy &= buyConAvoidDelisted;
		Sell |= sellConAvoidDelisted;
	}
}

{//4. POSITIONS
	PositionScore = #required;
	SetPositionSize(100/MOP, spsPercentOfEquity);
}

{//5. STOPS
	stopLoss = #required;
	ApplyStop(stopTypeLoss, stopModePercent, stopLoss);
	//ApplyStop(stopTypeTrailing, stopModePercent, #required);
	//ApplyStop(stopTypeProfit, stopModePercent, #required);
	//ApplyStop(stopTypeNBar, stopModeBars, #required);
}

{//6. SIMULATION AND RISK
	missingTrades = #required;
	Buy &= IIf(Random() < 1 - missingTrades/100, 1, 0);
	Sell &= IIf(Random() < 1 - missingTrades/100, 1, 0);
	
	slippage = #required;
	BuyPrice = BuyPrice*(1 + slippage*Random()/100); 
	SellPrice = SellPrice*(1 - slippage*Random()/100);
	
	{//Correct tick size (Don't change)
		//avgPrice = (Open + Close)/2;
		//avgPrice = (Low + High)/2;
		avgPrice = Open;
		TickSize = 	IIf(avgPrice < 2, 0.01,
					IIf(avgPrice < 5, 0.02,
					IIf(avgPrice < 10, 0.05,
					IIf(avgPrice < 25, 0.10,
					IIf(avgPrice < 100, 0.25,
					IIf(avgPrice < 200, 0.50,
					IIf(avgPrice < 400, 1.00,
						2.00 )))))));

		BuyPrice = Prec(ceil(BuyPrice/TickSize)*TickSize,2);
		SellPrice = Prec(floor(SellPrice/TickSize)*TickSize,2);
	}
}

{//7. CUSTOM BACKTEST (empty)
}

FINAL TEMPLATE with Simulation Runs

/** FINAL TEMPLATE with Simulation Runs and #required info
	No Copyright or Any Rights Reserved
	@link https://bit.ly/tq-template
*/

{//1. OPTIONS
	MOP = #required;
	SetOption("MaxOpenPositions", MOP);
	SetOption("InitialEquity", #required);
	SetOption("CommissionMode", 1);
	SetOption("CommissionAmount", 0.2);
	SetOption("AllowPositionShrinking", 1);
	//SetOption("UsePrevBarEquityForPosSizing", 1);
	//SetOption("MinPosValue", 10000);
	//SetOption("MinShares", 100);
	RoundLotSize = 100;
	
	BuyPrice = SellPrice = Open;

	signalDelay = #required;
	SetTradeDelays(signalDelay, signalDelay, 0, 0);
}

{//2. MARKET ANALYSIS
	SetForeign("SET");
		buyConMK = #required;
	RestorePriceArrays();
}

{//3. SIGNALS
	buyCon1 = #required;
	buyCon2 = #required;
	Buy = buyConMK AND buyCon1 AND buyCon2;
	
	sellCon1 = #required;
	sellCon2 = #required;
	Sell = sellCon1 AND sellCon2;

	Short = 0; Cover = 0;
	
	{//Avoid stock split (Don't change)	
		C0 = Ref(C, 0); C1 = Ref(C, 1); C2 = Ref(C, 2);
		difC1C0 = abs( (C1 - C0)/C0 ) * 100;
		difC2C1 = abs( (C2 - C1)/C1 ) * 100;
		detectSplit = 0.35 * 100;
		buyConAvoidSplit = !(difC1C0 > detectSplit) AND !(difC2C1 > detectSplit);
		sellConAvoidSplit = (difC1C0 > detectSplit) OR   (difC2C1 > detectSplit);   
	
		Buy &= buyConAvoidSplit;
		Sell |= sellConAvoidSplit;
	}

	{//Avoid stock delisted and renamed	(Don't change)	
		buyConAvoidDelisted = BarIndex() < (LastValue(BarIndex()) - signalDelay - 1);
		sellConAvoidDelisted = BarIndex() >= (LastValue(BarIndex()) - signalDelay - 1);
		Buy &= buyConAvoidDelisted;
		Sell |= sellConAvoidDelisted;
	}
}

{//4. POSITIONS
	PositionScore = #required;
	SetPositionSize(100/MOP, spsPercentOfEquity);
}

{//5. STOPS
	stopLoss = #required;
	ApplyStop(stopTypeLoss, stopModePercent, stopLoss);
	//ApplyStop(stopTypeTrailing, stopModePercent, #required);
	//ApplyStop(stopTypeProfit, stopModePercent, #required);
	//ApplyStop(stopTypeNBar, stopModeBars, #required);
}

{//6. SIMULATION AND RISK
	simRuns = #required;
	missingTrades = #required;
	slippage = #required;
	
	if(simRuns){//Simulation runs (Don't change)
		simRuns = Optimize("simRuns", 1, 1, simRuns, 1);
		{//Missing Trades
			Buy &= IIf(Random() < 1 - missingTrades/100, 1, 0);
			Sell &= IIf(Random() < 1 - missingTrades/100, 1, 0);
		}
		
		{//Slippage
			BuyPrice = BuyPrice*(1 + slippage*Random()/100); 
			SellPrice = SellPrice*(1 - slippage*Random()/100);
		}
	}
	
	{//Correct tick size (Don't change)
		//avgPrice = (Open + Close)/2;
		//avgPrice = (Low + High)/2;
		avgPrice = Open;
		TickSize = 	IIf(avgPrice < 2, 0.01,
					IIf(avgPrice < 5, 0.02,
					IIf(avgPrice < 10, 0.05,
					IIf(avgPrice < 25, 0.10,
					IIf(avgPrice < 100, 0.25,
					IIf(avgPrice < 200, 0.50,
					IIf(avgPrice < 400, 1.00,
						2.00 )))))));

		BuyPrice = Prec(ceil(BuyPrice/TickSize)*TickSize,2);
		SellPrice = Prec(floor(SellPrice/TickSize)*TickSize,2);
	}
}

{//7. CUSTOM BACKTEST (empty)
}

 

ตัวอย่าง Win 62%; CAR 24%;  MDD -15% (ต้น 2010 ถึงสิ้น 2019)

เหมาะสำหรับนำไปศึกษาองค์ประกอบโค้ดต่างๆใน Template รวมถึงศึกษารูปแบบกลยุทธ์ที่ใช้ Market Analysis เป็นหลัก (มิใช่การใช้ indicators) ในการซื้อขายหุ้นทุกๆต้นเดือน ซึ่งถือว่าเป็นท่าไม้ตาย

/** EXAMPLE from FULL TEMPLATE
	No Copyright or Any Rights Reserved
	@link https://bit.ly/tq-template
*/

{//1. OPTIONS
	MOP = 10;
	SetOption("MaxOpenPositions", MOP);
	SetOption("InitialEquity", 100000);
	SetOption("CommissionMode", 1);
	SetOption("CommissionAmount", 0.2);
	SetOption("AllowPositionShrinking", 1);
	//SetOption("UsePrevBarEquityForPosSizing", 1);
	//SetOption("MinPosValue", 10000);
	//SetOption("MinShares", 100);
	RoundLotSize = 100;
	
	BuyPrice = SellPrice = Open;

	signalDelay = 1;
	SetTradeDelays(signalDelay, signalDelay, 0, 0);
}

{//2. MARKET ANALYSIS
	periodMK = 60;
	SetForeign("SET");
		buyConMK = High > Ref(HHV(Close, periodMK),-1);
	RestorePriceArrays();
}

{//3. SIGNALS
	firstDayOfMonth = Month() != Ref(Month(),-1);
	buyCon1 = firstDayOfMonth;
	buyCon2 = MA(V*C, periodMK) >= 10*1000000;
	Buy = buyConMK AND buyCon1 AND buyCon2;// AND C < 50;
	
	sellCon1 = !buyConMK;
	sellCon2 = firstDayOfMonth;
	Sell = sellCon1 AND sellCon2;// AND diffCloseToMaSET > 1;

	Short = 0; Cover = 0;
	
	{//Avoid stock split (Don't change)	
		C0 = Ref(C, 0); C1 = Ref(C, 1); C2 = Ref(C, 2);
		difC1C0 = abs( (C1 - C0)/C0 ) * 100;
		difC2C1 = abs( (C2 - C1)/C1 ) * 100;
		detectSplit = 0.35 * 100;
		buyConAvoidSplit = !(difC1C0 > detectSplit) AND !(difC2C1 > detectSplit);
		sellConAvoidSplit = (difC1C0 > detectSplit) OR   (difC2C1 > detectSplit);   
	
		Buy &= buyConAvoidSplit;
		Sell |= sellConAvoidSplit;
	}

	{//Avoid stock delisted and renamed	(Don't change)	
		buyConAvoidDelisted = BarIndex() < (LastValue(BarIndex()) - signalDelay - 1);
		sellConAvoidDelisted = BarIndex() >= (LastValue(BarIndex()) - signalDelay - 1);
		Buy &= buyConAvoidDelisted;
		Sell |= sellConAvoidDelisted;
	}
}

{//4. POSITIONS
	PositionScore = H/Ref(Highest(C),-1);
	SetPositionSize(100/MOP, spsPercentOfEquity);
}

{//5. STOPS
	stopLoss = 15;
	ApplyStop(stopTypeLoss, stopModePercent, stopLoss);
	//ApplyStop(stopTypeTrailing, stopModePercent, #required);
	//ApplyStop(stopTypeProfit, stopModePercent, #required);
	//ApplyStop(stopTypeNBar, stopModeBars, #required);
}

{//6. SIMULATION AND RISK
	//missingTrades = 10;
	//Buy &= IIf(Random() < 1 - missingTrades/100, 1, 0);
	//Sell &= IIf(Random() < missingTrades/100, 1, 0);
	
	//slippage = 2;
	//BuyPrice = BuyPrice*(1 + slippage*Random()/100); 
	//SellPrice = SellPrice*(1 - slippage*Random()/100);
	
	{//Correct tick size (Don't change)
		//avgPrice = (Open + Close)/2;
		//avgPrice = (Low + High)/2;
		avgPrice = Open;
		TickSize = 	IIf(avgPrice < 2, 0.01,
					IIf(avgPrice < 5, 0.02,
					IIf(avgPrice < 10, 0.05,
					IIf(avgPrice < 25, 0.10,
					IIf(avgPrice < 100, 0.25,
					IIf(avgPrice < 200, 0.50,
					IIf(avgPrice < 400, 1.00,
						2.00 )))))));

		BuyPrice = Prec(ceil(BuyPrice/TickSize)*TickSize,2);
		SellPrice = Prec(floor(SellPrice/TickSize)*TickSize,2);
	}
}

{//7. CUSTOM BACKTEST (empty)
}

 

One for All & All for One