GitHub で見る
史上最も簡単なデイトレード戦略
概要
- MetaTrader 4 エキスパート アドバイザー 「史上最も簡単 - デイトレード ロボット」 を StockSharp の高レベル API に変換します。
- シンプルなデイトレード向けに設計されています。各セッションは、前の日次ローソク足の方向に従う最大 1 つの市場ポジションをオープンします。
- テクニカル指標やオシレーターを使用せず、ローソク足データのみを使用します。すべての注文管理は成行注文を通じて実行されます。
取引ロジック
- 毎日のローソク足 (
DailyCandleType、デフォルトは TimeSpan.FromDays(1)) を収集し、最後に完了した日の始値と終値を保存します。
- 日中ローソク足(
IntradayCandleType、デフォルトは TimeSpan.FromMinutes(1))を購読して実行を促進します。
- セッションの早い時間中(ローソク足の開始時間は厳密に
EntryHourLimit より短い間、デフォルトは 1):
- 前回の日次終値が前回の日次始値を上回っている場合は、
BuyMarket(TradeVolume) を使用してロングポジションを入力します。
- 前回の日次終値が前回の日次始値を下回っている場合は、
SellMarket(TradeVolume) を使用してショート ポジションを入力します。
- 日足のローソク足が横ばいで終了した場合 (始値と終値が等しい)、取引は開始されません。
- 一日中ポジションを維持します。日中のローソク足が
MarketCloseHour (デフォルトは 20) 以上の場合、開いているエクスポージャーを成行注文で閉じます (ロングの場合は SellMarket、ショートの場合は BuyMarket)。
- この戦略は、アクティブなポジションが存在しない場合にのみ新しいポジションをオープンし、最大でも 1 日あたり 1 回の取引を保証します。
パラメーター
| パラメータ |
説明 |
デフォルト |
TradeVolume |
ロングエントリーとショートエントリーの両方の注文量。ポジティブである必要があります。 |
1 |
EntryHourLimit |
新しい取引を開始できる最新の時間 (限定)。 [0, 23] の外側の値は検証によってクランプされます。 |
1 |
MarketCloseHour |
ストラテジーがオープンポジションを強制的に決済する時間。毎日適用されます。 |
20 |
IntradayCandleType |
取引実行ロジックとポジション管理に使用される時間枠。 |
TimeSpan.FromMinutes(1).TimeFrame() |
DailyCandleType |
前日の始値と終値を読み取るために使用される時間枠。 |
TimeSpan.FromMinutes(5).TimeFrame() |
すべてのパラメータは Param() を通じて登録され、StockSharp オプティマイザーで最適化できます。
リスク管理
- この戦略ではストップロスやテイクプロフィットのレベルは使用しません。リスクは毎日
MarketCloseHour で終了することで制御されます。
StartProtection() は、取引中の予期しない非フラットポジションを防ぐために開始時に有効になります。
- 1 日にアクティブにできるポジションは 1 つだけであるため、最大露出は
TradeVolume によって定義されます。
使用上の注意
- 日中および毎日のローソク足履歴を提供する商品で戦略を実行します。デフォルト設定では、分単位および日単位のローソク足が必要です。
EntryHourLimit と MarketCloseHour を選択した商品の取引セッションに合わせます。
- このアルゴリズムは、ローソク足のタイムスタンプに為替現地時間を想定しています。それに応じてデータ ソースを調整します。
- このロジックは元の MQL エキスパート アドバイザを反映しており、Python コンポーネントを使用せずに動作を StockSharp 環境内で複製できます。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Easiest Ever Daytrade: EMA trend following with ATR stops.
/// </summary>
public class EasiestEverDaytradeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevClose;
private decimal _entryPrice;
public EasiestEverDaytradeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 20)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0; _entryPrice = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (_prevClose == 0 || atrVal <= 0) { _prevClose = close; return; }
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || close < emaVal) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || close > emaVal) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (close > emaVal && _prevClose <= emaVal) { _entryPrice = close; BuyMarket(); }
else if (close < emaVal && _prevClose >= emaVal) { _entryPrice = close; SellMarket(); }
}
_prevClose = close;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
class easiest_ever_daytrade_strategy(Strategy):
def __init__(self):
super(easiest_ever_daytrade_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_close = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(easiest_ever_daytrade_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._entry_price = 0.0
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
ev = float(ema_val)
av = float(atr_val)
close = float(candle.ClosePrice)
if self._prev_close == 0 or av <= 0:
self._prev_close = close
return
if self.Position > 0:
if close >= self._entry_price + av * 2.5 or close <= self._entry_price - av * 1.5 or close < ev:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 2.5 or close >= self._entry_price + av * 1.5 or close > ev:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_close = close
return
if self.Position == 0:
if close > ev and self._prev_close <= ev:
self._entry_price = close
self.BuyMarket()
elif close < ev and self._prev_close >= ev:
self._entry_price = close
self.SellMarket()
self._prev_close = close
def OnReseted(self):
super(easiest_ever_daytrade_strategy, self).OnReseted()
self._prev_close = 0.0
self._entry_price = 0.0
def CreateClone(self):
return easiest_ever_daytrade_strategy()