Auto-Optimization Framework.afl
上传用户:shiqiang
上传日期:2009-06-12
资源大小:1289k
文件大小:27k
源码类别:

金融证券系统

开发平台:

Others

  1. //------------------------------------------------------------------------------
  2. //
  3. //  Formula Name:    Auto-Optimization Framework
  4. //  Author/Uploader: Dave Merrill 
  5. //  E-mail:          
  6. //  Date/Time Added: 2003-10-22 11:58:47
  7. //  Origin:          
  8. //  Keywords:        optimize,auto-optimize,backtest
  9. //  Level:           advanced
  10. //  Flags:           system,exploration,function
  11. //  Formula URL:     http://www.amibroker.com/library/formula.php?id=304
  12. //  Details URL:     http://www.amibroker.com/library/detail.php?id=304
  13. //
  14. //------------------------------------------------------------------------------
  15. //
  16. //  this is a backtest framework that continously and automatically optimizes
  17. //  settings for a trading rule you provide. it chooses settings that measure
  18. //  best over a selectable lookback period. this re-optimization can be done
  19. //  every bar, at a user-definable interval, or once at the start of trading.
  20. //  it requires AFL editing to use; instructions are provided.
  21. //
  22. //------------------------------------------------------------------------------
  23. //////////////////////////////////////////////////////////////////////////
  24. // INTRO
  25. //////////////////////////////////////////////////////////////////////////
  26. /*
  27. ------------------------------------------------------------------------
  28. Auto-Optimization Framework
  29. v 1.0 (hah) 10/20/03 Dave Merrill (email: dmerrill at usa dot net)
  30. thanks to Tomasz Janeczko for AmiBroker, and everyone in the AmiBroker groups at Yahoo
  31. comments, suggestions, bug reports etc are most welcome
  32. ------------------------------------------------------------------------
  33. WHAT DOES THIS DO?
  34. you've probably done walk-forward optimizations, where you optimize a trading system over some time period, then trade with those settings for a while, optimize again at some later date, trade on, etc.. in fact, this is like real life, unless you assume your initial settings will be optimal forever and you'll never change them.
  35. this code is a backtest framework that does this continously and automatically. you provide a trading rule, and it constantly optimizes the parameters for it, always using the settings that measure best over a selectable lookback period. this re-optimization can be done every bar, at a user-definable interval, or once at the start of trading.
  36. one interesting fact is that so far, almost no trading rules I've tested are very profitable when managed this way, with any framework settings I've tried. the ones that are are far from universally successful over various stocks and time frames. I find this very interesting and quite puzzling, as is well documented on the AB group. maybe there are bugs that cause this; please let me know about any you find. maybe the equity evaluation function or other framework behaviors could be improved; please let me know if you come up with great changes. maybe it's just not a very effective method; if you can explain to me why this is so, please do!
  37. and lastly, if you find any hugely profitable uses for this thing, or other interesting insights from poking around with it, PLEASE let me in on 'em! (:-)
  38. IMPORTANT USAGE NOTE!!!!
  39. parts of this code belong to the framework itself, and shouldn't be modified in normal use. other parts you need to edit, to select the specific trading rule you want to test, and establish settings for how the framework will operate. 
  40. all sections are clearly labelled as editable or not. it's in your best interest not to change the ones that say NOT EDITABLE, at least to start with.
  41. DON'T BE IMTIMIDATED
  42. though this is pretty long, it's actually not that complicated, either to use or in its internals. it's broken down into sections, most of which you don't need to even look at. if I could hide the no-touchy bits away I would (:-). also, a lot of it is explanation, not code, and a lot of the code is a collection of sample trading rules.
  43. INSTRUCTIONS
  44. 1) add trading rule(s) you want to test to the CUSTOM TRADING RULES section. see comments there for how your rules will be called, and what they need to do. some examples are provided. you can have as many rules here as you want, but only one can be in use at a time. step 3 below controls which one is active.
  45. 2) if those rules require other custom functions, add them to the CUSTOM UTILITY FUNCTIONS section.
  46. 3) edit the framework function BuySellRule(), at the start of the FRAMEWORK CONFIGURATION section, to call the trading rule you want to test now.
  47. 4) edit the SetParameterRanges() call, next in the FRAMEWORK CONFIGURATION section, to set the ranges over which you want to test each of the two optimization parameters. some examples are provided; comment out all but the one you want to use.
  48. 5) if desired, edit the rest of the FRAMEWORK CONFIGURATION section, to control various aspects of how the framework itself behaves.
  49. 6) set the AA range, stock universes you want to test, and other AA settings, then run a Backtest or Portfolio Backtest.
  50. 7) if you want to see the parameters selected and other decisions being made under the hood, you can Explore instead of backtest. if desired, edit the EXPLORATION section at the end to change the columns and bars displayed; some possible extensions and modifications are provided. note that exploring doesn't actually trade, it's only a readout for your interest.
  51. GETTING STARTED
  52. this comes set up to run the included trading rule 'BuySellCCI'. backtest it on, say, the NASDAQ Composite Index, to get an idea of how it performs (nothing great). explore it to see the parameter values the framework selected (the best_p1 column). try changing the parameter ranges tested, as described in the instructions above.
  53. BuySellCCI uses a single parameter, requiring only 40 tests to optimize. this makes it quicker to run than most two-parameter systems, which typically need about 40 x 40 tests or more, depending on the number of values you want to test. performance can definitely be an issue as the number of tests goes up, and even single parameter systems can be pretty slow with a large number of stocks.
  54. in the FRAMEWORK CONFIGURATION section, try changing equity_lookback_bars (how far back performance is evaluated during optimization) and equity_lookback_frequency (how often optimization is done). if you want, you can actually optimize these; possible optimization settings are provided. be cautious in interpreting those results though. I'd do it more for a quick sense of how things behave than to necessarily choose the highest performing settings. at the very least, test a variety of stocks and time frames with any settings you arrive at, to make sure they're not a fluke.
  55. try some of the other included trading rules, maybe starting with the MA/EMA/DEMA/TEMA cross rules. hook them up and select parameter ranges to test as described in the instructions above.
  56. try adding your own trading rules, and experiment with optimization ranges and equity feedback parameters.
  57. try modifying the performance scoring methods, and adding your own.
  58. */
  59. //////////////////////////////////////////////////////////////////////////
  60. // FRAMEWORK INIT - NOT EDITABLE
  61. //////////////////////////////////////////////////////////////////////////
  62. buy = sell = short = cover = 0;
  63. equity_lookback_bars = equity_lookback_frequency = equity_lookback_smoothing = equity_drawdown_discount_pct = 0;
  64. e = best_e = best_perf_score = best_p1 = best_p2 = 0;
  65. p1_start = p2_start = 2;
  66. p1_end = p2_end = 20;
  67. p1_step = p2_step = 1;
  68. bar_index = BarIndex();
  69. //////////////////////////////////////////////////////////////////////////
  70. // FRAMEWORK FUNCTIONS - NOT EDITABLE
  71. //////////////////////////////////////////////////////////////////////////
  72. function SetParameterRanges(p1_start_p, p1_end_p, p1_step_p, p2_start_p, p2_end_p, p2_step_p) {
  73. if(p1_step_p != 0) {
  74. p1_start = p1_start_p;
  75. p1_end = p1_end_p;
  76. p1_step = p1_step_p;
  77. } else {
  78. p1_start = p1_end = p1_step = 1;
  79. }
  80. if(p2_step_p != 0) {
  81. p2_start = p2_start_p;
  82. p2_end = p2_end_p;
  83. p2_step = p2_step_p;
  84. } else {
  85. p2_start = p2_end = p2_step = 1;
  86. }
  87. }
  88. function PerformanceScoreMDDDiscount(e, perf_score) {
  89. e_max = Highest(e); // peak equity so far
  90. mdd = Highest(e_max - e); // max drawdown so far
  91. mdd_fraction = Highest(mdd / e_max); // fraction max drawdown is of peak equity
  92. perf_score = perf_score - (perf_score * mdd_fraction * (equity_drawdown_discount_pct/100)); // reduce score by mdd fraction scaled by drawdown discount
  93. return perf_score;
  94. }
  95. //////////////////////////////////////////////////////////////////////////
  96. // CUSTOM UTILITY FUNCTIONS - EDITABLE
  97. //////////////////////////////////////////////////////////////////////////
  98. function EMAx(array, period) { // use in place of AB EMA function when period needs to be an array
  99. return AMA(array, 2 / (period + 1));
  100. }
  101. function StochTransform(array, period) {
  102. return 100*(array-LLV(array,period))/(HHV(array,period)-LLV(array,period));
  103. }
  104. function MeanDev(array, mean, range) {
  105. result = 0;
  106. for(i = LastValue(Highest(range)); i < BarCount; i++) {
  107. result[i] = 0; // the mean is not 'moving' over the range (outside the loop)
  108. tm = mean[i]; 
  109. for(j = 0; j < range[i]; j++) {
  110. result[i] = result[i] + abs(array[i - j] - tm);
  111. }
  112. result[i] = result[i] / range[i]; 
  113. }
  114. return result;
  115. }
  116. function CCIx(period) { // use in place of AB CCI function when period needs to be an array
  117. // CCI = (TypicalPrice - MA(TypicalPrice, 20)) / (.015 x MeanDeviation)
  118. SMATP = MA(Avg, period);
  119. MD = MeanDev(Avg, SMATP, period);
  120. result = (Avg - SMATP) / (0.015 * MD);
  121. return result;
  122. }
  123. function TradeDivergences(array1, array2) {
  124. dir1 = IIf(array1 > Ref(array1, -1), 1, IIf(array1 < Ref(array1, -1), -1, 0));
  125. dir2 = IIf(array2 > Ref(array2, -1), 1, IIf(array2 < Ref(array2, -1), -1, 0));
  126. buy = cover = (dir1 == 1) and (dir2 == -1);
  127. sell = short = (dir1 == -1) and (dir2 == 1);
  128. }
  129. function TradeReversalsArith(array, threshold) {
  130. array = last_hi = last_lo = IIf(IsNull(array), -1, array);
  131. last_signal[0] = 0;
  132. buy = sell = 0;
  133. for(i = 1; i < BarCount; i++) {
  134. buy[i] = array[i] >= last_lo[i-1] + threshold[i] and last_signal[i-1] != 1 and array[i-1] != -1;
  135. sell[i] = array[i] <= last_hi[i-1] - threshold[i] and last_signal[i-1] != -1 and array[i-1] != -1;
  136. last_signal[i] = IIf(buy[i], 1, IIf(sell[i], -1, last_signal[i-1]));
  137. always_track = array[i-1] == -1 or buy[i] or sell[i];
  138. last_lo[i] = IIf(always_track or array[i] < last_lo[i-1], array[i], last_lo[i-1]);
  139. last_hi[i] = IIf(always_track or array[i] > last_hi[i-1], array[i], last_hi[i-1]);
  140. }
  141. short = sell;
  142. cover = buy;
  143. }
  144. /////////////////////////////////////////////////////////////////////////
  145. // CUSTOM TRADING RULES - EDITABLE
  146. /////////////////////////////////////////////////////////////////////////
  147. /*
  148. this is where you put the various trading rules you may want to test. only one rule can be in use at a time. select which one actually gets used by editing the framework function BuySellRule(), at the start of the FRAMEWORK CONFIGURATION section.
  149. all rules should set buy, sell, short and cover, in response to the parameters passed to it and whatever logic you want.
  150. your rule will be passed 3 parameters:
  151. dynamic (true/false) : true if parameters are arrays, not just single values; see ABOUT THE 'DYNAMIC' PARAMETER, below
  152. p1 (number/array) : first optimization parameter
  153. p2 (number/array) : second optimization parameter
  154. p1 and p2 are the values being optimized on. trading rules can use them for any optimizable purpose, MA periods or crossover thresholds for example.
  155. during optimization, the rule in use will be called multiple times, once with each combination of p1 and p2 being tested. at this time, p1 and p2 will be simple numeric values, not arrays. once optimal settings have been determined for every bar, the rule will be called once more, to generate actual trading signals. this time, p1 and p2 will be arrays, each bar containing the optimal p1 and p2 value for that bar.
  156. ABOUT THE 'DYNAMIC' PARAMETER
  157. not all AB functions can take arrays as inputs. for example, MA, TEMA and DEMA can use an array as their period, but EMA can't. it's usually possible to code an equivalent that can in AFL, but it may be slower, or in a DLL, which I didn't want to depend on for this. this speed difference can be big, since the trading rule is called many times during optimization. 
  158. if desired, your rule can include both slower array-capable code for use in the final signal-generation phase, and faster, non-array-capable code for use during the many optimization tries. if you do this, use the 'dynamic' parameter to know which to call. see BuySellCCI, below, for an example.
  159. */
  160. function BuySellEMACrossPrice(dynamic, p1, p2) { // p1 = EMA period
  161. m = EMAx(c, p1);
  162. buy = cover = Cross(c, m);
  163. sell = short = Cross(m, c);
  164. }
  165. function BuySellMACross(dynamic, p1, p2) { // p1 = MA1 period, p2 = MA2 period increase over MA1 period
  166. m1 = MA(c, p1);
  167. m2 = MA(c, p1 + p2);
  168. buy = cover = Cross(m1, m2);
  169. sell = short = Cross(m2, m1);
  170. }
  171. function BuySellEMACross(dynamic, p1, p2) { // p1 = MA1 period, p2 = MA2 period increase over MA1 period
  172. m1 = EMAx(c, p1);
  173. m2 = EMAx(c, p2);
  174. buy = cover = Cross(m1, m2);
  175. sell = short = Cross(m2, m1);
  176. }
  177. function BuySellDEMACross(dynamic, p1, p2) { // p1 = MA1 period, p2 = MA2 period increase over MA1 period
  178. m1 = DEMA(c, p1);
  179. m2 = DEMA(c, p1 + p2);
  180. buy = cover = Cross(m1, m2);
  181. sell = short = Cross(m2, m1);
  182. }
  183. function BuySellTEMACross(dynamic, p1, p2) { // p1 = MA1 period, p2 = MA2 period increase over MA1 period
  184. m1 = TEMA(c, p1);
  185. m2 = TEMA(c, p1 + p2);
  186. buy = cover = Cross(m1, m2);
  187. sell = short = Cross(m2, m1);
  188. //short = cover = 0; // at least sometimes, short is perf_scoreable, but only barely, so RAR drops
  189. }
  190. function BuySellCCI(dynamic, p1, p2) { // p1 = CCI period; fixed threshold of zero
  191. If(dynamic) {
  192. cc = CCIx(p1);
  193. } else {
  194. cc = CCI(p1);
  195. }
  196. buy = cover = Cross(cc, 0);
  197. sell = short = Cross(0, cc);
  198. }
  199. function BuySellCCI2(dynamic, p1, p2) { // p1 = CCI period; moveable symetrical thresholds, mirrored above and below zero for long and short
  200. If(dynamic) {
  201. cc = CCIx(p1);
  202. } else {
  203. cc = CCI(p1);
  204. }
  205. buy = cover = Cross(cc, p2);
  206. sell = short = Cross(-p2, cc);
  207. }
  208. function BuySellCCI3(dynamic, p1, p2) { // p1 = CCI period; moveable absolute threshold, same numeric level for long and short
  209. If(dynamic) {
  210. cc = CCIx(p1);
  211. } else {
  212. cc = CCI(p1);
  213. }
  214. buy = cover = Cross(cc, p2);
  215. sell = short = Cross(p2, cc);
  216. }
  217. function BuySellStochCCI(dynamic, p1, p2) { // p1 = CCI period
  218. cc = CCIx(period);
  219. cc = StochTransform(cc, period);
  220. buy = cover = Cross(cc, 50);
  221. sell = short = Cross(50, cc);
  222. }
  223. function BuySellStoch(dynamic, p1, p2) { // p1 = range, p2 = K & D smoothing
  224. If(dynamic) {
  225. stoK = MA(100*(C-LLV(L,best_p1))/(HHV(H,best_p1)-LLV(L,best_p1)),best_p2);
  226. stoD = MA(stoK, best_p2);
  227. } else {
  228. stoD = StochD(p1, p2, p2);
  229. stoK = StochK(p1, p2);
  230. }
  231. sig = stoD - stoK;
  232. buy = cover = Cross(sig, 0);;
  233. sell = short = Cross(0, sig);
  234. }
  235. function BuySellStochDivergence(dynamic, p1, p2) { // p1 = range, p2 = K & D smoothing
  236. If(dynamic) {
  237. stoK = MA(100*(C-LLV(L,best_p1))/(HHV(H,best_p1)-LLV(L,best_p1)),best_p2);
  238. stoD = MA(stoK, best_p2);
  239. } else {
  240. stoD = StochD(p1, p2, p2);
  241. stoK = StochK(p1, p2);
  242. }
  243. TradeDivergences(stoD, stoK);
  244. }
  245. function BuySellROC(dynamic, p1, p2) { // p1 = range, p2 = threshold
  246. sig = ROC(MA(c, p1), 1);
  247. buy = cover = Cross(sig, p2);
  248. sell = short = Cross(p2, sig);
  249. }
  250. function BuySellDemandIndex(dynamic, p1, p2) { // p1 = DI & TEMA period, p2 = EMA period
  251. period = 21; //p1; // 21
  252. period2 = 30; //p2; // 30
  253. A=(H+L+2*C);
  254. B=EMAx((HHV(H,2)-LLV(L,2)),21);
  255. BuyP= /*{BuyPower}*/
  256. V/EMAx(V,21) * ((A>=Ref(A,-1)) +(A<Ref(A,-1)) / exp((0.375 * (A+Ref(A,-1)) /B ) *(Ref(A,-1)-A) / A));
  257. SellP = /*{SellPressure}*/ 
  258. V/EMAx(V,21) * ((A<=Ref(A,-1)) + (A>Ref(A,-1)) / exp((0.375 * (A+Ref(A,-1)) / B ) * (A-Ref(A,-1)) / Ref(A,-1)));
  259. mabp=EMAx(BuyP,period);       
  260. masp=EMAx(SellP,period); // {smooth Selling Pressure}
  261. divsor=IIf(mabp>masp,mabp,masp); // {BP:SP ratio}
  262. divend=IIf(mabp<masp,mabp,masp); // {biggest=divisor}
  263. var2=1-(divend/divsor); // {adjust ratio to plot in}
  264. var3=IIf((masp>mabp), -var2, var2); // {range -100 to 100}
  265. var4=var3*100;
  266. DI = var4;
  267. DI_TEMA = TEMA(DI, period);
  268. DI_TEMA_MA = EMAx(DI_TEMA, period2);
  269. buy = cover = Cross(DI_TEMA, 0); // try various crosses of DI, DI_TEMA, DI_TEMA_MA and zero
  270. sell = short = Cross(0, DI_TEMA);
  271. short = cover = 0; // improves performance sometimes
  272. }
  273. function BuySellMTIReversals(dyanamic, p1, p2) { // p1 = reversal threshold; .12 for San Diego Wave timing
  274. // requires MTI in ticker !!MTI;
  275. // note that MTI data doesn't go back very far, and was calculated differently before some time in early '00
  276. // adjust test period and keep results in perspective accordingly
  277. MTI = Foreign("!!MTI", "C", true);
  278. TradeReversalsArith(MTI, p1);
  279. }
  280. //////////////////////////////////////////////////////////////////////////
  281. // PERFORMANCE SCORING METHODS - EDITABLE
  282. //////////////////////////////////////////////////////////////////////////
  283. // one of these functions will be called to evaluate the performance of each parameter combination tried
  284. // select the one that will be active by editing the framework function PerformanceScore
  285. // it's at the beginning of EQUITY FEEDBACK BEHAVIOR, in the FRAMEWORK CONFIGURATION section
  286. // you can edit these or add your own
  287. function PerformanceScore1() { // profit per bar
  288. e = Equity(0, 0);
  289. if(equity_lookback_bars == 0) { // test all performance to date
  290. e_ref = e[0];
  291. } else { // test performance back spec'd number of bars
  292. e_ref = Ref(e, -equity_lookback_bars);
  293. }
  294. perf_score = (e - e_ref) / e_ref; // growth over lookback period
  295. perf_score = MA(perf_score, IIf(equity_lookback_smoothing >= 1, equity_lookback_smoothing, 1)) * 100; // smoothed pct
  296. perf_score = perf_score * 100 / IIf(equity_lookback_bars == 0, Cum(1), equity_lookback_bars); // per 100 bars
  297. perf_score = PerformanceScoreMDDDiscount(e, perf_score);
  298. return perf_score;
  299. }
  300. function PerformanceScore2() { // net of right-sided bars per bar
  301. e = Equity(0, 0);
  302. e_ref = Ref(e, -1);
  303. e_delta = e - e_ref;
  304. last_buy_bar = ValueWhen(buy, bar_index);
  305. last_sell_bar = ValueWhen(sell, bar_index);
  306. last_short_bar = ValueWhen(short, bar_index);
  307. last_cover_bar = ValueWhen(cover, bar_index);
  308. entry_status = IIf(last_buy_bar > last_sell_bar, 1, IIf(last_short_bar > last_cover_bar, -1, 0));
  309. bar_is_right = IIf(entry_status == 1, e_delta > 0, IIf(entry_status == -1, e_delta < 0, 0));
  310. bar_is_wrong = IIf(entry_status == 1, e_delta < 0, IIf(entry_status == -1, e_delta < 0, 0));
  311. if(equity_lookback_bars == 0) { // test all performance to date
  312. bars_right = Cum(bar_is_right); // bars long and rising or short and falling
  313. bars_wrong = Cum(bar_is_wrong); // bars long and falling or short and rising
  314. } else { // test performance back spec'd number of bars
  315. bars_right = Sum(bar_is_right, equity_lookback_bars); // bars long and rising or short and falling
  316. bars_wrong = Sum(bar_is_wrong, equity_lookback_bars); // bars long and falling or short and rising
  317. }
  318. perf_score = (bars_right - bars_wrong) / bar_index; // net bars right per bar
  319. perf_score = MA(perf_score, IIf(equity_lookback_smoothing >= 1, equity_lookback_smoothing, 1)) * 100; // smoothed pct
  320. perf_score = PerformanceScoreMDDDiscount(e, perf_score);
  321. return perf_score;
  322. }
  323. //////////////////////////////////////////////////////////////////////////
  324. // FRAMEWORK CONFIGURATION - EDITABLE
  325. //////////////////////////////////////////////////////////////////////////
  326. // TRADING RULE: EDIT TO CALL THE CUSTOM RULE YOU WANT TO TEST
  327. function BuySellRule(dynamic, p1, p2) {
  328. BuySellCCI(dynamic, p1, p2);
  329. }
  330. // PARAMETER RANGES TO TEST
  331. // each of the two parameters, p1 and p2, has start, end and step values that control the values tried during optimization
  332. // you'll probably need to adjust these to the ranges that are useful for the active trading rule
  333. // to set them all at once, call SetParameterRanges(p1_start, p1_end, p1_step, p2_start, p2_end, p2_step)
  334. //  if your rule doesn't use p2, use p2_step = 0 to speed up optimization
  335. //  some typical settings are provided below; uncomment and edit the one you want to use
  336. SetParameterRanges(2, 40, 1, 1, 1, 0);  // basic single parameter range
  337. //SetParameterRanges(2, 40, 1, 2, 40, 1);  // basic two parameter ranges
  338. //SetParameterRanges(2, 40, 1, 0, 100, 10);  // 2-40 period, 0-100 threshold
  339. //SetParameterRanges(.01, .5, .01, 1, 1, 0); // for MTI reversals
  340. // EQUITY FEEDBACK BEHAVIOR
  341. // parameter values selected by the framework are the ones that performed best over a specified time in the past
  342. // this section controls how this is done
  343. // two performance metrics are provided, and you can add your own
  344. // only one can be active at a time
  345. // edit the function PerformanceScore to call the one you want to use
  346. // equity_lookback_bars controls how many bars worth of history will be examined
  347. // 0 = examine all performance to date
  348. // equity_lookback_frequency is the number of bars between optimizations
  349. // 0 = optimize every bar, -1 = first bar only
  350. // equity_lookback_smoothing applies an MA to the equity curve before picking optimum values
  351. // minimizes the effect of daily price fluctuations on optimization; only matters much when pretty high, multiple months
  352. // equity_drawdown_discount_pct reduces the performance score by the maximum drawdown amount * this percentage
  353. // at zero, MDD has no effect; at 100, 20% MDD reduces the score by 20%
  354. function PerformanceScore() {
  355. return PerformanceScore2();
  356. }
  357. equity_lookback_bars = 0; // 126 * Optimize("Equity Lookback", 0, 0, 5*2, 1);
  358. equity_lookback_frequency = 0; //21 * Optimize("Equity Lookback Frequency", 0, 0, 12*2, 1);
  359. equity_lookback_smoothing = 0; //21*6;
  360. equity_drawdown_discount_pct = 0; //100;
  361. // TRADE ENTRY RESTRICTIONS
  362. // edit to set minimum price, volume and length of history required to enter a trade
  363. // note that the volume requirement is ignored for mutual funds and tickers starting w '!' or '~'
  364. has_min_vol = (MA(v, 100) > 1000000) OR TRUE;
  365. is_min_price_long = c > 1;
  366. is_min_price_short = c > 5;
  367. has_min_history = bar_index >= (252 * 3); // require 3 years of history before trading
  368. // PORTFOLIO MANAGEMENT
  369. // these settings aren't well tested (:-)
  370. max_positions = 0; // number of simultaneous positions to hold; zero for no portfolio management
  371. use_ranking = false; // true to rank simultaneous entry signals by backtest return in portfolio mode
  372. //////////////////////////////////////////////////////////////////////////
  373. //////////////////////////////////////////////////////////////////////////
  374. //////////////////////////////////////////////////////////////////////////
  375. ////////////////////  FRAMEWORK ENGINE - NOT EDITABLE   //////////////////
  376. //////////////////////////////////////////////////////////////////////////
  377. //////////////////////////////////////////////////////////////////////////
  378. //////////////////////////////////////////////////////////////////////////
  379. // tweak volume entry requirements for things with no volume data
  380. has_min_vol = has_min_vol 
  381. or (MarketID(1) == "Mutual funds") or (StrLeft(Name(), 1) == "!") or (StrLeft(Name(), 1) == "~");
  382. // loop through requested parameter ranges, calculating signals and choosing parameter values that score best
  383. first_bar_index = ValueWhen(Status("FirstBarInRange"), bar_index);
  384. for(p1 = p1_start; p1 <= p1_end; p1 = p1 + p1_step) {
  385. for(p2 = p2_start; p2 <= p2_end; p2 = p2 + p2_step) {
  386. // calc buy/sell/short/cover w those settings
  387. BuySellRule(false, p1, p2);
  388. // apply overall entry restrictions
  389. buy = buy and is_min_price_long and has_min_vol;
  390. short = short and is_min_price_short and has_min_vol;
  391. // calc performance score w those settings
  392. perf_score = PerformanceScore();
  393. // track settings w best performance and resulting equity
  394. if(equity_lookback_frequency == 0) {
  395. optimize_this_bar = 1;
  396. } else if(equity_lookback_frequency == -1) {
  397. optimize_this_bar = Status("FirstBarInRange");
  398. } else {
  399. optimize_this_bar = (first_bar_index - bar_index) % equity_lookback_frequency == 0;
  400. }
  401. is_new_best = IIf(optimize_this_bar and perf_score > best_perf_score, 1, 0);
  402. best_perf_score = IIf(is_new_best, perf_score, best_perf_score);
  403. best_p1 = IIf(is_new_best, p1, best_p1);
  404. best_p2 = IIf(is_new_best, p2, best_p2);
  405. best_e = IIf(is_new_best, e, best_e);
  406. best_perf_score = IIf(optimize_this_bar, best_perf_score, ValueWhen(optimize_this_bar, best_perf_score));
  407. best_p1 = IIf(optimize_this_bar, best_p1, ValueWhen(optimize_this_bar, best_p1));
  408. best_p2 = IIf(optimize_this_bar, best_p2, ValueWhen(optimize_this_bar, best_p2));
  409. best_e = IIf(optimize_this_bar, best_e, ValueWhen(optimize_this_bar, best_e));
  410. }
  411. }
  412. // calc real buy/sell/short/cover w optimal settings
  413. BuySellRule(true, best_p1, best_p2);
  414. // apply overall entry restrictions
  415. buy = buy and is_min_price_long and has_min_vol and has_min_history;
  416. short = short and is_min_price_short and has_min_vol and has_min_history;
  417. // don't enter, exit now, unless lookback was perf_scoreable
  418. buy = buy and best_perf_score > 0;
  419. short = short and best_perf_score > 0;
  420. sell = sell or best_perf_score <= 0;
  421. cover = cover or best_perf_score <= 0;
  422. // kill sells and covers before respective entries; cleans up exploration
  423. sell = sell and Cum(buy);
  424. cover = cover and Cum(short);
  425. // kill dupe signals, for exploration
  426. sell = ExRem(sell, buy);
  427. cover = ExRem(cover, short);
  428. // set PostionSize and PositionScore if requested
  429. if(max_positions > 0) {
  430. PositionSize = -((100 / max_positions) - 1);
  431. }
  432. if(use_ranking) {
  433. PositionScore = IIf(buy, best_perf_score, IIf(short, -best_perf_score, 0));
  434. }
  435. //////////////////////////////////////////////////////////////////////////
  436. // EXPLORATION - EDITABLE IF YOU WANT, BUT USUALLY NOT NECCESSARY
  437. //////////////////////////////////////////////////////////////////////////
  438. filter = buy or sell or short or cover;
  439. //filter = optimize_this_bar; // to show every bar where optimization was done
  440. //filter = filter or Ref(filter, 1); // to show the day before each signal too
  441. //filter = 1; //to show all bars
  442. AddColumn(bar_index, "Bar Index", 1.0);
  443. AddColumn(buy, "buy", 1);
  444. AddColumn(sell, "sell", 1);
  445. AddColumn(short, "short", 1);
  446. AddColumn(cover, "cover", 1);
  447. AddColumn(optimize_this_bar, "optimize_this_bar", 1.0);
  448. AddColumn(best_perf_score, "best_perf_score", 1.4);
  449. AddColumn(best_e, "best_e", 1.0);
  450. AddColumn(((ValueWhen(filter or Status("LastBarInTest"), best_e, 0) - best_e) / best_e) * 100, "best_e_pct_change", 1.2);
  451. AddColumn(best_p1, "best_p1");
  452. AddColumn(best_p2, "best_p2");
  453. AddColumn(BarsSince(best_p1 != Ref(best_p1, -1) or best_p2 != Ref(best_p2, -1)) + 1, "bars_since_settings", 1.0);
  454. AddColumn(ValueWhen(filter or Status("LastBarInTest"), bar_index, 0) - bar_index, "bars", 1.0);
  455. AddColumn(has_min_history, "has_min_vol", 1.0);
  456. AddColumn(has_min_history, "has_min_history", 1.0);
  457. AddColumn(c, "price");
  458. AddColumn(equity_lookback_bars, "equity_lookback_bars");
  459. AddColumn(equity_lookback_frequency, "equity_lookback_frequency");
  460. // enable next lines to see more optimization details; note that only the last parameter set tried will show
  461. //AddColumn(e, "e");
  462. //AddColumn(e_ref, "e_ref");
  463. //AddColumn(perf_score, "perf_score", 1.4);