GitHub で見る
MTrendLine戦略
概要
MTrendLine 戦略 は、MetaTrader スクリプト MTrendLine.mq4 を StockSharp の高レベル戦略 API に移植します。オリジナル
エキスパートアドバイザーは、既存の指値注文の価格を繰り返し調整し、指値に引かれたトレンドラインと一致した状態を保ちます。
チャート。 StockSharp バージョンは、構成可能なオプションを使用して移動傾向線を再構築することで、同じ動作を自動化します。
LinearRegression インジケーター。最大 3 つの独立した指値注文スロットは、計算された回帰直線に従うことができます。
独自の注文タイプ、距離、数量。新しいローソク足が閉じるたびに、ストラテジーはライン値を再計算し、
必要なオフセットを取得し、それに応じて未決注文を更新します。
この移植により、構造化パラメータ、MetaTrader ポイントからの自動変換など、最新のリスクと使いやすさの改善が追加されました。
実際の価格ステップ、および未決注文と一緒に移動するオプションのストップロス/テイクプロフィットディスタンスに変換します。入札・質問
更新は SubscribeLevel1() 経由で監視されるため、戦略はブローカーが要求する最小距離を尊重します。
現在の市場価格と休止注文。
取引ロジック
SubscribeCandles() を通じて設定されたローソク足シリーズをサブスクライブし、各ローソク足で LinearRegression インジケーターをフィードします
完成したバー。このインジケーターは、MetaTrader バージョンからの手動トレンド ラインを表します。
- レベル 1 サブスクリプションを維持して、最新の最良入札値と最良売値をキャッシュします。最低限のことを強制するために使用されます
未決注文を再配置する前の距離パラメーター。
- 有効なスロットごとに、回帰値 + 距離 × ポイント サイズ として希望の価格を計算します。ポイント サイズのデフォルトは次のとおりです
証券価格ステップですが、MetaTrader の
Point 定数と一致するようにオーバーライドできます。
- スロット構成を StockSharp 順序ヘルパー (
BuyLimit、SellLimit、BuyStop、SellStop) に変換します。オプション
ストップロスとテイクプロフィットの価格はポイント単位で要求された距離から導出されるため、各動きの後に注文を追跡します。
- スロットにアクティブな未決注文がすでに存在し、新しい目標価格が異なる場合は、まず現在の注文をキャンセルしてから、
次のキャンドルが更新されたキャンドルを配置するまで待ちます。これは、MQL コードの
OrderModify の動作を反映しています。
リクエストが重複する危険があります。
- スロットが無効になった場合、または計算された価格が無効になった場合(マイナスなど)、関連する未決注文をキャンセルします。
そしてキャッシュされた状態をクリアします。
未決注文スロット
各スロットは、元の EA 内の modify() への 1 つの呼び出しをエミュレートします。スロットは個別に構成できます。
- タイプ — 買い指値、買いストップ、売り指値、または売りストップから選択します。
- 距離 — 新しい価格を取得するために回帰値に追加される MetaTrader ポイント単位の距離。負の値を使用すると、
回帰直線の下に次数を配置します。
- 出来高 — 未決注文のサイズ。 0 または負に設定すると、戦略はグローバル
TradeVolume に戻ります。
- フラグを有効にする — 設定を削除せずにスロットを無効にすることができます。無効化されたスロットは、アクティブなスロットを自動的にキャンセルします。
彼らに属する命令。
パラメーター
| 名前 |
種類 |
デフォルト |
説明 |
CandleType |
DataType |
1時間キャンドル |
回帰トレンドラインを作成するために使用される主な時間枠。 |
RegressionLength |
int |
24 |
LinearRegression インジケーターに入力される完了したローソク足の数。 |
PointValue |
decimal |
0 |
1 MetaTrader ポイントの金銭的価値。ゼロの場合、戦略は証券価格ステップを使用します。 |
TradeVolume |
decimal |
1 |
すべてのスロット自身のボリュームがゼロの場合に、すべてのスロットによって使用されるデフォルトのボリューム。 |
StopLossPoints |
decimal |
0 |
ポイント単位のストップロス距離。自動ストップロスの設定を無効にするには、ゼロに設定します。 |
TakeProfitPoints |
decimal |
0 |
テイクプロフィット距離(ポイント単位)。自動テイクプロフィット配置を無効にするには、ゼロに設定します。 |
MinDistancePoints |
decimal |
0 |
最良の買値/売値と未決注文の間に存在する必要がある最小ギャップ (ポイント単位)。 |
PendingOrder{1,2,3}Enabled |
bool |
スロット固有 |
指定されたスロットを有効または無効にします。 |
PendingOrder{1,2,3}Mode |
enum |
スロット固有 |
未決注文タイプ: BuyLimit、BuyStop、SellLimit、または SellStop。 |
PendingOrder{1,2,3}DistancePoints |
decimal |
スロット固有 |
注文価格を計算するために回帰値に追加される距離 (ポイント単位)。 |
PendingOrder{1,2,3}Volume |
decimal |
スロット固有 |
スロットのボリューム。ゼロは TradeVolume を再利用します。 |
- MetaTrader は既存の注文を変更します。 StockSharp は確認を待つ間、キャンセルと置換のセマンティクスを使用します
次のキャンドルに交換注文を登録する前に。
- 元のコードは、手動で描画された傾向線の値を読み取ります。ポートはこれを自動ポートに置き換えます。
LinearRegression インジケーターなので、動作は決定的であり、無人で実行できます。
MODE_STOPLEVEL は StockSharp では利用できません。代わりに、この戦略は構成可能な MinDistancePoints を提供します
パラメータを設定し、リアルタイムの買値/売値更新を使用してそれを強制します。
- ストップロスとテイクプロフィットの距離は、既存の注文設定を読み取る代わりにオプションのパラメーターです。これにより値が維持されます
注文の再登録全体で一貫性が保たれます。
使い方のヒント
- 証券
PriceStep と異なる場合は、ブローカーのポイント定義と一致するように PointValue を設定します。これにより、
距離パラメータは、対応する MetaTrader を反映します。
- 必要なスロットのみを有効にします。各スロットは独自の未決注文とコメント (
"MTrendLine slot N") を維持するため、
レポートまたは注文ログでそれらを確認するのは簡単です。
- トレーリング ストップやアカウントが必要な場合は、この戦略と StockSharp の組み込みリスク保護ヘルパーを組み合わせることを検討してください。
レベルコントロール。この実装は、元の順序変更ロジックをミラーリングすることに重点を置いています。
インジケーター
LinearRegression は完成したキャンドルに適用されます。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trend line strategy using linear regression slope for direction.
/// Enters long when slope is positive and price is above regression, short otherwise.
/// </summary>
public class MTrendLineStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _regressionLength;
private decimal _prevSlope;
private bool _hasPrev;
public MTrendLineStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for analysis.", "General");
_regressionLength = Param(nameof(RegressionLength), 20)
.SetDisplay("Regression Length", "Length of the linear regression.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RegressionLength
{
get => _regressionLength.Value;
set => _regressionLength.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSlope = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSlope = 0;
_hasPrev = false;
var lr = new LinearReg { Length = RegressionLength };
var ema = new ExponentialMovingAverage { Length = RegressionLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(lr, ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, lr);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal lrValue, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
// Compute slope as difference between LR and EMA
var slope = lrValue - emaValue;
if (!_hasPrev)
{
_prevSlope = slope;
_hasPrev = true;
return;
}
var close = candle.ClosePrice;
// Exit conditions
if (Position > 0 && slope < 0 && _prevSlope >= 0)
{
SellMarket();
}
else if (Position < 0 && slope > 0 && _prevSlope <= 0)
{
BuyMarket();
}
// Entry conditions
if (Position == 0)
{
if (slope > 0 && _prevSlope <= 0 && close > lrValue)
{
BuyMarket();
}
else if (slope < 0 && _prevSlope >= 0 && close < lrValue)
{
SellMarket();
}
}
_prevSlope = slope;
}
}
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 LinearReg, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class m_trend_line_strategy(Strategy):
"""
Trend line strategy using linear regression slope for direction.
"""
def __init__(self):
super(m_trend_line_strategy, self).__init__()
self._regression_length = self.Param("RegressionLength", 20) \
.SetDisplay("Regression Length", "Linear regression length", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._prev_slope = 0.0
self._has_prev = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(m_trend_line_strategy, self).OnReseted()
self._prev_slope = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(m_trend_line_strategy, self).OnStarted2(time)
lr = LinearReg()
lr.Length = self._regression_length.Value
ema = ExponentialMovingAverage()
ema.Length = self._regression_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(lr, ema, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, lr)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, lr_val, ema_val):
if candle.State != CandleStates.Finished:
return
lr = float(lr_val)
ema = float(ema_val)
slope = lr - ema
if not self._has_prev:
self._prev_slope = slope
self._has_prev = True
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_slope = slope
return
close = float(candle.ClosePrice)
if self.Position > 0 and slope < 0 and self._prev_slope >= 0:
self.SellMarket()
elif self.Position < 0 and slope > 0 and self._prev_slope <= 0:
self.BuyMarket()
if self.Position == 0:
if slope > 0 and self._prev_slope <= 0 and close > lr:
self.BuyMarket()
elif slope < 0 and self._prev_slope >= 0 and close < lr:
self.SellMarket()
self._prev_slope = slope
def CreateClone(self):
return m_trend_line_strategy()