/// <summary> /// calculate bear/bull min/max /// </summary> void RangeInit() { // calulate bear/bull min/max if tickSize is set if (tickSize > 0) { tickBearContValue = EmMath.MultiplyTwoDoubles((double)configBearContValue, tickSize); tickBearInitValue = EmMath.MultiplyTwoDoubles((double)configBearInitValue, tickSize); tickBearRangeMax = EmMath.MultiplyTwoDoubles((double)configBearRangeMax, tickSize); tickBearRangeMin = EmMath.MultiplyTwoDoubles((double)configBearRangeMin, tickSize); tickBullContValue = EmMath.MultiplyTwoDoubles((double)configBullContValue, tickSize); tickBullInitValue = EmMath.MultiplyTwoDoubles((double)configBullInitValue, tickSize); tickBullRangeMax = EmMath.MultiplyTwoDoubles((double)configBullRangeMax, tickSize); tickBullRangeMin = EmMath.MultiplyTwoDoubles((double)configBullRangeMin, tickSize); } // swap bear min/max if entered incorrectly if (tickBearRangeMin > tickBearRangeMax || tickBullRangeMin > tickBullRangeMax) { double tmpBear = tickBearRangeMax; tickBearRangeMax = tickBearRangeMin; tickBearRangeMin = tmpBear; } // swap bull min/max if entered incorrectly if (tickBullRangeMin > tickBullRangeMax) { double tmpBull = tickBullRangeMax; tickBullRangeMax = tickBullRangeMin; tickBullRangeMin = tmpBull; } }
/// <summary> /// Called on each bar update event (incoming tick) /// </summary> protected override void OnBarUpdate() { /// ensure bar has EmProps if (props is IEmProps) { /// get EmProps for the specified bar, matching on OHLCVT EmProps barProps = props.GetEmProps(Open[0], High[0], Low[0], Close[0], (long)Volume[0], Time[0]) as EmProps; /// ensure EmProps exist for given bar (note: some historical data may not be available) if (barProps == null) { return; } /// update plots Surge.Set(barProps.Surge); Tide.Set(barProps.Tide); ErrorCount.Set(barProps.ErrorCount); ErrorVolume.Set(barProps.ErrorVolume); /// update average TideAvg.Set(EmMath.EMA((double)barProps.Tide, CurrentBar, smooth, CurrentBar == 0 ? Tide[0] : TideAvg[1])); } else { /// generate log message one time if (!invalid) { Log(String.Format("{0} does not implement interface IEmProps", Bars.BarsType.ToString()), LogLevel.Warning); invalid = true; } } }
/// <summary> /// set thisOpen, thisCloseUp, thisCloseDown /// </summary> /// <param name="bars">bars array</param> /// <param name="thisPrice">current value of thisClose</param> /// <param name="tickPrice">real price used for next open for gap bars</param> void AdjustOpenClose(Bars bars, double thisPrice, double tickPrice) { // local variables double tmpRange = 0; // update class fields thisOpen = (OpenOption == EmOpens.NoGap) ? thisPrice : tickPrice; if (thisBias == -1) { // counter-trend - use bull-init for thisCloseUp switch (configBullInitType) { case 1: tmpRange = tickBullRangeMax; break; case -1: tmpRange = tickBullRangeMin; break; case 0: default: tmpRange = EmMath.RangeValidate(AddTwoDoubles(bars, tickRange, tickBullInitValue), tickBullRangeMax, tickBullRangeMin); break; } thisCloseUp = AddTwoDoubles(bars, thisOpen, tmpRange); // with-trend - use bear-cont for thisCloseDown tickRange = EmMath.RangeValidate(AddTwoDoubles(bars, tickRange, tickBearContValue), tickBearRangeMax, tickBearRangeMin); thisCloseDown = AddTwoDoubles(bars, thisOpen, -tickRange); } else { // counter-trend - use bear-init for thisCloseUp switch (configBearInitType) { case 1: tmpRange = tickBearRangeMax; break; case -1: tmpRange = tickBearRangeMin; break; case 0: default: tmpRange = EmMath.RangeValidate(AddTwoDoubles(bars, tickRange, tickBearInitValue), tickBearRangeMax, tickBearRangeMin); break; } thisCloseDown = AddTwoDoubles(bars, thisOpen, -tmpRange); // with-trend - use bull-cont for thisCloseUp tickRange = EmMath.RangeValidate(AddTwoDoubles(bars, tickRange, tickBullContValue), tickBullRangeMax, tickBullRangeMin); thisCloseUp = AddTwoDoubles(bars, thisOpen, tickRange); } }
/// <summary> /// Called on each bar update event (incoming tick) /// </summary> protected override void OnBarUpdate() { if ((High[0] - Low[0]) == 0) { emBop.Set(0); } else { emBop.Set((Close[0] - Open[0]) / (High[0] - Low[0])); } EmBop1.Set(EmMath.SMA(ref emBop, CurrentBar, Smooth1, CurrentBar == 0 ? emBop[0] : EmBop1[1])); EmBop2.Set(EmMath.SMA(ref emBop, CurrentBar, Smooth2, CurrentBar == 0 ? emBop[0] : EmBop2[1])); if (EmBop1[0] > EmBop2[0]) { DrawRegion("EmBoP_" + CurrentBar.ToString(), 1, 0, EmBop1, EmBop2, Color.Transparent, BullColor, BullOpacity); } else if (EmBop2[0] > EmBop1[0]) { DrawRegion("EmBoP_" + CurrentBar.ToString(), 1, 0, EmBop2, EmBop1, Color.Transparent, BearColor, BearOpacity); } }
/// <summary> /// set form values from xml /// </summary> void FormApplyXml(ConfigXml22 xml) { // flag isLoading isLoading = true; // set form values from config bearContValue.Value = (decimal)EmMath.RangeValidate(xml.BearContValue, 100, -100); bearInitValue.Value = (decimal)EmMath.RangeValidate(xml.BearInitValue, 100, -100); bearRangeMax.Value = Math.Max(1, (int)xml.BearRangeMax); bearRangeMin.Value = Math.Max(1, (int)xml.BearRangeMin); switch (xml.BearInitType) { case 1: // range max bearInitMax.Checked = true; bearInitMin.Checked = bearInitPrev.Checked = false; break; case -1: // range min bearInitMin.Checked = true; bearInitMax.Checked = bearInitPrev.Checked = false; break; case 0: default: // prev range bearInitPrev.Checked = true; bearInitMin.Checked = bearInitMax.Checked = false; break; } bullContValue.Value = (decimal)EmMath.RangeValidate(xml.BullContValue, 100, -100); bullInitValue.Value = (decimal)EmMath.RangeValidate(xml.BullInitValue, 100, -100); bullRangeMax.Value = Math.Max(1, (int)xml.BullRangeMax); bullRangeMin.Value = Math.Max(1, (int)xml.BullRangeMin); switch (xml.BullInitType) { case 1: // range max bullInitMax.Checked = true; bullInitMin.Checked = bullInitPrev.Checked = false; break; case -1: // range min bullInitMin.Checked = true; bullInitMax.Checked = bullInitPrev.Checked = false; break; case 0: default: // prev range bullInitPrev.Checked = true; bullInitMin.Checked = bullInitMax.Checked = false; break; } cboCloseOption.SelectedItem = (EmCloses)cboCloseOption.FindString(xml.CloseOption); cboOpenOption.SelectedItem = (EmOpens)cboOpenOption.FindString(xml.OpenOption); // reset isLoading isLoading = false; }
/// <summary> /// Called on each incoming tick /// </summary> void OnTick(Bars bars, double open, double high, double low, double close, DateTime time, long volume, bool isRealtime) { // ensure xml config loaded ConfigCheck(); // create initial bar on first tick and handle NT7 session-break issue (note: removing IsNewSession() creates invalid bars; remove if preferred) if ((bars.Count == 0) || bars.IsNewSession(time, isRealtime)) { // set class fields tickSize = bars.Instrument.MasterInstrument.TickSize; // calculate bear/bull min/max RangeInit(); // update class fields AdjustOpenClose(bars, close, close); prevClose = close; // add first bar AddBar(bars, thisOpen, thisOpen, thisOpen, thisOpen, time, volume, isRealtime); // add EmProps AddEmProps(thisOpen, volume, thisCloseDown, thisCloseUp, time); } // continue all subsequent ticks/bars else { // local variables Bar bar = (Bar)bars.Get(bars.Count - 1); int compareCloseUp = bars.Instrument.MasterInstrument.Compare(close, thisCloseUp); int compareCloseDown = bars.Instrument.MasterInstrument.Compare(close, thisCloseDown); // range exceeded; create new bar(s) if (((compareCloseUp > 0 || compareCloseDown < 0) && CloseOption == EmCloses.TickThru) || ((compareCloseUp >= 0 || compareCloseDown <= 0) && CloseOption == EmCloses.OnTouch)) { // local variables bool newBar = true; double thisClose = (compareCloseUp > 0) ? Math.Min(close, thisCloseUp) : (compareCloseDown < 0) ? Math.Max(close, thisCloseDown) : close; // close current bar; volume included for on-touch only // see this post for more info on volume calculation: http://www.ninjatrader.com/support/forum/showthread.php?p=302208#post302208 UpdateBar(bars, bar.Open, ((compareCloseUp > 0) ? thisClose : bar.High), ((compareCloseDown < 0) ? thisClose : bar.Low), thisClose, time, ((CloseOption == EmCloses.OnTouch) ? volume : 0), isRealtime); barProps.Finalize(thisClose, prevClose, ((CloseOption == EmCloses.OnTouch) ? volume : 0), time, ((close > bar.Open) ? 1 : (close < bar.Open) ? -1 : 0)); // add next bar and loop phantom bars, if needed do { // update class fields thisBias = (close > bar.Open) ? 1 : (close < bar.Open) ? -1 : 0; tickRange = EmMath.RangeValidate(Math.Abs(AddTwoDoubles(bars, bar.Open, -thisClose)), (thisBias == -1) ? tickBearRangeMax : tickBullRangeMax, (thisBias == -1) ? tickBearRangeMin : tickBullRangeMin); AdjustOpenClose(bars, thisClose, close); thisClose = (compareCloseUp > 0) ? Math.Min(close, thisCloseUp) : (compareCloseDown < 0) ? Math.Max(close, thisCloseDown) : close; // add new bar; include volume once (except for on-touch), then create phantom bars // see this post for more info on volume calculation: http://www.ninjatrader.com/support/forum/showthread.php?p=302208#post302208 AddBar(bars, thisOpen, ((compareCloseUp > 0) ? thisClose : thisOpen), ((compareCloseDown < 0) ? thisClose : thisOpen), thisClose, time, ((CloseOption == EmCloses.TickThru && newBar) ? volume : 0), isRealtime); // add EmProps AddEmProps(thisOpen, ((compareCloseUp > 0) ? thisClose : thisOpen), ((compareCloseDown < 0) ? thisClose : thisOpen), thisClose, ((CloseOption == EmCloses.TickThru && newBar) ? volume : 0), time, thisBias, thisCloseDown, thisCloseUp); // update class fields newBar = false; compareCloseUp = bars.Instrument.MasterInstrument.Compare(close, thisCloseUp); compareCloseDown = bars.Instrument.MasterInstrument.Compare(close, thisCloseDown); }while (((compareCloseUp > 0 || compareCloseDown < 0) && CloseOption == EmCloses.TickThru) || ((compareCloseUp >= 0 || compareCloseDown <= 0) && CloseOption == EmCloses.OnTouch)); } // range not exceeded; continue current bar else { // update current bar UpdateBar(bars, bar.Open, ((close > bar.High) ? close : bar.High), ((close < bar.Low) ? close : bar.Low), close, time, volume, isRealtime); // update EmProps barProps.Update(close, prevClose, volume, time); } } // update prevClose and last price bars.LastPrice = prevClose = close; }