Add indicators to backtest and bot (#14)

* Add indicators to backtest and bot

* Remove
This commit is contained in:
Oda
2025-02-28 00:53:25 +07:00
committed by GitHub
parent e0a8347953
commit c715da8a17
30 changed files with 787 additions and 109 deletions

View File

@@ -4,6 +4,7 @@ using Managing.Domain.MoneyManagements;
using Managing.Domain.Strategies;
using Managing.Domain.Trades;
using System.ComponentModel.DataAnnotations;
using Managing.Domain.Strategies.Base;
using static Managing.Common.Enums;
namespace Managing.Domain.Backtests;
@@ -30,45 +31,30 @@ public class Backtest
AccountName = accountName;
}
[Required]
public string Id { get; set; }
[Required]
public decimal FinalPnl { get; set; }
[Required]
public int WinRate { get; set; }
[Required]
public decimal GrowthPercentage { get; set; }
[Required]
public decimal HodlPercentage { get; set; }
[Required]
public Ticker Ticker { get; }
[Required]
public string Scenario { get; set; }
[Required]
public List<Position> Positions { get; }
[Required]
public List<Signal> Signals { get; }
[Required]
public Timeframe Timeframe { get; }
[Required]
public BotType BotType { get; }
[Required]
public string AccountName { get; }
[Required]
public List<Candle> Candles { get; }
[Required]
public PerformanceMetrics Statistics { get; set; }
[Required]
public decimal Fees { get; set; }
[Required]
public List<KeyValuePair<DateTime, decimal>> WalletBalances { get; set; }
[Required]
public MoneyManagement OptimizedMoneyManagement { get; set; }
[Required]
public MoneyManagement MoneyManagement { get; set; }
[Required] public string Id { get; set; }
[Required] public decimal FinalPnl { get; set; }
[Required] public int WinRate { get; set; }
[Required] public decimal GrowthPercentage { get; set; }
[Required] public decimal HodlPercentage { get; set; }
[Required] public Ticker Ticker { get; }
[Required] public string Scenario { get; set; }
[Required] public List<Position> Positions { get; }
[Required] public List<Signal> Signals { get; }
[Required] public Timeframe Timeframe { get; }
[Required] public BotType BotType { get; }
[Required] public string AccountName { get; }
[Required] public List<Candle> Candles { get; }
[Required] public PerformanceMetrics Statistics { get; set; }
[Required] public decimal Fees { get; set; }
[Required] public List<KeyValuePair<DateTime, decimal>> WalletBalances { get; set; }
[Required] public MoneyManagement OptimizedMoneyManagement { get; set; }
[Required] public MoneyManagement MoneyManagement { get; set; }
public Dictionary<StrategyType, StrategiesResultBase> StrategiesValues { get; set; }
public string GetStringReport()
{
return $"{Ticker} | {Timeframe} | Positions: {Positions.Count} | Winrate: {WinRate}% | Pnl: {FinalPnl:#.##}$ | %Pnl: {GrowthPercentage:#.##}% | %Hodl: {HodlPercentage:#.##}%";
return
$"{Ticker} | {Timeframe} | Positions: {Positions.Count} | Winrate: {WinRate}% | Pnl: {FinalPnl:#.##}$ | %Pnl: {GrowthPercentage:#.##}% | %Hodl: {HodlPercentage:#.##}%";
}
}

View File

