|
2 registered members (AndrewAMD, gbe),
8,006
guests, and 3
spiders. |
|
Key:
Admin,
Global Mod,
Mod
|
|
|
Re: Has anyone developed a turtle trading script?
[Re: jcl]
#425009
06/25/13 13:27
06/25/13 13:27
|
Joined: Jun 2013
Posts: 3
wastinglight
OP
Guest
|
OP
Guest
Joined: Jun 2013
Posts: 3
|
I agree that the idea is simple. Trade any breakout above/below a 20 or 55 day High/Low. Yet the adding of entry positions as the trade moves in a positive direction, together with the money management suggested in the books I read by Michael Covel make the whole of the strategy a bit more complicated. I believe it is not only for stocks but any market showing regular trends, as the forex market does. Also commodities were among that traded by the turtles. I plan on writing the script myself since I originally only downloaded Zorro to backtest the strategy, what I would like to know is if I will be able to tell Zorro for instance to start on a certain account balance, or must that be programmed via a seperate variable that is then monitored by the script?
Last edited by wastinglight; 06/25/13 13:29.
|
|
|
Re: Has anyone developed a turtle trading script?
[Re: jcl]
#425046
06/25/13 20:01
06/25/13 20:01
|
Joined: May 2013
Posts: 245
swingtraderkk
Member
|
Member
Joined: May 2013
Posts: 245
|
wastinglight,
I've been looking at other implementations of the donchian channel system, all are profitable (so long as you select the correct assets) but not very and have high drawdowns, because trades can last months - this means losing years!!
Courtney Smith has some amendments in his book on how to make the Donchian system more profitable and others on here have posted a sample script to get you started.
I'm still intending to get back to working on them, but I'm still learning zorro so my skills are not yet up to trying the things I want to improve them, particularly adding on to profitable trades like the turtles.
I'm also not really clear on the turtle's selection of 20 day vs 55 day breakout depending on whether the last trade was a winner or loser. That sounds like it might be difficult to code.
I'm also in a rather confused situation, everything I thought I knew about trading is not really very profitable when I backtest with optimisation and WFO with zorro and accurately describes my manual trading, low long term profitability and large drawdowns.
I like jcl's logic in the tutorial and nothing I've been doing prior to zorro has backtested as well as even the simple workshop scripts!!
So where do I invest my efforts? testing and tweaking a few % out of my old methods or taking the plunge and trying to follow Ehlers, the statisticians and signal processing engineers. I'm a long way from understanding what even a Lowpass filer or Dominant cycle really is. I don't really get how wave frequencies are applied to price curves...
Back to the turtles, if you post the rules in english I'll have a stab at coding them.
Last edited by swingtraderkk; 06/25/13 20:03.
|
|
|
Re: Has anyone developed a turtle trading script?
[Re: swingtraderkk]
#425100
06/26/13 19:36
06/26/13 19:36
|
Joined: Jun 2013
Posts: 3
wastinglight
OP
Guest
|
OP
Guest
Joined: Jun 2013
Posts: 3
|
Hi swingtraderkk. Thank you for the reply. I also would like to trade my own version of the turtle system at some point(when I have tested some theories and backtested them). I do believe however that it is important to stick to the fundamentals of the original system. I think it is very important to stick with a system once you have decided on it, to give it a fair chance of working. Here is a link to the system : http://www.metastocktools.com/downloads/turtlerules.pdfThis is my interpretation of it: Money management: Risk between 2-4% of your notional account value. Notional account value is the same as your equity if you are on 0% drawdown for the calendar year. A 10% drawdown of your account will result in a 20% drop in your notional account value(the value from which you work out what the 2% risk is in dollar terms). So the idea with this system is that during big drawdowns you trade less and less risky, waiting for those trends to start coming, and then you go back to risking a bit more. 1. Firstly you use daily candlestick charts. 2. With Donchian channels on 20 and 55 day high and lows. 3. N is the variable used to represent volatility. ( It is the ATR of the past 20 days) 4. A stop loss is place at 2 times the ATR (2N) from entry.(This is a trailing stop-loss) 5. And the lot size is obviously then worked out from the 2% risk amount divided by the value in dolars of your 2N stop-loss. 6. Once your first order is entered, three additional orders in that same direction are placed 1/2N from the first. 7. So if the market does trend, you will have 4 entries that are 1/2N apart. 8. If you made profit from a 20-day breakout trade, you will skip the next 20-day breakout. 9. BUT, you will always trade a 55-day breakout. 10. If a loss is made from a 20-day breakout trade, you trade the next 20-day breakout again. 11. There are some additional rules regarding total trades in play, and correlated markets, but they might be really hard to program. I am still learning the programming of scripts, but if you could write up something in the meantime it will definetely help me, and I could even check if the logic follows the rules. One thing I wanted to test after reading what I think is a clue dropped in one of Covels books is to play around with the "20" and "55", the way I understand it there is a range in there that is more profittable. (Maybe 44 or whatever) Let me know if you have any questions. Thanks.
|
|
|
Re: Has anyone developed a turtle trading script?
[Re: wastinglight]
#489687
43 minutes ago
43 minutes ago
|
Joined: Oct 2025
Posts: 4
gbe
Guest
|
Guest
Joined: Oct 2025
Posts: 4
|
// ---- Real-trade scan buffers: rebuilt every bar, nothing carried ---- [R-33] #define SCAN_NA 16 // [R-33] max assets in the list #define SCAN_W 4 // [R-33] events kept per asset and bar int ScanFillN[SCAN_NA], ScanClsN[SCAN_NA]; var ScanFillID[64], ScanFillDir[64], ScanFillPx[64]; // [R-33] 64 = SCAN_NA * SCAN_W var ScanClsID[64], ScanClsPx[64]; int FltBars = 0, FltGreen = 0; // [R-22] filter census, trace only
// [R-33] Index of an asset in the list, -1 if absent. int scanIdx(string Name) { int i; for(i = 0; i < NumAssetsListed; i++) if(strcmp(Name, Assets[i]) == 0) return i; return -1; }
// [R-33] Slot of this bar's fill on asset k in direction Dir, -1 if none. int scanFill(int k, int Dir) { int j; for(j = 0; j < ScanFillN[k]; j++) if(j < SCAN_W && ScanFillDir[k*SCAN_W + j] == Dir) return j; return -1; }
// [R-33] Slot of this bar's close of trade ID on asset k, -1 if none. int scanCls(int k, var ID) { int j; for(j = 0; j < ScanClsN[k]; j++) if(j < SCAN_W && ScanClsID[k*SCAN_W + j] == ID) return j; return -1; }
// [R-28] Classify a closed signal by its move before costs, signed by direction. // [R-28] A move of exactly zero changes nothing. void classify(vars PrevLoss, vars HaveHist, int Dir, int How, var Move) { printf("\n@SIG,%s,%i,%i,%i,%.8f", Asset, (int)Bar, Dir, How, (var)Move); // [R-28] trace if(Move < 0) { PrevLoss[0] = 1; HaveHist[0] = 1; } else if(Move > 0) { PrevLoss[0] = 0; HaveHist[0] = 1; } }
function run() { // ---- Engine / mode setup ---------------------------------- [R-01..R-05] BarPeriod = 1440; // [R-01] daily bars BarOffset = 940; // [R-01] bar starts 15:40 in the bar time zone set(LOGFILE); // [R-03] export logs and statistics files set(TICKS); // [R-02] tick-precise intrabar simulation (D021) Verbose = 3; // [R-03] skipped trades + parameter errors // !! [R-03] Warning 047 verbosity is OBSERVED, not documented -- see 3.1.
// ---- Data source ------------------------------------------------ [R-04] // !! [R-04] '*' reads the year files but keeps Zorro's silent bare-file // !! fallback; the AssetFirstBar preflight below is the only control on it. History = "put your \\Zorro\\History\\*.t6"; // [R-04] NumYears = 20; // [R-05] simulation window EndDate = 20261001; // [R-05] window end pinned (D029 Add. 3)
// ---- Parameter block -------------- [R-07, R-08, R-24, R-26, R-27, R-31] int EntryDays = 20; // [R-07] System 1 entry channel, trading days int ExitDays = 10; // [R-07] System 1 exit channel, trading days int FailDays = 55; // [R-07] failsafe entry channel, trading days int NPeriod = 20; // [R-26] N = average true range over 20 days var StopMult = 2.0; // [R-27] hard stop at 2N from the fill price int BreakTicks = 1; // [R-08] strict break: price increments beyond the level
// ---- Warm-up ---------------------------------------------- [R-09, R-26] LookBack = 70; // [R-09] covers 55-day channel and 20-day N UnstablePeriod = 40; // [R-26] recursive TA-Lib indicators re-run over 40 bars
// ---- Order handling ------------------------------------- [R-10..R-13] EntryTime = 2; // [R-11] pending order lifetime, bars. INERT (see R-18) Fill = 2; // [R-12] alternative order filling (D023) Hedge = 0; // [R-13] netting by ASSET, not by algo
// ---- Universe --------------------------------------------------- [R-06] if(is(INITRUN)) assetList("AssetsDryRunP7"); // [R-06] frozen P7 (D019)
MaxLong = 1; // [R-10] one position per side per asset MaxShort = 1; // [R-10] magnitude live, sign inert (see R-18)
// ---- Real-trade scan: this bar's fills and closes ------------- [R-33] // [R-33] Above the asset loop: enumeration loops cannot be nested. int si, sk, ss; for(si = 0; si < SCAN_NA; si++) { ScanFillN[si] = 0; ScanClsN[si] = 0; } for(past_trades) { if(TradeID != 0) { sk = scanIdx(TradeAsset); if(sk >= 0) { // !! [R-33] TradeInt is 0 on a new trade: OBSERVED, not documented. if(TradeInt[15] == 0) { // [R-33] fill not seen yet TradeInt[15] = 1; if(ScanFillN[sk] < SCAN_W) { ss = sk*SCAN_W + ScanFillN[sk]; ScanFillID[ss] = TradeID; ScanFillDir[ss] = ifelse(TradeIsLong, 1, -1); ScanFillPx[ss] = TradePriceOpen; } ScanFillN[sk] += 1; } if(TradeIsClosed && TradeInt[14] == 0) { // [R-33] close not seen yet TradeInt[14] = 1; if(ScanClsN[sk] < SCAN_W) { ss = sk*SCAN_W + ScanClsN[sk]; ScanClsID[ss] = TradeID; ScanClsPx[ss] = TradePriceClose; } ScanClsN[sk] += 1; } } } } if(is(EXITRUN)) printf("\n@FGR,%i,%i", FltGreen, FltBars); // [R-22] filter census, trace only
for(listed_assets) { // [R-25] for(listed_assets) only assigns Asset; it does not select. if(!asset(Asset)) continue;
// [R-04] Reject any asset whose history does not cover the window. if(AssetFirstBar > 0) { printf("\n SKIP %s -- history starts at bar %i/%i", Asset, (int)AssetFirstBar, (int)NumBars); continue; }
// [R-08] Read PIP inside the loop: it is an asset-specific parameter. var BreakOffset = BreakTicks * PIP;
// ---- Price series ------------------------------------------------ vars H = series(priceHigh()); vars L = series(priceLow());
// ---- Rolling trading-day channels ------------------ [R-15, R-25] // [R-25] Per-asset state lives in series(), NEVER in a bare static. // [R-15] Carried as series so the code can read the level that was // actually RESTING during a given bar -- see R-16. vars ChEntryHigh = series(0), ChEntryLow = series(0); vars ChExitLow = series(0), ChExitHigh = series(0); vars ChFailHigh = series(0), ChFailLow = series(0);
ChEntryHigh[0] = MaxVal(H, EntryDays); // 20-day high ChEntryLow[0] = MinVal(L, EntryDays); // 20-day low ChExitLow[0] = MinVal(L, ExitDays); // 10-day low ChExitHigh[0] = MaxVal(H, ExitDays); // 10-day high ChFailHigh[0] = MaxVal(H, FailDays); // 55-day high ChFailLow[0] = MinVal(L, FailDays); // 55-day low
// ---- Volatility primitive N -------------------------------- [R-26] vars N = series(ATR(NPeriod));
// ---- Warm-up counter ---------------------------------------- [R-09] vars BarsSeen = series(0); BarsSeen[0] = BarsSeen[1] + 1;
// ---- Shadow-tracker series -------------------- [R-22, R-25, R-28] // [R-25] Created ABOVE the warm-up gate: series() calls must never be // skipped by a bar-varying condition. Only their [0] elements are set // conditionally, further down. vars ShDir = series(0); // shadow signal: 0 none, +1 long, -1 short vars ShEntry = series(0); // shadow entry price (level + break offset) vars ShAdv = series(0); // [R-28] the 2N adverse level, fixed at signal vars PrevLoss = series(0); // 1 = last CLOSED shadow signal was a loser vars HaveHist = series(0); // 1 = at least one shadow signal has closed
ShDir[0] = ShDir[1]; ShEntry[0] = ShEntry[1]; ShAdv[0] = ShAdv[1]; PrevLoss[0] = PrevLoss[1]; HaveHist[0] = HaveHist[1];
// ---- Real-trade link series ---------------------------- [R-33, R-25] vars TrkID = series(0); // [R-33] real trade tied to the open signal, 0 none vars PlL = series(0); // [R-33] 1 = a 20-day long entry rested this bar vars PlS = series(0); // [R-33] 1 = a 20-day short entry rested this bar TrkID[0] = TrkID[1];
// [R-09] Warm-up gate. Every series() call for this asset is ABOVE it. if(BarsSeen[0] < 70) continue;
// ---- Level-or-distance preflight -------------------- [R-17, R-32] var HalfPrice = 0.5 * priceClose(); bool badEntryHigh = (ChEntryHigh[0] < HalfPrice); bool badEntryLow = (ChEntryLow[0] < HalfPrice); bool badExitLow = (ChExitLow[0] < HalfPrice); bool badExitHigh = (ChExitHigh[0] < HalfPrice); bool badFailHigh = (ChFailHigh[0] < HalfPrice); bool badFailLow = (ChFailLow[0] < HalfPrice); bool badStopDist = (StopMult * N[0] >= HalfPrice);
if(badEntryHigh || badEntryLow || badExitLow || badExitHigh || badFailHigh || badFailLow) printf("\n!!! %s bar %i: channel level below half the asset price %.5f (EntryHigh %.5f, EntryLow %.5f, ExitLow %.5f, ExitHigh %.5f, FailHigh %.5f, FailLow %.5f) -- Zorro reads such a value as a DISTANCE, not a level (stop.htm).", Asset, (int)Bar, (var)HalfPrice, (var)ChEntryHigh[0], (var)ChEntryLow[0], (var)ChExitLow[0], (var)ChExitHigh[0], (var)ChFailHigh[0], (var)ChFailLow[0]); if(badStopDist) printf("\n!!! %s bar %i: 2N stop distance %.5f is at or above half the asset price %.5f -- Zorro would read it as a LEVEL, not a distance (stop.htm).", Asset, (int)Bar, (var)(StopMult * N[0]), (var)HalfPrice); // [R-32] Exit levels are NOT gated -- suppressing an exit removes protection. if(badExitLow || badExitHigh) printf("\n@XLV,%s,%i,%.8f,%.8f,%.8f", Asset, (int)Bar, (var)ChExitLow[0], (var)ChExitHigh[0], (var)HalfPrice);
// ---- One-bar order-placement lag -------------------------- [R-16] // [R-16] Convention: every TEST reads [1], every ORDER is placed with [0]. var RestEntryHigh = ChEntryHigh[1]; var RestEntryLow = ChEntryLow[1]; var RestExitLow = ChExitLow[1]; var RestExitHigh = ChExitHigh[1]; var RestFailHigh = ChFailHigh[1]; var RestFailLow = ChFailLow[1]; var RestN = N[1];
// [R-08] Entry breaks are STRICT: the offset is in the test and in the // order, so shadow and real fill on the same bar at the same price. bool hitEntryHigh = (H[0] >= RestEntryHigh + BreakOffset); bool hitEntryLow = (L[0] <= RestEntryLow - BreakOffset); // [R-20] Exit stops are taken AT the level -- the break rule is on entries. bool hitExitLow = (L[0] <= RestExitLow); bool hitExitHigh = (H[0] >= RestExitHigh);
// ---- Shadow signal bookkeeping ---------- [R-22, R-28, R-21, R-33] int ScanK = scanIdx(Asset); // [R-33] int RealClosed = 0, Slot = -1, FillSlot = -1, OpenDir = 0; var RealExit = 0, Rested20 = 0; if(TrkID[0] != 0) { Slot = scanCls(ScanK, TrkID[0]); if(Slot >= 0) { RealClosed = 1; RealExit = ScanClsPx[ScanK*SCAN_W + Slot]; } } int ShStart = ShDir[0]; // [R-22] shadow state at bar start if(ShStart != 0 && TrkID[0] != 0) { // [R-33] A taken breakout closes with its real trade, never on the shadow tests. if(RealClosed) { classify(PrevLoss, HaveHist, ShStart, 3, (RealExit - ShEntry[0]) * ShStart); ShDir[0] = 0; } } else if(ShStart > 0) { // [R-28] 2N adverse excursion tested BEFORE the 10-day exit. if(L[0] <= ShAdv[0]) { classify(PrevLoss, HaveHist, 1, 1, ShAdv[0] - ShEntry[0]); ShDir[0] = 0; } else if(hitExitLow) { classify(PrevLoss, HaveHist, 1, 2, RestExitLow - ShEntry[0]); ShDir[0] = 0; } } else if(ShStart < 0) { if(H[0] >= ShAdv[0]) { classify(PrevLoss, HaveHist, -1, 1, ShEntry[0] - ShAdv[0]); ShDir[0] = 0; } else if(hitExitHigh) { classify(PrevLoss, HaveHist, -1, 2, ShEntry[0] - RestExitHigh); ShDir[0] = 0; } } if(RealClosed) TrkID[0] = 0; if(ShStart == 0) { // [R-21] Outside-bar tie-break: long tested first, deterministically. if(hitEntryHigh) OpenDir = 1; else if(hitEntryLow) OpenDir = -1; // [R-22] No same-bar close: a shadow opened here is tested from // the NEXT bar. } else if(ShDir[0] == 0) // [R-22] closed on this bar { // [R-22] An OPPOSITE break on the closing bar opens a new signal. if(ShStart > 0 && hitEntryLow) OpenDir = -1; else if(ShStart < 0 && hitEntryHigh) OpenDir = 1; } if(OpenDir != 0) { ShDir[0] = OpenDir; if(OpenDir > 0) ShEntry[0] = RestEntryHigh + BreakOffset; else ShEntry[0] = RestEntryLow - BreakOffset; ShAdv[0] = ShEntry[0] - OpenDir * StopMult * RestN; // [R-28] // [R-33] Taken: a same-direction fill this bar, from a 20-day order rested last bar. FillSlot = scanFill(ScanK, OpenDir); Rested20 = PlS[1]; if(OpenDir > 0) Rested20 = PlL[1]; if(FillSlot >= 0 && Rested20 == 1) { TrkID[0] = ScanFillID[ScanK*SCAN_W + FillSlot]; ShEntry[0] = ScanFillPx[ScanK*SCAN_W + FillSlot]; // [R-33] the real fill Slot = scanCls(ScanK, TrkID[0]); if(Slot >= 0) { // [R-33] The real trade also closed on its fill bar. classify(PrevLoss, HaveHist, OpenDir, 3, (ScanClsPx[ScanK*SCAN_W + Slot] - ShEntry[0]) * OpenDir); ShDir[0] = 0; TrkID[0] = 0; } } }
// ---- Previous-signal filter ------------------------ [R-22, R-23] // [R-22] Read AFTER this bar's shadow update. // [R-23] First signal on each asset is taken: there is no previous one. bool filterOK = (HaveHist[0] == 0) || (PrevLoss[0] > 0); FltBars += 1; if(filterOK) FltGreen += 1; // [R-22] census, trace only
// ---- Real orders ---- [R-14, R-18..R-21, R-27, R-29..R-32] if(NumOpenLong > 0) { // [R-19] Cancel the opposite pending order left alive by the fill. exitShort(0); // [R-20, R-30] Ratcheted 10-day channel exit, re-issued each bar at // the current level; it can only tighten. [R-27] the 2N stop stays // attached and the nearer of the two closes the trade. exitLong(0, ChExitLow[0]); } else if(NumOpenShort > 0) { exitLong(0); exitShort(0, ChExitHigh[0]); } else { // [R-18] Cancel yesterday's unfilled orders, then re-place fresh. // Reached ONLY when flat -- the branch structure IS the guard. exitLong(0); exitShort(0);
Stop = StopMult * N[0]; // [R-27] 2N hard stop, a DISTANCE to the fill price
// [R-29] Filter green: the 20-day levels. Filter red: the 55-day failsafe. var LongLevel = ChEntryHigh[0]; var ShortLevel = ChEntryLow[0]; bool badLong = badEntryHigh; bool badShort = badEntryLow; if(!filterOK) { LongLevel = ChFailHigh[0]; badLong = badFailHigh; ShortLevel = ChFailLow[0]; badShort = badFailLow; }
// [R-14] Family B: resting stop orders AT the level, not close tests. // [R-08] Entry carries the break offset. // [R-32] An order the engine would misread is suppressed, per side. if(badLong || badStopDist) printf("\n@SUP,%s,%i,L,%.8f,%.8f,%.8f", Asset, (int)Bar, (var)LongLevel, (var)HalfPrice, (var)(StopMult * N[0])); else { enterLong(1, LongLevel + BreakOffset); PlL[0] = filterOK; // [R-33] 1 = rested at the 20-day level }
if(badShort || badStopDist) printf("\n@SUP,%s,%i,S,%.8f,%.8f,%.8f", Asset, (int)Bar, (var)ShortLevel, (var)HalfPrice, (var)(StopMult * N[0])); else { enterShort(1, ShortLevel - BreakOffset); PlS[0] = filterOK; // [R-33] 1 = rested at the 20-day level } } } }
|
|
|
|