Add indicators to backtest and bot (#14)
* Add indicators to backtest and bot * Remove
This commit is contained in:
@@ -129,7 +129,7 @@ namespace Managing.Application.Tests
|
||||
}
|
||||
|
||||
[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)]
|
||||
public void GetBestPeriodRsiForDivergenceFlippingBot(Timeframe timeframe, int days, StrategyType strategyType,
|
||||
BotType botType)
|
||||
|
||||
@@ -3,6 +3,7 @@ using Managing.Domain.Bots;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Strategies;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Managing.Domain.Trades;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
@@ -23,7 +24,7 @@ namespace Managing.Application.Abstractions
|
||||
MoneyManagement MoneyManagement { get; set; }
|
||||
BotType BotType { get; set; }
|
||||
Dictionary<DateTime, decimal> WalletBalances { get; set; }
|
||||
|
||||
Dictionary<StrategyType, StrategiesResultBase> StrategiesValues { get; set; }
|
||||
|
||||
Task Run();
|
||||
Task ToggleIsForWatchOnly();
|
||||
@@ -32,5 +33,6 @@ namespace Managing.Application.Abstractions
|
||||
decimal GetTotalFees();
|
||||
void LoadStrategies(IEnumerable<IStrategy> strategies);
|
||||
void LoadScenario(string scenarioName);
|
||||
void UpdateStrategiesValues();
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,15 @@
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Core;
|
||||
using Managing.Core.FixedSizedQueue;
|
||||
using Managing.Domain.Accounts;
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Shared.Helpers;
|
||||
using Managing.Domain.Strategies;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Managing.Domain.Workflows;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using static Managing.Common.Enums;
|
||||
@@ -20,17 +23,19 @@ namespace Managing.Application.Backtesting
|
||||
private readonly ILogger<Backtester> _logger;
|
||||
private readonly IExchangeService _exchangeService;
|
||||
private readonly IBotFactory _botFactory;
|
||||
private readonly IScenarioService _scenarioService;
|
||||
|
||||
public Backtester(
|
||||
IExchangeService exchangeService,
|
||||
IBotFactory botFactory,
|
||||
IBacktestRepository backtestRepository,
|
||||
ILogger<Backtester> logger)
|
||||
ILogger<Backtester> logger, IScenarioService scenarioService)
|
||||
{
|
||||
_exchangeService = exchangeService;
|
||||
_botFactory = botFactory;
|
||||
_backtestRepository = backtestRepository;
|
||||
_logger = logger;
|
||||
_scenarioService = scenarioService;
|
||||
}
|
||||
|
||||
public Backtest RunSimpleBotBacktest(Workflow workflow, bool save = false)
|
||||
@@ -124,7 +129,7 @@ namespace Managing.Application.Backtesting
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Backtest GetBacktestingResult(
|
||||
private Backtest GetBacktestingResult(
|
||||
Ticker ticker,
|
||||
Scenario scenario,
|
||||
Timeframe timeframe,
|
||||
@@ -143,10 +148,15 @@ namespace Managing.Application.Backtesting
|
||||
foreach (var candle in candles)
|
||||
{
|
||||
bot.OptimizedCandles.Enqueue(candle);
|
||||
bot.Candles.Add(candle);
|
||||
bot.Run();
|
||||
}
|
||||
|
||||
bot.Candles = new HashSet<Candle>(candles);
|
||||
// bot.UpdateStrategiesValues();
|
||||
|
||||
var strategies = _scenarioService.GetStrategies();
|
||||
var strategiesValues = GetStrategiesValues(strategies, candles);
|
||||
|
||||
var finalPnl = bot.GetProfitAndLoss();
|
||||
var winRate = bot.GetWinRate();
|
||||
@@ -165,12 +175,39 @@ namespace Managing.Application.Backtesting
|
||||
WalletBalances = bot.WalletBalances.ToList(),
|
||||
Statistics = stats,
|
||||
OptimizedMoneyManagement = optimizedMoneyManagement,
|
||||
MoneyManagement = moneyManagement
|
||||
MoneyManagement = moneyManagement,
|
||||
StrategiesValues = strategiesValues
|
||||
};
|
||||
|
||||
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()
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Shared.Helpers;
|
||||
using Managing.Domain.Strategies;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Managing.Domain.Trades;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
@@ -46,6 +47,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
public decimal Fee { get; set; }
|
||||
public Scenario Scenario { get; set; }
|
||||
public Dictionary<DateTime, decimal> WalletBalances { get; set; }
|
||||
public Dictionary<StrategyType, StrategiesResultBase> StrategiesValues { get; set; }
|
||||
|
||||
public TradingBot(
|
||||
string accountName,
|
||||
@@ -87,6 +89,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
Candles = new HashSet<Candle>();
|
||||
Positions = new List<Position>();
|
||||
WalletBalances = new Dictionary<DateTime, decimal>();
|
||||
StrategiesValues = new Dictionary<StrategyType, StrategiesResultBase>();
|
||||
|
||||
if (!isForBacktest)
|
||||
{
|
||||
@@ -184,7 +187,10 @@ public class TradingBot : Bot, ITradingBot
|
||||
await ManagePositions();
|
||||
|
||||
if (!IsForBacktest)
|
||||
{
|
||||
SaveBackup();
|
||||
UpdateStrategiesValues();
|
||||
}
|
||||
|
||||
await UpdateWalletBalances();
|
||||
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()
|
||||
{
|
||||
if (OptimizedCandles.Any())
|
||||
|
||||
@@ -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:#.##}%";
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,21 @@ 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)
|
||||
{
|
||||
var result = BuildStrategy(strategy);
|
||||
strategies.Add(result);
|
||||
}
|
||||
|
||||
return strategies;
|
||||
}
|
||||
|
||||
public static IStrategy BuildStrategy(Strategy strategy, int size = 600)
|
||||
{
|
||||
IStrategy result = strategy.Type switch
|
||||
{
|
||||
@@ -37,11 +48,8 @@ public static class ScenarioHelpers
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
result.Candles = new FixedSizeQueue<Candle>(600);
|
||||
strategies.Add(result);
|
||||
}
|
||||
|
||||
return strategies;
|
||||
result.Candles = new FixedSizeQueue<Candle>(size);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Strategy BuildStrategy(
|
||||
|
||||
18
src/Managing.Domain/Strategies/Base/StrategiesResultBase.cs
Normal file
18
src/Managing.Domain/Strategies/Base/StrategiesResultBase.cs
Normal 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; }
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,7 +275,7 @@ public class EvmManagerTests
|
||||
var manager = new EvmManager(Subgraphs);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 { setTheme } = useTheme()
|
||||
|
||||
@@ -139,6 +139,7 @@ const BacktestCards: React.FC<IBacktestCards> = ({ list, setBacktests }) => {
|
||||
positions={backtest.positions}
|
||||
walletBalances={backtest.walletBalances}
|
||||
signals={backtest.signals}
|
||||
strategiesValues={backtest.strategiesValues}
|
||||
width={720}
|
||||
height={512}
|
||||
></TradeChart>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { TradeChart, CardPositionItem } from '..'
|
||||
import type { IBotRowDetails } from '../../../global/interface'
|
||||
import { IBotRowDetails } from '../../../global/type'
|
||||
import { CardPosition } from '../../mollecules'
|
||||
|
||||
const BacktestRowDetails: React.FC<IBotRowDetails> = ({
|
||||
candles,
|
||||
positions,
|
||||
walletBalances,
|
||||
strategiesValues,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
@@ -30,11 +31,12 @@ const BacktestRowDetails: React.FC<IBotRowDetails> = ({
|
||||
<div>
|
||||
<figure>
|
||||
<TradeChart
|
||||
width={1000}
|
||||
height={500}
|
||||
width={1400}
|
||||
height={1400}
|
||||
candles={candles}
|
||||
positions={positions}
|
||||
walletBalances={walletBalances}
|
||||
strategiesValues={strategiesValues}
|
||||
signals={[]}
|
||||
></TradeChart>
|
||||
</figure>
|
||||
|
||||
@@ -247,6 +247,7 @@ const BacktestTable: React.FC<IBacktestCards> = ({ list, isFetching }) => {
|
||||
candles={row.original.candles}
|
||||
positions={row.original.positions}
|
||||
walletBalances={row.original.walletBalances}
|
||||
strategiesValues={row.original.strategiesValues}
|
||||
></BacktestRowDetails>
|
||||
</>
|
||||
),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type {
|
||||
BaselineSeriesOptions,
|
||||
CandlestickData,
|
||||
IChartApi,
|
||||
ISeriesApi,
|
||||
@@ -18,6 +19,8 @@ import type {
|
||||
KeyValuePairOfDateTimeAndDecimal,
|
||||
Position,
|
||||
Signal,
|
||||
StrategiesResultBase,
|
||||
StrategyType,
|
||||
} from '../../../../generated/ManagingApi'
|
||||
import {
|
||||
PositionStatus,
|
||||
@@ -25,11 +28,27 @@ import {
|
||||
} from '../../../../generated/ManagingApi'
|
||||
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 = {
|
||||
candles: Candle[]
|
||||
positions: Position[]
|
||||
signals: Signal[]
|
||||
walletBalances?: KeyValuePairOfDateTimeAndDecimal[] | null
|
||||
strategiesValues?: { [key in keyof typeof StrategyType]?: StrategiesResultBase; } | null;
|
||||
stream?: Candle | null
|
||||
width: number
|
||||
height: number
|
||||
@@ -40,6 +59,7 @@ const TradeChart = ({
|
||||
positions,
|
||||
signals,
|
||||
walletBalances,
|
||||
strategiesValues,
|
||||
stream,
|
||||
width,
|
||||
height,
|
||||
@@ -68,6 +88,12 @@ const TradeChart = ({
|
||||
}
|
||||
}
|
||||
|
||||
const baselineOptions: BaselineSeriesOptions = {
|
||||
bottomLineColor: theme.secondary,
|
||||
topLineColor: theme.primary,
|
||||
lineWidth: 1,
|
||||
} as BaselineSeriesOptions
|
||||
|
||||
function buildMarker(
|
||||
shape: SeriesMarkerShape,
|
||||
color: string,
|
||||
@@ -142,7 +168,7 @@ const TradeChart = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (chartRef.current) {
|
||||
const lineColor = theme.secondary
|
||||
const lineColor = theme['base-100']
|
||||
chart.current = createChart(chartRef.current, {
|
||||
crosshair: {
|
||||
mode: CrosshairMode.Normal,
|
||||
@@ -159,8 +185,8 @@ const TradeChart = ({
|
||||
},
|
||||
height: height,
|
||||
layout: {
|
||||
background: {color: '#121212'},
|
||||
textColor: theme.secondary,
|
||||
background: {color: theme['base-300']},
|
||||
textColor: theme.accent,
|
||||
},
|
||||
localization: {
|
||||
dateFormat: 'yyyy-MM-dd',
|
||||
@@ -198,11 +224,11 @@ const TradeChart = ({
|
||||
if (!chart.current) return
|
||||
|
||||
series1.current = chart.current.addCandlestickSeries({
|
||||
borderDownColor: theme.secondary,
|
||||
borderDownColor: theme.accent,
|
||||
borderUpColor: theme.primary,
|
||||
downColor: theme.secondary,
|
||||
downColor: theme.accent,
|
||||
upColor: theme.primary,
|
||||
wickDownColor: theme.secondary,
|
||||
wickDownColor: theme.accent,
|
||||
wickUpColor: theme.primary,
|
||||
})
|
||||
|
||||
@@ -230,7 +256,6 @@ const TradeChart = ({
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
const markers: SeriesMarker<Time>[] = []
|
||||
|
||||
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) {
|
||||
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) {
|
||||
const walletSeries = chart.current.addBaselineSeries({
|
||||
baseValue: {price: walletBalances[0].value, type: 'price'},
|
||||
bottomFillColor1: 'rgba( 239, 83, 80, 0.05)',
|
||||
bottomFillColor2: 'rgba( 239, 83, 80, 0.28)',
|
||||
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)',
|
||||
pane: paneCount,
|
||||
title: '$',
|
||||
})
|
||||
|
||||
const walletData = walletBalances.map((w) => {
|
||||
@@ -296,12 +569,15 @@ const TradeChart = ({
|
||||
// @ts-ignore
|
||||
walletSeries.setData(walletData)
|
||||
walletSeries.applyOptions({
|
||||
...baselineOptions,
|
||||
priceFormat: {
|
||||
minMove: 0.0001,
|
||||
precision: 4,
|
||||
type: 'price',
|
||||
},
|
||||
|
||||
})
|
||||
paneCount++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1158,6 +1158,54 @@ export class ScenarioClient extends AuthorizedApiBase {
|
||||
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[]> {
|
||||
let url_ = this.baseUrl + "/Scenario/strategy";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
@@ -1291,6 +1339,68 @@ export class ScenarioClient extends AuthorizedApiBase {
|
||||
}
|
||||
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 {
|
||||
@@ -1893,6 +2003,7 @@ export interface Backtest {
|
||||
walletBalances: KeyValuePairOfDateTimeAndDecimal[];
|
||||
optimizedMoneyManagement: MoneyManagement;
|
||||
moneyManagement: MoneyManagement;
|
||||
strategiesValues?: { [key in keyof typeof StrategyType]?: StrategiesResultBase; } | null;
|
||||
}
|
||||
|
||||
export enum Ticker {
|
||||
@@ -2152,6 +2263,94 @@ export interface KeyValuePairOfDateTimeAndDecimal {
|
||||
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 {
|
||||
botType: BotType;
|
||||
botName: string;
|
||||
|
||||
@@ -17,6 +17,8 @@ import type {
|
||||
RiskLevel,
|
||||
Scenario,
|
||||
Signal,
|
||||
StrategiesResultBase,
|
||||
StrategyType,
|
||||
Ticker,
|
||||
Timeframe,
|
||||
TradeDirection,
|
||||
@@ -144,6 +146,7 @@ export type IBotRowDetails = {
|
||||
candles: Candle[]
|
||||
positions: Position[]
|
||||
walletBalances?: KeyValuePairOfDateTimeAndDecimal[] | null
|
||||
strategiesValues?: { [key in keyof typeof StrategyType]?: StrategiesResultBase; } | null;
|
||||
}
|
||||
|
||||
export type IBacktestFormInput = {
|
||||
|
||||
@@ -36,17 +36,39 @@ const themes: ThemesInterface = {
|
||||
'--rounded-btn': '0',
|
||||
'--tab-radius': '0',
|
||||
accent: '#343232',
|
||||
'base-100': '#000000',
|
||||
'base-100': '#0B0B0B',
|
||||
'base-200': '#0D0D0D',
|
||||
'base-300': '#1A1919',
|
||||
error: '#ff0000',
|
||||
info: '#0000ff',
|
||||
neutral: '#272626',
|
||||
'base-300': '#0B0B0B',
|
||||
error: '#FF5340',
|
||||
info: '#B0DB43',
|
||||
neutral: '#151515',
|
||||
'neutral-focus': '#343232',
|
||||
primary: '#343232',
|
||||
secondary: '#343232',
|
||||
success: '#008000',
|
||||
warning: '#ffff00',
|
||||
primary: '#54B5F9',
|
||||
secondary: '#a3d9fe',
|
||||
success: '#08C25F',
|
||||
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]': {
|
||||
@@ -61,18 +83,18 @@ const themes: ThemesInterface = {
|
||||
success: '#9DB787',
|
||||
warning: '#FFD25F',
|
||||
},
|
||||
'[data-theme=cyberpunk]': {
|
||||
'--rounded-badge': '0',
|
||||
'--rounded-box': '0',
|
||||
'--rounded-btn': '0',
|
||||
'--tab-radius': '0',
|
||||
accent: '#c07eec',
|
||||
'base-100': '#ffee00',
|
||||
neutral: '#423f00',
|
||||
'neutral-content': '#ffee00',
|
||||
primary: '#ff7598',
|
||||
secondary: '#75d1f0',
|
||||
},
|
||||
// '[data-theme=cyberpunk]': {
|
||||
// '--rounded-badge': '0',
|
||||
// '--rounded-box': '0',
|
||||
// '--rounded-btn': '0',
|
||||
// '--tab-radius': '0',
|
||||
// accent: '#c07eec',
|
||||
// 'base-100': '#ffee00',
|
||||
// neutral: '#423f00',
|
||||
// 'neutral-content': '#ffee00',
|
||||
// primary: '#ff7598',
|
||||
// secondary: '#75d1f0',
|
||||
// },
|
||||
'[data-theme=lofi]': {
|
||||
'--animation-btn': '0',
|
||||
'--animation-input': '0',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
||||
daisyui: {
|
||||
themes: true,
|
||||
themes: ['black', 'coffee', 'cyberpunk', 'lofi', 'retro', 'kaigen'],
|
||||
},
|
||||
plugins: [require('@tailwindcss/typography'), require('daisyui')],
|
||||
theme: {
|
||||
|
||||
Reference in New Issue
Block a user