@@ -7,43 +7,51 @@ namespace Managing.Domain.Scenarios;
public static class ScenarioHelpers
{
public static IEnumerable<IStrategy> GetStrategiesFromScenario(Scenario scenario)
{
var strategies = new List<IStrategy>();
foreach (var strategy in scenario.Strategies)
{
IStrategy result = strategy.Type switch
{
StrategyType.StDev => new StDevContext(strategy.Name, strategy.Period.Value),
StrategyType.RsiDivergence => new RSIDivergenceStrategy(strategy.Name,
strategy.Period.Value),
StrategyType.RsiDivergenceConfirm => new RSIDivergenceConfirmStrategy(strategy.Name,
strategy.Period.Value),
StrategyType.MacdCross => new MacdCrossStrategy(strategy.Name,
strategy.FastPeriods.Value, strategy.SlowPeriods.Value, strategy.SignalPeriods.Value),
StrategyType.EmaCross => new EmaCrossStrategy(strategy.Name, strategy.Period.Value),
StrategyType.ThreeWhiteSoldiers => new ThreeWhiteSoldiersStrategy(strategy.Name,
strategy.Period.Value),
StrategyType.SuperTrend => new SuperTrendStrategy(strategy.Name,
strategy.Period.Value, strategy.Multiplier.Value),
StrategyType.ChandelierExit => new ChandelierExitStrategy(strategy.Name,
strategy.Period.Value, strategy.Multiplier.Value),
StrategyType.EmaTrend => new EmaTrendStrategy(strategy.Name, strategy.Period.Value),
StrategyType.StochRsiTrend => new StochRsiTrendStrategy(strategy.Name,
strategy.Period.Value, strategy.StochPeriods.Value, strategy.SignalPeriods.Value,
strategy.SmoothPeriods.Value),
StrategyType.Stc => new STCStrategy(strategy.Name, strategy.CyclePeriods.Value,
strategy.FastPeriods.Value, strategy.SlowPeriods.Value),
_ => throw new NotImplementedException(),
};
result.Candles = new FixedSizeQueue<Candle>(600);
var result = BuildStrategy(strategy);
strategies.Add(result);
}
return strategies;
}
public static IStrategy BuildStrategy(Strategy strategy, int size = 600)
{
IStrategy result = strategy.Type switch
{
StrategyType.StDev => new StDevContext(strategy.Name, strategy.Period.Value),
StrategyType.RsiDivergence => new RSIDivergenceStrategy(strategy.Name,
strategy.Period.Value),
StrategyType.RsiDivergenceConfirm => new RSIDivergenceConfirmStrategy(strategy.Name,
strategy.Period.Value),
StrategyType.MacdCross => new MacdCrossStrategy(strategy.Name,
strategy.FastPeriods.Value, strategy.SlowPeriods.Value, strategy.SignalPeriods.Value),
StrategyType.EmaCross => new EmaCrossStrategy(strategy.Name, strategy.Period.Value),
StrategyType.ThreeWhiteSoldiers => new ThreeWhiteSoldiersStrategy(strategy.Name,
strategy.Period.Value),
StrategyType.SuperTrend => new SuperTrendStrategy(strategy.Name,
strategy.Period.Value, strategy.Multiplier.Value),
StrategyType.ChandelierExit => new ChandelierExitStrategy(strategy.Name,
strategy.Period.Value, strategy.Multiplier.Value),
StrategyType.EmaTrend => new EmaTrendStrategy(strategy.Name, strategy.Period.Value),
StrategyType.StochRsiTrend => new StochRsiTrendStrategy(strategy.Name,
strategy.Period.Value, strategy.StochPeriods.Value, strategy.SignalPeriods.Value,
strategy.SmoothPeriods.Value),
StrategyType.Stc => new STCStrategy(strategy.Name, strategy.CyclePeriods.Value,
strategy.FastPeriods.Value, strategy.SlowPeriods.Value),
_ => throw new NotImplementedException(),
};
result.Candles = new FixedSizeQueue<Candle>(size);
return result;
}
public static Strategy BuildStrategy(
StrategyType type,
string name,

View File

@@ -0,0 +1,18 @@
using Skender.Stock.Indicators;
namespace Managing.Domain.Strategies.Base;
public class StrategiesResultBase
{
public List<EmaResult> Ema { get; set; }
public List<MacdResult> Macd { get; set; }
public List<RsiResult> Rsi { get; set; }
public List<StochResult> Stoch { get; set; }
public List<StochRsiResult> StochRsi { get; set; }
public List<BollingerBandsResult> BollingerBands { get; set; }
public List<ChandelierResult> ChandelierShort { get; set; }
public List<StcResult> Stc { get; set; }
public List<StdDevResult> StdDev { get; set; }
public List<SuperTrendResult> SuperTrend { get; set; }
public List<ChandelierResult> ChandelierLong { get; set; }
}

View File

@@ -1,6 +1,7 @@
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
@@ -38,6 +39,15 @@ public class ChandelierExitStrategy : Strategy
}
}
public override StrategiesResultBase GetStrategyValues()
{
return new StrategiesResultBase()
{
ChandelierLong = Candles.GetChandelier(Period.Value, Multiplier.Value, ChandelierType.Long).ToList(),
ChandelierShort = Candles.GetChandelier(Period.Value, Multiplier.Value, ChandelierType.Short).ToList()
};
}
private void GetSignals(ChandelierType chandelierType)
{
var chandelier = Candles.GetChandelier(Period.Value, Multiplier.Value, chandelierType)

View File

@@ -16,6 +16,14 @@ public class EmaCrossStrategy : EmaBaseStrategy
Period = period;
}
public override StrategiesResultBase GetStrategyValues()
{
return new StrategiesResultBase()
{
Ema = Candles.GetEma(Period.Value).ToList()
};
}
public override List<Signal> Run()
{
if (Candles.Count <= Period)

View File

@@ -54,6 +54,14 @@ public class EmaTrendStrategy : EmaBaseStrategy
}
}
public override StrategiesResultBase GetStrategyValues()
{
return new StrategiesResultBase()
{
Ema = Candles.GetEma(Period.Value).ToList()
};
}
public void AddSignal(CandleEma candleSignal, TradeDirection direction, Confidence confidence)
{
var signal = new Signal(MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker), direction, confidence,

View File

@@ -1,5 +1,6 @@
using Managing.Core.FixedSizedQueue;
using Managing.Domain.Candles;
using Managing.Domain.Strategies.Base;
using static Managing.Common.Enums;
namespace Managing.Domain.Strategies
@@ -16,6 +17,7 @@ namespace Managing.Domain.Strategies
FixedSizeQueue<Candle> Candles { get; set; }
List<Signal> Run();
StrategiesResultBase GetStrategyValues();
void UpdateCandles(HashSet<Candle> newCandles);
string GetName();
}

