GitHub で見る
Parabolic SAR Cross戦略
この戦略は、MetaTraderの「PSAR Trader EA」エキスパートアドバイザーをStockSharpへ移植したものです。価格がParabolic SAR指標とどう相互作用するかを観察し、点のフィールドがローソク足実体の片側から反対側へ反転した場合にのみ反応します。変換では元の資金管理ロジックを保持しています。固定ロットで取引するか、口座残高に基づいて注文数量を動的調整でき、固定ストップロスとテイクプロフィットを適用し、取引が十分な利益を積み上げるとトレーリングストップを起動します。
戦略ロジック
- 選択したローソク足系列(既定は30分足)上に、ユーザー定義の加速値と最大値を持つParabolic SAR指標を構築します。
- SARドットがローソク足実体の上から下へ移動したとき、強気反転を検出します。ポジションがなければ成行買い注文を送信します。ショートポジションがある場合は先に閉じ、次のシグナルでロングへ再エントリーします。
- SARドットがローソク足実体の下から上へ移動したとき、弱気反転を検出します。フラットならショートを開きます。ロングポジションが有効な場合は閉じ、次のシグナルまでエントリーを延期します。
- 各確定ローソク足でオープン取引を監視し、現在ローソク足の高値/安値が保護水準(ストップロス、テイクプロフィット、トレーリングストップ)に到達した場合に決済します。
リスク管理
- Stop loss: ポイント(価格ステップ)で表します。ロングではエントリー価格の下、ショートでは上に置きます。
- Take profit: これもポイントで表します。目標はストップの反対方向に対応し、到達するとポジション全体を閉じます。
- Trailing stop: 価格が設定ポイント数だけ取引に有利に動いた後で開始します。トレーリングストップは利益方向にのみ引き締まり、元EAの「tighten stops only」動作を再現します。
数量管理
- 固定ロット: auto-lotが無効な場合、戦略は設定した固定ロットサイズで注文を送信します。
- 残高ベースロット: auto-lotが有効な場合、数量は
(Account Balance / 1000) * LotsPerThousandとして計算され、銘柄の数量ステップと最小数量に合わせられます。
パラメーターと既定値
SarStep: Parabolic SAR加速係数。既定値: 0.02。
SarMaximum: Parabolic SAR最大加速。既定値: 0.2。
CandleType: 分析時間軸。既定値: 30分足。
UseAutoLot: 動的ロットサイズを有効化。既定値: false。
FixedLot: auto lot sizingが無効な場合の数量。既定値: 0.1。
LotsPerThousand: auto-lot計算用乗数。既定値: 0.05。
StopLossPoints: ストップまでの距離(ポイント)。既定値: 500。
TakeProfitPoints: テイクプロフィットまでの距離(ポイント)。既定値: 1000。
TrailingStartPoints: トレーリングを有効にする利益しきい値。既定値: 500。
TrailingDistancePoints: 有効化後のトレーリングオフセット。既定値: 100。
注記
- 戦略はロングとショートの両方向を取引しますが、一度に保持するポジションは最大1つです。
- 保護注文はローソク足データ上でシミュレートされます。選択時間軸より短いバー内スパイクは、ライブ取引で約定品質に影響する場合があります。
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Parabolic SAR Cross strategy: PSAR crossover.
/// Buys when close crosses above PSAR. Sells when close crosses below PSAR.
/// </summary>
public class ParabolicSarCrossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public ParabolicSarCrossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sar = new ParabolicSar { Acceleration = 0.02m, AccelerationStep = 0.02m, AccelerationMax = 0.2m };
decimal? prevSar = null;
decimal? prevClose = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sar, (candle, sarVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (prevSar.HasValue && prevClose.HasValue)
{
var crossUp = prevClose.Value <= prevSar.Value && close > sarVal;
var crossDown = prevClose.Value >= prevSar.Value && close < sarVal;
if (crossUp && Position <= 0)
BuyMarket();
else if (crossDown && Position >= 0)
SellMarket();
}
prevSar = sarVal;
prevClose = close;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sar);
DrawOwnTrades(area);
}
}
}
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 ParabolicSar
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class parabolic_sar_cross_strategy(Strategy):
def __init__(self):
super(parabolic_sar_cross_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Candle timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(parabolic_sar_cross_strategy, self).OnReseted()
self._prev_sar = None
self._prev_close = None
def OnStarted2(self, time):
super(parabolic_sar_cross_strategy, self).OnStarted2(time)
self._prev_sar = None
self._prev_close = None
sar = ParabolicSar()
sar.Acceleration = 0.02
sar.AccelerationStep = 0.02
sar.AccelerationMax = 0.2
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(sar, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, sar)
self.DrawOwnTrades(area)
def OnProcess(self, candle, sar_val):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
if self._prev_sar is not None and self._prev_close is not None:
cross_up = self._prev_close <= self._prev_sar and close > sar_val
cross_down = self._prev_close >= self._prev_sar and close < sar_val
if cross_up and self.Position <= 0:
self.BuyMarket()
elif cross_down and self.Position >= 0:
self.SellMarket()
self._prev_sar = sar_val
self._prev_close = close
def CreateClone(self):
return parabolic_sar_cross_strategy()