Skip to main content

Custom Signal

Create your own signal by extending the Signal base class. Custom signals plug into the decision tree alongside built-in signals.

Namespace: MZpack.NT8.Algo

Steps

1. Inherit from Signal

public class DeltaThresholdSignal : Signal
{
public DeltaThresholdSignal() { }

public DeltaThresholdSignal(Strategy strategy,
MarketDataSource source, SignalCalculate calculate)
: base(strategy, source, calculate, isReset: true)
{
}
}

2. Override Name

public override string Name => "Delta Threshold";

3. Declare Parameters

Use public properties for configurable parameters. Decorate with [Display] for the property grid:

[Display(Name = "Delta Threshold", GroupName = "Custom", Order = 1)]
public double DeltaThreshold { get; set; } = 500;

[Display(Name = "Reverse", GroupName = "Custom", Order = 2)]
public bool IsReverse { get; set; } = false;

4. Reference Indicators

Override GetIndicatorsTemplates() to declare which strategy indicators your signal needs:

StrategyFootprintIndicator footprint;

public override List<IndicatorTemplate> GetIndicatorsTemplates()
{
return new List<IndicatorTemplate>
{
new IndicatorTemplate(typeof(StrategyFootprintIndicator), (i) => footprint = (StrategyFootprintIndicator)i)
};
}

The framework calls ReferIndicators() during initialization to resolve the indicator reference from the strategy's indicator collection.

5. Implement Calculate

Override OnCalculate for your data source. Return the direction by setting Direction:

public override void OnCalculate(MarketDataEventArgs e, int barIdx,
SignalDirection allowed)
{
if (footprint == null) return;
if (!footprint.FootprintBars.ContainsKey(barIdx)) return;

IFootprintBar bar = footprint.FootprintBars[barIdx];
long delta = bar.Delta;

if (delta > DeltaThreshold)
Direction = Node.ReverseDirection(SignalDirection.Long, IsReverse);
else if (delta < -DeltaThreshold)
Direction = Node.ReverseDirection(SignalDirection.Short, IsReverse);
else
Direction = SignalDirection.None;

if (Node.IsDetermined(Direction))
{
ChartRange = new ChartRange
{
MinBarIdx = barIdx,
MaxBarIdx = barIdx,
High = bar.Hi,
Low = bar.Lo
};
EntryPrice = GetBestEntryPrice(Direction);
}
}

6. Add to Decision Tree

OnCreateAlgoStrategy = () =>
{
var strategy = new Strategy("DeltaStrategy", this);

var signal = new DeltaThresholdSignal(strategy,
MarketDataSource.Level1,
SignalCalculate.OnBarClose);
signal.DeltaThreshold = 1000;

var pattern = new Pattern(strategy, Logic.And, new Range(),
isShortCircuitANDEvaluation: true);
pattern.Signals.Root.Add(signal);

strategy.Initialize(pattern);
return strategy;
};
tip

Use SignalCalculate.OnBarClose for backtesting performance. OnEachTick requires Tick Replay or live data and is significantly slower.

Complete Example

public class DeltaThresholdSignal : Signal
{
StrategyFootprintIndicator footprint;

[Display(Name = "Delta Threshold", GroupName = "Custom", Order = 1)]
public double DeltaThreshold { get; set; } = 500;

[Display(Name = "Reverse", GroupName = "Custom", Order = 2)]
public bool IsReverse { get; set; } = false;

public override string Name => "Delta Threshold";

public DeltaThresholdSignal() { }

public DeltaThresholdSignal(Strategy strategy,
MarketDataSource source, SignalCalculate calculate)
: base(strategy, source, calculate, isReset: true) { }

public override List<IndicatorTemplate> GetIndicatorsTemplates()
{
return new List<IndicatorTemplate>
{
new IndicatorTemplate(typeof(StrategyFootprintIndicator),
(i) => footprint = (StrategyFootprintIndicator)i)
};
}

public override void OnCalculate(MarketDataEventArgs e, int barIdx,
SignalDirection allowed)
{
if (footprint == null) return;
if (!footprint.FootprintBars.ContainsKey(barIdx)) return;

IFootprintBar bar = footprint.FootprintBars[barIdx];
long delta = bar.Delta;

if (delta > DeltaThreshold)
Direction = Node.ReverseDirection(SignalDirection.Long, IsReverse);
else if (delta < -DeltaThreshold)
Direction = Node.ReverseDirection(SignalDirection.Short, IsReverse);
else
Direction = SignalDirection.None;

if (Node.IsDetermined(Direction))
{
ChartRange = new ChartRange
{
MinBarIdx = barIdx,
MaxBarIdx = barIdx,
High = bar.Hi,
Low = bar.Lo
};
EntryPrice = GetBestEntryPrice(Direction);
}
}
}

See Also