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

@@ -129,7 +129,7 @@ namespace Managing.Application.Tests
} }
[Theory] [Theory]
[InlineData(Timeframe.FifteenMinutes, -6, StrategyType.RsiDivergenceConfirm, BotType.ScalpingBot)] [InlineData(Timeframe.FifteenMinutes, -6, StrategyType.Stc, BotType.ScalpingBot)]
//[InlineData(Timeframe.FifteenMinutes, -6, Enums.StrategyType.RsiDivergenceConfirm, Enums.BotType.FlippingBot)] //[InlineData(Timeframe.FifteenMinutes, -6, Enums.StrategyType.RsiDivergenceConfirm, Enums.BotType.FlippingBot)]
public void GetBestPeriodRsiForDivergenceFlippingBot(Timeframe timeframe, int days, StrategyType strategyType, public void GetBestPeriodRsiForDivergenceFlippingBot(Timeframe timeframe, int days, StrategyType strategyType,
BotType botType) BotType botType)

View File

@@ -3,6 +3,7 @@ using Managing.Domain.Bots;
using Managing.Domain.Candles; using Managing.Domain.Candles;
using Managing.Domain.MoneyManagements; using Managing.Domain.MoneyManagements;
using Managing.Domain.Strategies; using Managing.Domain.Strategies;
using Managing.Domain.Strategies.Base;
using Managing.Domain.Trades; using Managing.Domain.Trades;
using static Managing.Common.Enums; using static Managing.Common.Enums;
@@ -23,7 +24,7 @@ namespace Managing.Application.Abstractions
MoneyManagement MoneyManagement { get; set; } MoneyManagement MoneyManagement { get; set; }
BotType BotType { get; set; } BotType BotType { get; set; }
Dictionary<DateTime, decimal> WalletBalances { get; set; } Dictionary<DateTime, decimal> WalletBalances { get; set; }
Dictionary<StrategyType, StrategiesResultBase> StrategiesValues { get; set; }
Task Run(); Task Run();
Task ToggleIsForWatchOnly(); Task ToggleIsForWatchOnly();
@@ -32,5 +33,6 @@ namespace Managing.Application.Abstractions
decimal GetTotalFees(); decimal GetTotalFees();
void LoadStrategies(IEnumerable<IStrategy> strategies); void LoadStrategies(IEnumerable<IStrategy> strategies);
void LoadScenario(string scenarioName); void LoadScenario(string scenarioName);
void UpdateStrategiesValues();
} }
} }

View File

