Unit SimpleCandles; Interface Implementation Uses DataNodes, GenericDataNodes, AlertBase, StandardCandles, Prices, StandardPlaceHolders, GenericTosDataNode, IntradaySma, SynchronizedTimers, SysUtils, Math, StrUtils, Classes; //////////////////////////////////////////////////////////////////////// // TStandardCandleChart //////////////////////////////////////////////////////////////////////// { For many of the candle alerts we look at a standard chart with 40 candles. This seems to be implied in the definition of "big" and "small" and other vague words used in the defintions associated with these patterns. As a bonus, we weed out all of the stocks which don't trade much because they won't have 40 consecutive candles! Additionally, some of the alerts below take advantage of the fact that they can look back a small fixed number of candles because they know that we must have at least 40 candles. Because this is so standard, we make this a seperate data node, and we avoid recomputing a lot of work each time. } Const StandardChartWidth = 40; Type TStandardCandleChart = Class(TDataNodeWithStringKey) Private FChartHeight : Double; CandleData : TStandardCandles; FInitialized : Boolean; FNewValidData : Boolean; Procedure NewCandleData; Procedure Initialize; Constructor Create(Symbol : String; MinutesPerBar : Byte); Protected Class Function CreateNew(Data : String) : TDataNodeWithStringKey; Override; Public Class Procedure Find(Symbol : String; MinutesPerBar : Byte; OnChange : TThreadMethod; Out Node : TStandardCandleChart; Out Link : TDataNodeLink); Function GetCandles : TCandleArray; Property ChartHeight : Double Read FChartHeight; Function GetMinutesPerBar : Integer; Function ChartHeightValid : Boolean; // The original users of this class expected a callback only when (a) // there was a new candle and (b) the data was valid. Those consumers // would only read from this class during the callback. // // Now this class is more like a general purpose data node. It can // be read at any time. It will make callbacks even when the data is // not valid (i.e. when it switches from invalid to valid). It can // now notify listeners when its initialization is complete. // // The older users of this class should ignore any callbacks unless this // property is true. Property NewValidData : Boolean Read FNewValidData; End; Function TStandardCandleChart.ChartHeightValid : Boolean; Begin Result := ChartHeight > 0.0 End; Function TStandardCandleChart.GetMinutesPerBar : Integer; Begin Result := CandleData.GetMinutesPerBar End; Procedure TStandardCandleChart.Initialize; Begin If Not FInitialized Then NewCandleData End; Procedure TStandardCandleChart.NewCandleData; Var Candles : TCandleArray; ChartTop, ChartBottom : Double; I : Integer; Begin { Do we need to check the epoch here? I think that only applies to filters, and other places where we have multiple paths to the same data. } Candles := GetCandles; If Length(Candles) < StandardChartWidth Then FChartHeight := 0.0 Else Begin ChartTop := -MaxDouble; ChartBottom := MaxDouble; For I := Pred(Length(Candles)) DownTo Length(Candles) - StandardChartWidth Do Begin ChartTop := Max(ChartTop, Candles[I].High); ChartBottom := Min(ChartBottom, Candles[I].Low) End; FChartHeight := ChartTop - ChartBottom End; FNewValidData := FInitialized And ChartHeightValid; // We notify the listeners on every call, mostly for simplicity. It would // be hard to explain the exact cases when we were doing the callback, // otherwise. I had some interesting thoughts on optimiztion, but the // confusion caused by this would have been too much. NotifyListeners; FNewValidData := False; // It can only be *new* durring the callback! FInitialized := True End; Constructor TStandardCandleChart.Create(Symbol : String; MinutesPerBar : Byte); Var Link : TDataNodeLink; Begin Inherited Create; TStandardCandles.Find(Symbol, MinutesPerBar, NewCandleData, CandleData, Link); AddAutoLink(Link); DoInCorrectThread(Initialize) End; Function TStandardCandleChart.GetCandles : TCandleArray; Begin Result := CandleData.GetHistory End; Class Function TStandardCandleChart.CreateNew(Data : String) : TDataNodeWithStringKey; Begin Assert(Data <> ''); Result := Create(Copy(Data, 2, MaxInt), Byte(Data[1])) End; Class Procedure TStandardCandleChart.Find(Symbol : String; MinutesPerBar : Byte; OnChange : TThreadMethod; Out Node : TStandardCandleChart; Out Link : TDataNodeLink); Var TempNode : TDataNodeWithStringKey; Begin FindCommon(TStandardCandleChart, Char(MinutesPerBar) + Symbol, OnChange, TempNode, Link); Node := TempNode As TStandardCandleChart End; //////////////////////////////////////////////////////////////////////// // TDoji //////////////////////////////////////////////////////////////////////// Type TDoji = Class(TAlert) Private CandleData : TStandardCandleChart; Procedure NewData; Protected Constructor Create(Params : TParamList); Override; End; Constructor TDoji.Create(Params : TParamList); Var Symbol : String; MinutesPerBar : Integer; Link : TDataNodeLink; Begin Assert(Length(Params)=2, 'Expected params: (Symbol, Minutes / Bar)'); Symbol := Params[0]; MinutesPerBar := Params[1]; Inherited Create; TStandardCandleChart.Find(Symbol, MinutesPerBar, NewData, CandleData, Link); AddAutoLink(Link) End; Procedure TDoji.NewData; Const MinHeight = 0.09; Var Msg : String; Candles : TCandleArray; Begin Candles := CandleData.GetCandles; If CandleData.NewValidData Then With Candles[Pred(Length(Candles))] Do If (Open = Close) And ((High - Low) > CandleData.ChartHeight * MinHeight) Then Begin If Open = High Then Msg := 'Dragonfly Doji' Else If Open = Low Then Msg := 'Gravestone Doji' Else If ((High - Open) > CandleData.ChartHeight * MinHeight) And ((Open - Low) > CandleData.ChartHeight * MinHeight) Then Msg := 'Long-Legged Doji' Else Msg := 'Doji'; Report(IntToStr(CandleData.GetMinutesPerBar) + ' minute ' + Msg) End End; //////////////////////////////////////////////////////////////////////// // TTraditionalChannelBreak //////////////////////////////////////////////////////////////////////// Type TTraditionalChannelBreak = Class(TAlert) Private CandleData : TStandardCandleChart; TosData : TGenericTosDataNode; LastReportLevel, PreviousLastReportLevel : Integer; FUp : Boolean; CurrentlyConsolidated : Boolean; BasePrice, LevelSize : Double; Procedure NewCandleData; Procedure NewTosData; Protected Constructor Create(Params : TParamList); Override; End; Constructor TTraditionalChannelBreak.Create(Params : TParamList); Var Symbol : String; MinutesPerBar : Integer; Link : TDataNodeLink; Begin Assert(Length(Params)=3, 'Expected params: (Symbol, Minutes / Bar, Up)'); Symbol := Params[0]; MinutesPerBar := Params[1]; FUp := Params[2]; Inherited Create; TStandardCandleChart.Find(Symbol, MinutesPerBar, NewCandleData, CandleData, Link); AddAutoLink(Link); TGenericTosDataNode.Find(Symbol, NewTosData, TosData, Link); AddAutoLink(Link); DoInCorrectThread(NewCandleData) End; Procedure TTraditionalChannelBreak.NewCandleData; Var Candles : TCandleArray; I : Integer; Top, Bottom, PotentialTop, PotentialBottom : Double; Begin PreviousLastReportLevel := LastReportLevel; LastReportLevel := 0; CurrentlyConsolidated := False; Candles := CandleData.GetCandles; If CandleData.ChartHeightValid Then Begin LevelSize := CandleData.ChartHeight * 0.09; I := 1; Top := -MaxDouble; Bottom := MaxDouble; Repeat If I > StandardChartWidth Then Break; With Candles[Length(Candles) - I] Do Begin PotentialTop := Max(Top, Max(Open, Close)); PotentialBottom := Min(Bottom, Min(Open, Close)) End; If PotentialTop - PotentialBottom > LevelSize Then Break; Top := PotentialTop; Bottom := PotentialBottom; Inc(I) Until False; Dec(I); // I is now the number of candles in the consolidation. CurrentlyConsolidated := I >= 7; If FUp Then BasePrice := Top Else BasePrice := Bottom End Else CurrentlyConsolidated := False; End; Procedure TTraditionalChannelBreak.NewTosData; Var Last : PTosData; Level : Integer; Quality : Double; Msg : String; Begin If CurrentlyConsolidated And TosData.IsValid Then Begin Last := TosData.GetLast; If (Not Last^.FormT) And (Last^.EventType = etNewPrint) Then Try If FUp Then Quality := Last^.Price - BasePrice Else Quality := BasePrice - Last^.Price; Level := Ceil(Quality / LevelSize); If Level > LastReportLevel Then Begin If Level > PreviousLastReportLevel Then Begin If FUp Then Msg := 'consolidation breakout' Else Msg := 'consolidation breakdown'; If Level = 2 Then Msg := Msg + ' with follow through' Else If Level > 2 Then Msg := Msg + ' with significant follow through'; Report(IntToStr(CandleData.GetMinutesPerBar) + ' minute ' + Msg, Quality) End; LastReportLevel := Level End Except // Ceil and divide can both throw exceptions. End End End; //////////////////////////////////////////////////////////////////////// // THammerOrHangingMan //////////////////////////////////////////////////////////////////////// { Definition of a hammer: Scale: Start with the standard 40 candles of history. The difference between the highest high and the lowest low on the chart is the range for the chart. Shape: Look at the last candle. There is no top wick. The size of the bottom wick must be at least twice as big as the body. The size of the body must be at least 2% of the range of the chart. (A Doji cannot be a hammer.) Downtrend: Draw a box around the previous 9 candles. The bottom of that box is the lowest low of those candles. The top is the highest high. Draw a line separating the bottom quarter of the box from the top three quarters. The top of the hammer must be below that line. Quality: A = The length of the bottom wick of the last candle. B = The bottom of the second to last candle minus the top of the last candle. This is the gap down, and it might be negative. This is not our standard defintion of gap, but the low of the previous, minus the high of the current. C = The range for the chart Quality = 100 * (A + (B / 2)) / C Listed as percent. Definition of a hanging man: Scale and Shape: Same as hammer. Uptrend: Draw a box around the previous 9 candles. The bottom of that box is the lowest low of those candles. The top is the highest high. Draw a line separating the top quarter of the box from the bottom three quarters. The bottom of the body of the last candle must be above that line. Quality: A = The length of the bottom wick of the last candle. B = The bottom of the body of the last candle minus the high of the second to last candle. This is a distorted defintion of the gap up. C = The range for the chart Quality = 67 * (A + (B / 2)) / C In the hammer A and B are competing, so we can never get above 100%. In this pattern they can overlap, so we have to scale the result back. Listed as percent. In both cases quality cannot be negative. That makes the output and the help less confusing for the user. } Type THammerOrHangingMan = Class(TAlert) Private CandleData : TStandardCandleChart; FIsHammer : Boolean; Procedure NewData; Protected Constructor Create(Params : TParamList); Override; End; Constructor THammerOrHangingMan.Create(Params : TParamList); Var Symbol : String; MinutesPerBar : Integer; Link : TDataNodeLink; Begin Assert(Length(Params)=3, 'Expected params: (Symbol, Minutes / Bar, IsHammer)'); Symbol := Params[0]; MinutesPerBar := Params[1]; FIsHammer := Params[2]; Inherited Create; TStandardCandleChart.Find(Symbol, MinutesPerBar, NewData, CandleData, Link); AddAutoLink(Link) End; Procedure THammerOrHangingMan.NewData; Function GetCutoff(Candles : TCandleArray; Position : Double) : Double; Var I : Integer; Highest, Lowest : Double; Begin { GetBox } { Precondition: We know that we have at least 10 candles. } With Candles[Length(Candles) - 10] Do Begin Highest := High; Lowest := Low; End; For I := Length(Candles) - 9 To Length(Candles) - 2 Do With Candles[I] Do Begin Highest := Max(High, Highest); Lowest := Min(Low, Lowest) End; Result := (Highest - Lowest) * Position + Lowest End; { GetBox } Var Candles : TCandleArray; Gap, LastBodyTop, LastBodyBottom, Quality : Double; LastCandle, PreviousCandle : Integer; Valid : Boolean; Begin { THammerOrHangingMan.NewData } Candles := CandleData.GetCandles; If CandleData.NewValidData Then Begin LastCandle := Pred(Length(Candles)); PreviousCandle := Pred(LastCandle); With Candles[LastCandle] Do Begin If Open > Close Then Begin LastBodyTop := Open; LastBodyBottom := Close End Else Begin LastBodyTop := Close; LastBodyBottom := Open End; Valid := { There is no top wick. } (LastBodyTop = High) And { The size of the bottom wick must be at least twice as big as the body. } ((LastBodyTop - LastBodyBottom) * 2 <= LastBodyBottom - Low) And { The size of the body must be at least 2% of the range of the chart. } (LastBodyTop - LastBodyBottom >= CandleData.ChartHeight * 0.02); If Valid Then If FIsHammer Then Begin Gap := Candles[PreviousCandle].Low - LastBodyTop; Quality := (Gap/2 + (LastBodyBottom - Low)) / CandleData.ChartHeight * 100.0; Valid := Quality > 0; If Valid Then Valid := High < GetCutoff(Candles, 0.25); If Valid Then Report('Hammer', Quality) End Else Begin Gap := LastBodyBottom - Candles[PreviousCandle].High; Quality := (Gap/2 + (LastBodyBottom - Low)) / CandleData.ChartHeight * 100.0 * 2.0 / 3.0; Valid := Quality > 0; If Valid Then Valid := High > GetCutoff(Candles, 0.75); If Valid Then Report('Hanging Man', Quality) End End End End; { THammerOrHangingMan.NewData } //////////////////////////////////////////////////////////////////////// // TEngulfing //////////////////////////////////////////////////////////////////////// { Engulfing means that the top of the body of the last candle is higher than the top of the body of the previous candle and the bottom of the body of the last candle is lower than the body of the bottom of the previous candle. Bullish engulfing additionally requires that the last candle's close be higher than its open, and the previous candle's close be lower than its open. (Last candle is white or green, previous candle is black or red.) Bearish engulfing is exactly the opposite. Quality: http://www.streetauthority.com/terms/engulfing-candles.asp "The power of the engulfing candle is increased by two factors -- the size of the candle and the volume on the day it occurs." } Type TEngulfing = Class(TAlert) Private CandleData : TStandardCandleChart; FUp : Boolean; Procedure NewData; Protected Constructor Create(Params : TParamList); Override; End; Constructor TEngulfing.Create(Params : TParamList); Var Symbol : String; MinutesPerBar : Integer; Link : TDataNodeLink; Begin Assert(Length(Params)=3, 'Expected params: (Symbol, Minutes / Bar, Up)'); Symbol := Params[0]; MinutesPerBar := Params[1]; FUp := Params[2]; Inherited Create; TStandardCandleChart.Find(Symbol, MinutesPerBar, NewData, CandleData, Link); AddAutoLink(Link) End; Procedure TEngulfing.NewData; Const MinFirstHeightRatio = 0.025; MinDifferenceRatio = 0.01; Var Candles : TCandleArray; LastBodyTop, LastBodyBottom, PreviousBodyTop, PreviousBodyBottom : Double; MinFirstHeight, MinDifference : Double; I : Integer; RecentVolume : Integer; Quality : Double; Begin Candles := CandleData.GetCandles; If CandleData.NewValidData Then Begin If FUp Then Begin LastBodyTop := Candles[Pred(Length(Candles))].Close; LastBodyBottom := Candles[Pred(Length(Candles))].Open; PreviousBodyTop := Candles[Length(Candles) - 2].Open; PreviousBodyBottom := Candles[Length(Candles) - 2].Close End Else Begin LastBodyTop := Candles[Pred(Length(Candles))].Open; LastBodyBottom := Candles[Pred(Length(Candles))].Close; PreviousBodyTop := Candles[Length(Candles) - 2].Close; PreviousBodyBottom := Candles[Length(Candles) - 2].Open End; MinFirstHeight := CandleData.ChartHeight * MinFirstHeightRatio; MinDifference := CandleData.ChartHeight * MinDifferenceRatio; If (PreviousBodyTop - PreviousBodyBottom > MinFirstHeight) And (LastBodyTop - PreviousBodyTop > MinDifference) And (PreviousBodyBottom - LastBodyBottom > MinDifference) Then Begin Quality := (LastBodyTop - LastBodyBottom) / CandleData.ChartHeight * 100.0; RecentVolume := 0; For I := Length(Candles) - 10 To Pred(Length(Candles)) Do RecentVolume := RecentVolume + Candles[I].Volume; If RecentVolume > 0 Then Quality := Min(100.0, Quality * 0.5 + Candles[Pred(Length(Candles))].Volume / RecentVolume * 100.0); Report(IfThen(FUp, 'Bullish Engulfing', 'Bearish Engulfing'), Quality) End End End; //////////////////////////////////////////////////////////////////////// // TDarkCloudCoverOrPeircing //////////////////////////////////////////////////////////////////////// { Dark cloud cover is a white candle followed by a black candle. The white candle should have a large body. We will say a minumum of 4% of the height of the chart. The open of the second candle must be higher than the high of the previous candle. The close must be in the lower half of the body of the candle. Being closer to the bottom is better. I add the case that the top of the body of the second candle must be at least 0.5% over the top of the body of the first, to match the sample charts better. I also added the requirement that the bottom of the last candle body must be above the bottom of the previous candle body, to match the charts. Quality: http://www.streetauthority.com/terms/darkcloudcover.asp : The larger the penetration of the previous candle (that is, the closer this candle is to being a bearish engulfing), the more powerful the signal. It also says that the larger the first candle is, the better. http://candlestickforum.com/PPF/Parameters/16_498_/candlestick.asp : Talks about those two, plus the volume of the last two candles should be above average, plus the size of the gap is helpful. We use the size of the body of the first candle directly. This is worth 25% We use the size of the body of the second candle for two purposes. A bigger candle may mean a bigger gap, or a bigger penetration. As either of those grows, so does the candle. This is worth 25% Finally we comare the last two candle's volume to the last 20. If the average of the last two is the same and the average of the previous 18, then that's 50%. But it's not bounded, so we have to limit it ourselves. Dark cloud cover is negative. The chart was going up, but now it's going down. Peircing pattern. Same thing but up side down. } Type TDarkCloudCoverOrPeircing = Class(TAlert) Private CandleData : TStandardCandleChart; FUp : Boolean; Procedure NewData; Protected Constructor Create(Params : TParamList); Override; End; Constructor TDarkCloudCoverOrPeircing.Create(Params : TParamList); Var Symbol : String; MinutesPerBar : Integer; Link : TDataNodeLink; Begin Assert(Length(Params)=3, 'Expected params: (Symbol, Minutes / Bar, Up)'); Symbol := Params[0]; MinutesPerBar := Params[1]; FUp := Params[2]; Inherited Create; TStandardCandleChart.Find(Symbol, MinutesPerBar, NewData, CandleData, Link); AddAutoLink(Link) End; Procedure TDarkCloudCoverOrPeircing.NewData; Var Candles : TCandleArray; LastBodyTop, LastBodyBottom, PreviousCandleHigh, PreviousBodyTop, PreviousBodyBottom, PreviousBodyMiddle : Double; I : Integer; RecentVolume : Integer; Quality : Double; Begin Candles := CandleData.GetCandles; If CandleData.NewValidData Then Begin If FUp Then Begin { Flip the image up side down so that we can use the Dark Cloud Covering definition from above. } LastBodyTop := -Candles[Pred(Length(Candles))].Open; LastBodyBottom := -Candles[Pred(Length(Candles))].Close; PreviousBodyTop := -Candles[Length(Candles) - 2].Close; PreviousBodyBottom := -Candles[Length(Candles) - 2].Open; PreviousCandleHigh := -Candles[Length(Candles) - 2].Low End Else Begin { This is really a Dark Cloud Covering. } LastBodyTop := Candles[Pred(Length(Candles))].Open; LastBodyBottom := Candles[Pred(Length(Candles))].Close; PreviousBodyTop := Candles[Length(Candles) - 2].Close; PreviousBodyBottom := Candles[Length(Candles) - 2].Open; PreviousCandleHigh := Candles[Length(Candles) - 2].High End; PreviousBodyMiddle := (PreviousBodyTop + PreviousBodyBottom) / 2.0; If (PreviousBodyTop - PreviousBodyBottom > CandleData.ChartHeight * 0.04) And (LastBodyTop > PreviousCandleHigh) And (LastBodyTop - PreviousBodyTop > CandleData.ChartHeight * 0.005) And (PreviousBodyMiddle > LastBodyBottom) And (LastBodyBottom > PreviousBodyBottom) Then Begin Quality := ((LastBodyTop - LastBodyBottom) + (PreviousBodyTop - PreviousBodyBottom)) / CandleData.ChartHeight * 50.0; RecentVolume := 0; For I := Length(Candles) - 20 To Pred(Length(Candles)) Do RecentVolume := RecentVolume + Candles[I].Volume; If RecentVolume > 0 Then Quality := Min(100.0, Quality * 0.5 + (Candles[Pred(Length(Candles))].Volume + Candles[Length(Candles) - 2].Volume) / RecentVolume * 100.0); Report(IfThen(FUp, 'Piercing Pattern', 'Dark Cloud Cover'), Quality) End End End; //////////////////////////////////////////////////////////////////////// // TToppingTail // // The definition of a topping tail seems to be a bit vauge. // https://www.shadowtrader.net/index.aspx?pg=glossary.html has some // specifics, but that seems to be contradicted by some of the pictures // in http://www.billnewell.net/core_trading_velez.pdf. The second // document seems especially vague and self contradictory, but it was // written by Oliver Valez, and we are creating this alert in large part // for Oliver. The definition below the best I could get from that // document. I took the spirit of the words, and stretched out limits // to match the worst of the pictures. // // For a topping tail we need the last bar to be at least 50% as big (from // high to low) as the previous bar. The three bars before the last bar need // to be green, close above open. The top wick (the "tail") of the last // candle has to be at least 0.6 times the size of the body of the last // candle, and at least 1/3 of the entire size of the last candle. // // Here were my initial thoughts on quality. Unfortunately all the stuff // that sounds good would have gotten rid of a lot // of the examples in that paper. It would be nice to see each candle // above the previous one. (Higher highs and higher lows.) It would // be nice to see the bars each at least 5% of the size of the chart. // The top tail of the last candle should be as long as possible, // compared to the chart as a whole. The body of the last candle should // be small compared to the chart as a whole. Having more than 3 // consecutive green candles before is good; more is better. If the // second to last candle as a good tail that's good. More in a row is // better. // // After viewing some a lot of examples from the alert server, I came // up with simpler quality standards. The wick of the last bar should // be as big as possible. The three previous bars should be as big as // possible. This is simple. It makes the pattern obvious on the // chart, so it looks good. And it gets rid of some of the random // stuff. We look at the entire candle for the first three, not just // the wick or the body. // // A Bottoming Tail is exactly the same as a Topping Tail, but the chart // is flipped over. //////////////////////////////////////////////////////////////////////// Type TToppingTail = Class(TAlert) Private CandleData : TStandardCandleChart; FTop : Boolean; Procedure NewData; Protected Constructor Create(Params : TParamList); Override; End; Constructor TToppingTail.Create(Params : TParamList); Var Symbol : String; MinutesPerBar : Integer; Link : TDataNodeLink; Begin Assert(Length(Params)=3, 'Expected params: (Symbol, Minutes / Bar, Top)'); Symbol := Params[0]; MinutesPerBar := Params[1]; FTop := Params[2]; Inherited Create; TStandardCandleChart.Find(Symbol, MinutesPerBar, NewData, CandleData, Link); AddAutoLink(Link) End; Procedure TToppingTail.NewData; Function CorrectDirection(Const Candle : TSingleCandle) : Boolean; Begin { CorrectDirection } If FTop Then // We are looking for a green candle. Result := Candle.Close > Candle.Open Else Result := Candle.Open > Candle.Close End; { CorrectDirection } Var Candles : TCandleArray; LastBodySize, LastWickSize, LastCandleSize, PrevCandleSize, Quality : Double; LastCandle : Integer; Begin { TToppingTail.NewData } Candles := CandleData.GetCandles; If CandleData.NewValidData Then Begin LastCandle := Pred(Length(Candles)); If CorrectDirection(Candles[Pred(LastCandle)]) And CorrectDirection(Candles[LastCandle - 2]) And CorrectDirection(Candles[LastCandle - 3]) Then Begin With Candles[LastCandle] Do Begin // The last candle can be red or green regardless as of the // type of alert. LastBodySize := Abs(Close-Open); If FTop Then LastWickSize := High - Max(Open, Close) Else LastWickSize := Min(Open, Close) - Low; LastCandleSize := High - Low End; With Candles[Pred(LastCandle)] Do PrevCandleSize := High - Low; If (LastCandleSize >= PrevCandleSize * 0.5) And (LastWickSize >= LastBodySize * 0.6) And (LastWickSize >= LastCandleSize * 0.3333333) Then Begin // Quality can be anywhere between 0 and 100. Each of the // last four candles contributes 0-25. For the most recent // candle we look at the height of the tail relative to the // height of the entire chart. For the three previous candles // we look at the height of the entire candle, low to high. Quality := LastWickSize + PrevCandleSize; With Candles[LastCandle-2] Do Quality := Quality + High - Low; With Candles[LastCandle-3] Do Quality := Quality + High - Low; Quality := Quality / CandleData.ChartHeight * 25.0; Report(IfThen(FTop, 'Topping Tail', 'Bottoming Tail'), Quality) End End End End; { TToppingTail.NewData } //////////////////////////////////////////////////////////////////////// // TNarrowRangeBar // // These defintions come from a private memo from Oliver Vasquez at VCM: // // Narrow Range Buy Bar Scan: // Three Green Bars (close above open) or more followed by a bar that is // smaller (from high to low) than 25% of the average range of the past // 5 bars. // // Narrow Range Sell Bar Scan: // The same applies in reverse. See above // // I'm using the size of the bars as the quality, based on the way // we do it in TToppingTail. //////////////////////////////////////////////////////////////////////// Type TNarrowRangeBar = Class(TAlert) Private CandleData : TStandardCandleChart; FBuy : Boolean; Procedure NewData; Protected Constructor Create(Params : TParamList); Override; End; Constructor TNarrowRangeBar.Create(Params : TParamList); Var Symbol : String; MinutesPerBar : Integer; Link : TDataNodeLink; Begin Assert(Length(Params)=3, 'Expected params: (Symbol, Minutes / Bar, Buy)'); Symbol := Params[0]; MinutesPerBar := Params[1]; FBuy := Params[2]; Inherited Create; TStandardCandleChart.Find(Symbol, MinutesPerBar, NewData, CandleData, Link); AddAutoLink(Link) End; Procedure TNarrowRangeBar.NewData; Var LastHeight, PreviousHeights, Quality : Double; BodyHeight : Array[1..3] Of Double; // 1 is one candle back. 2 is two candles back. I : Integer; Candles : TCandleArray; LastCandle : Integer; Begin Candles := CandleData.GetCandles; If CandleData.NewValidData Then Begin LastCandle := Pred(Length(Candles)); For I := Low(BodyHeight) To High(BodyHeight) Do Begin With Candles[LastCandle - I] Do If FBuy Then // Looking for a green candle. BodyHeight[I] := Close - Open Else // Looking for a red candle. BodyHeight[I] := Open - Close; If BodyHeight[I] <= 0 Then Exit End; PreviousHeights := 0; For I := 1 To 5 Do With Candles[LastCandle - I] Do PreviousHeights := PreviousHeights + High - Low; With Candles[LastCandle] Do LastHeight := High - Low; If LastHeight * 20 >= PreviousHeights Then Exit; // Quality can be anywhere between 0 and 100. // The last bar contributes 0-20 based on how small it is. If the // high and low are the same, we get 20. If the size is the maximum // allowed to trigger the alert, we get 0. // Then we look at the body height of the previous three candles. // They should each be red or green depending on the direction of the // alert. If the body height is the entire height of the graph then // we call the candle "very" red or green and we add 10 for that // candle. As the body height goes closer to 0, the contribution to // quality approaches 0. // Finally we look at the total height of the last 5 candles // preceeding the most recent candle. Each of these can contribute // up to 10 each to the quality. If the height of the candle is the // entire height of the chart then the candle makes the full // contribution. Quality := (PreviousHeights + BodyHeight[1] + BodyHeight[2] + BodyHeight[3]) * 10 / CandleData.ChartHeight + 20 - LastHeight / PreviousHeights * 400; Report(IfThen(FBuy, 'Narrow Range Buy Bar', 'Narrow Range Sell Bar'), Quality) End End; //////////////////////////////////////////////////////////////////////// // TRisingSma //////////////////////////////////////////////////////////////////////// Type TRisingSma = Class(TAlert) Private CandleData : TStandardCandleChart; FBuy : Boolean; FConsecutive : Integer; Procedure NewData; Protected Constructor Create(Params : TParamList); Override; End; Constructor TRisingSma.Create(Params : TParamList); Var Symbol : String; MinutesPerBar : Integer; Link : TDataNodeLink; Begin Assert(Length(Params)=3, 'Expected params: (Symbol, Minutes / Bar, Buy)'); Symbol := Params[0]; MinutesPerBar := Params[1]; FBuy := Params[2]; Inherited Create; TStandardCandleChart.Find(Symbol, MinutesPerBar, NewData, CandleData, Link); AddAutoLink(Link) End; Procedure TRisingSma.NewData; Var Quality : Double; I, LastCandle, PreviousConsecutive : Integer; ConsecutiveStr : String; Candles : TCandleArray; Begin PreviousConsecutive := FConsecutive; FConsecutive := 0; // There are multiple ways to fail, and only one way to succeed, so we assume failure until proven otherwise. Candles := CandleData.GetCandles; If CandleData.NewValidData And (Length(Candles) >= 205) Then Begin LastCandle := Pred(Length(Candles)); If FBuy Then For I := LastCandle DownTo LastCandle - 4 Do Begin If Candles[I].Close <= Candles[I-8].Close Then Exit; If Candles[I].Close <= Candles[I-20].Close Then Exit End Else For I := LastCandle DownTo LastCandle - 4 Do Begin If Candles[I].Close >= Candles[I-8].Close Then Exit; If Candles[I].Close >= Candles[I-20].Close Then Exit End; If PreviousConsecutive = 0 Then FConsecutive := 5 Else FConsecutive := PreviousConsecutive + 1; Case FConsecutive Of 5 : ConsecutiveStr := '5'; 8 : ConsecutiveStr := '8'; 13 : ConsecutiveStr := '13'; 21 : ConsecutiveStr := '21'; 34 : ConsecutiveStr := '34'; 55 : ConsecutiveStr := '55'; 89 : ConsecutiveStr := '89'; 144 : ConsecutiveStr := '144'; Else Exit End; Quality := 0; For I := LastCandle DownTo LastCandle - 4 Do Quality := Quality + Candles[I].Close - Candles[I-200].Close; Quality := 100.0 - Abs(Quality / CandleData.ChartHeight / 2.0); Report(IfThen(FBuy, '8 and 20 period SMA both sloping up. ', '8 and 20 period SMA both sloping down. ') + ConsecutiveStr + ' periods.', Quality) End End; //////////////////////////////////////////////////////////////////////// // TWideRangeBar // // This is the definition of Wide Range Bar that Oliver publishes: // WRB -- the range of the bar is much greater than the average bar // // This is what he sent us: // A candle where each tail is less than 15% of the total bar, and the // total bar is at least twice the average of the last 20 bars. //////////////////////////////////////////////////////////////////////// Type TWideRangeBar = Class(TAlert) Private CandleData : TStandardCandleChart; Procedure NewData; Protected Constructor Create(Params : TParamList); Override; End; Constructor TWideRangeBar.Create(Params : TParamList); Var Symbol : String; MinutesPerBar : Integer; Link : TDataNodeLink; Begin Assert(Length(Params)=2, 'Expected params: (Symbol, Minutes / Bar)'); Symbol := Params[0]; MinutesPerBar := Params[1]; Inherited Create; TStandardCandleChart.Find(Symbol, MinutesPerBar, NewData, CandleData, Link); AddAutoLink(Link) End; Procedure TWideRangeBar.NewData; Var AverageRange, LastRange, WickSize : Double; Candles : TCandleArray; I : Integer; Begin SetLength(Candles, 0); // Silly Compiler Warning. If Not CandleData.NewValidData Then Exit; // We only need 21 candles for this alert. TStandardCandleChart // assures us that we have 40. Candles := CandleData.GetCandles; With Candles[Pred(Length(Candles))] Do Begin LastRange := High - Low; If LastRange <= 0.0 Then Exit; WickSize := High - Max(Open, Close); If WickSize > 0.15 * LastRange Then Exit; WickSize := Min(Open, Close) - Low; If WickSize > 0.15 * LastRange Then Exit End; AverageRange := 0.0; For I := Length(Candles) - 21 To Length(Candles) - 2 Do With Candles[I] Do AverageRange := AverageRange + (High - Low); AverageRange := AverageRange / 20.0; If LastRange > AverageRange * 2 Then Report('Wide Range Bar', LastRange) End; //////////////////////////////////////////////////////////////////////// // TGreenBarReversal // // This comes from Oliver / VCM. We pieced togather the exact // requirements from multiple places. // // * The alert will fire only at the end of a candle. // * The alert will require at least 3 bars running down, not counting the last bar. // o We will look at both types of running down, and report either. // o The alert specific filter will report the number of bars. // o The message will report the number of bars, and which type of running down // - We do this in part for Google to see different words on this page: http://www.trade-ideas.com/SingleAlertType/PFL75/75%25_pullback_from_lows.html // - And in part because of the number of times people have come back to me with confusion over how we define 3 down bars. // * The flip of the alert will be the red bar reversal (RBR) // * We do not care about the distance from the SMA. // o The user can do this himself with the other filters. // o Core Trader.PDF describes at least three different ways to use this. // * We require the close of the last candle to be above the 20 bar SMA if the SMA is rising, or below the SMA if the SMA is falling. // o This is independent of the direction of the alert. // o We considered several variations on this. // - The high must be above the rising sma or the low must be below the falling sma. // - The same as above, but for all candles in the pattern, not just the last one. // - Above or not too far below the rising sma. // o This seemed like the simplest definition which covered most if not all of the pictures. // // It seems like a lot of this is standard for Oliver's alerts. Perhaps the // wide range bar should have existed in two forms, depending on which // way we were going previously. Topping Tail has some of this built in, // but not all of it, and perhaps it should. We considered making more // of the common stuff into a seperate filter, but that doesn't always make // sense. In particular, you couldn't have an alert for every green or red // bar! // // If we did try to create a filter or library routine for the common stuff, // it would be different from a lot of the filters that we do. Currently, // if a filter looks at candle data, and an alert fires at the end of a // candle, we make sure that the filter updates before the alert does. If an // alert happens in the middle of a candle, the filter looks at the end of the // last complete candle. This filter, in particular the number of consecutive // up bars, would have to be exactly the opposite. In the case of a green // bar reveral, wide range bar, etc., we would want to know the number of // up bars up to and including the next to last one, but not the last one. // For alerts like running up which happen in the middle of a bar, we would // want to know the number of bars running up including the last complete one. // // Oliver's documents divide the strategies into three pieces. // 1) The trigger is a green bar in this case, but the narrow range bar, the // topping tail, the wide range bar, and many others are all other possible // triggers. Reading all the possibilities, the trigger seems almost // arbitrary. // 2) The setup before the trigger includes having several consecutive bars // all moving in the same direction. This is the most important part of // the strategy. // 3) The position of the price relative to the SMA and the direction the SMA // is moving is the final part. We split up the position relative to the // SMA and the direction of the SMA into two parts to better fit into our // structure. //////////////////////////////////////////////////////////////////////// Type TGreenBarReversal = Class(TAlert) Private CandleData : TStandardCandleChart; // If FGreen is true, we are looking for a chart which was going down, // but turned around and the *last* bar is green. If FGreen is false, // we are looking for the flip of that situation. SmaData : TGenericDataNode; FGreen : Boolean; Procedure NewData; Protected Constructor Create(Params : TParamList); Override; End; Constructor TGreenBarReversal.Create(Params : TParamList); Var Symbol : String; MinutesPerBar : Integer; Link : TDataNodeLink; Factory : IGenericDataNodeFactory; Begin Assert(Length(Params)=3, 'Expected params: (Symbol, Minutes / Bar, Green)'); Symbol := Params[0]; MinutesPerBar := Params[1]; FGreen := Params[2]; Inherited Create; TStandardCandleChart.Find(Symbol, MinutesPerBar, NewData, CandleData, Link); AddAutoLink(Link); Factory := CreateIntradaySmaFactory(MinutesPerBar, 20); Factory.SetValue(StandardSymbolPlaceHolder.GetName, Symbol); Factory.Find(Nil, SmaData, Link); AddAutoLink(Link) End; Procedure TGreenBarReversal.NewData; Var Candles : TCandleArray; I, LastCandle : Integer; HighLowCount, ColorCount : Integer; CandlePosition : Double; Quality : Integer; Description : String; Begin SetLength(Candles, 0); // Silly Compiler Warning. If Not CandleData.NewValidData Then Exit; If Not SmaData.IsValid Then // I doubt if this is possible, considering that the CandleData is // valid, but it doesn't hurt to check. Exit; Candles := CandleData.GetCandles; LastCandle := Pred(Length(Candles)); // Make sure the last bar is the right color. With Candles[LastCandle] Do If (FGreen And (Close <= Open)) Or ((Not FGreen) And (Open <= Close)) Then Exit; // Check the position relative to the rising or falling SMA. CandlePosition := Candles[LastCandle].Close - SmaData.GetDouble; If CandlePosition > 0.0 Then Begin // If the last print is above the SMA, then we require that the SMA // has increased for each of the last 4 periods. This defination of // a "rising SMA" is copied from TRisingSma. Oliver uses that term a // lot but never really defines it. For I := LastCandle DownTo LastCandle - 4 Do If Candles[I].Close <= Candles[I-20].Close Then Exit End Else If CandlePosition < 0.0 Then Begin // If the last print is below the SMA, then we require that the SMA // has decreased for each of the last 4 periods. If the candle is // right on the SMA, then we don't enforce this rule at all. That's // not very likely but it's possible. The documentation doesn't cover // this case explicitly. It might be reasonable to say that anything // close the SMA should be treated in this same way. For I := LastCandle DownTo LastCandle - 4 Do If Candles[I].Close >= Candles[I-20].Close Then Exit End; // Check the number of candles before the last one all going in the same // direction. HighLowCount := 0; I := Pred(LastCandle); Repeat If FGreen Then Begin // These should be going down. If Candles[I].High >= Candles[Pred(I)].High Then Break; If Candles[I].Low >= Candles[Pred(I)].Low Then Break End Else Begin // These should be going up. If Candles[I].High <= Candles[Pred(I)].High Then Break; If Candles[I].Low <= Candles[Pred(I)].Low Then Break End; Inc(HighLowCount); Dec(I) Until I < 1; ColorCount := 0; I := Pred(LastCandle); Repeat With Candles[I] Do If (FGreen And (Close >= Open)) Or ((Not FGreen) And (Close <= Open)) Then Break; Inc(ColorCount); Dec(I) Until I < 0; If (ColorCount < 3) And (HighLowCount < 3) Then Exit; // We have an alert! Quality := Max(ColorCount, HighLowCount); If FGreen Then Begin Description := 'Green bar after ' + IntToStr(Quality); If ColorCount >= HighLowCount Then Begin Description := Description + ' red bars'; If ColorCount = HighLowCount Then Description := Description + ' with' End; If ColorCount <= HighLowCount Then Description := Description + ' lower highs and lower lows' End Else Begin Description := 'Red bar after ' + IntToStr(Quality); If ColorCount >= HighLowCount Then Begin Description := Description + ' green bars'; If ColorCount = HighLowCount Then Description := Description + ' with' End; If ColorCount <= HighLowCount Then Description := Description + ' higher highs and higher lows' End; Report(Description, Quality) End; //////////////////////////////////////////////////////////////////////// // TRunningUpNow // // This is intended to be a very short term running alert. It's meant // to be very simple and easy to understand. Because the traditional // channel break works so well, this new alert is based on that. This // alert is basically a channel break without the requirement of a // channel, first. // // We use the same requirements for the size of the move. We start by // looking at the height of a 5 minute chart. But, there is no // guarantee that the 5 minute chart is valid, so we try larger time- // frames after that. We always start with the smallest time-frame // to get a tigher size, closer to a minute chart. // // We reset every minute. The channel breaks reset every 5, 10, 15, or // 30 minutes. We look at the last price at the end of the previous // minute. We don't require a complete candle for the previous minute; // we will look back as far as we need to to find the last print. // // A quick scan of the data shows that only 1/3 of the NASDAQ stocks // have enough 15 minute data to pass the 40 candle rule. That's a // shame, and I'd like to improve that. But I want to make this as // similar as possible to the channel breaks. //////////////////////////////////////////////////////////////////////// Type TRunningUpLevelStatus = (rulsUnknown, rulsInvalid, rulsValid); TRunningUpNow = Class(TAlert) Private CandleData : Array [0 .. 3] Of TStandardCandleChart; TosData : TGenericTosDataNode; MinuteTimer : TSynchronizedTimer; LastReportLevel, PreviousLastReportLevel : Integer; FUp : Boolean; BasePriceValid : Boolean; BasePrice, LevelSize : Double; LevelStatus : TRunningUpLevelStatus; Procedure NewCandleData; Procedure NewTosData; Procedure NewMinute; Procedure UpdateLevel(BasePrice : Double); Protected Constructor Create(Params : TParamList); Override; End; Constructor TRunningUpNow.Create(Params : TParamList); Var Symbol : String; Link : TDataNodeLink; Begin Assert(Length(Params)=2, 'Expected params: (Symbol, Up)'); Symbol := Params[0]; FUp := Params[1]; Inherited Create; TStandardCandleChart.Find(Symbol, 5, NewCandleData, CandleData[0], Link); AddAutoLink(Link); TStandardCandleChart.Find(Symbol, 10, NewCandleData, CandleData[1], Link); AddAutoLink(Link); TStandardCandleChart.Find(Symbol, 15, NewCandleData, CandleData[2], Link); AddAutoLink(Link); TStandardCandleChart.Find(Symbol, 30, NewCandleData, CandleData[3], Link); AddAutoLink(Link); TGenericTosDataNode.Find(Symbol, NewTosData, TosData, Link); AddAutoLink(Link); TSynchronizedTimer.Find(NewMinute, MinuteTimer, Link); AddAutoLink(Link) End; Procedure TRunningUpNow.UpdateLevel(BasePrice : Double); Var I : Integer; Begin If LevelStatus = rulsUnknown Then Begin For I := Low(CandleData) To High(CandleData) Do With CandleData[I] Do If ChartHeightValid Then Begin LevelSize := ChartHeight * 0.09; If LevelSize > 0 Then Begin If BasePrice >= 1.00 Then // Assume that anything costing more than a dollar is // quoted in pennies, otherwise it is quoted in $0.0001. LevelSize := Max(LevelSize, 0.02) Else LevelSize := Max(LevelSize, 0.0002); LevelStatus := rulsValid; Exit End End; LevelStatus := rulsInvalid End End; Procedure TRunningUpNow.NewMinute; Begin If MinuteTimer.TimePhase = tpUpdate Then Begin PreviousLastReportLevel := LastReportLevel; LastReportLevel := 0; If TosData.IsValid Then Begin BasePrice := TosData.GetLast^.Price; // This is not ideal, but it's a known problem in a lot of places. // Some special indexes can have 0 as a valid value. BasePriceValid := BasePrice <> 0 End Else Begin BasePriceValid := False; BasePrice := 0.0 End End End; Procedure TRunningUpNow.NewCandleData; Begin LevelStatus := rulsUnknown End; Procedure TRunningUpNow.NewTosData; Var Last : PTosData; Level : Integer; Quality : Double; Msg : String; Begin If BasePriceValid And TosData.IsValid Then Begin Last := TosData.GetLast; If Last^.EventType = etNewPrint Then Try UpdateLevel(Last^.Price); If LevelStatus = rulsValid Then Begin If FUp Then Quality := Last^.Price - BasePrice Else Quality := BasePrice - Last^.Price; Level := Floor(Quality / LevelSize); If Level < 0 Then // Assume we have filtered out late prints in the common // parts of the code, long before we get here. BasePrice := Last^.Price Else If Level > LastReportLevel Then Begin If Level > PreviousLastReportLevel Then Begin Case Level Of 1 : Msg := ''; 2 : Msg := ' quickly'; 3 : Msg := ' very quickly'; Else Msg := ' extremely quickly'; End; If FUp Then Msg := 'Running up' + Msg + ': +' Else Msg := 'Running down' + Msg + ': -'; Msg := Msg + FormatPrice(Quality, False) + ' in less than one minute.'; Report(Msg, Quality) End; LastReportLevel := Level End End Except // Floor and divide can both throw exceptions. End End End; //////////////////////////////////////////////////////////////////////// // Initialization //////////////////////////////////////////////////////////////////////// Initialization TGenericDataNodeFactory.StoreFactory('Doji-5', TGenericDataNodeFactory.CreateWithArgs(TDoji, StandardSymbolPlaceHolder, 5)); TGenericDataNodeFactory.StoreFactory('Doji-10', TGenericDataNodeFactory.CreateWithArgs(TDoji, StandardSymbolPlaceHolder, 10)); TGenericDataNodeFactory.StoreFactory('Doji-15', TGenericDataNodeFactory.CreateWithArgs(TDoji, StandardSymbolPlaceHolder, 15)); TGenericDataNodeFactory.StoreFactory('Doji-30', TGenericDataNodeFactory.CreateWithArgs(TDoji, StandardSymbolPlaceHolder, 30)); TGenericDataNodeFactory.StoreFactory('Doji-60', TGenericDataNodeFactory.CreateWithArgs(TDoji, StandardSymbolPlaceHolder, 60)); TGenericDataNodeFactory.StoreFactory('TraditionalChannelBreakout-5', TGenericDataNodeFactory.CreateWithArgs(TTraditionalChannelBreak, StandardSymbolPlaceHolder, 5, True)); TGenericDataNodeFactory.StoreFactory('TraditionalChannelBreakdown-5', TGenericDataNodeFactory.CreateWithArgs(TTraditionalChannelBreak, StandardSymbolPlaceHolder, 5, False)); TGenericDataNodeFactory.StoreFactory('TraditionalChannelBreakout-10', TGenericDataNodeFactory.CreateWithArgs(TTraditionalChannelBreak, StandardSymbolPlaceHolder, 10, True)); TGenericDataNodeFactory.StoreFactory('TraditionalChannelBreakdown-10', TGenericDataNodeFactory.CreateWithArgs(TTraditionalChannelBreak, StandardSymbolPlaceHolder, 10, False)); TGenericDataNodeFactory.StoreFactory('TraditionalChannelBreakout-15', TGenericDataNodeFactory.CreateWithArgs(TTraditionalChannelBreak, StandardSymbolPlaceHolder, 15, True)); TGenericDataNodeFactory.StoreFactory('TraditionalChannelBreakdown-15', TGenericDataNodeFactory.CreateWithArgs(TTraditionalChannelBreak, StandardSymbolPlaceHolder, 15, False)); TGenericDataNodeFactory.StoreFactory('TraditionalChannelBreakout-30', TGenericDataNodeFactory.CreateWithArgs(TTraditionalChannelBreak, StandardSymbolPlaceHolder, 30, True)); TGenericDataNodeFactory.StoreFactory('TraditionalChannelBreakdown-30', TGenericDataNodeFactory.CreateWithArgs(TTraditionalChannelBreak, StandardSymbolPlaceHolder, 30, False)); TGenericDataNodeFactory.StoreFactory('Hammer-2', TGenericDataNodeFactory.CreateWithArgs(THammerOrHangingMan, StandardSymbolPlaceHolder, 2, True)); TGenericDataNodeFactory.StoreFactory('Hammer-5', TGenericDataNodeFactory.CreateWithArgs(THammerOrHangingMan, StandardSymbolPlaceHolder, 5, True)); TGenericDataNodeFactory.StoreFactory('Hammer-10', TGenericDataNodeFactory.CreateWithArgs(THammerOrHangingMan, StandardSymbolPlaceHolder, 10, True)); TGenericDataNodeFactory.StoreFactory('Hammer-15', TGenericDataNodeFactory.CreateWithArgs(THammerOrHangingMan, StandardSymbolPlaceHolder, 15, True)); TGenericDataNodeFactory.StoreFactory('Hammer-30', TGenericDataNodeFactory.CreateWithArgs(THammerOrHangingMan, StandardSymbolPlaceHolder, 30, True)); TGenericDataNodeFactory.StoreFactory('Hammer-60', TGenericDataNodeFactory.CreateWithArgs(THammerOrHangingMan, StandardSymbolPlaceHolder, 60, True)); TGenericDataNodeFactory.StoreFactory('HangingMan-2', TGenericDataNodeFactory.CreateWithArgs(THammerOrHangingMan, StandardSymbolPlaceHolder, 2, False)); TGenericDataNodeFactory.StoreFactory('HangingMan-5', TGenericDataNodeFactory.CreateWithArgs(THammerOrHangingMan, StandardSymbolPlaceHolder, 5, False)); TGenericDataNodeFactory.StoreFactory('HangingMan-10', TGenericDataNodeFactory.CreateWithArgs(THammerOrHangingMan, StandardSymbolPlaceHolder, 10, False)); TGenericDataNodeFactory.StoreFactory('HangingMan-15', TGenericDataNodeFactory.CreateWithArgs(THammerOrHangingMan, StandardSymbolPlaceHolder, 15, False)); TGenericDataNodeFactory.StoreFactory('HangingMan-30', TGenericDataNodeFactory.CreateWithArgs(THammerOrHangingMan, StandardSymbolPlaceHolder, 30, False)); TGenericDataNodeFactory.StoreFactory('HangingMan-60', TGenericDataNodeFactory.CreateWithArgs(THammerOrHangingMan, StandardSymbolPlaceHolder, 60, False)); TGenericDataNodeFactory.StoreFactory('BullishEngulfing-5', TGenericDataNodeFactory.CreateWithArgs(TEngulfing, StandardSymbolPlaceHolder, 5, True)); TGenericDataNodeFactory.StoreFactory('BearishEngulfing-5', TGenericDataNodeFactory.CreateWithArgs(TEngulfing, StandardSymbolPlaceHolder, 5, False)); TGenericDataNodeFactory.StoreFactory('BullishEngulfing-10', TGenericDataNodeFactory.CreateWithArgs(TEngulfing, StandardSymbolPlaceHolder, 10, True)); TGenericDataNodeFactory.StoreFactory('BearishEngulfing-10', TGenericDataNodeFactory.CreateWithArgs(TEngulfing, StandardSymbolPlaceHolder, 10, False)); TGenericDataNodeFactory.StoreFactory('BullishEngulfing-15', TGenericDataNodeFactory.CreateWithArgs(TEngulfing, StandardSymbolPlaceHolder, 15, True)); TGenericDataNodeFactory.StoreFactory('BearishEngulfing-15', TGenericDataNodeFactory.CreateWithArgs(TEngulfing, StandardSymbolPlaceHolder, 15, False)); TGenericDataNodeFactory.StoreFactory('BullishEngulfing-30', TGenericDataNodeFactory.CreateWithArgs(TEngulfing, StandardSymbolPlaceHolder, 30, True)); TGenericDataNodeFactory.StoreFactory('BearishEngulfing-30', TGenericDataNodeFactory.CreateWithArgs(TEngulfing, StandardSymbolPlaceHolder, 30, False)); TGenericDataNodeFactory.StoreFactory('PiercingPattern-5', TGenericDataNodeFactory.CreateWithArgs(TDarkCloudCoverOrPeircing, StandardSymbolPlaceHolder, 5, True)); TGenericDataNodeFactory.StoreFactory('PiercingPattern-10', TGenericDataNodeFactory.CreateWithArgs(TDarkCloudCoverOrPeircing, StandardSymbolPlaceHolder, 10, True)); TGenericDataNodeFactory.StoreFactory('PiercingPattern-15', TGenericDataNodeFactory.CreateWithArgs(TDarkCloudCoverOrPeircing, StandardSymbolPlaceHolder, 15, True)); TGenericDataNodeFactory.StoreFactory('PiercingPattern-30', TGenericDataNodeFactory.CreateWithArgs(TDarkCloudCoverOrPeircing, StandardSymbolPlaceHolder, 30, True)); TGenericDataNodeFactory.StoreFactory('DarkCloudCover-5', TGenericDataNodeFactory.CreateWithArgs(TDarkCloudCoverOrPeircing, StandardSymbolPlaceHolder, 5, False)); TGenericDataNodeFactory.StoreFactory('DarkCloudCover-10', TGenericDataNodeFactory.CreateWithArgs(TDarkCloudCoverOrPeircing, StandardSymbolPlaceHolder, 10, False)); TGenericDataNodeFactory.StoreFactory('DarkCloudCover-15', TGenericDataNodeFactory.CreateWithArgs(TDarkCloudCoverOrPeircing, StandardSymbolPlaceHolder, 15, False)); TGenericDataNodeFactory.StoreFactory('DarkCloudCover-30', TGenericDataNodeFactory.CreateWithArgs(TDarkCloudCoverOrPeircing, StandardSymbolPlaceHolder, 30, False)); TGenericDataNodeFactory.StoreFactory('ToppingTail-2', TGenericDataNodeFactory.CreateWithArgs(TToppingTail, StandardSymbolPlaceHolder, 2, True)); TGenericDataNodeFactory.StoreFactory('ToppingTail-5', TGenericDataNodeFactory.CreateWithArgs(TToppingTail, StandardSymbolPlaceHolder, 5, True)); TGenericDataNodeFactory.StoreFactory('ToppingTail-10', TGenericDataNodeFactory.CreateWithArgs(TToppingTail, StandardSymbolPlaceHolder, 10, True)); TGenericDataNodeFactory.StoreFactory('ToppingTail-15', TGenericDataNodeFactory.CreateWithArgs(TToppingTail, StandardSymbolPlaceHolder, 15, True)); TGenericDataNodeFactory.StoreFactory('ToppingTail-30', TGenericDataNodeFactory.CreateWithArgs(TToppingTail, StandardSymbolPlaceHolder, 30, True)); TGenericDataNodeFactory.StoreFactory('ToppingTail-60', TGenericDataNodeFactory.CreateWithArgs(TToppingTail, StandardSymbolPlaceHolder, 60, True)); TGenericDataNodeFactory.StoreFactory('BottomingTail-2', TGenericDataNodeFactory.CreateWithArgs(TToppingTail, StandardSymbolPlaceHolder, 2, False)); TGenericDataNodeFactory.StoreFactory('BottomingTail-5', TGenericDataNodeFactory.CreateWithArgs(TToppingTail, StandardSymbolPlaceHolder, 5, False)); TGenericDataNodeFactory.StoreFactory('BottomingTail-10', TGenericDataNodeFactory.CreateWithArgs(TToppingTail, StandardSymbolPlaceHolder, 10, False)); TGenericDataNodeFactory.StoreFactory('BottomingTail-15', TGenericDataNodeFactory.CreateWithArgs(TToppingTail, StandardSymbolPlaceHolder, 15, False)); TGenericDataNodeFactory.StoreFactory('BottomingTail-30', TGenericDataNodeFactory.CreateWithArgs(TToppingTail, StandardSymbolPlaceHolder, 30, False)); TGenericDataNodeFactory.StoreFactory('BottomingTail-60', TGenericDataNodeFactory.CreateWithArgs(TToppingTail, StandardSymbolPlaceHolder, 60, False)); TGenericDataNodeFactory.StoreFactory('NarrowRangeBuyBar-5', TGenericDataNodeFactory.CreateWithArgs(TNarrowRangeBar, StandardSymbolPlaceHolder, 5, True)); TGenericDataNodeFactory.StoreFactory('NarrowRangeBuyBar-10', TGenericDataNodeFactory.CreateWithArgs(TNarrowRangeBar, StandardSymbolPlaceHolder, 10, True)); TGenericDataNodeFactory.StoreFactory('NarrowRangeBuyBar-15', TGenericDataNodeFactory.CreateWithArgs(TNarrowRangeBar, StandardSymbolPlaceHolder, 15, True)); TGenericDataNodeFactory.StoreFactory('NarrowRangeBuyBar-30', TGenericDataNodeFactory.CreateWithArgs(TNarrowRangeBar, StandardSymbolPlaceHolder, 30, True)); TGenericDataNodeFactory.StoreFactory('NarrowRangeSellBar-5', TGenericDataNodeFactory.CreateWithArgs(TNarrowRangeBar, StandardSymbolPlaceHolder, 5, False)); TGenericDataNodeFactory.StoreFactory('NarrowRangeSellBar-10', TGenericDataNodeFactory.CreateWithArgs(TNarrowRangeBar, StandardSymbolPlaceHolder, 10, False)); TGenericDataNodeFactory.StoreFactory('NarrowRangeSellBar-15', TGenericDataNodeFactory.CreateWithArgs(TNarrowRangeBar, StandardSymbolPlaceHolder, 15, False)); TGenericDataNodeFactory.StoreFactory('NarrowRangeSellBar-30', TGenericDataNodeFactory.CreateWithArgs(TNarrowRangeBar, StandardSymbolPlaceHolder, 30, False)); TGenericDataNodeFactory.StoreFactory('SmaUp-2', TGenericDataNodeFactory.CreateWithArgs(TRisingSma, StandardSymbolPlaceHolder, 2, True)); TGenericDataNodeFactory.StoreFactory('SmaUp-5', TGenericDataNodeFactory.CreateWithArgs(TRisingSma, StandardSymbolPlaceHolder, 5, True)); TGenericDataNodeFactory.StoreFactory('SmaUp-15', TGenericDataNodeFactory.CreateWithArgs(TRisingSma, StandardSymbolPlaceHolder, 15, True)); TGenericDataNodeFactory.StoreFactory('SmaDown-2', TGenericDataNodeFactory.CreateWithArgs(TRisingSma, StandardSymbolPlaceHolder, 2, False)); TGenericDataNodeFactory.StoreFactory('SmaDown-5', TGenericDataNodeFactory.CreateWithArgs(TRisingSma, StandardSymbolPlaceHolder, 5, False)); TGenericDataNodeFactory.StoreFactory('SmaDown-15', TGenericDataNodeFactory.CreateWithArgs(TRisingSma, StandardSymbolPlaceHolder, 15, False)); TGenericDataNodeFactory.StoreFactory('WideRangeBar-2', TGenericDataNodeFactory.CreateWithArgs(TWideRangeBar, StandardSymbolPlaceHolder, 2)); TGenericDataNodeFactory.StoreFactory('WideRangeBar-5', TGenericDataNodeFactory.CreateWithArgs(TWideRangeBar, StandardSymbolPlaceHolder, 5)); TGenericDataNodeFactory.StoreFactory('WideRangeBar-15', TGenericDataNodeFactory.CreateWithArgs(TWideRangeBar, StandardSymbolPlaceHolder, 15)); TGenericDataNodeFactory.StoreFactory('GreenBarReversal-2', TGenericDataNodeFactory.CreateWithArgs(TGreenBarReversal, StandardSymbolPlaceHolder, 2, True)); TGenericDataNodeFactory.StoreFactory('GreenBarReversal-5', TGenericDataNodeFactory.CreateWithArgs(TGreenBarReversal, StandardSymbolPlaceHolder, 5, True)); TGenericDataNodeFactory.StoreFactory('GreenBarReversal-15', TGenericDataNodeFactory.CreateWithArgs(TGreenBarReversal, StandardSymbolPlaceHolder, 15, True)); TGenericDataNodeFactory.StoreFactory('GreenBarReversal-60', TGenericDataNodeFactory.CreateWithArgs(TGreenBarReversal, StandardSymbolPlaceHolder, 60, True)); TGenericDataNodeFactory.StoreFactory('RedBarReversal-2', TGenericDataNodeFactory.CreateWithArgs(TGreenBarReversal, StandardSymbolPlaceHolder, 2, False)); TGenericDataNodeFactory.StoreFactory('RedBarReversal-5', TGenericDataNodeFactory.CreateWithArgs(TGreenBarReversal, StandardSymbolPlaceHolder, 5, False)); TGenericDataNodeFactory.StoreFactory('RedBarReversal-15', TGenericDataNodeFactory.CreateWithArgs(TGreenBarReversal, StandardSymbolPlaceHolder, 15, False)); TGenericDataNodeFactory.StoreFactory('RedBarReversal-60', TGenericDataNodeFactory.CreateWithArgs(TGreenBarReversal, StandardSymbolPlaceHolder, 60, False)); TGenericDataNodeFactory.StoreFactory('RunningUpNow', TGenericDataNodeFactory.CreateWithArgs(TRunningUpNow, StandardSymbolPlaceHolder, True)); TGenericDataNodeFactory.StoreFactory('RunningDownNow', TGenericDataNodeFactory.CreateWithArgs(TRunningUpNow, StandardSymbolPlaceHolder, False)); End.