在 GitHub 上查看
Parabolic SAR Cross 策略
本策略是 MetaTrader 平台「PSAR Trader EA」专家顾问在 StockSharp 上的移植版本。算法持续监控价格与 Parabolic SAR 指标之间的关系,只在指标点从蜡烛一侧翻转到另一侧时做出反应。移植过程中保留了原始的资金管理思想:既可以使用固定手数,也可以根据账户余额动态调整下单量,同时设置固定的止损/止盈,并在盈利达到指定阈值后启动追踪止损。
交易逻辑
- 在选定的 K 线序列上(默认 30 分钟周期)根据用户参数计算 Parabolic SAR 指标。
- 当 SAR 点从蜡烛实体上方翻到下方时认定为看多翻转。若当前无持仓则市价买入;若持有空单则先平掉空头,等待下一根信号再开多。
- 当 SAR 点从蜡烛实体下方翻到上方时认定为看空翻转。若当前空仓则市价卖出;若持有多单则先平掉多头,下一次信号再开空。
- 每根收盘 K 线都会检查已有仓位:一旦当根最高价或最低价触及任何保护性价位(止损、止盈或追踪止损),即刻平仓。
风险控制
- 止损以点数(价格最小变动单位)表示。多单止损设置在入场价下方,空单止损设置在入场价上方。
- 止盈同样以点数表示,与止损距离对称,价格触及即平仓。
- 追踪止损在价格向有利方向移动到指定点数后激活,仅在盈利方向上收紧止损,复刻原始 EA 中“只收紧不放松”的逻辑。
手数管理
- 固定手数:关闭自动手数时,按照参数中的固定值下单。
- 自动手数:开启后按
(账户余额 / 1000) * LotsPerThousand 计算下单量,并对齐品种的手数步长与最小下单量。
参数及默认值
SarStep — Parabolic SAR 加速步长,默认 0.02。
SarMaximum — Parabolic SAR 最大加速度,默认 0.2。
CandleType — 分析所用的 K 线类型,默认 30 分钟。
UseAutoLot — 是否启用自动手数,默认 false。
FixedLot — 关闭自动手数时的下单量,默认 0.1。
LotsPerThousand — 自动手数的余额系数,默认 0.05。
StopLossPoints — 止损距离(点),默认 500。
TakeProfitPoints — 止盈距离(点),默认 1000。
TrailingStartPoints — 启动追踪止损的盈利阈值(点),默认 500。
TrailingDistancePoints — 追踪止损的固定距离(点),默认 100。
备注
- 策略可做多亦可做空,但始终只保持单向一笔仓位。
- 保护性价位基于收盘 K 线进行判断,若实际交易中存在低于当前周期的瞬时波动,可能会影响真实成交效果。
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()