@@ -2,12 +2,15 @@
using Managing.Application.Abstractions.Repositories; using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services; using Managing.Application.Abstractions.Services;
using Managing.Core; using Managing.Core;
using Managing.Core.FixedSizedQueue;
using Managing.Domain.Accounts; using Managing.Domain.Accounts;
using Managing.Domain.Backtests; using Managing.Domain.Backtests;
using Managing.Domain.Candles; using Managing.Domain.Candles;
using Managing.Domain.MoneyManagements; using Managing.Domain.MoneyManagements;
using Managing.Domain.Scenarios; using Managing.Domain.Scenarios;
using Managing.Domain.Shared.Helpers; using Managing.Domain.Shared.Helpers;
using Managing.Domain.Strategies;
using Managing.Domain.Strategies.Base;
using Managing.Domain.Workflows; using Managing.Domain.Workflows;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using static Managing.Common.Enums; using static Managing.Common.Enums;
@@ -20,17 +23,19 @@ namespace Managing.Application.Backtesting
private readonly ILogger<Backtester> _logger; private readonly ILogger<Backtester> _logger;
private readonly IExchangeService _exchangeService; private readonly IExchangeService _exchangeService;
private readonly IBotFactory _botFactory; private readonly IBotFactory _botFactory;
private readonly IScenarioService _scenarioService;
public Backtester( public Backtester(
IExchangeService exchangeService, IExchangeService exchangeService,
IBotFactory botFactory, IBotFactory botFactory,
IBacktestRepository backtestRepository, IBacktestRepository backtestRepository,
ILogger<Backtester> logger) ILogger<Backtester> logger, IScenarioService scenarioService)
{ {
_exchangeService = exchangeService; _exchangeService = exchangeService;
_botFactory = botFactory; _botFactory = botFactory;
_backtestRepository = backtestRepository; _backtestRepository = backtestRepository;
_logger = logger; _logger = logger;
_scenarioService = scenarioService;
} }
public Backtest RunSimpleBotBacktest(Workflow workflow, bool save = false) public Backtest RunSimpleBotBacktest(Workflow workflow, bool save = false)
@@ -124,7 +129,7 @@ namespace Managing.Application.Backtesting
return result; return result;
} }
private static Backtest GetBacktestingResult( private Backtest GetBacktestingResult(
Ticker ticker, Ticker ticker,
Scenario scenario, Scenario scenario,
Timeframe timeframe, Timeframe timeframe,
@@ -143,10 +148,15 @@ namespace Managing.Application.Backtesting
foreach (var candle in candles) foreach (var candle in candles)
{ {
bot.OptimizedCandles.Enqueue(candle); bot.OptimizedCandles.Enqueue(candle);
bot.Candles.Add(candle);
bot.Run(); bot.Run();
} }
bot.Candles = new HashSet<Candle>(candles); bot.Candles = new HashSet<Candle>(candles);
// bot.UpdateStrategiesValues();
var strategies = _scenarioService.GetStrategies();
var strategiesValues = GetStrategiesValues(strategies, candles);
var finalPnl = bot.GetProfitAndLoss(); var finalPnl = bot.GetProfitAndLoss();
var winRate = bot.GetWinRate(); var winRate = bot.GetWinRate();
@@ -165,12 +175,39 @@ namespace Managing.Application.Backtesting
WalletBalances = bot.WalletBalances.ToList(), WalletBalances = bot.WalletBalances.ToList(),
Statistics = stats, Statistics = stats,
OptimizedMoneyManagement = optimizedMoneyManagement, OptimizedMoneyManagement = optimizedMoneyManagement,
MoneyManagement = moneyManagement MoneyManagement = moneyManagement,
StrategiesValues = strategiesValues
}; };
return result; return result;
} }
private Dictionary<StrategyType, StrategiesResultBase> GetStrategiesValues(IEnumerable<Strategy> strategies,
List<Candle> candles)
{
var strategiesValues = new Dictionary<StrategyType, StrategiesResultBase>();
var fixedCandles = new FixedSizeQueue<Candle>(10000);
foreach (var candle in candles)
{
fixedCandles.Enqueue(candle);
}
foreach (var strategy in strategies)
{
try
{
var s = ScenarioHelpers.BuildStrategy(strategy, 10000);
s.Candles = fixedCandles;
strategiesValues[strategy.Type] = s.GetStrategyValues();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
return strategiesValues;
}
public IEnumerable<Backtest> GetBacktests() public IEnumerable<Backtest> GetBacktests()
{ {

View File

@@ -10,6 +10,7 @@ using Managing.Domain.MoneyManagements;
using Managing.Domain.Scenarios; using Managing.Domain.Scenarios;
using Managing.Domain.Shared.Helpers; using Managing.Domain.Shared.Helpers;
using Managing.Domain.Strategies; using Managing.Domain.Strategies;
using Managing.Domain.Strategies.Base;
using Managing.Domain.Trades; using Managing.Domain.Trades;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
@@ -46,6 +47,7 @@ public class TradingBot : Bot, ITradingBot
public decimal Fee { get; set; } public decimal Fee { get; set; }
public Scenario Scenario { get; set; } public Scenario Scenario { get; set; }
public Dictionary<DateTime, decimal> WalletBalances { get; set; } public Dictionary<DateTime, decimal> WalletBalances { get; set; }
public Dictionary<StrategyType, StrategiesResultBase> StrategiesValues { get; set; }
public TradingBot( public TradingBot(
string accountName, string accountName,
@@ -87,6 +89,7 @@ public class TradingBot : Bot, ITradingBot
Candles = new HashSet<Candle>(); Candles = new HashSet<Candle>();
Positions = new List<Position>(); Positions = new List<Position>();
WalletBalances = new Dictionary<DateTime, decimal>(); WalletBalances = new Dictionary<DateTime, decimal>();
StrategiesValues = new Dictionary<StrategyType, StrategiesResultBase>();
if (!isForBacktest) if (!isForBacktest)
{ {
@@ -184,7 +187,10 @@ public class TradingBot : Bot, ITradingBot
await ManagePositions(); await ManagePositions();
if (!IsForBacktest) if (!IsForBacktest)
{
SaveBackup(); SaveBackup();
UpdateStrategiesValues();
}
await UpdateWalletBalances(); await UpdateWalletBalances();
if (OptimizedCandles.Count % 100 == 0) // Log every 10th execution if (OptimizedCandles.Count % 100 == 0) // Log every 10th execution
@@ -197,6 +203,14 @@ public class TradingBot : Bot, ITradingBot
} }
} }
public void UpdateStrategiesValues()
{
foreach (var strategy in Strategies)
{
StrategiesValues[strategy.Type] = ((Strategy)strategy).GetStrategyValues();
}
}
private async Task PreloadCandles() private async Task PreloadCandles()
{ {
if (OptimizedCandles.Any()) if (OptimizedCandles.Any())

View File

@@ -4,6 +4,7 @@ using Managing.Domain.MoneyManagements;
using Managing.Domain.Strategies; using Managing.Domain.Strategies;
using Managing.Domain.Trades; using Managing.Domain.Trades;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Managing.Domain.Strategies.Base;
using static Managing.Common.Enums; using static Managing.Common.Enums;
namespace Managing.Domain.Backtests; namespace Managing.Domain.Backtests;
@@ -30,45 +31,30 @@ public class Backtest
AccountName = accountName; AccountName = accountName;
} }
[Required] [Required] public string Id { get; set; }
public string Id { get; set; } [Required] public decimal FinalPnl { get; set; }
[Required] [Required] public int WinRate { get; set; }
public decimal FinalPnl { get; set; } [Required] public decimal GrowthPercentage { get; set; }
[Required] [Required] public decimal HodlPercentage { get; set; }
public int WinRate { get; set; } [Required] public Ticker Ticker { get; }
[Required] [Required] public string Scenario { get; set; }
public decimal GrowthPercentage { get; set; } [Required] public List<Position> Positions { get; }
[Required] [Required] public List<Signal> Signals { get; }
public decimal HodlPercentage { get; set; } [Required] public Timeframe Timeframe { get; }
[Required] [Required] public BotType BotType { get; }
public Ticker Ticker { get; } [Required] public string AccountName { get; }
[Required] [Required] public List<Candle> Candles { get; }
public string Scenario { get; set; } [Required] public PerformanceMetrics Statistics { get; set; }
[Required] [Required] public decimal Fees { get; set; }
public List<Position> Positions { get; } [Required] public List<KeyValuePair<DateTime, decimal>> WalletBalances { get; set; }
[Required] [Required] public MoneyManagement OptimizedMoneyManagement { get; set; }
public List<Signal> Signals { get; } [Required] public MoneyManagement MoneyManagement { get; set; }
[Required]
public Timeframe Timeframe { get; } public Dictionary<StrategyType, StrategiesResultBase> StrategiesValues { get; set; }
[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 string GetStringReport() 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,10 +7,21 @@ namespace Managing.Domain.Scenarios;
public static class ScenarioHelpers public static class ScenarioHelpers
{ {
public static IEnumerable<IStrategy> GetStrategiesFromScenario(Scenario scenario) public static IEnumerable<IStrategy> GetStrategiesFromScenario(Scenario scenario)
{ {
var strategies = new List<IStrategy>(); var strategies = new List<IStrategy>();
foreach (var strategy in scenario.Strategies) foreach (var strategy in scenario.Strategies)
{
var result = BuildStrategy(strategy);
strategies.Add(result);
}
return strategies;
}
public static IStrategy BuildStrategy(Strategy strategy, int size = 600)
{ {
IStrategy result = strategy.Type switch IStrategy result = strategy.Type switch
{ {
@@ -37,11 +48,8 @@ public static class ScenarioHelpers
_ => throw new NotImplementedException(), _ => throw new NotImplementedException(),
}; };
result.Candles = new FixedSizeQueue<Candle>(600); result.Candles = new FixedSizeQueue<Candle>(size);
strategies.Add(result); return result;
}
return strategies;
} }
public static Strategy BuildStrategy( public static Strategy BuildStrategy(

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.Core;
using Managing.Domain.Candles; using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules; using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators; using Skender.Stock.Indicators;
using static Managing.Common.Enums; 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) private void GetSignals(ChandelierType chandelierType)
{ {
var chandelier = Candles.GetChandelier(Period.Value, Multiplier.Value, chandelierType) var chandelier = Candles.GetChandelier(Period.Value, Multiplier.Value, chandelierType)

View File

@@ -16,6 +16,14 @@ public class EmaCrossStrategy : EmaBaseStrategy
Period = period; Period = period;
} }
public override StrategiesResultBase GetStrategyValues()
{
return new StrategiesResultBase()
{
Ema = Candles.GetEma(Period.Value).ToList()
};
}
public override List<Signal> Run() public override List<Signal> Run()
{ {
if (Candles.Count <= Period) 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) public void AddSignal(CandleEma candleSignal, TradeDirection direction, Confidence confidence)
{ {
var signal = new Signal(MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker), direction, confidence, var signal = new Signal(MiscExtensions.ParseEnum<Ticker>(candleSignal.Ticker), direction, confidence,

View File

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

View File

@@ -1,6 +1,7 @@
using Managing.Core; using Managing.Core;
using Managing.Domain.Candles; using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules; using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators; using Skender.Stock.Indicators;
using static Managing.Common.Enums; 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) private List<CandleMacd> MapMacdToCandle(List<MacdResult> macd, IEnumerable<Candle> candles)
{ {
var macdList = new List<CandleMacd>(); var macdList = new List<CandleMacd>();

View File

@@ -1,5 +1,6 @@
using Managing.Core; using Managing.Core;
using Managing.Domain.Shared.Rules; using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators; using Skender.Stock.Indicators;
using static Managing.Common.Enums; using static Managing.Common.Enums;
using Candle = Managing.Domain.Candles.Candle; 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) private void GetLongSignals(List<CandleRsi> candlesRsi)
{ {
// Set the low and high for first candle // Set the low and high for first candle

View File

@@ -1,5 +1,6 @@
using Managing.Core; using Managing.Core;
using Managing.Domain.Shared.Rules; using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators; using Skender.Stock.Indicators;
using static Managing.Common.Enums; using static Managing.Common.Enums;
using Candle = Managing.Domain.Candles.Candle; 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) private void GetLongSignals(List<CandleRsi> candlesRsi)
{ {
// Set the low and high for first candle // Set the low and high for first candle

View File

@@ -1,6 +1,7 @@
using Managing.Core; using Managing.Core;
using Managing.Domain.Candles; using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules; using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators; using Skender.Stock.Indicators;
using static Managing.Common.Enums; 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) private List<CandleSct> MapStcToCandle(List<StcResult> stc, IEnumerable<Candle> candles)
{ {
var sctList = new List<CandleSct>(); var sctList = new List<CandleSct>();

View File

@@ -1,6 +1,7 @@
using Managing.Core; using Managing.Core;
using Managing.Domain.Candles; using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules; using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators; using Skender.Stock.Indicators;
using static Managing.Common.Enums; 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) private List<CandleStDev> MapStDev(List<StdDevResult> stDev, IEnumerable<Candle> candles)
{ {
var sctList = new List<CandleStDev>(); var sctList = new List<CandleStDev>();

View File

@@ -1,6 +1,7 @@
using Managing.Core; using Managing.Core;
using Managing.Domain.Candles; using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules; using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators; using Skender.Stock.Indicators;
using static Managing.Common.Enums; 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) private List<CandleStochRsi> MapStochRsiToCandle(List<StochRsiResult> ema, IEnumerable<Candle> candles)
{ {
var emaList = new List<CandleStochRsi>(); var emaList = new List<CandleStochRsi>();

View File

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

View File

@@ -1,6 +1,7 @@
using Managing.Core; using Managing.Core;
using Managing.Domain.Candles; using Managing.Domain.Candles;
using Managing.Domain.Shared.Rules; using Managing.Domain.Shared.Rules;
using Managing.Domain.Strategies.Base;
using Skender.Stock.Indicators; using Skender.Stock.Indicators;
using static Managing.Common.Enums; 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) private List<CandleSuperTrend> MapSuperTrendToCandle(List<SuperTrendResult> superTrend, IEnumerable<Candle> candles)
{ {
var superTrends = new List<CandleSuperTrend>(); var superTrends = new List<CandleSuperTrend>();

View File

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

View File

@@ -275,7 +275,7 @@ public class EvmManagerTests
var manager = new EvmManager(Subgraphs); var manager = new EvmManager(Subgraphs);
var account = PrivateKeys.GetAccount(); var account = PrivateKeys.GetAccount();
var allowance = await manager.GetAllowance(account.Key, Ticker.BTC); var allowance = await manager.GetAllowance(account.Key, Ticker.USDC);
Assert.IsType<decimal>(allowance); Assert.IsType<decimal>(allowance);
} }

View File

@@ -1,5 +1,5 @@
import useTheme from '../../../hooks/useTheme' import useTheme from '../../../hooks/useTheme'
const themes = ['black', 'coffee', 'cyberpunk', 'lofi', 'retro'] const themes = ['black', 'coffee', 'cyberpunk', 'lofi', 'retro', 'kaigen']
const ThemeSelector = (): JSX.Element => { const ThemeSelector = (): JSX.Element => {
const { setTheme } = useTheme() const { setTheme } = useTheme()

View File

@@ -139,6 +139,7 @@ const BacktestCards: React.FC<IBacktestCards> = ({ list, setBacktests }) => {
positions={backtest.positions} positions={backtest.positions}
walletBalances={backtest.walletBalances} walletBalances={backtest.walletBalances}
signals={backtest.signals} signals={backtest.signals}
strategiesValues={backtest.strategiesValues}
width={720} width={720}
height={512} height={512}
></TradeChart> ></TradeChart>

View File

@@ -1,11 +1,12 @@
import { TradeChart, CardPositionItem } from '..' import { TradeChart, CardPositionItem } from '..'
import type { IBotRowDetails } from '../../../global/interface' import { IBotRowDetails } from '../../../global/type'
import { CardPosition } from '../../mollecules' import { CardPosition } from '../../mollecules'
const BacktestRowDetails: React.FC<IBotRowDetails> = ({ const BacktestRowDetails: React.FC<IBotRowDetails> = ({
candles, candles,
positions, positions,
walletBalances, walletBalances,
strategiesValues,
}) => { }) => {
return ( return (
<> <>
@@ -30,11 +31,12 @@ const BacktestRowDetails: React.FC<IBotRowDetails> = ({
<div> <div>
<figure> <figure>
<TradeChart <TradeChart
width={1000} width={1400}
height={500} height={1400}
candles={candles} candles={candles}
positions={positions} positions={positions}
walletBalances={walletBalances} walletBalances={walletBalances}
strategiesValues={strategiesValues}
signals={[]} signals={[]}
></TradeChart> ></TradeChart>
</figure> </figure>

View File

@@ -247,6 +247,7 @@ const BacktestTable: React.FC<IBacktestCards> = ({ list, isFetching }) => {
candles={row.original.candles} candles={row.original.candles}
positions={row.original.positions} positions={row.original.positions}
walletBalances={row.original.walletBalances} walletBalances={row.original.walletBalances}
strategiesValues={row.original.strategiesValues}
></BacktestRowDetails> ></BacktestRowDetails>
</> </>
), ),

View File

@@ -1,4 +1,5 @@
import type { import type {
BaselineSeriesOptions,
CandlestickData, CandlestickData,
IChartApi, IChartApi,
ISeriesApi, ISeriesApi,
@@ -18,6 +19,8 @@ import type {
KeyValuePairOfDateTimeAndDecimal, KeyValuePairOfDateTimeAndDecimal,
Position, Position,
Signal, Signal,
StrategiesResultBase,
StrategyType,
} from '../../../../generated/ManagingApi' } from '../../../../generated/ManagingApi'
import { import {
PositionStatus, PositionStatus,
@@ -25,11 +28,27 @@ import {
} from '../../../../generated/ManagingApi' } from '../../../../generated/ManagingApi'
import useTheme from '../../../../hooks/useTheme' import useTheme from '../../../../hooks/useTheme'
// var customTheme = {
// background: '#0B0B0B',
// neutral: '#151515',
// primary: '#54B5F9',
// secondary: '#C492B1',
// third: '#B0DB43',
// fourth: '#F2D398',
// fifth: '#99EDCC',
// red: '#FF5340',
// green: '#08C25F',
// orange: '#EB6F22',
// }
type ITradeChartProps = { type ITradeChartProps = {
candles: Candle[] candles: Candle[]
positions: Position[] positions: Position[]
signals: Signal[] signals: Signal[]
walletBalances?: KeyValuePairOfDateTimeAndDecimal[] | null walletBalances?: KeyValuePairOfDateTimeAndDecimal[] | null
strategiesValues?: { [key in keyof typeof StrategyType]?: StrategiesResultBase; } | null;
stream?: Candle | null stream?: Candle | null
width: number width: number
height: number height: number
@@ -40,6 +59,7 @@ const TradeChart = ({
positions, positions,
signals, signals,
walletBalances, walletBalances,
strategiesValues,
stream, stream,
width, width,
height, height,
@@ -68,6 +88,12 @@ const TradeChart = ({
} }
} }
const baselineOptions: BaselineSeriesOptions = {
bottomLineColor: theme.secondary,
topLineColor: theme.primary,
lineWidth: 1,
} as BaselineSeriesOptions
function buildMarker( function buildMarker(
shape: SeriesMarkerShape, shape: SeriesMarkerShape,
color: string, color: string,
@@ -142,7 +168,7 @@ const TradeChart = ({
useEffect(() => { useEffect(() => {
if (chartRef.current) { if (chartRef.current) {
const lineColor = theme.secondary const lineColor = theme['base-100']
chart.current = createChart(chartRef.current, { chart.current = createChart(chartRef.current, {
crosshair: { crosshair: {
mode: CrosshairMode.Normal, mode: CrosshairMode.Normal,
@@ -159,8 +185,8 @@ const TradeChart = ({
}, },
height: height, height: height,
layout: { layout: {
background: {color: '#121212'}, background: {color: theme['base-300']},
textColor: theme.secondary, textColor: theme.accent,
}, },
localization: { localization: {
dateFormat: 'yyyy-MM-dd', dateFormat: 'yyyy-MM-dd',
@@ -198,11 +224,11 @@ const TradeChart = ({
if (!chart.current) return if (!chart.current) return
series1.current = chart.current.addCandlestickSeries({ series1.current = chart.current.addCandlestickSeries({
borderDownColor: theme.secondary, borderDownColor: theme.accent,
borderUpColor: theme.primary, borderUpColor: theme.primary,
downColor: theme.secondary, downColor: theme.accent,
upColor: theme.primary, upColor: theme.primary,
wickDownColor: theme.secondary, wickDownColor: theme.accent,
wickUpColor: theme.primary, wickUpColor: theme.primary,
}) })
@@ -230,7 +256,6 @@ const TradeChart = ({
}, },
}) })
const markers: SeriesMarker<Time>[] = [] const markers: SeriesMarker<Time>[] = []
if (signals) { if (signals) {
@@ -271,20 +296,268 @@ const TradeChart = ({
} }
} }
// Price panel
if (strategiesValues?.EmaTrend != null || strategiesValues?.EmaCross != null)
{
const emaSeries = chart.current.addLineSeries({
color: theme.secondary,
lineWidth: 1,
priceLineVisible: true,
priceLineWidth: 1,
priceFormat: {
minMove: 0.0001,
precision: 4,
type: 'price',
},
title: 'EMA',
})
const ema = strategiesValues.EmaTrend?.ema ?? strategiesValues.EmaCross?.ema
const emaData = ema?.map((w) => {
return {
time: moment(w.date).unix(),
value: w.ema,
}
})
if (emaData != null)
{
// @ts-ignore
emaSeries.setData(emaData)
}
}
if (strategiesValues?.SuperTrend != null) {
const superTrendSeries = chart.current.addLineSeries({
color: theme.info,
lineWidth: 1,
priceLineVisible: false,
priceLineWidth: 1,
priceLineColor: theme.info,
title: 'SuperTrend',
pane: 0,
})
const superTrend = strategiesValues.SuperTrend.superTrend?.map((w) => {
return {
time: moment(w.date).unix(),
value: w.superTrend,
}
})
// @ts-ignore
superTrendSeries.setData(superTrend)
}
if (markers.length > 0) { if (markers.length > 0) {
series1.current.setMarkers(markers) series1.current.setMarkers(markers)
} }
// Indicator panel
var paneCount = 1
if (strategiesValues?.RsiDivergence != null || strategiesValues?.RsiDivergenceConfirm != null)
{
const rsiSeries = chart.current.addLineSeries({
pane: paneCount,
title: 'RSI',
})
const rsi = strategiesValues.RsiDivergence?.rsi ?? strategiesValues.RsiDivergenceConfirm?.rsi
const rsiData = rsi?.map((w) => {
return {
time: moment(w.date).unix(),
value: w.rsi,
}
})
// @ts-ignore
rsiSeries.setData(rsiData)
rsiSeries.applyOptions({
...baselineOptions,
priceFormat: {
minMove: 0.01,
precision: 4,
type: 'price',
},
})
paneCount++
}
if (strategiesValues?.Stc != null) {
const stcSeries = chart.current.addBaselineSeries({
pane: paneCount,
baseValue: {price: 50, type: 'price'},
title: 'STC',
})
stcSeries.createPriceLine(buildLine(theme.error, 25, 'low'))
stcSeries.createPriceLine(buildLine(theme.info, 75, 'high'))
const stcData = strategiesValues?.Stc.stc?.map((w) => {
return {
time: moment(w.date).unix(),
value: w.stc,
}
})
// @ts-ignore
stcSeries.setData(stcData)
stcSeries.applyOptions({
...baselineOptions,
priceFormat: {
minMove: 1,
precision: 1,
type: 'price',
},
})
paneCount++
}
if (strategiesValues?.MacdCross != null) {
const histogramSeries = chart.current.addHistogramSeries({
color: theme.accent,
title: 'MACD',
pane: paneCount,
priceFormat: {
precision: 6,
type: 'volume',
}
})
const macd = strategiesValues.MacdCross.macd?.map((w) => {
return {
time: moment(w.date).unix(),
value: w.histogram,
}
})
var priceOptions = {
scaleMargins:{
top: 0.7,
bottom: 0.02,
}
}
histogramSeries.priceScale().applyOptions(priceOptions)
// @ts-ignore
histogramSeries.setData(macd)
const macdSeries = chart.current.addLineSeries({
color: theme.primary,
lineWidth: 1,
priceLineVisible: false,
priceLineWidth: 1,
title: 'EMA',
pane: paneCount,
priceFormat: {
precision: 6,
type: 'price',
},
})
const macdData = strategiesValues.MacdCross.macd?.map((w) => {
return {
time: moment(w.date).unix(),
value: w.macd,
}
})
macdSeries.priceScale().applyOptions(priceOptions)
// @ts-ignore
macdSeries.setData(macdData)
const signalSeries = chart.current.addLineSeries({
color: theme.info,
lineWidth: 1,
priceLineVisible: false,
priceLineWidth: 1,
lineStyle: LineStyle.Dotted,
title: 'Signal',
pane: paneCount,
priceFormat: {
precision: 6,
type: 'price',
},
})
const signalData = strategiesValues.MacdCross.macd?.map((w) => {
return {
time: moment(w.date).unix(),
value: w.signal,
}
})
signalSeries.priceScale().applyOptions(priceOptions)
// @ts-ignore
signalSeries.setData(signalData)
paneCount++
}
if (strategiesValues?.StochRsiTrend){
const stochRsiSeries = chart.current.addLineSeries({
...baselineOptions,
priceLineVisible: false,
title: 'Stoch RSI',
pane: paneCount,
})
const stochRsi = strategiesValues.StochRsiTrend.stochRsi?.map((w) => {
return {
time: moment(w.date).unix(),
value: w.stochRsi,
}
})
// @ts-ignore
stochRsiSeries.setData(stochRsi)
paneCount++
}
if (strategiesValues?.StDev != null) {
const stDevSeries = chart.current.addLineSeries({
color: theme.primary,
lineWidth: 1,
priceLineVisible: false,
priceLineWidth: 1,
title: 'StDev',
pane: paneCount,
})
const stDev = strategiesValues.StDev.stdDev?.map((w) => {
return {
time: moment(w.date).unix(),
value: w.stdDev,
}
})
// @ts-ignore
stDevSeries.setData(stDev)
paneCount++
const zScoreSeries = chart.current.addBaselineSeries({
...baselineOptions,
baseValue: {price: 0, type: 'price'},
title: 'ZScore',
pane: paneCount,
})
const zScore = strategiesValues.StDev.stdDev?.map((w) => {
return {
time: moment(w.date).unix(),
value: w.zScore,
}
})
// @ts-ignore
zScoreSeries.setData(zScore)
paneCount++
}
if (walletBalances != null) { if (walletBalances != null) {
const walletSeries = chart.current.addBaselineSeries({ const walletSeries = chart.current.addBaselineSeries({
baseValue: {price: walletBalances[0].value, type: 'price'}, baseValue: {price: walletBalances[0].value, type: 'price'},
bottomFillColor1: 'rgba( 239, 83, 80, 0.05)', pane: paneCount,
bottomFillColor2: 'rgba( 239, 83, 80, 0.28)', title: '$',
bottomLineColor: 'rgba( 239, 83, 80, 1)',
pane: 1,
topFillColor1: 'rgba( 38, 166, 154, 0.28)',
topFillColor2: 'rgba( 38, 166, 154, 0.05)',
topLineColor: 'rgba( 38, 166, 154, 1)',
}) })
const walletData = walletBalances.map((w) => { const walletData = walletBalances.map((w) => {
@@ -296,12 +569,15 @@ const TradeChart = ({
// @ts-ignore // @ts-ignore
walletSeries.setData(walletData) walletSeries.setData(walletData)
walletSeries.applyOptions({ walletSeries.applyOptions({
...baselineOptions,
priceFormat: { priceFormat: {
minMove: 0.0001, minMove: 0.0001,
precision: 4, precision: 4,
type: 'price', type: 'price',
}, },
}) })
paneCount++
} }
} }

View File

@@ -1158,6 +1158,54 @@ export class ScenarioClient extends AuthorizedApiBase {
return Promise.resolve<FileResponse>(null as any); return Promise.resolve<FileResponse>(null as any);
} }
scenario_UpdateScenario(name: string | null | undefined, loopbackPeriod: number | null | undefined, strategies: string[]): Promise<FileResponse> {
let url_ = this.baseUrl + "/Scenario?";
if (name !== undefined && name !== null)
url_ += "name=" + encodeURIComponent("" + name) + "&";
if (loopbackPeriod !== undefined && loopbackPeriod !== null)
url_ += "loopbackPeriod=" + encodeURIComponent("" + loopbackPeriod) + "&";
url_ = url_.replace(/[?&]$/, "");
const content_ = JSON.stringify(strategies);
let options_: RequestInit = {
body: content_,
method: "PUT",
headers: {
"Content-Type": "application/json",
"Accept": "application/octet-stream"
}
};
return this.transformOptions(options_).then(transformedOptions_ => {
return this.http.fetch(url_, transformedOptions_);
}).then((_response: Response) => {
return this.processScenario_UpdateScenario(_response);
});
}
protected processScenario_UpdateScenario(response: Response): Promise<FileResponse> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200 || status === 206) {
const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
let fileNameMatch = contentDisposition ? /filename\*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/g.exec(contentDisposition) : undefined;
let fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[3] || fileNameMatch[2] : undefined;
if (fileName) {
fileName = decodeURIComponent(fileName);
} else {
fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
}
return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<FileResponse>(null as any);
}
scenario_GetStrategies(): Promise<Strategy[]> { scenario_GetStrategies(): Promise<Strategy[]> {
let url_ = this.baseUrl + "/Scenario/strategy"; let url_ = this.baseUrl + "/Scenario/strategy";
url_ = url_.replace(/[?&]$/, ""); url_ = url_.replace(/[?&]$/, "");
@@ -1291,6 +1339,68 @@ export class ScenarioClient extends AuthorizedApiBase {
} }
return Promise.resolve<FileResponse>(null as any); return Promise.resolve<FileResponse>(null as any);
} }
scenario_UpdateStrategy(strategyType: StrategyType | undefined, name: string | null | undefined, period: number | null | undefined, fastPeriods: number | null | undefined, slowPeriods: number | null | undefined, signalPeriods: number | null | undefined, multiplier: number | null | undefined, stochPeriods: number | null | undefined, smoothPeriods: number | null | undefined, cyclePeriods: number | null | undefined): Promise<FileResponse> {
let url_ = this.baseUrl + "/Scenario/strategy?";
if (strategyType === null)
throw new Error("The parameter 'strategyType' cannot be null.");
else if (strategyType !== undefined)
url_ += "strategyType=" + encodeURIComponent("" + strategyType) + "&";
if (name !== undefined && name !== null)
url_ += "name=" + encodeURIComponent("" + name) + "&";
if (period !== undefined && period !== null)
url_ += "period=" + encodeURIComponent("" + period) + "&";
if (fastPeriods !== undefined && fastPeriods !== null)
url_ += "fastPeriods=" + encodeURIComponent("" + fastPeriods) + "&";
if (slowPeriods !== undefined && slowPeriods !== null)
url_ += "slowPeriods=" + encodeURIComponent("" + slowPeriods) + "&";
if (signalPeriods !== undefined && signalPeriods !== null)
url_ += "signalPeriods=" + encodeURIComponent("" + signalPeriods) + "&";
if (multiplier !== undefined && multiplier !== null)
url_ += "multiplier=" + encodeURIComponent("" + multiplier) + "&";
if (stochPeriods !== undefined && stochPeriods !== null)
url_ += "stochPeriods=" + encodeURIComponent("" + stochPeriods) + "&";
if (smoothPeriods !== undefined && smoothPeriods !== null)
url_ += "smoothPeriods=" + encodeURIComponent("" + smoothPeriods) + "&";
if (cyclePeriods !== undefined && cyclePeriods !== null)
url_ += "cyclePeriods=" + encodeURIComponent("" + cyclePeriods) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "PUT",
headers: {
"Accept": "application/octet-stream"
}
};
return this.transformOptions(options_).then(transformedOptions_ => {
return this.http.fetch(url_, transformedOptions_);
}).then((_response: Response) => {
return this.processScenario_UpdateStrategy(_response);
});
}
protected processScenario_UpdateStrategy(response: Response): Promise<FileResponse> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200 || status === 206) {
const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
let fileNameMatch = contentDisposition ? /filename\*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/g.exec(contentDisposition) : undefined;
let fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[3] || fileNameMatch[2] : undefined;
if (fileName) {
fileName = decodeURIComponent(fileName);
} else {
fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
}
return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<FileResponse>(null as any);
}
} }
export class SettingsClient extends AuthorizedApiBase { export class SettingsClient extends AuthorizedApiBase {
@@ -1893,6 +2003,7 @@ export interface Backtest {
walletBalances: KeyValuePairOfDateTimeAndDecimal[]; walletBalances: KeyValuePairOfDateTimeAndDecimal[];
optimizedMoneyManagement: MoneyManagement; optimizedMoneyManagement: MoneyManagement;
moneyManagement: MoneyManagement; moneyManagement: MoneyManagement;
strategiesValues?: { [key in keyof typeof StrategyType]?: StrategiesResultBase; } | null;
} }
export enum Ticker { export enum Ticker {
@@ -2152,6 +2263,94 @@ export interface KeyValuePairOfDateTimeAndDecimal {
value?: number; value?: number;
} }
export interface StrategiesResultBase {
ema?: EmaResult[] | null;
macd?: MacdResult[] | null;
rsi?: RsiResult[] | null;
stoch?: StochResult[] | null;
stochRsi?: StochRsiResult[] | null;
bollingerBands?: BollingerBandsResult[] | null;
chandelierShort?: ChandelierResult[] | null;
stc?: StcResult[] | null;
stdDev?: StdDevResult[] | null;
superTrend?: SuperTrendResult[] | null;
chandelierLong?: ChandelierResult[] | null;
}
export interface ResultBase {
date?: Date;
}
export interface EmaResult extends ResultBase {
ema?: number | null;
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
}
export interface MacdResult extends ResultBase {
macd?: number | null;
signal?: number | null;
histogram?: number | null;
fastEma?: number | null;
slowEma?: number | null;
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
}
export interface RsiResult extends ResultBase {
rsi?: number | null;
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
}
/** Stochastic indicator results includes aliases for those who prefer the simpler K,D,J outputs. See documentation for more information. */
export interface StochResult extends ResultBase {
oscillator?: number | null;
signal?: number | null;
percentJ?: number | null;
k?: number | null;
d?: number | null;
j?: number | null;
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
}
export interface StochRsiResult extends ResultBase {
stochRsi?: number | null;
signal?: number | null;
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
}
export interface BollingerBandsResult extends ResultBase {
sma?: number | null;
upperBand?: number | null;
lowerBand?: number | null;
percentB?: number | null;
zScore?: number | null;
width?: number | null;
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
}
export interface ChandelierResult extends ResultBase {
chandelierExit?: number | null;
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
}
export interface StcResult extends ResultBase {
stc?: number | null;
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
}
export interface StdDevResult extends ResultBase {
stdDev?: number | null;
mean?: number | null;
zScore?: number | null;
stdDevSma?: number | null;
"skender.Stock.Indicators.IReusableResult.Value"?: number | null;
}
export interface SuperTrendResult extends ResultBase {
superTrend?: number | null;
upperBand?: number | null;
lowerBand?: number | null;
}
export interface StartBotRequest { export interface StartBotRequest {
botType: BotType; botType: BotType;
botName: string; botName: string;

View File

@@ -17,6 +17,8 @@ import type {
RiskLevel, RiskLevel,
Scenario, Scenario,
Signal, Signal,
StrategiesResultBase,
StrategyType,
Ticker, Ticker,
Timeframe, Timeframe,
TradeDirection, TradeDirection,
@@ -144,6 +146,7 @@ export type IBotRowDetails = {
candles: Candle[] candles: Candle[]
positions: Position[] positions: Position[]
walletBalances?: KeyValuePairOfDateTimeAndDecimal[] | null walletBalances?: KeyValuePairOfDateTimeAndDecimal[] | null
strategiesValues?: { [key in keyof typeof StrategyType]?: StrategiesResultBase; } | null;
} }
export type IBacktestFormInput = { export type IBacktestFormInput = {

View File

@@ -36,17 +36,39 @@ const themes: ThemesInterface = {
'--rounded-btn': '0', '--rounded-btn': '0',
'--tab-radius': '0', '--tab-radius': '0',
accent: '#343232', accent: '#343232',
'base-100': '#000000', 'base-100': '#0B0B0B',
'base-200': '#0D0D0D', 'base-200': '#0D0D0D',
'base-300': '#1A1919', 'base-300': '#0B0B0B',
error: '#ff0000', error: '#FF5340',
info: '#0000ff', info: '#B0DB43',
neutral: '#272626', neutral: '#151515',
'neutral-focus': '#343232', 'neutral-focus': '#343232',
primary: '#343232', primary: '#54B5F9',
secondary: '#343232', secondary: '#a3d9fe',
success: '#008000', success: '#08C25F',
warning: '#ffff00', warning: '#EB6F22',
},
'[data-theme=kaigen]': {
'--animation-btn': '0',
'--animation-input': '0',
'--btn-focus-scale': '1',
'--btn-text-case': 'lowercase',
'--rounded-badge': '0',
'--rounded-box': '0',
'--rounded-btn': '0',
'--tab-radius': '0',
accent: '#343232',
'base-100': '#0B0B0B',
'base-200': '#0D0D0D',
'base-300': '#0B0B0B',
error: '#FF5340',
info: '#B0DB43',
neutral: '#151515',
'neutral-focus': '#343232',
primary: '#54B5F9',
secondary: '#a3d9fe',
success: '#08C25F',
warning: '#EB6F22',
}, },
'[data-theme=coffee]': { '[data-theme=coffee]': {
@@ -61,18 +83,18 @@ const themes: ThemesInterface = {
success: '#9DB787', success: '#9DB787',
warning: '#FFD25F', warning: '#FFD25F',
}, },
'[data-theme=cyberpunk]': { // '[data-theme=cyberpunk]': {
'--rounded-badge': '0', // '--rounded-badge': '0',
'--rounded-box': '0', // '--rounded-box': '0',
'--rounded-btn': '0', // '--rounded-btn': '0',
'--tab-radius': '0', // '--tab-radius': '0',
accent: '#c07eec', // accent: '#c07eec',
'base-100': '#ffee00', // 'base-100': '#ffee00',
neutral: '#423f00', // neutral: '#423f00',
'neutral-content': '#ffee00', // 'neutral-content': '#ffee00',
primary: '#ff7598', // primary: '#ff7598',
secondary: '#75d1f0', // secondary: '#75d1f0',
}, // },
'[data-theme=lofi]': { '[data-theme=lofi]': {
'--animation-btn': '0', '--animation-btn': '0',
'--animation-input': '0', '--animation-input': '0',

View File

@@ -1,7 +1,7 @@
module.exports = { module.exports = {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
daisyui: { daisyui: {
themes: true, themes: ['black', 'coffee', 'cyberpunk', 'lofi', 'retro', 'kaigen'],
}, },
plugins: [require('@tailwindcss/typography'), require('daisyui')], plugins: [require('@tailwindcss/typography'), require('daisyui')],
theme: { theme: {