GitHub で見る
AIS2 トレーディング ロボット 20005 (StockSharp ポート)
概要
AIS2 Trading Robot 20005 は、もともと MetaTrader 4 用に書かれた日中ブレイクアウト エキスパート アドバイザーです。ポートは、StockSharp の高レベル戦略 API に基づいてマルチタイムフレーム ロジックを再作成します。この戦略は、以前のより高いタイムフレームのローソク足の中間点を上回る/下で勢いがブレイクアウトするのを待ち、そのローソク足の範囲から導出される動的なテイクプロフィットとストップロスの距離を適用し、トレーリングストップを駆動する2番目のより速いタイムフレームでポジションを管理します。
この変換は透明性と手動制御に重点を置いています。ポジションは成行注文でオープンされ、戦略自体の内部で保護レベルが強制され、設定可能な取引一時停止により急速な再エントリーが防止されます。株式ベースのポジションサイジングは元の「リザーブ」ロジックを反映しており、ユーザーは資本バッファーに手を加えずにポートフォリオ価値の一部を各取引に割り当てることができます。
中核ロジック
- プライマリ タイムフレーム分析 – メイン タイムフレーム (デフォルトは 15 分) の終了したローソク足ごとに、戦略は以下を計算します。
- ローソクの中点
(High + Low) / 2。
- レンジベースのテイクプロフィットとストップロスの距離 (
range * TakeFactor と range * StopFactor)。
- 現在の広がり近似、バッファーの停止/フリーズ、および最小のトレーリング ステップ。
- ブレイクアウト条件 – ロングエントリーには、中間点を上回る終値と、現在のアスク価格が前の高値プラススプレッドを突破する両方が必要です。ショートパンツは低音の状態を反映しています。計算されたストップ/ターゲット距離がブローカーレベルの制約を満たさない場合、注文はブロックされます。
- リスク管理 – ポジション サイズはポートフォリオの資本から導出されます。
OrderReserve は取引可能な部分を定義し、AccountReserve は一部をそのまま維持します。利用可能な資本またはブローカーの制限により取引が許可されない場合、セットアップはスキップされます。
- 取引管理 – より速い時間枠 (デフォルトは 1 分) により、後続距離が継続的に更新されます。価格が上昇するにつれて、セカンダリレンジが正当化されると、ストップは取引に有利に移動します。目標またはストップのいずれかに到達すると、即時に市場から退場します。
- 運用上のガードレール – クールダウン タイマー (
TradingPauseSeconds) は、元の MQL 取引一時停止を再現します。この戦略は、ライブの買値/売値を取得するためにオーダーブックにもサブスクライブします。利用できない場合は、ローソク足の終値に戻ります。
パラメーター
| パラメータ |
説明 |
デフォルト |
PrimaryCandleType |
エントリーシグナルの生成に使用されるより高いタイムフレーム。 |
15分キャンドル |
SecondaryCandleType |
トレーリングストップ計算のより短い時間枠。 |
1分キャンドル |
TakeFactor |
テイクプロフィットディスタンスを構築するためにプライマリローソク足レンジに適用される乗数。 |
1.7 |
StopFactor |
ストップロス距離を構築するためにプライマリローソク足の範囲に適用される乗数。 |
1.7 |
TrailFactor |
トレーリング更新のセカンダリローソク足範囲に適用される乗数。 |
0.5 |
AccountReserve |
準備金として保有される株式の一部(取引には使用されません)。 |
0.20 |
OrderReserve |
バッファ前の取引ごとに割り当てられた総資本の割合。 |
0.04 |
BaseVolume |
リスクサイジングを計算できない場合のフォールバック取引量。 |
1ロット |
StopBufferTicks |
ブローカーのストップレベルのコンプライアンスチェックに追加のティックが追加されました。 |
0 |
FreezeBufferTicks |
追加のティックにより、フリーズ レベル付近での頻繁な停止更新が防止されます。 |
0 |
TrailStepMultiplier |
後続のステップを検証するときにスプレッドに適用される乗数。 |
1 |
TradingPauseSeconds |
連続した取引間のクールダウン。 |
5秒 |
すべての数値パラメーターは SetCanOptimize() (意味のある場合) を公開するため、StockSharp の最適化シナリオに参加できます。
使用上の注意
- ストラテジーを証券に添付し、レベル 1/オーダーブック データが正確なスプレッド検出に利用できるようにします。ライブクオートがない場合でも、ロジックはローソク足の終値を使用して実行されますが、ストップ検証は保守的になります。
PrimaryCandleType/SecondaryCandleType をデータ フィードに存在するタイムフレームに設定します。ポートは SubscribeCandles を使用し、StockSharp の高レベル API を通じてハンドラーをバインドします。
- トレーリングストップは仮想です (内部で管理されます)。ストップ注文はブローカーに送信されません。サーバー側での停止が必要な場合は、エントリ後に保護命令を登録するようにコードを拡張します。
StartProtection() は開始時に呼び出され、必要に応じてエンジンが予期しないポジションを清算します。
オリジナルの EA との違い
- MetaTrader バージョンは端末全体のグローバル変数を操作しました。このポートはパラメータをストラテジー内に保持し、
StrategyParam ラッパー経由で公開します。
- StockSharp はアルゴリズム自体内でストップ/ターゲット ロジックを処理するため、注文の変更は直接市場からの決済に置き換えられました。
- リスク計算は、MT4 からの口座残高クエリではなく、StockSharp によって提供されるポートフォリオの資本に基づいて行われます。
ファイル
CS/Ais2TradingRobot20005Strategy.cs – StockSharp の高レベル API を使用した戦略の実装。
README.md – 英語の説明 (このファイル)。
README_zh.md – 中国語翻訳。
README_ru.md – ロシア語の翻訳。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// AIS2 Trading Robot: range breakout strategy.
/// Enters on close above/below previous candle range midpoint,
/// uses ATR for trailing stop management.
/// </summary>
public class Ais2TradingRobot20005Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _takeFactor;
private readonly StrategyParam<decimal> _stopFactor;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _prevMid;
private decimal _entryPrice;
private decimal _stopPrice;
public Ais2TradingRobot20005Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
_takeFactor = Param(nameof(TakeFactor), 1.7m)
.SetDisplay("Take Factor", "ATR multiplier for take profit.", "Risk");
_stopFactor = Param(nameof(StopFactor), 1.0m)
.SetDisplay("Stop Factor", "ATR multiplier for stop loss.", "Risk");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public decimal TakeFactor
{
get => _takeFactor.Value;
set => _takeFactor.Value = value;
}
public decimal StopFactor
{
get => _stopFactor.Value;
set => _stopFactor.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = 0;
_prevLow = 0;
_prevMid = 0;
_entryPrice = 0;
_stopPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = 0;
_prevLow = 0;
_prevMid = 0;
_entryPrice = 0;
_stopPrice = 0;
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
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 (_prevHigh == 0 || atrVal <= 0)
{
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevMid = (candle.HighPrice + candle.LowPrice) / 2m;
return;
}
var close = candle.ClosePrice;
var takeDistance = atrVal * TakeFactor;
var stopDistance = atrVal * StopFactor;
// Manage open position
if (Position > 0)
{
// Take profit
if (close - _entryPrice >= takeDistance)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
}
// Stop loss
else if (_stopPrice > 0 && close <= _stopPrice)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
}
// Trail stop
else
{
var newStop = close - stopDistance;
if (newStop > _stopPrice)
_stopPrice = newStop;
}
}
else if (Position < 0)
{
if (_entryPrice - close >= takeDistance)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
}
else if (_stopPrice > 0 && close >= _stopPrice)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
}
else
{
var newStop = close + stopDistance;
if (newStop < _stopPrice || _stopPrice == 0)
_stopPrice = newStop;
}
}
// New entry: breakout above previous high with close above midpoint
if (Position == 0)
{
if (close > _prevHigh && close > _prevMid)
{
_entryPrice = close;
_stopPrice = close - stopDistance;
BuyMarket();
}
else if (close < _prevLow && close < _prevMid)
{
_entryPrice = close;
_stopPrice = close + stopDistance;
SellMarket();
}
}
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevMid = (candle.HighPrice + candle.LowPrice) / 2m;
}
}
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 AverageTrueRange
class ais2_trading_robot20005_strategy(Strategy):
def __init__(self):
super(ais2_trading_robot20005_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period", "Indicators")
self._take_factor = self.Param("TakeFactor", 1.7) \
.SetDisplay("Take Factor", "ATR multiplier for take profit", "Risk")
self._stop_factor = self.Param("StopFactor", 1.0) \
.SetDisplay("Stop Factor", "ATR multiplier for stop loss", "Risk")
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_mid = 0.0
self._entry_price = 0.0
self._stop_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def AtrLength(self):
return self._atr_length.Value
@property
def TakeFactor(self):
return self._take_factor.Value
@property
def StopFactor(self):
return self._stop_factor.Value
def OnStarted2(self, time):
super(ais2_trading_robot20005_strategy, self).OnStarted2(time)
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_mid = 0.0
self._entry_price = 0.0
self._stop_price = 0.0
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, atr_val):
if candle.State != CandleStates.Finished:
return
av = float(atr_val)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self._prev_high == 0 or av <= 0:
self._prev_high = high
self._prev_low = low
self._prev_mid = (high + low) / 2.0
return
take_distance = av * float(self.TakeFactor)
stop_distance = av * float(self.StopFactor)
# Manage open position
if self.Position > 0:
if close - self._entry_price >= take_distance:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
elif self._stop_price > 0 and close <= self._stop_price:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
else:
new_stop = close - stop_distance
if new_stop > self._stop_price:
self._stop_price = new_stop
elif self.Position < 0:
if self._entry_price - close >= take_distance:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
elif self._stop_price > 0 and close >= self._stop_price:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
else:
new_stop = close + stop_distance
if new_stop < self._stop_price or self._stop_price == 0:
self._stop_price = new_stop
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_high = high
self._prev_low = low
self._prev_mid = (high + low) / 2.0
return
# New entry
if self.Position == 0:
if close > self._prev_high and close > self._prev_mid:
self._entry_price = close
self._stop_price = close - stop_distance
self.BuyMarket()
elif close < self._prev_low and close < self._prev_mid:
self._entry_price = close
self._stop_price = close + stop_distance
self.SellMarket()
self._prev_high = high
self._prev_low = low
self._prev_mid = (high + low) / 2.0
def OnReseted(self):
super(ais2_trading_robot20005_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_mid = 0.0
self._entry_price = 0.0
self._stop_price = 0.0
def CreateClone(self):
return ais2_trading_robot20005_strategy()