GitHub で見る
ハードプロフィット
概要
Hard Profit は、MetaTrader 4 エキスパート アドバイザー hardprofit.mq4 の StockSharp 移植です。戦略はブレイクアウトを捕捉しようとします
消耗の動きの後、終値がローソク足の極値で終了し、平滑化されたトレンドフィルターが方向を確認したとき。
ポートは、StockSharp を使用して、元の資金管理モード、段階的な利益確定、および停止管理を再構築します。
高レベルの API。
戦略ロジック
ブレークアウトのセットアップ
- この戦略は、設定された時間枠から終了したローソク足を監視し、その最高値と最低値を追跡します。
前の
Breakout Period バー(現在のローソク足は除外され、シフト 1 で iHighest/iLowest 呼び出しをエミュレートします)。
- 中央価格は、期間
Trend Period の平滑化移動平均をフィードします。移動平均の傾き(現在値からマイナスした値)
前の値) は、元の EA で使用される方向フィルタです。
エントリールール
- 長いエントリは、次の場合に考慮されます。
- ローソク足はその高値で終了し、前の範囲の高値を上抜けます。
- 平滑化された移動平均の傾きは正です。
- オープンポジションはなく、バーごとの取引制限に達していません。
- 双方が利用可能な場合、現在のスプレッド(最良売値から最良入札を差し引いた値)は
Max Spread (pips) のしきい値を下回ります。
- ロング取引は
Only Short によって無効になりません。
- ショートエントリーは上記の条件を反映しています: 安値で閉じる、前のレンジの安値を下抜け、マイナスのトレンドの傾き、
スプレッド フィルタは尊重され、
Only Long は無効になります。
出口管理
- 固定のストップロス (
Stop Loss (pips)) とオプションのテイクプロフィット (Take Profit (pips)) が外側の保護エンベロープを定義します。
- 未実現利益が
Break-even (pips) に達すると、ストップはエントリー価格に移動します。 Trailing Activation (pips) の後、
stop はストップロス距離だけ先にジャンプし、MetaTrader の実装と同様に利益を確定します。
- 2 つの部分的な終了では、元のパーセンテージが再利用されます。
Partial TP1 (pips) はアクティブなポジションの Partial Ratio 1 (%) をクローズします。
Partial TP2 (pips) は残りのポジションの Partial Ratio 2 (%) を決済します。
ロジックは現在の位置のボリュームに作用し、最初のトリム後に残ったものに合わせて 2 番目の部分がスケーリングされます。
- ストップとターゲットはバー内の極端な値に反応します。ロングトレードは、ローソク足の安値がストップを突破したとき、または高値が突破したときに終了します。
利益目標を達成する。ショートトレードでは対称条件が使用されます。
お金の管理
5 つのサイジング モードは、StockSharp ポートフォリオ データを考慮しながら、MetaTrader の動作を模倣します。
- 修正 – すべてのエントリで
Fixed Volume を使用します。
- 幾何学的 – ポートフォリオ値 (
0.1 * sqrt(balance / 1000) * Geometrical Factor) の平方根に応じてスケールします。
- 比例 – 最新の終値 (
equity * Risk Percent / (price * 1000)) に対してフリー エクイティの一部を割り当てます。
- スマート – 比例配分から開始し、複数の連続損失が検出された場合にサイズを縮小します。
Decrease Factor 分周器を使用します。
- TSSF – トリガーされたスマート セーフ ファクター ロジックを再作成します。平均勝ち、平均負け、勝率は、最も多いものから計算されます。
最近の
Last Trades の実現結果。派生指標は、構成された TSSF Ratio 除数の間で切り替わるか、フォールバックします。
条件が悪化した場合は最小0.1ロットまで。すべての音量は楽器の VolumeStep、MinVolume、
および MaxVolume 制約。
パラメーター
- ブレイクアウト期間 – ブレイクアウト高値と安値を計算するために使用される完成したローソク足の数。
- トレンド期間 – 中央価格に適用される平滑化移動平均の長さ。
- ショートのみ / ロングのみ – 反対側を無効にする方向の切り替え。
- バーごとの最大取引数 – バーごとの取引ガード (0 は制限を無効にします)。
- ストップロス (pips) – 初期ストップロス距離。無効にするには 0 に設定します。
- 損益分岐点 (pips) – ストップをエントリーレベルに移動する利益のしきい値。
- トレーリング アクティベーション (pips) – ストップを元のストップ サイズだけ先に移動させる利益しきい値。
- 部分 TP1 (pips) / 部分比率 1 (%) – 最初の部分出口の距離とパーセンテージ。
- 部分 TP2 (pips) / 部分比率 2 (%) – 2 番目の部分出口の距離とパーセンテージ。
- テイクプロフィット (pips) – 最終的な利益目標。 0 はハード ターゲットを無効にします。
- 最大スプレッド (pips) – エントリー時に許可される最大スプレッド。
- 資金管理 – サイジング モード (固定、幾何学的、比例、スマート、TSSF) を選択します。
- 固定ボリューム – 資金管理モードが固定の場合の基本ボリューム。
- 幾何学的係数 – 幾何学的サイジング公式で使用される乗数。
- リスク パーセント – 比例サイジング、スマート サイジング、および TSSF サイジングによって使用されるフリー エクイティの割合。
- 最後の取引 – 適応サイジングのために保存された最近の実現した取引の数。
- 減少係数 – 連続損失が発生した場合にスマート モードに適用される除算器。
- TSSF トリガー 1/2/3 および TSSF 比率 1/2/3 – TSSF メトリック遷移のしきい値と除数。
- ローソク足タイプ – インジケーターの更新とシグナルの評価を促進する主要な時間枠。
追加の注意事項
- Pip 値は証券価格ステップから導出されます。 5 桁の FX シンボルは、1 ピップを 10 ポイントに自動的にマッピングします。
- 部分的なエグジットでは、足取引カウンターはリセットされず、新規エントリーのみをカウントする MetaTrader の動作が再現されます。
- 資金管理の統計は実現された損益の差から構築されるため、最初の取引が行われると履歴が意味を持つようになります。
StockSharp 環境では閉じます。
- 最良の買値/売値データが利用できない場合、スプレッド フィルタは実質的に無効になり、元の EA の動作と一致します。
ブローカーはゼロスプレッドを報告しました。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Hard Profit: Previous candle breakout with EMA filter and ATR stops.
/// </summary>
public class HardProfitStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _entryPrice;
public HardProfitStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).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();
_prevHigh = 0; _prevLow = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = 0; _prevLow = 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 (_prevHigh == 0 || atrVal <= 0) { _prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; return; }
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (close > _prevHigh && close > emaVal) { _entryPrice = close; BuyMarket(); }
else if (close < _prevLow && close < emaVal) { _entryPrice = close; SellMarket(); }
}
_prevHigh = candle.HighPrice; _prevLow = candle.LowPrice;
}
}
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 hard_profit_strategy(Strategy):
def __init__(self):
super(hard_profit_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.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_high = 0.0
self._prev_low = 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(hard_profit_strategy, self).OnStarted2(time)
self._prev_high = 0.0
self._prev_low = 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_high == 0 or av <= 0:
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
return
if self.Position > 0:
if close >= self._entry_price + av * 2.5 or close <= self._entry_price - av * 1.5:
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:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
return
if self.Position == 0:
if close > self._prev_high and close > ev:
self._entry_price = close
self.BuyMarket()
elif close < self._prev_low and close < ev:
self._entry_price = close
self.SellMarket()
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
def OnReseted(self):
super(hard_profit_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
def CreateClone(self):
return hard_profit_strategy()