Bot für Spot-Markt - Benutzerdefiniertes Grid-Strategie
Die Bot-für-Spot-Markt-Strategie mit benutzerdefiniertem Grid kauft eine Anfangsposition und fügt neue Aufträge hinzu, wenn der Preis um einen bestimmten Prozentsatz unter den letzten Einstieg fällt. Alle Positionen werden geschlossen, wenn der Preis das Gewinnziel über dem durchschnittlichen Einstiegspreis überschreitet.
Details
- Einstiegskriterien:
- Kauf zum Startzeitpunkt.
- Zusätzliche Menge kaufen, wenn der Preis
NextEntryPercent% unter den letzten Einstieg fällt.
- Long/Short: Nur Long.
- Ausstiegskriterien:
- Alle Positionen schließen, wenn der Preis den durchschnittlichen Einstiegspreis um
ProfitPercent% übersteigt und die offene Position profitabel ist.
- Alle Positionen schließen, wenn der Preis den durchschnittlichen Einstiegspreis um
- Stops: Keine.
- Standardwerte:
OrderValue= 10MinAmountMovement= 0.00001Rounding= 5NextEntryPercent= 0.5ProfitPercent= 2
- Filter:
- Kategorie: Grid-Trading
- Richtung: Long
- Indikatoren: Keine
- Stops: Nein
- Komplexität: Niedrig
- Zeitrahmen: Beliebig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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>
/// Bot for Spot Market - Custom Grid strategy.
/// </summary>
public class BotForSpotMarketCustomGridStrategy : Strategy
{
private readonly StrategyParam<decimal> _orderValue;
private readonly StrategyParam<decimal> _minAmountMovement;
private readonly StrategyParam<int> _rounding;
private readonly StrategyParam<decimal> _nextEntryPercent;
private readonly StrategyParam<decimal> _profitPercent;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<DateTimeOffset> _startTime;
private decimal _lastEntryPrice;
private decimal _avgPrice;
private decimal _positionVolume;
private bool _initialOrderSent;
/// <summary>
/// Order value in asset currency.
/// </summary>
public decimal OrderValue { get => _orderValue.Value; set => _orderValue.Value = value; }
/// <summary>
/// Minimum amount movement.
/// </summary>
public decimal MinAmountMovement { get => _minAmountMovement.Value; set => _minAmountMovement.Value = value; }
/// <summary>
/// Number of decimal places for rounding.
/// </summary>
public int Rounding { get => _rounding.Value; set => _rounding.Value = value; }
/// <summary>
/// Percentage drop from last entry to place next buy.
/// </summary>
public decimal NextEntryPercent { get => _nextEntryPercent.Value; set => _nextEntryPercent.Value = value; }
/// <summary>
/// Profit target percentage from average entry price.
/// </summary>
public decimal ProfitPercent { get => _profitPercent.Value; set => _profitPercent.Value = value; }
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Start time for trading.
/// </summary>
public DateTimeOffset StartTime { get => _startTime.Value; set => _startTime.Value = value; }
/// <summary>
/// Initializes a new instance of the <see cref="BotForSpotMarketCustomGridStrategy"/>.
/// </summary>
public BotForSpotMarketCustomGridStrategy()
{
_orderValue = Param(nameof(OrderValue), 10m)
.SetDisplay("Order Value", "Desired order value", "Parameters");
_minAmountMovement = Param(nameof(MinAmountMovement), 0.00001m)
.SetDisplay("Min Amount Movement", "Minimum allowed amount movement", "Parameters");
_rounding = Param(nameof(Rounding), 5)
.SetDisplay("Rounding", "Decimal places for rounding", "Parameters");
_nextEntryPercent = Param(nameof(NextEntryPercent), 10m)
.SetDisplay("Next Entry Less Than (%)", "Price drop from last entry to add new order", "Parameters");
_profitPercent = Param(nameof(ProfitPercent), 15m)
.SetDisplay("Profit (%)", "Profit target from average price", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_startTime = Param(nameof(StartTime), new DateTimeOffset(2000, 1, 1, 0, 0, 0, TimeSpan.Zero))
.SetDisplay("Start Time", "Time to begin trading", "Time");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_lastEntryPrice = 0;
_avgPrice = 0;
_positionVolume = 0;
_initialOrderSent = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (candle.OpenTime < StartTime)
return;
var price = candle.ClosePrice;
if (Position <= 0 && !_initialOrderSent)
{
BuyMarket();
_lastEntryPrice = price;
_avgPrice = price;
_initialOrderSent = true;
return;
}
if (Position > 0 && _lastEntryPrice > 0 && price < _lastEntryPrice * (1 - NextEntryPercent / 100m))
{
BuyMarket();
return;
}
if (Position > 0 && _avgPrice > 0)
{
var target = _avgPrice * (1 + ProfitPercent / 100m);
if (price > target)
SellMarket();
}
}
private decimal GetQuantity(decimal closePrice)
{
var price = OrderValue / closePrice;
var round = Math.Round(price, Rounding);
var quant = round >= price ? round + MinAmountMovement : round + (MinAmountMovement * 2m);
return quant;
}
/// <inheritdoc />
protected override void OnOwnTradeReceived(MyTrade trade)
{
if (trade.Order == null)
return;
if (trade.Order.Side == Sides.Buy)
{
var newVolume = _positionVolume + trade.Trade.Volume;
_avgPrice = (_avgPrice * _positionVolume + trade.Trade.Price * trade.Trade.Volume) / newVolume;
_positionVolume = newVolume;
_lastEntryPrice = trade.Trade.Price;
}
else if (trade.Order.Side == Sides.Sell)
{
_positionVolume -= trade.Trade.Volume;
if (_positionVolume <= 0)
{
_positionVolume = 0;
_avgPrice = 0;
_lastEntryPrice = 0;
_initialOrderSent = false;
}
}
}
}
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
class bot_for_spot_market_custom_grid_strategy(Strategy):
def __init__(self):
super(bot_for_spot_market_custom_grid_strategy, self).__init__()
self._next_entry_percent = self.Param("NextEntryPercent", 10.0) \
.SetDisplay("Next Entry Less Than (%)", "Price drop from last entry to add new order", "Parameters")
self._profit_percent = self.Param("ProfitPercent", 15.0) \
.SetDisplay("Profit (%)", "Profit target from average price", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._last_entry_price = 0.0
self._avg_price = 0.0
self._initial_order_sent = False
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(bot_for_spot_market_custom_grid_strategy, self).OnReseted()
self._last_entry_price = 0.0
self._avg_price = 0.0
self._initial_order_sent = False
def OnStarted2(self, time):
super(bot_for_spot_market_custom_grid_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
price = float(candle.ClosePrice)
if self.Position <= 0 and not self._initial_order_sent:
self.BuyMarket()
self._last_entry_price = price
self._avg_price = price
self._initial_order_sent = True
return
next_entry = float(self._next_entry_percent.Value)
if self.Position > 0 and self._last_entry_price > 0 and price < self._last_entry_price * (1.0 - next_entry / 100.0):
self.BuyMarket()
self._avg_price = (self._avg_price + price) / 2.0
self._last_entry_price = price
return
profit_pct = float(self._profit_percent.Value)
if self.Position > 0 and self._avg_price > 0:
target = self._avg_price * (1.0 + profit_pct / 100.0)
if price > target:
self.SellMarket()
self._last_entry_price = 0.0
self._avg_price = 0.0
self._initial_order_sent = False
def CreateClone(self):
return bot_for_spot_market_custom_grid_strategy()