パターン EA 戦略
概要
パターン EA 戦略は、最新の 3 本の完成したローソク足をスキャンし、単一、二重、三重バーの幅広い形成を検索する価格行動システムです。このロジックは、MQL5 の元のエキスパートアドバイザー「Patterns_EA」の StockSharp ポートで、30 種類のローソク足セットアップの設定可能なカタログを保持しています。各パターンは独立して有効化または無効化でき、ロングまたはショートの実行に割り当てることができ、戦略がソーススクリプトの裁量ルールを模倣できます。
パターングループ
検出器は、パターングループに応じて現在のローソク足と最大 2 本前のローソク足を評価します:
- グループ 1 – 一本バーパターン: Neutral Bar、Force Bar Up、Force Bar Down、Hammer、Shooting Star。
- グループ 2 – 二本バーパターン: Inside、Outside、Double Bar High Lower Close、Double Bar Low Higher Close、Mirror Bar、Bearish Harami、Bearish Harami Cross、Bullish Harami、Bullish Harami Cross、Dark Cloud Cover、Doji Star、Engulfing Bearish Line、Engulfing Bullish Line、Two Neutral Bars。
- グループ 3 – 三本バーパターン: Double Inside、Pin Up、Pin Down、Pivot Point Reversal Up、Pivot Point Reversal Down、Close Price Reversal Up、Close Price Reversal Down、Evening Star、Morning Star、Evening Doji Star、Morning Doji Star。
許容パラメーター(Equality Pips)は、2 つの価格が等価チェックを満たすためにどれだけ一致する必要があるかを制御し、元の EA の「最大 pip 距離」設定を再現します。
パラメーター
- Candle Type – パターン検出に使用する時間軸。
- Opened Mode – MQL バージョンから複製されたポジション管理ロジック(Any、Swing、Buy One、Buy Many、Sell One、Sell Many)。
- Equality Pips – 価格等価を定義する pip 距離;銘柄の価格ステップで調整。
- Enable One-Bar Patterns / Enable Two-Bar Patterns / Enable Three-Bar Patterns – 各パターングループのトグル。
- Enable – 30 の全形成の個別スイッチ。
- Order – 対応するパターンに割り当てられた取引方向(買いまたは売り)。
すべてのパラメーターは StrategyParam オブジェクトを通じて公開されており、StockSharp アプリケーション内で使用する際に最適化または UI バインディングを可能にします。
トレードロジック
- 戦略は設定されたローソク足シリーズを購読し、完成したローソク足を待ちます。
- 新しいバーが閉じると、最新の 3 本のローソク足がキャッシュされ、検出器が有効なパターングループを評価します。
- 各パターンは、許容比較とシャドウ/ボディの関係を含む MQL5 ソースからの条件ルールを複製します。
- パターンが確認されると、
TriggerPatternはグループと個別パターンが有効かどうかを確認し、選択した方向を検証し、設定されたポジションモードを適用します。 - 戦略は割り当てられた方向に成行注文を送信します。スイングモードでは、最初に逆方向のオープンポジションを閉じます。
ポジションモード
- Any: 追加の制約なしに両方向のシグナルを許可。
- Swing: 単一のネットポジションを維持;逆方向シグナルは新しいポジションに入る前に既存のポジションを解消。
- Buy One / Sell One: それぞれ単一のロングまたはショートポジションに取引を制限。
- Buy Many / Sell Many: 逆方向のシグナルを無視しながら、指定した方向への複数エントリーを許可。
注記
- アルゴリズムは
Security.PriceStepを使用して等価許容度を絶対価格距離に変換します。銘柄が価格ステップを定義しない場合、デフォルトの 0.0001 が適用されます。 - 追加のインジケーターは不要;すべてのロジックはローソク足のジオメトリのみに依存しており、元のエキスパートアドバイザーの意図と一致しています。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Price action strategy that trades configurable candlestick patterns converted from the MQL5 Patterns EA.
/// </summary>
public class PatternsEaStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<OpenedModes> _openedMode;
private readonly StrategyParam<decimal> _equalityPips;
private readonly StrategyParam<bool> _enableGroup1;
private readonly StrategyParam<bool> _enableGroup2;
private readonly StrategyParam<bool> _enableGroup3;
private readonly PatternDefinition[] _patternDefinitions;
private readonly PatternParameterSet _patternParams;
private CandleInfo? _current;
private CandleInfo? _previous;
private CandleInfo? _previous2;
public PatternsEaStrategy()
{
Volume = 1;
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles used for pattern search", "General");
_openedMode = Param(nameof(Mode), OpenedModes.Any)
.SetDisplay("Opened Mode", "Position handling mode", "Trading");
_equalityPips = Param(nameof(EqualityPips), 1m)
.SetNotNegative()
.SetDisplay("Equality Pips", "Maximum pip distance to treat prices as equal", "Detection");
_enableGroup1 = Param(nameof(EnableOneBarPatterns), true)
.SetDisplay("Enable One-Bar Patterns", "Toggle detection of one-bar formations", "Groups");
_enableGroup2 = Param(nameof(EnableTwoBarPatterns), true)
.SetDisplay("Enable Two-Bar Patterns", "Toggle detection of two-bar formations", "Groups");
_enableGroup3 = Param(nameof(EnableThreeBarPatterns), true)
.SetDisplay("Enable Three-Bar Patterns", "Toggle detection of three-bar formations", "Groups");
_patternDefinitions = new[]
{
new PatternDefinition(PatternTypes.DoubleInside, "Double Inside", PatternGroups.ThreeBars),
new PatternDefinition(PatternTypes.Inside, "Inside Bar", PatternGroups.TwoBars),
new PatternDefinition(PatternTypes.Outside, "Outside Bar", PatternGroups.TwoBars),
new PatternDefinition(PatternTypes.PinUp, "Pin Up", PatternGroups.ThreeBars),
new PatternDefinition(PatternTypes.PinDown, "Pin Down", PatternGroups.ThreeBars),
new PatternDefinition(PatternTypes.PivotPointReversalUp, "Pivot Point Reversal Up", PatternGroups.ThreeBars),
new PatternDefinition(PatternTypes.PivotPointReversalDown, "Pivot Point Reversal Down", PatternGroups.ThreeBars),
new PatternDefinition(PatternTypes.DoubleBarLowHigherClose, "Double Bar Low With A Higher Close", PatternGroups.TwoBars),
new PatternDefinition(PatternTypes.DoubleBarHighLowerClose, "Double Bar High With A Lower Close", PatternGroups.TwoBars),
new PatternDefinition(PatternTypes.ClosePriceReversalUp, "Close Price Reversal Up", PatternGroups.ThreeBars),
new PatternDefinition(PatternTypes.ClosePriceReversalDown, "Close Price Reversal Down", PatternGroups.ThreeBars),
new PatternDefinition(PatternTypes.NeutralBar, "Neutral Bar", PatternGroups.OneBar),
new PatternDefinition(PatternTypes.ForceBarUp, "Force Bar Up", PatternGroups.OneBar),
new PatternDefinition(PatternTypes.ForceBarDown, "Force Bar Down", PatternGroups.OneBar),
new PatternDefinition(PatternTypes.MirrorBar, "Mirror Bar", PatternGroups.TwoBars),
new PatternDefinition(PatternTypes.Hammer, "Hammer", PatternGroups.OneBar),
new PatternDefinition(PatternTypes.ShootingStar, "Shooting Star", PatternGroups.OneBar),
new PatternDefinition(PatternTypes.EveningStar, "Evening Star", PatternGroups.ThreeBars),
new PatternDefinition(PatternTypes.MorningStar, "Morning Star", PatternGroups.ThreeBars),
new PatternDefinition(PatternTypes.BearishHarami, "Bearish Harami", PatternGroups.TwoBars),
new PatternDefinition(PatternTypes.BearishHaramiCross, "Bearish Harami Cross", PatternGroups.TwoBars),
new PatternDefinition(PatternTypes.BullishHarami, "Bullish Harami", PatternGroups.TwoBars),
new PatternDefinition(PatternTypes.BullishHaramiCross, "Bullish Harami Cross", PatternGroups.TwoBars),
new PatternDefinition(PatternTypes.DarkCloudCover, "Dark Cloud Cover", PatternGroups.TwoBars),
new PatternDefinition(PatternTypes.DojiStar, "Doji Star", PatternGroups.TwoBars),
new PatternDefinition(PatternTypes.EngulfingBearishLine, "Engulfing Bearish Line", PatternGroups.TwoBars),
new PatternDefinition(PatternTypes.EngulfingBullishLine, "Engulfing Bullish Line", PatternGroups.TwoBars),
new PatternDefinition(PatternTypes.EveningDojiStar, "Evening Doji Star", PatternGroups.ThreeBars),
new PatternDefinition(PatternTypes.MorningDojiStar, "Morning Doji Star", PatternGroups.ThreeBars),
new PatternDefinition(PatternTypes.TwoNeutralBars, "Two Neutral Bars", PatternGroups.TwoBars),
};
var patternEnabled = new StrategyParam<bool>[_patternDefinitions.Length];
var patternSides = new StrategyParam<Sides>[_patternDefinitions.Length];
for (var i = 0; i < _patternDefinitions.Length; i++)
{
var definition = _patternDefinitions[i];
var groupName = definition.Group switch
{
PatternGroups.OneBar => "Group 1 - One Bar",
PatternGroups.TwoBars => "Group 2 - Two Bars",
PatternGroups.ThreeBars => "Group 3 - Three Bars",
_ => "Patterns",
};
patternEnabled[i] = Param($"Enable{definition.Type}", true)
.SetDisplay($"Enable {definition.DisplayName}", $"Use {definition.DisplayName} pattern", groupName);
patternSides[i] = Param($"Order{definition.Type}", Sides.Buy)
.SetDisplay($"{definition.DisplayName} Order", "Market order direction for the pattern", groupName);
}
_patternParams = new PatternParameterSet(patternEnabled, patternSides);
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public OpenedModes Mode
{
get => _openedMode.Value;
set => _openedMode.Value = value;
}
public decimal EqualityPips
{
get => _equalityPips.Value;
set => _equalityPips.Value = value;
}
public bool EnableOneBarPatterns
{
get => _enableGroup1.Value;
set => _enableGroup1.Value = value;
}
public bool EnableTwoBarPatterns
{
get => _enableGroup2.Value;
set => _enableGroup2.Value = value;
}
public bool EnableThreeBarPatterns
{
get => _enableGroup3.Value;
set => _enableGroup3.Value = value;
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return new[] { (Security, CandleType) };
}
protected override void OnReseted()
{
base.OnReseted();
_current = null;
_previous = null;
_previous2 = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_previous2 = _previous;
_previous = _current;
_current = new CandleInfo(candle);
EvaluatePatterns();
}
private void EvaluatePatterns()
{
if (_current is null)
return;
var point = Security?.PriceStep ?? 0.0001m;
var equality = EqualityPips * point;
var minDeviation = Math.Max(equality, point);
var minDeviation4 = Math.Max(equality * 4m, point * 4m);
var candle0 = _current.Value;
var hasPrevious = _previous.HasValue;
var hasPrevious2 = _previous2.HasValue;
var candle1 = hasPrevious ? _previous.Value : default;
var candle2 = hasPrevious2 ? _previous2.Value : default;
// One-bar patterns require only the current bar.
if (EnableOneBarPatterns)
{
if (Compare(candle0.Open, candle0.Close, equality) == 0 && candle0.UpperShadow > minDeviation4 && candle0.LowerShadow > minDeviation4)
TriggerPattern(PatternTypes.NeutralBar);
if (Compare(candle0.Close, candle0.High, equality) == 0)
TriggerPattern(PatternTypes.ForceBarUp);
if (Compare(candle0.Close, candle0.Low, equality) == 0)
TriggerPattern(PatternTypes.ForceBarDown);
if (candle0.UpperShadow <= minDeviation && candle0.LowerShadow > 2m * candle0.BodySize)
TriggerPattern(PatternTypes.Hammer);
if (candle0.LowerShadow <= minDeviation && candle0.UpperShadow > 2m * candle0.BodySize)
TriggerPattern(PatternTypes.ShootingStar);
}
if (!hasPrevious)
return;
// Two-bar patterns evaluate the current bar together with the previous one.
if (EnableTwoBarPatterns)
{
if (Compare(candle0.High, candle1.High, equality) < 0 && Compare(candle0.Low, candle1.Low, equality) > 0)
TriggerPattern(PatternTypes.Inside);
if (Compare(candle0.High, candle1.High, equality) > 0 && Compare(candle0.Low, candle1.Low, equality) < 0)
TriggerPattern(PatternTypes.Outside);
if (Compare(candle0.High, candle1.High, equality) == 0 && Compare(candle0.Close, candle1.Close, equality) < 0 && Compare(candle0.Low, candle1.Low, equality) <= 0)
TriggerPattern(PatternTypes.DoubleBarHighLowerClose);
if (Compare(candle0.Low, candle1.Low, equality) == 0 && Compare(candle0.Close, candle1.Close, equality) > 0 && Compare(candle0.High, candle1.High, equality) >= 0)
TriggerPattern(PatternTypes.DoubleBarLowHigherClose);
if (Compare(candle1.BodySize, candle0.BodySize, equality) == 0 && Compare(candle1.Open, candle0.Close, equality) == 0)
TriggerPattern(PatternTypes.MirrorBar);
if (Compare(candle0.High, candle1.High, equality) < 0 && Compare(candle0.Low, candle1.Low, equality) > 0 && Compare(candle1.Close, candle1.Open, equality) > 0 && Compare(candle0.Close, candle0.Open, equality) < 0 && Compare(candle1.BodySize, candle0.BodySize, equality) > 0 && Compare(candle1.Close, candle0.Open, equality) > 0 && Compare(candle1.Open, candle0.Close, equality) < 0)
TriggerPattern(PatternTypes.BearishHarami);
if (Compare(candle0.High, candle1.High, equality) < 0 && Compare(candle0.Low, candle1.Low, equality) > 0 && Compare(candle1.Close, candle1.Open, equality) > 0 && Compare(candle0.Open, candle0.Close, equality) == 0 && Compare(candle1.BodySize, candle0.BodySize, equality) > 0 && Compare(candle1.Close, candle0.Open, equality) > 0 && Compare(candle1.Open, candle0.Close, equality) < 0)
TriggerPattern(PatternTypes.BearishHaramiCross);
if (Compare(candle0.High, candle1.High, equality) < 0 && Compare(candle0.Low, candle1.Low, equality) > 0 && Compare(candle1.Close, candle1.Open, equality) < 0 && Compare(candle0.Close, candle0.Open, equality) > 0 && Compare(candle1.BodySize, candle0.BodySize, equality) > 0 && Compare(candle1.Close, candle0.Open, equality) < 0 && Compare(candle1.Open, candle0.Close, equality) > 0)
TriggerPattern(PatternTypes.BullishHarami);
if (Compare(candle0.High, candle1.High, equality) < 0 && Compare(candle0.Low, candle1.Low, equality) > 0 && Compare(candle1.Close, candle1.Open, equality) < 0 && Compare(candle0.Open, candle0.Close, equality) == 0 && Compare(candle1.BodySize, candle0.BodySize, equality) > 0 && Compare(candle1.Close, candle0.Open, equality) < 0 && Compare(candle1.Open, candle0.Close, equality) > 0)
TriggerPattern(PatternTypes.BullishHaramiCross);
if (Compare(candle1.Close, candle1.Open, equality) > 0 && Compare(candle0.Close, candle0.Open, equality) < 0 && Compare(candle1.High, candle0.Open, equality) < 0 && Compare(candle0.Close, candle1.Close, equality) < 0 && Compare(candle0.Close, candle1.Open, equality) > 0)
TriggerPattern(PatternTypes.DarkCloudCover);
if (Compare(candle0.Open, candle0.Close, equality) == 0 && Compare(candle1.Close, candle1.Open, equality) > 0 && Compare(candle0.Open, candle1.High, equality) > 0 && Compare(candle1.Close, candle1.Open, equality) < 0 && Compare(candle0.Open, candle1.Low, equality) < 0)
TriggerPattern(PatternTypes.DojiStar);
if (Compare(candle1.Close, candle1.Open, equality) > 0 && Compare(candle0.Close, candle0.Open, equality) < 0 && Compare(candle0.Open, candle1.Close, equality) > 0 && Compare(candle0.Close, candle1.Open, equality) < 0)
TriggerPattern(PatternTypes.EngulfingBearishLine);
if (Compare(candle1.Close, candle1.Open, equality) < 0 && Compare(candle0.Close, candle0.Open, equality) > 0 && Compare(candle0.Open, candle1.Close, equality) < 0 && Compare(candle0.Close, candle1.Open, equality) > 0)
TriggerPattern(PatternTypes.EngulfingBullishLine);
if (Compare(candle0.Open, candle0.Close, equality) == 0 && candle0.UpperShadow > minDeviation4 && candle0.LowerShadow > minDeviation4 && Compare(candle1.Open, candle1.Close, equality) == 0 && candle1.UpperShadow > minDeviation4 && candle1.LowerShadow > minDeviation4)
TriggerPattern(PatternTypes.TwoNeutralBars);
}
if (!hasPrevious2)
return;
// Three-bar patterns combine the last three candles.
if (EnableThreeBarPatterns)
{
if (Compare(candle0.High, candle1.High, equality) < 0 && Compare(candle0.Low, candle1.Low, equality) > 0 && Compare(candle1.High, candle2.High, equality) < 0 && Compare(candle1.Low, candle2.Low, equality) > 0)
TriggerPattern(PatternTypes.DoubleInside);
if (Compare(candle1.High, candle2.High, equality) > 0 && Compare(candle1.High, candle0.High, equality) > 0 && Compare(candle1.Low, candle2.Low, equality) > 0 && Compare(candle1.Low, candle0.Low, equality) > 0 && candle1.BodySize * 2m < candle1.UpperShadow)
TriggerPattern(PatternTypes.PinUp);
if (Compare(candle1.High, candle2.High, equality) < 0 && Compare(candle1.High, candle0.High, equality) < 0 && Compare(candle1.Low, candle2.Low, equality) < 0 && Compare(candle1.Low, candle0.Low, equality) < 0 && candle1.BodySize * 2m < candle1.LowerShadow)
TriggerPattern(PatternTypes.PinDown);
if (Compare(candle1.High, candle2.High, equality) > 0 && Compare(candle1.High, candle0.High, equality) > 0 && Compare(candle1.Low, candle2.Low, equality) > 0 && Compare(candle1.Low, candle0.Low, equality) > 0 && Compare(candle0.Close, candle1.Low, equality) < 0)
TriggerPattern(PatternTypes.PivotPointReversalDown);
if (Compare(candle1.High, candle2.High, equality) < 0 && Compare(candle1.High, candle0.High, equality) < 0 && Compare(candle1.Low, candle2.Low, equality) < 0 && Compare(candle1.Low, candle0.Low, equality) < 0 && Compare(candle0.Close, candle1.High, equality) > 0)
TriggerPattern(PatternTypes.PivotPointReversalUp);
if (Compare(candle1.High, candle2.High, equality) < 0 && Compare(candle1.Low, candle2.Low, equality) < 0 && Compare(candle0.High, candle1.High, equality) < 0 && Compare(candle0.Low, candle1.Low, equality) < 0 && Compare(candle0.Close, candle1.Close, equality) > 0 && Compare(candle0.Open, candle0.Close, equality) < 0)
TriggerPattern(PatternTypes.ClosePriceReversalUp);
if (Compare(candle1.High, candle2.High, equality) > 0 && Compare(candle1.Low, candle2.Low, equality) > 0 && Compare(candle0.High, candle1.High, equality) > 0 && Compare(candle0.Low, candle1.Low, equality) > 0 && Compare(candle0.Close, candle1.Close, equality) < 0 && Compare(candle0.Open, candle0.Close, equality) > 0)
TriggerPattern(PatternTypes.ClosePriceReversalDown);
if (Compare(candle2.Close, candle2.Open, equality) > 0 && Compare(candle1.Close, candle1.Open, equality) > 0 && Compare(candle0.Close, candle0.Open, equality) < 0 && Compare(candle2.Close, candle1.Open, equality) < 0 && Compare(candle2.BodySize, candle1.BodySize, equality) > 0 && Compare(candle1.BodySize, candle0.BodySize, equality) < 0 && Compare(candle0.Close, candle2.Open, equality) > 0 && Compare(candle0.Close, candle2.Close, equality) < 0)
TriggerPattern(PatternTypes.EveningStar);
if (Compare(candle2.Close, candle2.Open, equality) < 0 && Compare(candle1.Close, candle1.Open, equality) > 0 && Compare(candle0.Close, candle0.Open, equality) > 0 && Compare(candle2.Close, candle1.Open, equality) > 0 && Compare(candle2.BodySize, candle1.BodySize, equality) > 0 && Compare(candle1.BodySize, candle0.BodySize, equality) < 0 && Compare(candle0.Close, candle2.Close, equality) > 0 && Compare(candle0.Close, candle2.Open, equality) < 0)
TriggerPattern(PatternTypes.MorningStar);
if (Compare(candle2.Close, candle2.Open, equality) > 0 && Compare(candle1.Close, candle1.Open, equality) == 0 && Compare(candle0.Close, candle0.Open, equality) < 0 && Compare(candle2.Close, candle1.Open, equality) < 0 && Compare(candle2.BodySize, candle1.BodySize, equality) > 0 && Compare(candle1.BodySize, candle0.BodySize, equality) < 0 && Compare(candle0.Close, candle2.Open, equality) > 0 && Compare(candle0.Close, candle2.Close, equality) < 0)
TriggerPattern(PatternTypes.EveningDojiStar);
if (Compare(candle2.Close, candle2.Open, equality) < 0 && Compare(candle1.Close, candle1.Open, equality) == 0 && Compare(candle0.Close, candle0.Open, equality) > 0 && Compare(candle2.Close, candle1.Open, equality) > 0 && Compare(candle2.BodySize, candle1.BodySize, equality) > 0 && Compare(candle1.BodySize, candle0.BodySize, equality) < 0 && Compare(candle0.Close, candle2.Close, equality) > 0 && Compare(candle0.Close, candle2.Open, equality) < 0)
TriggerPattern(PatternTypes.MorningDojiStar);
}
}
private int Compare(decimal price1, decimal price2, decimal tolerance)
{
var diff = price1 - price2;
if (Math.Abs(diff) < tolerance)
return 0;
return diff > 0 ? 1 : -1;
}
private bool IsGroupEnabled(PatternGroups group)
{
return group switch
{
PatternGroups.OneBar => EnableOneBarPatterns,
PatternGroups.TwoBars => EnableTwoBarPatterns,
PatternGroups.ThreeBars => EnableThreeBarPatterns,
_ => true,
};
}
private void TriggerPattern(PatternTypes type)
{
var index = (int)type;
var definition = _patternDefinitions[index];
if (!IsGroupEnabled(definition.Group))
return;
if (!_patternParams.Enabled[index].Value)
return;
var side = _patternParams.Sides[index].Value;
if (!CanExecute(side))
return;
if (Mode == OpenedModes.Swing)
{
if (side == Sides.Buy && Position < 0)
BuyMarket(Math.Abs(Position));
else if (side == Sides.Sell && Position > 0)
SellMarket(Math.Abs(Position));
}
if (side == Sides.Buy)
BuyMarket();
else
SellMarket();
}
private bool CanExecute(Sides side)
{
return Mode switch
{
OpenedModes.Any => true,
OpenedModes.Swing => true,
OpenedModes.BuyOne => side == Sides.Buy && Position <= 0,
OpenedModes.BuyMany => side == Sides.Buy,
OpenedModes.SellOne => side == Sides.Sell && Position >= 0,
OpenedModes.SellMany => side == Sides.Sell,
_ => false,
};
}
private enum PatternGroups
{
OneBar = 1,
TwoBars = 2,
ThreeBars = 3,
}
private enum PatternTypes
{
DoubleInside,
Inside,
Outside,
PinUp,
PinDown,
PivotPointReversalUp,
PivotPointReversalDown,
DoubleBarLowHigherClose,
DoubleBarHighLowerClose,
ClosePriceReversalUp,
ClosePriceReversalDown,
NeutralBar,
ForceBarUp,
ForceBarDown,
MirrorBar,
Hammer,
ShootingStar,
EveningStar,
MorningStar,
BearishHarami,
BearishHaramiCross,
BullishHarami,
BullishHaramiCross,
DarkCloudCover,
DojiStar,
EngulfingBearishLine,
EngulfingBullishLine,
EveningDojiStar,
MorningDojiStar,
TwoNeutralBars,
}
public enum OpenedModes
{
Any,
Swing,
BuyOne,
BuyMany,
SellOne,
SellMany,
}
private readonly struct PatternDefinition
{
public PatternDefinition(PatternTypes type, string displayName, PatternGroups group)
{
Type = type;
DisplayName = displayName;
Group = group;
}
public PatternTypes Type { get; }
public string DisplayName { get; }
public PatternGroups Group { get; }
}
private readonly struct CandleInfo
{
public CandleInfo(ICandleMessage candle)
{
Open = candle.OpenPrice;
Close = candle.ClosePrice;
High = candle.HighPrice;
Low = candle.LowPrice;
BodyTop = Math.Max(Open, Close);
BodyBottom = Math.Min(Open, Close);
BodySize = BodyTop - BodyBottom;
UpperShadow = High - BodyTop;
LowerShadow = BodyBottom - Low;
}
public decimal Open { get; }
public decimal Close { get; }
public decimal High { get; }
public decimal Low { get; }
public decimal BodyTop { get; }
public decimal BodyBottom { get; }
public decimal BodySize { get; }
public decimal UpperShadow { get; }
public decimal LowerShadow { get; }
}
private sealed class PatternParameterSet : IEquatable<PatternParameterSet>
{
public PatternParameterSet(StrategyParam<bool>[] enabled, StrategyParam<Sides>[] sides)
{
Enabled = enabled;
Sides = sides;
}
public StrategyParam<bool>[] Enabled { get; }
public StrategyParam<Sides>[] Sides { get; }
public bool Equals(PatternParameterSet other)
{
if (other is null || Enabled.Length != other.Enabled.Length || Sides.Length != other.Sides.Length)
return false;
for (var i = 0; i < Enabled.Length; i++)
{
if (Enabled[i].Value != other.Enabled[i].Value)
return false;
}
for (var i = 0; i < Sides.Length; i++)
{
if (Sides[i].Value != other.Sides[i].Value)
return false;
}
return true;
}
public override bool Equals(object obj)
{
return obj is PatternParameterSet other && Equals(other);
}
public override int GetHashCode()
{
var hash = new HashCode();
foreach (var enabled in Enabled)
hash.Add(enabled.Value);
foreach (var side in Sides)
hash.Add(side.Value);
return hash.ToHashCode();
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class patterns_ea_strategy(Strategy):
def __init__(self):
super(patterns_ea_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Type of candles for pattern search", "General")
self._equality_pips = self.Param("EqualityPips", 1.0).SetNotNegative().SetDisplay("Equality Pips", "Max pip distance to treat prices as equal", "Detection")
self.Volume = 1
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(patterns_ea_strategy, self).OnReseted()
self._current = None
self._previous = None
self._previous2 = None
def OnStarted2(self, time):
super(patterns_ea_strategy, self).OnStarted2(time)
self._current = None
self._previous = None
self._previous2 = None
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self.OnProcess).Start()
def _candle_info(self, candle):
o = float(candle.OpenPrice)
c = float(candle.ClosePrice)
h = float(candle.HighPrice)
l = float(candle.LowPrice)
body_top = max(o, c)
body_bottom = min(o, c)
body_size = body_top - body_bottom
upper_shadow = h - body_top
lower_shadow = body_bottom - l
return {"o": o, "c": c, "h": h, "l": l, "bt": body_top, "bb": body_bottom, "bs": body_size, "us": upper_shadow, "ls": lower_shadow}
def _compare(self, a, b, tol):
diff = a - b
if abs(diff) < tol:
return 0
return 1 if diff > 0 else -1
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
self._previous2 = self._previous
self._previous = self._current
self._current = self._candle_info(candle)
self._evaluate_patterns()
def _evaluate_patterns(self):
if self._current is None:
return
step = 0.0001
if self.Security is not None and self.Security.PriceStep is not None and self.Security.PriceStep > 0:
step = float(self.Security.PriceStep)
eq = self._equality_pips.Value * step
min_dev = max(eq, step)
min_dev4 = max(eq * 4, step * 4)
c0 = self._current
cmp = self._compare
# One-bar: Hammer -> Buy, ShootingStar -> Sell
if c0["us"] <= min_dev and c0["ls"] > 2 * c0["bs"]:
self.BuyMarket()
return
if c0["ls"] <= min_dev and c0["us"] > 2 * c0["bs"]:
self.SellMarket()
return
if self._previous is None:
return
c1 = self._previous
# Two-bar: Engulfing Bullish -> Buy
if cmp(c1["c"], c1["o"], eq) < 0 and cmp(c0["c"], c0["o"], eq) > 0 and cmp(c0["o"], c1["c"], eq) < 0 and cmp(c0["c"], c1["o"], eq) > 0:
self.BuyMarket()
return
# Two-bar: Engulfing Bearish -> Sell
if cmp(c1["c"], c1["o"], eq) > 0 and cmp(c0["c"], c0["o"], eq) < 0 and cmp(c0["o"], c1["c"], eq) > 0 and cmp(c0["c"], c1["o"], eq) < 0:
self.SellMarket()
return
if self._previous2 is None:
return
c2 = self._previous2
# Three-bar: Morning Star -> Buy
if cmp(c2["c"], c2["o"], eq) < 0 and cmp(c1["c"], c1["o"], eq) > 0 and cmp(c0["c"], c0["o"], eq) > 0:
if cmp(c2["c"], c1["o"], eq) > 0 and cmp(c2["bs"], c1["bs"], eq) > 0 and cmp(c1["bs"], c0["bs"], eq) < 0:
if cmp(c0["c"], c2["c"], eq) > 0 and cmp(c0["c"], c2["o"], eq) < 0:
self.BuyMarket()
return
# Three-bar: Evening Star -> Sell
if cmp(c2["c"], c2["o"], eq) > 0 and cmp(c1["c"], c1["o"], eq) > 0 and cmp(c0["c"], c0["o"], eq) < 0:
if cmp(c2["c"], c1["o"], eq) < 0 and cmp(c2["bs"], c1["bs"], eq) > 0 and cmp(c1["bs"], c0["bs"], eq) < 0:
if cmp(c0["c"], c2["o"], eq) > 0 and cmp(c0["c"], c2["c"], eq) < 0:
self.SellMarket()
return
def CreateClone(self):
return patterns_ea_strategy()