GitHub で見る
Aeron JJN スキャルパー EA 戦略
概要
この戦略は、Aeron JJN Scalper エキスパートアドバイザーの StockSharp 高レベル API ポートです。完成したロウソク足を観察し、特定の2本のロウソク足逆転状況を識別し、最新の反対方向のロウソク足の始値に模擬ストップ注文を出します。市場が保存されたストップレベルに達すると、戦略は成行注文でエントリーし、ATR ベースのリスク目標を適用し、pip ベースのトレーリングストップで取引を管理します。
主要なアイデア:
- 取引方向は強気/弱気の2本のロウソク足逆転パターンによって決定されます。
- エントリーレベルは、反対方向の最後の強いロウソク足の始値から来ます。
- シグナルバーで測定された ATR(8) 値がストップロスとテイクプロフィットの距離を設定します。
- トレーリングストップロジックは、設定された pip オフセット分価格が進むと保護レベルを移動させます。
- 保留レベルは設定された分数後に自動的に期限切れになります。
取引ルール
シグナル検出
- 設定された時間軸の完成したロウソク足のみで作業します(デフォルト:1分)。
- 銘柄の価格ステップから pip サイズを計算し、3桁または5桁の価格については MetaTrader の pip 動作を模倣するために10倍にします。
- 参照バーを検索するために最後の120本のロウソク足のローリングウィンドウを維持します。
- 以下の場合にロングセットアップを検出:
- 現在のロウソク足が始値より高く閉じる(強気)、かつ
- 前のロウソク足が
DojiDiff1Pips より大きい本体サイズを持つ弱気。
DojiDiff2Pips を超える本体を持つ最新の弱気ロウソク足を後方に検索;その始値が buy stop レベルになります。
- 以下の場合にショートセットアップを検出:
- 現在のロウソク足が始値より低く閉じる(弱気)、かつ
- 前のロウソク足が
DojiDiff1Pips より大きい本体サイズを持つ強気。
DojiDiff2Pips を超える本体を持つ最新の強気ロウソク足を後方に検索;その始値が sell stop レベルになります。
- 同じ方向にすでに保留レベルがある場合、またはロウソク足の ATR 値がまだ利用できない場合は新しいセットアップを無視します。
保留レベル管理
- 保存されたレベルは保留ストップ注文として扱われます。
ResetMinutes の期限が切れるまで価格がトリガーを下回る(ロング)または上回る(ショート)場合は破棄されます。
- 後のロウソク足で価格がレベルに触れると(高値 ≥ 買いレベル または安値 ≤ 売りレベル)、既存のエクスポージャーを反転させ
Volume コントラクトを追加するようにサイズ設定された成行注文を送信します。
- ロングポジションに入ると未決のショートレベルがクリアされ、逆も同様です。
ストップロス、テイクプロフィット、トレーリング
- エントリー時、戦略はシグナルロウソク足の ATR(8) 値を記録します。
- ロング取引:ストップロス =
entry - ATR、テイクプロフィット = entry + ATR。
- ショート取引:ストップロス =
entry + ATR、テイクプロフィット = entry - ATR。
- 各完成ロウソク足で戦略は:
- 価格がストップロスまたはテイクプロフィットに達したかどうかを確認し、触れた場合は成行注文で決済します。
- 価格がポジションに有利な方向に少なくとも
TrailingStopPips + TrailingStepPips 動いたときにトレーリングを適用します。新しいストップは最後の終値から TrailingStopPips 後方に位置します。ストップは決して後退しません。
- ポジションが手動で閉じられた場合、内部状態は自動的にリセットされます。
パラメーター
| パラメーター |
デフォルト |
説明 |
Volume |
0.1 |
エントリーに使用されるネットポジションサイズ;戦略は方向を反転させるために必要な場合は現在の絶対ポジションを追加します。 |
TrailingStopPips |
5 |
ベーストレーリングストップ距離(価格単位に変換)。 |
TrailingStepPips |
5 |
トレーリングストップを再度移動させる前に必要な追加の前進。 |
ResetMinutes |
10 |
保存された保留レベルの有効期限(分)。 |
DojiDiff1Pips |
10 |
シグナルに先行する逆転ロウソク足の最小本体サイズ(pips)。 |
DojiDiff2Pips |
4 |
エントリー参照レベルとして使用されるロウソク足の最小本体サイズ(pips)。 |
CandleType |
1分時間軸 |
計算に使用されるロウソク足データタイプ。 |
実装上の注意
- 戦略は純粋に完成したロウソク足で動作し、実際のストップ注文の代わりにメモリ内レベルを使用します;レベルが突破されると直ちに成行注文が送信されます。これは StockSharp 高レベル API 内でのオリジナル EA の動作を反映します。
- ATR(8) は
AverageTrueRange で計算されキャッシュされるため、各取引の元のストップ/目標距離は一定に保たれます。
- pip 変換は3桁および5桁の価格表示に対する MetaTrader の調整を再現します。銘柄に
PriceStep がない場合、デフォルトのステップ 1 が使用されます。
- 元の
CopyRates のルックバック100バーをいくらかの安全マージンで複製するために最大120本の過去のロウソク足が保存されます。
- この戦略には Python ポートは提供されていません。
使用方法
- 希望する銘柄とポートフォリオに戦略を接続します。
- ロウソク足の時間軸、pip オフセット、ATR ベースのフィルターを銘柄に合わせて調整します。
- 戦略を開始します;シグナルを追跡し、トリガーレベルに触れたら成行注文を送信し、決済を自動的に管理します。
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Port of the Aeron JJN Scalper expert advisor.
/// Detects reversal candle patterns (bullish candle after strong bearish, and vice versa)
/// and enters at breakout of the prior candle's open level.
/// </summary>
public class AeronJjnScalperEaStrategy : Strategy
{
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _bodyMinAtr;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private AverageTrueRange _atr;
private decimal _prevOpen;
private decimal _prevClose;
private bool _hasPrev;
private int _cooldown;
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public decimal BodyMinAtr
{
get => _bodyMinAtr.Value;
set => _bodyMinAtr.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public AeronJjnScalperEaStrategy()
{
_atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR indicator period", "Indicators");
_bodyMinAtr = Param(nameof(BodyMinAtr), 1.5m)
.SetDisplay("Body Min ATR", "Minimum candle body size as ATR multiple", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetGreaterThanZero()
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle Type", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_hasPrev = false;
_cooldown = 0;
_prevOpen = 0;
_prevClose = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_atr = new AverageTrueRange { Length = AtrLength };
_hasPrev = false;
_cooldown = 0;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_atr, ProcessCandle).Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (atrVal <= 0)
{
SavePrev(candle);
return;
}
if (_cooldown > 0)
_cooldown--;
if (_hasPrev && _cooldown == 0 && Position == 0)
{
var prevBody = Math.Abs(_prevClose - _prevOpen);
var minBody = atrVal * BodyMinAtr;
// Bullish candle after strong bearish candle => buy
if (candle.ClosePrice > candle.OpenPrice && _prevClose < _prevOpen && prevBody >= minBody)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Bearish candle after strong bullish candle => sell
else if (candle.ClosePrice < candle.OpenPrice && _prevClose > _prevOpen && prevBody >= minBody)
{
SellMarket();
_cooldown = CooldownBars;
}
}
SavePrev(candle);
}
private void SavePrev(ICandleMessage candle)
{
_prevOpen = candle.OpenPrice;
_prevClose = candle.ClosePrice;
_hasPrev = true;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class aeron_jjn_scalper_ea_strategy(Strategy):
def __init__(self):
super(aeron_jjn_scalper_ea_strategy, self).__init__()
self._atr_length = self.Param("AtrLength", 14)
self._body_min_atr = self.Param("BodyMinAtr", 1.5)
self._cooldown_bars = self.Param("CooldownBars", 10)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._prev_open = 0.0
self._prev_close = 0.0
self._has_prev = False
self._cooldown = 0
@property
def AtrLength(self):
return self._atr_length.Value
@property
def BodyMinAtr(self):
return self._body_min_atr.Value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(aeron_jjn_scalper_ea_strategy, self).OnStarted2(time)
self._has_prev = False
self._cooldown = 0
atr = AverageTrueRange()
atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(atr, self._process_candle).Start()
self.StartProtection(
takeProfit=Unit(2, UnitTypes.Percent),
stopLoss=Unit(1, UnitTypes.Percent)
)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle, atr_val):
if candle.State != CandleStates.Finished:
return
atr_value = float(atr_val)
if atr_value <= 0:
self._save_prev(candle)
return
if self._cooldown > 0:
self._cooldown -= 1
if self._has_prev and self._cooldown == 0 and float(self.Position) == 0:
prev_body = abs(self._prev_close - self._prev_open)
min_body = atr_value * float(self.BodyMinAtr)
if float(candle.ClosePrice) > float(candle.OpenPrice) and self._prev_close < self._prev_open and prev_body >= min_body:
self.BuyMarket()
self._cooldown = self.CooldownBars
elif float(candle.ClosePrice) < float(candle.OpenPrice) and self._prev_close > self._prev_open and prev_body >= min_body:
self.SellMarket()
self._cooldown = self.CooldownBars
self._save_prev(candle)
def _save_prev(self, candle):
self._prev_open = float(candle.OpenPrice)
self._prev_close = float(candle.ClosePrice)
self._has_prev = True
def OnReseted(self):
super(aeron_jjn_scalper_ea_strategy, self).OnReseted()
self._has_prev = False
self._cooldown = 0
self._prev_open = 0.0
self._prev_close = 0.0
def CreateClone(self):
return aeron_jjn_scalper_ea_strategy()