GitHub で見る
HPCS Inter5戦略
概要
HPCS Inter5 戦略 は、MetaTrader 4 エキスパート _HPCS_Inter5_MT4_EA_V01_WE から変換されたシングルショットのモメンタム スクリプトです。この戦略が開始されると、最後に完了したローソク足を検査し、5 バー前の終値が最新の終値よりも高ければ、成行買い注文を 1 つ送信します。オプションの保護ストップロスとテイクプロフィットディスタンスは、オリジナルの EA の pip ベースの動作をエミュレートします。
取引ロジック
- 設定されたローソク足シリーズを購読し、最後の 6 つの完了した終値を維持します。
- バッファがいっぱいになったら、5 バー前の終値と最新の終値 (MetaTrader で言えば
Close[5] > Close[1]) を比較します。
- 条件が満たされ、まだ取引が行われていない場合は、設定された数量で成行買い注文を送信します。
- 保護命令は、MetaTrader スタイルのピップ変換を使用して、起動時に
StartProtection を通じて一度発動されます。小数点以下 3 桁または 5 桁の商品は、PriceStep に 10 を乗じてピップ サイズを決定します。それ以外の場合は、生の PriceStep が使用されます。
この戦略は、最初のポジションが満たされると追加の取引を開始せず、その後のすべてのシグナルを無視します。
パラメーター
| 名前 |
デフォルト |
説明 |
Candle Type |
1分の時間枠 |
終値を収集するために使用されるローソクのタイプ。希望する信号間隔に一致する時間枠に設定します。 |
Stop Loss (pips) |
10 |
MetaTrader ピップス単位の保護ストップロスの距離。 0 の値は停止を無効にします。 |
Take Profit (pips) |
10 |
MetaTrader ピップ単位の保護テイクプロフィットまでの距離。 0 の値はテイクプロフィットを無効にします。 |
Trade Volume |
1 |
エントリー条件がトリガーされたときに送信された成行注文のボリューム。 |
実装メモ
- この戦略では、pip 距離を変換するために設定された
Security.PriceStep (または Security.Step) が必要です。この情報が欠落している場合、保護オフセットは非アクティブのままですが、エントリ信号は引き続き機能します。
- 完成したキャンドル (
CandleStates.Finished) のみが、Close[1] 以前の値に依存する MetaTrader の動作と一致するように処理されます。
- 内部バッファは、ソース EA の最小限の性質を尊重し、インジケーター履歴を使用せずにちょうど 6 つの終値を保持します。
IsFormedAndOnlineAndAllowTrading() は注文を送信する前にチェックされ、環境が実行の準備ができていることを確認します。
使用のヒント
- 適切な価格と出来高設定を備えた外国為替商品を割り当てます。
- 分析したい時間枠に合わせて
Candle Type を調整します。
- 手動でエグジットを管理したい場合は、ストップロスまたはテイクプロフィットをゼロのままにしておきます。
- ストラテジはセッションごとに 1 回だけ起動されるため、エントリ条件を再評価する場合は必ずストラテジを再起動してください。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Single-shot momentum strategy converted from the "_HPCS_Inter5" MetaTrader script.
/// Places one market buy when the close price from five bars ago exceeds the most recent close.
/// </summary>
public class HpcsInter5Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<int> _takeProfitPips;
private readonly StrategyParam<decimal> _tradeVolume;
private readonly decimal?[] _recentCloses = new decimal?[6];
private decimal _pipSize;
private bool _wasLongSignal;
private bool _hasSignal;
/// <summary>
/// Candle type used to evaluate the closing prices.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Stop-loss distance expressed in MetaTrader-style pips.
/// </summary>
public int StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take-profit distance expressed in MetaTrader-style pips.
/// </summary>
public int TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Trade volume submitted with the market entry.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set => _tradeVolume.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="HpcsInter5Strategy"/> class.
/// </summary>
public HpcsInter5Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(120).TimeFrame())
.SetDisplay("Candle Type", "Candle type used for the close comparison", "General");
_stopLossPips = Param(nameof(StopLossPips), 20)
.SetDisplay("Stop Loss (pips)", "Stop-loss distance expressed in pips", "Risk Management");
_takeProfitPips = Param(nameof(TakeProfitPips), 20)
.SetDisplay("Take Profit (pips)", "Take-profit distance expressed in pips", "Risk Management");
_tradeVolume = Param(nameof(TradeVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Trade Volume", "Volume submitted with the market entry", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
Array.Clear(_recentCloses, 0, _recentCloses.Length);
_pipSize = 0m;
_wasLongSignal = false;
_hasSignal = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
if (Security is null)
throw new InvalidOperationException("Security must be assigned before starting the strategy.");
base.OnStarted2(time);
InitializePipSize();
Volume = TradeVolume;
_wasLongSignal = false;
_hasSignal = false;
var stopLoss = StopLossPips > 0 && _pipSize > 0m
? new Unit(StopLossPips * _pipSize, UnitTypes.Absolute)
: null;
var takeProfit = TakeProfitPips > 0 && _pipSize > 0m
? new Unit(TakeProfitPips * _pipSize, UnitTypes.Absolute)
: null;
if (stopLoss != null || takeProfit != null)
StartProtection(stopLoss: stopLoss, takeProfit: takeProfit);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
ShiftCloses(candle.ClosePrice);
if (_recentCloses[1] is not decimal lastClose || _recentCloses[5] is not decimal olderClose)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var threshold = lastClose * 0.005m;
var longSignal = olderClose - lastClose > threshold;
var shortSignal = lastClose - olderClose > threshold;
var crossedLong = longSignal && (!_hasSignal || !_wasLongSignal);
var crossedShort = shortSignal && (!_hasSignal || _wasLongSignal);
if (crossedLong && Position <= 0)
{
BuyMarket();
}
else if (crossedShort && Position >= 0)
{
SellMarket();
}
if (longSignal || shortSignal)
{
_wasLongSignal = longSignal;
_hasSignal = true;
}
}
private void ShiftCloses(decimal close)
{
for (var i = _recentCloses.Length - 1; i > 0; i--)
_recentCloses[i] = _recentCloses[i - 1];
_recentCloses[0] = close;
}
private void InitializePipSize()
{
var step = Security.PriceStep ?? 0.01m;
if (step <= 0m)
step = 0.01m;
var pipFactor = Security.Decimals is 3 or 5 ? 10m : 1m;
_pipSize = step * pipFactor;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class hpcs_inter5_strategy(Strategy):
"""
HPCS Inter5: momentum strategy comparing close from 5 bars ago
vs current close. Buys when older close exceeds recent by threshold.
Sells on reverse. Uses StartProtection for SL/TP.
"""
def __init__(self):
super(hpcs_inter5_strategy, self).__init__()
self._stop_loss_pips = self.Param("StopLossPips", 20) \
.SetDisplay("Stop Loss (pips)", "Stop-loss distance in pips", "Risk Management")
self._take_profit_pips = self.Param("TakeProfitPips", 20) \
.SetDisplay("Take Profit (pips)", "Take-profit distance in pips", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(120))) \
.SetDisplay("Candle Type", "Candle type for close comparison", "General")
self._recent_closes = [None] * 6
self._pip_size = 0.0
self._was_long_signal = False
self._has_signal = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(hpcs_inter5_strategy, self).OnReseted()
self._recent_closes = [None] * 6
self._pip_size = 0.0
self._was_long_signal = False
self._has_signal = False
def OnStarted2(self, time):
super(hpcs_inter5_strategy, self).OnStarted2(time)
step = 0.01
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 0.01
decimals = 0
if self.Security is not None and self.Security.Decimals is not None:
decimals = int(self.Security.Decimals)
pip_factor = 10.0 if decimals in (3, 5) else 1.0
self._pip_size = step * pip_factor
sl_pips = self._stop_loss_pips.Value
tp_pips = self._take_profit_pips.Value
sl = None
tp = None
if sl_pips > 0 and self._pip_size > 0:
sl = Unit(sl_pips * self._pip_size, UnitTypes.Absolute)
if tp_pips > 0 and self._pip_size > 0:
tp = Unit(tp_pips * self._pip_size, UnitTypes.Absolute)
if sl is not None or tp is not None:
self.StartProtection(tp, sl)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
for i in range(len(self._recent_closes) - 1, 0, -1):
self._recent_closes[i] = self._recent_closes[i - 1]
self._recent_closes[0] = close
last_close = self._recent_closes[1]
older_close = self._recent_closes[5]
if last_close is None or older_close is None:
return
threshold = last_close * 0.005
long_signal = older_close - last_close > threshold
short_signal = last_close - older_close > threshold
crossed_long = long_signal and (not self._has_signal or not self._was_long_signal)
crossed_short = short_signal and (not self._has_signal or self._was_long_signal)
if crossed_long and self.Position <= 0:
self.BuyMarket()
elif crossed_short and self.Position >= 0:
self.SellMarket()
if long_signal or short_signal:
self._was_long_signal = long_signal
self._has_signal = True
def CreateClone(self):
return hpcs_inter5_strategy()