Bot para Mercado Spot - Estrategia de Cuadrícula Personalizada
La estrategia Bot para Mercado Spot - Cuadrícula Personalizada compra una posición inicial y agrega nuevas órdenes cuando el precio cae un porcentaje especificado por debajo del último punto de entrada. Cierra todas las posiciones cuando el precio sube por encima del precio de entrada promedio en el objetivo de ganancia.
Detalles
- Criterios de entrada:
- Comprar en el momento de inicio.
- Comprar cantidad adicional cuando el precio cae
NextEntryPercent% por debajo del último punto de entrada.
- Largo/Corto: Solo largos.
- Criterios de salida:
- Cerrar todas las posiciones cuando el precio supera el precio de entrada promedio en
ProfitPercent% y la posición abierta es rentable.
- Cerrar todas las posiciones cuando el precio supera el precio de entrada promedio en
- Stops: Ninguno.
- Valores predeterminados:
OrderValue= 10MinAmountMovement= 0.00001Rounding= 5NextEntryPercent= 0.5ProfitPercent= 2
- Filtros:
- Categoría: Grid trading
- Dirección: Largo
- Indicadores: Ninguno
- Stops: No
- Complejidad: Bajo
- Marco temporal: Cualquiera
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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()