GitHub で見る
ワンミニットスキャルパー戦略
この戦略は、MetaTrader 4のエキスパートアドバイザー1 MINUTE SCALPERをStockSharpの高レベルAPIに移植したものです。元のロボット
の多層トレンド確認、マルチタイムフレームモメンタム、長期MACDフィルターを保持しながら、StockSharpのポジション中心モデルにリスク
コントロールを適応させます。
コアロジック
- トレンドスタック – 13本の線形加重移動平均(LWMA 3/5/8/10/12/15/30/35/40/45/50/55/200)が厳密な順序で整列している必要が
あります。ロング取引は各短い平均が次のものより上にある必要があり、ショートは条件を逆転させます。
- 主要トレンドゲート – 追加の高速LWMA(デフォルト6)がロングでは低速LWMA(デフォルト85)より上に留まり、ショートでは
下に留まる必要があり、EAの高速対低速チェックを反映します。
- 足構造 – スクリプトのオーバーラップパターンが存在する場合にのみエントリーがトリガーされます:ロングでは2本前の安値が
前回高値より下にある必要があり、ショートでは前回安値が2本前の高値より低い必要があります。
- モメンタムフィルター – 上位タイムフレーム(デフォルト15分足)で計算された14期間モメンタムインジケーターが、最後の3つの
値のいずれかで設定された閾値以上だけ100から乖離する必要があります。これは
MomLevelB/MomLevelSの比較を再現します。
- 月次MACDバイアス – 選択されたMACDタイムフレーム(月次データのプロキシとしてデフォルトで30日足)で構築されたMACDが、
ロングでは主線がシグナルより上、ショートでは下を示す必要があります。
取引管理
- 初期保護 – ストップロスとテイクプロフィットの距離は銘柄ステップ(ポイント)で表現されます。ポジションが開くと、戦略は
Security.PriceStepを使用してこれらのステップ数を絶対価格に変換します。
- ブレイクイーブン移動 – 価格が
BreakEvenTriggerSteps有利な方向に動いた後、ストップはエントリーにBreakEvenOffsetSteps
を加えた位置に移動します(ショートには鏡像ロジックが適用されます)。フラグはポジションごとに一度トリガーされます。
- ステップトレーリング –
TrailingStopStepsが正の場合、ストップはエントリー以来の最高値(または最低値)を指定ステップ数
だけ追跡します。
- マネートレーリング – 浮動利益が
MoneyTrailTarget(通貨)を超えると、戦略はピーク浮動PnLを追跡し、反落がMoneyTrailStop
に等しい場合にポジションを閉じます。
- 金銭/パーセンテージターゲット – オプションの絶対またはパーセンテージのテイクプロフィットターゲットは、浮動PnLが設定された
閾値を超えるとすべてのポジションを閉じます。パーセンテージターゲットは戦略開始時に取得された初期ポートフォリオ値を使用します。
- 資本ストップ – 戦略は最大資本(ポートフォリオ価値+オープンPnL)を監視します。そのピークからのドローダウンが
EquityRiskPercentを超えると、すべてのポジションが清算され、EAのAccountEquityHigh()セーフガードを複製します。
パラメーター
| パラメーター |
説明 |
Volume |
新規エントリーの注文ボリューム。絶対的な現在のポジションに追加されるため、反転により即座にポジションが変わります。 |
FastMaPeriod / SlowMaPeriod |
主要トレンドフィルターのLWMA長さ。 |
MomentumPeriod |
上位タイムフレームのモメンタムインジケーターの長さ。 |
MomentumBuyThreshold / MomentumSellThreshold |
ロング/ショートモメンタム確認に必要な100からの最小絶対乖離。 |
MacdFastLength / MacdSlowLength / MacdSignalLength |
MacdCandleTypeに適用されるMACD設定。 |
StopLossSteps / TakeProfitSteps |
価格ステップで測定されたプロテクティブストップとターゲット距離。無効化するにはゼロに設定。 |
TrailingStopSteps |
ステップベースのトレーリングストップ距離(0で無効)。 |
BreakEvenTriggerSteps / BreakEvenOffsetSteps |
ブレイクイーブン移動をトリガーする距離とストップ移動時のオフセット。 |
UseMoneyTakeProfit, MoneyTakeProfit |
通貨ベースの浮動利益ターゲットを有効化してサイジング。 |
UsePercentTakeProfit, PercentTakeProfit |
初期資本のパーセントとして浮動利益ターゲットを有効化してサイジング。 |
EnableMoneyTrailing, MoneyTrailTarget, MoneyTrailStop |
浮動利益のトレーリングロジックを設定。 |
UseEquityStop, EquityRiskPercent |
ドローダウンストップを有効化し、最大ドローダウンパーセンテージを定義。 |
CandleType |
主要な作業足(デフォルト1分)。 |
MomentumCandleType |
モメンタムインジケーターの上位タイムフレーム足(デフォルト15分)。 |
MacdCandleType |
MACDトレンドフィルターに使用される足(デフォルト30日≈月次)。 |
MT4エキスパートとの違い
- StockSharpはネットポジションを使用するため、戦略は
Max_Tradesまでの複数チケットの代わりに常に単一の集約ポジションを維持
します。反転は反対方向に開く前に既存のポジションを閉じます。
PercentTakeProfitはMetaTraderが使用する常に変化するAccountBalance()ではなく、開始時に取得されたポートフォリオ価値を参照
するため、外部取引が残高を変更した場合のノイジーなターゲットを回避します。
- 金銭ベースの決済ロジック(
Take_Profit_In_MoneyとTRAIL_PROFIT_IN_MONEY2)は戦略の平均エントリー価格から計算されたライブ
浮動PnLで動作します。これはEAの動作に対応しますが、StockSharpの保護フレームワーク内です。
- プラットフォームは選択されたタイムフレーム(
CandleType、MomentumCandleType、MacdCandleType)の足フィードを供給する
必要があります。使用するアダプターが要求された解像度をサポートしていることを確認してください。
お使いの銘柄とセッションに合わせて閾値を調整してください。スプレッドが狭いまたは高ボラティリティのペアは、ノイズを減らすために
より広いステップ距離または大きなモメンタム閾値が必要かもしれません。
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// One-minute scalper strategy using fast/slow WMA crossover.
/// Buys on bullish crossover, sells on bearish crossover.
/// </summary>
public class OneMinuteScalperStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private WeightedMovingAverage _fast;
private WeightedMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast WMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow WMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="OneMinuteScalperStrategy"/> class.
/// </summary>
public OneMinuteScalperStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast WMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 85)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow WMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 500)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new WeightedMovingAverage { Length = FastPeriod };
_slow = new WeightedMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fast.IsFormed || !_slow.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// WMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 60;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 60;
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class one_minute_scalper_strategy(Strategy):
def __init__(self):
super(one_minute_scalper_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 20).SetGreaterThanZero().SetDisplay("Fast Period", "Fast WMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 85).SetGreaterThanZero().SetDisplay("Slow Period", "Slow WMA period", "Indicator")
self._sl_points = self.Param("StopLossPoints", 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._tp_points = self.Param("TakeProfitPoints", 500).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
def OnReseted(self):
super(one_minute_scalper_strategy, self).OnReseted()
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
self._cooldown = 0
def OnStarted2(self, time):
super(one_minute_scalper_strategy, self).OnStarted2(time)
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
self._cooldown = 0
self._fast = WeightedMovingAverage()
self._fast.Length = self._fast_period.Value
self._slow = WeightedMovingAverage()
self._slow.Length = self._slow_period.Value
sub = self.SubscribeCandles(tf(5))
sub.Bind(self._fast, self._slow, self.OnProcess).Start()
def OnProcess(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = candle.ClosePrice
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None and self.Security.PriceStep > 0:
step = float(self.Security.PriceStep)
# Check SL/TP
if self.Position > 0 and self._entry_price > 0:
if self._sl_points.Value > 0 and close <= self._entry_price - self._sl_points.Value * step:
self.SellMarket()
self._entry_price = 0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._tp_points.Value > 0 and close >= self._entry_price + self._tp_points.Value * step:
self.SellMarket()
self._entry_price = 0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self._sl_points.Value > 0 and close >= self._entry_price + self._sl_points.Value * step:
self.BuyMarket()
self._entry_price = 0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._tp_points.Value > 0 and close <= self._entry_price - self._tp_points.Value * step:
self.BuyMarket()
self._entry_price = 0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
# WMA crossover
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 60
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return one_minute_scalper_strategy()