View File

@@ -1,6 +1,7 @@
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
@@ -58,6 +59,15 @@ public class MacdCrossStrategy : Strategy
}
}
public override StrategiesResultBase GetStrategyValues()
{
return new StrategiesResultBase()
{
Macd = Candles.GetMacd(FastPeriods.Value, SlowPeriods.Value, SignalPeriods.Value).ToList()
};
}
private List<CandleMacd> MapMacdToCandle(List<MacdResult> macd, IEnumerable<Candle> candles)
{
var macdList = new List<CandleMacd>();

View File

@@ -1,5 +1,6 @@
using Managing.Core;
using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
using Candle = Managing.Domain.Candles.Candle;
@@ -48,6 +49,14 @@ public class RSIDivergenceConfirmStrategy : Strategy
}
}
public override StrategiesResultBase GetStrategyValues()
{
return new StrategiesResultBase()
{
Rsi = Candles.GetRsi(Period.Value).ToList()
};
}
private void GetLongSignals(List<CandleRsi> candlesRsi)
{
// Set the low and high for first candle

View File

@@ -1,5 +1,6 @@
using Managing.Core;
using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
using Candle = Managing.Domain.Candles.Candle;
@@ -51,6 +52,14 @@ public class RSIDivergenceStrategy : Strategy
}
}
public override StrategiesResultBase GetStrategyValues()
{
return new StrategiesResultBase()
{
Rsi = Candles.GetRsi(Period.Value).ToList()
};
}
private void GetLongSignals(List<CandleRsi> candlesRsi)
{
// Set the low and high for first candle

View File

@@ -1,6 +1,7 @@
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
@@ -57,6 +58,15 @@ public class STCStrategy : Strategy
}
}
public override StrategiesResultBase GetStrategyValues()
{
var stc = Candles.GetStc(FastPeriods.Value, FastPeriods.Value, SlowPeriods.Value).ToList();
return new StrategiesResultBase
{
Stc = stc
};
}
private List<CandleSct> MapStcToCandle(List<StcResult> stc, IEnumerable<Candle> candles)
{
var sctList = new List<CandleSct>();

View File

@@ -1,6 +1,7 @@
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
@@ -50,6 +51,16 @@ public class StDevContext : Strategy
}
}
public override StrategiesResultBase GetStrategyValues()
{
var test = new StrategiesResultBase()
{
StdDev = Candles.GetStdDev(Period.Value).ToList()
};
return test;
}
private List<CandleStDev> MapStDev(List<StdDevResult> stDev, IEnumerable<Candle> candles)
{
var sctList = new List<CandleStDev>();

View File

@@ -1,6 +1,7 @@
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
@@ -64,6 +65,15 @@ public class StochRsiTrendStrategy : Strategy
}
}
public override StrategiesResultBase GetStrategyValues()
{
return new StrategiesResultBase()
{
StochRsi = Candles.GetStochRsi(Period.Value, StochPeriods.Value, SignalPeriods.Value, SmoothPeriods.Value)
.ToList()
};
}
private List<CandleStochRsi> MapStochRsiToCandle(List<StochRsiResult> ema, IEnumerable<Candle> candles)
{
var emaList = new List<CandleStochRsi>();

View File

@@ -2,6 +2,7 @@
using Managing.Core.FixedSizedQueue;
using Managing.Domain.Candles;
using Managing.Domain.Scenarios;
using Managing.Domain.Strategies.Base;
using static Managing.Common.Enums;
namespace Managing.Domain.Strategies
@@ -34,6 +35,11 @@ namespace Managing.Domain.Strategies
return new List<Signal>();
}
public virtual StrategiesResultBase GetStrategyValues()
{
return new StrategiesResultBase();
}
public void UpdateCandles(HashSet<Candle> newCandles)
{
if (newCandles == null || newCandles.Count == 0)

View File

@@ -1,6 +1,7 @@
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators;
using static Managing.Common.Enums;
@@ -60,6 +61,15 @@ public class SuperTrendStrategy : Strategy
}
}
public override StrategiesResultBase GetStrategyValues()
{
return new StrategiesResultBase()
{
SuperTrend = Candles.GetSuperTrend(Period.Value, Multiplier.Value).Where(s => s.SuperTrend.HasValue)
.ToList()
};
}
private List<CandleSuperTrend> MapSuperTrendToCandle(List<SuperTrendResult> superTrend, IEnumerable<Candle> candles)
{
var superTrends = new List<CandleSuperTrend>();

View File

@@ -1,5 +1,6 @@
using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Managing.Domain.Strategies.Rules;
using static Managing.Common.Enums;
@@ -50,5 +51,10 @@ namespace Managing.Domain.Strategies
return null;
}
}
public override StrategiesResultBase GetStrategyValues()
{
throw new NotImplementedException();
}
}
}