Files
managing-apps/src/Managing.Domain/Shared/Helpers/TradingBox.cs
Oda 0987fa76cf Global fix (#9)
* Fix time for candle

* Fix out ouf range

* Fix pnl, fix custom money management

* Clean a bit
2025-02-04 14:59:39 +07:00

175 lines
6.8 KiB
C#

using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Strategies;
using Managing.Domain.Trades;
using static Managing.Common.Enums;
namespace Managing.Domain.Shared.Helpers;
public static class TradingBox
{
public static Signal GetSignal(HashSet<Candle> newCandles, HashSet<IStrategy> strategies,
HashSet<Signal> previousSignal)
{
var signalOnCandles = new HashSet<Signal>();
foreach (var strategy in strategies)
{
strategy.UpdateCandles(newCandles);
var signals = strategy.Run();
if (signals == null || signals.Count == 0) continue;
foreach (var signal in signals.Where(s => s.Date == newCandles.Last().Date))
{
if (previousSignal.SingleOrDefault(s => s.Identifier == signal.Identifier) == null)
{
if (previousSignal.Count == 0 || previousSignal.Last().Date < signal.Date)
{
signalOnCandles.Add(signal);
}
}
}
}
if (signalOnCandles.Count != strategies.Count)
return null;
var data = newCandles.First();
return ComputeSignals(strategies, signalOnCandles, MiscExtensions.ParseEnum<Ticker>(data.Ticker),
data.Timeframe);
}
public static Signal ComputeSignals(HashSet<IStrategy> strategies, HashSet<Signal> signalOnCandles, Ticker ticker,
Timeframe timeframe)
{
Signal signal = null;
if (strategies.Count > 1)
{
var trendSignal = signalOnCandles.Where(s => s.SignalType == SignalType.Trend).ToList();
var signals = signalOnCandles.Where(s => s.SignalType == SignalType.Signal).ToList();
var contextStrategiesCount = strategies.Count(s => s.SignalType == SignalType.Context);
var validContext = true;
if (contextStrategiesCount > 0 &&
signalOnCandles.Count(s => s.SignalType == SignalType.Context) != contextStrategiesCount)
{
validContext = false;
}
if (signals.All(s => s.Direction == TradeDirection.Long) &&
trendSignal.All(t => t.Direction == TradeDirection.Long) && validContext)
{
signal = new Signal(
ticker,
TradeDirection.Long,
Confidence.High,
signals.Last().Candle,
signals.Last().Date,
signals.Last().Exchange,
timeframe,
StrategyType.Composite, SignalType.Signal);
}
else if (signals.All(s => s.Direction == TradeDirection.Short) &&
trendSignal.All(t => t.Direction == TradeDirection.Short) && validContext)
{
signal = new Signal(
ticker,
TradeDirection.Short,
Confidence.High,
signals.Last().Candle,
signals.Last().Date,
signals.Last().Exchange,
timeframe,
StrategyType.Composite, SignalType.Signal);
}
}
else
{
// Only one strategy, we just add the single signal to the bot
signal = signalOnCandles.Single();
}
return signal;
}
public static MoneyManagement GetBestMoneyManagement(List<Candle> candles, List<Position> positions,
MoneyManagement originMoneyManagement)
{
// Foreach positions, identitify the price when the position is open
// Then, foreach candles, get the maximum price before the next position
// Then, identify the lowest price before the maximum price
// Base on that, return the best StopLoss and TakeProfit to use and build a
var moneyManagement = new MoneyManagement();
var stoplossPercentage = new List<decimal>();
var takeProfitsPercentage = new List<decimal>();
if (positions.Count == 0)
return null;
for (var i = 0; i < positions.Count; i++)
{
var position = positions[i];
var nextPosition = i + 1 < positions.Count ? positions[i + 1] : null;
var (stopLoss, takeProfit) = GetBestSltpForPosition(candles, position, nextPosition);
stoplossPercentage.Add(stopLoss);
takeProfitsPercentage.Add(takeProfit);
}
moneyManagement.StopLoss = stoplossPercentage.Average();
moneyManagement.TakeProfit = takeProfitsPercentage.Average();
moneyManagement.BalanceAtRisk = originMoneyManagement.BalanceAtRisk * 100;
moneyManagement.Timeframe = originMoneyManagement.Timeframe;
moneyManagement.Leverage = originMoneyManagement.Leverage;
moneyManagement.Name = "Optimized";
return moneyManagement;
}
public static (decimal Stoploss, decimal TakeProfit) GetBestSltpForPosition(List<Candle> candles, Position position,
Position nextPosition)
{
var stopLoss = 0M;
var takeProfit = 0M;
var candlesBeforeNextPosition = candles.Where(c =>
c.Date >= position.Date && c.Date <= (nextPosition == null ? candles.Last().Date : nextPosition.Date))
.ToList();
if (position.OriginDirection == TradeDirection.Long)
{
var maxPrice = candlesBeforeNextPosition.Max(c => c.High);
var minPrice = candlesBeforeNextPosition.TakeWhile(c => c.High <= maxPrice).Min(c => c.Low);
stopLoss = GetPercentageFromEntry(position.Open.Price, minPrice);
takeProfit = GetPercentageFromEntry(position.Open.Price, maxPrice);
}
else if (position.OriginDirection == TradeDirection.Short)
{
var minPrice = candlesBeforeNextPosition.Min(c => c.Low);
var maxPrice = candlesBeforeNextPosition.TakeWhile(c => c.Low >= minPrice).Max(c => c.High);
stopLoss = GetPercentageFromEntry(position.Open.Price, maxPrice);
takeProfit = GetPercentageFromEntry(position.Open.Price, minPrice);
}
return (stopLoss, takeProfit);
}
private static decimal GetPercentageFromEntry(decimal entry, decimal price)
{
return Math.Abs(100 - ((100 * price) / entry));
}
public static ProfitAndLoss GetProfitAndLoss(Position position, decimal quantity, decimal price, decimal leverage)
{
var orders = new List<Tuple<decimal, decimal>>
{
new Tuple<decimal, decimal>(position.Open.Quantity, position.Open.Price),
new Tuple<decimal, decimal>(-quantity, price)
};
var pnl = new ProfitAndLoss(orders, position.OriginDirection);
// Apply leverage on the realized pnl
pnl.Realized = pnl.Realized * leverage;
return pnl;
}
}