Created
November 13, 2019 17:21
-
-
Save distagon/f4841c936872e839dbed3bf9ebca47a9 to your computer and use it in GitHub Desktop.
Simple AmiBroker stock market system template. Things to be mindful of when designing your system.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// IMPORTANT! When ready to trade your AB system set the following settings: | |
// Report > Detailed Log | |
// Portfolio > Tick "Add artificial future bar" | |
// | |
// Set the From-To dates of the Backtest to the current month (eg. 1/Jan/16-1/Feb/16). | |
// Observe the last row to get your instructions for the next trading day. | |
// I like placing all variables that can be changed and optimised at the top. | |
// Then when the time comes to optimise, I create a simple: | |
// a = Optimize( "a", 1, 1, 10, 1 ); | |
// And then change the reference below to `a`. (eg. maxOpenPositions = a) | |
maxOpenPositions = 3; | |
maxOpenLongs = 3; | |
maxOpenShorts = 0; | |
minValueTrading = 1000000; | |
barsValue = 30; | |
riskPerTrade = 500; | |
// To minimise "selection bias" whenever the system triggers multiple | |
// entries, it is best to manually control the maximum number of | |
// allowable positions and the rank of which stocks are selected according | |
// to a predetermined score. See `PositionScore` below. | |
SetOption( "MaxOpenPositions", maxOpenPositions ); | |
SetOption( "MaxOpenLong", maxOpenLongs ); | |
SetOption( "MaxOpenShort", maxOpenShorts ); | |
// Here are some other important SETOPTION variables you might like to add, see here | |
// for more details: https://www.amibroker.com/guide/afl/setoption.html | |
SetOption( "InitialEquity", 100000 ); | |
SetOption( "FuturesMode", False ); // stock market trading, if CFD trading set to True | |
SetOption( "MinShares", 1 ); // minimum quantity of shares that can be bought | |
SetOption( "MinPosValue", 500 ); // minimum trade size (num of stocks x entry price) | |
SetOption( "CommissionMode", 0 ); // I prefer using the table as it's easier to enter my stockbroker's charges | |
SetOption( "AccountMargin", 100 ); // no margin | |
SetOption( "UsePrevBarEquityForPosSizing", True ); // use previous bar to get equity value - not really needed if using a static $ risk per trade | |
// These next two options ARE very important. As we do not know when funds will be available | |
// when we exit a position we need to delay the settlement of these funds at least 1 day. | |
SetOption( "SettlementDelay", 1 ); | |
SetBacktestMode( backtestRegularRaw ); | |
// Define other system variables here that are needed, but not necessarily | |
// for optimisation. | |
i = 2; // initialise numbers | |
str = 'Hello'; // initialise strings | |
testArray[0] = Null; // initialise arrays | |
temp = Null; // initialise a variable with Null value | |
// The simple loop through all bars in a security. Try not to have too many of these, | |
// preferably just one. And also try to assign variables to arrays on a per-bar basis. | |
// And then do your analysis on those arrays in the `IIf` functions outside. | |
for ( i; i < BarCount; i += 1 ) { | |
if ( C[i] > C[i-1] ) { | |
temp = C[i]; | |
} | |
testArray[i] = temp; | |
} | |
// Begin your system by defining entry gates and triggers here. Code your | |
// operations on the current bar. | |
// If using PremiumData.Net data and only looking to trade stocks, you may want | |
// to use the following initialising `IIf` to remove all ETFs, OPTIONS and other | |
// non-stock securities: | |
BuySignal = IIf( StrFind( FullName(), " ORDINARY" ) > 0, 1, 0 ); | |
// From here on out simply define your gates and triggers one per line and capture BuySignal: | |
BuySignal = BuySignal && MA( C * V, barsValue ) > minValueTrading; | |
...etc... | |
// We then determine what our price would have been and we need to reference a value that | |
// would have been determined according to the current bar (our signal bar). | |
BuyLimitPrice = C; | |
// Now let's determine our entry price. To prevent another bias creeping into our results | |
// we've apportioned every trade an equal amount of `riskPerTrade`. We then just need to | |
// determine what our stop distance will be. Whatever calculations we do here we need to | |
// again reference the current signal bar, and will likely need to `round` our calculation. An | |
// example would be something like: | |
posSize = round( riskPerTrade / ( 2 * ATR( 14 ) ) ); | |
// When you've finished entering your buy signal entry gates and triggers we now need to | |
// determine whether or not entry has occurred according to HOW you get into the market. | |
// This will primarily be determined according to what your stockbroker allows for entry. | |
// Most offer either Limit or Market Orders. Problem with Market Orders is that they | |
// cannot be created during off-market hours. So most orders would need to be LIMIT if | |
// you are testing/trading an end-of-day system. | |
// Therefore, to check if we've had a successful buy entry we need to look forwards, or | |
// back one bar by making the actual entry bar the current bar. | |
Buy = Ref( BuySignal, -1 ); | |
// We then determine if the current bar ended up having it's LOW less than our buy limit | |
// price. Note that we check the `Low` being LESS than our `BuyLimitPrice`. We could check | |
// if `L <= Ref( BuyLimitPrice, -1 )` but this could give us a false positive, prefer price being | |
// lower than our entry price to assert that entry would likely have occurred. | |
Buy = Buy && L < Ref( BuyLimitPrice, -1 ); | |
// If it does we then calculate what the likely entry price would have been, either the | |
// open of that bar, if price opened below our `BuyLimitPrice` or the `BuyLimitPrice`. | |
BuyPrice = Min( O, Ref( BuyLimitPrice, -1 ) ); | |
// Then we would use the following declaration to implement the number of shares to purchase: | |
// (`spsShares` tells AmiBroker how many shares to trade, which `posSize` calculated for us) | |
// More here: https://www.amibroker.com/guide/afl/setpositionsize.html | |
// Note also that we are referencing the previous bar (as we are assuming that we are now on | |
// the entry bar) | |
SetPositionSize( Ref( posSize, -1 ), spsShares ); | |
// Lastly, and perhaps most importantly prior to entry, we need to tell AmiBroker | |
// our precedence of entry in to our selected stocks when there is an over-abundance | |
// of stocks to enter into at the same time. | |
// Simply reference a calculation with the intent of this calculation producing the | |
// largest number for stocks you would prefer to get in rather than others. | |
// Important: reference the signal bar. | |
posScore = RSI( 100 ); // priority given to stocks with the highest RSI(100) - if you wanted to reference stocks with the lowest RSI(100) you could either go 100-RSI(100) or 1/RSI(100) | |
// Set your calculated variable to the AmiBroker variable `PositionScore`. Be mindful | |
// AmiBroker uses absoluted values! Need to be careful of indicators that may give | |
// negative numbers (eg. MACD). | |
PositionScore = Ref( posScore, -1 ); | |
// Then we need to determine how we would like to exit our trades. For all LONG trades we have | |
// Buy for entry and Sell for exit. For all SHORT trades we have Short for entry and Cover for | |
// exit. Again, we determine our exit by considering our exit on the current bar. | |
SellSignal = C < Ref( LLV( L, 3 ), -1 ); | |
// Then we determine what our exit price will be, taking into consideration that all values we | |
// use need to reference the current bar as it assumes we are on the signal bar. | |
SellLimitPrice = L; | |
// If you would like to have a target price set this to a value, otherwise set to 0. | |
SellTargetPrice = HHV( H, 10 ); | |
// Next we check to see whether this value ends up being hit on the next bar. | |
SellTargetSignal = SellTargetPrice > 0 && H > Ref( SellTargetPrice, -1 ); | |
// Then we once again need to determine what orders our stockbroker takes to exit our trade. | |
// Assuming that limit sell orders are only possible, we either need to peek forward in our system, OR | |
// again make the next bar our current bar and look back at the previous bar to determine if | |
// `SellSignal` occurred. I prefer the latter. Then we check to determine if on our new current bar | |
// whether the limit price was actually touched. | |
// We have also added the check to determine whether the limit sell (exit) price was also hit. | |
Sell = IIf( ( Ref( SellSignal, -1 ) && H > Ref( SellLimitPrice, -1 ) ) || SellSignalTarget, 1, 0 ); | |
// If so, we then set our sell price as either the Opening bar, if price opened higher than | |
// our `SellLimitPrice`, or the `SellLimitPrice`, or if we were exiting at our `SellTargetPrice`. | |
SellPrice = Max( O, IIf( SellSignalTarget, Ref( SellTargetPrice, -1 ), Ref( SellLimitPrice, -1 ) ) ); | |
// If we do not wish to receive any further buy signals once we have received our first | |
// buy signal then we can use the following. | |
Buy = ExRem( Buy, Sell ); | |
Sell = ExRem( Sell, Buy ); | |
// EXPLORATION | |
// To successfully monitor when we are to enter our stocks we need to create a filter that | |
// we run every day to help determine entry. We can run a backtest to help determine when | |
// we exit. | |
Filter = Buy; | |
AddColumn( BuyLimitPrice, "Entry Limit Order", 1.3 ); | |
AddColumn( posSize, "# Shares", 1.0 ); | |
AddColumn( posScore, "Score", 1.3 ); | |
AddColumn( BuyLimitPrice * posSize, "Investment $", 1.2 ); | |
// You can continue to add any more columns of data as needed |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment